(江西財(cái)經(jīng)大學(xué) 現(xiàn)代經(jīng)濟(jì)管理學(xué)院,南昌330013)
Linux內(nèi)核支持兩種主要類型的USB驅(qū)動(dòng)程序:宿主(host)系統(tǒng)上的驅(qū)動(dòng)程序和設(shè)備(device)上的驅(qū)動(dòng)程序。從宿主的觀點(diǎn)來看(一個(gè)普通的USB宿主是一個(gè)桌面計(jì)算機(jī)),宿主系統(tǒng)的USB驅(qū)動(dòng)層次控制插入其中的USB設(shè)備,而USB設(shè)備的驅(qū)動(dòng)程序控制該設(shè)備如何作為一個(gè)USB設(shè)備和主機(jī)通信。設(shè)備驅(qū)動(dòng)程序一般存放在內(nèi)核的drivers/usb/gadget目錄中,針對(duì)某個(gè)具體設(shè)備的USB驅(qū)動(dòng)程序一般都是指宿主設(shè)備驅(qū)動(dòng),本文的USB讀卡器驅(qū)動(dòng)就是這一類。在Linux內(nèi)核中,USB設(shè)備驅(qū)動(dòng)是分很多層的,驅(qū)動(dòng)層只是整個(gè)框架中的一小部分,要所有層的配合才能將設(shè)備驅(qū)動(dòng)起來。USB驅(qū)動(dòng)程序存在于不同的內(nèi)核子系統(tǒng)(塊設(shè)備、網(wǎng)絡(luò)設(shè)備、字符設(shè)備等)和USB硬件控制器中。
USB核心為USB驅(qū)動(dòng)程序提供了一個(gè)用于訪問和控制USB硬件的接口,而不必考慮系統(tǒng)當(dāng)前存在的各種不同類型的USB硬件控制器,在USB 核心中對(duì)USB需要使用的各種資源進(jìn)行分配及初始化,如注冊(cè)USB總線usb_bus_type、初始化USB主控制器、初始化USB HUB等;同時(shí)還提供了一些接口給其它層使用,如usb_alloc_dev函數(shù)(當(dāng)有USB設(shè)備插入時(shí),用于分配并初始化usb_device)。USB控制器主要負(fù)責(zé)處理各個(gè)USB設(shè)備的通信,解析驅(qū)動(dòng)發(fā)送給每個(gè)設(shè)備的請(qǐng)求,并按照請(qǐng)求通知USB設(shè)備進(jìn)行相應(yīng)的處理,同時(shí)發(fā)送處理結(jié)果給驅(qū)動(dòng)程序,并調(diào)用驅(qū)動(dòng)傳入的complete函數(shù)。
USB設(shè)備是一個(gè)非常復(fù)雜的東西。如圖1所示,USB設(shè)備由配置、接口、端點(diǎn)組成,而USB驅(qū)動(dòng)是綁定到接口上的,每個(gè)接口對(duì)應(yīng)于一個(gè)設(shè)備驅(qū)動(dòng),對(duì)于某些多接口的設(shè)備(如帶音頻接口的USB鍵盤),此設(shè)備就同時(shí)需要USB鍵盤和USB音頻這2個(gè)驅(qū)動(dòng)。
圖1 USB設(shè)備框架
如圖1所示,一個(gè)USB設(shè)備通常包含一個(gè)或多個(gè)配置,一個(gè)配置通常包含一個(gè)或多個(gè)接口,一個(gè)接口通常包含0個(gè)或多個(gè)端點(diǎn)。層次結(jié)構(gòu)如圖2所示。
圖2 USB設(shè)備層次結(jié)構(gòu)
(1)端點(diǎn)
USB通信的最基本形式是通過端點(diǎn)來實(shí)現(xiàn)的,驅(qū)動(dòng)和接口的通信都是通過端點(diǎn),一個(gè)接口可以有0個(gè)或多個(gè)端點(diǎn),端點(diǎn)根據(jù)傳輸方式的不同分為以下4種:控制端點(diǎn)、中斷端點(diǎn)、等時(shí)端點(diǎn)和批量端點(diǎn),分別對(duì)應(yīng)于4種不同傳輸方式。
控制端點(diǎn)主要通過向USB設(shè)備發(fā)送請(qǐng)求來設(shè)置或獲取USB設(shè)備的配置、狀態(tài)等信息,每個(gè)USB設(shè)備都有一個(gè)名為“端點(diǎn)0”的控制端點(diǎn),USB核心使用該端點(diǎn)在插入時(shí)進(jìn)行設(shè)備的配置;中斷端點(diǎn)主要實(shí)現(xiàn)以一個(gè)固定的速率來傳輸少量的數(shù)據(jù),中斷端點(diǎn)是USB鍵盤、鼠標(biāo)等設(shè)備所使用的主要傳輸方式;批量端點(diǎn)用于傳輸大量數(shù)據(jù),這些端點(diǎn)通常比中斷端點(diǎn)大得多,常用于需要確保沒有數(shù)據(jù)丟失的設(shè)備(如打印機(jī)、存儲(chǔ)設(shè)備、網(wǎng)絡(luò)設(shè)備);等時(shí)端點(diǎn)同樣可以傳輸大批量數(shù)據(jù),當(dāng)數(shù)據(jù)是否到達(dá)沒有保證,等時(shí)端點(diǎn)用于可以應(yīng)付數(shù)據(jù)丟失的設(shè)備,這類設(shè)備更注重于保持一個(gè)恒定的數(shù)據(jù)流(如USB音頻、視頻)。
(2)接口
USB端點(diǎn)被捆綁為接口,USB接口對(duì)應(yīng)于一個(gè)設(shè)備驅(qū)動(dòng),對(duì)應(yīng)于一種功能的設(shè)備,對(duì)于多接口的復(fù)合設(shè)備就需要多個(gè)驅(qū)動(dòng)。
(3)配置
USB接口被捆綁為配置,一個(gè)USB設(shè)備可以有多個(gè)配置,而且可以在配置之間切換已改變?cè)O(shè)備的狀態(tài)。例如一些允許下載固件到其上的設(shè)備包含多個(gè)配置以完成這個(gè)工作,而某一個(gè)時(shí)刻只能激活一個(gè)配置。
Linux內(nèi)核中的USB代碼通過一個(gè)稱為urb(USB請(qǐng)求塊)的東西和所有的USB設(shè)備通信。這個(gè)請(qǐng)求塊使用struct urb結(jié)構(gòu)體來描述,可以從include/linux/usb.h文件中找到。urb被用來以一種異步的方式向/從特定的USB 設(shè)備上的特定USB端點(diǎn)發(fā)送/接收數(shù)據(jù)。USB設(shè)備驅(qū)動(dòng)程序可能會(huì)為單個(gè)端點(diǎn)分配許多urb,也可能對(duì)許多不同的端點(diǎn)重用單個(gè)的urb,這取決于驅(qū)動(dòng)程序的需要。設(shè)備中的每個(gè)端點(diǎn)都可以處理一個(gè)urb隊(duì)列,所以多個(gè)urb可以在隊(duì)列為空之前發(fā)送到同一個(gè)端點(diǎn)。一個(gè)urb的典型生命周期如下:
①由USB設(shè)備驅(qū)動(dòng)程序創(chuàng)建;②分配給一個(gè)特定USB 設(shè)備的特定端點(diǎn);③由USB設(shè)備驅(qū)動(dòng)程序遞交到USB 核心;④由USB核心遞交到特定設(shè)備的特定USB 主控制器驅(qū)動(dòng)程序;⑤由USB主控制器驅(qū)動(dòng)程序處理,從設(shè)備進(jìn)行USB 傳送;⑥當(dāng)urb結(jié)束之后,USB主控制器驅(qū)動(dòng)程序通知USB設(shè)備驅(qū)動(dòng)程序。
urb可以在任何時(shí)刻被遞交該urb的驅(qū)動(dòng)程序取消掉,或者被USB核心取消,如果該設(shè)備已從系統(tǒng)中移除。urb被動(dòng)態(tài)地創(chuàng)建,它包含一個(gè)內(nèi)部引用計(jì)數(shù),使得它們可以在最后一個(gè)使用者釋放它們時(shí)自動(dòng)地銷毀。
struct urb結(jié)構(gòu)體不能在驅(qū)動(dòng)程序中或者另一個(gè)結(jié)構(gòu)體中靜態(tài)地創(chuàng)建,因?yàn)檫@樣會(huì)破壞USB核心對(duì)urb所使用的引用計(jì)數(shù)機(jī)制。它必須使用usb_alloc_urb函數(shù)來創(chuàng)建。該函數(shù)原型如下:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
第一個(gè)參數(shù)iso_packets是該urb應(yīng)該包含的等時(shí)數(shù)據(jù)包的數(shù)量。如果不打算創(chuàng)建等時(shí)urb,該值應(yīng)該設(shè)置為0。第二個(gè)參數(shù)mem_flags和傳遞給用于從內(nèi)核分配內(nèi)存的kmalloc函數(shù)的標(biāo)志有相同的類型。如果該函數(shù)成功地為urb分配了足夠的內(nèi)存空間,指向該urb的指針將被返回給調(diào)用函數(shù)。如果返回值為NULL,說明USB核心內(nèi)發(fā)生了錯(cuò)誤,驅(qū)動(dòng)程序需要進(jìn)行適當(dāng)?shù)那謇怼?/p>
當(dāng)一個(gè)urb被創(chuàng)建之后,在它可以被USB核心使用之前必須被正確地初始化。
驅(qū)動(dòng)程序必須調(diào)用usb_free_urb函數(shù)來告訴USB核心驅(qū)動(dòng)程序已經(jīng)使用完urb。該函數(shù)只有一個(gè)參數(shù):
void usb_free_urb(struct urb *urb);
這個(gè)參數(shù)指向所需釋放的struct urb的指針。在該函數(shù)被調(diào)用之后,urb結(jié)構(gòu)體就消失了,驅(qū)動(dòng)程序不能再訪問它。
一旦urb被USB驅(qū)動(dòng)程序正確地創(chuàng)建和初始化之后,就可以提交到USB核心以發(fā)送到USB設(shè)備了。這是通過調(diào)用usb_submit_urb函數(shù)來完成的:
int usb_submit_urb(struct urb *urb, int mem_flags);
urb參數(shù)是指向即將被發(fā)送到設(shè)備的urb的指針。mem_flags參數(shù)等同于傳遞給kmalloc調(diào)用的同一個(gè)參數(shù),用于通知USB核心如何在此時(shí)及時(shí)地分配內(nèi)存緩沖區(qū)。
當(dāng)一個(gè)urb被成功地提交到USB核心之后,在接收函數(shù)被調(diào)用之前不能訪問該urb結(jié)構(gòu)體中的任何字段。因?yàn)閡sb_submit_urb函數(shù)可以在任何時(shí)刻調(diào)用(包括從一個(gè)中斷上下文中),mem_flags變量的內(nèi)容必須是正確的。其實(shí)只有三個(gè)有效的值可以被使用,取決于usb_submit_urb何時(shí)被調(diào)用:
(1)GFP_ATOMIC
只要下列條件成立就應(yīng)該使用該值:
①調(diào)用者是在一個(gè)urb結(jié)束處理例程、中斷處理例程、底半部、tasklet或者定時(shí)器回調(diào)函數(shù)中。
②調(diào)用者正持有一個(gè)自旋鎖或讀寫鎖,注意如果持有了信號(hào)量,該值就不需要了。
③current->state不是TASK_RUNNING,該狀態(tài)永遠(yuǎn)是TASK_RUNNING,除非驅(qū)動(dòng)程序自己改變了當(dāng)前的狀態(tài)。
(2)GFP_NOIO
如果驅(qū)動(dòng)程序處于塊I/O路徑中應(yīng)該使用該值,在所有存儲(chǔ)類型的設(shè)備的錯(cuò)誤處理路徑中也應(yīng)該使用它。
(3)GFP_KERNEL
該值應(yīng)該在前述類別之外的所有情況中使用。
在寫USB驅(qū)動(dòng)前,首先需要確定當(dāng)前設(shè)備的一些基本信息,例如當(dāng)前設(shè)備屬于哪類設(shè)備,有哪些接口,每個(gè)接口有哪些端點(diǎn),設(shè)備、接口、端點(diǎn)、配置描述符的信息是什么,這些信息都可以通過軟件來獲取,常用軟件有windriver、USBtrace、bushound等,其中windriver不但可以看到設(shè)備的相關(guān)信息,還可以向設(shè)備發(fā)送標(biāo)準(zhǔn)請(qǐng)求,也可以直接向端點(diǎn)傳輸數(shù)據(jù),通過此工具也可以驗(yàn)證寫好的驅(qū)動(dòng)通信是否正確。
U盤一般提供4個(gè)端點(diǎn):一個(gè)控制端點(diǎn)、一個(gè)中斷端點(diǎn)、兩個(gè)批量端點(diǎn),進(jìn)入每個(gè)端點(diǎn)后就可以對(duì)各個(gè)端點(diǎn)進(jìn)行操作了,對(duì)于控制端點(diǎn)可以對(duì)其發(fā)送各種USB標(biāo)準(zhǔn)請(qǐng)求,這里就可以發(fā)送獲取描述符的請(qǐng)求了,對(duì)于兩個(gè)bulk端點(diǎn)可以直接對(duì)其進(jìn)行讀寫。
剛剛拿到讀卡器,首先當(dāng)然是要獲取USB讀卡器的相關(guān)信息,通過工具USBtrace獲取各描述符信息如表1所列。
表1 USBtrace獲取各描述符信息
續(xù)表1
從這張表中可以獲取到一些有用的信息,USB設(shè)備的venderID為0x3EB,deviceID為0x6124,設(shè)備有兩個(gè)接口,第一個(gè)接口的類型為0x2,即Communications and CDC Control,這是一個(gè)通信類的接口,子類型為0x2 (Abstract Control Model),這個(gè)接口提供了1個(gè)端點(diǎn),端點(diǎn)類型為中斷端點(diǎn),方向?yàn)閕n。第二個(gè)接口類型為0xa(CDC DATA),這個(gè)接口一般用于傳輸數(shù)據(jù),所以這個(gè)結(jié)構(gòu)提供2個(gè)bulk端點(diǎn),一個(gè)作為輸入,一個(gè)作為輸出。當(dāng)然每個(gè)設(shè)備都會(huì)有控制端點(diǎn),控制端點(diǎn)既可作為輸入又可作為輸出。每個(gè)端點(diǎn)都是最大允許傳輸數(shù)據(jù)大小,2個(gè)bulk端點(diǎn)最大可傳輸64 B,而中斷端點(diǎn)最大8 B。至此信息基本獲取完畢,開始寫驅(qū)動(dòng)。
首先注冊(cè)u(píng)sb_driver到設(shè)備模型,前面講了是通過函數(shù)usb_register實(shí)現(xiàn)的,下面就是實(shí)現(xiàn)usb_driver了,在這里只實(shí)現(xiàn)了4個(gè)成員:id_table、probe、disconnect、name。id_table就是利用前面獲取的venderID、deviceID來判斷設(shè)備,probe和disconnect就是相應(yīng)的初始化和卸載函數(shù)。
2.2.1 初始化、卸載設(shè)備
初始化工作在probe函數(shù)中實(shí)現(xiàn),主要就是針對(duì)前面獲取的端點(diǎn)信息,對(duì)每個(gè)端點(diǎn)進(jìn)行初始化。首先要獲取設(shè)備的端點(diǎn),從設(shè)備描述符中看到設(shè)備有2個(gè)bulk端點(diǎn)和1個(gè)interrupt端點(diǎn)。Probe函數(shù)傳入的是當(dāng)前接口,即一個(gè)usb_interface結(jié)構(gòu)體,如何從usb_interface中獲取端點(diǎn)信息呢?在usb_interface結(jié)構(gòu)中有個(gè)成員cur_altsetting,表示當(dāng)前設(shè)置,這是一個(gè)usb_host_interface結(jié)構(gòu)體,其中又有一個(gè)成員endpoint,這是一個(gè)usb_host_endpoint結(jié)構(gòu)體的數(shù)組,每一個(gè)元素代表一個(gè)端點(diǎn),但這里不保存控制端點(diǎn),因?yàn)榭刂贫它c(diǎn)是被單獨(dú)保存在設(shè)備結(jié)構(gòu)體usb_device中的。
找到每個(gè)端點(diǎn)后開始為其創(chuàng)建相應(yīng)管道。USB通信中有4種端點(diǎn):控制、等時(shí)、中斷、批量,同時(shí)對(duì)應(yīng)4種傳輸方式,也對(duì)應(yīng)了4種管道,其中4種管道中又分in或out管道,內(nèi)核中提供了8個(gè)宏來創(chuàng)建管道,定義如下:
#defineusb_sndctrlpipe(dev,endpoint)/*創(chuàng)建out控制管道*/
((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))
#defineusb_rcvctrlpipe(dev,endpoint)/*創(chuàng)建in控制管道*/
((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
#defineusb_sndisocpipe(dev,endpoint)/*創(chuàng)建out等時(shí)管道*/
((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))
#defineusb_rcvisocpipe(dev,endpoint)/*創(chuàng)建in等時(shí)管道*/
((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) |USB_DIR_IN)
#defineusb_sndbulkpipe(dev,endpoint)/*創(chuàng)建out批量管道*/
((PIPE_BULK << 30) | __create_pipe(dev,endpoint))
#defineusb_rcvbulkpipe(dev,endpoint)/*創(chuàng)建in批量管道*/
((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
#defineusb_sndintpipe(dev,endpoint)/*創(chuàng)建out中斷管道*/
((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))
#defineusb_rcvintpipe(dev,endpoint)/*創(chuàng)建in中斷管道*/
((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
其中__create_pipe也是一個(gè)宏,定義如下:
static inline unsigned int __create_pipe(struct usb_device *dev,
unsigned int endpoint){
return (dev->devnum << 8) | (endpoint << 15);
}
從以上幾個(gè)宏可以知道管道的組成,其實(shí)管道就是提供了一個(gè)通信地址,讓HC知道這個(gè)urb包應(yīng)該發(fā)到哪個(gè)設(shè)備、哪個(gè)端點(diǎn)、是什么類型的包。在這里看到了4個(gè)宏,其中PIPE_ISOCHRONOUS就是標(biāo)志等時(shí)通道、PIPE_INTERRUPT就是中斷通道、PIPE_CONTROL就是控制通道、PIPE_BULK 就是BULK通道。
在內(nèi)核里使用一個(gè)unsigned int類型的變量來表征一個(gè)pipe,其中8~14位是設(shè)備號(hào),即devnum,15~18位是端點(diǎn)號(hào),即endpoint。宏USB_DIR_IN用來在pipe里面標(biāo)志數(shù)據(jù)傳輸方向,一個(gè)管道要么只能輸入,要么只能輸出。在pipe里面,第7位(bit 7)是表征方向的。所以這里0x80也就是說讓bit 7 為1,這就表示傳輸方向是由設(shè)備向主機(jī),也就是所謂的in,而如果這一位是0,就表示傳輸方向是由主機(jī)向設(shè)備的,也就是所謂的out。正是因?yàn)閁SB_DIR_OUT是0,而USB_DIR_IN是1,所以定義管道的時(shí)候只用到了USB_DIR_IN,而沒有用到USB_DIR_OUT,因?yàn)樗?,任何數(shù)和0相或都沒有意義。
圖3 循環(huán)緩沖區(qū)
接下來為每一個(gè)端點(diǎn)分配一個(gè)緩沖區(qū),這個(gè)緩沖區(qū)就是urb結(jié)構(gòu)體中的transfer_buffer,這個(gè)緩沖區(qū)就是傳輸過程中用來保存數(shù)據(jù)的,這時(shí)使用的分配函數(shù)是usb_buffer_alloc,這個(gè)函數(shù)的作用就是先分配一段內(nèi)存空間,然后對(duì)其進(jìn)行dma映射,就是從dma緩沖池分配一段內(nèi)存,即usb_hcd->pool[i]。
最后將接口注冊(cè)到字符設(shè)備層,通過函數(shù)usb_register_dev實(shí)現(xiàn),由于客戶需要定位每個(gè)USB口,這里筆者做了一個(gè)封裝,做了一點(diǎn)點(diǎn)簡(jiǎn)單的改動(dòng),下面會(huì)講到。
2.2.2 讀、寫數(shù)據(jù)
所謂讀、寫設(shè)備就是實(shí)現(xiàn)file_operations中的read、write函數(shù)。
這里的read、write就是實(shí)現(xiàn)對(duì)usb的讀、寫數(shù)據(jù)和USB通信,這里在一個(gè)接口函數(shù)基礎(chǔ)上筆者做了一個(gè)簡(jiǎn)單的封裝,根據(jù)實(shí)際需求改進(jìn)了一點(diǎn)小地方,函數(shù)聲明如下:
int usb_reader_bulk_msg(struct urb *urb, struct usb_reader_data *usb_reader, unsigned int pipe,void *data, int len,int *actual_length, int timeout);
函數(shù)傳入7個(gè)參數(shù),第一個(gè)參數(shù)為需要發(fā)送的urb包,這個(gè)urb在probe函數(shù)中已分配好內(nèi)存,usb_reader就是讀卡器相關(guān)信息結(jié)構(gòu)體,這是為該驅(qū)動(dòng)專門定義的結(jié)構(gòu)體,data是傳輸數(shù)據(jù)的緩沖區(qū),len為希望傳輸多少數(shù)據(jù),actual_length為實(shí)際傳輸?shù)臄?shù)據(jù)大小,timeout為傳輸?shù)某瑫r(shí)值,若在timeout內(nèi)未完成傳輸則返回傳輸失敗。
這個(gè)函數(shù)主要實(shí)現(xiàn)填充傳入的urb,主要填充管道、緩沖區(qū)、傳輸數(shù)據(jù)長(zhǎng)度、dma緩沖區(qū)、complete函數(shù)等成員,最后通過usb_submit_urb將urb提交到HC層做處理,usb_submit_urb在講HC層時(shí)已經(jīng)分析過了,這里只要提交過去就行了。
在實(shí)現(xiàn)讀寫函數(shù)時(shí),使用了一個(gè)4 096 B大小的循環(huán)緩沖區(qū),每次應(yīng)用程序調(diào)用write時(shí),先將應(yīng)用程序傳入的數(shù)據(jù)寫到USB讀卡器,然后馬上從讀卡器上讀取返回?cái)?shù)據(jù),并將返回?cái)?shù)據(jù)寫到緩沖區(qū)中。當(dāng)應(yīng)用程序調(diào)用read時(shí),將循環(huán)緩沖區(qū)的數(shù)據(jù)返回給用戶,而不是直接從設(shè)備讀取數(shù)據(jù)再返回。這樣處理的好處就是允許用戶進(jìn)行多次連續(xù)寫入操作,最后只要通過一個(gè)讀取操作就能把前幾次寫入操作的結(jié)果全部讀取出去。
循環(huán)緩沖區(qū)寫入時(shí)將數(shù)據(jù)存入數(shù)組的尾部,讀取時(shí)從數(shù)組的另一端開始,當(dāng)寫入數(shù)據(jù)到達(dá)數(shù)組的尾部時(shí),回到數(shù)組頭部繼續(xù)寫。因此,一個(gè)循環(huán)緩沖區(qū)需要一個(gè)數(shù)組以及兩個(gè)索引值:一個(gè)用于下一個(gè)要寫入的數(shù)據(jù)的位置,另一個(gè)用于指定下一個(gè)要從緩沖區(qū)中移走的位置。
如圖3所示,這個(gè)緩存被定義成一個(gè)空情況,由讀寫指針相同來指示, 而滿情況發(fā)生在寫指針緊跟在讀指針后面的時(shí)候(小心解決繞回!)。
2.2.3 ioctl函數(shù)
這里還提供了一個(gè)ioctl接口,主要用于實(shí)現(xiàn)USB的標(biāo)準(zhǔn)請(qǐng)求,USB標(biāo)準(zhǔn)請(qǐng)求都是通過控制端點(diǎn)來實(shí)現(xiàn)的,控制端點(diǎn)的管道創(chuàng)建比較特殊,最后一個(gè)參數(shù)為0,創(chuàng)建函數(shù)如下:
usb_rcvctrlpipe(usb_reader->dev, 0);
usb_sndctrlpipe(usb_reader->dev, 0);
標(biāo)準(zhǔn)請(qǐng)求和請(qǐng)求參數(shù)通過setup包傳送給USB設(shè)備,一個(gè)setup包由8個(gè)字節(jié)組成,在內(nèi)核中用usb_ctrlrequest結(jié)構(gòu)體表示,結(jié)構(gòu)體定義如下:
struct usb_ctrlrequest{
__u8bRequestType;
__u8bRequest;
__le16wValue;
__le16wIndex;
__le16wLength;
} __attribute__((packed));
usb_ctrlrequest被保存在urb->setup_packet里,共8個(gè)字節(jié),意義如下:
byte0:bmRequestType,注意在剛才代碼中數(shù)據(jù)結(jié)構(gòu)struct ctrlrequest 里邊寫的是bRequestType,但是它們對(duì)應(yīng)的是相同的內(nèi)容。而之所以USB協(xié)議里寫成bmRequestType,是因?yàn)樗鼘?shí)際上又是一個(gè)位圖(m表示map),也就是說,盡管它只有1個(gè)字節(jié),但是仍然被當(dāng)作8位來用。
USB協(xié)議定義了11個(gè)標(biāo)準(zhǔn)請(qǐng)求,各請(qǐng)求對(duì)應(yīng)setup包的各成員的值。
每個(gè)標(biāo)準(zhǔn)請(qǐng)求對(duì)應(yīng)的代碼見表2,這個(gè)表對(duì)應(yīng)的代碼和內(nèi)核中為每個(gè)請(qǐng)求定義的宏是一一對(duì)應(yīng)的。
對(duì)于DESCRIPTOR設(shè)置或獲取請(qǐng)求,需要填充一個(gè)描述符種類或索引,USB設(shè)備描述符對(duì)應(yīng)的索引見表3。
表中列出了USB設(shè)備的5大描述符,USB設(shè)備的所有信息都保存在這些描述符中,在內(nèi)核中為這5大描述符分別定義了相應(yīng)的結(jié)構(gòu)體,分別是usb_device_descriptor、usb_config_descriptor、usb_string_descriptor、usb_interface_descriptor、usb_endpoint_descriptor,上面提到的一些標(biāo)準(zhǔn)請(qǐng)求就是圍繞這些結(jié)構(gòu)體來展開的。
表2 USB標(biāo)準(zhǔn)請(qǐng)求對(duì)應(yīng)代碼
表3 設(shè)備描述符代碼