Linux那些事儿 之 戏说USB(12)接口是设备的接口(一)
前面的前面已經說了,接口是設備的接口。設備可以有多個接口,每個接口代表一個功能,每個接口對應著一個驅動。Linux設備模型的device落實在USB子系統,成了兩個結構,一個是struct usb_device,一個是struct usb_interface。聽聽復旦人甲怎么說,一個usb設備,兩種功能,一個鍵盤,上面帶一個揚聲器,兩個接口,那這樣肯定得要兩個驅動程序,一個是鍵盤驅動程序,一個是音頻流驅動程序。道上的兄弟喜歡把這樣兩個整合在一起的東西叫做一個設備,那好,讓他們去叫吧,我們用interface來區分這兩者行了吧。于是有了這里提到的那個數據結構,struct usb_interface。
include/linux/usb.h
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 *//* If there is an interface association descriptor then it will list* the associated interfaces */struct usb_interface_assoc_descriptor *intf_assoc;int minor; /* minor number this interface is* bound to */enum usb_interface_condition condition; /* state of binding */unsigned sysfs_files_created:1; /* the sysfs attributes exist */unsigned ep_devs_created:1; /* endpoint "devices" exist */unsigned unregistering:1; /* unregistration is in progress */unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */unsigned needs_binding:1; /* needs delayed unbind/rebind */unsigned reset_running:1;unsigned resetting_device:1; /* true: bandwidth alloc after reset */struct device dev; /* interface specific device info */struct device *usb_dev;atomic_t pm_usage_cnt; /* usage counter for autosuspend */struct work_struct reset_ws; /* for resets in atomic context */
};這里有個altsetting成員,只用耗費一個腦細胞就可以明白它的意思就是alternate setting,可選的設置。那么再耗費一個腦細胞就可以知道cur_altsetting表示當前正在使用的設置,num_altsetting表示這個接口具有可選設置的數量。前面提過USB設備的配置,那這里的設置是嘛意思?這可不是耗費一兩個腦細胞就可以明白的了,不過不要怕,雖然說腦細胞是不可再生資源,但并不是多寶貴的東東,不然咋黃金煤炭的股票都在瘋長,就不見人的工資長。
咱們是難得糊涂幾千年了,不會去區分配置還有設置有什么區別,起碼我平時即使是再無聊也不會去想這個,但老外不一樣,他們挺較真兒這個,還分了兩個詞,配置是configuration,設置是setting。先說配置,一個手機可以有多種配置,比如可以攝像,可以接在電腦里當做一個U盤,那么這兩種情況就屬于不同的配置,在手機里面有相應的選擇菜單,你選擇了哪種它就按哪種配置進行工作,供你選擇的這個就叫做配置。很顯然,當你攝像的時候你不可以訪問這塊U盤,當你訪問這塊U盤的時候你不可以攝像,因為你做了選擇。第二,既然一個配置代表一種不同的功能,那么很顯然,不同的配置可能需要的接口就不一樣,我假設你的手機里從硬件上來說一共有5個接口,那么可能當你配置成U盤的時候它只需要用到某一個接口,當你配置成攝像的時候,它可能只需要用到另外兩個接口,可能你還有別的配置,然后你可能就會用到剩下那兩個接口。
再說說設置,一個手機可能各種配置都確定了,是振動還是鈴聲已經確定了,各種功能都確定了,但是聲音的大小還可以變吧,通常手機的音量是一格一格的變動,大概也就5、6格,那么這個可以算一個setting吧。
你還是不明白啥是配置啥是設置的話,那我多說一下,就直接用大小關系來理解好了,畢竟大家對互相之間的大小關系都更敏感一些,這么說吧,設備大于配置,配置大于接口,接口大于設置,更準確的說是設備可以有多個配置,配置里可以包含一個或更多的接口,而接口通常又具有一個或更多的設置。minor,分配給接口的次設備號。地球人都知道,linux下所有的硬件設備都是用文件來表示的,俗稱設備文件,在/dev目錄下邊,為了顯示自己并不是普通的文件,它們都會有一個主設備號和次設備號,比如
brw-r----- 1 root disk 8, 0 Sep 26 09:17 /dev/sdabrw-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命令查看的,當然只是顯示了其中的幾個來表示表示而已。咱們對數字都是比較敏感的,一眼就能看到,在每一行的日期前面有兩個逗號隔開的數字,對于普通文件而言這個位置顯示的是文件的長度,而對于設備文件,這里顯示的兩個數字表示了該設備的主設備號和次設備號。一般來說,主設備號表明了設備的種類,也表明了設備對應著哪個驅動程序,而次設備號則是因為一個驅動程序要支持多個設備而為了讓驅動程序區分它們而設置的。也就是說,主設備號用來幫你找到對應的驅動程序,次設備號給你的驅動用來決定對哪個設備進行操作。上面就顯示了俺的移動硬盤主設備號為8,系統里tty設備的主設備為4。
設備要想在linux里分得一個主設備號,有個立足之地,也并不是那么容易的,主設備號雖說不是什么特別稀缺的資源,但還是需要設備先在驅動里提出申請,獲得系統的批準才能擁有一個。因為一部分的主設備號已經被靜態的預先指定給了許多常見的設備,你申請的時候要避開它們,選擇一個里面沒有列出來的。這些已經被分配掉的主設備號都列在Documentation/devices.txt文件里。當然,如果你是用動態分配的形式,就可以不去理會這些,直接讓系統為你作主,替你選擇一個即可。
很顯然,任何一個有理智有感情的人都會認為USB設備是很常見的,linux理應為它預留了一個主設備號。看看include/linux/usb.h文件#define USB_MAJOR 180
#define USB_DEVICE_MAJOR 189當我們知道了主設備號,滿懷激情與向往的來尋找USB的主設備號時,我們卻發現這里在上演真假李逵。這兩個哪個才是我們苦苦追尋的她?
你可以在內核里搜索它們都曾經出現什么地方,或者就跟隨我回到usb_init函數。
retval = usb_major_init();if (retval)goto major_init_failed;retval = usb_register(&usbfs_driver);if (retval)goto driver_register_failed;retval = usb_devio_init();if (retval)goto usb_devio_init_failed;前面只提了句上面代碼是與usbfs相關的就簡單的飄過了,這里略微說的多一點。usbfs為咱們提供了在用戶空間直接訪問usb硬件設備的接口,但是它需要內核的大力支持,usbfs_driver就是用來完成這個光榮任務的。咱們可以去usb_devio_init函數里看一看,它在drivers/usb/core/devio.c文件里定義
int __init usb_devio_init(void)
{int retval;retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,"usb_device");if (retval) {printk(KERN_ERR "Unable to register minors for usb_device\n");goto out;}cdev_init(&usb_device_cdev, &usbdev_file_operations);retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);if (retval) {printk(KERN_ERR "Unable to get usb_device major %d\n",USB_DEVICE_MAJOR);goto error_cdev;}usb_register_notify(&usbdev_nb);
out:return retval;error_cdev:unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);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函數調用的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文件里顯示了所有當前系統里已經分配出去的主設備號,當然上面只是列出了字符設備,Block devices被有意的飄過了。很明顯,咱們前面提到的usb_device和usb都在里面。
不過到這里還沒完,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起作用時起作用。
說完了設備號,回到struct usb_interface的151行,condition字段表示接口和驅動的綁定狀態,enum usb_interface_condition類型,在include/linux/usb.h里定義enum usb_interface_condition {USB_INTERFACE_UNBOUND = 0,USB_INTERFACE_BINDING,USB_INTERFACE_BOUND,USB_INTERFACE_UNBINDING,
};
前面說linux設備模型的時候說了,設備和驅動是相生相依的關系,總線上的每個設備和驅動都在等待著命中的那個她,找到了,執子之手與子偕老,找不到,孤苦伶仃北冰洋。enum usb_interface_condition形象的描繪了這個過程中接口的個中心情,孤苦、期待、幸福、分開,人生又何嘗不是如此?
needs_remote_wakeup和pm_usage_cnt是關于掛起和喚醒的。協議里規定,所有的usb設備都必須支持掛起狀態,就是說為了達到節電的目的,當設備在指定的時間內,3ms吧,如果沒有發生總線傳輸,就要進入掛起狀態。當它收到一個non-idle的信號時,就會被喚醒。needs_remote_wakeup表示是否需要打開遠程喚醒功能。遠程喚醒允許掛起的設備給主機發信號,通知主機它將從掛起狀態恢復,注意如果主機處于掛起狀態,就會喚醒主機,不然主機仍然在睡著,設備自個醒過來干嗎用。協議里并沒有要求USB設備一定要實現遠程喚醒的功能,即使實現了,從主機這邊兒也可以打開或關閉它。pm_usage_cnt,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那些事儿 之 戏说USB(12)接口是设备的接口(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux那些事儿 之 戏说USB(11
- 下一篇: Linux那些事儿 之 戏说USB(13