RIOT USBUS 大容量存储类(MSC)测试应用:将 MTD 设备导出为 U 盘
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
导读
本文围绕 RIOT OS 测试套件中的tests/sys/usbus_msc测试应用展开,讲解如何借助 RIOT USBUS 设备栈,将板载 MTD(Memory Technology Device)存储设备以 USB Mass Storage Class(MSC)形式导出到主机,使其在 Linux/Windows/macOS 上以/dev/sdX磁盘分区的形式出现。读完本文,你将掌握该测试应用的编译参数、shell 交互命令(add_lun、remove_lun、usb_attach、usb_detach、usb_reset)、无板载存储时使用mtd_emulated进行 RAM 模拟的方法,以及 USBUS MSC 底层实现原理与已知限制。
1. 应用概览:用 USBUS 把 MTD 变成 U 盘
测试应用 README 明确指出:该应用使用 RIOT 的 USBUS 设备栈,将 MTD 设备关联起来并作为 Mass Storage Class 导出到主机。它属于 RIOT 的tests目录体系,是 USBUS MSC 实现的演示与验证样例。
应用启动后进入一个 RIOT shell。默认情况下 USB 设备不会自动挂载到主机,需要先在 shell 中执行命令、选择要导出的 MTD 设备,再手动 "attach" 到主机。整个流程设计为:
- 列出板载 MTD 设备(
add_lun不带参数); - 用
add_lun <编号>将一个或多个 MTD 设备注册为 LUN(Logical Unit Number); - 用
usb_attach将 USB 设备接入主机; - 在主机侧看到
/dev/sdX设备节点并正常读写; - 用
usb_detach随时断开。
从 main.c 的启动信息可以看到这一设计意图:
RIOT USB MSC test application Add one or more MTD devices as USB LUN Then use the attach command to connect your device and start USB operation2. 编译与 VID/PID 配置
2.1 测试用 VID/PID 及其警告
README 特别强调:RIOT 不拥有任何 USB vendor ID 和 product ID。测试应用默认使用
USB_VID_TESTING = 0x1209USB_PID_TESTING = 0x7d01
作为制造商 ID 与产品 ID。这两个 ID 来自 makefiles/usb-codes.inc.mk,其中还定义了usb_id_check目标:构建时会检查当前 VID/PID 是否命中测试保留值(详见该文件中基于dist/usb_id_testing的 grep 检查逻辑),命中会打印警告——这些 ID 不是唯一的,不得用于任何再分发、销售或量产设备,只能在测试环境中使用。
2.2 自定义 VID/PID
若要使用自己的 VID/PID,可在 makefile 或命令行设置USB_VID与USB_PID变量,例如:
USB_VID=1234 USB_PID=5678 BOARD=... make -C tests/pkg/tinyusb_cdc_msc在 测试应用的 Makefile 中,默认配置为:
USB_VID ?= $(USB_VID_TESTING) USB_PID ?= $(USB_PID_TESTING)usb-codes.inc.mk会把USB_VID/USB_PID转换为 C 编译宏CONFIG_USB_VID/CONFIG_USB_PID传入编译器;若未设置USB_VID,则保持默认测试 ID。需要注意:1209/7D00(RIOT 标准外设码)不允许被显式设置,构建会报错提示改用测试 ID 或自行申请 ID。
2.3 默认板卡与模块依赖
Makefile 默认BOARD ?= same54-xpro,并引入tests/Makefile.sys_common。核心模块依赖如下:
USEMODULE += auto_init_usbus USEMODULE += mtd USEMODULE += mtd_write_page USEMODULE += ps USEMODULE += shell USEMODULE += usbus_msc USEMODULE += ztimer_msecauto_init_usbus:启动时自动初始化 USBUS 栈(见下文第 5 节);mtd/mtd_write_page:提供 MTD 抽象与页写入能力;usbus_msc:MSC 类实现;shell/ps/ztimer_msec:提供交互式 shell、进程查看与毫秒定时(usb_reset命令中的 100ms 延时依赖 ztimer)。
3. shell 交互命令详解
对应 main.c 中注册的 5 个 shell 命令:
| 命令 | 功能 | 底层调用 |
|---|---|---|
add_lun <mtd dev> | 将指定 MTD 设备注册为新的 LUN;无参数时列出可用 MTD 设备 | usbus_msc_add_lun(usbus, mtd_dev) |
remove_lun <mtd dev> | 移除已注册的 LUN | usbus_msc_remove_lun(usbus, mtd_dev) |
usb_attach | 将 USB 设备 attach 到主机 | usbdev_set(usbus->dev, USBOPT_ATTACH, ...) |
usb_detach | 从主机 detach,停止 USB 操作 | usbdev_set(usbus->dev, USBOPT_ATTACH, ...)(DISABLE) |
usb_reset | 先 detach,延时 100ms 后再 attach | 组合上述两个命令 |
3.1 add_lun:列出并注册 MTD 设备
不带参数执行add_lun会打印用法并列出板载所有 MTD 设备:
usage: add_lun <mtd dev> MTD devices available: 0: MTD_DEV(0) 1: MTD_DEV(1) ...带参数执行时,通过atol(argv[1])解析设备编号,校验范围0 <= dev < MTD_NUMOF,随后调用mtd_dev_get(dev)取得设备指针并调用usbus_msc_add_lun()。失败时会打印:
Cannot add LUN device (error:<errno> <strerror>)3.2 remove_lun:撤销 LUN
remove_lun <mtd dev>调用usbus_msc_remove_lun()。若目标设备并未注册,返回-EAGAIN,命令打印MTD device was not registered。
3.3 usb_attach / usb_detach / usb_reset
usb_attach通过usbdev_set(usbus->dev, USBOPT_ATTACH, &enable, ...)(USBOPT_ENABLE)把 USB 设备接入主机;usb_detach以USBOPT_DISABLE断开。usb_reset则依次执行 detach、ztimer_sleep(ZTIMER_MSEC, 100)延时、再 attach,用于模拟插拔。
4. 用 mtd_emulated 在 RAM 中模拟 MTD
如果板卡没有真实的 MTD 设备(例如某些开发板只有 SPI Flash 但未接 MTD 驱动),可以启用mtd_emulated模块在 RAM 中模拟:
USEMODULE=mtd_emulated BOARD=... make ...4.1 默认参数
模拟设备默认配置为:
- 64 个 sector(扇区)
- 每个 sector 4 个 page
- 每个 page 128 字节
总容量为64 × 4 × 128 = 32768字节(32 KiB)。README 指出这是能够创建 FAT 文件系统分区的最小规模——main.c 中的注释也明确说明:要创建带 FAT 文件系统的分区,至少需要 64 个 sector。
对应的 C 宏定义(位于 main.c 的条件编译段,仅当MODULE_MTD_EMULATED启用时生效):
#ifndef SECTOR_COUNT #define SECTOR_COUNT 64 #endif #ifndef PAGES_PER_SECTOR #define PAGES_PER_SECTOR 4 #endif #ifndef PAGE_SIZE #define PAGE_SIZE 128 #endif MTD_EMULATED_DEV(0, SECTOR_COUNT, PAGES_PER_SECTOR, PAGE_SIZE);4.2 覆盖参数
这三个宏都可以通过 CFLAGS 覆盖,例如将 sector 数增加到 128:
CFLAGS='-DSECTOR_COUNT=128' USEMODULE=mtd_emulated BOARD=... make ...同理可调整PAGES_PER_SECTOR与PAGE_SIZE。调整后总容量与逻辑块大小随之变化,需保证满足 MSC 最小容量要求(见第 6 节限制)。
4.3 启用方式
在 测试应用的 Makefile 中,mtd_emulated被注释掉,按需取消注释即可:
# If your board does not provide a MTD for testing, use the following line # to emulate an MTD with 64 sectors with 4 pages of 128 bytes each in RAM. You # can override these parameters by SECTOR_COUNT, PAGES_PER_SECTOR and PAGE_SIZE. # USEMODULE += mtd_emulated5. USBUS MSC 底层实现要点
5.1 自动初始化链路
auto_init_usbus模块(sys/auto_init/usb/auto_init_usb.c)在系统启动时创建静态的usbus_msc_device_t msc实例,并在MODULE_USBUS_MSC使能时调用usbus_msc_init(&usbus, &msc),随后创建 USBUS 线程。测试应用在main()中通过usbus_auto_init_get()拿到该 USBUS 上下文指针,供各 shell 命令使用。
5.2 接口配置与端点
在 msc.c 的_init()中,MSC 接口被配置为:
- 接口类:
USB_CLASS_MASS_STORAGE - 子类:
USB_MSC_SUBCLASS_SCSI_TCS(0x06,SCSI 透明命令集) - 协议:
USB_MSC_PROTOCOL_BBB(0x50,Bulk-Only Transport)
并创建一对 Bulk 端点(IN/OUT 各一),端点数据长度由USBUS_MSC_EP_DATA_SIZE决定:高速 USB(启用MODULE_PERIPH_USBDEV_HS_UTMI/MODULE_PERIPH_USBDEV_HS_ULPI)时为 512 字节,全速时为 64 字节。
5.3 add_lun 的内部逻辑
usbus_msc_add_lun()(msc.c)执行以下步骤:
- 遍历接口链表找到 MSC 接口(
USB_CLASS_MASS_STORAGE),失败返回-ENODEV; - 检查该 MTD 设备是否已被注册,是则返回
-EBUSY; - 在
lun_dev[]数组中寻找空槽,调用mtd_init(dev)初始化设备; - 计算逻辑块大小
block_size = page_size × pages_per_sector,若block_size × sector_count < 512返回-ENOMEM(容量不足,见第 6 节); - 按需为数据传输分配(或 realloc)DMA 对齐缓冲区;
- 填写 LUN 描述符并返回 0。
若lun_dev[]已满(超过MTD_NUMOF),返回-ENFILE。usbus_msc_remove_lun()则查找匹配设备、清空对应槽位并释放缓冲区。
5.4 状态机与数据传输
MSC 设备内部维护状态机(msc.h):
WAITING -> WAIT_FOR_TRANSFER -> DATA_TRANSFER_IN / DATA_TRANSFER_OUT -> GEN_CSW- 控制传输:处理
USB_MSC_SETUP_REQ_GML(Get Max LUN)等 setup 请求(_control_handler); - 批量传输:
_read_xfer/_write_xfer负责 READ10/WRITE10 命令的数据搬运,通过mtd_read/mtd_write_page与 MTD 设备交互; - 出错时:stall 对应 Bulk 端点,并以
USB_MSC_CSW_STATUS_COMMAND_FAILED状态生成 CSW 通知主机。
5.5 自动导出选项
若启用 Kconfig 选项CONFIG_USBUS_MSC_AUTO_MTD(默认开启,见 sys/include/usb/usbus.h 与 sys/usb/usbus/msc/Kconfig),_init()会自动将所有板载 MTD 设备注册为 LUN,无需手动执行add_lun。
6. 已知限制
README 明确列出了两条限制:
- 容量下限:总存储小于 512 字节的 MTD 设备无法用于 MSC。这与 USB 规范中每扇区 512 字节的报告粒度有关——从 msc.c 可以看到,写入时若
page_size × pages_per_sector < 512,会计算sector_count = 512 / (page_size × pages_per_sector),通过一次擦写多个物理扇区来凑足 512 字节的逻辑扇区; - flashpage 大小上限:flashpage 大小大于 4096 字节的 MTD 设备尚未实现,目前无法工作(对应 TODO 条目 "Add support for MTD devices with flashpage size > 4096")。
7. 运行流程小结
- 选择一块板卡(默认
same54-xpro,可换成任何带 USB 设备端口的板卡); - 若无板载 MTD,编译时追加
USEMODULE=mtd_emulated(必要时通过 CFLAGS 覆盖容量参数); - 烧录并启动,进入 shell;
add_lun查看可用 MTD,add_lun <编号>注册 LUN(可注册多个);usb_attach接入主机,主机侧出现/dev/sdX设备;- 使用完毕执行
usb_detach断开,或usb_reset模拟重新插拔。
注意:根据 README,MSC 操作耗时取决于 MTD 设备性能与 USB 速度,大容量设备写入时可能耗时较长,属正常现象。
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考