13.3. USB Urbs
linux 内核中的 USB 代码和所有的 USB 设备通讯使用称为 urb 的东西( USB request block).这个请求块用 struct urb 结构描述并且可在 include/linux/usb.h 中找到.
一个 urb 用来发送或接受数据到或者从一个特定 USB 设备上的特定的 USB 端点, 以一种异步的方式. 根据驱动的需要,一个 USB 设备驱动可能分配许多 urb 给一个端点或者可能重用单个 urb 给多个不同的端点。
一个 urb 的典型生命循环如下:
· 被一个USB 设备驱动创建.
· 安排给一个特定USB 设备的特定端点.
· 提交给USB 核心,被USB 设备驱动.
· 提交给特定设备的被USB 核心指定的USB 主机控制器驱动,.
· 被USB 主机控制器处理,它做一个USB 传送到设备.
· 当urb 完成,USB 主机控制器驱动通知USB 设备驱动.
13.3.1. struct urb
Struct urb定义在include/linux/usb.h(line1179-1215)中,对于每一个对象得具体功能在结构的头部有详细的解释:
struct urb {
/*private: usb core and host controller only fields in the urb */
structkref kref; /* reference count ofthe URB */
void*hcpriv; /* private data forhost controller */
atomic_tuse_count; /* concurrentsubmissions counter */
atomic_treject; /* submissions will fail*/
intunlinked; /* unlink errorcode */
/*public: documented fields in the urb that can be used by drivers */
structlist_head urb_list; /* list head for useby the urb's
* current owner */
structlist_head anchor_list; /* the URB maybe anchored */
structusb_anchor *anchor;
structusb_device *dev; /* (in)pointer to associated device */
structusb_host_endpoint *ep; /* (internal)pointer to endpoint */
unsignedint pipe; /* (in) pipeinformation */
unsignedint stream_id; /* (in) streamID */
intstatus; /* (return)non-ISO status */
unsignedint transfer_flags; /* (in)URB_SHORT_NOT_OK | ...*/
void*transfer_buffer; /* (in)associated data buffer */
dma_addr_ttransfer_dma; /* (in) dma addr fortransfer_buffer */
structscatterlist *sg; /* (in) scattergather buffer list */
intnum_sgs; /* (in) number ofentries in the sg list */
u32transfer_buffer_length; /* (in) databuffer length */
u32actual_length; /* (return)actual transfer length */
unsignedchar *setup_packet; /* (in) setup packet(control only) */
dma_addr_tsetup_dma; /* (in) dma addr forsetup_packet */
intstart_frame; /* (modify) startframe (ISO) */
intnumber_of_packets; /* (in)number of ISO packets */
intinterval; /* (modify)transfer interval
* (INT/ISO) */
interror_count; /* (return) numberof ISO errors */
void*context; /* (in)context for completion */
usb_complete_tcomplete; /* (in) completion routine*/
structusb_iso_packet_descriptor iso_frame_desc[0];
/*(in) ISO ONLY */
};
13.3.2. 创建和注销 Urbs
struct urb结构在驱动程序中不能够动态创建,也不能在其他结构中创建,因为这可能破坏 USB 核心给 urb 使用的引用计数方法.它必须使用usb_alloc_urb 函数创建:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
iso_packet:是urb 应当包含的同步报文的数目. 如果你不想创建一个同步 urb, 这个变量应当被设置为 0.
mem_flags:是传递给 kmalloc 函数,用来从内核分配内存的相同的标志类型
如果这个函数在分配足够内存给这个 urb 成功, 一个指向 urb 的指针被返回给调用者. 如果返回值是 NULL, 某个错误在 USB 核心中发生了, 并且驱动需要正确地清理.
为了告诉 USB 核心驱动用完这个 urb, 驱动必须调用 usb_free_urb 函数:
void usb_free_urb(struct urb *urb);
13.3.2.1 中断urbs
函数 usb_fill_int_urb 是一个辅助函数, 来正确初始化一个urb 来发送给 USB 设备的一个中断端点:
void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
intbuffer_length, usb_complete_t complete,
void*context, int interval);
13.3.2.2 批量 urbs
批量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);
13.3.2.3 控制urbs
控制 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_tcomplete, void *context);
参数定义:
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 结构的描述, 来找到这个值的正确单位.
13.3.2.4 同步 urbs
同步 urb 没有一个象中断, 控制, 和块 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;
}
13.3.3. 提交 Urbs
一旦 urb 被正确创建并且被 USB 驱动初始化, 即可提交给 USB 核心来发送出到 USB 设备. 这通过调用函数 usb_submit_urb 实现:
intusb_submit_urb(struct urb *urb, int mem_flags);
urb:是一个指向 urb 的指针, 它要被发送到设备.
mem_flags 参数等同于传递给 kmalloc 调用的同样的参数, 并且用来告诉 USB 核心如何及时分配任何内存缓冲在这个时间,可用状态如下:
GFP_ATOMIC
This valueshould be used whenever the following are true:
The caller is within a urb completion handler, an interrupt, abottom half, a tasklet, or a timer callback.
The caller is holding a spinlock or rwlock. Note that if a semaphoreis being held, this value is not necessary.
The current->state is not TASK_RUNNING. The state is alwaysTASK_RUNNING unless the driver has changed the current state itself.
GFP_NOIO
This valueshould be used if the driver is in the block I/O patch. It should also be usedin the error handling path of all storage-type devices.
GFP_KERNEL
This should beused for all other situations that do not fall into one of the previouslymentioned categories.
13.3.5. 取消 Urbs
为停止一个已经提交给 USB 核心的 urb, 应当调用函数 usb_kill_urb 或者 usb_unlink_urb:
int usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);
当函数是 usb_kill_urb, 这个 urb 的生命循环就停止了. 通常当设备从系统中断开时,在断开回调函数时使用。