1. 程式人生 > >《Linux那些事兒之我是USB》我是U盤(21)傳說中的URB

《Linux那些事兒之我是USB》我是U盤(21)傳說中的URB

有人問,怎麼寫一個驅動寫這麼久啊?

的確,一路走來,大家都不容易,但既然已經走到今天,我們能做的也只有是堅持下去。

usb_stor_acquire_resources(),從名字上來看是獲取資源。什麼是資源?之前不是申請了一大堆記憶體了嗎?寫個USB裝置驅動程式怎麼這麼麻煩啊?不是專門為USB Mass Storage裝置準備了一個struct us_data這麼一個結構體了嗎?不是說故事已經到高潮了嗎?

如果你以為看到這裡你已經對USB裝置驅動程式有了足夠的認識,認為接下來的程式碼已經沒有必要再分析了,那麼,我只想說,上帝創造世界的計劃中,未必包括使你會寫USB裝置驅動程式。

的確,別看usb_stor_acquire_resources的程式碼不多,每一行都有每一行的故事。本節我們只講其中的一行程式碼,沒錯,就是一行程式碼,因為我們需要隆重推出一個名詞,一個響噹噹的名字,它就是傳說中的“urb”,全稱USB RequestBlock。USB裝置需要通訊,要傳遞資料,就需要使用urb,確切地說,應該是USB裝置驅動程式使用urb。實際上,作為USB裝置驅動,它本身並不能直接操縱資料的傳輸,在USB這個大觀園裡,外接裝置永遠都是配角,真正的核心只是USB Core,而真正負責排程的是USB主機控制。這個通常看不見的USB主機控制器晶片,儼然是USB大觀園中的大管家。裝置驅動要傳送資訊,所需要做的是建立一個urb資料結構,並把這個資料結構交給核心層,而核心層會為所有裝置統一完成排程,而裝置在提交了urb之後需要做的,只是等待。別急,我們慢慢來。

784行,一條賦值語句,等號左邊us->current_urb,等號右邊usb_alloc_urb()函式被呼叫。如果說struct us_data是usb mass storage中的主角,那麼struct urb將毫無爭議地成為整個USB子系統中的主角。Linux中所有的USB裝置驅動,都必然也必須要使用urb。那麼urb究竟長成什麼樣呢?在include/linux/usb.h中能找到它:

1126 struct urb

1127 {

1128   /* private: usb core and hostcontroller only fields in the urb */

1129   struct kref kref;              /* reference count of the URB */

1130  spinlock_t lock;               /* lock for the URB */

1131   void *hcpriv;                  /* private data for host controller */

1132    atomic_tuse_count;            /* concurrent submissions counter */

1133   u8 reject;                     /* submissions will fail */

1134

1135   /* public: documented fields inthe urb that can be used by drivers*/

1136   struct list_head urb_list;      /* list headfor use by the urb's

1137                                         * current owner */

1138  struct usb_device *dev;       /* (in)pointer to associated device */

1139    unsignedint pipe;             /* (in) pipe information */

1140    intstatus;                    /* (return) non-ISO status */

1141   unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK |...*/

1142    void*transfer_buffer;          /* (in)associated data buffer */

1143 dma_addr_ttransfer_dma;     /*(in) dma addr for transfer_buffer */

1144  int transfer_buffer_length;     /* (in) data bufferlength */

1145   int actual_length;             /* (return) actual transfer length */

1146  unsigned char *setup_packet;  /* (in) setup packet (control only) */

1147    dma_addr_tsetup_dma;           /* (in) dma addr for setup_packet*/

1148    intstart_frame;               /* (modify) start frame (ISO) */

1149    intnumber_of_packets;          /* (in)number of ISO packets */

1150    intinterval;                  /* (modify) transfer interval

1151                                         * (INT/ISO) */

1152   int error_count;               /* (return) number of ISO errors */

1153    void*context;                 /* (in) context for completion */

1154    usb_complete_tcomplete;        /* (in) completionroutine */

1155  struct usb_iso_packet_descriptoriso_frame_desc[0];

1156                                        /* (in) ISO ONLY */

1157 };

我們常常抱怨,Linux核心原始碼中註釋太少了,以至於我們常常看不懂那些程式碼究竟是什麼含義。但urb讓開發人員做足了文章,結構體struct urb的定義不過30行,而說明文字卻用了足足160餘行。可見urb的地位。當然我們這裡貼出來主要還是為了保持原汁原味,另一方面這個註釋也說得很清楚,對於每一個成員都解釋了,而我們接下來將必然要用到urb的許多個成員。

此刻,我們暫時不去理會這個結構體每一個元素的作用,只需要知道,一個urb包含了執行USB傳輸所需要的所有資訊。而作為驅動程式,要通訊就必須建立這麼一個數據結構,並且賦值,顯然不同型別的傳輸,需要對urb賦不同的值,然後將她提交給底層,完了底層的USB Core會找到相應的USB主機控制器,從而具體實現資料的傳輸。傳輸完了之後,USB主機控制器會通知裝置驅動程式。

總之我們知道,784行就是呼叫usb_alloc_urb()申請了一個struct urb結構體。關於usb_alloc_urb()這個函式,我們不打算講,它是USB Core所提供的一個函式,來自drivers/usb/core/urb.c,USB開發人員的確是給足了urb的面子,專門把和這個資料結構相關的程式碼整理在這麼一個檔案中了。我們可以在include/linux/usb.h中找到這個函式的宣告:

1266 extern struct urb *usb_alloc_urb(intiso_packets, gfp_t mem_flags);

這個函式的作用很明顯,就是為一個urb結構體申請記憶體。它有兩個引數,其中第一個iso_packets用來在等時傳輸的方式下指定你需要傳輸多少個包,對於非等時模式來說,這個引數直接使用0。另一個引數mem_flags就是一個flag,表示申請記憶體的方式,這個flag將最終傳遞給kmalloc函式,我們這裡傳遞的是GFP_KERNEL,這個flag是記憶體申請中最常用的,我們之前也用過,在為us申請記憶體時。usb_alloc_urb最終將返回一個urb指標,而us的成員current_urb也是一個struct urb的指標,所以就賦給它了。不過需要記住,usb_alloc_urb除了申請記憶體以外,還對結構體做了初始化,結構體urb被初始化為0,雖然這裡我們沒有把這個函式的程式碼“貼”出來,但你也千萬不要以為寫程式碼的人跟我似的,申請變數還能忘了初始化。同時,struct urb中還有一個引用計數,以及一個自旋鎖,這些也同樣被初始化了。

所以,接下來我們就將要和us->current_urb打交道了。如果你對urb究竟怎麼用還有些困惑的話,可以檢視主機控制器驅動的程式碼。如果你不想看,那麼我可以用一種你最能接受的方式告訴你,USB是一種匯流排,是匯流排它就要通訊。我們現實生活中真正要使用的是裝置,但是光有裝置還不足以實現USB通訊,於是世界上有了USB主機控制器,它來負責統一排程。這就好比城市的交警,這個城市裡真正需要的本來是車輛和行人,而光有車輛和行人,沒有交警,那麼這個城市裡的車輛和行人必將亂套。於是誕生了交警這個行業,交警站在路口統一來管理排程混亂的交通。假如車輛和行人可以完全自覺遵守某種規矩而來來往往於這個城市的每一個角落及每一個路口,那麼交警就沒有必要存在了。同樣,假如裝置能夠完全自覺地傳遞資訊,每一個數據包都能到達它應該去的地方,那麼我們根本就不需要有主機控制器。然而,事實上總會有不遵守交通規則的人。同樣,在USB的世界中,裝置也總是那麼不守規矩,我們必須要設計一個東西出來管理來控制所有的USB裝置的通訊,這樣,主機控制器就橫空出世了。

那麼裝置和主機控制器的分工又是如何呢?硬體實現上我們就不說了,說點兒具體的,在Linux中,裝置驅動程式只要為每一次請求準備一個urb結構體變數,把它填充好(就是說賦上該賦的值),然後它呼叫USB Core提供的函式,把這個urb傳遞給主機控制器,主機控制器就會把各個裝置驅動程式所提交的urb統一規劃,去執行每一個操作。而這期間,USB裝置驅動程式通常會進入睡眠,而一旦主機控制器把urb要做的事情給做完了,它會呼叫一個函式去喚醒USB裝置驅動程式,然後USB裝置驅動程式就可以繼續往下走了。

這又好比我們學校裡的師生關係。考試時,我們只管把試卷填好,然後我們交給老師,然後老師拿去批改試卷,這期間我們除了等待別無選擇,等待老師改完了試卷,告訴了我們分數,我們又繼續我們的生活。同樣,USB裝置驅動程式也是如此,如果urb提交給USB主機了,但是最終卻沒有成功執行,那麼也許該USB裝置驅動程式的生命也就提前結束。不過這都是後話,現在只要有一個感性認識即可,稍後看到了就能更深刻的體會了,這種崗位分工的方式給我們編寫裝置驅動程式帶來了巨大的方便。

繼續usb_stor_acquire_resources函式。

785行到788行,就是剛才urb申請了之後判斷是否申請成功了,如果指標為NULL那麼就是失敗了,直接返回-ENOMEM。

792行,us->unusual_dev->initFunction是什麼?在分析unusual_devs.h檔案時曾經專門舉過例子的,說有些裝置需要一些初始化函式,它就定義在unusual_devs.h檔案中,而我們通過UNUSUAL_DEV的定義已經把這些初始化函式賦給了us->unusual_dev的initFunction指標了。所以這時候,在傳輸開始之前,我們判斷是不是有這樣一個函式,即這個函式指標是否為空,如果不為空,很好辦,執行這個函式就行了。比如當時我們舉例子時說的那兩個裝置就有初始化函式,那麼就讓它執行好了。當然,一般的裝置肯定不需要這麼一個函式。至於傳遞給這個函式的引數,在struct us_unusual_dev結構體定義時,就把這個函式需要什麼樣的引數定義好了,需要的就是一個struct us_data *,那麼很自然,傳遞的就是us。

至此,我們終於走到了usb_stor_acquire_resources()中第799行,即將見到這個千呼萬喚始出來的核心精靈。