USB Gadget Framework 与 USB 大容量存储(UMS)
USB Gadget Framework 与 USB 大容量存储(UMS)
USB Gadget Framework 是 Linux 在嵌入式领域非常强大的能力,可以让设备通过 USB 模拟成:
- U 盘(Mass Storage)
- USB 网络适配器(RNDIS / ECM / NCM)
- HID 输入设备
- 串口 ACM
- 复合设备(组合多种功能)
本教程以 Radxa 瑞莎系列设备(如 ROCK 系列、E 系列、O 系列)为示例,展示如何在 Linux 上启用 USB Mass Storage Gadget(UMS),让设备通过 USB‑C 被主机识别为一块虚拟 U 盘。
UMS 的典型应用包括:
- 固件量产、镜像写盘
- 工控设备与 PC 之间的数据交换
- 模拟 U 盘用于系统调试
- 将 NAS 或网络镜像映射为 USB 存储设备
本文提供完整脚本、排错步骤和工程化实践,适合希望在嵌入式设备上启用 USB Gadget 的工程师参考。
一、前置要求(适用于 Radxa 官方系统)
在开始之前,请确认以下条件。
1. 内核支持 USB Gadget 与 ConfigFS
检查内核配置:
zcat /proc/config.gz | grep CONFIG_USB_CONFIGFS
zcat /proc/config.gz | grep CONFIG_USB_GADGET
关键配置必须包含:
CONFIG_USB_GADGET=y
CONFIG_USB_CONFIGFS=m 或 y
2. configfs 已挂载
检查 configfs 挂载状态:
mount | grep configfs
若未挂载:
sudo mount -t configfs none /sys/kernel/config
3. Radxa 设备的 UDC 名称
在瑞莎设备上,一般通过以下命令查看 UDC(USB Device Controller)名称:
ls /sys/class/udc
示例输出:
23000000.usb
该名称即为你的 gadget_name,后续脚本中会频繁使用。
二、加载 Gadget 所需内核模块
如果使用 Radxa 官方系统,且内核配置为:
CONFIG_USB_CONFIGFS=m
则需要手动加载模块:
sudo modprobe libcomposite
加载完成后,确认 gadget 子系统是否出现:
ls /sys/kernel/config
应包含如下目录:
usb_gadget/
三、初始化 Gadget(只需执行一次)
创建初始化脚本:
sudo nano /usr/local/sbin/gadget-init.sh
脚本内容如下:
#!/bin/bash
set -euo pipefail
GADGET_NAME=${1:-}
if [[ -z "${GADGET_NAME}" ]]; then
echo "用法: sudo $0 <gadget_name>"
exit 1
fi
if [[ $EUID -ne 0 ]]; then
echo "请以 root 权限运行"
exit 1
fi
# 确保 configfs 已挂载
if ! mountpoint -q /sys/kernel/config; then
mount -t configfs none /sys/kernel/config
fi
# 尝试加载 configfs gadget 核心
# 在 CONFIG_USB_CONFIGFS=m 时需要这一句
modprobe libcomposite 2>/dev/null || true
# 检查 usb_gadget 子系统是否存在
if [[ ! -d /sys/kernel/config/usb_gadget ]]; then
echo "当前内核已启用 CONFIG_USB_CONFIGFS=m,但未发现 /sys/kernel/config/usb_gadget。"
echo "请确认 libcomposite 模块已正确加载,或检查内核模块安装是否完整。"
exit 1
fi
G="/sys/kernel/config/usb_gadget/${GADGET_NAME}"
if [[ -d "${G}" ]]; then
echo "Gadget 已存在:${GADGET_NAME}"
exit 0
fi
mkdir -p "${G}"
cd "${G}"
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
mkdir -p strings/0x409
echo "1234567890" > strings/0x409/serialnumber
echo "Radxa" > strings/0x409/manufacturer
echo "Radxa USB Gadget" > strings/0x409/product
mkdir -p configs/r.1
mkdir -p configs/r.1/strings/0x409
echo "UMS Config" > configs/r.1/strings/0x409/configuration
echo "Gadget ${GADGET_NAME} 初始化完成。"
赋权并执行(以 23000000.usb 为例):
sudo chmod +x /usr/local/sbin/gadget-init.sh
sudo /usr/local/sbin/gadget-init.sh 23000000.usb
执行成功后,应能看到:
ls /sys/kernel/config/usb_gadget
23000000.usb
四、准备 UMS 镜像文件(可放在 NAS 上)
例如,在 Radxa 设备上挂载 NAS 共享目录:
sudo mount -t cifs -o username=user,password=pass \
//192.168.1.100/share /mnt/nas
创建一个 8GB 镜像文件:
sudo dd if=/dev/zero of=/mnt/nas/pcdisk.img bs=1M count=8192
不要在 Radxa(Linux)一侧挂载这个镜像,镜像中的文件系统将由主机电脑独占控制。
五、核心脚本:启用 / 禁用 UMS
创建控制脚本:
sudo nano /usr/local/sbin/ums.sh
脚本内容(生产可用版本):
#!/bin/bash
set -euo pipefail
ACTION=${1:-}
GADGET_NAME=${2:-}
IMG=${3:-}
if [[ $EUID -ne 0 ]]; then
echo "请以 root 身份运行"
exit 1
fi
G="/sys/kernel/config/usb_gadget/${GADGET_NAME}"
detect_udc() {
if [[ -f "${G}/UDC" ]]; then
local cur
cur=$(cat "${G}/UDC" 2>/dev/null || echo "")
if [[ -n "${cur}" ]]; then
echo "${cur}"
return
fi
fi
if [[ -e "/sys/class/udc/${GADGET_NAME}" ]]; then
echo "${GADGET_NAME}"
return
fi
local lst
mapfile -t lst < <(ls /sys/class/udc || true)
if [[ ${#lst[@]} -eq 1 ]]; then
echo "${lst[0]}"
return
fi
echo ""
}
disable_gadget() {
if [[ ! -d "${G}" ]]; then
return 0
fi
local cur
cur=$(cat "${G}/UDC" 2>/dev/null || echo "")
if [[ -n "${cur}" ]]; then
echo "" > "${G}/UDC" 2>/dev/null || true
fi
rm -f "${G}/configs/r.1/mass_storage.usb1" 2>/dev/null || true
rmdir "${G}/functions/mass_storage.usb1" 2>/dev/null || true
}
enable_gadget() {
local UDC
UDC=$(detect_udc)
if [[ -z "${UDC}" ]]; then
echo "无法确定 UDC"
exit 1
fi
disable_gadget
local loopdev
loopdev=$(losetup -j "${IMG}" | cut -d: -f1 || true)
if [[ -n "${loopdev}" ]]; then
losetup -d "${loopdev}" || true
fi
mount | grep -q " ${IMG} " && umount "${IMG}" || true
mkdir -p "${G}/functions/mass_storage.usb1"
echo 0 > "${G}/functions/mass_storage.usb1/stall"
echo 1 > "${G}/functions/mass_storage.usb1/lun.0/removable"
echo 0 > "${G}/functions/mass_storage.usb1/lun.0/ro"
echo 1 > "${G}/functions/mass_storage.usb1/lun.0/nofua"
echo "${IMG}" > "${G}/functions/mass_storage.usb1/lun.0/file"
ln -sf "${G}/functions/mass_storage.usb1" "${G}/configs/r.1/mass_storage.usb1"
echo "${UDC}" > "${G}/UDC"
echo "UMS 已启用"
}
case "${ACTION}" in
enable)
enable_gadget
;;
disable)
disable_gadget
echo "UMS 已关闭"
;;
*)
echo "用法:"
echo " sudo ums.sh enable <gadget_name> <image>"
echo " sudo ums.sh disable <gadget_name>"
;;
esac
赋予执行权限:
sudo chmod +x /usr/local/sbin/ums.sh
六、启用 UMS(Radxa 设备 → 上位机)
示例命令:
sudo ums.sh enable 23000000.usb /mnt/nas/pcdisk.img
此时 Radxa 设备会模拟成一个 U 盘,主机电脑应立即识别并弹出磁盘设备。
七、关闭 UMS
- 在主机上先弹出 USB 设备(安全移除)。
- 在 Radxa 设备上执行:
sudo ums.sh disable 23000000.usb
八、常见问题排查(建议收藏)
1. 主机完全无反应
查看 USB 控制器是否处于 Device 模式:
dmesg | grep -i dwc3
期望输出中包含:
mode: peripheral
若不是 peripheral 模式,UMS 将无法工作。
2. UDC 未绑定
查看当前 gadget 绑定状态:
cat /sys/kernel/config/usb_gadget/*/UDC
输出应为你的 gadget_name,否则说明尚未正确绑定。
3. 使用了只支持充电的 USB‑C 线
请务必使用支持 USB 数据传输的线缆,而非仅支持充电的线缆。
4. 镜像被 Linux 侧占用
检查是否有 loop 设备或挂载:
losetup -j <img>
mount | grep <img>
必须确保没有任何进程或挂载点正在占用该镜像文件,否则可能导致 UMS 行为异常或数据损坏。
更多推荐



所有评论(0)