zoukankan      html  css  js  c++  java
  • .Net Micro Framework USB Mass Storage功能实现

    1. 说明

    由于.Net Micro FrameworkUSB驱动架构中,没有为Mass Storage功能提供原生支持,所以除了要编写Mass Storage主体代码外,还需要在原有的USB驱动中添加部分枚举代码。其实从结构上来说,该部分代码应该添加在PAL层,不过考虑到这层代码为.Net Micro Framework Poring Kit Rtm 3.0标准代码,所以把这部分代码添加到我们自己编写的USB驱动之中去了。

    此外,由于Mass Storage功能需要不断地检测和处理USB端口的数据,需要一个进程(或线程)去进行驱动。.Net Micro Framework在应用层仅支持一个进程(单个用户程序),所以必须在应用程序中专开一个线程去进行驱动,考虑到这样实现需要用户做额外的工作,最后摒弃了这一实现。最终的做法是,在Mass Storage驱动中添加了时钟中断处理函数,Mass Storage被初始化后,该时钟中断被激活,以一个用户可设定的间隔去监控和处理USB端口的数据。

    在实现Mass Storage功能的代码中,并没有直接去读写相关Flash,而是借助PAL层的SectorCache模块间接访问Flash,这样有两个好处,一是读写有缓存,操作速度较快,二是程序比较通用,代码在不用修改的情况下可以访问不同的FlashSD卡等存储模块。

    Mass Storage驱动仅实现了一个功能子集,仅支持单个存储模块,不支持从PC机进行格式化(可通过本地提供的接口进行格式化,文件系统目前必须是FAT32)。

    2. USB Config

    Mass Storage要求的USB Config和我们.Net Micro Framework的标准驱动不同,一是PIDVID不同,由于Mass Storage设备是免驱动安装的,这PIDVID应该没有多大用处,但是由于以前PC上已经安装了该PIDVID.Net Micro Framework驱动,所以Mass Storage设备插入时,优先去找匹配PIDVID的设备驱动(这样也可以让一些厂家有机会安装自己专门的Mass Storage设备驱动),所以对我们已有该驱动的PC,肯定会有问题,所以我们仅需要调整一下VID的值即可。

    其二我们USB接口描述类中,接口类必须为0x08Mass storage class),子类为0x06SCSI transparent command set)或0x04UFI),接口协议为0x50Bulk-only transport)。

    1 子类表

     

    2 接口协议

     

    其它描述信息由于非关键,所以可修改,也可以不改。

     

    3. 调用接口

    Mass Storage驱动位于"DeviceCode"Drivers目录下, 属于通用驱动,其它设备都可以调用。包括如下三个文件:UsbMassStorage.hUsbMassStorage.cppUsbMassStorage_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();

     }

    4USB枚举代码添加

    Mass Storage类规范定义了两个请求:Get_Max_LUNMass Storage Reset,所有的Mass Storage类设备都必须支持这两个请求。

    处理GET MAX LUN命令时,我们返回实际的逻辑单元(LUN0~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会通过端点2Mass Storage设备发送各种命令,Mass Storage设备根据相应的命令,进行不同的应答。

    其命令、数据、状态相关的流程图如下:

     

    PC机发送的数据必须符合CBW格式(31byte,小端模式),而Mass Storage设备的应答,其格式必须符合CSW格式(13byte,小端模式)。至于中间过程传输的数据,根据不同的命令,格式也有不同地要求。

    5.1.1 CBW命令块(Command Block Wrapper

    3 CBW命令块

    dCBWSignature:常数0x43425355,标识为CBW命令块。

    dCBWTag: 由主机发送的CBW标签。设备应该在相关的CSWdCSWTag以相同的值应答主机。

    dCBWDataTransferLength 在本命令执行期间,主机期望通过Bulk-InBulk-Out端点传输的数据长度。如果为0,则表示这之间没有数据传输。

    bmCBWFlags 定义如下(Bit7 DirectiondCBWDataTransferLength0时,该值无意义)

     0= DataOut,数据从主机到设备

    1= DataIn, 数据从设备到主机

    Bit6 Obsolete 0

    Bits 5..0 Reserved 0

    bCBWLUN: 表示正在发送命令字的设备的逻辑单元号(LUN)。对于支持多个LUN的设备,主机设置相对应的LUN值。否则,该值为0

    bCBWCBLength: CBWCB的有效字节长度。有效值是在116之间。

    CBWCB 被设备解析执行的命令块。

    注:该部分是重中之重,通过对这部分的命令的解析,实现实际的Mass Storage功能。

    5.1.2 CSW状态块(Command Status Wrapper

    4 CSW状态块

    dCSWSignature: 常数0x53425355,标识为CSW状态块

    dCSWTag: 取相对应的CBWdCBWTag值。

    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的状态值是否为0Good Status),如果不为0,则PC机马上发送REQUEST_SENSE命令,询问出错的进一步信息。

    我们这里Sense Key的值直接设为0x05ILLEGAL REQUEST)即可,主要原因是PC端会询问各种命令,由于我们没有实现,返回的CSW状态都非Good Status而已。

    详细说明请参见《UFI Command Specification》。

    5.2.7 TEST_UNIT_READY命令

    在没有其它命令进行操作时,PC端会每隔一定时间,就会发送该命令,主要是为了探测Mass Storage设备是否存在(类似心跳信号)。

    由于该命令没有数据交互,我们直接返回状态Good StatusCSW状态块即可。

  • 相关阅读:
    Linux下sed,awk,grep,cut,find学习笔记
    Python文件处理(1)
    KMP详解
    Java引用详解
    解决安卓中页脚被输入法顶起的问题
    解决swfupload上传控件文件名中文乱码问题 三种方法 flash及最新版本11.8.800.168
    null id in entry (don't flush the Session after an exception occurs)
    HQL中的Like查询需要注意的地方
    spring mvc controller间跳转 重定向 传参
    node to traverse cannot be null!
  • 原文地址:https://www.cnblogs.com/yefanqiu/p/1415281.html
Copyright © 2011-2022 走看看