打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
linux usb urb详解-steven
谨以此文纪念过往的岁月
一.前言
在前文中看过了hub的驱动以及host的驱动还有usb设备的驱动,在把这些东西关联起来的东东中,一个很重要的urb(usb request blk),在本文中会详细来看urb的实现,以及具体的应用。
二.Urb
urb是什么东西,那先来看urb的定义吧。
1struct urb
2{
3/* 私有的:只能由usb核心和主机控制器访问的字段*/
4struct kref kref; /*urb引用计数*/
5spinlock_t lock; /* urb*/
6void *hcpriv; /* 主机控制器私有数据*/
7int bandwidth; /* int/iso请求的带宽*/
8atomic_t use_count; /* 并发传输计数*/
9u8 reject; /* 传输将失败*/
10
11/* 公共的: 可以被驱动使用的字段*/
12struct list_head urb_list; /* 链表头*/
13struct usb_device *dev; /* 关联的usb设备*/
14unsigned int pipe; /* 管道信息*/
15int status; /* urb的当前状态*/
16unsigned int transfer_flags; /* urb_short_not_ok | ...*/
17void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区*/
18dma_addr_t transfer_dma; /*用来以dma方式向设备传输数据的缓冲区*/
19int transfer_buffer_length;/*transfer_buffertransfer_dma 指向缓冲区的大小*/
20
21int actual_length; /* urb结束后,发送或接收数据的实际长度*/
22unsigned char *setup_packet; /* 指向控制urb的设置数据包的指针*/
23dma_addr_t setup_dma; /*控制urb的设置数据包的dma缓冲区*/
24int start_frame; /*等时传输中用于设置或返回初始帧*/
25int number_of_packets; /*等时传输中等时缓冲区数据*/
26int interval; /* urb被轮询到的时间间隔(对中断和等时urb有效)*/
27int error_count;/* 等时传输错误数量 */
28void *context; /* completion函数上下文*/
29usb_complete_t complete; /* urb被完全传输或发生错误时,被调用*/
30struct usb_iso_packet_descriptor iso_frame_desc[0];
31/*单个urb一次可定义多个等时传输时,描述各个等时传输*/
32 };
2.1 urb申请
usb_alloc_urb开辟一个urb空间并对其部分的成员初始化。
iso_packets:等时传输的包数
mem_flags:开辟urb空间的mem旗标,一般为GFP_KERNEL
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
urb = kmalloc(sizeof(struct urb) +iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);--开辟空间
if (!urb) {
return NULL;
}
usb_init_urb(urb); --初始化部分成员
return urb;
}
void usb_init_urb(struct urb *urb)
{
if (urb) {
memset(urb, 0, sizeof(*urb));
kref_init(kref);--初始化urb计数器
INIT_LIST_HEAD(anchor_list);--初始化锁定链表
}
}
一般会在初始化之后,对urb的各个成员进行初始化。例如调用usb_fill_int_urburb进行成员的填充。在此不做讲述,这个还是比较简单的。
2.2 usb_submit_urb 提交urb
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
intxfertype, max;
struct usb_device*dev;
struct usb_host_endpoint*ep;
intis_out;
if (!urb || urb->hcpriv || !urb->complete)
return -EINVAL;
dev = urb->dev;
if ((!dev) || (dev->state < USB_STATE_DEFAULT))
return -ENODEV;
ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) [usb_pipeendpoint(urb->pipe)]; --查找设备端点
if (!ep)
return -ENOENT;
urb->ep = ep;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
xfertype = usb_endpoint_type(desc);
if (xfertype == USB_ENDPOINT_XFER_CONTROL) {--如果是控制类型
struct usb_ctrlrequest *setup = (struct usb_ctrlrequest *) urb->setup_packet;
if (!setup)
return -ENOEXEC;
is_out = !(setup->bRequestType & USB_DIR_IN) ||!setup->wLength;
} else {
is_out = usb_endpoint_dir_out(desc);
}
urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) | (is_out ? URB_DIR_OUT : URB_DIR_IN);--缓存的方向留待后用
if (xfertype != USB_ENDPOINT_XFER_CONTROL &state < USB_STATE_CONFIGURED)--在设备没有配置前,传输的类型必为控制类型,唯有设备配置完成后才能传输其他类型的urb
return -ENODEV;
max = le16_to_cpu(ep->desc.wMaxPacketSize);
if (max <= 0) {
return -EMSGSIZE;
}
if (xfertype == USB_ENDPOINT_XFER_ISOC) {--等时传输
intn, len;
if (dev->speed == USB_SPEED_HIGH) {
intmult = 1 + ((max >> 11)
max &
max *= mult;
}
if (urb->number_of_packets <= 0)
return -EINVAL;
for (n = 0; n < urb->number_of_packets; n++) {
len = urb->iso_frame_desc[n].length;
if (len < 0 || len > max)
return -EMSGSIZE;
urb->iso_frame_desc[n].status = -EXDEV;
urb->iso_frame_desc[n].actual_length = 0;
}
}
if (urb->transfer_buffer_length < 0)
return -EMSGSIZE;
switch (xfertype) {--对等时传输和中断传输的等待时间进行检测
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
if (urb->interval <= 0)
return -EINVAL;
switch (dev->speed) {
case USB_SPEED_HIGH:
/* NOTE usb handles 2^15 */
if (urb->interval > (1024 * 8))
urb->interval = 1024 * 8;
max = 1024 * 8;
break;
case USB_SPEED_FULL:
case USB_SPEED_LOW:
if (xfertype == USB_ENDPOINT_XFER_INT) {
if (urb->interval > 255)
return -EINVAL;
max = 128;
} else {
if (urb->interval > 1024)
urb->interval = 1024;
max = 1024;
}
break;
default:
return -EINVAL;
}
urb->interval = min(max, 1 << ilog2(urb->interval));
}
return usb_hcd_submit_urb(urb, mem_flags);
}
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
intstatus;
struct usb_hcd*hcd = bus_to_hcd(urb->dev->bus);
usb_get_urb(urb);
atomic_inc(use_count);--增减urb计数
atomic_inc(dev->urbnum);--增加devurb计数
usbmon_urb_submit(self, urb);--退化为空函数
status = map_urb_for_dma(hcd, urb, mem_flags);--urb的缓冲区与dma的区间映射
if (unlikely(status)) {
usbmon_urb_submit_error(self, urb, status);
goto error;
}
if (is_root_hub(urb->dev))
status = rh_urb_enqueue(hcd, urb);
else
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);--urb入队
if (unlikely(status)) {
usbmon_urb_submit_error(self, urb, status);
unmap_urb_for_dma(hcd, urb);
error:
urb->hcpriv = NULL;
INIT_LIST_HEAD(urb_list);
atomic_dec(use_count);
atomic_dec(dev->urbnum);
if (atomic_read(reject))
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
return status;
}
在函数usb_hcd_submit_urb调用该hcdurb_enqueue,在ohci中将会调用ohci_urb_enqueue。其实到此我们需要放下urb的探索,重新来看ohci中关于edtd的应用。在前文中对ohciprobe进行了讲述,但是对于其余的函数并没有仔细说明,那在后中仔细来看关于ohci的详情。
2.3usb_anchor_urb
urb在处理的时候锁定。这个函数其实实现很简单就是将urb->anchor_list添加到anchor->urb_list中。关于该函数的具体实现我并不知道具体是做什么用的。
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
unsigned long flags;
spin_lock_irqsave(&anchor->lock, flags);
usb_get_urb(urb);
urb->anchor = anchor;
if (unlikely(anchor->poisoned)) {
atomic_inc(reject);
}
spin_unlock_irqrestore(&anchor->lock, flags);
}
2.4 usb_unanchor_urb
该函数与上面的函数刚好相反,解锁一个urb同时删除urb
void usb_unanchor_urb(struct urb *urb)
{
unsigned long flags;
struct usb_anchor *anchor;
if (!urb)
return;
anchor = urb->anchor;
if (!anchor)
return;
spin_lock_irqsave(&anchor->lock, flags);
if (unlikely(anchor != urb->anchor)) {
spin_unlock_irqrestore(&anchor->lock, flags);
return;
}
urb->anchor = NULL;
list_del(anchor_list);--urbanchor的链表中删除
spin_unlock_irqrestore(&anchor->lock, flags);
usb_put_urb(urb);--减少urb计数同时删除urb == usb_free_urb
if (list_empty(&anchor->urb_list))
wake_up(&anchor->wait);
}
void usb_free_urb(struct urb *urb)
{
if (urb)
kref_put(kref, urb_destroy);
}
在某一方面来讲anchor类似一个urb的守护链表,在urb被使用时是不能被删除的。在删除一个urb时需要调用usb_wait_anchor_empty_timeout来等待urb传输完全结束。下面的wait_event_timeout则与上面的wake_up相互对应。
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,unsigned int timeout)
{
return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
msecs_to_jiffies(timeout));
}
三.总结
urb在某一方面来说是设备驱动与hcd的通信的东东,关于urb的处理其本质还是在ohci中。这个将会在后面的学习中好好学习。
阅读(4282) | 评论(0) | 转发(2) |
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
linux设备驱动之USB数据传输分析
Linux USB驱动工作流程
Linux系统下USB总线工作原理
USB设备主机侧驱动
[转贴] 编写Linux下的USB键盘驱(一) - linux - cheney1982
usb中urb相关接口函数
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服