1. urb简介
usb总线是一种轮询式总线,协议规定所有的数据传输都必须由主机发起,usb主机与设备之间是通过管道(pipe)传输的,管道两边分别对应主机中的数据缓冲区和设备侧的端点(endpoint),端点是通信的发送和接收点,要发送数据,只要把数据发到对应的端点就可以,而这个数据发送的动作由usb主机实现,驱动中只需确定接收端点,然后把数据提交给主机控制器,主机会把数据发送给接收端点,原理同i2c,uart类似。每个usb设备中都存在一个特殊端点endpoint0,在usb设备枚举过程里,就是通过endpoint0来获取usb设备信息。
USB按传输类型分可以分为控制传输(control),中断传输(interrupt),等时传输(isochronous),批量传输(bulk),其中控制传输和批量传输又称为非周期性传输方式(nonperiodic),而中断传输和等时传输称为周期性传输方式(periodic);
一个urb 用来发送或接受数据到或者从一个特定 USB 设备上的特定的 USB 端点, 以一种异步的方式.一个 USB 设备驱动可能分配许多 urb 给一个端点或者可能重用单个 urb 给多个不同的端点, 根据驱动的需要. 设备中的每个端点都处理一个 urb 队列, 以至于多个 urb 可被发送到相同的端点。
USB控制器和设备之间的传输数据结构由urb表示,urb具体内容如下所示:
struct urb
{
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
spinlock_t lock; /* lock for the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
u8 reject; /* submissions will fail */
/* public: documented fields in the urb that can be used by drivers */
struct list_head urb_list; /* list head for use by the urb's
* current owner */
struct usb_device *dev; /* (in) pointer to associated device */
unsigned int pipe; /* (in) pipe information */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
int transfer_buffer_length; /* (in) data buffer length */
int actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};
struct usb_device *dev;
//指向这个urb要发送的struct usb_device 的指针,在urb发送到usb核心之前,这个变量必须被usb驱动初始化。
unsigned int pipe;
//可以称之为管道,也可以称为端点消息,是给这个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 设备
unsigned int transfer_flags;
//这个变量可被设置为不同的位值,根据这个位,usb驱动可以设置urb传输的状态,具体可用值详见ldd3.
void *transfer_buffer;
//指向缓冲区的指针,这个指针可以是OUT urb 或者是In urb。主机控制器为了正确使用这个缓冲区,必须使用kmalloc调用来创建它,而不是堆栈或者静态数据区。对于控制端点,这个缓冲是给发送的数据。
dma_addr_t transfer_dma;
//用来使用DMA传送数据到usb设备的缓冲。
u32 transfer_buffer_length;
//缓冲区的长度,由于urb只会使用buffer或者dma其中一种来传输数据,所以这个长度可以被它们共用。如果这是0,表示没有传送缓冲被usb核心所使用。
unsigned char *setup_packet;
//指向一个控制urb的setup报文的指针,在位于正常传送缓冲的数据之前被传送,这个变量只对控制urb有效。
dma_addr_t setup_dma;
//给控制 urb 的 setupt 报文的 DMA 缓冲. 在位于正常传送缓冲的数据之前被传送. 这个变量只对控制 urb 有效
.
u32 actual_length;
//当urb完成处理后,这个变量被设置为数据的真实长度,或者由这个urb(OUT)发送,或者由这个urb(IN)接受。对于IN urb,这个值必须被用来替代taansfer_buffer_length,因为接收的数据可能比整个缓冲区的大小小。
int 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
-EPROTO
-EILSEQ //在这个 urb 传送中有一个 CRC 不匹配.
-EPIPE
-ECOMM //在传送中数据接收快于能被写入系统内存. 这个错误值只对 IN urb.
-ENOSR
-EOVERFLOW
-EREMOTEIO
-ENODEV //这个 USB 设备现在从系统中消失.
-EXDEV
-EINVAL
-ESHUTDOWN
33 usb_complete_t complete;
//指向完成处理者函数的指针, 它被 USB 核心调用当这个 urb 被完全传送或者当 urb 发生一个错误. 在这个函数中, USB 驱动可检查这个 urb, 释放它, 或者重新提交它给另一次传送.
32 void *context;
//指向数据点的指针, 它可被 USB 驱动设置. 它可在完成处理者中使用当 urb 被返回到驱动. 可能是在回调函数使用的数据。
27 int start_frame;
//设置或者返回同步传送要使用的初始帧号。
29 int interval;
//只对同步 urb 有效, 并且指定这个 urb 要处理的同步传送缓冲的编号. 在这个 urb 发送给 USB 核心之前,这个值必须被 USB 驱动设置给同步 urb .
31 int error_count;
//被 USB 核心设置, 只给同步 urb 在它们完成之后. 它指定报告任何类型错误的同步传送的号码.
34 struct usb_iso_packet_descriptor iso_frame_desc[0];
//只对同步 urb 有效. 这个变量是组成这个 urb 的一个 struct usb_iso_packet_descriptor 结构数组. 这个结构允许单个 urb 来一次定义多个同步传送. 它也用来收集每个单独传送的传送状态
2. urb接口函数
2.1 申请urb
usb_alloc_urb(int iso_packets, gfp_t mem_flags);
- 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) {
err("alloc_urb: kmalloc failed");
return NULL;
}
usb_init_urb(urb);
return urb;
}
2.2 填充urb
usb_fill_bulk_urb(struct urb * urb, struct usb_device * dev,
unsigned int pipe, void * transfer_buffer, int buffer_length,
usb_complete_t complete_fn, void * context)
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_fn,
void * context)
usb_fill_int_urb(struct urb * urb, struct usb_device * dev,
unsigned int pipe, void * transfer_buffer, int buffer_length,
usb_complete_t complete_fn, void * context, int interval)
对于usb的四种不同传输方式(控制传输(control),中断传输(interrupt),批量传输(bulk),等时传输),usb驱动中已经实现control,bulk,interrupt三种传输类型传输填充函数。
其中urb为要初始化的控制类型urb,dev为USB设备,pipe为管道,setup_packet专门用于发送control的usb_contrlrequest的setup包,transfer_buffer发送缓冲区, buffer_length传输长度, complete_fn为urb递交给hcd后将会运行的回调函数,comtext为回调函数参数, 对于interrupt而言,还需设置定时查询时间间隔interval。
2.3 提交urb
经过前面的创建、初始化和填充数据后,urb需要提交给usb core,让它移交给主机控制器驱动进行处理,然后等待hcd的反馈结果,用于提交urb函数接口为:
usb_submit_urb(struct urb * urb, gfp_t mem_flags)
其中urb即为提交给hcd的urb
,mem_flags为申请urb中的hcprv,endpoint descripotr和 transfer descriptor时要用到的申请内存标志。
将urb提交给控制器后,由控制器进行处理,并通过回调函数返回urb发送结果
2.4 取消urb
如果想取消之前提交的urb,可以用usb_unlink_urb来实现:
usb_kill_urb(struct urb * urb)
2.5 销毁urb
usb_free_urb(struct urb * urb)
2.6 urb其他接口
用前面的方式提交urb或取消urb时,程序不会阻塞,属于异步方式。除了异步方式外,usb还可用同步方式来提交和取消urb。同样由于isochronous(实时传输)中发送数据包个数不确定性,驱动只实现了control,interrupt和bulk三种方式 的同步方式操作urb接口。
usb_control_msg(struct usb_device * dev, unsigned int pipe, __u8 request,
__u8 requesttype, __u16 value, __u16 index,
void * data, __u16 size, int timeout)
usb_control_msg用于发送control类数据,对于control类型,除发送正常数据外,还要发送一个setup transaction, request, requesttype指定请求包的类型和属性,data为要发送的数据,size为发送数据长度,timeout为发送超时时间。
usb_interrupt_msg(struct usb_device * usb_dev, unsigned int pipe,
void * data, int len, int * actual_length, int timeout)
usb_bulk_msg(struct usb_device * usb_dev, unsigned int pipe,
void * data, int len, int * actual_length, int timeout)
上面三个接口函数都已经将之前提到过的申请urb,填充urb和提交urb过程封装在一起,在使用时只要指定对应该数据,数据长度及超时时间就可以。在使用上面三个接口提交urb时,程序阻塞,直到超时或urb提交成功并通过回调函数返回结果并唤醒等待队列。