STM32L4 USB双CDC&MSC设备固件实现Windows只读功能的问题
问题分析与解决方案
针对你遇到的STM32L4 CDC+MSC复合设备写保护状态下枚举缓慢、Windows不触发Request Sense的问题,我结合USB MSC协议和Windows设备枚举逻辑来拆解并给出解决方案:
一、为什么Windows不发送Request Sense命令?
主要有两个核心原因:
- 错误响应不符合BOT协议规范:你当前在
SCSI_Write10中直接返回-1,没有正确维护BOT状态机。按照BOT(Bulk-Only Transport)协议,当命令执行失败时,需要将设备状态设置为USBD_BOT_ERROR,而不是直接终止命令流程。这种不规范的响应会让Windows认为设备处于异常状态,跳过Request Sense请求,直接进入重试逻辑。 - 枚举阶段的命令优先级:Windows在磁盘枚举初期会执行一系列初始化操作(如读取分区表、验证磁盘属性),如果此时Write10命令失败,Windows会优先重试操作而非请求Sense数据——它默认枚举阶段的错误可能是临时的,而非设备属性(如写保护)导致的。
二、为什么写保护下返回-1会导致枚举缓慢?
Windows在磁盘枚举过程中会多次尝试写入磁盘元数据(比如分区表备份、系统隐藏文件),当你的设备返回不规范的错误时:
- Windows会触发多次命令重试,每次重试都有固定超时等待时间(通常秒级),累积起来就造成了枚举耗时极长。
- 复合设备的CDC和MSC共享USB带宽,MSC接口的异常响应会干扰整个设备的枚举流程,进一步拉长枚举时间。
三、实现读写/只读模式快速枚举的解决方案
1. 修正SCSI命令的错误处理流程
按照BOT协议规范处理写保护错误,让Windows能正确触发Request Sense:
static int8_t SCSI_Write10 (USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t *params) { USBD_CDC_MSC_HandleTypeDef *hmsc = pdev->pClassData; uint32_t len; if (hmsc->bot_state == USBD_BOT_IDLE) /* Idle */ { // ... 省略其他校验代码 ... /* Check If media is write-protected */ if(((USBD_CDC_StorageItfTypeDef *)pdev->pUserData)->IsWriteProtected(lun) !=0 ) { // 写入Sense数据 SCSI_SenseCode(pdev, lun, NOT_READY, WRITE_PROTECTED); // 设置BOT状态为错误,等待主机发送Request Sense hmsc->bot_state = USBD_BOT_ERROR; // 返回USBD_OK,让BOT状态机处理后续流程,而非直接终止 return USBD_OK; } // ... 省略其他代码 ... } }
同时确保SCSI_ProcessCmd中正确处理Request Sense分支,当设备处于USBD_BOT_ERROR状态时,能正确返回Sense数据。
2. 在枚举阶段提前告知Windows设备只读属性
最根本的解决方式是让Windows在枚举初期就知道设备是只读的,避免触发写入操作:
- 修改
SCSI_Inquiry函数,在返回的Inquiry数据中设置写保护标志位(SCSI规范中,Inquiry响应数据的第1字节第7位为写保护位,具体以你使用的SCSI版本为准)。 - 示例代码片段(调整Inquiry响应数据):
uint8_t inquiry_data[] = {0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00}; // 如果处于写保护状态,设置第1字节的第7位 if(IsWriteProtected(lun)) { inquiry_data[0] |= 0x80; } USBD_CtlSendData(pdev, inquiry_data, sizeof(inquiry_data));
这样Windows在枚举时就能识别设备为只读,不会尝试发送Write10命令,从根源上避免错误和重试。
3. 优化复合设备的USB配置
- 检查STM32CubeMX生成的复合设备描述符,确保添加了接口关联描述符(Interface Association Descriptor),让Windows能正确识别CDC和MSC两个独立接口,避免枚举混淆。
- 确认MSC的批量端点缓冲区大小配置合理(建议设置为USB_MAX_EP_SIZE,通常64字节),避免数据传输超时。
4. 验证Request Sense触发逻辑
使用USB调试工具(如USBlyzer)抓取枚举过程的USB数据包,确认:
- 当设备处于写保护状态时,Inquiry命令返回的写保护位是否正确。
- 如果仍触发Write10命令,设备的错误响应是否符合BOT规范,是否会触发Windows发送Request Sense。
内容的提问来源于stack exchange,提问作者Donotalo
相关产品推荐
相关产品推荐

