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命令块
};
交互细节:
-
主机准备CBW:
- 设置dCBWTag(唯一标识本次命令)
- 根据操作设置方向标志(读/写)
- 填入SCSI命令(如READ(10), WRITE(10), INQUIRY等)
- 计算期望的数据传输长度
-
主机发送CBW:
- 通过**批量输出端点(Bulk-Out Endpoint)**发送
- 发送固定31字节的CBW结构
-
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 ----------|
|<--- ... -------------|
| |
交互细节:
-
U盘准备数据:
- 根据CBW中的LBA地址和长度读取闪存数据
- 将数据放入内部缓冲区
-
主机读取数据:
// 主机执行批量读取 result = usb_bulk_read(device, bulk_in_endpoint, // 批量输入端点 buffer, // 数据缓冲区 length, // 期望长度 timeout); // 超时时间 -
数据传输:
- U盘通过**批量输入端点(Bulk-In Endpoint)**发送数据
- 数据分多次传输(取决于U盘缓冲区大小)
- 每批数据最大为端点最大包大小(通常512字节)
情况B:写入操作(主机 → 设备)
主机 U盘
| |
|--- CBW(写命令) ------>|
| |
|--- 数据块1 ---------->|
|--- 数据块2 ---------->|
|--- ... -------------->|
| |
交互细节:
-
主机发送数据:
// 主机执行批量写入 result = usb_bulk_write(device, bulk_out_endpoint, // 批量输出端点 buffer, // 数据缓冲区 length, // 数据长度 timeout); // 超时时间 -
U盘接收数据:
- 通过批量输出端点接收数据
- 验证数据完整性(CRC校验)
- 将数据写入闪存缓冲区
-
数据验证:
- 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=阶段错误
};
交互细节:
-
U盘准备CSW:
- 计算实际传输的数据量
- 设置状态码:
0x00(COMMAND_PASSED): 命令成功0x01(COMMAND_FAILED): 命令失败(如读取错误)0x02(PHASE_ERROR): 阶段错误(如无效的命令序列)0x03-0xFE: 保留0xFF: Vendor Specific错误
-
U盘发送CSW:
- 通过批量输入端点发送13字节CSW
-
主机接收并验证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;
}
关键要点总结
- 命令阶段:主机通过CBW告诉U盘要做什么
- 数据阶段:根据命令方向传输实际数据(可选)
- 状态阶段:U盘通过CSW报告执行结果
- 严格匹配:CSW的tag必须与对应的CBW匹配
- 错误恢复:通过状态码和剩余数据量实现可靠的错误处理
这种三阶段通信模型确保了USB存储设备的可靠性和互操作性,即使底层是复杂的闪存操作,主机也只需关注这三个标准阶段。
浙公网安备 33010602011771号