USB設備---URB請求塊


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, 
                                   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()函數來創建。

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的取消

        如果想取消之前提交的urb,可以用usb_unlink_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)

         usb_kill_urb提交取消urb申請后,會一直等待urb取消完成才會退出,里面的等待也是通過等待隊列實現的。



參考:
https://www.cnblogs.com/chd-zhangbo/p/5261045.html
http://book.51cto.com/art/200803/66930.htm
http://blog.sina.com.cn/s/blog_a336aee70102x5hk.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM