U盘工作流程详细说明

U盘工作流程详细说明(命令→数据→状态三阶段交互)

我来详细解释U盘与主机在命令、数据、状态三个阶段的完整交互过程。我们将以BOT(Bulk-Only Transport)协议为例,因为这是现代U盘最常用的协议。

总体交互流程图

主机 (Host)                 U盘 (Device)
   |                           |
   |--- 1. 命令阶段 (CBW) ---->|
   |                           |
   |<--- 2. 数据阶段 (可选) ---|
   |   或 --- 数据阶段 (可选) ->|
   |                           |
   |<--- 3. 状态阶段 (CSW) ---|
   |                           |

详细分阶段说明

阶段1:命令阶段 (Command Phase)

主机 → U盘:发送命令块包装器 (CBW)

/* CBW结构 (31字节) */
struct command_block_wrapper {
    u32 dCBWSignature;           // 签名: 0x43425355 ('USBC')
    u32 dCBWTag;                 // 命令标识符,用于匹配响应
    u32 dCBWDataTransferLength;  // 本次命令要传输的数据长度(字节)
    u8  bmCBWFlags;              // 方向标志:0x80=IN(读),0x00=OUT(写)
    u8  bCBWLUN;                 // 逻辑单元号(LUN),多LUN设备用
    u8  bCBWCBLength;           // 命令块长度(1-16字节)
    u8  CBWCB[16];              // SCSI命令块
};

交互细节

  1. 主机准备CBW

    • 设置dCBWTag(唯一标识本次命令)
    • 根据操作设置方向标志(读/写)
    • 填入SCSI命令(如READ(10), WRITE(10), INQUIRY等)
    • 计算期望的数据传输长度
  2. 主机发送CBW

    • 通过**批量输出端点(Bulk-Out Endpoint)**发送
    • 发送固定31字节的CBW结构
  3. U盘接收并解析CBW

    • 验证签名(0x43425355)
    • 提取SCSI命令和参数
    • 准备执行命令

示例:读取512字节数据的CBW

// READ(10)命令的CBW内容
CBW = {
    .dCBWSignature = 0x43425355,
    .dCBWTag = 0x12345678,       // 唯一标识
    .dCBWDataTransferLength = 512, // 期望读取512字节
    .bmCBWFlags = 0x80,          // IN方向(设备到主机)
    .bCBWLUN = 0,                // LUN 0
    .bCBWCBLength = 10,          // READ(10)命令长度
    .CBWCB = {
        0x28,                    // READ(10)操作码
        0x00,                    // 保留位
        // LBA地址 (4字节)
        0x00, 0x00, 0x00, 0x01,  // 读取LBA 1(第2个扇区)
        // 传输长度 (2字节)
        0x00, 0x01,              // 读取1个扇区
        0x00,                    // 控制字节
    }
};

阶段2:数据阶段 (Data Phase)

可选阶段:根据命令可能需要数据传输

情况A:读取操作(设备 → 主机)

主机                    U盘
 |                       |
 |--- CBW(读命令) ------>|
 |                       |
 |<--- 数据块1 ----------|
 |<--- 数据块2 ----------|
 |<--- ... -------------|
 |                       |

交互细节

  1. U盘准备数据

    • 根据CBW中的LBA地址和长度读取闪存数据
    • 将数据放入内部缓冲区
  2. 主机读取数据

    // 主机执行批量读取
    result = usb_bulk_read(device,
                           bulk_in_endpoint,  // 批量输入端点
                           buffer,            // 数据缓冲区
                           length,           // 期望长度
                           timeout);         // 超时时间
    
  3. 数据传输

    • U盘通过**批量输入端点(Bulk-In Endpoint)**发送数据
    • 数据分多次传输(取决于U盘缓冲区大小)
    • 每批数据最大为端点最大包大小(通常512字节)

情况B:写入操作(主机 → 设备)

主机                    U盘
 |                       |
 |--- CBW(写命令) ------>|
 |                       |
 |--- 数据块1 ---------->|
 |--- 数据块2 ---------->|
 |--- ... -------------->|
 |                       |

交互细节

  1. 主机发送数据

    // 主机执行批量写入
    result = usb_bulk_write(device,
                            bulk_out_endpoint,  // 批量输出端点
                            buffer,            // 数据缓冲区
                            length,           // 数据长度
                            timeout);         // 超时时间
    
  2. U盘接收数据

    • 通过批量输出端点接收数据
    • 验证数据完整性(CRC校验)
    • 将数据写入闪存缓冲区
  3. 数据验证

    • U盘可能内部验证写入的数据
    • 如果写入失败,在状态阶段报告错误

情况C:无数据操作

  • 某些命令如TEST UNIT READY、REQUEST SENSE不需要数据传输
  • 直接进入状态阶段

阶段3:状态阶段 (Status Phase)

U盘 → 主机:发送命令状态包装器 (CSW)

/* CSW结构 (13字节) */
struct command_status_wrapper {
    u32 dCSWSignature;           // 签名: 0x53425355 ('USBS')
    u32 dCSWTag;                 // 必须与对应CBW的dCBWTag匹配
    u32 dCSWDataResidue;         // 剩余数据量(实际传输与期望的差值)
    u8  bCSWStatus;              // 状态码: 0x00=成功,0x01=失败,0x02=阶段错误
};

交互细节

  1. U盘准备CSW

    • 计算实际传输的数据量
    • 设置状态码:
      • 0x00 (COMMAND_PASSED): 命令成功
      • 0x01 (COMMAND_FAILED): 命令失败(如读取错误)
      • 0x02 (PHASE_ERROR): 阶段错误(如无效的命令序列)
      • 0x03 - 0xFE: 保留
      • 0xFF: Vendor Specific错误
  2. U盘发送CSW

    • 通过批量输入端点发送13字节CSW
  3. 主机接收并验证CSW

    • 验证签名(0x53425355)
    • 验证dCSWTag与发送的CBW匹配
    • 检查状态码
    • 处理剩余数据量

完整示例:读取一个扇区(512字节)

让我们跟踪一个具体的读取操作:

步骤1:主机发送读取命令

// 主机准备并发送CBW
CBW = {
    .dCBWSignature = 0x43425355,
    .dCBWTag = 0x0001,
    .dCBWDataTransferLength = 512,
    .bmCBWFlags = 0x80,  // IN方向
    .bCBWLUN = 0,
    .bCBWCBLength = 10,
    .CBWCB = {0x28, ...}  // READ(10)命令
};

// 通过批量输出端点发送
usb_bulk_write(device, bulk_out_ep, &CBW, 31, 1000);

步骤2:U盘响应数据

// U盘接收CBW后:
1. 解析CBW,发现是READ(10)命令
2. 从闪存读取LBA 1的数据(512字节)
3. 通过批量输入端点发送数据

// 数据在USB总线上传输:
[批量IN事务1] 512字节数据包
[可选:如果数据 > 最大包大小,分多个事务]

步骤3:U盘发送状态

// U盘发送CSW
CSW = {
    .dCSWSignature = 0x53425355,
    .dCSWTag = 0x0001,      // 与CBW的tag匹配
    .dCSWDataResidue = 0,   // 完全传输了512字节
    .bCSWStatus = 0x00,     // 成功
};

// 通过批量输入端点发送
usb_bulk_write(device, bulk_in_ep, &CSW, 13, 1000);

错误处理流程

场景1:传输过程中断

主机:发送CBW → 等待数据 → 超时 → 发送复位命令
U盘:接收CBW → 准备数据 → 传输中断 → 收到复位 → 清理状态

场景2:数据校验错误

主机:发送CBW → 接收数据 → CRC错误 → 重试命令
U盘:接收CBW → 发送数据 → 主机报告错误 → 准备接收新命令

场景3:U盘繁忙

主机:发送CBW → 等待数据 → 超时 → 发送REQUEST SENSE命令
U盘:接收CBW → 处理中... → 返回CSW状态为失败
主机:发送REQUEST SENSE → 获取详细错误信息

时序考虑

典型时序

时间线 (ms)
0:  主机发送CBW (31字节) → 传输时间: ~0.1ms
1:  U盘处理命令 (内部延迟) → 处理时间: ~1-10ms
11: 数据传输 (512字节) → 传输时间: ~1ms
12: U盘发送CSW (13字节) → 传输时间: ~0.1ms

超时处理

// 主机端的超时设置
#define CBW_TIMEOUT    1000  // 命令阶段超时: 1秒
#define DATA_TIMEOUT   5000  // 数据阶段超时: 5秒  
#define CSW_TIMEOUT    1000  // 状态阶段超时: 1秒

多命令队列(现代U盘支持)

现代U盘支持命令队列,允许主机发送多个CBW而不必等待每个CSW:

主机:CBW1 → CBW2 → CBW3 → ...
U盘:处理CBW1 → 处理CBW2 → 处理CBW3 → ...
主机:← 数据1 ← 数据2 ← 数据3 ← ...
U盘:CSW1 → CSW2 → CSW3 → ...

在U-Boot中的具体实现

查看U-Boot中实际的传输函数:

/* usb_stor_BBB_transport 函数内部 */
static int usb_stor_BBB_transport(struct scsi_cmd *srb, struct us_data *us)
{
    int result;
    
    /* 阶段1:发送CBW */
    result = usb_stor_BBB_send_cbw(us, srb);
    if (result != USB_STOR_XFER_GOOD)
        return USB_STOR_TRANSPORT_ERROR;
    
    /* 阶段2:数据传输(如果需要) */
    if (srb->datalen > 0) {
        if (srb->direction == DMA_FROM_DEVICE) {
            // 读取数据
            result = usb_stor_BBB_bulk_in(us, srb->buff, srb->datalen);
        } else {
            // 写入数据
            result = usb_stor_BBB_bulk_out(us, srb->buff, srb->datalen);
        }
        
        if (result != USB_STOR_XFER_GOOD)
            return USB_STOR_TRANSPORT_ERROR;
    }
    
    /* 阶段3:接收CSW并验证 */
    result = usb_stor_BBB_get_csw(us);
    if (result != USB_STOR_XFER_GOOD)
        return USB_STOR_TRANSPORT_ERROR;
    
    return USB_STOR_TRANSPORT_GOOD;
}

关键要点总结

  1. 命令阶段:主机通过CBW告诉U盘要做什么
  2. 数据阶段:根据命令方向传输实际数据(可选)
  3. 状态阶段:U盘通过CSW报告执行结果
  4. 严格匹配:CSW的tag必须与对应的CBW匹配
  5. 错误恢复:通过状态码和剩余数据量实现可靠的错误处理

这种三阶段通信模型确保了USB存储设备的可靠性和互操作性,即使底层是复杂的闪存操作,主机也只需关注这三个标准阶段。

posted on 2025-12-29 11:32  zxddesk  阅读(4)  评论(0)    收藏  举报

导航