linux內核之USB驅動分析


第一部分  USB驅動程序框架

  app:
  -------------------------------------------
  USB設備驅動程序    // 知道數據含義
  內核 --------------------------------------
  USB總線驅動程序   // 1. 識別, 2. 找到匹配的設備驅動, 3. 提供USB讀寫函數 (它不知道數據含義)
  -------------------------------------------
  USB主機控制器
  UHCI OHCI EHCI
  硬件 -----------
  USB設備

  UHCI: intel, 低速(1.5Mbps)/全速(12Mbps)  
  OHCI: microsoft 低速/全速
  EHCI: 高速(480Mbps)

 

第二部分 USB設備基礎概念

  在終端用戶看來,USB設備為主機提供了多種多樣的附加功能,如文件傳輸,聲音播放等,但對USB主機來說,它與所有USB設備的接口都是一致的。一個USB設備由3個功能模塊組成:USB總線接口、USB邏輯設備和功能單元: 

  a -- 這里的USB總線接口指的是USB設備中的串行接口引擎(SIE);
  b -- USB邏輯設備被USB系統軟件看作是一個端點的集合;
  c -- 功能單元被客戶軟件看作是一個接口的集合。SIE、端點和接口都是USB設備的組成單元;

  為了更好地描述USB設備的特征,USB提出了設備架構的概念。從這個角度來看,可以認為USB設備是由一些配置、接口和端點組成,即一個USB設備可以含有一個或多個配置(不同的配置使設備表現出不同的功能組合,在探測/連接期間需要從中選定一個),在每個配置中可含有一個或多個接口(一個配置中的所有接口可以同時有效,並可被不同的程序連接),在每個接口中可含有若干個端點(代表一個基本功能,每個端點都有一定的屬性,其中包括傳輸方式、總線訪問頻率、帶寬、端點號和數據包的最大容量等)。其中,配置和接口是對USB設備功能的抽象,實際的數據傳輸由端點來完成。在使用USB設備前,必須指明其采用的配置和接口。這個步驟一般是在設備接入主機時設備進行枚舉時完成的。

  usb設備非常復雜,有許多不同的邏輯單元組成,這些單元的關系如下:

    

  設備描述符(usb_device_descriptor):關於設備的通用信息,如供貨商ID及適用的協議等,在Linux內核中,USB設備用usb_device結構體來描述,位於include/uapi/linux/usb/ch9.h中,一個USB設備只能有一個設備描述符

 1 struct usb_device_descriptor {
 2     __u8  bLength;                    //描述符長度
 3     __u8  bDescriptorType;            //描述符類型編號
 4 
 5     __le16 bcdUSB;                    //USB版本號
 6     __u8  bDeviceClass;                //USB分配的設備類code
 7     __u8  bDeviceSubClass;            //USB分配的子類code
 8     __u8  bDeviceProtocol;            //USB分配的協議code
 9     __u8  bMaxPacketSize0;            //endpoint0最大包大小
10     __le16 idVendor;                //廠商編號
11     __le16 idProduct;
12     __le16 bcdDevice;
13     __u8  iManufacturer;
14     __u8  iProduct;
15     __u8  iSerialNumber;
16     __u8  bNumConfigurations;        //可能的配置數量
17 } __attribute__ ((packed));
usb_device_descriptor

 

  配置描述符(usb_config_descriptor):一個USB設備可以包含一個或多個配置,如USB設備的低功耗模式和高功耗模式可分別對應一個配置。在使用USB設備前,必須為其選擇一個合適的配置。配置描述符用於說明USB設備中各個配置的特性,如配置所含接口的個數等。USB設備的每一個配置都必須有一個配置描述符。

 1 struct usb_config_descriptor {
 2     __u8  bLength;
 3     __u8  bDescriptorType;
 4 
 5     __le16 wTotalLength;
 6     __u8  bNumInterfaces;
 7     __u8  bConfigurationValue;
 8     __u8  iConfiguration;
 9     __u8  bmAttributes;
10     __u8  bMaxPower;
11 } __attribute__ ((packed));
usb_config_descriptor

 

  接口描述符(usb_interface_descriptor):一個配置可以包含一個或多個接口,例如對一個光驅來說,當用於文件傳輸時,使用其大容量存儲接口;而當用於播放CD時,使用其音頻接口。接口是端點的集合,可以包含一個或多個可替換設置,用戶能夠在USB處於配置狀態時改變當前接口所含的個數和特性。接口描述符用於說明設備中各個接口的特性,如接口所屬的設備類及其子類等。USB設備的每個接口(usb_interface)都必須有一個接口描述符。

 1 struct usb_interface_descriptor {
 2     __u8  bLength;
 3     __u8  bDescriptorType;
 4 
 5     __u8  bInterfaceNumber;
 6     __u8  bAlternateSetting;
 7     __u8  bNumEndpoints;
 8     __u8  bInterfaceClass;
 9     __u8  bInterfaceSubClass;
10     __u8  bInterfaceProtocol;
11     __u8  iInterface;
12 } __attribute__ ((packed));
usb_interface_descriptor

 

  端點描述符(usb_endpoint_descriptor):端點地址、方向、類型以及支持的最大包大小等。端點是USB設備中的實際物理單元,USB數據傳輸就是在主機和USB設備各個端點之間進行的。端點一般由USB接口芯片提供,例如Freescale公司的MC68HC908JB8和MC9S12UF32。USB設備中的每一個端點都有唯一的端點號,每個端點所支持的數據傳輸方向一般而言也是確定的:或是輸入(IN),或是輸出(OUT)。也有些芯片提供的端點的數據方向是可以配置的,例如MC68HC908JB8包含有兩個用於數據收發的端點:端點1和端點2。其中端點1只能用於數據發送,即支持輸入(IN)操作;端點2既能用於數據發送,也可用於數據接收,即支持輸入(IN)和輸出(OUT)操作。而MC9S12UF32具有6個端點。利用設備地址、端點號和傳輸方向就可以指定一個端點,並與它進行通信。端點的傳輸特性還決定了其與主機通信是所采用的傳輸類型,例如控制端點只能使用控制傳輸。根據端點的不同用途,可將端點分為兩類:0號端點和非0號端點。0號端點比較特殊,它有數據輸入IN和數據輸出OUT兩個物理單元,且只能支持控制傳輸。所有的USB設備都必須含有一個0號端點,用作默認控制管道。USB系統軟件就是使用該管道與USB邏輯設備進行配置通信的。0號端點在USB設備上的以后就可以使用,而非0號端點必須要在配置以后才可以使用。

 1 struct usb_endpoint_descriptor {
 2     __u8  bLength;
 3     __u8  bDescriptorType;
 4 
 5     __u8  bEndpointAddress;
 6     __u8  bmAttributes;
 7     __le16 wMaxPacketSize;
 8     __u8  bInterval;
 9 
10     /* NOTE:  these two are _only_ in audio endpoints. */
11     /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
12     __u8  bRefresh;
13     __u8  bSynchAddress;
14 } __attribute__ ((packed));
usb_endpoint_descriptor

 

第三部分 USB設備驅動

  (1)在編寫新的USB設備驅動時,主要應該完成的工作是probe()和disconnect()函數,即探測函數和斷開函數。USB設備驅動的模塊加載函數通用的方法是在USB設備驅動的模塊加載函數中使用usb_register(struct *usb_driver)函數添加usb_driver的工作,而在模塊卸載函數中利用usb_deregister(struct *usb_driver)做相反的工作。 對應I2C設備驅動中的 i2c_add_driver(&i2c_driver)與i2c_del_driver(&i2c_driver)。 

  static inline int usb_register(struct usb_driver *driver)
  void usb_deregister(struct usb_driver *driver)

 1 static int __init usb_skel_init(void)
 2 {
 3     int result;
 4 
 5     /* register this driver with the USB subsystem */
 6     result = usb_register(&skel_driver);
 7     if (result)
 8         err("usb_register failed. Error number %d", result);
 9 
10     return result;
11 }
12 
13 static void __exit usb_skel_exit(void)
14 {
15     /* deregister this driver with the USB subsystem */
16     usb_deregister(&skel_driver);
17 }
usb_skel_init

  usb_driver結構體中的id_table成員描述了這個usb驅動所支持的USB設備列表,它指向一個usb_device_id數組,usb_device_id結構體包含有USB設備的制造商ID、產品ID、產品版本等信息。

 1 struct usb_driver {
 2     const char *name;
 3     int (*probe) (struct usb_interface *intf,
 4               const struct usb_device_id *id);
 5     void (*disconnect) (struct usb_interface *intf);
 6     int (*ioctl) (struct usb_interface *intf, unsigned int code,
 7             void *buf);
 8     int (*suspend) (struct usb_interface *intf, pm_message_t message);
 9     int (*resume) (struct usb_interface *intf);
10     int (*reset_resume)(struct usb_interface *intf);
11     int (*pre_reset)(struct usb_interface *intf);
12     int (*post_reset)(struct usb_interface *intf);
13     const struct usb_device_id *id_table;
14     struct usb_dynids dynids;
15     struct usbdrv_wrap drvwrap;
16     unsigned int no_dynamic_id:1;
17     unsigned int supports_autosuspend:1;
18     unsigned int soft_unbind:1;
19 };
usb_driver

  設備驅動工作過程:當USB設備核心檢測到某個設備的屬性和某個驅動程序的usb_device_id結構體所攜帶的信息一致時,這個程序的probe()函數就會執行,拔掉設備或者卸載驅動模塊之后USB核心就會執行disconnect()函數。

  usb_driver本身只是找到USB設備、管理USB設備連接和斷開作用,也就是說它是公司入口處的“打卡機”,可以獲取員工(USB設備)的上下班情況,樹葉和員工一樣,可以是研發工程師,也可以是銷售工程師,而作為USB設備的樹葉可以是字符樹葉、網絡樹葉或塊樹葉,因此必須實現相應設備類的驅動

 1 retval = usb_register_dev(interface, &skel_class);
 2     static struct usb_class_driver skel_class = {
 3         .name =        "skel%d",
 4         .fops =        &skel_fops,
 5         .minor_base =    USB_SKEL_MINOR_BASE,
 6     };
 7         //實現設備類的驅動
 8         static const struct file_operations skel_fops = {   
 9             .owner =    THIS_MODULE,
10             .read =        skel_read,
11             .write =    skel_write,
12             .open =        skel_open,
13             .release =    skel_release,
14             .flush =    skel_flush,
15             };
skel_class

  (2)USB請求塊(urb結構體)

  USB請求塊是USB設備驅動中用來描述與USB設備通信所用的基本載體和核心數組結構,非常類似於網絡設備驅動中的sk_buff結構體。

  a、URB處理流程

  USB設備中每個端點都處理一個urb隊列,在隊列被清空之前,一個urb的典型生命周期如下:

  1)創建urb:struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
    參數:iso_packets是這個URB應當包含的等時數據包的數目,mem_flags參數是分配內存的標志,如果分配成功則返回一個urb結構體指針。

  2)初始化urb(被安排給一個特定的USB設備的特定端點)static inline 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_fn,void *context)
    參數:urb指向要被初始化的urb的指針,dev指向這個urb要被發送到的USB設備,pipe是這個URB要被發送到的USB設備的特定端點,transfer_buffer是指向發送數據或者接收數據的緩沖區的指針,是動態分配的;complete_fn函數指向當這個URB完成時被調用的完成處理函數;context是完成處理函數的“上下文”。 

  3)提交urb:int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
    參數:mem_flag,與傳遞給kmalloc()函數參數的意義相同,用於告知USB核心如何在此時分配內存緩沖區。
    如果usb_submit_urb()函數調用成功,即URB的控制權被移交給USB核心,該函數返回0,否則返回錯誤號。

  4)提交由USB核心指定的USB主機控制器驅動。

  5)被USB主機控制器處理,進行一次到USB設備的傳送。

    第4)~5)步由USB核心和主機控制器完成,不受USB設備驅動的控制。

  6)當URB完成,URB將結束,USB主機控制器驅動通知USB設備驅動。

 


免責聲明!

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



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