《Linux那些事兒之我是USB》我是U盤(14)冰凍三尺非一日之寒

羅馬不是一天建成的。在讓U盤工作之前,其實我們的驅動做了很多準備工作。

我們繼續跟着感覺走,storage_probe(),972行至975行,一系列的以init_*命名的函數在此刻被調用,這裏涉及了一些鎖機制,等待機制,不過只是初始化,暫且不理睬,到後面用到時再細說,不過請記住,這幾行每一行都是有用的。後面自然會用得着。

此時,我們先往下走,978行的associate_dev()和989行的get_device_info(),這兩個函數是我們目前需要看的。

先看associate_dev()函數,定義於drivers/usb/storage/usb.c中。

438 /* Associate our private data with the USBdevice */

439 static intassociate_dev(struct us_data *us, struct usb_interface *intf)

440 {

441    US_DEBUGP("--%s\n", __FUNCTION__);

442

443    /* Fill inthe device-related fields */

444    us->pusb_dev= interface_to_usbdev(intf);

445    us->pusb_intf= intf;

446    us->ifnum= intf->cur_altsetting->desc.bInterfaceNumber;

447     US_DEBUGP("Vendor:0x%04x, Product: 0x%04x, Revision: 0x%04x\n",

448                        le16_to_cpu(us->pusb_dev->descriptor.idVendor),

449                        le16_to_cpu(us->pusb_dev->descriptor.idProduct),

450                        le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));

451   US_DEBUGP("InterfaceSubclass: 0x%02x, Protocol: 0x%02x\n",

452                        intf->cur_altsetting->desc.bInterfaceSubClass,

453                        intf->cur_altsetting->desc.bInterfaceProtocol);

454

455   /* Store our private data in theinterface */

456    usb_set_intfdata(intf,us);

457

458    /*Allocate the device-related DMA-mapped buffers */

459    us->cr= usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),

460                        GFP_KERNEL, &us->cr_dma);

461    if(!us->cr) {

462        US_DEBUGP("usb_ctrlrequest allocation failed\n");

463      return-ENOMEM;

464    }

465

466    us->iobuf= usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,

467                        GFP_KERNEL, &us->iobuf_dma);

468    if(!us->iobuf) {

469        US_DEBUGP("I/O buffer allocation failed\n");

470        return -ENOMEM;

471    }

472

473   us->sensebuf =kmalloc(US_SENSE_SIZE, GFP_KERNEL);

474   if (!us->sensebuf) {

475       US_DEBUGP("Sense buffer allocation failed\n");

476       return -ENOMEM;

477     }

478     return0;

479 }

我們首先來關注函數associate_dev的參數,struct us_data *us,傳遞給它的是us,這個不用多說了吧,此前剛剛爲它申請了內存,並且初始化各成員爲0。這個us將一直陪伴我們走下去,直到我們的故事結束。所以其重要性不言而喻。struct usb_interface *intf,storage_probe()函數傳進來的兩個參數之一。

總之,此處鄭重申明一次,structus_data的結構體指針us,struct usb_interface結構體的指針intf,以及struct usb_device結構體和struct usb_device_id結構體在整個U盤驅動的故事中是唯一的,每次提到都是那個。而以後我們會遇上的幾個重要的數據結構,struct urb urb,struct scsi_cmnd srb也非常重要,但是它們並不唯一,也許每次遇上都不一樣,就像演戲一樣。前邊這幾個數據結構的變量就像那些主角,而之後遇見的urb、srb,雖然頻繁露面,但是隻是羣衆演員,只不過這次是路人甲,下次是路人乙。所以,以後我們將只說us,不再說structus_data *us,structusb_interface * intf也將只用intf來代替。

us之所以重要,是因爲接下來很多函數都要用到它,以及它的各個成員。實際上目前這個函數associate_dev所做的事情就是爲us的各個成員賦值,畢竟此刻us和我們之前提到的那些函數struct usb_device,struct usb_interface,還沒有一點關係。因而,這個函數,以及這之後的好幾個函數都是爲了給us的各成員賦上適當的值,之所以如此興師動衆去爲它賦值,主要就是因爲後面要利用它。所謂天下沒有免費的午餐。

441行,本來無須多講,因爲只是一個debug語句,不過提一下__FUNCTION__這個宏,GCC 2.95以後的版本支持這個宏在編譯時會被轉換爲函數名(字符串),這裏自然就是“associate_dev”這個字符串,於是函數執行到這裏就會打印一句話告訴世人我們執行到這個函數來了,這種做法顯然會有利於調試程序。不過這個東西實際上不是宏,因爲預處理器對它一無所知。它的心只有編譯器才懂。

444行,pusb_dev,意思是point of usb device,struct us_data中的一個成員。按照我們剛纔約定的規矩,此刻我將說它是us的一個成員,us->pusb_dev=interface_to_usbdev(intf), interface_to_usbdev我們前面已經講過,其含義是把一個struct interface結構體的指針轉換成一個struct usb_device的結構體指針。前面說過,struct usb_device對我們沒有什麼用,但是USB Core層的一些函數要求使用這個參數,所以我們不得已而爲止,正所謂人在江湖身不由己。

445行,把intf賦給us的pusb_intf。

446行,us的ifnum,先看intf的cur_altsetting,這個容易令外行混淆。USB設備有一個配置,這個我們前面講協議時講了,而這裏又有一個setting(設置)。咋一看有些奇怪,這兩個詞不是一回事嗎?還是拿我們最熟悉的手機來打比方,配置不說了,只說設置。一個手機可能各種配置都確定了,如是振動還是鈴聲,各種功能都確定了,但是聲音的大小還可以改變,通常手機的音量是一格一格地變動,大概也就五六格,那麼這個可以算一個設置。這裏cur_altsetting就是表示的當前的這個設置。cur_altsetting是一個struct usb_host_interface的指針,這個結構體定義於include/linux/usb.h:

69 /* host-side wrapper for one interfacesetting's parsed descriptors */

70 struct usb_host_interface {

71   struct usb_interface_descriptordesc;

72

73    /* arrayof desc.bNumEndpoint endpoints associated with this

74     * interface setting.  these will be in no particular order.

75      */

76   struct usb_host_endpoint*endpoint;

77

78    char *string;           /*iInterface string, if present */

79    unsignedchar *extra;   /* Extradescriptors */

80    intextralen;

81 };

它的成員desc是一個struct usb_interface_descriptor結構體變量,這個結構體的定義是和USB協議直接對應的,定義於include/linux/usb/ch9.h。(這裏取名爲ch9是因爲這個文件中很多東西對應於USB spec 2.0中的第9章,chapter 9。)

294 /* USB_DT_INTERFACE: Interface descriptor */

295 struct usb_interface_descriptor {

296    __u8  bLength;

297   __u8  bDescriptorType;

298

299    __u8  bInterfaceNumber;

300    __u8  bAlternateSetting;

301  __u8 bNumEndpoints;

302    __u8  bInterfaceClass;

303    __u8  bInterfaceSubClass;

304    __u8  bInterfaceProtocol;

305    __u8  iInterface;

306 } __attribute__ ((packed));

而其中我們這裏提到的是bInterfaceNumber,一個設備可以有多個接口,於是每一個接口當然就得用一個編號了,要不然怎麼區分啊?所有這些描述符裏的東西都是出廠時就固化在設備中邊的,而我們這裏之所以可以用bInterfaceNumber來賦值,是因爲USB Core在爲設備初始化時就已經做足了這些功課,否則的話,我們真是寸步難行。

總之,us->ifnum就是這樣,最終就是等於咱們眼下這個接口的編號。

447行到453行就是兩句調試語句,打印更多一些描述符信息,包括設備描述符和接口描述符。

447行,usb_set_intfdata(),這其實是一個內聯函數,就一行代碼,也是定義於include/linux/usb.h中:

168 staticinline void usb_set_intfdata (struct usb_interface *intf, void *data)

169 {

170    dev_set_drvdata(&intf->dev,data);

171 } 

有趣的是,dev_set_drvdata這個函數也是內聯函數,也只有一行代碼,它定義於include/linux/device.h中:

491 static inline void
  492dev_set_drvdata (struct device *dev, void *data)
  493{
  494   dev->driver_data = data;
  495}

所以,結合來看,最終做的事情就是讓&intf->dev->driver_data=data,即&intf->dev->driver_data=us。

再往下走,就是申請內存了,us->cr和us->iobuf都是指針,這裏讓它們指向兩段內存空間,下面會用得到。需要注意的是,usb_buffer_alloc()這個函數是USB Core提供的,我們只管調用即可。從名字上就能知道它是用來申請內存的,第一個參數就是struct usb_device結構體的指針,所以我們要傳遞一個pusb_dev。第三個參數,GFP_KERNEL是一個內存申請的flag,通常內存申請都用這個flag,除非是中斷上下文,不能睡眠,那就得用GPF_ATOMIC,這裏沒那麼多要求。第二個參數申請的buffer的大小,對於cr,傳遞的是sizeof(*us->cr),而對於iobuf,傳遞的是US_IOBUF_SIZE,這是一個宏,大小是64,是我們自己定義的,來自drivers/usb/storage/usb.h:

90 #define US_IOBUF_SIZE       64  /* Size of the DMA-mapped I/O buffer */

91 #define US_SENSE_SIZE       18   /* Size of the autosense data buffer */

而usb_buffer_alloc()的第四個參數很有意思,第一次我們傳遞的是&us->cr_dma,第二次傳遞的是&us->iobuf_dma,這涉及DMA傳輸。這兩個參數此前我們都沒有賦過值,相反它們是在這個函數調用之後被賦上值的。cr_dma和iobuf_dma都是dma_addr_t類型的變量,這個數據類型是Linux內核中專門爲DMA傳輸而準備的。爲了支持DMA傳輸,usb_buffer_alloc不僅僅是申請了地址,並且建立了DMA映射,cr_dma和iobuf_dma就是記錄着cr和iobuf的dma地址。關於什麼是cr,關於這些DMA地址究竟有什麼用,我們稍後就會遇到,那時候再講也不遲。現在需要知道的就是usb_buffer_alloc申請的空間分別返回給了cr和iobuf。順便提一下,用usb_buffer_alloc申請的內存空間需要用它的搭檔usb_buffer_free()來釋放。

461行和468行,每一次申請完內存就要檢查成功與否,這是慣例。驅動程序能否驅動設備,關鍵就是看能否申請到內存空間,任何一處內存空間申請失敗,整個驅動程序就沒法正常工作。

此外我們還看到473行,我們還爲us->sensebuf申請了內存,關於sense buffer,等到講SCSI命令數據傳輸時我們再來看,現在還不需要了解。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章