乾坤合一~Linux設備驅動之USB主機和設備驅動

  • 若是不能陪你到最後
  • 是否後悔當初咱們牽手
  • 若是當初沒能碰見你
  • 如今的我
  • 在哪裏逗留
  •  
  • 全部的愛都是冒險
  • 那就心甘情願
  • 等待咱們一輩子中 全部懸念
  •  
  • 我一往情深的戀人
  • 她是個人愛人
  • 她給個人愛就像是
  • 帶着露水的清晨
  • 我多想給她個人真
  • 我心疼的愛人
  • 我願爲她守候寂寞
  • 就像這夜晚 深沉

  這一章從主機側角度看到的USB 主機控制器驅動和設備驅動從主機側的角度而言,須要編寫的USB 驅動程序包括主機控制器驅動和設備驅動兩類,USB 主機控制器驅動程序控制插入其中的USB 設備,而USB 設備驅動程序控制該設備如何做爲從設備與主機通訊。html

1. Linux USB驅動層次網絡

1.1 主機側與設備側USB 驅動數據結構

  USB 採用樹形拓撲結構,每條總線上只有一個主機控制器,負責協調主機和設備間的通訊,而設備不能主動向主機發送任何消息。併發

1.2 設備、配置、口、函數

  在USB 設備的邏輯組織中,包含設備、配置、接口和端點4 個層次,每一個USB 設備都提供了不一樣級別的配置信息,能夠包含一個或多個配置,不一樣的配置使設備表現出不一樣的功能組合,配置由多個接口組成,接口由多個端點組成,表明一個基本的功能,是USB 設備驅動程序控制的對象,以下圖是USB 設備、配置、接口和端點之間的關係。ui

  設備描述符:關於設備的通用信息,如供應商ID 、產品ID 和修訂ID,支持的設備類、子類和適用的協議以及默認端點的最大包大小等。在Linux 內核中,USB 設備用 usb_device 結構體來描述,USB 設備描述符定義爲usb_device_descriptor 結構體,其代碼以下:編碼

struct usb device descriptor 

  { 

      _ _u8 bLength; // 述符長度 

      _ _u8 bDescriptorType; // 述符類型編號 
   
      _ _le16 bcdUSB; //USB版本號 

      _ _u8 bDeviceClass; //USB分配的設備類code 

      _ _u8 bDeviceSubClass;// USB 分配的子類code 

      _ _u8 bDeviceProtocol; //USB 分配的協議code 

      _ _u8 bMaxPacketSize0; //endpoint0 最大包大小 
 
      _ _le16 idVendor; //廠商編號 
   
      _ _le16 idProduct; //產品編號 

      _ _le16 bcdDevice; //設備出廠編號 
    
      _ _u8  iManufacturer; // 述廠商字符串的索引 
   
      _ _u8  iProduct; // 述產品字符串的索引 
   
      _ _u8  iSerialNumber; // 述設備序列號字符串的索引 

      _ _u8 bNumConfigurations; //可能的配置數量 

  } _ _attribute_ _ ((packed)); 

  配置描述符:此配置中的接口數、支持的掛起和恢復能力以及功率要求。USB配置在內核中使用usb_host_config 結構體描述,USB 配置描述符定義爲結構體usb_config_descriptor,其代碼以下:atom

struct usb config descriptor 

 { 

    _ _u8 bLength; // 述符長度 

    _ _u8 bDescriptorType; // 述符類型編號 

    _ _le16 wTotalLength; //配置所返回的全部數據的大小 

    _ _u8 bNumInterfaces; // 配置所支持的接口數 

    _ _u8 bConfigurationValue; //Set Configuration命令須要的參數值 

    _ _u8  iConfiguration; // 述該配置的字符串的索引值 

    _ _ u8 bmAttributes; //供電模式的選擇 
 
    _ _u8 bMaxPower; //設備從總線提取的最大電流 

 } _ _attribute_ _ ((packed)); 

   接口描述符:接口類、子類和適用的協議,接口備用配置的數目和端點數目。USB接口在內核中使用 usb_interface 結構體描述,USB 接口描述符定義爲結構體 usb_interface_descriptor,其代碼以下:spa

struct usb interface descriptor 

      { 

       _ _u8 bLength;            // 述符長度 

       _ _u8 bDescriptorType; // 述符類型 

       _ _ u8 bInterfaceNumber;     // 接口的編號 
       _ _u8 bAlternateSetting; //備用的接口 述符編號 
       _ _u8 bNumEndpoints;        //該接口使用的端點數,不包括端點0 
         
        _ _ u8 bInterfaceClass;      //接口類型 
         
        _ _ u8 bInterfaceSubClass; //接口子類型 
        
        _ _ u8 bInterfaceProtocol; //接口所遵循的協議 
 
        _ _ u8  iInterface; // 述該接口的字符串索引值 
     } _ _attribute_ _ ((packed)); 

  端點描述符:端點地址、方向和類型,支持的最大包大小,若是是中斷類型的端點則還包括輪詢頻率。在 Linux 內核中,U SB 端點使用 usb_host_endpoint 結構體來描述,USB端點描述符定義爲 usb_ endpoint_ descriptor 結構體,其代碼以下:線程

struct usb endpoint descriptor 
      { 
       _ _u8 bLength; // 述符長度 
       _ _u8 bDescriptorType; // 述符類型 
       _ _u8  bEndpointAddress; //端點地址:0 ~3 位是端點號,第 7 位是方向 (0-OUT,1-IN) 
       
       _ _ u8 bmAttributes; //端點屬性:bit[0:1] 的值爲00表示控制,爲01表示同步,爲02表示批量,爲03表示中 
         
       _ _le16 wMaxPacketSize; //// 本端點接收或發送的最大信息包的大小 
        
       _ _u8 bInterval;      //輪詢數據傳送端點的時間間隔 
                             //對於批量傳送的端點以及控制傳送的端點,此域忽略 
                            //對於同步傳送的端點,此域必須爲1 
                            //對於中 傳送的端點,此域值的範圍爲1~255 
        
       _ _u8 bRefresh; 
       _ _u8 bSynchAddress; 
     } _ _attribute_ _ ((packed)); 

  字符串描述符:在其餘描述符中會爲某些字段提供字符串索引,它們被用來檢索描述性字符串,能夠以多種語言形式提供。字符串描述符是可選的,有的設備有,有的設備沒有 ,字符 串描述符對應於usb_string_ descriptor 結構體,其代碼以下:

struct usb string descriptor 
      { 
        _ _u8 bLength; // 述符長度 
        _ _u8 bDescriptorType; // 述符類型 
        _ _le16 wData [1];        /* 以UTF-16LE編碼 */ 
      } _ _attribute_ _ ((packed)); 

2 USB主機驅動

  USB 主機控制器有 3 種規格:OHCI (Open Host Controller Interface) 、UHCI (Universal Host Controller Interface) 和EHCI (Enhanced Host Controller Interface) 。

2.1 主機控制器驅動

  在Linux 內核中,用usb_hcd 結構體描述USB 主機控制器驅動,它包含USB 主機控制器的 「家務」信息、硬件資源、狀態描述和用於操做主機控制器的 hc_driver等,其代碼以下:

struct usb hcd 

      { 

        /* 管理 「家務」 */ 

        struct usb bus self; 
 
        const char *product desc; /* 產品/廠商字符串 */ 

        char irq descr [24]; /* 驅動 + 總線 # */ 
 
         struct timer list rh timer; /* 根Hub 輪詢 */ 
                         
         struct urb *status urb; /* 目前的狀態urb */ 
      
        /* 硬件信息/狀態 */ 
                          
        const struct hc driver *driver; /* 硬件特定的鉤子函數 */ 
      
        /* 須要維護的標誌 */ 
        unsigned long flags; 
                        
        #define HCD FLAG HW ACCESSIBLE  0x00000001 
                       
        #define HCD FLAG SAW IRQ            0x00000002 
      
        unsigned rh_registered: 1; /* 根Hub 註冊? */ 
      
        /* 下一個標誌的採用只是 「權益之計」,當全部HCDs 支持新的根Hub 輪詢機制後將移除 */ 
                
        unsigned uses new polling: 1; 
                        
        unsigned poll rh: 1; /* 輪詢根Hub 狀態? */ 
                         
        unsigned poll pending: 1; /* 狀態已經改變? */ 
      
        int irq; /* 被分配的irq */ 
void _ _iomem *regs; /* 設備內存和I/O */ u64 rsrc start; /* 內存和I/O資源開始位置 */ u64 rsrc len; /* 內存和I/O資源長度 */ unsigned power budget; /* mA, 0 = 無限制 */ #define HCD BUFFER POOLS 4 struct dma pool *pool[HCD BUFFER POOLS]; int state; #define ACTIVE 0x01 #define SUSPEND 0x04 #define TRANSIENT 0x80 #define HC STATE HALT 0 #define HC STATE RUNNING ( ACTIVE) #define HC STATE QUIESCING ( SUSPEND| TRANSIENT| ACTIVE) #define HC STATE RESUMING ( SUSPEND| TRANSIENT) #define HC STATE SUSPENDED ( SUSPEND) #define HC IS RUNNING (state) ((state) & ACTIVE) #define HC IS SUSPENDED(state) ((state) & SUSPEND) /* 主機控制器驅動的私有數據 */
     unsigned long hcd priv [0] attribute ((aligned (sizeof(unsigned long)))); };

  Linux中採用如下函數建立HCD:

struct usb hcd *usb create hcd (const struct hc driver *driver, 
                                         
struct device *dev, char *bus name); 

    如下函數用來增長和移除:

int usb add hcd (struct usb hcd *hcd, 
unsigned int irqnum, unsigned long irqflags); 
void usb remove hcd (struct usb hcd *

2.2 OHCI 主機控制器驅動

  OHCI HCD 驅動屬於HCD 驅動的實例,它定義了一個ohci_hcd 結構體,使用以下內聯函數可實現usb_hcd 和ohci_hcd 的相互轉換:

struct ohci hcd *hcd to ohci (struct usb hcd *hcd); 
  
struct usb hcd *ohci to hcd (const struct ohci hcd *ohci); 

  從usb_hcd 獲得ohci_hcd 只是取得「私有」數據,而從ohci_hcd 獲得usb_hcd 則是經過container_of()從結構體成員得到結構體指,使用以下函數可初始化OHCI 主機控制器: 

int ohci init (struct ohci hcd *ohci); 

以下函數分別用於開啓、中止及復位OHCI 控制器:

int ohci run (struct ohci hcd *ohci); 

void ohci stop (struct usb hcd *hcd); 

void ohci usb reset (struct ohci hcd *ohci);

3 USB設備驅動

3.1 USB設備驅動總體結構

有如下設備類

  • 音頻設備類。
  • 通訊設備類。
  • HID (人機接口)設備類。
  • 顯示設備類。
  • 海量存儲設備類。
  • 電源設備類。
  • 打印設備類。 
  • 集線器設備類。

  Linux 內核爲各種USB 設備分配了相應的設備號,內核中提供了USB 設備文件系統 (usbdevfs,Linux 2.6 改成usbfs,即USB 文件系統),它和/proc 相似,都是動態產生的。經過在/etc/fstab 文件中添加以下一行:

none /proc/bus/usb usbfs defaults 

或者輸入命令:

mount -t usbfs none /proc/bus/usb

能夠實現USB 設備文件系統的掛載。

  此外,在sysfs 文件系統中,一樣包含了USB 相關信息的描述,但只限於接口級別。USB 設備和USB 接口在sysfs 中均表示爲單獨的USB 設備,其目錄命名規則以下:

根集線器-集線器端口號 (-集線器端口號-...):配置.接口。

3.2 USB請求塊(URB)

  USB 請求塊 (USB request block,urb )是USB 設備驅動中用來描述與USB 設備通訊所用的基本載體和核心數據結構,很是相似於網絡設備驅動中的sk_buff 結構體,是USB 主機與設備通訊的 「電波」,urb 結構體,代碼以下:

struct urb 

  { 

        /* 私有的:只能由USB核心和主機控制器訪問的字段 */ 
        struct kref kref; /*urb 引用計數 */ 
        
        spinlock t lock; /* urb鎖 */ 
        void *hcpriv; /* 主機控制器私有數據 */ 
        int bandwidth; /* INT/ISO請求的帶寬 */ 
             
        atomic t use count; /* 併發傳輸計數 */ 
        u8 reject; /* 傳輸將失敗*/ 

       /* 公共的: 能夠被驅動使用的字段 */ 
               
       struct list head urb list; /* 鏈表頭*/ 
               
       struct usb device *dev; /* 關聯的USB 設備 */ 
       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 被徹底傳輸或發生錯誤時,被調用 */

     struct usb iso packet descriptor iso frame desc[0];
     /*單個URB 一次可定義多個等時傳輸時,描述各個等時傳輸 */

   };

  USB 設備中的每一個端點都處理一個urb 隊列,在隊列被清空以前,一個urb 的典型生命週期有如下幾個過程:

  1. 被一個 USB設備驅動建立
  2. 初始化,被安排給一個特定USB 設備的特定端點
  3. 被USB 設備驅動提交給USB 
  4. 提交由USB 核心指定的USB 主機控制器驅動。
  5. 被USB 主機控制器處理,進行一次到USB 設備的傳送。
  6. 當urb 完成,USB 主機控制器驅動通知USB 設備驅動

3.3 簡單的批量與控制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 是發送超時,以ji ffies 爲單位,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                 

3) 探測和斷開函數

在USB 設備驅動usb_driver 結構體的探測函數中,應該完成以下工做:

  • 探測設備的端點地址、緩衝區大小,初始化任何可能用於控制 USB 設備的數據結構。
  • 把已初始化數據結構的指 保存到接口設備中
  • 註冊USB 設備

  對探測函數的調用發生在USB 設備被安裝且USB 核心認爲該驅動程序與安裝的USB 設備對應時 (usb_driver 的id_table 成員在此時發揮做用),而對斷開函數的調用則發生在驅動由於種種緣由再也不控制該設備的時候。對這兩個函數的調用都是在內核線程中進行的.

4) USB 骨架程序 

  Linux 內核源代碼中的 driver/usb/usb-skeleton.c 文件爲咱們提供了一個最基礎的USB 驅動程序,即USB 骨架程序,可被看作一個最簡單的USB 設備驅動實例。儘管USB驅動驅動程序千差萬別,可是骨架程序萬變不離其宗。這裏我也很少介紹啦~

 

  版權全部,轉載請註明轉載地址:http://www.cnblogs.com/lihuidashen/p/4521915.html

相關文章
相關標籤/搜索