Linux設備模型中的總線落實在USB子系統里就是usb_bus_type,它在usb_init的函數bus_register(&usb_bus_type)里注冊。usb_bus_type定義如下:
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.suspend = usb_suspend,
.resume = usb_resume,
};
顯然,在這個結構體里重要的是usb_device_match函數,以后會專門一節來詳細分析

熟悉Linux設備模型,上述函數的參數我們應該也能夠理解。對應的就是總線兩條鏈表里的設備和驅動。注釋已經很明確告訴我們分兩條路走,一條是設備,另一條是接口,你問我為什么?我說你去問問內核吧。(其實我也不知道)
/* devices and interfaces are handled separately */
接口是設備的接口。設備可以有多個接口,每個接口代表一個功能,每個接口對應着一個驅動。Linux設備模型的device落實在USB子系統,成了兩個結構,一個是struct usb_device,一個是struct usb_interface。假設一個usb設備具有兩種功能,一個是鍵盤功能,還有揚聲器功能,那么就會存在兩個接口,對應得要有兩個驅動程序,一個是鍵盤驅動程序,一個是音頻流驅動程序。兄弟喜歡把這樣兩個整合在一起的東西叫做一個設備,那好,讓他們去叫吧,我們用interface來區分這兩者行了吧。於是有了這里提到的那個數據結構,struct usb_interface。去看看usb_interface結構體吧。
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting; /* the currently
* active alternate setting */
unsigned num_altsetting; /* number of alternate settings */
int minor; /* minor number this interface is
* bound to */
enum usb_interface_condition condition; /* state of binding */
unsigned is_active:1; /* the interface is not suspended */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
struct device dev; /* interface specific device info */
struct device *usb_dev; /* pointer to the usb class's device, if any */
int pm_usage_cnt; /* usage counter for autosuspend */
};
這里有個altsetting成員,意思就是alternate setting,可選的設置,cur_altsetting表示當前正在使用的設置;num_altsetting表示這個接口具有可選設置的數量;minor,分配給接口的次設備號;,condition字段表示接口和驅動的綁定狀態,前面說linux設備模型的時候說了,設備和驅動是相生相依的關系usb_interface_condition形象的描繪了這個過程中接口的個中心情,孤苦、期待、幸福、分開,人生又何嘗不是如此?is_active:1、needs_remote_wakeup:1和pm_usage_cnt是關於掛起和喚醒的。協議里規定,所有的usb設備都必須支持掛起狀態,就是說為了達到節電的目的,當設備在指定的時間內,3ms吧,如果沒有發生總線傳輸,就要進入掛起狀態。當它收到一個non-idle的信號時,就會被喚醒。 is_active表示接口是不是處於掛起狀態。needs_remote_wakeup表示是否需要打開遠程喚醒功能。遠程喚醒允許掛起的設備給主機發信號,通知主機它將從掛起狀態恢復,注意如果主機處於掛起狀態,就會喚醒主機,不然主機仍然在睡着。協議里並沒有要求USB設備一定要實現遠程喚醒的功能,即使實現了,從主機這邊兒也可以打開或關閉它。pm_usage_cnt,就是使用計數,當它為0時,接口允許autosuspend。什么叫autosuspend?有時合上筆記本后,它會自動進入休眠,這就叫autosuspend。但不是每次都是這樣的,就像這里只有當pm_usage_cnt為0時才會允許接口autosuspend。
struct device dev和struct device *usb_dev,看到struct device沒,它們就是linux設備模型里的device嵌在這兒的對象,我們的心中要時時有個模型。不過這么想當然是不正確的,兩個里面只有dev才是模型里的device嵌在這兒的,usb_dev則不是。當接口使用USB_MAJOR作為主設備號時,usb_dev才會用到,你找遍整個內核,也只在usb_register_dev和usb_deregister_dev兩個函數里能夠看到它,usb_dev指向的就是usb_register_dev函數里創建的usb class device。
下面插講一下關於設備號的概念。
linux下所有的硬件設備都是用文件來表示的,俗稱設備文件,在/dev目錄下邊兒,為了顯示自己並不是普通的文件,它們都會有一個主設備號和次設備號,比如
brw-r----- 1 root disk 8, 0 Sep 26 09:17 /dev/sda
brw-r----- 1 root disk 8, 1 Sep 26 09:17 /dev/sda1
crw-r----- 1 root tty 4, 1 Sep 26 09:17 /dev/tty1
這是在我的系統里使用ls –l命令查看的,當然只是顯示了其中的幾個來表示表示而已。很顯然,任何一個有理智有感情的人都會認為USB設備是很常見的,linux理應為它預留了一個主設備號。看看include/linux/usb.h文件
7 #define USB_MAJOR 180
8 #define USB_DEVICE_MAJOR 189
你可以在內核里搜索它們都曾經出現什么地方,或者就跟隨我回到usb_init函數。
之前和usbfs相關的就簡單的飄過了,這里略微說的多一點。usbfs為咱們提供了在用戶空間直接訪問usb硬件設備的接口,但是世界上沒有免費的午餐,它需要內核的大力支持,usbfs_driver就是用來完成這個光榮任務的。咱們可以去usb_init的usb_devio_init函數里看一看,它在drivers/usb/devio.c文件里定義
retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
"usb_device");
if (retval) {
err("unable to register minors for usb_device");
goto out;
}
register_chrdev_region函數獲得了設備usb_device對應的設備編號,設備usb_device對應的驅動當然就是usbfs_driver,參數USB_DEVICE_DEV也在同一個文件里有定義
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
終於再次見到了USB_DEVICE_MAJOR,也終於明白它是為了usbfs而生,為了讓廣大人民群眾能夠在用戶空間直接和usb設備通信而生。因此,它並不是我們所要尋找的。
那么答案很明顯了,USB_MAJOR就是咱們苦苦追尋的那個她,就是linux為USB設備預留的主設備號。事實上,前面usb_init函數的880行,usb_major_init函數已經使用USB_MAJOR注冊了一個字符設備,名字就叫usb。我們可以在文件/proc/devices里看到它們。
localhost:/usr/src/linux/drivers/usb/core # cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
29 fb
116 alsa
128 ptm
136 pts
162 raw
180 usb
189 usb_device
/proc/devices文件里顯示了所有當前系統里已經分配出去的主設備號,當然上面只是列出了字符設備。事實上,USB設備有很多種,並不是都會用到這個預留的主設備號。比如移動硬盤顯示出來的主設備號是8,你的攝像頭在linux顯示的主設備號也絕對不會是這里的USB_MAJOR。坦白的說,咱們經常遇到的大多數usb設備都會與input、video等子系統關聯,並不單單只是作為usb設備而存在。如果usb設備沒有與其它任何子系統關聯,就需要在對應驅動的probe函數里使用usb_register_dev函數來注冊並獲得主設備號USB_MAJOR,你可以在drivers/usb/misc目錄下看到一些例子,drivers/usb/ usb-skeleton.c文件也屬於這種。如果usb設備關聯了其它子系統,則需要在對應驅動的probe函數里使用相應的注冊函數,USB_MAJOR也就該干嗎干嗎去,用不着它了。比如,usb鍵盤關聯了input子系統,驅動對應drivers/hid/usbhid目錄下的usbkbd.c文件,在它的probe函數里可以看到使用了input_register_device來注冊一個輸入設備。准確的說,這里的USB設備應該說成USB接口,畢竟一個USB接口才對應着一個USB驅動。當USB接口關聯有其它子系統,也就是說不使用USB_MAJOR作為主設備號時,struct usb_interface的字段minor可以簡單的忽略。minor只在USB_MAJOR起作用時起作用。