這一節(jié)主要把在實現(xiàn)“linux模擬U盤功能”過程中的一些調(diào)試過程記錄下來,并加以解析。
一、背景知識
1、USB Mass Storage類規(guī)范概述
USB 組織在universal Serial Bus Mass Storage Class Spaceification 1.1版本中定義了海量存儲設(shè)備類(Mass Storage Class)的規(guī)范,這個類規(guī)范包括四個
獨(dú)立的子類規(guī)范,即:
1. USB Mass Storage Class Control/Bulk/Interrupt (CBI) Transport
2.USB Mass Storage Class Bulk-Only Transport
3.USB Mass Storage Class ATA Command Block
4.USB Mass Storage Class UFI Command Specification
前兩個子規(guī)范定義了數(shù)據(jù)/命令/狀態(tài)在USB 上的傳輸方法。Bulk- Only 傳輸規(guī)范僅僅使用Bulk 端點傳送數(shù)據(jù)/命令/狀態(tài),CBI 傳輸規(guī)范則使用Control/Bulk/Interrupt 三種類型的端點進(jìn)行數(shù)據(jù)/命令/狀態(tài)傳送。后兩個子規(guī)范則定義了存儲介質(zhì)的操作命令。ATA 命令規(guī)范用于硬盤,UFI 命令規(guī)范是針對USB 移動存儲。
Microsoft Windows 中提供對Mass Storage 協(xié)議的支持,因此USB 移動設(shè)備只需要遵循 Mass Storage 協(xié)議來組織數(shù)據(jù)和處理命令,即可實現(xiàn)與PC 機(jī)交換數(shù)據(jù)。而Flash 的存儲單元組織形式采用FAT16 文件系統(tǒng),這樣,就可以直接在Windows的瀏覽器中通過可移動磁盤來交換數(shù)據(jù)了,Windows 負(fù)責(zé)對FAT16 文件系統(tǒng)的管理,USB 設(shè)備不需要干預(yù)FAT16 文件系統(tǒng)操作的具體細(xì)節(jié)。
USB(Host)唯一通過描述符了解設(shè)備的有關(guān)信息,根據(jù)這些信息,建立起通信,在這 些描述符中,規(guī)定了設(shè)備所使用的協(xié)議、端點情況等。因此,正確地提供描述符,是USB 設(shè)備正常工作的先決條件。
Linux-2.6.26內(nèi)核中在利用USB gadget驅(qū)動實現(xiàn)模擬U盤時主要涉及到file_storage.c、s3c2410_udc.c等驅(qū)動文件(這些文件的具體結(jié)構(gòu),將在下一篇文章中描述)。此時我們想先從這些代碼中找到USB描述描述符,從中確定使用的存儲類規(guī)范,從而確定協(xié)議。確定通訊協(xié)議是我們調(diào)試的基礎(chǔ)。
存儲類規(guī)范是由接口描述符決定的。接口描述符各項的定義義如下:

其中,bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol可以判斷出設(shè)備是否是存儲類,以及屬于哪種存儲子類和存儲介質(zhì)的操作命令。
在file_storage.c文件中,
/* USB protocol value = the transport method */
#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt
#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt
#define USB_PR_BULK 0x50 // Bulk-only
/* USB subclass value = the protocol encapsulation */
#define USB_SC_RBC 0x01 // Reduced Block Commands (flash)
#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM)
#define USB_SC_QIC 0x03 // QIC-157 (tape)
#define USB_SC_UFI 0x04 // UFI (floppy)
#define USB_SC_8070 0x05 // SFF-8070i (removable)
#define USB_SC_SCSI 0x06 // Transparent SCSI
默認(rèn)的情況是:
mod_data = { // Default values
.transport_parm = "BBB",
.protocol_parm = "SCSI",
……
默認(rèn)的賦值如下:
bInterfaceClass=08 表示:存儲類
bInterfaceSubClass=0x06 表示:透明的SCSI指令
bInterfaceProtocol=0x50 表示:bulk-only 傳輸
2、Bulk-Only 傳輸協(xié)議
下面看看Bulk-Only 傳輸協(xié)議:(詳細(xì)的規(guī)范請閱讀《Universal Serial BusMass Storage ClassBulk-Only Transport》)
設(shè)備插入到USB 后,USB 即對設(shè)備進(jìn)行搜索,并要求設(shè)備提供相應(yīng)的描述符。在USBHost 得到上述描述符后,即完成了設(shè)備的配置,識別出為Bulk-Only 的Mass Storage 設(shè)備, 然后即進(jìn)入Bulk-Only 傳輸方式。在此方式下,USB 與設(shè)備間的所有數(shù)據(jù)均通過Bulk-In和Bulk-Out 來進(jìn)行傳輸,不再通過控制端點傳輸任何數(shù)據(jù)。
在這種傳輸方式下,有三種類型的數(shù)據(jù)在USB 和設(shè)備之間傳送,CBW、CSW 和普通數(shù)據(jù)。CBW(Command Block Wrapper,即命令塊包)是從USB Host 發(fā)送到設(shè)備的命令, 命令格式遵從接口中的bInterfaceSubClass 所指定的命令塊,這里為SCSI 傳輸命令集。USB設(shè)備需要將SCSI 命令從CBW 中提取出來,執(zhí)行相應(yīng)的命令,完成以后,向Host 發(fā)出反映 當(dāng)前命令執(zhí)行狀態(tài)的CSW(Command Status Wrapper),Host 根據(jù)CSW 來決定是否繼續(xù)發(fā) 送下一個CBW 或是數(shù)據(jù)。Host 要求USB 設(shè)備執(zhí)行的命令可能為發(fā)送數(shù)據(jù),則此時需要將 特定數(shù)據(jù)傳送出去,完畢后發(fā)出CSW,以使Host 進(jìn)行下一步的操作。USB 設(shè)備所執(zhí)行的操
作可用下圖描述:

CBW的格式如下:

dCBWSignature:
CBW的標(biāo)識,固定值:43425355h (little endian)。
dCBWTag:
主機(jī)發(fā)送的一個命令塊標(biāo)識,設(shè)備需要原樣作為dCSWTag(CSW中的一部分)再發(fā)送給Host;主要用于關(guān)聯(lián)CSW到對應(yīng)的CBW。
dCBWDataTransferLength:
本次CBW命令要求在命令與回應(yīng)之間傳輸?shù)淖止?jié)數(shù)。如果為0,則不傳輸數(shù)據(jù)。
bmCBWFlags:
反映數(shù)據(jù)傳輸?shù)姆较颍? 表示來自Host,1 表示發(fā)至Host;
bCBWLUN:
對于有多個LUN邏輯單元的設(shè)備,用來選擇具體目標(biāo)。如果沒有多個LUN,則寫0。
bCBWCBLength:
命令的長度,范圍在0~16.
CBWCB:
傳輸?shù)木唧w命令,符合bInterfaceSubClass.中定義的命令規(guī)范,此處是SCSI
CSW命令格式如下:

dCSWSignature:
CSW的標(biāo)識,固定值:53425355h (little endian)
dCSWTag:
設(shè)置這個標(biāo)識和CBW中的dCBWTag一致,參照上面關(guān)于dCBWTag的解釋
dCSWDataResidue:
還需要傳送的數(shù)據(jù),此數(shù)據(jù)根據(jù)dCBWDataTransferLength-本次已經(jīng)傳送的數(shù)據(jù)得到
bCSWStatus:
指示命令的執(zhí)行狀態(tài)。如果命令正確執(zhí)行,bCSWStatus 返回0 即可。
3、SCSI指令集
Bulk-Only 的CBW 中的CBWCB 中的內(nèi)容即為如下格式的命令塊描述符(Command Block Descriptor)。SCSI-2 有三種字長的命令,6 字節(jié)、10字節(jié)和12字節(jié),Microsoft Windows 環(huán)境下支持12 字節(jié)長的命令。

Operation Code:
操作代碼,表示特定的命令。高3 位為Group Code,共有8 種組合,
即8 個組,低5 五位為Command Code,可以有32 種命令。
Logicol unit Number:
為了兼容SCSI-1 而設(shè)的,此處可以不必關(guān)心。
Logical block address:
為高位在前,低位在后的邏輯塊地址,即扇區(qū)地址。第2 位為高位,第3、4、5 依次為低位。
Transfer length:
為需要從邏輯塊地址處開始傳輸?shù)纳葏^(qū)數(shù)(比如在Write 命令中)。
Parameter list length:
為需要傳輸?shù)臄?shù)據(jù)長度(比如在Mode Sense 命令中);
Allocation length:
為初始程序為返回數(shù)據(jù)所分配的大字節(jié)數(shù),此值可以為零,表示不需要傳送數(shù)據(jù)。
SCSI指令集的Direct Accesss 類型存儲介質(zhì)的傳輸命令有許多, Mass Storage協(xié)議只用到了其中的一些。更多的SCSI指令參見://en.wikipedia.org/wiki/SCSI_command
指令代碼 指令名稱 說明
04h Format Unit 格式化存儲單元
12h Inquiry 索取器件信息
1Bh Start/Stop load/unload
55h Mode select 允許Host對外部設(shè)備設(shè)置參數(shù)。
5Ah Mode Sense 向host傳輸參數(shù)
Eh Prevent/Allow Medium Removal 寫保護(hù)
>28h Read(10) Host讀存儲介質(zhì)中的二進(jìn)制數(shù)據(jù)
A8h Read(12) 同上,不過比較詳細(xì)一點
25h Read Capacity 要求設(shè)備返回當(dāng)前容量
23h Read Format Capacity 查詢當(dāng)前容量及可用空間
03h Request Sense 請求設(shè)備向主機(jī)返回執(zhí)行結(jié)果,及狀態(tài)數(shù)據(jù)
01h Rexero Unit 返回零軌道
2Bh Seek(10) 為設(shè)備分配到特定地址
1Dh Send Diagnostic 執(zhí)行固件復(fù)位并執(zhí)行診斷
00h Test Unit Ready 請求設(shè)備報告是否處于Ready狀態(tài)
2Fh Verify 在存儲中驗證數(shù)據(jù)
2Ah Write(10) 從主機(jī)向介質(zhì)寫二進(jìn)制數(shù)據(jù)
AAh Write(12) 同上,不過比較詳細(xì)
2Eh Write and Verify 寫二進(jìn)制數(shù)據(jù)并驗證
對于不同的命令,其命令塊描述符略有不同,其要求的返回內(nèi)容也有所不同,根據(jù)相 應(yīng)的文檔,可以對每種請求作出適當(dāng)?shù)幕貞?yīng)。比如,下面是INQUIRY 請求的命令塊描述符和其返回內(nèi)容的數(shù)據(jù)格式:如:INQUIRY
命令描述符:

返回數(shù)據(jù)格式

Host 會依次發(fā)出INQUIRY、Read Capacity、UFI Mode Sense 請求,如果上述請求的返回結(jié)果都正確,則Host 會發(fā)出READ 命令,讀取文件系統(tǒng)0 簇0 扇區(qū)的MBR 數(shù)據(jù),進(jìn)入文件系統(tǒng)識別階段。
4、利用USB View觀察結(jié)果
可通過USB View軟件查看到USB設(shè)置階段獲取到的信息。

二、出現(xiàn)的主要問題
在調(diào)試過程中遇到了一個問題。現(xiàn)象是:在目標(biāo)板加載完驅(qū)動后,即執(zhí)行完:
# insmod g_file_storage.ko file=/dev/mtdblock2 stall=0 removable=1
后,接好USB線。此時在windows端設(shè)備出有usb storage設(shè)備加入,但出現(xiàn)不了盤符。
下面記錄下調(diào)試過程。
三、調(diào)試過程
根據(jù)規(guī)范,當(dāng)完成SCSI指令集中Inquiry 命令時,可以出現(xiàn)盤符。所以可以通過bushound軟件查看通訊過程,找出原因。
下面是利用bushound工具在出現(xiàn)問題時采集到的數(shù)據(jù)。
Dev Phase Data Info Time Cmd.Phase. Ofs
--- ----- --------------------------------- ---------- ----- -----------
26 CTL 80 06 00 01 - 00 00 12 00 GET DESCRIPTR 0us 1.1.0
26 DI 12 01 10 01 - 00 00 00 10 - 25 05 a5 a4 - 12 03 01 02 ........%....... 4.8ms 1.2.0
03 01 .. 1.2.16
26 CTL 80 06 00 02 - 00 00 09 00 GET DESCRIPTR 14us 2.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 .. ...... 3.9ms 2.2.0
26 CTL 80 06 00 02 - 00 00 20 00 GET DESCRIPTR 16us 3.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 09 04 00 - 00 02 08 06 .. ............. 4.9ms 3.2.0
50 05 07 05 - 81 02 40 00 - 00 07 05 02 - 02 40 00 00 P.....@......@.. 3.2.16
26 CTL 80 06 00 03 - 00 00 02 00 GET DESCRIPTR 60us 4.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 .. ...... 3.9ms 2.2.0
26 DI 04 03 .. 3.9ms 3.1.0
26 CTL 80 06 00 03 - 00 00 04 00 GET DESCRIPTR 15us 5.1.0
26 DI 04 03 09 04 .... 3.9ms 6.1.0
26 CTL 80 06 03 03 - 09 04 02 00 GET DESCRIPTR 10us 1.2.16
26 DI 1a 03 .... 4.0ms 6.2.0
26 CTL 80 06 03 03 - 09 04 1a 00 GET DESCRIPTR 18us 7.1.0
26 DI 1a 03 33 00 - 37 00 32 00 - 30 00 34 00 - 31 00 37 00 ..3.7.2.0.4.1.7. 4.9ms 7.2.0
35 00 36 00 - 37 00 37 00 - 35 00 5.6.7.7.5. 7.2.16
26 CTL 00 09 01 00 - 00 00 00 00 SET CONFIG 16us 8.1.0
26 CTL 01 0b 00 00 - 00 00 00 00 SET INTERFACE 60ms 9.1.0
26 CTL a1 fe 00 00 - 00 00 01 00 CLASS 62ms 10.1.0
26 DI 00 . 3.9ms 10.2.0
26 DO 55 53 42 43 - 08 60 e0 86 - 24 00 00 00 - 80 00 06 12 USBC.`..$....... 985us 11.1.0
00 00 00 24 - 00 00 00 00 - 00 00 00 00 - 00 00 00 ...$........... 11.1.16
26 DI 00 80 02 02 - 1f 00 00 00 - 4c 69 6e 75 - 78 20 20 20 ........Linux 1.0ms 12.1.0
46 69 6c 65 - 2d 53 74 6f - 72 20 47 61 - 64 67 65 74 File-Stor Gadget 12.1.16
30 33 31 32 0312 12.1.32
26 CTL 80 06 00 02 - 00 00 20 00 GET DESCRIPTR 893ms 13.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 09 04 00 - 00 02 08 06 .. ............. 4.1ms 13.2.0
50 05 07 05 - 81 02 40 00 - 00 07 05 02 - 02 40 00 00 P.....@......@.. 13.2.16
26 CTL 80 06 00 02 - 00 00 20 00 GET DESCRIPTR 2.7sc 14.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 09 04 00 - 00 02 08 06 .. ............. 4.4ms 14.2.0
50 05 07 05 - 81 02 40 00 - 00 07 05 02 - 02 40 00 00 P.....@......@.. 14.2.16
26 USTS 05 00 00 c0 no response 2.8sc 15.1.0
注意上面紅色部分的代碼,DO發(fā)出了55 53 42 43開始的CBW命令塊,命令碼是12,即Inquiry命令。要求目標(biāo)返回Inquiry命令要求的數(shù)據(jù),長度是0x24。接下來設(shè)備端通過DI返回了設(shè)備信息。按照規(guī)范,在返回完了數(shù)據(jù)后,設(shè)備端還應(yīng)該通過DI向系統(tǒng)返回CSW的值。但實際的捕獲內(nèi)容并沒有。所以導(dǎo)致不能正確出現(xiàn)盤符。
在file_storage.c中,發(fā)送數(shù)據(jù)時都會調(diào)用到start_transfer()函數(shù)。在此函數(shù)中加入printk調(diào)試語句,觀察現(xiàn)象。發(fā)現(xiàn)只要加入的調(diào)試語句,windows端就能夠正常設(shè)別設(shè)備了。于是,可以猜測是因為需要在連續(xù)兩次發(fā)送之間加上一些延時。在函數(shù)中加入udelay(800)后,windows系統(tǒng)可以正常發(fā)現(xiàn)設(shè)備了。具體的代碼架構(gòu),將在下一遍文章中解析。
下面是程序正常后,用bushound捕獲到的數(shù)據(jù)。
紅色部分,可以看出設(shè)備正確的按照規(guī)范在發(fā)送完數(shù)據(jù)后,返回CSW信息。

四、總結(jié)做好USB gadget驅(qū)動、或者USB host驅(qū)動調(diào)試需要:
·掌握一定的知識基礎(chǔ)
包括:USB協(xié)議、具體的類設(shè)備規(guī)范、USB驅(qū)動程序架構(gòu)、USB設(shè)備端控制器操作等。
·合理利用調(diào)試工具。
包括:USB view 、bushound 、及一些硬件USB信號分析儀。