Chinaunix首页 | 论坛 | 博客
  • 博客访问: 440515
  • 博文数量: 177
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-22 19:16
文章分类

全部博文(177)

文章存档

2017年(1)

2016年(12)

2015年(112)

2014年(52)

我的朋友

分类: LINUX

2015-03-25 19:39:27

原文地址:linux usb urb详解 作者:steven_miao

linux usb urb详解

谨以此文纪念过往的岁月

一.   前言

在前文中看过了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_buffertransfer_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_urburb进行成员的填充。在此不做讲述,这个还是比较简单的。

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);   --增加devurb计数

         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调用该hcdurb_enqueue,在ohci中将会调用ohci_urb_enqueue。其实到此我们需要放下urb的探索,重新来看ohci中关于edtd的应用。在前文中对ohciprobe进行了讲述,但是对于其余的函数并没有仔细说明,那在后中仔细来看关于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);  --urbanchor的链表中删除

         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中。这个将会在后面的学习中好好学习。

 

阅读(1101) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~