RIOT USBUS 大容量存储类(MSC)测试应用:将 MTD 设备导出为 U 盘
2026/9/20 14:51:38 网站建设 项目流程

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_lunremove_lunusb_attachusb_detachusb_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" 到主机。整个流程设计为:

  1. 列出板载 MTD 设备(add_lun不带参数);
  2. add_lun <编号>将一个或多个 MTD 设备注册为 LUN(Logical Unit Number);
  3. usb_attach将 USB 设备接入主机;
  4. 在主机侧看到/dev/sdX设备节点并正常读写;
  5. 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 operation

2. 编译与 VID/PID 配置

2.1 测试用 VID/PID 及其警告

README 特别强调:RIOT 不拥有任何 USB vendor ID 和 product ID。测试应用默认使用

  • USB_VID_TESTING = 0x1209
  • USB_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_VIDUSB_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_msec
  • auto_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>移除已注册的 LUNusbus_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_detachUSBOPT_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_SECTORPAGE_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_emulated

5. 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)执行以下步骤:

  1. 遍历接口链表找到 MSC 接口(USB_CLASS_MASS_STORAGE),失败返回-ENODEV
  2. 检查该 MTD 设备是否已被注册,是则返回-EBUSY
  3. lun_dev[]数组中寻找空槽,调用mtd_init(dev)初始化设备;
  4. 计算逻辑块大小block_size = page_size × pages_per_sector,若block_size × sector_count < 512返回-ENOMEM(容量不足,见第 6 节);
  5. 按需为数据传输分配(或 realloc)DMA 对齐缓冲区;
  6. 填写 LUN 描述符并返回 0。

lun_dev[]已满(超过MTD_NUMOF),返回-ENFILEusbus_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 明确列出了两条限制:

  1. 容量下限:总存储小于 512 字节的 MTD 设备无法用于 MSC。这与 USB 规范中每扇区 512 字节的报告粒度有关——从 msc.c 可以看到,写入时若page_size × pages_per_sector < 512,会计算sector_count = 512 / (page_size × pages_per_sector),通过一次擦写多个物理扇区来凑足 512 字节的逻辑扇区;
  2. flashpage 大小上限:flashpage 大小大于 4096 字节的 MTD 设备尚未实现,目前无法工作(对应 TODO 条目 "Add support for MTD devices with flashpage size > 4096")。

7. 运行流程小结

  1. 选择一块板卡(默认same54-xpro,可换成任何带 USB 设备端口的板卡);
  2. 若无板载 MTD,编译时追加USEMODULE=mtd_emulated(必要时通过 CFLAGS 覆盖容量参数);
  3. 烧录并启动,进入 shell;
  4. add_lun查看可用 MTD,add_lun <编号>注册 LUN(可注册多个);
  5. usb_attach接入主机,主机侧出现/dev/sdX设备;
  6. 使用完毕执行usb_detach断开,或usb_reset模拟重新插拔。

注意:根据 README,MSC 操作耗时取决于 MTD 设备性能与 USB 速度,大容量设备写入时可能耗时较长,属正常现象。

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询