第一部分 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_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_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_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設備驅動
(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_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設備核心檢測到某個設備的屬性和某個驅動程序的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 };
(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設備驅動。