《Linux那些事兒之我是USB》我是U盤(11)從協議中來到協議中去

從協議中來,到協議中去(上)

在structusb_driver中,.probe和.disconnect的原型如下:

836     int(*probe) (struct usb_interface *intf,

837                      const struct usb_device_id *id);

838

839    void(*disconnect) (struct usb_interface *intf);

我們來看其中的參數,structusb_device_id這個不用說了,剛纔已經介紹過,那麼struct usb_interface從何而來?還是讓我們先從struct usb_device說起。

我們知道每一個設備對應一個struct device結構體變量,但是設備不可能是萬能的,生命是多樣性的,就像我們可以用“人”來統稱全人類,但是分得細一點,又有男人和女人的區別。那麼設備也一樣,由於有各種各樣的設備,於是又出來了更多的詞彙(數據結構),比如針對USB設備,開發人員們設計了一個叫做struct usb_device的結構體。它定義於include/linux/usb.h:

336 struct usb_device {

337    int            devnum;         /* Address on USBbus */

338   char           devpath [16]; /* Use in messages: /port/port/... */

339    enumusb_device_state   state;/*configured, not attached, etc */

340   enum usb_device_speed   speed;  /* high/full/low (or error) */

341

342    structusb_tt   *tt;        /* low/full speed dev, highspeed hub */

343    int            ttport;         /* device port onthat tt hub */

344

345    unsigned inttoggle[2];         /* one bit foreach endpoint

346                                         * ([0] = IN, [1] = OUT) */

347

348    structusb_device *parent;   /* ourhub, unless we're the root */

349    structusb_bus *bus;           /* Bus we're part of */

350    structusb_host_endpoint ep0;

351

352    structdevice dev;             /* Generic device interface */

353

354    structusb_device_descriptor descriptor;/* Descriptor */

355   struct usb_host_config *config; /*All of the configs */

356

357     structusb_host_config *actconfig;/* the active configuration */

358    structusb_host_endpoint *ep_in[16];

359    structusb_host_endpoint *ep_out[16];

360

361    char**rawdescriptors;       /* Raw descriptors for eachconfig */

362

363     unsignedshort bus_mA;       /* Current available from thebus */

364    u8portnum;                    /* Parent port number (origin 1) */

365    u8level;                      /* Number of USB hub ancestors */

366

367    unsigneddiscon_suspended:1;    /* Disconnected while suspended */

368     unsignedhave_langid:1;     /* whether string_langid is valid */

369     intstring_langid;             /* language ID for strings */

370

371    /* staticstrings from the device */

372     char*product;                 /* iProduct string, if present */

373    char*manufacturer;          /* iManufacturer string, if present */

374    char*serial;               /* iSerialNumber string, if present */

375

376     structlist_head filelist;

377 #ifdef CONFIG_USB_DEVICE_CLASS

378    structdevice *usb_classdev;

379 #endif

380 #ifdef CONFIG_USB_DEVICEFS

381    structdentry *usbfs_dentry;/*usbfs dentry entry for the device*/

382 #endif

383    /*

384     * Child devices - these can be eithernew devices

385      * (if this is a hub device), ordifferent instances

386     * of this same device.

387     *

388      * Each instance needs its own set ofdata structures.

389      */

390

391    intmaxchild;                  /* Number of ports if hub */

392    structusb_device *children[USB_MAXCHILDREN];

393

394    intpm_usage_cnt;              /* usage counter for autosuspend */

395    u32quirks;                    /* quirks of the whole device */

396

397 #ifdef CONFIG_PM

398    structdelayed_work autosuspend; /* for delayed autosuspends */

399   struct mutex pm_mutex;          /* protectsPM operations */

400

401    unsignedlong last_busy;        /* time of last use */

402    intautosuspend_delay;          /* injiffies */

403

404    unsignedauto_pm:1;            /*autosuspend/resume in progress */

405    unsigneddo_remote_wakeup:1;/* remote wakeup should be enabled */

406    unsignedautosuspend_disabled:1; /* autosuspend and autoresume */

407    unsignedautoresume_disabled:1;  /*  disabled by the user */

408 #endif

409 };

410 #define to_usb_device(d) container_of(d,struct usb_device, dev)

看起來很複雜的一個數據結構,不過我們目前不需要去理解她的每一個成員,不過我們可以看到,其中有一個成員struct device dev,這就是前面說的那個屬於每個設備的struct device結構體變量。

實際上,U盤驅動裏邊並不會直接去處理這個結構體,因爲對於一個U盤來說,它就是對應這麼一個struct usb_device的變量,這個變量由USB Core負責申請和賦值。但是我們需要記住這個結構體變量,因爲日後我們調用USBCore提供的函數時,會把這個變量作爲參數傳遞上去。因爲很簡單,要和USB Core交流,總得讓人家知道我們是誰吧。比如後來要調用的一個函數,usb_buffer_alloc,它就需要這個參數。

而對U盤設備驅動來說,比這個struct usb_device更重要的數據結構是,struct usb_interface。走到這一步,我們不得不去了解一些USB設備的規範了,或者專業一點說,USB協議。因爲我們至少要知道什麼是USB接口(Interface)。

從協議中來,到協議中去(中)

任何事物都有其要遵守的規矩。USB設備要遵循的就是USB協議。 不管是軟件還是硬件,在設計的開始,總是要參考USB協議。怎麼設計硬件?如何編寫軟件?不看USB協議,誰也不可能憑空想象出來。

USB協議規定了,每個USB設備都得有一些基本的元素,稱爲描述符。有四類描述符是任何一種USB設備都得有的,它們是,設備描述符(Device Descriptor),配置描述符(ConfigurationDescriptor),接口描述符(Interface Descriptor),端點描述符(Endpoint Descriptor)。描述符裏的東西是一個設備出廠時就被廠家給固化在設備中了。這種東西不管怎樣也改變不了,比如我有一個Intel的U盤,裏面的固有的信息肯定是在Intel出廠時就被烙在裏邊了,廠家早已把它的一切,烙上Intel印。所以當我插入U盤,用cat /proc/scsi/scsi命令看一下的話,“Vendor”一項顯示的肯定是Intel。 

關於這幾種描述符,USB Core在總線掃描就會去讀取,獲得裏邊的信息,其中,設備描述符描述的是整個設備。注意了,這個設備和咱們一直講的設備驅動那裏的設備是不一樣的。因爲一個USB設備實際上指的是一種宏觀上的概念,它可以是一種多功能的設備,改革開放之後,多功能的東西越來越多了,比如外企常見的多功能一體機,就是集打印機、複印機、掃描儀、傳真機於一體的設備。當然,這不屬於USB設備,但是USB設備當然也有這種情況,比如電臺DJ可能會用到的,一個鍵盤,上邊帶一個揚聲器,它們用兩個USB接口接到USB Hub上去,而設備描述符描述的就是這整個設備的特點。

那麼配置描述符呢?老實說,對我們瞭解U盤驅動真是沒有什麼意義,但是作爲一個有責任的人,此刻,我必須爲它多說幾句,一個設備可以有一種或者幾種配置。沒見過具體的USB設備?那麼好,手機見過吧,每個手機都會有多種配置,或者說“設定”,比如,我的這款,Nokia 6300。手機語言可以設定爲English、繁體中文或簡體中文。一旦選擇了其中一種,那麼手機裏面所顯示的所有的信息都是該種語言/字體。還有最簡單的例子,操作模式也有好幾種,標準、無聲、會議等。基本上如果我設爲“會議”,那麼就是隻振動不發聲;要是設爲無聲,那麼就什麼動靜也不會有,只能憑感覺了。那麼USB設備的配置也是如此,不同的USB設備當然有不同的配置了,或者說需要配置哪些東西也會不一樣。好了,關於配置,就說這麼多。

對於USB設備驅動程序編寫者來說,更爲關鍵的是下面的接口和端點。先說接口。第一句,一個接口對應一個USB設備驅動程序。沒錯,還舉前邊那個例子。一個USB設備,兩種功能,一個鍵盤,上面帶一個揚聲器,兩個接口,那肯定得要兩個驅動程序,一個是鍵盤驅動程序,一個是音頻流驅動程序。“道上”的兄弟喜歡把這樣兩個整合在一起的東西叫做一個設備,我門用接口來區分這兩者。於是有了我們前面提到的那個數據結構,struct usb_interface,它定義於include/linux/usb.h:

140 struct usb_interface {

141   /* array of alternate settings forthis interface,

142    * stored in no particular order */

143   struct usb_host_interface *altsetting;

144

145   struct usb_host_interface*cur_altsetting;     /* the currently

146                                         * active alternate setting */

147    unsignednum_altsetting;        /* number of alternatesettings */

148

149   int minor;                     /* minor number this interface is

150                                         * bound to */

151  enumusb_interface_condition condition;     /* state of binding */

152    unsignedis_active:1;         /* the interfaceis not suspended */

153    unsignedneeds_remote_wakeup:1; /*driver requires remote wakeup*/

154

155    structdevice dev;            /* interface specific device info */

156    struct device *usb_dev;/* pointer to the usbclass's device, if any*/

157   int pm_usage_cnt;              /* usage counter for autosuspend */

158 };

159 #defineto_usb_interface(d) container_of(d, struct usb_interface, dev)

160 #define interface_to_usbdev(intf) \

161        container_of(intf->dev.parent, struct usb_device, dev)

這個結構體是一個貫穿整個U盤驅動程序的,所以雖然我們不用去深入瞭解,但是需要記住,整個U盤驅動程序在後面任何一處提到的struct usb_interface都是同一個變量,這個變量是在USB Core總線掃描時就申請好了的。我們只需要直接用就是了,比如前面說過的storage_probe(struct usb_interface *intf,const struct usb_device_id*id),storage_disconnect(structusb_interface *intf)這兩個函數中的參數intf。

而這裏130行的宏interface_to_usbdev,也會用得着的,顧名思義,就是從一個structusb_interface轉換成一個structusb_device,我們說過了,有些函數需要的參數就是struct usb_device,而不是usb_interface,所以這種轉換是經常會用到的,而這個宏,USB Core的設計者們也爲我們準備好了,除了感激,我們還能說什麼呢?

從協議中來,到協議中去(下)

如果你是急性子,那這時候你一定很想開始看storage_probe函數了,因爲整個U盤的工作就是從這裏開始的。

前面我們已經瞭解了設備、配置和接口,還剩最後一個就是端點。USB通信的最基本的形式就是通過端點,一個接口有一個或多個端點,而作爲像U盤這樣的存儲設備,它至少有一個控制端點,兩個批量端點。這些端點都是幹什麼用的?說來話長,真是一言難盡啊。

USB協議中規定了,USB設備有四種通信方式,分別是控制傳輸、中斷傳輸、批量傳輸、等時傳輸。其中,等時傳輸顯然是用於音頻和視頻一類的設備,這類設備期望能夠有一個比較穩定的數據流,比如你在網上QQ視頻聊天,肯定希望每分鐘傳輸的圖像/聲音速率是比較穩定的。usb-storage裏邊肯定不會用到等時傳輸。因爲我們只管複製一個文件,管它第一秒和第二秒的傳輸有什麼區別,只要幾十秒內傳完了就行了。

相比之下,等時傳輸是四種傳輸中最麻煩的,所以,U盤用不着。不過我要說,中斷傳輸也用不着,對於U盤來說,的確用不着,雖然說USBMass Storage的協議中邊有一個叫做CBI的傳輸協議,CBI就是Control/Bulk/Interrupt,即控制/批量/中斷,這三種傳輸都會用到,但這種傳輸協議並不適用於U盤,U盤使用的是叫做Bulk-Only的傳輸協議。使用這種協議的設備只有兩種傳輸方式,一種是批量傳輸,另一種是控制傳輸,控制傳輸是任何一種USB設備都必須支持的,它專門用於傳輸一些控制信息。比如我想查詢一下關於這個接口的一些信息,那麼就用控制傳輸。而批量傳輸,它就是U盤的主要工作了,讀寫數據,這種情況就得用批量傳輸。具體的傳輸我們後面再講。

好了,知道了傳輸方式,就可以來認識端點了。和端點齊名的有一個叫做管道,或者有文化的人管這個叫Pipe。端點就是通信的發送點或者接收點,要發送數據,那你只要把數據發送到正確的端點那裏就可以了。之所以U盤有兩個批量端點,是因爲端點也是有方向的,一個叫做Bulk IN,一個叫做Bulk OUT。從USB主機到設備稱爲OUT,從設備到主機稱爲IN。而管道實際上只是爲了讓我們能夠找到端點,就相當於我們日常說的郵編地址,比如一個國家,爲了通信,我們必須給各個地方取名,給各條大大小小的路取名,比如要從某偏僻的小縣城出發,要到北京,那你的這個端點就是北京,而你得知道你來北京的路線,那這個從你們縣城到北京的路線就算一條管道。有人好奇地問了,管道應該有兩個端點吧,一個端點是北京,那另一個端點呢?答案是,這條管道有些特殊,我們只需要知道一端的目的地是北京,而另一端是哪裏無所謂,因爲不管你在哪裏你都得到北京。

嚴格來說,管道的另一端應該是USB主機,USB協議中邊也是這麼規定的,協議中管道代表着一種能力。怎樣一種能力呢?在主機和設備上的端點之間移動數據,聽上去挺玄的。因爲事實上,USB裏邊所有的數據傳輸都是由主機發起的。一切都是以主機爲核心,各種設備緊緊圍繞在主機周圍。所以USB Core裏邊很多函數就是爲了讓USB主機能夠正確地完成數據傳輸或者說傳輸調度,就得告訴主機這個管道,換而言之,它得知道自己這次調度的數據是傳送給誰,或者從誰那裏傳輸過來。不過有人又要問了,比如說要從U盤裏讀一個文件,那告訴USB主機某個端點能有用嗎?那個文件又不是存放在一個端點裏邊,它不是存放在U盤裏面嗎?這個就只能在後面的代碼裏去知道了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章