Zephyr RTOS的USB大容量存储:分区管理实现
在嵌入式系统开发中,USB大容量存储(USB Mass Storage Class, MSC)功能让设备能像U盘一样被主机识别,而分区管理则是实现多分区存储的核心。本文将详细介绍如何在Zephyr RTOS中实现USB MSC的分区管理,解决多存储区域隔离问题。
核心实现架构
Zephyr的USB MSC功能通过Bulk-Only传输协议与SCSI命令集实现通信,分区管理则通过逻辑单元号(LUN, Logical Unit Number)机制实现。每个分区对应一个独立LUN,主机通过不同LUN访问不同存储设备。
核心实现文件位于:
- subsys/usb/device/class/msc.c:MSC协议状态机与SCSI命令处理
- subsys/usb/device_next/class/usbd_msc.c:新一代USB MSC驱动框架
- subsys/usb/device_next/class/usbd_msc_scsi.c:SCSI命令解析与存储交互
分区管理关键实现
1. LUN配置与初始化
在Zephyr中,通过usbd_msc_lun结构体定义每个分区(LUN)的存储设备信息,包括设备名称、厂商信息和产品描述。
// 分区配置示例
static const struct usbd_msc_lun msc_luns[] = {
{
.disk = "SD", // 对应物理存储设备
.vendor = "Zephyr", // 厂商名称
.product = "SD Card", // 产品描述
.revision = "1.0" // 版本号
},
{
.disk = "SPIFFS",
.vendor = "Zephyr",
.product = "Internal Flash",
.revision = "1.0"
}
};
// 注册LUN配置
STRUCT_SECTION_ITEM_REGISTER(usbd_msc_lun, msc_luns[0]);
STRUCT_SECTION_ITEM_REGISTER(usbd_msc_lun, msc_luns[1]);
在MSC驱动初始化时,会遍历所有注册的LUN并初始化SCSI上下文:
// [subsys/usb/device_next/class/usbd_msc.c]
static int msc_bot_init(struct usbd_class_data *const c_data)
{
// ...
STRUCT_SECTION_FOREACH(usbd_msc_lun, lun) {
scsi_init(&ctx->luns[ctx->registered_luns++], lun->disk,
lun->vendor, lun->product, lun->revision);
}
// ...
}
2. SCSI命令处理与分区隔离
SCSI命令处理是分区隔离的核心,通过LUN字段区分不同分区请求。在msc_process_cbw函数中,根据CBW(命令块包装器)中的LUN值路由到对应的存储设备:
// [subsys/usb/device_next/class/usbd_msc.c]
static void msc_process_cbw(struct msc_bot_ctx *ctx)
{
struct scsi_ctx *lun = &ctx->luns[ctx->cbw.bCBWLUN]; // 获取LUN上下文
// ...
data_len = scsi_cmd(lun, ctx->cbw.CBWCB, cb_len, ctx->scsi_buf); // 执行SCSI命令
// ...
}
关键SCSI命令处理流程:
READ_CAPACITY:返回指定LUN的存储容量READ10/WRITE10:处理对应LUN的读写请求INQUIRY:返回LUN的设备信息(厂商、产品等)
3. 存储设备抽象层
Zephyr通过磁盘访问接口(Disk Access API)实现对不同存储设备的统一访问,为分区管理提供底层支持。主要接口包括:
// 磁盘访问API [include/zephyr/storage/disk_access.h]
int disk_access_init(const char *pdrv); // 初始化存储设备
int disk_access_read(const char *pdrv, uint8_t *data_buf,
uint32_t start_sector, uint32_t num_sector); // 读扇区
int disk_access_write(const char *pdrv, const uint8_t *data_buf,
uint32_t start_sector, uint32_t num_sector); // 写扇区
int disk_access_status(const char *pdrv); // 获取设备状态
在SCSI命令处理中,通过设备名称调用对应存储设备的接口:
// [subsys/usb/device_next/class/usbd_msc_scsi.c]
static int scsi_read_10(struct scsi_ctx *ctx, const uint8_t *cb)
{
// ...
ret = disk_access_read(ctx->disk, data_buf, lba, num_blocks);
// ...
}
配置与使用步骤
1. 启用USB MSC与多LUN支持
在项目配置文件(prj.conf)中添加:
# 启用USB MSC驱动
CONFIG_USB_DEVICE_STACK=y
CONFIG_USBD_MSC=y
CONFIG_USBD_MSC_INSTANCES_COUNT=1 # MSC实例数量
CONFIG_USBD_MSC_LUNS_PER_INSTANCE=2 # 每个实例支持的LUN数量(分区数)
# 启用存储设备支持(根据实际硬件选择)
CONFIG_DISK_ACCESS=y
CONFIG_DISK_ACCESS_SDHC=y # SD卡支持
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_SPIFFS=y # SPIFFS文件系统支持
2. 定义存储设备与分区映射
在板级设备树(DTS)中定义存储设备节点:
// 示例:SD卡与内部Flash设备树配置
&sdhc0 {
status = "okay";
label = "SD"; // 与LUN配置中的disk字段对应
};
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
storage_partition: partition@20000 {
label = "SPIFFS"; // 与LUN配置中的disk字段对应
reg = <0x20000 0x60000>;
};
};
};
3. 实现自定义分区管理逻辑
通过重写SCSI命令处理函数,可实现高级分区功能,如动态容量调整、访问权限控制等:
// 自定义SCSI命令处理示例(扩展READ CAPACITY命令)
static int custom_scsi_read_capacity(struct scsi_ctx *ctx, uint8_t *buf)
{
uint32_t capacity;
uint32_t block_size;
// 获取实际存储容量
disk_access_ioctl(ctx->disk, DISK_IOCTL_GET_SECTOR_COUNT, &capacity);
disk_access_ioctl(ctx->disk, DISK_IOCTL_GET_SECTOR_SIZE, &block_size);
// 根据分区策略调整容量(示例:限制为总容量的50%)
capacity = capacity / 2;
// 填充SCSI响应数据
sys_put_be32(capacity - 1, &buf[0]); // 最后LBA地址
sys_put_be32(block_size, &buf[4]); // 块大小
return 0;
}
测试与验证
1. 主机识别测试
将设备连接到PC,通过lsblk(Linux)或磁盘管理工具(Windows)查看是否识别到多个USB存储设备:
# Linux系统验证
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 1 14.9G 0 disk
└─sdb1 8:17 1 14.9G 0 part /media/user/Zephyr SD
sdc 8:32 1 476M 0 disk
└─sdc1 8:33 1 476M 0 part /media/user/Zephyr Flash
2. 功能验证
使用dd命令测试读写性能与分区隔离性:
# 向第一个分区写入测试数据
$ dd if=/dev/zero of=/dev/sdb1 bs=1M count=10
# 向第二个分区写入不同数据
$ echo "Zephyr MSC Test" > /media/user/Zephyr\ Flash/test.txt
高级优化与注意事项
1. 性能优化
- 缓冲区大小调整:通过
CONFIG_USBD_MSC_SCSI_BUFFER_SIZE配置SCSI数据缓冲区(默认512字节) - 中断优先级:提高USB中断优先级确保数据传输及时性
CONFIG_USB_DEVICE_DRIVER_PRIORITY=6
2. 可靠性设计
- 数据完整性:启用写缓存与同步机制
CONFIG_DISK_ACCESS_ERASE_BLOCK_SIZE=4096 - 错误处理:在SCSI命令处理中添加详细错误码返回
ctx->sense_key = SCSI_SENSE_MEDIUM_ERROR; ctx->asc = SCSI_ASC_WRITE_ERROR;
3. 安全性考虑
- 分区访问控制:在SCSI命令处理中添加权限检查
if (ctx->cbw.bCBWLUN == 1 && !is_authorized()) { ctx->sense_key = SCSI_SENSE_ACCESS_DENIED; return -EACCES; }
总结
Zephyr RTOS通过LUN机制与SCSI命令集实现了灵活的USB MSC分区管理,支持多存储设备的统一接入。开发者可通过配置LUN映射、扩展SCSI命令处理和优化存储访问,构建满足复杂应用需求的嵌入式存储解决方案。
核心优势:
- 架构灵活:LUN与物理设备解耦,支持动态配置
- 兼容性好:遵循USB MSC与SCSI规范,兼容主流操作系统
- 可扩展性强:通过分层设计便于添加新存储设备和分区策略
后续可进一步探索的方向:
- 动态LUN配置(热插拔存储设备)
- 加密分区与安全访问
- 分区容量动态调整
完整实现可参考Zephyr官方示例:samples/subsys/usb/msc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



