天天看點

USB請求塊(URB)——架構及機制

USB請求塊(URB)——架構及機制

《Linux裝置驅動開發詳解》本書全面而詳細地講解了Linux裝置驅動開發中涉及的理論以及多種裝置驅動的架構。第20章主要講解從主機側角度看到的USB主機控制器驅動和裝置驅動。本文寫的是USB請求塊(urb)。 

1.urb結構體

USB請求塊(USB request block,urb)是USB裝置驅動中用來描述與USB裝置通信所用的基本載體和核心資料結構,非常類似于網絡裝置驅動中的sk_buff結構體,是USB主機與裝置通信的“電波”。

代碼清單20.13  urb結構體

1 struct urb

2  {

3   

4   struct kref kref;

5   spinlock_t lock;

6   void *hcpriv;

7   int bandwidth;

8   atomic_t use_count;

9   u8 reject;

10

11  

12  struct list_head urb_list;

13  struct usb_device *dev;

14  unsigned int pipe;

15  int status;

16  unsigned int transfer_flags;

17  void *transfer_buffer;

18  dma_addr_t transfer_dma;

19  int transfer_buffer_length;

20                      

21  int actual_length;

22  unsigned char *setup_packet;

23  dma_addr_t setup_dma;

24  int start_frame;

25  int number_of_packets;

26  int interval;

27  int error_count; 

28  void *context;

29  usb_complete_t complete;

30  struct usb_iso_packet_descriptor iso_frame_desc[0];

31  

32 };

        當transfer_flags标志中的URB_NO_TRANSFER_DMA_MAP被置位時,USB核心将使用transfer_dma指向的緩沖區而非transfer_buffer指向的緩沖區,意味着即将傳輸DMA緩沖區。

        當transfer_flags标志中的URB_NO_SETUP_DMA_MAP被置位時,對于有DMA緩沖區的控制urb而言,USB核心将使用setup_dma指向的緩沖區而非setup_packet指向的緩沖區。

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,

                                         intbuffer_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,

                                              intbuffer_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()函數來建立。

等時urb沒有像中斷、控制和批量urb的初始化函數,我們隻能手動地初始化urb,而後才能送出給USB核心。代碼清單20.14給出了初始化等時urb的例子,它來自drivers/usb/media/usbvideo.c檔案。

代碼清單20.14  初始化等時urb 

 1  for(i = 0; i < USBVIDEO_NUMSBUF; i++)

2  {

3   int j, k;

4   struct urb *urb = uvd->sbuf[i].urb;

5   urb->dev = dev;

6   urb->context = uvd;

7   urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);

8   urb->interval = 1;

9   urb->transfer_flags = URB_ISO_ASAP;

10  urb->transfer_buffer = uvd->sbuf[i].data;

11  urb->complete = usbvideo_IsocIrq;

12  urb->number_of_packets = FRAMES_PER_DESC;

13  urb->transfer_buffer_length = uvd->iso_packet_len*FRAMES_PER_DESC;

14  for (j = k = 0; j < FRAMES_PER_DESC; j++, k +=uvd->iso_packet_len)

15  {

16    urb->iso_frame_desc[j].offset = k;

17    urb->iso_frame_desc[j].length = uvd->iso_packet_len;

18  }

19 } 

(3)被USB裝置驅動送出給USB 核心。

        在完成第(1)、(2)步的建立和初始化urb後,urb便可以送出給USB核心,通過usb_submit_urb()函數來完成,如下所示:

        int usb_submit_urb(struct urb *urb, intmem_flags);

        urb參數是指向urb的指針,mem_flags參數與傳遞給kmalloc()函數參數的意義相同,它用于告知USB核心如何在此時配置設定記憶體緩沖區。

        在送出urb到USB核心後,直到完成函數被調用之前,不要通路urb中的任何成員。

        usb_submit_urb()在原子上下文和程序上下文中都可以被調用,mem_flags變量需根據調用環境進行相應的設定,如下所示。

  1. GFP_ATOMIC:在中斷處理函數、底半部、tasklet、定時器處理函數以及urb完成函數中,在調用者持有自旋鎖或者讀寫鎖時以及當驅動将current->state修改為非 TASK_ RUNNING時,應使用此标志。
  2. GFP_NOIO:在儲存設備的塊I/O和錯誤處理路徑中,應使用此标志;
  3. 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裝置被拔出的情況下。

        usb_unlink_urb()和usb_kill_urb()這兩個函數用于取消已送出的urb,其參數為要被取消的urb指針。對usb_unlink_urb()而言,如果urb結構體中的URB_ASYNC_UNLINK(即異步unlink)的标志被置位,則對該urb的usb_unlink_urb()調用将立即傳回,具體的unlink動作将在背景進行。否則,此函數一直等到urb被解開連結或結束時才傳回。usb_kill_urb()會徹底終止urb的生命周期,它通常在裝置的disconnect()函數中被調用。

        當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核心和主機控制器處理時,被驅動程式取消的情況下才發生。

3.簡單的批量與控制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請求塊(URB)——架構及機制

圖20.5 urb處理流程

        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, __u16index, 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()函數掌握足夠的資訊,以判斷和等待該調用的結束。

轉自:http://book.51cto.com/art/200803/66930.htm

繼續閱讀