1. 程式人生 > >linux usb驅動中的urb詳解

linux usb驅動中的urb詳解

linux 核心中的 USB 程式碼和所有的 USB 裝置通訊使用稱為 urb 的東西( USB request block). 這個請求塊用 struct urb 結構描述並且可在 include/linux/usb.h 中找到. 

    一個urb 用來發送或接受資料到或者從一個特定 USB 裝置上的特定的 USB 端點, 以一種非同步的方式.一個 USB 裝置驅動可能分配許多 urb 給一個端點或者可能重用單個 urb 給多個不同的端點, 根據驅動的需要. 裝置中的每個端點都處理一個 urb 佇列, 以至於多個 urb 可被髮送到相同的端點, 在佇列清空之前. 一個 urb 的典型生命迴圈如下:

  • 被一個 USB 裝置驅動建立.

  • 安排給一個特定 USB 裝置的特定端點.

  • 提交給 USB 核心, 被 USB 裝置驅動.

  • 提交給特定裝置的被 USB 核心指定的 USB 主機控制器驅動, .

  • 被 USB 主機控制器處理, 它做一個 USB 傳送到裝置.

  • 當 urb 完成, USB 主機控制器驅動通知 USB 裝置驅動.

    urb 也可被提交這個 urb 的驅動在任何時間取消, 或者被 USB 核心如果裝置被從系統中移出. urb 被動態建立並且包含一個內部引用計數, 使它們在這個 urb 的最後一個使用者釋放它時被自動釋放.

1、URB結構體分析    

    struct urb程式碼如下:

複製程式碼
 1 struct urb {
 2     
 3     struct kref kref;        
 4     void *hcpriv;            
 5     atomic_t use_count;        
 6     atomic_t reject;        
 7     int unlinked;            
 8 
 9     
10     struct list_head urb_list;    
12     struct list_head anchor_list;    
13
struct usb_anchor *anchor; 14 struct usb_device *dev; 15 struct usb_host_endpoint *ep; 16 unsigned int pipe; 17 int status; 18 unsigned int transfer_flags; 19 void *transfer_buffer; 20 dma_addr_t transfer_dma; 21 struct usb_sg_request *sg; 22 int num_sgs; 23 u32 transfer_buffer_length; 24 u32 actual_length; 25 unsigned char *setup_packet; 26 dma_addr_t setup_dma; 27 int start_frame; 28 int number_of_packets; 29 int interval; 31 int error_count; 32 void *context; 33 usb_complete_t complete; 34 struct usb_iso_packet_descriptor iso_frame_desc[0]; 35 36 };
複製程式碼
14     struct usb_device *dev;     

    指向這個urb要傳送的struct usb_device 的指標,在urb傳送到usb核心之前,這個變數必須被usb驅動初始化。 

16     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 裝置
複製程式碼
18     unsigned int transfer_flags;    
這個變數可被設定為不同的位值,根據這個位,usb驅動可以設定urb傳輸的狀態,具體可用值詳見ldd3.
19     void *transfer_buffer;        
指向緩衝區的指標,這個指標可以是OUT urb 或者是In urb。主機控制器為了正確使用這個緩衝區,必須使用kmalloc呼叫來建立它,而不是堆疊或者靜態資料區。對於控制端點,這個緩衝是給傳送的資料。
20     dma_addr_t transfer_dma;    
用來使用DMA傳送資料到usb裝置的緩衝。
23     u32 transfer_buffer_length;    
緩衝區的長度,由於urb只會使用buffer或者dma其中一種來傳輸資料,所以這個長度可以被它們共用。如果這是0,表示沒有傳送緩衝被usb核心所使用。
25     unsigned char *setup_packet;    
指向一個控制urb的setup報文的指標,在位於正常傳送緩衝的資料之前被傳送,這個變數只對控制urb有效。
26     dma_addr_t setup_dma;        
給控制 urb 的 setupt 報文的 DMA 緩衝. 在位於正常傳送緩衝的資料之前被傳送. 這個變數只對控制 urb 有效.
24     u32 actual_length;        
當urb完成處理後,這個變數被設定為資料的真實長度,或者由這個urb(OUT)傳送,或者由這個urb(IN)接受。對於IN urb,這個值必須被用來替代taansfer_buffer_length,因為接收的資料可能比整個緩衝區的大小小。
17     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建立和銷燬

struct urb 結構在驅動中必須不能被靜態建立, 或者在另一個結構中, 因為這可能破壞 USB 核心給 urb 使用的引用計數方法. 它必須使用對 usb_alloc_urb 函式的呼叫而被建立. 這個函式有這個原型:

struct urb *usb_alloc_urb(int iso_packets, int mem_flags);

第一個引數, iso_packet, 是這個 urb 應當包含的同步報文的數目. 如果你不想建立一個同步 urb, 這個變數應當被設定為 0. 第 2 個引數, mem_flags, 是和傳遞給 kmalloc 函式呼叫來從核心分配記憶體的相同的標誌型別(見"flags 引數"一節, 第 8 章, 關於這些標誌的細節). 如果這個函式在分配足夠記憶體給這個 urb 成功, 一個指向 urb 的指標被返回給呼叫者. 如果返回值是 NULL, 某個錯誤在 USB 核心中發生了, 並且驅動需要正確地清理.

在建立了一個 urb 之後, 它必須被正確初始化在它可被 USB 核心使用之前. 如何初始化不同型別 urb 見下一節

為了告訴 USB 核心驅動用完這個 urb, 驅動必須呼叫 usb_free_urb 函式. 這個函式只有一個引數:

void usb_free_urb(struct urb *urb);

引數是一個指向你要釋放的 struct urb 的指標. 在這個函式被呼叫之後, urb 結構消失, 驅動不能再存取它.

   3、中斷 urb

函式 usb_fill_int_urb 是一個幫忙函式, 來正確初始化一個urb 來發送給 USB 裝置的一箇中斷端點:

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);

這個函式包含許多引數:

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 結構的描述, 來找到這個值的正確單位.

4、塊urb

塊 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);

這個函式引數和 usb_fill_int_urb 函式的都相同. 但是, 沒有 interval 引數因為 bulk urb 沒有間隔值. 請注意這個 unsiged int pipe 變數必須被初始化用對 usb_sndbulkpipe 或者 usb_rcvbulkpipe 函式的呼叫.

usb_fill_int_urb 函式不設定 urb 中的 transfer_flags 變數, 因此任何對這個成員的修改不得不由這個驅動自己完成.

5、控制urb

控制 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_t complete, void *context);

函式引數和 usb_fill_bulk_urb 函式都相同, 除了有個新引數, unsigned char *setup_packet, 它必須指向要傳送給端點的 setup 報文資料. 還有, unsigned int pipe 變數必須被初始化, 使用對 usb_sndctrlpipe 或者 usb_rcvictrlpipe 函式的呼叫.

usb_fill_control_urb 函式不設定 transfer_flags 變數在 urb 中, 因此任何對這個成員的修改必須遊驅動自己完成. 大部分驅動不使用這個函式, 因為使用在"USB 傳送不用 urb"一節中介紹的同步 API 呼叫更簡單.

6、同步urb
不幸的是, 同步 urb 沒有一個象中斷, 控制, 和塊 urb 的初始化函式. 因此它們必須在驅動中"手動"初始化, 在它們可被提交給 USB 核心之前. 下面是一個如何正確初始化這類 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;
}
複製程式碼

  7、提交 urb

一旦 urb 被正確地建立,並且被 USB 驅動初始化, 它已準備好被提交給 USB 核心來發送出到 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 變數的指定必須正確.根據 usb_submit_urb 被呼叫的時間,只有 3 個有效值可用:

GFP_ATOMIC 只要滿足以下條件,就應當使用此值: 1.呼叫者處於一個 urb 結束處理例程,中斷處理例程,底半部,tasklet或者一個定時器回撥函式. 2.呼叫者持有自旋鎖或者讀寫鎖. 注意如果正持有一個訊號量, 這個值不必要. 3.current->state 不是 TASK_RUNNING. 除非驅動已自己改變 current 狀態,否則狀態應該一直是 TASK_RUNNING . GFP_NOIO     驅動處於塊 I/O 處理過程中. 它還應當用在所有的儲存型別的錯誤處理過程中. GFP_KERNEL     所有不屬於之前提到的其他情況

  8、完成 urb: 完成回撥處理者

如果對 usb_submit_urb 的呼叫成功, 驅動將傳遞對 urb 的控制給 USB 核心, 這個函式返回 0; 否則, 一個負錯誤值被返回. 如果函式呼叫成功, urb 的完成處理者(就是complete指定的的函式)被確切地呼叫一次, 當 urb 被完成. 當這個函式被呼叫, USB 核心完成這個 urb, 並且對它的控制現在返回給裝置驅動.

只有 3 個方法, 一個urb 可被結束並且使完成函式被呼叫:

  • urb 被成功傳送給裝置, 並且裝置返回正確的確認. 對於一個 OUT urb, 資料被成功傳送, 對於一個 IN urb, 請求的資料被成功收到. 如果發生這個, urb 中的狀態變數被設定為 0.

  • 一些錯誤連續發生, 當傳送或者接受資料從裝置中. 被 urb 結構中的 status 變數中的錯誤值所記錄.

  • 這個 urb 被從 USB 核心去鏈. 這發生在要麼當驅動告知 USB 核心取消一個已提交的 urb 通過呼叫 usb_unlink_urb 或者 usb_kill_urb, 要麼當裝置從系統中去除, 以及一個 urb 已經被提交給它

9、取消 urb

為停止一個已經提交給 USB 核心的 urb, 函式 usb_kill_urb 或者 usb_unlink_urb 應當被呼叫:

int usb_kill_urb(struct urb *urb); 
int usb_unlink_urb(struct urb *urb);

The urb parameter for both of these functions is a pointer to the urb that is to be canceled.

當函式是 usb_kill_urb, 這個 urb 的生命迴圈就停止了. 這個函式常常在裝置從系統去除時被使用, 在去連接回調中.

對一些驅動, 應當用 usb_unlink_urb 函式來告知 USB 核心去停止 urb. 這個函式在返回到呼叫者之前不等待這個 urb 完全停止. 這對於在中斷處理或者持有一個自旋鎖時停止 urb 時是有用的, 因為等待一個 urb 完全停止需要 USB 核心有能力使呼叫程序睡眠. 為了正確工作這個函式要求 URB_ASYNC_UNLINK 標誌值被設定在正被要求停止的 urb 中.

至此,關於urb的結構,功能,提交和取消全部介紹完畢,對於一般的USB驅動,應該是足夠用了,本文大多數內容引用自ldd3,有少部分我做了修改,方便自己查閱,同時分享自己的學習心得。