1.urb 結構體
USB 請求塊(USB request block,urb)是USB 設備驅動中用來描述與USB 設備通信所用的基本載體和核心數據結構,非常類似於網絡設備驅動中的sk_buff 結構體。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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)
創建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)
對於中斷urb,使用usb_fill_int_urb()函數來初始化urb,如下所示:void usb_fill_int_urb(struct urb *urb,
對於批量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()函數來創建。
unsigned int pipe 一個管道號碼,該管道記錄了目標設備的端點以及管道的類型。每個管道只有一種類型和一個方向,它與他的目標設備的端點相對應,我們可以通過以下幾個函數來獲得管道號並設置管道類型: unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個控制OUT端點。 unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個控制IN端點。 unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個批量OUT端點。 /* 把數據從批量OUT端口發出 */ unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個批量IN端點。 unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個中斷OUT端點。 unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個中斷IN端點。 unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個等時OUT端點。 unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint) 把指定USB設備的指定端點設置為一個等時IN端點。
(3)被USB 設備驅動提交給USB 核心。(提交URB)
在完成第(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 設備驅動。
(處理URB)
在如下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 核心和主機控制器處理時,被驅動程序取消的情況下才發生。
(7). urb的取消
int usb_unlink_urb(struct urb *urb);
3.簡單的批量與控制URB
用前面的方式提交urb或取消urb時,程序不會阻塞,屬於異步方式。除了異步方式外,usb還可用同步方式來提交和取消urb。同樣由於isochronous中發送數據包個數不確定性,驅動只實現了control,interrupt和bulk三種方式 的同步方式操作urb接口。
有時USB驅動程序只是從USB設備上接收或向USB設備發送一些簡單的數據,這時候,沒有必要將urb創建、初始化、提交、完成處理的整個流程走一遍,而可以使用兩個更簡單的函數,如下所示。
(1)usb_bulk_msg()
usb_bulk_msg()函數創建一個USB批量urb 並將它發送到特定設備,這個函數是同步的,它一直等待urb完成后才返回。usb_bulk_msg()函數的原型為:
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;否則,返回1個負的錯誤值。
(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是發送超時,以jiffies為單位,0意味着永遠等待。
參數request、requesttype、value和index與USB規范中定義的USB控制消息直接對應。
如果函數調用成功,該函數返回發送到設備或從設備接收到的字節數;否則,返回一個負的錯誤值。
對usb_bulk_msg()和usb_control_msg()函數的使用要特別慎重,由於它們是同步的,因此不能在中斷上下文和持有自旋鎖的情況下使用。而且,該函數也不能被任何其他函數取消,因此,務必要使得驅動程序的disconnect()函數掌握足夠的信息,以判斷和等待該調用的結束。
(3)usb_interrupt_msg() 函數
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
上面三個接口函數都已經將之前提到過的申請urb,填充urb和提交urb過程封裝在一起,在使用時只要指定對應該數據,數據長度及超時時間就可以。在使用上面三個接口提交urb時,程序阻塞,直到超時或urb提交成功並通過回調函數返回結果並喚醒等待隊列。
在同步方式下對應的urb取消函數接口為:
void usb_kill_urb(struct urb *urb)