感谢漂移菌分享的教程:将树莓派 4B USB-A 口上插入的 U 盘/移动硬盘,通过 USB-C 口透传给 Windows PC(或任意USB Host),使 PC 把树莓派识别为一个普通 USB 大容量存储设备。
1. 工作原理
基于 Linux USB Gadget ConfigFS + libcomposite + USB Mass Storage Function:
1. 树莓派 USB-C 口配置为 peripheral(设备)模式。
2. 脚本在 /sys/kernel/config/usb_gadget/ 下创建一个 composite gadget。
3. 将 USB-A 口接入的 U 盘(或指定块设备、 backing 文件)绑定到 Mass Storage 的 LUN。
4. 把 gadget 绑定到 UDC(USB Device Controller),PC 即可识别到一个新的 USB 磁盘。
5. 通过 udev 监听 USB-A 口磁盘插拔事件,自动重启 gadget 服务,实现“即插即用”。
2. 文件清单
系统级配套文件(不在本目录):
下载教程,以及教程所需要的文件:
https://edatec.cn/storage/zip/20260727/2c1b964bc3ea5b421925083733eed227.zip
3. 前置条件
树莓派 4B(或其他支持 USB gadget 的 Linux 设备)。
USB-C 口用于连接 PC;USB-A 口用于接入 U 盘/移动硬盘。
内核已启用 USB gadget 支持,并已加载或可调出 libcomposite 模块。
/boot/firmware/config.txt 中已添加:
dtoverlay=dwc2,dr_mode=peripheral
已安装 systemd 、 udev 、 lsblk 、 fuser (可选,用于占用检测)。
4. 安装与启用
4.1 放置脚本
确保脚本具有可执行权限:
chmod +x /usr/local/bin/usb-gadget.shchmod +x /usr/local/bin/usb-gadget-hotplug.sh
4.2 创建 systemd 服务
创建 /etc/systemd/system/usb-gadget.service :
[Unit]Description=USB Gadget Mass Storage (USB-A to USB-C pass-through)After=systemd-modules-load.serviceWants=systemd-modules-load.service[Service]Type=oneshotRemainAfterExit=yesExecStart=/usr/local/bin/usb-gadget.sh startExecStop=/usr/local/bin/usb-gadget.sh stopRestart=no[Install]WantedBy=multi-user.target
启用并启动:
systemctl daemon-reloadsystemctl enable usb-gadget.servicesystemctl start usb-gadget.service
4.3 创建 udev 规则
创建 /etc/udev/rules.d/99-usb-gadget.rules :
# 当 USB-A 口插入/拔出 USB 大容量存储设备时,重启 usb-gadget 服务ACTION=="add|remove", SUBSYSTEM=="block", ENV{ID_BUS}=="usb", ENV{DEVTYPE}=="disk",RUN+="/usr/local/bin/usb-gadget-hotplug.sh"
重载规则:
udevadm control --reload-rulesudevadm trigger
5. 配置项
在 /usr/local/bin/usb-gadget.sh 头部修改,或通过在 /etc/default/usb-gadget 等位置导出环境变量来覆盖。
5.1 工作模式
5.2 只读与兼容性
5.3 Gadget 资源名
5.4 电源属性
6. 使用命令
6.1 手动控制
# 启动 gadget/usr/local/bin/usb-gadget.sh start# 停止 gadget/usr/local/bin/usb-gadget.sh stop# 重启 gadget/usr/local/bin/usb-gadget.sh restart# 查看状态/usr/local/bin/usb-gadget.sh status
6.2 通过 systemd 控制
# 启动systemctl start usb-gadget.service# 停止systemctl stop usb-gadget.service# 重启systemctl restart usb-gadget.service# 查看状态systemctl status usb-gadget.service# 开机自启systemctl enable usb-gadget.service
6.3 热插拔行为
在 USB-A 口插入 U 盘/移动硬盘后,udev 会自动触发 usb-gadget-hotplug.sh 。
脚本通过 flock 加锁并延迟 2 秒防抖,然后执行 systemctl restart usb-gadget.service 。
拔出 U 盘后同样会触发重启,此时若未检测到 USB 盘,gadget 不会被激活。
7. 安全设计
防止暴露系统盘: is_system_disk() 会检查目标设备是否与根分区位于同一块物理磁盘,若是则拒绝。
自动卸载 Pi 侧挂载: unmount_disk() 在将磁盘交给 PC 之前,会先卸载树莓派上已挂载的分区,避免双端同时写入损坏文件系统。
进程占用检测:is_disk_busy() 使用 fuser 检查目标设备是否被占用,发现占用时给出警告。
幂等停止/启动:start_gadget() 会先调用 stop_gadget() 清理已有 gadget,再重新创建。
8. 常见问题
8.1 PC 识别不到设备
确认 /boot/firmware/config.txt 中已设置 dtoverlay=dwc2,dr_mode=peripheral 。
确认 USB-C 口连接的是 PC 的 USB 口,且线缆支持数据。
执行 ls /sys/class/udc/ ,应能看到 UDC 名称;若为空,说明 peripheral 模式未生效。
查看服务日志: journalctl -u usb-gadget.service -n 50 。
8.2 没有检测到 USB 盘
确认 U 盘插在 USB-A 口,且被系统识别: lsblk -d -n -p -o NAME,TRAN 应显示TRAN=usb 。
检查 USB_DISK_MODE 是否为 auto ,或手动改为 device 并设置 USB_DISK。
8.3 文件系统损坏警告
树莓派和 Windows 不能同时挂载同一分区。脚本会尝试卸载 Pi 侧挂载,但仍需确保没有手动挂载。
建议设置 READ_ONLY=1 用于只读场景,避免误写。
8.4 gadget 启动失败
检查 libcomposite 和 usb_f_mass_storage 模块是否能正常加载: modprobe libcomposite && modprobe usb_f_mass_storage 。
检查 ConfigFS 是否已挂载: mount | grep configfs 。
9. 版本说明
当前正式版: usb-gadget.sh (357 行)、 usb-gadget-hotplug.sh (13 行)。
.bak 文件为历史备份,当前正式版功能更完善,包含更安全的系统盘判断、进程占用检测、 usb_f_mass_storage 模块加载、Windows 兼容性 stall=1 等改进。
如需回滚,可用 .bak 文件替换当前脚本,但建议先备份当前版本。
10. 注意事项
该方案会直接将物理块设备暴露给 PC,操作不当可能导致数据丢失;生产使用前请充分测试。
若使用 file 模式,首次启动会自动创建 backing 文件并格式化为 FAT32。
修改配置后需重启服务生效: systemctl restart usb-gadget.service 。
一起学习、讨论更多的树莓派知识?扫码加入树莓派交流群!
Tip:对于刚接触树莓派的新手来说,面对英文文档、零散教程和复杂配置,很容易无从下手。pidoc.cn( https://pidoc.cn/ )就是为解决这些痛点而生的树莓派中文一站式学习平台,界面清晰、内容系统、更新及时,堪称新手入门的“保姆级” 网站,让零基础用户也能轻松上手树莓派。
195