分类: LINUX
2014-08-31 11:03:43
原文地址:linux usb urb详解 作者:steven_miao
谨以此文纪念过往的岁月
一. 前言
在前文中看过了hub的驱动以及host的驱动还有usb设备的驱动,在把这些东西关联起来的东东中,一个很重要的urb(usb request blk),在本文中会详细来看urb的实现,以及具体的应用。
二. Urb
urb是什么东西,那先来看urb的定义吧。
1 struct urb
2 {
3 /* 私有的:只能由usb核心和主机控制器访问的字段 */
4 struct kref kref; /*urb引用计数 */
5 spinlock_t lock; /* urb锁 */
6 void *hcpriv; /* 主机控制器私有数据 */
7 int bandwidth; /* int/iso请求的带宽 */
8 atomic_t use_count; /* 并发传输计数 */
9 u8 reject; /* 传输将失败*/
10
11 /* 公共的: 可以被驱动使用的字段 */
12 struct list_head urb_list; /* 链表头*/
13 struct usb_device *dev; /* 关联的usb设备 */
14 unsigned int pipe; /* 管道信息 */
15 int status; /* urb的当前状态 */
16 unsigned int transfer_flags; /* urb_short_not_ok | ...*/
17 void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */
18 dma_addr_t transfer_dma; /*用来以dma方式向设备传输数据的缓冲区 */
19 int transfer_buffer_length;/*transfer_buffer或transfer_dma 指向缓冲区的大小 */
20
21 int actual_length; /* urb结束后,发送或接收数据的实际长度 */
22 unsigned char *setup_packet; /* 指向控制urb的设置数据包的指针*/
23 dma_addr_t setup_dma; /*控制urb的设置数据包的dma缓冲区*/
24 int start_frame; /*等时传输中用于设置或返回初始帧*/
25 int number_of_packets; /*等时传输中等时缓冲区数据 */
26 int interval; /* urb被轮询到的时间间隔(对中断和等时urb有效) */
27 int error_count; /* 等时传输错误数量 */
28 void *context; /* completion函数上下文 */
29 usb_complete_t complete; /* 当urb被完全传输或发生错误时,被调用 */
30 struct 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, gfp_t )
{
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(&urb->kref); --初始化urb计数器
INIT_LIST_HEAD(&urb->anchor_list); --初始化锁定链表
}
}
一般会在初始化之后,对urb的各个成员进行初始化。例如调用usb_fill_int_urb对urb进行成员的填充。在此不做讲述,这个还是比较简单的。
2.2 usb_submit_urb 提交urb
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
int xfertype, max;
struct usb_device *dev;
struct usb_host_endpoint *ep;
int is_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(&ep->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(&ep->desc);
}
urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) | (is_out ? URB_DIR_OUT : URB_DIR_IN); --缓存的方向留待后用
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&dev->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) { --等时传输
int n, len;
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
max &= 0x07ff;
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 (urb, mem_flags);
}
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
usb_get_urb(urb);
atomic_inc(&urb->use_count); --增减urb计数
atomic_inc(&urb->dev->urbnum); --增加dev的urb计数
usbmon_urb_submit(&hcd->self, urb); --退化为空函数
status = map_urb_for_dma(hcd, urb, mem_flags); --将urb的缓冲区与dma的区间映射
if (unlikely(status)) {
usbmon_urb_submit_error(&hcd->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(&hcd->self, urb, status);
unmap_urb_for_dma(hcd, urb);
error:
urb->hcpriv = NULL;
INIT_LIST_HEAD(&urb->urb_list);
atomic_dec(&urb->use_count);
atomic_dec(&urb->dev->urbnum);
if (atomic_read(&urb->reject))
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
return status;
}
在函数usb_hcd_submit_urb调用该hcd的urb_enqueue,在ohci中将会调用ohci_urb_enqueue。其实到此我们需要放下urb的探索,重新来看ohci中关于ed,td的应用。在前文中对ohci的probe进行了讲述,但是对于其余的函数并没有仔细说明,那在后中仔细来看关于ohci的详情。
2.3 usb_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);
list_add_tail(&, &;
urb->anchor = anchor;
if (unlikely(anchor->poisoned)) {
atomic_inc(&urb->reject);
}
spin_unlock_irqrestore(&anchor->lock, flags);
}
2.4 usb_unanchor_urb
该函数与上面的函数刚好相反,解锁一个urb同时删除urb
void(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(&urb->anchor_list); --将urb从anchor的链表中删除
spin_unlock_irqrestore(&anchor->lock, flags);
usb_put_urb(urb); --减少urb计数同时删除urb == usb_free_urb
if (list_empty(&anchor->urb_list))
(&anchor->wait);
}
void (struct urb *urb)
{
if (urb)
kref_put(&urb->kref, urb_destroy);
}
在某一方面来讲anchor类似一个urb的守护链表,在urb被使用时是不能被删除的。在删除一个urb时需要调用usb_wait_anchor_empty_timeout来等待urb传输完全结束。下面的wait_event_timeout则与上面的wake_up相互对应。
int(struct usb_anchor *anchor,unsigned int timeout)
{
return (anchor->wait, list_empty(&anchor->urb_list),
msecs_to_jiffies(timeout));
}
三. 总结
urb在某一方面来说是设备驱动与hcd的通信的东东,关于urb的处理其本质还是在ohci中。这个将会在后面的学习中好好学习。