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


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

    请添加图片描述

    前期准备

    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         
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

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

    大容量存储设备类:0x08

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

    接口协议代码:0x50

    0x08	bInterfaceClass      (Mass Storage Device Class)
    0x06	bInterfaceSubClass   (Transparent SCSI subclass)
    0x50	bInterfaceProtocol   (Bulk only transport)
    
    • 1
    • 2
    • 3

    Bulk only transport 协议

    命令块;数据块;状态块

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

    包头0x55534243 “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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 数据块(端点IN和端点OUT)
    • 状态块(设备发送,即端点IN):向主机发送状态完成封包,成为CSW(Command Status Wrapper)

    包头0x55534253 “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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    请添加图片描述

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

    首先U盘传输,命令块中,cbwcb[16] (usb_device_msc_cbw_t结构体),用于UFI命令(cbwcb[0]为命令标识符)

    /*! @brief UFI Commands code*/
    #define USB_DEVICE_MSC_INQUIRY_COMMAND (0x12U)
    #define USB_DEVICE_MSC_READ_10_COMMAND (0x28U)
    #define USB_DEVICE_MSC_READ_12_COMMAND (0xA8U)
    #define USB_DEVICE_MSC_REQUEST_SENSE_COMMAND (0x03U)
    #define USB_DEVICE_MSC_TEST_UNIT_READY_COMMAND (0x00U)
    #define USB_DEVICE_MSC_WRITE_10_COMMAND (0x2AU)
    #define USB_DEVICE_MSC_WRITE_12_COMMAND (0xAAU)
    #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)
    #define USB_DEVICE_MSC_READ_FORMAT_CAPACITIES_COMMAND (0x23U)
    #define USB_DEVICE_MSC_MODE_SENSE_10_COMMAND (0x5AU)
    #define USB_DEVICE_MSC_MODE_SENSE_6_COMMAND (0x1AU)
    #define USB_DEVICE_MSC_MODE_SELECT_10_COMMAND (0x55U)
    #define USB_DEVICE_MSC_MODE_SELECT_6_COMMAND (0x15U)
    #define USB_DEVICE_MSC_SEND_DIAGNOSTIC_COMMAND (0x1DU)
    #define USB_DEVICE_MSC_VERIFY_COMMAND (0x2FU)
    #define USB_DEVICE_MSC_START_STOP_UNIT_COMMAND (0x1BU)
    
    ......
        
    /*!
     * @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;
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    READ CAPACITY(0x25)

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

    请添加图片描述

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

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

    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盘设备制作完成
    请添加图片描述
    请添加图片描述

  • 相关阅读:
    detr目标检测算法源码详解
    CopyOnWriteArrayList 是如何保证线程安全的?
    登录认证,登录校验
    62. 如何通过增强(Enhancement) 的方式给 SAP ABAP 标准程序增添新功能
    枚举与#define 宏的区别
    Java项目:零食超市商城系统(java+SSM+JSP+jQuery+Mysql)
    Python基础之数据库:5、创建表的完整语法、MySQL数据类型
    电脑被删除的文件怎么恢复?2023年数据恢复方法分享
    1200万像素通过算法无失真扩展到1.92亿像素——加权概率模型收缩模型图像像素扩展算法
    py1_Linux/Windows 的 Python 安装以及运行方式
  • 原文地址:https://blog.csdn.net/weixin_38426553/article/details/126293412