linux的usb驱动中urb的理解
linux 内核中的 USB 代码和所有的 USB 设备通讯使用称为 urb 的东西( USB request block). 这个请求块用 struct urb 结构描述并且可在 include/linux/usb.h 中找到.
一个urb 用来发送或接受数据到或者从一个特定 USB 设备上的特定的 USB 端点, 以一种异步的方式.一个 USB 设备驱动可能分配许多 urb 给一个端点或者可能重用单个 urb 给多个不同的端点, 根据驱动的需要. 设备中的每个端点都处理一个 urb 队列, 以至于多个 urb 可被发送到相同的端点, 在队列清空之前. 一个 urb 的典型生命循环如下:
-
被一个 USB 设备驱动创建.
-
安排给一个特定 USB 设备的特定端点.
-
提交给 USB 核心, 被 USB 设备驱动.
-
提交给特定设备的被 USB 核心指定的 USB 主机控制器驱动, .
-
被 USB 主机控制器处理, 它做一个 USB 传送到设备.
-
当 urb 完成, USB 主机控制器驱动通知 USB 设备驱动.
urb 也可被提交这个 urb 的驱动在任何时间取消, 或者被 USB 核心如果设备被从系统中移出. urb 被动态创建并且包含一个内部引用计数, 使它们在这个 urb 的最后一个用户释放它时被自动释放.
1、URB结构体分析
struct urb代码如下:
1 struct urb { 2 /* private: usb core and host controller only fields in the urb */ 3 struct kref kref; /* reference count of the URB */ 4 void *hcpriv; /* private data for host controller */ 5 atomic_t use_count; /* concurrent submissions counter */ 6 atomic_t reject; /* submissions will fail */ 7 int unlinked; /* unlink error code */ 8 9 /* public: documented fields in the urb that can be used by drivers */ 10 struct list_head urb_list; /* list head for use by the urb's 11 * current owner */ 12 struct list_head anchor_list; /* the URB may be anchored */ 13 struct usb_anchor *anchor; 14 struct usb_device *dev; /* (in) pointer to associated device */ 15 struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */ 16 unsigned int pipe; /* (in) pipe information */ 17 int status; /* (return) non-ISO status */ 18 unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ 19 void *transfer_buffer; /* (in) associated data buffer */ 20 dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ 21 struct usb_sg_request *sg; /* (in) scatter gather buffer list */ 22 int num_sgs; /* (in) number of entries in the sg list */ 23 u32 transfer_buffer_length; /* (in) data buffer length */ 24 u32 actual_length; /* (return) actual transfer length */ 25 unsigned char *setup_packet; /* (in) setup packet (control only) */ 26 dma_addr_t setup_dma; /* (in) dma addr for setup_packet */ 27 int start_frame; /* (modify) start frame (ISO) */ 28 int number_of_packets; /* (in) number of ISO packets */ 29 int interval; /* (modify) transfer interval 30 * (INT/ISO) */ 31 int error_count; /* (return) number of ISO errors */ 32 void *context; /* (in) context for completion */ 33 usb_complete_t complete; /* (in) completion routine */ 34 struct usb_iso_packet_descriptor iso_frame_desc[0]; 35 /* (in) ISO ONLY */ 36 };
14 struct usb_device *dev; /* (in) pointer to associated device */
指向这个urb要发送的struct usb_device 的指针,在urb发送到usb核心之前,这个变量必须被usb驱动初始化。
16 unsigned int pipe; /* (in) pipe information */
可以称之为管道,也可以称为端点消息,是给这个urb发送到的特定struct usb_device,同理,在urb发送到usb核心之前,这个变量必须被usb驱动初始化。
为了设置这个pipe,驱动有一些初始化函数,这里要注意,每个pipe只可能是其中一种类型、、、
unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint) //指定一个控制 OUT 端点给特定的带有特定端点号的 USB 设备. unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint) //指定一个控制 IN 端点给带有特定端点号的特定 USB 设备. unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint) //指定一个块 OUT 端点给带有特定端点号的特定 USB 设备 unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint) //指定一个块 IN 端点给带有特定端点号的特定 USB 设备 unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint) //指定一个中断 OUT 端点给带有特定端点号的特定 USB 设备 unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint) //指定一个中断 IN 端点给带有特定端点号的特定 USB 设备 unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint) //指定一个同步 OUT 端点给带有特定端点号的特定 USB 设备 unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint) //指定一个同步 IN 端点给带有特定端点号的特定 USB 设备
18 unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
这个变量可被设置为不同的位值,根据这个位,usb驱动可以设置urb传输的状态,具体可用值详见ldd3.
19 void *transfer_buffer; /* (in) associated data buffer */
指向缓冲区的指针,这个指针可以是OUT urb 或者是In urb。主机控制器为了正确使用这个缓冲区,必须使用kmalloc调用来创建它,而不是堆栈或者静态数据区。对于控制端点,这个缓冲是给发送的数据。
20 dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
用来使用DMA传送数据到usb设备的缓冲。
23 u32 transfer_buffer_length; /* (in) data buffer length */
缓冲区的长度,由于urb只会使用buffer或者dma其中一种来传输数据,所以这个长度可以被它们共用。如果这是0,表示没有传送缓冲被usb核心所使用。
25 unsigned char *setup_packet; /* (in) setup packet (control only) */
指向一个控制urb的setup报文的指针,在位于正常传送缓冲的数据之前被传送,这个变量只对控制urb有效。
26 dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
给控制 urb 的 setupt 报文的 DMA 缓冲. 在位于正常传送缓冲的数据之前被传送. 这个变量只对控制 urb 有效.
24 u32 actual_length; /* (return) actual transfer length */
当urb完成处理后,这个变量被设置为数据的真实长度,或者由这个urb(OUT)发送,或者由这个urb(IN)接受。对于IN urb,这个值必须被用来替代taansfer_buffer_length,因为接收的数据可能比整个缓冲区的大小小。
17 int status; /* (return) non-ISO status */
当这个 urb 被结束, 或者开始由 USB 核心处理, 这个变量被设置为 urb 的当前状态. 一个 USB 驱动可安全存取这个变量的唯一时间是在 urb 完成处理者函数中(在"CompletingUrbs: 完成回调处理者"一节中描述). 这个限制是阻止竞争情况, 发生在这个 urb 被 USB 核心处理当中. 对于同步 urb, 在这个变量中的一个成功的值(0)只指示是否这个 urb 已被去链. 为获得在同步 urb 上的详细状态, 应当检查 iso_frame_desc 变量.
这个变量的有效值包括:
0 //这个 urb 传送是成功的. -ENOENT //这个 urb 被对 usb_kill_urb 的调用停止. -ECONNRESET //urb 被对 usb_unlink_urb 的调用去链, 并且 transfer_flags 变量被设置为 URB_ASYNC_UNLINK. -EINPROGRESS /*这个 urb 仍然在被 USB 主机控制器处理中. 如果你的驱动曾见到这个值, 它是一个你的驱动中的 bug.*/ -EPROTO /*这个 urb 发生下面一个错误: 一个 bitstuff 错误在传送中发生. 硬件没有及时收到响应帧.*/ -EILSEQ //在这个 urb 传送中有一个 CRC 不匹配. -EPIPE /*这个端点现在被停止. 如果这个包含的端点不是一个控制端点, 这个错误可被清除通过一个对函数 usb_clear_halt 的调用.*/ -ECOMM //在传送中数据接收快于能被写入系统内存. 这个错误值只对 IN urb. -ENOSR /*在传送中数据不能从系统内存中获取得足够快, 以便可跟上请求的 USB 数据速率. 这个错误只对 OUT urb.*/ -EOVERFLOW /*这个 urb 发生一个"babble"错误. 一个"babble"错误发生当端点接受数据多于端点的特定最大报文大小.*/ -EREMOTEIO /*只发生在当 URB_SHORT_NOT_OK 标志被设置在 urb 的 transfer_flags 变量, 并且意味着 urb 请求的完整数量的数据没有收到.*/ -ENODEV //这个 USB 设备现在从系统中消失. -EXDEV /*只对同步 urb 发生, 并且意味着传送只部分完成. 为了决定传送什么, 驱动必须看单独的帧状态.*/ -EINVAL /*这个 urb 发生了非常坏的事情. USB 内核文档描述了这个值意味着什么: ISO 疯了, 如果发生这个: 退出并回家. 它也可发生, 如果一个参数在 urb 结构中被不正确地设置了, 或者如果在提交这个 urb 给 USB 核心的 usb_submit_urb 调用中, 有一个不正确的函数参数.*/ -ESHUTDOWN /*这个 USB 主机控制器驱动有严重的错误; 它现在已被禁止, 或者设备和系统去掉连接, 并且这个urb 在设备被去除后被提交. 它也可发生当这个设备的配置改变, 而这个 urb 被提交给设备. 通常, 错误值 -EPROTO, -EILSEQ, 和 -EOVERFLOW 指示设备的硬件问题, 设备固件, 或者连接设备到计算机的线缆.*/
33 usb_complete_t complete; /* (in) completion routine */
指向完成处理者函数的指针, 它被 USB 核心调用当这个 urb 被完全传送或者当 urb 发生一个错误. 在这个函数中, USB 驱动可检查这个 urb, 释放它, 或者重新提交它给另一次传送.
32 void *context; /* (in) context for completion */
指向数据点的指针, 它可被 USB 驱动设置. 它可在完成处理者中使用当 urb 被返回到驱动. 可能是在回调函数使用的数据。
27 int start_frame; /* (modify) start frame (ISO) */
设置或者返回同步传送要使用的初始帧号。
29 int interval; /* (modify) transfer interval
urb 被轮询的间隔. 这只对中断或者同步 urb 有效. 这个值的单位依据设备速度而不同. 对于低速和高速的设备, 单位是帧, 它等同于毫秒. 对于单位是宏帧的设备, 它等同于 1/8 微秒单位. 在这个 urb被发送到 USB 核心之前,这个值必须被 USB 驱动设置给同步或者中断 urb .
28 int number_of_packets; /* (in) number of ISO packets */
只对同步 urb 有效, 并且指定这个 urb 要处理的同步传送缓冲的编号. 在这个 urb 发送给 USB 核心之前,这个值必须被 USB 驱动设置给同步 urb .
31 int error_count; /* (return) number of ISO errors */
被 USB 核心设置, 只给同步 urb 在它们完成之后. 它指定报告任何类型错误的同步传送的号码.
34 struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */
只对同步 urb 有效. 这个变量是组成这个 urb 的一个 struct usb_iso_packet_descriptor 结构数组. 这个结构允许单个 urb 来一次定义多个同步传送. 它也用来收集每个单独传送的传送状态
2、URB创建和销毁
struct urb 结构在驱动中必须不能被静态创建, 或者在另一个结构中, 因为这可能破坏 USB 核心给 urb 使用的引用计数方法. 它必须使用对 usb_alloc_urb 函数的调用而被创建. 这个函数有这个原型:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
第一个参数, iso_packet, 是这个 urb 应当包含的同步报文的数目. 如果你不想创建一个同步 urb, 这个变量应当被设置为 0. 第 2 个参数, mem_flags, 是和传递给 kmalloc 函数调用来从内核分配内存的相同的标志类型(见"flags 参数"一节, 第 8 章, 关于这些标志的细节). 如果这个函数在分配足够内存给这个 urb 成功, 一个指向 urb 的指针被返回给调用者. 如果返回值是 NULL, 某个错误在 USB 核心中发生了, 并且驱动需要正确地清理.
在创建了一个 urb 之后, 它必须被正确初始化在它可被 USB 核心使用之前. 如何初始化不同类型 urb 见下一节
为了告诉 USB 核心驱动用完这个 urb, 驱动必须调用 usb_free_urb 函数. 这个函数只有一个参数:
void usb_free_urb(struct urb *urb);
参数是一个指向你要释放的 struct urb 的指针. 在这个函数被调用之后, urb 结构消失, 驱动不能再存取它.
3、中断 urb
函数 usb_fill_int_urb 是一个帮忙函数, 来正确初始化一个urb 来发送给 USB 设备的一个中断端点:
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,
void *context, int interval);
这个函数包含许多参数:
- struct urb *urb
-
指向要被初始化的 urb 的指针.
- struct usb_device *dev
-
这个 urb 要发送到的 USB 设备.
- unsigned int pipe
-
这个 urb 要被发送到的 USB 设备的特定端点. 这个值被创建, 使用前面提过的 usb_sndintpipe 或者 usb_rcvintpipe 函数.
- void *transfer_buffer
-
指向缓冲的指针, 从那里外出的数据被获取或者进入数据被接受. 注意这不能是一个静态的缓冲并且必须使用 kmalloc 调用来创建.
- int buffer_length
-
缓冲的长度, 被 transfer_buffer 指针指向.
- usb_complete_t complete
-
指针, 指向当这个 urb 完成时被调用的完成处理者.
- void *context
-
指向数据块的指针, 它被添加到这个 urb 结构为以后被完成处理者函数获取.
- int interval
-
这个 urb 应当被调度的间隔. 见之前的 struct urb 结构的描述, 来找到这个值的正确单位.
4、块urb
块 urb 被初始化非常象中断 urb. 做这个的函数是 usb_fill_bulk_urb, 它看来如此:
void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete, void *context);
这个函数参数和 usb_fill_int_urb 函数的都相同. 但是, 没有 interval 参数因为 bulk urb 没有间隔值. 请注意这个 unsiged int pipe 变量必须被初始化用对 usb_sndbulkpipe 或者 usb_rcvbulkpipe 函数的调用.
usb_fill_int_urb 函数不设置 urb 中的 transfer_flags 变量, 因此任何对这个成员的修改不得不由这个驱动自己完成.
5、控制urb
控制 urb 被初始化几乎和 块 urb 相同的方式, 使用对函数 usb_fill_control_urb 的调用:
void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, unsigned char *setup_packet,
void *transfer_buffer, int buffer_length,
usb_complete_t complete, void *context);
函数参数和 usb_fill_bulk_urb 函数都相同, 除了有个新参数, unsigned char *setup_packet, 它必须指向要发送给端点的 setup 报文数据. 还有, unsigned int pipe 变量必须被初始化, 使用对 usb_sndctrlpipe 或者 usb_rcvictrlpipe 函数的调用.
usb_fill_control_urb 函数不设置 transfer_flags 变量在 urb 中, 因此任何对这个成员的修改必须游驱动自己完成. 大部分驱动不使用这个函数, 因为使用在"USB 传送不用 urb"一节中介绍的同步 API 调用更简单.
6、同步urb
不幸的是, 同步 urb 没有一个象中断, 控制, 和块 urb 的初始化函数. 因此它们必须在驱动中"手动"初始化, 在它们可被提交给 USB 核心之前. 下面是一个如何正确初始化这类 urb 的例子. 它是从 konicawc.c 内核驱动中取得的, 它位于主内核源码树的 drivers/usb/media 目录.
urb->dev = dev; urb->context = uvd; urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1); urb->interval = 1; urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = cam->sts_buf[i]; urb->complete = konicawc_isoc_irq; urb->number_of_packets = FRAMES_PER_DESC; urb->transfer_buffer_length = FRAMES_PER_DESC; for (j=0; j < FRAMES_PER_DESC; j++) { urb->iso_frame_desc[j].offset = j; urb->iso_frame_desc[j].length = 1; }
7、提交 urb
一旦 urb 被正确地创建,并且被 USB 驱动初始化, 它已准备好被提交给 USB 核心来发送出到 USB 设备. 这通过调用函数 usb_submit_urb 实现:
int usb_submit_urb(struct urb *urb, int mem_flags);
urb 参数是一个指向 urb 的指针, 它要被发送到设备. mem_flags 参数等同于传递给 kmalloc 调用的同样的参数, 并且用来告诉 USB 核心如何及时分配任何内存缓冲在这个时间.
在 urb 被成功提交给 USB 核心之后, 应当从不试图存取 urb 结构的任何成员直到完成函数被调用.
因为函数 usb_submit_urb 可被在任何时候被调用(包括从一个中断上下文), mem_flags 变量的指定必须正确.根据 usb_submit_urb 被调用的时间,只有 3 个有效值可用:
GFP_ATOMIC
只要满足以下条件,就应当使用此值:
1.调用者处于一个 urb 结束处理例程,中断处理例程,底半部,tasklet或者一个定时器回调函数.
2.调用者持有自旋锁或者读写锁. 注意如果正持有一个信号量, 这个值不必要.
3.current->state 不是 TASK_RUNNING. 除非驱动已自己改变 current 状态,否则状态应该一直是 TASK_RUNNING .
GFP_NOIO
驱动处于块 I/O 处理过程中. 它还应当用在所有的存储类型的错误处理过程中.
GFP_KERNEL
所有不属于之前提到的其他情况
8、完成 urb: 完成回调处理者
如果对 usb_submit_urb 的调用成功, 驱动将传递对 urb 的控制给 USB 核心, 这个函数返回 0; 否则, 一个负错误值被返回. 如果函数调用成功, urb 的完成处理者(就是complete指定的的函数)被确切地调用一次, 当 urb 被完成. 当这个函数被调用, USB 核心完成这个 urb, 并且对它的控制现在返回给设备驱动.
只有 3 个方法, 一个urb 可被结束并且使完成函数被调用:
-
urb 被成功发送给设备, 并且设备返回正确的确认. 对于一个 OUT urb, 数据被成功发送, 对于一个 IN urb, 请求的数据被成功收到. 如果发生这个, urb 中的状态变量被设置为 0.
-
一些错误连续发生, 当发送或者接受数据从设备中. 被 urb 结构中的 status 变量中的错误值所记录.
-
这个 urb 被从 USB 核心去链. 这发生在要么当驱动告知 USB 核心取消一个已提交的 urb 通过调用 usb_unlink_urb 或者 usb_kill_urb, 要么当设备从系统中去除, 以及一个 urb 已经被提交给它
9、取消 urb
为停止一个已经提交给 USB 核心的 urb, 函数 usb_kill_urb 或者 usb_unlink_urb 应当被调用:
int usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);
The urb parameter for both of these functions is a pointer to the urb that is to be canceled.
当函数是 usb_kill_urb, 这个 urb 的生命循环就停止了. 这个函数常常在设备从系统去除时被使用, 在去连接回调中.
对一些驱动, 应当用 usb_unlink_urb 函数来告知 USB 核心去停止 urb. 这个函数在返回到调用者之前不等待这个 urb 完全停止. 这对于在中断处理或者持有一个自旋锁时停止 urb 时是有用的, 因为等待一个 urb 完全停止需要 USB 核心有能力使调用进程睡眠. 为了正确工作这个函数要求 URB_ASYNC_UNLINK 标志值被设置在正被要求停止的 urb 中.
至此,关于urb的结构,功能,提交和取消全部介绍完毕,对于一般的USB驱动,应该是足够用了,本文大多数内容引用自ldd3,有少部分我做了修改,方便自己查阅,同时分享自己的学习心得。