Net Micro Framework - USB Mass Storage功能實現
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
1. 說明
由於.Net Micro Framework的USB驅動架構中,沒有為Mass Storage功能提供原生支援,所以除了要編寫Mass Storage主體程式碼外,還需要在原有的USB
此外,由於Mass Storage功能需要不斷地檢測和處理USB埠的資料,需要一個程序(或執行緒)去進行驅動。.Net Micro Framework在應用層僅支援一個程序(單個使用者程式),所以必須在應用程式中專開一個執行緒去進行驅動,考慮到這樣實現需要使用者做額外的工作,最後摒棄了這一實現。最終的做法是,在Mass Storage驅動中添加了時鐘中斷處理函式,
在實現Mass Storage功能的程式碼中,並沒有直接去讀寫相關Flash,而是藉助PAL層的SectorCache模組間接訪問Flash,這樣有兩個好處,一是讀寫有快取,操作速度較快,二是程式比較通用,程式碼在不用修改的情況下可以訪問不同的Flash、SD卡等儲存模組。
本Mass Storage驅動僅實現了一個功能子集,僅支援單個儲存模組,不支援從PC機進行格式化(可通過本地提供的介面進行格式化,檔案系統目前必須是FAT32)。
2. USB Config
Mass Storage
其二我們USB介面描述類中,介面類必須為0x08(Mass storage class),子類為0x06(SCSI transparent command set)或0x04(UFI),介面協議為0x50(Bulk-only transport)。
表1 子類表
表2 介面協議
其它描述資訊由於非關鍵,所以可修改,也可以不改。
3. 呼叫介面
Mass Storage驅動位於/DeviceCode/Drivers目錄下, 屬於通用驅動,其它裝置都可以呼叫。包括如下三個檔案:UsbMassStorage.h、UsbMassStorage.cpp、UsbMassStorage_config.cpp。
3.1 PAL層介面
3.1.1 UsbMassStorage_Start
Mass Storage功能初始化和啟動函式。該函式執行時,先關閉原先的USB驅動介面,再用新USB Config初始化USB介面,最後啟動一個定時中斷函式。
函式原型:int UsbMassStorage_Start(UINT32 value);
參 數:value為時鐘中斷間隔,單位us。
返 回 值:0。
3.1.2 UsbMassStorage_Stop
Mass Storage功能停止函式。該函式先關閉時鐘中斷函式及自己定義的USB介面,最後恢復預設USB介面(即UsbDefaultConfiguration)。
函式原型:int UsbMassStorage_Stop();
返 回 值:0。
3.2 P/Invoke介面
考慮到使用者也可以自由開啟和關閉Mass Storage功能,所以為使用者提供了P/Invoke介面。該介面檔案位於:/Solutions/DM335/DeviceCode目錄下,其實該程式通用,可以放在PAL層。包括兩部分程式碼,一是託管程式碼,二是原生代碼。
介面宣告如下,函式功能同PAL層介面。
namespace YFSoft
{
public static class MassStorage
{
[MethodImplAttribute(MethodImplOptions.InternalCall)]
extern static public int Start(UInt32 value);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
extern static public int Stop();
}
}
注意:使用者在呼叫MassStorage.Start()函式之前,先執行以下程式碼,以使磁碟快取中的內容真實寫入儲存裝置。
VolumeInfo[] vis = VolumeInfo.GetVolumes();
foreach (VolumeInfo vi in vis)
{
vi.FlushAll();
}
4.USB列舉程式碼新增
Mass Storage類規範定義了兩個請求:Get_Max_LUN和Mass Storage Reset,所有的Mass Storage類裝置都必須支援這兩個請求。
處理GET MAX LUN命令時,我們返回實際的邏輯單元(LUN:0~15)個數即可,由於我們的Mass Storage驅動僅支援一個儲存裝置,所以直接返回0即可。其實該命令也可以不應答,這時PC會重試三次,不過重試過程比較緩慢,使用者體驗體驗很不好。
對於Mass Storage Reset命令,由於我們目前沒有任何工作可做,所以直接返回空資料即可。
相關程式碼如下:
if(len>0)
{
USB_SETUP_PACKET* Setup= (USB_SETUP_PACKET*)State->Data;
//GET MAX LUN
if(Setup->bmRequestType == 0xA1 && Setup->bRequest == 0xFE)
{
*(volatile UINT8 *)((UINT32)&usb.FIFO[0]) = 0;
usb.Indexed.PERI_CSR0=DM335_USB_Indexed::PERI_CSR0_TXPKTRDY | DM335_USB_Indexed::PERI_CSR0_DATAEND;
return;
}
//Mass Storage Reset
if(Setup->bmRequestType == 0x21 && Setup->bRequest == 0xFF)
{
usb.Indexed.PERI_CSR0=DM335_USB_Indexed::PERI_CSR0_TXPKTRDY | DM335_USB_Indexed::PERI_CSR0_DATAEND;
return;
}
}
該部分程式碼直接新增在/DeviceCode/Targets/Native/DM335/DeviceCode/DM335_USB.cpp中的DM335_USB_Driver::EP0_ISR()函式即可。
5. Mass Storage功能實現
其實只要實現了標準的USB驅動介面,在此基礎上實現Mass Storage功能應該算不太難,這裡不打算詳細介紹Mass Storage功能的方方面面,這會涉及到太多的相關知識,我這裡只是從我們實際的這部分功能出發,簡明扼要地介紹一下Mass Storage功能實現的原理。
這裡需要特別指出的是,Bulk-only transport協議,僅需要USB驅動提供兩個端點即可,一個是端點1(輸入端點),一個是端點2(輸出端點),兩者的型別都為BULK模式。很幸運的是我們的.Net Micro Framework的標準驅動和這個要求是一致的。
5.1 命令/資料/狀態
Mass Storage裝置列舉成功後,PC會通過端點2向Mass Storage裝置傳送各種命令,Mass Storage裝置根據相應的命令,進行不同的應答。
其命令、資料、狀態相關的流程圖如下:
PC機發送的資料必須符合CBW格式(31byte,小端模式),而Mass Storage裝置的應答,其格式必須符合CSW格式(13byte,小端模式)。至於中間過程傳輸的資料,根據不同的命令,格式也有不同地要求。
5.1.1 CBW命令塊(Command Block Wrapper)
表3 CBW命令塊
dCBWSignature:常數0x43425355,標識為CBW命令塊。
dCBWTag: 由主機發送的CBW標籤。裝置應該在相關的CSW的dCSWTag以相同的值應答主機。
dCBWDataTransferLength: 在本命令執行期間,主機期望通過Bulk-In或Bulk-Out端點傳輸的資料長度。如果為0,則表示這之間沒有資料傳輸。
bmCBWFlags: 定義如下(Bit7 Direction(dCBWDataTransferLength為0時,該值無意義) :
0= DataOut,資料從主機到裝置
1= DataIn, 資料從裝置到主機
Bit6 Obsolete 0
Bits 5..0 Reserved 0
bCBWLUN: 表示正在傳送命令字的裝置的邏輯單元號(LUN)。對於支援多個LUN的裝置,主機設定相對應的LUN值。否則,該值為0。
bCBWCBLength: CBWCB的有效位元組長度。有效值是在1到16之間。
CBWCB: 被裝置解析執行的命令塊。
注:該部分是重中之重,通過對這部分的命令的解析,實現實際的Mass Storage功能。
5.1.2 CSW狀態塊(Command Status Wrapper)
表4 CSW狀態塊
dCSWSignature: 常數0x53425355,標識為CSW狀態塊
dCSWTag: 取相對應的CBW的dCBWTag值。
dCSWDataResidue:實際傳輸的資料個數和期望要傳輸的資料個數之差。
bCSWStatus:命令執行情況,相關值如下:
5.2 SCSI 傳輸協議(或UFI傳輸協議)
很多資料上都是把子類協議設定為0x06,也就是SCSI 傳輸協議,實際測試表明設定為0x04(也就是UFI傳輸協議)也是可以的。實際看說明書,發現二者很多命令都是相同的,所以這兩種協議對我們來說都適合,不過我這裡建議最好看UFI傳輸協議手冊,它要比SCSI手冊簡明地多。
無論是SCSI 傳輸協議還是UFI傳輸協議,其命令都是非常多的,不過對於我們的應用,我們僅需實現如下幾條指令即可。
5.2.1 INQUIRY命令
該命令詢問Mass Storage裝置的基本資訊,如生產廠家,產品名稱,產品版本等等。
詳細引數說明請參見《UFI Command Specification》,比較有意思的是Peripheral Device Type引數,如果設定為0,則表示這是一個可移動的儲存裝置(類似U盤),而設定為0x1F,則表示是一個非移動裝置(類似硬碟,圖示在硬碟區出現)。
5.2.2 READ_FORMAT_CAPACITIES命令
該命令獲取Mass Storage裝置儲存大小,Block長度(一般為一個扇區大小,預設為512)等資訊。
該表僅包括部分反饋資訊,詳細說明請參見《UFI Command Specification》。需要注意的是,無論是塊個數,還是塊長度,其資料格式為大端模式。
5.2.3 READ_CAPACITY命令
該命令返回最後一個塊的索引和塊的長度,其實該命令可以看著是READ_FORMAT_CAPACITIES命令的一個子集。
注意資料格式為大端模式。
詳細說明請參見《UFI Command Specification》。
5.2.4 READ_10命令
該命令由PC端發出,請求Mass Storage裝置傳送指定扇區索引、扇區個數的資料。
這是PC機請求的命令,Mass Storage裝置直接返回相應的資料即可。
詳細說明請參見《UFI Command Specification》。
5.2.5 WRITE_10命令
該命令由PC端發出,CBW命令塊後面緊跟的就是相應扇區的資料。
Mass Storage裝置獲取資料後,寫到相應扇區即可。
這裡需要強調的是,由於要接收的資料量有可能很大,該部分功能又是在時鐘中斷中實現,所以不要試圖一次獲取所有的扇區資料,否則在實際的TinyCLR環境中執行是不正常的。其實在READ_10中也存在類似問題,不過實際測試,直接傳送所有資料,並沒有什麼問題,這從側面反映PC機的接收資料能力,遠遠大於MF裝置。
5.2.6 REQUEST_SENSE命令
PC機每傳送一個命令後,都會檢測裝置返回的CSW的狀態值是否為0(Good Status),如果不為0,則PC機馬上傳送REQUEST_SENSE命令,詢問出錯的進一步資訊。
我們這裡Sense Key的值直接設為0x05(ILLEGAL REQUEST)即可,主要原因是PC端會詢問各種命令,由於我們沒有實現,返回的CSW狀態都非Good Status而已。
詳細說明請參見《UFI Command Specification》。
5.2.7 TEST_UNIT_READY命令
在沒有其它命令進行操作時,PC端會每隔一定時間,就會發送該命令,主要是為了探測Mass Storage裝置是否存在(類似心跳訊號)。
由於該命令沒有資料互動,我們直接返回狀態Good Status的CSW狀態塊即可。