while(upll_value!
=readl(S3C2410_UPLLCON)){
writel(upll_value,S3C2410_UPLLCON);
udelay(20);
}
}
修改drivers/usb/gadget/file_storage.c
staticvoidstart_transfer(structfsg_dev*fsg,structusb_ep*ep,
structusb_request*req,int*pbusy,
enumfsg_buffer_state*state)
{
intrc;
udelay(800);
……
}
配置内核支持U盘模拟
<*>USBGadgetSupport--->
USBPeripheralController(S3C2410USBDeviceController)--->
S3C2410USBDeviceController
[*]S3C2410udcdebugmessages
USBGadgetDrivers
File-backedStorageGadget
3、编译内核
#makezImage
#makemodules
在目录drivers/usb/gadget下生成g_file_storage.ko
加载驱动,测试功能
利用前面的生成的内核,启动系统后,加载g_file_storage.ko
#insmodg_file_storage.ko
#insmodg_file_storage.kofile=/dev/mtdblock2stall=0removable=1
0.03USB:
usb_gadget_register_driver()\'g_file_storage\'
0.04USB:
bindinggadgetdriver\'g_file_storage\'
0.05USB:
s3c2410_set_selfpowered()
g_file_storagegadget:
File-backedStorageGadget,version:
20October2004
g_file_storagegadget:
NumberofLUNs=1
g_file_storagegadget-lun0:
ro=0,file:
/dev/mtdblock3
0.06USB:
udc_enablecalled
smdk2410_udc:
Pull-upenable
连接设备到windows,windows系统会自动设备到一个新的U盘加入。
格式化U盘,存入文件。
卸载U盘后,在目标板上执行如下操作:
#mkdir/mnt/gadget
#mount-tvfat/dev/mtdblock2/mnt/gadget/
#ls
可以看到windows存入U盘的文件。
二、usbnet功能的实现
配置内核支持usbnet
<*>USBGadgetSupport--->
USBPeripheralController(S3C2410USBDeviceController)--->
S3C2410USBDeviceController
[*]S3C2410udcdebugmessages
USBGadgetDrivers
EthernetGadget(withCDCEthernetsupport)
[*]RNDISsupport
2、编译内核
#makezImage
#makemodules
在目录drivers/usb/gadget下生成g_ether.ko
3、加载驱动,测试功能
利用前面的生成的内核,启动系统后,加载g_ether.ko
#insmodg_ether.ko
#ifconfigusb0192.168.1.120
……
usb0Linkencap:
EthernetHWaddr5E:
C5:
F6:
D4:
2B:
91
inetaddr:
192.168.1.120Bcast:
192.168.1.255Mask:
255.255.255.0
UPBROADCASTRUNNINGMULTICASTMTU:
1500Metric:
1
RXpackets:
253errors:
0dropped:
0overruns:
0frame:
0
TXpackets:
43errors:
0dropped:
0overruns:
0carrier:
0
collisions:
0txqueuelen:
1000
RXbytes:
35277(34.4KiB)TXbytes:
10152(9.9KiB)
连接设备到windows,windows系统会提示安装驱动,根据提示安装上RNDIS驱动。
这个驱动可以在网络上找到。
此时windows会新生成一个网络连接,配置它的ip地址等信息。
然后就可以和目标系统通过USB实现网络通讯了。
LinuxUSBgadget设备驱动解析
(2)---驱动调试
作者:
刘洪涛,华清远见嵌入式学院金牌讲师。
这一节主要把在实现“linux模拟U盘功能”过程中的一些调试过程记录下来,并加以解析。
一、背景知识
1、USBMassStorage类规范概述
USB组织在universalSerialBusMassStorageClassSpaceification1.1版本中定义了海量存储设备类(MassStorageClass)的规范,这个类规范包括四个独立的子类规范,即:
1.USBMassStorageClassControl/Bulk/Interrupt(CBI)Transport
2.USBMassStorageClassBulk-OnlyTransport
3.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描述描述符,从中确定使用的存储类规范,从而确定协议。
确定通讯协议是我们调试的基础。
存储类规范是由接口描述符决定的。
接口描述符各项的定义义如下:
其中,bInterfaceClass、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="BBB",
.protocol_parm="SCSI",
……
默认的赋值如下:
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设备所执行的操作可用下图描述:
CBW的格式如下:
dCBWSignature:
CBW的标识,固定值:
43425355h(littleendian)。
dCBWTag:
主机发送的一个命令块标识,设备需要原样作为dCSWTag(CSW中的一部分)再发送给Host;主要用于关联CSW到对应的CBW。
dCBWDataTransferLength:
本次CBW命令要求在命令与回应之间传输的字节数。
如果为0,则不传输数据。
bmCBWFlags:
反映数据传输的方向,0表示来自Host,1表示发至Host;
bCBWLUN:
对于有多个LUN逻辑单元的设备,用来选择具体目标。
如果没有多个LUN,则写0。
bCBWCBLength:
命令的长度,范围在0~16.
CBWCB:
传输的具体命令,符合bInterfaceSubClass.中定义的命令规范,此处是SCSI
CSW命令格式如下:
dCSWSignature:
CSW的标识,固定值:
53425355h(littleendian)
dCSWTag:
设置这个标识和CBW中的dCBWTag一致,参照上面关于dCBWTag的解释
dCSWDataResidue:
还需要传送的数据,此数据根据dCBWDataTransferLength-本次已经传送的数据得到
bCSWStatus:
指示命令的执行状态。
如果命令正确执行,bCSWStatus返回0即可。
3、SCSI指令集
Bulk-Only的CBW中的CBWCB中的内容即为如下格式的命令块描述符(CommandBlockDescriptor)。
SCSI-2有三种字长的命令,6字节、10字节和12字节,MicrosoftWindows环境下支持12字节长的命令。
OperationCode:
操作代码,表示特定的命令。
高3位为GroupCode,共有8种组合,
即8个组,低5五位为CommandCode,可以有32种命令。
LogicolunitNumber:
为了兼容SCSI-1而设的,此处可以不必关心。
Logicalblockaddress:
为高位在前,低位在后的逻辑块地址,即扇区地址。
第2位为高位,第3、4、5依次为低位。
Transferlength:
为需要从逻辑块地址处开始传输的扇区数(比如在Write命令中)。
Parameterlistlength:
为需要传输的数据长度(比如在ModeSense命令中);
Allocationlength:
为初始程序为返回数据所分配的最大字节数,此值可以为零,表示不需要传送数据。
SCSI指令集的DirectAccesss类型存储介质的传输命令有许多,MassStorage协议只用到了其中的一些。
更多的SCSI指令参见:
http:
//en.wikipedia.org/wiki/SCSI_command
指令代码指令名称说明
04hFormatUnit格式化存储单元
12hInquiry索取器件信息
1BhStart/Stopload/unload
55hModeselect允许Host对外部设备设置参数。
5AhModeSense向host传输参数
EhPrevent/AllowMediumRemoval写保护
>28hRead(10)Host读存储介质中的二进制数据
A8hRead(12)同上,不过比较详细一点
25hReadCapacity要求设备返回当前容量
23hReadFormatCapacity查询当前容量及可用空间
03hRequestSense请求设备向主机返回执行结果,及状态数据
01hRexeroUnit返回零轨道
2BhSeek(10)为设备分配到特定地址
1DhSendDiagnostic执行固件复位并执行诊断
00hTestUnitReady请求设备报告是否处于Ready状态
2FhVerify在存储中验证数据
2AhWrite(10)从主机向介质写二进制数据
AAhWrite(12)同上,不过比较详细
2EhWriteandVerify写二进制数据并验证
对于不同的命令,其命令块描述符略有不同,其要求的返回内容也有所不同,根据相应的文档,可以对每种请求作出适当的回应。
比如,下面是INQUIRY请求的命令块描述符和其返回内容的数据格式:
如:
INQUIRY
命令描述符:
返回数据格式
Host会依次发出INQUIRY、ReadCapacity、UFIModeSense请求,如果上述请求的返回结果都正确,则Host会发出READ命令,读取文件系统0簇0扇区的MBR数据,进入文件系统识别阶段。
4、利用USBView观察结果
可通过USBView软件查看到USB设置阶段获取到的信息。
二、出现的主要问题
在调试过程中遇到了一个问题。
现象是:
在目标板加载完驱动后,即执行完:
#insmodg_file_storage.kofile=/dev/mtdblock2stall=0removable=1
后,接好USB线。
此时在windows端设备出有usbstorage设备加入,但出现不了盘符。
下面记录下调试过程。
三、调试过程
根据规范,当完成SCSI指令集中Inquiry命令时,可以出现盘符。
所以可以通过bushound软件查看通讯过程,找出原因。
下面是利用bushound工具在出现问题时采集到的数据。
DevPhaseDataInfoTimeCmd.Phase.Ofs
-------------------------------------------------------------------
26CTL80060001-00001200GETDESCRIPTR0us1.1.0
26DI12011001-00000010-2505a5a4-12030102........%