U盤枚舉(自己總結)_第1頁
U盤枚舉(自己總結)_第2頁
U盤枚舉(自己總結)_第3頁
U盤枚舉(自己總結)_第4頁
U盤枚舉(自己總結)_第5頁
已閱讀5頁,還剩69頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

本文格式為Word版,下載可任意編輯——U盤枚舉(自己總結)

插入U盤

直接拔出

安全拔出

A9枚舉

LinuxUSBgadget設備驅動解析(2)驅動調試:劉洪濤,華清遠見嵌入式學院金牌講師。

這一節主要把在實現“linuxU盤功能〞過程中的一些調試過程記錄下來,并加以解析。

一、背景知識

1、USBMassStorage類規范概述

USB組織在universalSerialBusMassStorageClassSpaceification1.1版本中定義了海量存儲設備類(MassStorageClass)的規范,這個類規范包括四個

獨立的子類規范,即:

1.USBMassStorageClassControl/Bulk/Interrupt(CBI)Transport2.USBMassStorageClassBulk-OnlyTransport3.USBMassStorageClassATACommandBlock

4.USBMassStorageClassUFICommandSpecification

前兩個子規范定義了數據/命令/狀態在USB上的傳輸方法。Bulk-Only傳輸規范僅僅使用Bulk端點傳送數據/命令/狀態,CBI傳輸規范則使用

Control/Bulk/Interrupt三種類型的端點進行數據/命令/狀態傳送。后兩個子規范則定義了存儲介質的操作命令。ATA命令規范用于硬盤,UFI命令規范是針對USB移動存儲。MicrosoftWindows中提供對MassStorage協議的支持,因此USB移動設備只需要遵循MassStorage協議來組織數據和處理命令,即可實現與PC機交換數據。而Flash的存儲單元組織形式采用FAT16文件系統,這樣,就可以直接在Windows的瀏覽器中通過可移動磁盤來交換數據了,Windows負責對FAT16文件系統的管理,USB設備不需要干預FAT16文件系統操作的具體細節。

USB(Host)唯一通過描述符了解設備的有關信息,根據這些信息,建立起通信,在這些描述符中,規定了設備所使用的協議、端點狀況等。因此,正確地提供描述符,是USB設備正常工作的先決條件。

Linux-2.6.26內核中在利用USBgadget驅動實現模擬U盤時主要涉及到

file_storage.c、s3c2410_udc.c等驅動文件(這些文件的具體結構,將在下一篇文章中描述)。此時我們想先從這些代碼中找到USB描述描述符,從中確定使用的存儲類規范,從而確定協議。確定通訊協議是我們調試的基礎。存儲類規范是由接口描述符決定的。接口描述符各項的定義義如下:

其中,bInteaceClass、bInterfaceSubClass、bInterfaceProtocol可以判斷出設備是否是存儲類,以及屬于哪種存儲子類和存儲介質的操作命令。在file_storage.c文件中,

/*USBprotocolvalue=thetransportmethod*/

#defineUSB_PR_CBI0x00//Control/Bulk/Interrupt#defineUSB_PR_CB0x01//Control/Bulkw/ointerrupt#defineUSB_PR_BULK0x50//Bulk-only

/*USBsubclassvalue=theprotocolencapsulation*/

#defineUSB_SC_RBC0x01//ReducedBlockCommands(flash)#defineUSB_SC_80200x02//SFF-8020i,MMC-2,ATAPI(CD-ROM)

#defineUSB_SC_QIC0x03//QIC-157(tape)#defineUSB_SC_UFI0x04//UFI(floppy)

#defineUSB_SC_80700x05//SFF-8070i(removable)#defineUSB_SC_SCSI0x06//TransparentSCSI

默認的狀況是:

mod_data={//Defaultvalues.transport_parm=\.protocol_parm=\??

默認的賦值如下:

bInterfaceClass=08表示:存儲類

bInterfaceSubClass=0x06表示:透明的SCSI指令bInterfaceProtocol=0x50表示:bulk-only傳輸

2、Bulk-Only傳輸協議

下面看看Bulk-Only傳輸協議:(詳細的規范請閱讀《UniversalSerialBusMassStorageClassBulk-OnlyTransport》)

設備插入到USB后,USB即對設備進行探尋,并要求設備提供相應的描述符。在USBHost得到上述描述符后,即完成了設備的配置,識別出為Bulk-Only的MassStorage設備,然后即進入Bulk-Only傳輸方式。在此方式下,USB與設備間的所有數據均通過Bulk-In和Bulk-Out來進行傳輸,不再通過控制端點傳輸任何數據。

在這種傳輸方式下,有三種類型的數據在USB和設備之間傳送,CBW、CSW和普通數據。CBW(CommandBlockWrapper,即命令塊包)是從USBHost發送到設備的命令,命令格式遵從接口中的bInterfaceSubClass所指定的命令塊,這里為SCSI傳輸命令集。USB設備需要將SCSI命令從CBW中提取出來,執行相應的命令,完成以后,向Host發出反映當前命令執行狀態的CSW(CommandStatusWrapper),Host根據CSW來決定是否繼續發送下一個CBW或是數據。Host要求USB設備執行的命令可能為發送數據,則此時需要將特定數據傳送出去,完畢后發出CSW,以使Host進行下一步的操作。USB設備所執行的操作可用下圖描述:

下面是利用bushound工具在出現問題時采集到的數據。

DevPhaseDataInfoTimeCmd.Phase.Ofs

26CTL80060001-000012

00GETDESCRIPTR0us1.1.0

26DI12011001-00000010-2505a5a4-12030102%4.8ms1.2.003

01..1.2.1626CTL80060002-000009

00GETDESCRIPTR14us2.1.0

26DI09022000-010104c0-

01..3.9ms2.2.0

26CTL80060002-000020

00GETDESCRIPTR16us3.1.0

26DI09022000-010104c0-01090400-00020806..4.9ms3.2.0

50050705-81024000-00070502-02400000P@@..3.2.1626CTL80060003-000002

00GETDESCRIPTR60us4.1.0

26DI09022000-010104c0-

01..3.9ms2.2.0

26DI04

03..3.9ms3.1.026CTL80060003-000004

00GETDESCRIPTR15us5.1.026DI040309

043.9ms6.1.0

26CTL80060303-090402

00GET

DESCRIPTR10us1.2.1626DI1a

034.0ms6.2.026CTL80060303-09041a

00GETDESCRIPTR18us7.1.0

26DI1a033300-37003200-30003400-31003700....9ms7.2.035003600-37003700-35

00.626CTL00090100-000000

00SETCONFIG16us8.1.026CTL010b0000-000000

00SETINTERFACE60ms9.1.026CTLa1fe0000-000001

00CLASS62ms10.1.0

26DI00.3.9ms10.2.0

26DO55534243-086080000612USBC.`..$985us11.1.0

00000024-00000000-00000000-000000...$11.1.16

26DI00800202-1f000000-4c696e75-78202020Linux1.0ms12.1.0

46696c65-2d53746f-72204761-64676574File-StorGadget12.1.16303331

32031212.1.32

26CTL80060002-000020

00GETDESCRIPTR893ms13.1.0

26DI09022000-010104c0-01090400-00020806..4.1ms13.2.0

50050705-81024000-00070502-02400000P@@..13.2.1626CTL80060002-000020

00GETDESCRIPTR2.7sc14.1.0

26DI09022000-010104c0-01090400-000208

06..4.4ms14.2.0

50050705-81024000-00070502-02400000P@@..14.2.1626USTS050000

c0noresponse2.8sc15.1.0

注意上面紅色部分的代碼,DO發出了55534243開始的CBW命令塊,命令碼是12,即Inquiry命令。要求目標返回Inquiry命令要求的數據,長度是0x24。接下來設備端通過DI返回了設備信息。依照規范,在返回完了數據后,設備端還應當通過DI向系統返回CSW的值。但實際的捕獲內容并沒有。所以導致不能正確出現盤符。

在file_storage.c中,發送數據時都會調用到start_transfer()函數。在此函數中參與printk調試語句,觀測現象。發現只要參與的調試語句,windows端就能夠正常設別設備了。于是,可以猜測是由于需要在連續兩次發送之間加上一些延時。在函數中參與udelay(800)后,windows系統可以正常發現設備了。具體的代碼架構,將在下一遍文章中解析。下面是程序正常后,用bushound捕獲到的數據。

紅色部分,可以看出設備正確的依照規范在發送完數據后,返回CSW信息。

四、總結做好USBgadget驅動、或者USBhost驅動調試需要:·把握一定的知識基礎

包括:USB協議、具體的類設備規范、USB驅動程序架構、USB設備端控制器操作等。

·合理利用調試工具。

包括:USBview、bushound、及一些硬件USB信號分析儀。

一、追蹤USB大容量設備的實現流程

1、從main.c開始

(1)main函數的執行流程

Set_System();//設置時鐘、端口等。

Set_USBClock();//設置usb的時鐘

USB_Interrupts_Config();//設置中斷

Led_Config();//設置所使用的到的燈。

MSD_Init();//SD卡初始化

Get_Medium_Characteristics();//獲取SD塊總數、每塊字節數。

USB_Init();//USB_init.c提供的初始化函數。從這里開始USB設備被主機檢測到。

while(1)

{//USB的工作都是在中斷中完成的,主執行流程什么也沒做。

}

(2)與鼠標例程不同的地方

在中斷配置中,使能了USB高優先級中斷。

NVIC_InitStructure.NVIC_IRQChannel=USB_HP_CAN_TX_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_Init(

用到了幾個燈指示,這個我的開發板上用不到,就不詳細看了。

MSD_Init(),是對SD卡進行初始化。該函數在msd.c中,我看了一下它的SD卡實現的代碼,比我的SD函數代碼齊整多了。以后有時間要把我的USB驅動好好的整理一下。不過現在就先不管了。

接下來這個函數獲取SD卡的容量,這樣的函數我在SD卡驅動中也實現了,改變一下調用方式就行了。

USB_Init()函數在usb_init.c庫函數中,但它最終會調用user_prop.c宏的用戶初始化例程。下面就追蹤進去看一看。

(3)大容量存儲設備的初始化

voidMASS_init()

{

pInformation->Current_Configuration=0;

PowerOn();連接電纜主機很快發總線復位。

_SetISTR(0);

wInterrupt_Mask=IMR_MSK;

_SetCNTR(wInterrupt_Mask);開啟復位和傳輸中斷。

pInformation->Current_Feature=MASS_ConfigDescriptor[7];

while(pInformation->Current_Configuration==0)

{

NOP_Process();

}

bDeviceState=CONFIGURED;//這句執行完成后,設備處于已配置狀態。我先在這里加一句調試語句。

#ifusb_debug

Uart_PutString(“設備已配置〞);

#endif

}

2、進入復位中斷

(1)先列出中斷處理代碼

發生總線復位中斷以后,處理是在usb_prop.c的Mass_Reset()函數中完成的。

voidMASS_Reset()

{

Device_Info.Current_Configuration=0;

SetBTABLE(BTABLE_ADDRESS);

SetEPType(ENDP0,EP_CONTROL);//端點0控制端點

SetEPTxStatus(ENDP0,EP_TX_NAK);//不響應IN

SetEPRxAddr(ENDP0,ENDP0_RXADDR);//設置接收緩沖區(OUT)

SetEPRxCount(ENDP0,Device_Property.MaxPacketSize);接收長度。

SetEPTxAddr(ENDP0,ENDP0_TXADDR);//發送緩沖區(IN)

Clear_Status_Out(ENDP0);

SetEPRxValid(ENDP0);//使能端點0的接收。

SetEPType(ENDP1,EP_BULK);//端點1批量模式

SetEPTxAddr(ENDP1,ENDP1_TXADDR);//設置發送緩沖區(IN)

SetEPTxStatus(ENDP1,EP_TX_NAK);發送不響應。

SetEPRxStatus(ENDP1,EP_RX_DIS);//接收無效。對OUT無效

SetEPType(ENDP2,EP_BULK);//端點2批量模式

SetEPRxAddr(ENDP2,ENDP2_RXADDR);//設置接收緩沖區OUT

SetEPRxCount(ENDP2,Device_Property.MaxPacketSize);

SetEPRxStatus(ENDP2,EP_RX_VALID);

SetEPTxStatus(ENDP2,EP_TX_DIS);//發送無效,對IN無效

SetDeviceAddress(0);//使能USB接口模塊。

CBW.dSignature=BOT_CBW_SIGNATURE;

Bot_State=BOT_IDLE;//命令狀態機初始化為空閑狀態

}

在這里沒有我沒有看到將批量端點設置為雙緩沖模式的跡象,莫非這個例程沒有用它?

-3、進入枚舉過程

由于在鼠標例程中已經詳細分析過枚舉過程,這里主要是大容量設備枚舉過程中不同的地方做一下分析。

(1)獲取設備描述符、設置地址。

(2)獲取配置描述符

(3)獲取配置描述符集合,這里主要講接口描述符分析一下。

0x09,/*bLength:InterfaceDescriptorsize*/

0x04,/*bDescriptorType:*/

0x00,/*bInterfaceNumber:NumberofInterface*/

0x00,/*bAlternateSetting:Alternatesetting*/

0x02,/*bNumEndpoints*/使用兩個端點

0x08,/*bInterfaceClass:MASSSTORAGEClass,大容量存儲類*/

0x06,/*bInterfaceSubClass:SCSItransparent,SCSI傳輸*/

0x50,/*nInterfaceProtocol,僅批量傳輸*/

4,/*iInterface:*/

(4)獲取字符串描述符

(5)類請求實現:

類獲取規律盤:

一般返回0

類請求復位:

ClearDTOG_TX(ENDP1);

ClearDTOG_RX(ENDP2);

CBW.dSignature=BOT_CBW_SIGNATURE;

Bot_State=BOT_IDLE;

(6)設置配置

在用戶設置的回調函數中,又調用

voidMass_Storage_SetConfiguration(void)

{

if(pInformation->Current_Configuration)

{

ClearDTOG_TX(ENDP1);

ClearDTOG_RX(ENDP2);

Bot_State=BOT_IDLE;}

}

這個工作前面已經做過了。

4、主機發命令INQUIRY

(1)首先進入批量輸出中斷

該中斷的回調函數調用Mass_Storage_Out()進行處理。

(2)追蹤進入Mass_Storage_Out()

voidMass_Storage_Out(void)

{

u8CMD;

CMD=CBW.CB[0];//

Data_Len=GetEPRxCount(ENDP2);

PMAToUserBufferCopy(Bulk_Data_Buff,ENDP2_RXADDR,Data_Len);

switch(Bot_State)

{

caseBOT_IDLE:

CBW_Decode();//第一次收到命令確定調用這個解碼函數。

break;//它的作用應當是填充CBW命令塊封包結構

caseBOT_DATA_OUT:

if(CMD==SCSI_WRITE10)

{

SCSI_Write10_Cmd();

break;

}

}

(3)追蹤進入CBW_Decode()

這個函數的代碼較長,我就不列舉在這里了,我就分析一下本次的主要工作。

首先將用戶緩沖區的數據復制到命令封包結構里面。

然后準備好狀態封包結構:

CSW.dTag=CBW.dTag;//這個標志由主機生成,可以用于檢查設備是否正確收到該命令。

CSW.dDataResidue=CBW.dDataLength;

然后主要是根據命令操作碼,調用相應的SCSI命令處理函數。

switch(CBW.CB[0])

{

caseSCSI_REQUEST_SENSE:

SCSI_RequestSense_Cmd();

break;

caseSCSI_INQUIRY:

SCSI_Inquiry_Cmd();

break;

我這里就列出了兩項,實際的命令是好多的。本次主要是查詢處理。

(4)追蹤進入SCSI_Inquiry_Cmd()

以上函數是在usb_bot.c里面,現在跳轉到usb_scsi.c里面。

這個函數的主要工作是調用Transfer_Data_Request(Inquiry_Data,Inquiry_Data_Length)來完成。

這個Inquiry_Data=Standard_Inquiry_Data,后面這個Standard_Inquiry_Data是scsi_data.c里面定義的一個數據結構,專門用于inquiry命令的返回。

u8Standard_Inquiry_Data[]=

{

0x00,/*DirectAccessDevice*/

0x80,/*RMB=1:RemovableMedium*/

0x02,/*Version:Noconformanceclaimtostandard*/

0x02,//這里圈圈的書上說應當為0x01

36-4,//這里圈圈的書上說應當為31

0x00,0x00,0x00,/*SCCS=1:StorageControllerComponent*/

'S','T','M','','','','','',//廠商信息

'S','T','R','','','F','l','a','s','h','','D','i','s','k','',//產品信息

'1','.','0',''

//版本信息。

};

(5)追蹤進入Transfer_Data_Request()

voidTransfer_Data_Request(u8*Data_Pointer,u16Data_Len)

{

UserToPMABufferCopy(Data_Pointer,ENDP1_TXADDR,Data_Len);

SetEPTxCount(ENDP1,Data_Len);

SetEPTxStatus(ENDP1,EP_TX_VALID);

Bot_State=BOT_DATA_IN_LAST;

CSW.dDataResidue-=Data_Len;

CSW.bStatus=CSW_CMD_PASSED;//設置好命令狀態封包信息。

}

(6)接下來,主機遇發IN,取走查詢信息。并進入批量輸入中斷。

在該中斷中,將調用函數Mass_Storage_In(void)

voidMass_Storage_In(void)

{

switch(Bot_State)

{

caseBOT_CSW_Send:

caseBOT_ERROR:

Bot_State=BOT_IDLE;

SetEPRxStatus(ENDP2,EP_RX_VALID);/*enabletheEndpointtorecivethenextcmd*/

break;

caseBOT_DATA_IN_LAST:

Set_CSW(CSW_CMD_PASSED,SEND_CSW_ENABLE);

SetEPRxStatus(ENDP2,EP_RX_VALID);

break;

default:

break;

}

}

然后設備的命令狀態機狀態變為Set_CSW()這個函數所設置的狀態,一般為BOT_CSW_Send。

(7)追蹤進入Set_CSW()

在該函數中:

voidSet_CSW(u8CSW_Status,u8Send_Permission)

{

CSW.dSignature=BOT_CSW_SIGNATURE;

CSW.bStatus=CSW_Status;//命令狀態封包數據已經準備好

UserToPMABufferCopy((

SetEPTxCount(ENDP1,CSW_DATA_LENGTH);

Bot_State=BOT_ERROR;

if(Send_Permission)

{

Bot_State=BOT_CSW_Send;

SetEPTxStatus(ENDP1,EP_TX_VALID);

}

}

然后,主機再次發IN令牌包,取走命令狀態封包。

caseBOT_ERROR:

Bot_State=BOT_IDLE;

SetEPRxStatus(ENDP2,EP_RX_VALID);

端點2的接收又被使能,重新進入接收命令狀態

二、追蹤USB大容量設備的實現流程

5、主機發命令READFORMATCATPACITIES

再次分析一次命令執行的流程

(1)首先在批量輸出端點2產生RX中斷

在中斷中調用Mass_Storage_Out()。

在處理過程中先將接收緩沖區的數據、長度保存。

由于此時,命令處理狀態機處于BOT_IDLE狀態,所以調用命令解碼函數CBW_Decode()。

(2)解碼函數所做的工作

把接收到的數據先賦值給命令封包結構CBW。同時開始準備填充命令狀態封包結構CSW。

switch(CBW.CB[0]),根據命令操作碼進行命令處理散轉。

(3)操作碼0x23,進入相應處理函數SCSI_ReadFormatCapacity_Cmd()

這個命令處理主要是填充用戶返回的容量數據結構體,然后調用另外一個函數Transfer_Data_Request()來完成數據的傳輸。

這個函數接收發送數據緩沖區的起始地址、長度,首先把數據復制到數據輸出批量端點1:

UserToPMABufferCopy(Data_Pointer,ENDP1_TXADDR,Data_Len);

SetEPTxCount(ENDP1,Data_Len);

SetEPTxStatus(ENDP1,EP_TX_VALID);

Bot_State=BOT_DATA_IN_LAST;//設置命令處理的新狀態

CSW.dDataResidue-=Data_Len;

CSW.bStatus=CSW_CMD_PASSED;//填充命令狀態封包。

接下來主機遇連發兩個“IN〞,第一次將容量數據結構體返回。

然后在端點1輸入中斷中,又將命令狀態封包結構復制到批量端點1輸出緩沖區。主機的其次個“IN“將取走這個數據。

在其次次輸入中斷處理程序中,命令狀態重新回到“BOT_IDLE〞,于是又可以接收新的命令。

6、讀容量命令

這個跟上個命令返回的數據差不多,具體什么區別,等到移植調試的時候再看。

7、READ(10)命令

這是一個十分重要的命令,我們獲取U盤的文件主要就靠它了。

(1)前面的過程忽略,直接進入讀命令解碼SCSI_Read10_Cmd()

處理中,主要存在兩種狀況:

在BOT_IDLE時:

if((CBW.bmFlags

Read_Memory();

}

在BOT_DATA_IN時:

直接Read_Memory();

(2)進入Read_Memory()處理函數。

voidRead_Memory(void)

{

if(!Block_Read_count)

{//讀入一個扇區512字節,但是一次只能發送64個字節。

MSD_ReadBlock(Data_Buffer,Memory_Offset,512);

UserToPMABufferCopy(Data_Buffer,ENDP1_TXADDR,BULK_MAX_PACKET_SIZE);

Block_Read_count=512-BULK_MAX_PACKET_SIZE;

Block_offset=BULK_MAX_PACKET_SIZE;

}

else

{

UserToPMABufferCopy(Data_Buffer+Block_offset,ENDP1_TXADDR,BULK_MAX_PACKET_SIZE);

Block_Read_count-=BULK_MAX_PACKET_SIZE;

Block_offset+=BULK_MAX_PACKET_SIZE;

}

SetEPTxCount(ENDP1,BULK_MAX_PACKET_SIZE);

SetEPTxStatus(ENDP1,EP_TX_VALID);

Memory_Offset+=BULK_MAX_PACKET_SIZE;

Transfer_Length-=BULK_MAX_PACKET_SIZE;//剩下的需要傳輸的字節數。

CSW.dDataResidue-=BULK_MAX_PACKET_SIZE;

Led_RW_ON();

}

這里不明白的是主機是一次把512字節讀完,還是每次發一個命令讀取64字節。我覺得應當是發一次讀命令,8次“IN〞讀取一個扇區,再發一個“IN〞讀取命令狀態封包。

8、寫命令WRITE(10)

這個命令的處理過程跟讀命令的處理差不多,只是最終它會調用Write_Memory()進行處理。

i=0;

for(;Counter這三個函數我以前實際上都實現了,但是用到這個例程中,還需要改變一些參數的對應問題。

2、為了更明了的了解U盤的整個工作流程,在關鍵的地方加一些調試函數,向串口輸出信息。

二、開始移植

1、準備源文件

在usb目錄下新建udisk目錄,在其下建src和inc兩個子目錄。

將usb_istr.c、usb_pwr.c、usb_desc.c、usb_prop.c、hw_config.c,usb_endp.c、usb_bot.c、usb_scsi.c、scsi_data.c、memory.c、msd.c,main.c等共12個文件復制如src目錄。

將相應的頭文件復制入inc目錄。

在工程里新建文件組,把所有c源文件參與工程。

2、添加命令udisk

當在串口輸入udisk命令時,整個開發板將成為一個讀卡器。

3、修改各個源文件的包含關系、單獨編譯

(1)主程序main.c改變成“udisk〞命令處理程序。

voidUartCmdUsbMouse(u8argc,void**argv){

Uart_PutString(\進入U盤實現過程!\\r\\n\

USB_Connect_Init();

USB_Interrupts_Config();

Set_USBClock();

Get_Medium_Characteristics();

USB_Init();

while(1)

}

在這個過程中,去掉了Set_System()函數、led等配置函數等,這些函數都在hw_config.c文件中,該文件編譯通過以后,再修改hw_config.c的函數實現。

(2)修改hw_config.c

其它函數的修改在鼠標例程中已經分析過了,這里主要是以下這個函數:

voidGet_Medium_Characteristics(void)

{

u8res;

CardInfoMsdInfo;

res=SD_GetCardInfo(

if(res==0){

Mass_Block_Count=MsdInfo.BlockNumber;

Mass_Block_Size=MsdInfo.BlockLength;

Mass_Memory_Size=MsdInfo.Capacity;

}

}

獲取SD卡容量的函數重新修改,也不需要頭文件msd.h了。

(3)有9個文件只要修改頭文件包含關系就行了

(4)修改文件msd.c

由于我在sduser.c文件中已經實現了大部分的SD操作函數,所以這里實際上讀卡、寫卡函數調用以前編寫的函數就行了。

當然,在入口參數有不一致的地方必需做調整。

u8MSD_WriteBlock(u8*pBuffer,u32WriteAddr,u16NumByteToWrite)

{

u8res;

rea=SD_WriteBlock(WriteAddr>>9,pBuffer);

if(res==0)returnres;

return0xFF;

}

讀扇區的修改跟這個類似。

三、下載、測試和修改

1、整個工程編譯,生成Hex文件,下載到開發板。

2、輸入命令udisk

PC識別了該設備,但是磁盤為空。

這是串口發回來的消息。

Sh>udisk

進入U盤實現過程!

setup中斷8006000100004000獲取設備描述符

設備準備發送12字節:120100020000004083042057000101020301

IN令牌04中斷

OUT狀態中斷

setup中斷0005020000000000設置地址

IN狀態中斷

setup中斷8006000100001200獲取設備描述符

設備準備發送12字節:120100020000004083042057000101020301

IN令牌04中斷

OUT狀態中斷

setup中斷8006000200000900獲取配置描述符

設備準備發送09字節:090220000101008032

IN令牌04中斷

OUT狀態中斷

setup中斷800600030000FF00獲取字符串描述符

設備準備發送04字節:04030904

IN令牌04中斷

OUT狀態中斷

setup中斷800600020000FF00獲取配置描述符

設備準備發送20字節:0902200001010080320904000002080650040705810240000007050202400000

IN令牌04中斷

OUT狀態中斷

setup中斷8006000600000A00這是與高速有關的,這里不支持。

一、USB設備驅動入門

1、學習目的

(1)了解windows系統硬件驅動的一些基本知識。從應用程序給出要求、驅動程序假使處理、底層硬件工作的大致狀況有個基本了解。

(2)使用自定義的應用程序、自定義的驅動程序來控制與設備的交互。

2、學習工具

我手邊有去年買的一本《windows驅動開發技術詳解》,這本書寫的挺好的。不過去年我買的時候,看著像天書。到現在,反復看了兩遍以后,心里對windows底層的工作原理已經有了那么一點點概念了。

像PCI、USB類型的設備,都是屬于WDM驅動模型。特點是即插即用、分層、面向對象、數據驅動(IRP)。

3、WDM驅動特征分析

(1)即插即用

譬如對一個USB設備來說,就能夠做到即插即用。

USB主機控制器是PCI總線上的一個設備,在windows啟動的時候就已經安裝好的驅動。系統可以驅動USBHC,其下游端口有設備接入的時候,HCD也有相應的程序進行處理。

譬如現在插入一個u盤,在主機的根集線器端口。它向HC報告了這個事件后,有一個叫做即插即用管理器的組件,根據USB總線驅動對象創立一個PDO設備對象。

然后總線驅動獲取設備的VID、PID、設備類型等信息,這是通過設備枚舉取得的。根據這些信息,windows系統查找相應的功能驅動,譬如u盤就是大容量設備類驅動。然后這個驅動再創立一個FDO設備對象。這樣這個設備就可以供用戶使用了。

(2)分層

分層是現代操作系統的特征,也是我們編寫軟件時提高可讀性、靈活性、可重用性的方法。

分層使得設備對用戶提供了統一的操作接口。

譬如用戶要打卡設備,都是調用CreateFile()函數、讀寫用ReadFile()和WriteFile函數、控制用DeviceIOCtrl()函數。

這些函數都是win32子系統實現的,windows系統將這些函數調用轉化為系統調用,進入windows內核。

內核服務函數調用windows的執行組件,一般設備操作是用過IO管理器完成的。IO管理器根據用戶傳遞下來的設備名稱,找到相應的驅動對象和設備對象。

IO管理器把用戶的要求組合成一個用戶輸入輸出請求包(IRP),然后利用IRP調用相應的驅動程序。這里時間上使用的回調函數的概念,根據具體請求(讀、寫或其它要求),調用驅動程序的相應函數進行處理。

驅動一般也是多層。上層的驅動程序完成一些工作后,將IRP傳遞到下一層。譬如USB設備的操作,經過功能層次的處理,創立URB請求包附加到IRP中,最終由總線驅動和HCD驅動轉換為USB總線上的數據包。

二、USB設備驅動開發

1、開發過程簡介

這次只是了解一下windows驅動開發的過程,并沒有詳細學習windows驅動開發的計劃,我連VC都已經不熟悉了。

本次操作是根據《圈圈教你玩USB》第九章的源程序,做一些稍微的修改,使它適合智林開發板的驅動。

主要實現按鍵信息的讀取、led燈的控制兩個內容,跟上次實現的自定義HID設備功能一樣。只是現在采用自定義設備而不是HID設備類型、使用自定義的文件讀寫而不是從HID驅動獲取的報告描述符中獲取數據了。

2、驅動框架的建立

(1)編譯vdw_wdm.lib

(2)根據向導建立一個USBWDM驅動程序。

工程名“usbdevice〞、“WDM〞類型驅動、“功能驅動〞、“USB驅動〞

“使用端點1的中斷輸入、中斷輸出〞、“使用緩沖IO〞、“VID=8888,PID=1111〞。

(3)去掉link選項里的ntstrsafe.lib,整個工程編譯成功。

3、驅動程序的修改

主要是在讀寫函數里修改:

//在這里構建讀數據的URB

PURBpUrb=EP1_WRITE.BuildInterruptTransfer(

pBuffer,writeSize,TRUE,NULL,NULL,FALSE);

if(pUrb==NULL){

status=STATUS_INSUFFICIENT_RESOURCES;

}

else{

status=EP1_WRITE.SubmitUrb(pUrb,NULL,NULL,0);

bytesSent=pUrb->UrbInterruptTransfer.TransferBufferLengt

h;

deletepUrb;

}

依照圈圈書里的描述進行修改,實際修改的地方很少。

4、設備固件的修改

將設備描述符里的設備類改為“0xFF〞。

將設備的VID、PID改為“8888〞和“1111〞。

將HID類描述符刪除。

將報告描述符刪除。

編譯、下載,windows彈出安裝驅動的界面。安裝好以后,在設備管理器可

以看到如下設備。

MassStorage設備,即大容量存儲設備,最典型的莫過于U盤了,而U盤一般以BulkOnly傳輸方式實現。

四、USBMassStorage設備的描述符及枚舉過程

描述符就是對應標準請求的那些描述符,與HID設備不同,MassStorage設備沒有自己的類描述符。描述符在USBMassStorageClassBulk-OnlyTransport文檔中有詳細的一對一的描述。所以此處不再贅述,僅舉一例:

(設備描述符略,通用定義,與設備類無關)(配置描述符略,通用定義,與設備類無關)

_Interface_Descriptor:

.dw0x09//bLength:0x09byte

.dw0x04//bDescriptorType:INTERFACE.dw0x00//bInterfaceNumber:interface0

.dw0x00//bAlternateSetting:alternatesetting0

.dw0x02//bNumEndpoints:3endpoints(EP0,EP1,EP2).dw0x08//bInterfaceClass:MassStorageDevicesClass.dw0x06//bInterfaceSubClass:.dw0x50//bInterfaceProtocol

.dw0x02//iInterface:indexofstring_Interface_Descriptor_End:

_Endpoint1:

.dw0x07//bLength:0x07byte

.dw0x05//bDescriptorType:ENDPOINT.dw0x81//bEndpointAddress:INendpoint1.dw0x02//bmAttributes:Bulk

.dw0x40,0x00//wMaxPacketSize:64byte.dw0x00//bInterval:ignored

_Endpoint2:

//Endpoint2(0x07byte)

.dw0x07//bLength:0x07byte

.dw0x05//bDescriptorType:ENDPOINT

.dw0x02//bEndpointAddress:OUTendpoint2.dw0x02//bmAttributes:Bulk

.dw0x40,0x00//wMaxPacketSize:64byte.dw0x00//bInterval:ignored

關于請求:

第一,主機首先會發出一系列標準請求。其次,在標準請求完成之后,會發出兩個類請求:Bulk-OnlyMassStorageReset請求和GetMaxLUN請求。這兩個請求的格式可以在USBMassStorageClassBulk-OnlyTransport文檔中查詢。

Bulk-OnlyMassStorageReset沒有數據階段,只在狀態階段告訴主機設備的Reset過程完成與否。假使在狀態階段返回ACK,那么主機就認為設備已經Reset完畢并準備好接收CBW了。GetMaxLUN要求設備返回一個字節的數據給主機,以說明此USB設備有多少個規律設備。返回的這個數據就是最大的設備規律號(LogicUnitNumber),范圍是0到15。例如,假使返回2,那么代表有0、1、2三個規律設備。

2、USBMassStorage設備的Bulk數據交換流程

通過bulk端點進行的數據傳輸,都遵循這樣一個過程,即三個階段:CBW->DATA->CSW

CBW是一個數據塊,攜帶主機發給設備的SCSI命令。接收了CBW后,設備就可以從中知道在接下來的DATA階段中該干什么。DATA階段有三種狀況:無數據需要傳輸,IN傳輸(設備到主機)或OUT傳輸(主機到設備)。CSW階段反饋這次傳輸的結果給主機。

其中值得注意的是:

-在設備枚舉完成之后,主機發出的第一個bulkOUT事務就是請求向設備發出CBW。所以設備可以通過這第一次的bulkOUT事務來判定第一次bulk數據傳輸的開始。此后的bulk數據傳輸就依照上述的三個階段反復執行。也就是說,第一次傳輸CBW后,假使有數據要傳輸,那么就會經歷DATA階段,然后進入CSW階段;假使沒有數據要傳輸,則直接進入CSW階段,就此一次傳輸終止。接下來,假使又有傳輸,那么再發出CBW。因此,設備可以認為CSW完成后收到的下一個bulkOUT事務就是主機請求傳輸新的CBW。

-CBW[12](CBW數據塊的第13個字節)指明白傳輸方向,CBW[8-11]指明白傳輸的數據長度。實際上,CBW中的SCSI命令就暗含了數據要傳輸的方向和數據長度,由于SCSI規范中已明確規定這個命令所對應的數據格式。(在完整的應用中,要將CBW中的傳輸方向、數據長度與SCSI命令所說明的傳輸方向和數據長度做比較,不對應就要進行錯誤處理(MassStorageBulk-Only文檔中有相關描述),不過正常狀況下二者是匹配的,試驗的時候可以暫時不理)。

-CSW[12](CSW數據塊的第13個字節)這個字節很重要,它為0則表示此次傳輸成功,非0就是不成功。在DATA階段的數據傳完(或者無需數據傳輸)之后,主機遇發出IN事務請求設備返回CSW。假使CSW傳送

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論