【USB设备设计】-- MSC 设备开发(U 盘设备)

大容量存储设备(如:U盘),是我们很常用的设备,本章将简单介绍USB MSC开发流程

请添加图片描述
mass-storage-bulk
ufi-command

前期准备

1.带USB 功能的MCU (笔者使用的NXP RT1062)

2.存储介质(ROM:EMMC 、FLASH、SD卡等 或 RAM)

U盘描述符

MSC描述符很好找到参考,笔者抓包自己的U盘,读取出如下描述符。将读取的描述符,拷贝到工程中用于开发

Device Descriptor:
------------------------------
0x12	bLength
0x01	bDescriptorType
0x0200	bcdUSB
0x00	bDeviceClass      
0x00	bDeviceSubClass   
0x00	bDeviceProtocol   
0x40	bMaxPacketSize0   (64 bytes)
0x058F	idVendor
0x6387	idProduct
0x0100	bcdDevice
0x01	iManufacturer   "Generic"
0x02	iProduct        "Teclast CoolFlash"
0x03	iSerialNumber   "B9C88F53"
0x01	bNumConfigurations

Configuration Descriptor:
------------------------------
0x09	bLength
0x02	bDescriptorType
0x0020	wTotalLength   (32 bytes)
0x01	bNumInterfaces
0x01	bConfigurationValue
0x00	iConfiguration
0x80	bmAttributes   (Bus-powered Device)
0x64	bMaxPower      (200 mA)

Interface Descriptor:
------------------------------
0x09	bLength
0x04	bDescriptorType
0x00	bInterfaceNumber
0x00	bAlternateSetting
0x02	bNumEndPoints
0x08	bInterfaceClass      (Mass Storage Device Class)
0x06	bInterfaceSubClass   (Transparent SCSI subclass)
0x50	bInterfaceProtocol   (Bulk only transport)
0x00	iInterface

Endpoint Descriptor:
------------------------------
0x07	bLength
0x05	bDescriptorType
0x01	bEndpointAddress  (OUT endpoint 1)
0x02	bmAttributes      (Transfer: Bulk / Synch: None / Usage: Data)
0x0200	wMaxPacketSize    (512 bytes)
0x00	bInterval         

Endpoint Descriptor:
------------------------------
0x07	bLength
0x05	bDescriptorType
0x82	bEndpointAddress  (IN endpoint 2)
0x02	bmAttributes      (Transfer: Bulk / Synch: None / Usage: Data)
0x0200	wMaxPacketSize    (512 bytes)
0x00	bInterval         

MSC设备接口描述符(Interface Descriptor)

大容量存储设备类:0x08

子类接口代码:0x06 (SCSI 传输协议指令集)

接口协议代码:0x50

0x08	bInterfaceClass      (Mass Storage Device Class)
0x06	bInterfaceSubClass   (Transparent SCSI subclass)
0x50	bInterfaceProtocol   (Bulk only transport)
Bulk only transport 协议

命令块;数据块;状态块
请添加图片描述

  • 命令块(主机发送,即端点OUT):叫做CBW(Command Block Wrapper),主要描述接下来数据的传输方向和大小

包头0x43425355 “USBC”

/*! @brief Command Block Wrapper(CBW) */
typedef struct _usb_device_msc_cbw
{
    uint32_t signature;          /*!< Byte 0-3 dCBWSignature*/
    uint32_t tag;                /*!< Byte 4-7 dCBWTag*/
    uint32_t dataTransferLength; /*!< Byte 8-11 dCBWDataTransferLength*/
    uint8_t flags;               /*!< Byte 12 bmCBWFlags*/
    uint8_t logicalUnitNumber;   /*!< Byte 13 bCBWLUN*/
    uint8_t cbLength;            /*!< Byte 14 bCBWCBLength*/
    uint8_t cbwcb[16];           /*!< Byte 15-30 CBWCB, CBWCB is used to store UFI command*/
} usb_device_msc_cbw_t;
  • 数据块(端点IN和端点OUT)
  • 状态块(设备发送,即端点IN):向主机发送状态完成封包,成为CSW(Command Status Wrapper)

包头0x53425355 “USBS”

/*! @brief Command Status Wrapper(CSW) */
typedef struct _usb_device_msc_csw
{
    uint32_t signature;   /*!< Byte 0-3 dCSWSignature*/
    uint32_t tag;         /*!< Byte 4-7 dCSWTag*/
    uint32_t dataResidue; /*!< Byte 8-11 dCSWDataResidue*/
    uint8_t cswStatus;    /*!< Byte 12 bCSWStatus*/
} usb_device_msc_csw_t;

请添加图片描述

批量数据传输中的一些内容

首先U盘传输,命令块中,cbwcb[16] (usb_device_msc_cbw_t结构体),用于UFI命令(cbwcb[0]为命令标识符)。UFI 命令规范是针对USB 移动存储

/*! @brief UFI Commands code*/
#define USB_DEVICE_MSC_INQUIRY_COMMAND (0x12U)         //索取器件信息
#define USB_DEVICE_MSC_READ_10_COMMAND (0x28U)         //Host读取存储介质中的二进制数据
#define USB_DEVICE_MSC_READ_12_COMMAND (0xA8U)         //同 28h
#define USB_DEVICE_MSC_REQUEST_SENSE_COMMAND (0x03U)   //请求设备向主机返回执行结果和状态数据
#define USB_DEVICE_MSC_TEST_UNIT_READY_COMMAND (0x00U) //请求设备报告是否处于Ready状态
#define USB_DEVICE_MSC_WRITE_10_COMMAND (0x2AU) //从主机向介质写二进制数据
#define USB_DEVICE_MSC_WRITE_12_COMMAND (0xAAU) //同 2Ah
#define USB_DEVICE_MSC_PREVENT_ALLOW_MEDIUM_REM_COMMAND (0x1EU) //写保护
#define USB_DEVICE_MSC_FORMAT_UNIT_COMMAND (0x04U) //格式化存储单元
#define USB_DEVICE_MSC_READ_CAPACITY_10_COMMAND (0x25U) //要求设备返回当前容量
#define USB_DEVICE_MSC_READ_CAPACITY_16_COMMAND (0x9EU) //同 9Eh 
#define USB_DEVICE_MSC_READ_FORMAT_CAPACITIES_COMMAND (0x23U) //查询当前容量和可用空间
#define USB_DEVICE_MSC_MODE_SENSE_10_COMMAND (0x5AU)   //向Host传输参数
#define USB_DEVICE_MSC_MODE_SENSE_6_COMMAND (0x1AU)    //同 1Ah
#define USB_DEVICE_MSC_MODE_SELECT_10_COMMAND (0x55U)  //允许Host对外部设备设置参数
#define USB_DEVICE_MSC_MODE_SELECT_6_COMMAND (0x15U)   //同 55h
#define USB_DEVICE_MSC_SEND_DIAGNOSTIC_COMMAND (0x1DU) //执行固件复位并执行诊断
#define USB_DEVICE_MSC_VERIFY_COMMAND (0x2FU) //在存储中验证数据
#define USB_DEVICE_MSC_START_STOP_UNIT_COMMAND (0x1BU) //Start Stop Load Unload

......
    
/*!
 * @brief Process usb msc ufi command.
 *
 * This function analyse the cbw , get the command code.
 *
 * @param handle          The device msc class handle.
 *
 * @retval kStatus_USB_Success              Free device msc class handle successfully.
 */
static usb_status_t USB_DeviceMscProcessUfiCommand(usb_device_msc_struct_t *mscHandle)
{
    usb_status_t status;
    usb_device_msc_ufi_struct_t *ufi = NULL;

    ufi = &mscHandle->mscUfi;
    if (USB_DEVICE_MSC_REQUEST_SENSE_COMMAND != mscHandle->mscCbw->cbwcb[0])
    {
        ufi->requestSense->senseKey                = USB_DEVICE_MSC_UFI_NO_SENSE;
        ufi->requestSense->additionalSenseCode     = USB_DEVICE_MSC_UFI_NO_SENSE;
        ufi->requestSense->additionalSenseQualifer = USB_DEVICE_MSC_UFI_NO_SENSE;
    }
    ufi->thirteenCase.hostExpectedDataLength = mscHandle->mscCbw->dataTransferLength;
    ufi->thirteenCase.hostExpectedDirection = (uint8_t)(mscHandle->mscCbw->flags >> USB_DEVICE_MSC_CBW_DIRECTION_SHIFT);
    /*The first byte of all ufi command blocks shall contain an Operation Code, refer to ufi spec*/
    switch (mscHandle->mscCbw->cbwcb[0])
    {
        /* ufi command operation code*/
        case USB_DEVICE_MSC_INQUIRY_COMMAND: /*operation code : 0x12*/
            status = USB_DeviceMscUfiInquiryCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_READ_10_COMMAND: /*operation code : 0x28 */
        case USB_DEVICE_MSC_READ_12_COMMAND: /*operation code : 0xA8 */
            status = USB_DeviceMscUfiReadCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_REQUEST_SENSE_COMMAND: /*operation code : 0x03*/
            status = USB_DeviceMscUfiRequestSenseCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_TEST_UNIT_READY_COMMAND: /*operation code : 0x00 */
            status = USB_DeviceMscUfiTestUnitReadyCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_WRITE_10_COMMAND: /*operation code : 0x2A */
        case USB_DEVICE_MSC_WRITE_12_COMMAND: /*operation code : 0xAA */
            status = USB_DeviceMscUfiWriteCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_PREVENT_ALLOW_MEDIUM_REM_COMMAND: /*operation code :0x1E */
            status = USB_DeviceMscUfiPreventAllowMediumCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_FORMAT_UNIT_COMMAND: /*operation code : 0x04*/
            status = USB_DeviceMscUfiFormatUnitCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_READ_CAPACITY_10_COMMAND: /*operation code : 0x25*/
        case USB_DEVICE_MSC_READ_CAPACITY_16_COMMAND: /*operation code : 0x9E*/
            status = USB_DeviceMscUfiReadCapacityCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_MODE_SENSE_10_COMMAND: /* operation code :0x5A*/
        case USB_DEVICE_MSC_MODE_SENSE_6_COMMAND:  /* operation code : 0x1A */
            status = USB_DeviceMscUfiModeSenseCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_MODE_SELECT_10_COMMAND: /*operation code : 0x55 */
        case USB_DEVICE_MSC_MODE_SELECT_6_COMMAND:  /*operation code : 0x15 */
            status = USB_DeviceMscUfiModeSelectCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_READ_FORMAT_CAPACITIES_COMMAND: /*operation code : 0x23 */
            status = USB_DeviceMscUfiReadFormatCapacityCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_SEND_DIAGNOSTIC_COMMAND: /*operation code : 0x1D*/
            status = USB_DeviceMscUfiSendDiagnosticCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_VERIFY_COMMAND: /*operation code : 0x2F*/
            status = USB_DeviceMscUfiVerifyCommand(mscHandle);
            break;
        case USB_DEVICE_MSC_START_STOP_UNIT_COMMAND: /*operation code : 0x1B*/
            status = USB_DeviceMscUfiStartStopUnitCommand(mscHandle);
            break;
        default:
            status = USB_DeviceMscUfiUnsupportCommand(mscHandle);
            break;
    }
    if ((USB_DEVICE_MSC_UFI_NO_SENSE != ufi->requestSense->senseKey) &&
        (USB_DEVICE_MSC_COMMAND_PASSED == mscHandle->mscCsw->cswStatus) &&
        (USB_DEVICE_MSC_REQUEST_SENSE_COMMAND != mscHandle->mscCbw->cbwcb[0]))
    {
        mscHandle->mscCsw->cswStatus = USB_DEVICE_MSC_COMMAND_FAILED;
    }
    return status;
}    

INQUIRY (0x12U)

插入U盘,第一条UFI命令就是 INQUIRY

请添加图片描述

其中inquiry 表示的数据结构如下(基本就描述该设备的基础信息了,什么设备类型,是否可移除,厂商信息,版本信息等等):

/*! @brief  UFI inquiry data format structure*/
typedef struct _usb_device_inquiry_data_fromat_struct
{
    uint8_t peripheralDeviceType; /*!< Peripheral Device Type*/
    uint8_t rmb;                  /*!< Removable Media Bit*/
    uint8_t versions;             /*!< ISO Version, ECMA Version, ANSI Version*/
    uint8_t responseDataFormat;   /*!< Response Data Format*/
    uint8_t additionalLength;     /*!< The Additional Length field shall specify the length in bytes of the parameters*/
    uint8_t reserved[3];          /*!< reserved*/
    uint8_t vendorInformatin[8];  /*!< Vendor Identification*/
    uint8_t productId[16];        /*!< Product Identification*/
    uint8_t productVersionLevel[4]; /*!< Product Revision Level*/
} usb_device_inquiry_data_fromat_struct_t;

READ FORMAT CAPACITIES (0x23U)

该命令会获取设备最大容量

请添加图片描述

如图,0x23命令请求,端点IN返回12Byte

端点IN数据信息结构,参考上图红线
[0:3]  Capacity List Header
[4:7]  Number of Blocks
[8]    Descriptor Code
[9:11] Block Length
    
/*! @brief  UFI capacity list header structure*/
typedef struct _usb_device_capacity_list_header_struct
{
    uint8_t reserverd[3];       /*!< reserved*/
    uint8_t capacityListLength; /*!< Capacity List Length*/
} usb_device_capacity_list_header_struct_t;

/*! @brief  UFI current maximum capacity structure*/
typedef struct _usb_device_current_max_capacity_descriptor_struct
{
    uint32_t blockNumber;               /*!< Number of Blocks*/
    uint32_t descriptorCodeBlockLength; /*!< Byte 4 Descriptor Code , byte 5-7 Block Length*/
} usb_device_current_max_capacity_descriptor_struct_t;

READ CAPACITY(0x25)

该命令会获取当前内存容量

请添加图片描述

如图,命令请求,端点IN返回8Byte

[0:3] 最后逻辑块地址
[4:7] 每块大小Byte

READ_10 READ_12(0x28 0xA8)

该命令获取当前存储设备实际数据
请添加图片描述

WRITE_10 WRITE_12(0x2A 0xAA)

该命令向当前存储设备写数据
请添加图片描述

TEST UNIT READY (0x00) REQUEST SENSE(0x03)

这两个指令都是主机频繁发送的命令,0x00 可查询逻辑单元是否准备好了,0x03 查询设备状态等等。

请添加图片描述

测试

U盘设备枚举成功后,需要将存储介质格式化,至于格式化成什么格式都可以(NFS FAT32 exFAT等),反正文件系统靠主机制作,若设备也想读取,那么就得开发对应的文件系统了,本章不扩展。好了一个U盘设备制作完成
请添加图片描述
请添加图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值