1.urb 結構體
USB 請求塊(USB request block,urb)是USB 設備驅動中用來描述與USB 設備通信所用的基本載體和核心數據結構,非常類似於網絡設備驅動中的sk_buff 結構體。
struct urb { /* 私有的:只能由USB 核心和主機控制器訪問的字段 */ struct kref kref; /*urb 引用計數 */ void *hcpriv; /* 主機控制器私有數據 */ atomic_t use_count; /* 並發傳輸計數 */ u8 reject; /* 傳輸將失敗*/ int unlink; /* unlink 錯誤碼 */ /* 公共的: 可以被驅動使用的字段 */ struct list_head urb_list; /* 鏈表頭*/ struct usb_anchor *anchor; struct usb_device *dev; /* 關聯的USB 設備 */ struct usb_host_endpoint *ep; unsigned int pipe; /* 管道信息 */ int status; /* URB 的當前狀態 */ unsigned int transfer_flags; /* URB_SHORT_NOT_OK | ...*/ void *transfer_buffer; /* 發送數據到設備或從設備接收數據的緩沖區 */ dma_addr_t transfer_dma; /*用來以DMA 方式向設備傳輸數據的緩沖區 */ int transfer_buffer_length;/*transfer_buffer 或transfer_dma 指向緩沖區的大小 */ int actual_length; /* URB 結束后,發送或接收數據的實際長度 */ unsigned char *setup_packet; /* 指向控制URB 的設置數據包的指針*/ dma_addr_t setup_dma; /*控制URB 的設置數據包的DMA 緩沖區*/ int start_frame; /*等時傳輸中用於設置或返回初始幀*/ int number_of_packets; /*等時傳輸中等時緩沖區數量 */ int interval; /* URB 被輪詢到的時間間隔(對中斷和等時urb 有效) */ int error_count; /* 等時傳輸錯誤數量 */ void *context; /* completion 函數上下文 */ usb_complete_t complete; /* 當URB 被完全傳輸或發生錯誤時,被調用 */ /*單個URB 一次可定義多個等時傳輸時,描述各個等時傳輸 */ struct usb_iso_packet_descriptor iso_frame_desc[0]; };
2.urb 處理流程
USB 設備中的每個端點都處理一個urb 隊列,在隊列被清空之前,一個urb 的典型生命周期
如下。
(1)被一個USB 設備驅動創建。
創建urb 結構體的函數為:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
iso_packets 是這個urb 應當包含的等時數據包的數目,若為0 表示不創建等時數據包。
mem_flags 參數是分配內存的標志,和kmalloc()函數的分配標志參數含義相同。如果分配成功,該函數返回一個urb 結構體指針,否則返回0。
urb 結構體在驅動中不能靜態創建,因為這可能破壞USB 核心給urb 使用的引用計數方法。
usb_alloc_urb()的“反函數”為:
void usb_free_urb(struct urb *urb);
該函數用於釋放由usb_alloc_urb()分配的urb 結構體。
(2)初始化,被安排給一個特定USB 設備的特定端點。
對於中斷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,void *context, int interval);
urb 參數指向要被初始化的urb 的指針;dev 指向這個urb 要被發送到的USB 設備;pipe 是這個urb 要被發送到的USB 設備的特定端點;transfer_buffer 是指向發送數據或接收數據的緩沖區的指針,和urb 一樣,它也不能是靜態緩沖區,必須使用kmalloc()來分配;buffer_length 是transfer_buffer 指針所指向緩沖區的大小;complete 指針指向當這個 urb 完成時被調用的完成處理函數;context 是完成處理函數的“上下文”;interval 是這個urb 應當被調度的間隔。
上述函數參數中的pipe 使用usb_sndintpipe()或usb_rcvintpipe()創建。
對於批量urb,使用usb_fill_bulk_urb()函數來初始化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);
除了沒有對應於調度間隔的interval 參數以外,該函數的參數和usb_fill_int_urb()函數的參數含義相同。
上述函數參數中的pipe 使用usb_sndbulkpipe()或者usb_rcvbulkpipe()函數來創建。
對於控制 urb,使用usb_fill_control_urb()函數來初始化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);
除了增加了新的setup_packet 參數以外,該函數的參數和usb_fill_bulk_urb()函數的參數含義相同。setup_packet 參數指向即將被發送到端點的設置數據包。
上述函數參數中的pipe 使用usb_sndctrlpipe()或usb_rcvictrlpipe()函數來創建。
(3)被USB 設備驅動提交給USB 核心。
在完成第(1)、(2)步的創建和初始化urb 后,urb 便可以提交給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 變量需根據調用環
境進行相應的設置,如下所示。
GFP_ATOMIC:在中斷處理函數、底半部、tasklet、定時器處理函數以及urb 完成函數中,在調用者持有自旋鎖或者讀寫鎖時以及當驅動將current→state 修改為非 TASK_RUNNING 時,應使用此標志。
GFP_NOIO:在存儲設備的塊I/O 和錯誤處理路徑中,應使用此標志;
GFP_KERNEL:如果沒有任何理由使用GFP_ATOMIC 和GFP_NOIO,就使用GFP_KERNEL。
如果usb_submit_urb()調用成功,即urb 的控制權被移交給USB 核心,該函數返回0;否則,
返回錯誤號。
(4)提交由USB 核心指定的USB 主機控制器驅動。
(5)被USB 主機控制器處理,進行一次到USB 設備的傳送。
第(4)~(5)步由USB 核心和主機控制器完成,不受USB 設備驅動的控制。
(6)當urb 完成,USB 主機控制器驅動通知USB 設備驅動。
在如下3 種情況下,urb 將結束,urb 完成函數將被調用。
1、urb 被成功發送給設備,並且設備返回正確的確認。如果urb→status 為0,意味着對於一個輸出urb,數據被成功發送;對於一個輸入urb,請求的數據被成功收到。
2、如果發送數據到設備或從設備接收數據時發生了錯誤,urb→status 將記錄錯誤值。
3、urb 被從USB 核心“去除連接”,這發生在驅動通過usb_unlink_urb()或usb_kill_urb()函數取消urb,或urb 雖已提交,而USB 設備被拔出的情況下。
當urb 生命結束時(處理完成或被解除鏈接),通過urb 結構體的status 成員可以獲知其原因,
如0 表示傳輸成功,-ENOENT 表示被usb_kill_urb()殺死,-ECONNRESET 表示被usb_unlink_urb()
殺死,-EPROTO 表示傳輸中發生了bitstuff 錯誤或者硬件未能及時收到響應數據包,-ENODEV
表示USB 設備已被移除,-EXDEV 表示等時傳輸僅完成了一部分等。
對以上urb 的處理步驟進行一個總結,圖20.5 給出了一個urb 的整個處理流程,虛線框的usb_unlink_urb()和usb_kill_urb()並非一定會發生,它只是在urb 正在被USB 核心和主機控制器處理時,被驅動程序取消的情況下才發生。