1 USB請求塊 USB請求塊是USB設備驅動中用來描述與USB設備通信所用的基本載體和核心數據結構。 2 URB處理流程 USB設備中的每個端點都處理一個URB隊列,在隊列被清空之前,一個URB的典型生命周期如下: (1)使用usb_alloc_urb來分配一個URB。 函數原形 struct u ...
1 USB請求塊
USB請求塊是USB設備驅動中用來描述與USB設備通信所用的基本載體和核心數據結構。
1 /* include/linux/usb.h */ 2 struct urb { 3 ... 4 /* public: documented fields in the urb that can be used by drivers */ 5 struct list_head urb_list; /* list head for use by the urb's 6 * current owner */ 7 ... 8 struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */ 9 unsigned int pipe; /* (in) pipe information */ 10 unsigned int stream_id; /* (in) stream ID */ 11 int status; /* (return) non-ISO status */ 12 unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ 13 void *transfer_buffer; /* (in) associated data buffer */ 14 dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ 15 struct scatterlist *sg; /* (in) scatter gather buffer list */ 16 int num_mapped_sgs; /* (internal) mapped sg entries */ 17 int num_sgs; /* (in) number of entries in the sg list */ 18 u32 transfer_buffer_length; /* (in) data buffer length */ 19 u32 actual_length; /* (return) actual transfer length */ 20 unsigned char *setup_packet; /* (in) setup packet (control only) */ 21 dma_addr_t setup_dma; /* (in) dma addr for setup_packet */ 22 int start_frame; /* (modify) start frame (ISO) */ 23 int number_of_packets; /* (in) number of ISO packets */ 24 int interval; /* (modify) transfer interval 25 * (INT/ISO) */ 26 int error_count; /* (return) number of ISO errors */ 27 void *context; /* (in) context for completion */ 28 usb_complete_t complete; /* (in) completion routine */ 29 struct usb_iso_packet_descriptor iso_frame_desc[0]; 30 /* (in) ISO ONLY */ 31 };
2 URB處理流程
USB設備中的每個端點都處理一個URB隊列,在隊列被清空之前,一個URB的典型生命周期如下:
(1)使用usb_alloc_urb來分配一個URB。
函數原形 |
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags); |
函數參數 |
iso_packets:URB應當包含的等時數據包的數目,若為0表示不創建等時數據報 |
mem_flags:分配記憶體的標誌 |
|
返回值 |
成功:返回URB結構體指針;失敗:返回NULL |
URB結構體在驅動中不宜靜態創建,因為這可能破壞USB核心給URB使用的引用計數方法。
釋放由urb_alloc_urb()分配的URB結構體的函數:
函數原形 |
void usb_free_urb(struct urb *urb); |
(2)根據傳輸的類型填充URB。等時傳輸沒有相應的函數,需要手動來實現。
- 對於中斷URB,使用usb_fill_int_urb()函數來初始化URB:
函數原形 |
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_fn, void *context, int interval); |
函數參數 |
urb:指向要被初始化的URB指針 |
dev:指向這個URB要被髮送到的USB設備 |
|
pipe:是這個URB要被髮送到的USB設備的特定端點 |
|
transfer_buffer:是指向發送數據或接收數據的緩衝區的指針,必須使用kmalloc()來分配 |
|
buffer_length:是transfer_buffer指針所指向緩衝區的大小 |
|
complete_fn:指向當這個URB完成時被調用的完成處理函數 |
|
context:是完成處理函數的“上下文” |
|
interval:是這個URB應當被調度的間隔 |
上述函數參數的pipe使用usb_sndintpipe()或usb_rcvintpipe()創建。
- 對於批量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_fn, void *context); |
函數參數 |
參數和usb_fill_int_urb()相同 |
上述函數參數的pipe使用usb_sndbulkpipe()或usb_rcvbulkpipe()創建。
- 對於控制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_fn, void *context); |
函數參數 |
參數和usb_fill_bulk_urb()相同 |
setup_packet:指向即將被髮送到端點的設置數據包 |
上述函數參數的pipe使用usb_sndctrlpipe()或usb_rcvictrlpipe()創建。
- 對於等時URB沒有相應的初始化函數,只能手動對它初始化,而後才能提交給USB核心。
下麵是初始化等時URB的例子:
1 /* drivers/media/usb/uvc/uvc_video.c */ 2 for (i = 0; i < UVC_URBS; ++i) { 3 urb = usb_alloc_urb(npackets, gfp_flags); 4 if (urb == NULL) { 5 uvc_uninit_video(stream, 1); 6 return -ENOMEM; 7 } 8 9 urb->dev = stream->dev->udev; 10 urb->context = stream; 11 urb->pipe = usb_rcvisocpipe(stream->dev->udev, 12 ep->desc.bEndpointAddress); 13 #ifndef CONFIG_DMA_NONCOHERENT 14 urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; 15 urb->transfer_dma = stream->urb_dma[i]; 16 #else 17 urb->transfer_flags = URB_ISO_ASAP; 18 #endif 19 urb->interval = ep->desc.bInterval; 20 urb->transfer_buffer = stream->urb_buffer[i]; 21 urb->complete = uvc_video_complete; 22 urb->number_of_packets = npackets; 23 urb->transfer_buffer_length = size; 24 25 for (j = 0; j < npackets; ++j) { 26 urb->iso_frame_desc[j].offset = j * psize; 27 urb->iso_frame_desc[j].length = psize; 28 } 29 30 stream->urb[i] = urb; 31 }
(3)使用usb_submit_urb來提交一個URB來完成傳輸。(提交給URB核心)
在完成(1)、(2)步的創建和初始化URB後,URB便可以提交給USB核心了,可通過usb_submit_urb()函數來完成:
函數原形 |
int usb_submit_urb(struct urb *urb, gfp_t mem_flags); |
函數參數 |
urb:指向URB的指針 |
mem_flags:用於告知USB核心如何在此時分配記憶體緩衝區,與kmalloc()參數一致,通常為GPF |
|
返回值 |
成功:返回0;失敗:返回錯誤碼 |
usb_submit_urb()在原子上下文和進程上下文都可以被調用,mem_flags變數需根據調用環境進行相應的設置,如下所示:
- GFP_ATOMIC:在中斷處理函數、底半部、tasklet、定時器處理函數以及URB完成函數中,在調用者持有自旋鎖或者讀寫鎖時以及當驅動將current->state修改為非TASK_RUNNING時,應使用此標誌。
- GFP_NOIO:在存儲設備的塊I/O和錯誤處理路徑中,應使用此標誌。
- GFP_KERNEL:如果沒有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_KERNEL。
(4)提交由USB核心指定的USB主機控制器驅動。
(5)被USB主機控制器處理,進行一次到USB設備的傳送。
【溫馨提示】第(4)、(5)步由USB核心和主機控制器完成,不受USB設備驅動的控制。
(6)當URB完成,USB主機控制器驅動通知USB設備驅動。
在如下3種情況下,URB將結束,URB完成回調函數將被調用(完成回調是通過usb_fill_xxx_urb的參數傳入的)。在完成回調中,通常要進行urb->status判斷。
- URB被成功發送給設備,並且設備返回正確的確認。如果urb->status為0,意味著對於一個輸出URB,數據被成功發送;對於一個輸入URB,請求的數據被成功收到。
- 如果發送數據到設備或從設備接收數據時發送了錯誤,urb->status將記錄錯誤碼。
- URB被從USB核心“去除連接”,這發生在驅動通過usb->unlink_urb()或usb_kill_urb()函數取消或URB雖已提交而USB設備被拔出的情況下。
下麵函數用於取消已提交的URB:
函數原形 |
int usb_kill_urb(struct urb *urb); int usb_unlink_urb(struct urb *urb); |
函數參數 |
urb:要取消的URB |
【溫馨提示】usb_unlink_urb()是非同步的,搞定後對應的完成回調會被調用;而usb_kill_urb()會徹底終止URB的生命周期並等待這一行為,它通常在設備的disconnect()函數中被調用。
當URB生命結束時(處理完成或被解除鏈接),在URB的完成回調中通過URB結構體的status成員可以獲知其原因:
- 0:表示傳輸成功;
- -ENOENT:表示被usb_kill_urb()殺死;
- -ECONNRESET:表示被usb_unlink_urb()殺死;
- -EPROTO:表示傳輸中發生了bitstuff錯誤或者硬體未能及時收到響應數據包;
- -ENODEV:表示USB設備已被移除;
- -EXDEV:表示等時傳輸僅完成一部分等。
3 簡單的批量與控制URB
有時USB驅動程式只是從USB設備上接收或向USB設備發送一些簡單的數據,這時候,沒有必要將URB創建、初始化、提交、完成處理的整個流程走一遍,而可以使用更簡單的函數。
(1)usb_bulk_msg()
usb_bulk_msg()函數創建一個USB批量URB並將它們發送到特定的設備,這個函數是同步的,它一直等待URB完成後才返回。
函數原形 |
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout); |
函數參數 |
usb_dev:指向批量消息要發送的USB的設備指針 |
pipe:為批量消息要發送到的USB設備的端點 |
|
data:指向要發送或接收的數據緩衝區的指針 |
|
len:為data參數所指向的緩衝區的長度 |
|
actual_length:用於返回實際發送或接收的位元組數 |
|
timeout:發送超時,以jiffies為單位,0意味著永遠等待 |
|
返回值 |
成功:返回0;失敗:返回錯誤碼 |
(2)usb_control_msg()
usb_control_msg()函數與usb_bulk_msg()函數類似,不過它提供給驅動發送和結束USB控制信息而不是批量信息。
函數原形 |
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout); |
函數參數 |
dev:指向控制消息發往的USB設備 |
pipe:是控制消息要發往的USB設備的端點 |
|
request:是這個控制消息的USB請求值 |
|
requesttype:是這個控制消息的USB請求類型 |
|
value:是這個控制消息的USB消息值 |
|
index:是這個控制消息的USB消息索引值 |
|
data:指向要發送或接收的數據緩衝區 |
|
size:是data參數所指向的緩衝區的大小 |
|
timeout:是發送超時,以毫秒為單位,0意味著永遠等待 |
|
返回值 |
成功:返回發送到設備或從設備接收到的位元組數;失敗:返回錯誤碼 |
【溫馨提示】對usb_bulk_msg()和usb_control_msg()函數的使用要特別要慎重,usb_bulk_msg()和usb_control_msg()是同步的,因此不能在中斷上下文和持有自旋鎖的情況下使用。而且,該函數也不能被任何其他函數取消,因此,務必要使得驅動程式的disconnect()函數掌握足夠的信息,以判斷和等待該調用的結束。