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

來源:互聯網
上載者:User

 

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

 

在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結構體變數。

 

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

 

而對隨身碟裝置驅動來說,比這個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的隨身碟,裡面的固有的資訊肯定是在Intel出廠時就被烙在裡邊了,廠家早已把它的一切,烙上Intel印。所以當我插入隨身碟,用cat /proc/scsi/scsi命令看一下的話,“Vendor”一項顯示的肯定是Intel。

 

關於這幾種描述符,USB Core在匯流排掃描就會去讀取,獲得裡邊的資訊,其中,裝置描述符描述的是整個裝置。注意了,這個裝置和咱們一直講的裝置驅動那裡的裝置是不一樣的。因為一個USB裝置實際上指的是一種宏觀上的概念,它可以是一種多功能的裝置,改革開放之後,多功能的東西越來越多了,比如外企常見的多功能一體機,就是集印表機、複印機、掃描器、傳真機於一體的裝置。當然,這不屬於USB裝置,但是USB裝置當然也有這種情況,比如電台DJ可能會用到的,一個鍵盤,上邊帶一個擴音器,它們用兩個USB介面接到USB Hub上去,而裝置描述符描述的就是這整個裝置的特點。

 

那麼配置描述符呢?老實說,對我們瞭解隨身碟驅動真是沒有什麼意義,但是作為一個有責任的人,此刻,我必須為它多說幾句,一個裝置可以有一種或者幾種配置。沒見過具體的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)

 

這個結構體是一個貫穿整個隨身碟驅動程式的,所以雖然我們不用去深入瞭解,但是需要記住,整個隨身碟驅動程式在後面任何一處提到的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函數了,因為整個隨身碟的工作就是從這裡開始的。

 

前面我們已經瞭解了裝置、配置和介面,還剩最後一個就是端點。USB通訊的最基本的形式就是通過端點,一個介面有一個或多個端點,而作為像隨身碟這樣的存放裝置,它至少有一個控制端點,兩個批量端點。這些端點都是幹什麼用的?說來話長,真是一言難盡啊。

 

USB協議中規定了,USB裝置有四種通訊方式,分別是控制傳輸、中斷傳輸、批量傳輸、等時傳輸。其中,等時傳輸顯然是用於音頻和視頻一類的裝置,這類裝置期望能夠有一個比較穩定的資料流,比如你在網上QQ視訊交談,肯定希望每分鐘傳輸的映像/聲音速率是比較穩定的。usb-storage裡邊肯定不會用到等時傳輸。因為我們只管複製一個檔案,管它第一秒和第二秒的傳輸有什麼區別,只要幾十秒內傳完了就行了。

 

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

 

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

 

嚴格來說,管道的另一端應該是USB主機,USB協議中邊也是這麼規定的,協議中管道代表著一種能力。怎樣一種能力呢?在主機和裝置上的端點之間移動資料,聽上去挺玄的。因為事實上,USB裡邊所有的資料轉送都是由主機發起的。一切都是以主機為核心,各種裝置緊緊圍繞在主機周圍。所以USB Core裡邊很多函數就是為了讓USB主機能夠正確地完成資料轉送或者說傳輸調度,就得告訴主機這個管道,換而言之,它得知道自己這次調度的資料是傳送給誰,或者從誰那裡傳輸過來。不過有人又要問了,比如說要從隨身碟裡讀一個檔案,那告訴USB主機某個端點能有用嗎?那個檔案又不是存放在一個端點裡邊,它不是存放在隨身碟裡面嗎?這個就只能在後面的代碼裡去知道了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.