ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

树莓派4B USB Gadget实战:变身U盘、串口与虚拟网卡的完整指南

树莓派4B USB Gadget实战:变身U盘、串口与虚拟网卡的完整指南 1. 为什么我建议你把手里的树莓派4B折腾成USB Gadget先讲一个我自己的场景。有次出差给客户调试一台工控机那台机器没有网口、没有光驱、系统还进不去现场唯一的接口就是两个USB口。手边只有一台跑着Ubuntu 22.04的笔记本和一块树莓派4B客户还在旁边等着场面相当狼狈。当时我急中生智把树莓派4B配成了USB Gadget模式瞬间在电脑上变成了一块U盘把调试脚本和驱动包拖进去再从工控机上取出来问题当场解决。所谓USB Gadget核心思路是把Linux设备自己的USB口从主机模式切换成设备模式让它能以U盘、串口、网卡等从设备身份接入另一台电脑。平时我们插U盘、插手机是电脑作为主机访问从设备而USB Gadget正好反过来——树莓派4B通过USB-C口插入电脑后它不再当小电脑而是把自己模拟成一个外设。这项能力的实用价值被很多人低估了。除了上面那种紧急场景它至少还能干这几件事在没有显示器和网络的条件下把树莓派变成一个串口调试工具直接给其他嵌入式设备下发命令批量给多台电脑部署文件不用来回插拔U盘把树莓派变成虚拟网卡让两台电脑在没有路由器的情况下组网通信配合脚本动态切换身份一个设备既能当U盘又能当串口满足复杂的测试需求。这篇文章把我的完整操作链路、踩过的坑、排查思路都记录下来。如果你手里正好有一块树莓派4B或者只是对Linux的USB子系统感兴趣这篇实战记录都能让你少走不少弯路。整个过程不需要额外的硬件只要一根合格的数据线。2. 动手前先检查三件事硬件批次、系统版本和线缆很多教程上来就让你跑命令结果有人成功有人失败问题多半出在准备工作没做到位。树莓派4B的USB Gadget能不能启用取决于三个硬性条件缺一不可。2.1 确认你的树莓派4B支持OTG模式树莓派全系列里只有带有USB-C接口的型号才完整支持USB Gadget树莓派4B正是其中之一。但有一点要特别留意早期批次的树莓派4B在USB-C接口的CC电阻上存在设计缺陷这个缺陷会导致无法通过标准USB-C to USB-C线缆进入设备模式。怎么判断自己的板子是哪个批次跑一下命令cat /proc/device-tree/model如果你拿到的板子生产日期在2020年之前建议直接改用USB-A to USB-C的数据线也就是一头是标准USB大口、另一头是USB-C小口。这种线在电气连接上绕过了CC引脚协商反而更稳定。我用的是绿联的USB-A转USB-C数据线实测没有任何问题。注意必须是支持数据传输的线不是纯充电线。充电线里面只有电源线没有数据线插上去电脑完全没反应。区分方法很简单——看线材上有没有SS或USB标志或者直接试插手机看能不能识别。2.2 系统内核与固件的版本要求USB Gadget依赖内核里的dwc2和libcomposite模块这两个模块在树莓派官方系统、Ubuntu for Raspberry Pi、Debian等主流系统中都已内置。建议使用64位系统不仅性能更好内核模块也更齐全。我实际使用的是Ubuntu 22.04 64位版本内核版本5.15整个过程没遇到模块缺失的问题。升级系统组件到最新版本避免老固件的兼容问题sudo apt update sudo apt upgrade -y sudo rpi-eeprom-updaterpi-eeprom-update是树莓派专用的EEPROM更新工具建议顺手检查一下确保bootloader支持USB Gadget的引导流程。2.3 准备必要的软件工具后面操作中会用到以下工具提前装好sudo apt install -y git vim dosfstools usbutilsdosfstools提供mkfs.vfat用于格式化FAT32分区usbutils提供lsusb用来验证设备是否被电脑识别vim编辑配置文件也可以用nano代替。这里多说一句为什么我强调用lsusb而不是dmesg因为lsusb更直观直接列出当前的USB设备树一眼就能看到树莓派是否被电脑识别很多问题都能通过这个命令快速定位。3. 开启OTG模式改好两个配置让内核加载dwc2驱动系统准备好之后第一步是让树莓派在开机时启用USB设备模式。这涉及两处配置/boot/config.txt和/boot/cmdline.txt。3.1 修改config.txt启用dwc2打开配置文件sudo vim /boot/config.txt在文件末尾追加一行dtoverlaydwc2这行配置告诉内核在启动时加载dwc2设备树覆盖层。dwc2是Synopsys DesignWare USB 2.0控制器的驱动树莓派SoC内部的USB控制器恰好就是这个IP核。加载了它USB-C口才能脱离主机模式切换到外设模式。3.2 修改cmdline.txt加载libcomposite模块再打开另一个配置sudo vim /boot/cmdline.txt找到以root开头的那一行在行尾追加modules-loaddwc2,libcomposite注意不要换行cmdline.txt是一整行的内容。追加后重启验证sudo reboot重启后检查dwc2是否正常加载lsmod | grep dwc2 lsmod | grep libcomposite如果都能看到对应的模块信息说明OTG模式已经启动成功。很多教程到这里就结束了但实际上还差关键一步——创建USB Gadget配置。4. U盘模式实战把树莓派变成一块真正的U盘这次实战的目标很明确树莓派通过USB-C线插入电脑后电脑端弹出一个可读写的U盘盘符。我把完整过程拆成四个阶段每个阶段的命令都可以直接抄。4.1 创建并格式化镜像文件作为U盘内容USB Gadget的U盘功能底层其实是借助内核的g_mass_storage模块把树莓派上的一个文件或块设备暴露给主机。最简单的做法是先创建一个固定大小的镜像文件把它挂载到树莓派本地把想共享的文件放进去再把整个镜像文件作为U盘的后盾交给内核。创建1GB的镜像文件sudo mkdir -p /opt/usb-gadget cd /opt/usb-gadget sudo dd if/dev/zero ofusbdisk.img bs1M count1024 sudo mkfs.vfat usbdisk.imgdd命令用于生成全零填充的文件mkfs.vfat把它格式化成FAT32文件系统。为什么用FAT32而不是ext4因为FAT32是Windows、macOS、Linux三大平台都能原生读写的格式兼容性最好。如果你只需要在Linux主机上用可以用ext4但为了通用性我建议还是FAT32。接下来把镜像挂载到本地方便往里面放文件sudo mkdir -p /mnt/usbdisk sudo mount -o loop /opt/usb-gadget/usbdisk.img /mnt/usbdisk sudo chmod 777 /mnt/usbdisk现在你可以往/mnt/usbdisk里拷入任何想通过U盘分享的文件。拷完后需要先卸载再切换为USB功能否则文件系统处于占用状态Gadget侧无法安全接管。4.2 编写gadget激活脚本这步是整个流程的核心。USB Gadget的参数通过ConfigFS配置ConfigFS是一个虚拟文件系统挂载在/sys/kernel/config下。用户通过创建目录、写入文件来配置设备描述符、接口、端点等信息。先挂载ConfigFS并创建gadget目录sudo modprobe libcomposite sudo mkdir -p /sys/kernel/config/usb_gadget/myusb cd /sys/kernel/config/usb_gadget/myusb然后写入设备描述符echo 0x1d6b idVendor # Linux Foundation的Vendor ID echo 0x0104 idProduct # Multifunction Composite Gadget echo 0x0100 bcdDevice echo 0x0200 bcdUSB mkdir -p strings/0x409 echo 20240901 strings/0x409/serialnumber echo RaspberryPi strings/0x409/manufacturer echo Mass Storage Gadget strings/0x409/product这里解释一下这几个ID的含义。idVendor和idProduct是USB设备的身份证主机通过它们识别设备类型。Linux Foundation的Vendor ID0x1d6b是内核虚拟设备常用的ID0x0104对应多功能复合设备。如果你想让电脑显示成某个特定厂商的U盘可以改成对应厂商的ID但要注意合法性别侵权。接着创建配置并添加mass_storage功能mkdir -p configs/c.1/strings/0x409 echo Mass Storage Config configs/c.1/strings/0x409/configuration echo 120 configs/c.1/MaxPower mkdir -p functions/mass_storage.usb0 echo /opt/usb-gadget/usbdisk.img functions/mass_storage.usb0/lun.0/file echo 1 functions/mass_storage.usb0/lun.0/removable echo 0 functions/mass_storage.usb0/lun.0/ro ln -s functions/mass_storage.usb0 configs/c.1/这里有个细节lun.0/file指定的是镜像文件的绝对路径内核通过它模拟SCSI磁盘。removable设为1表示这是一个可移动设备ro设为0表示可读写。如果你只想做只读U盘把ro设为1即可这在给客户分发资料时非常实用防止误删。最后绑定UDCUSB Device Controllerecho dwc2 UDC执行完这一步把树莓派通过USB-C线插入电脑电脑端应该立即识别出一块新的可移动磁盘。4.3 把脚本固化成服务开机自动激活手动执行脚本虽然方便但每次开机都要重跑一遍太麻烦。我的建议是把上面的命令整合成一个脚本并注册为systemd服务。创建脚本文件/usr/local/bin/enable_usb_gadget.sh#!/bin/bash # 挂载configfs modprobe libcomposite mount -t configfs none /sys/kernel/config GADGET_DIR/sys/kernel/config/usb_gadget/myusb # 清理已存在的配置 if [ -d $GADGET_DIR ]; then echo $GADGET_DIR/UDC 2/dev/null rm -rf $GADGET_DIR fi mkdir -p $GADGET_DIR cd $GADGET_DIR # 设备描述符 echo 0x1d6b idVendor echo 0x0104 idProduct echo 0x0100 bcdDevice echo 0x0200 bcdUSB mkdir -p strings/0x409 echo 20240901 strings/0x409/serialnumber echo RaspberryPi strings/0x409/manufacturer echo Mass Storage Gadget strings/0x409/product # 配置 mkdir -p configs/c.1/strings/0x409 echo Mass Storage Config configs/c.1/strings/0x409/configuration echo 120 configs/c.1/MaxPower # U盘功能 mkdir -p functions/mass_storage.usb0 echo /opt/usb-gadget/usbdisk.img functions/mass_storage.usb0/lun.0/file echo 1 functions/mass_storage.usb0/lun.0/removable echo 0 functions/mass_storage.usb0/lun.0/ro ln -s functions/mass_storage.usb0 configs/c.1/ # 启用 echo dwc2 UDC赋予执行权限sudo chmod x /usr/local/bin/enable_usb_gadget.sh创建systemd服务文件/etc/systemd/system/usb-gadget.service[Unit] DescriptionEnable USB Gadget Mode Afternetwork.target [Service] Typeoneshot ExecStart/usr/local/bin/enable_usb_gadget.sh RemainAfterExityes [Install] WantedBymulti-user.target启用服务sudo systemctl daemon-reload sudo systemctl enable usb-gadget sudo systemctl start usb-gadget这里有个容易踩的坑ExecStart脚本执行完后UDC会一直处于被绑定状态所以服务类型必须是oneshot加RemainAfterExityes否则systemd会认为服务已经退出下次开机可能不会正常触发。4.4 实测结果与性能数据我用同一根USB-A to USB-C线把树莓派4B接入一台Windows 11笔记本电脑系统提示发现新硬件并在文件管理器中出现了新的可移动磁盘。实际测速结果如下测试项目结果识别耗时约3秒顺序写入速度约12 MB/s顺序读取速度约15 MB/s文件系统类型FAT32读写速度受限于树莓派4B的USB 2.0控制器无法达到USB 3.0的速率。对于传配置文件、拷贝脚本这种轻量任务完全够用。5. 串口模式实战当树莓派变成USB转串口设备U盘模式只是热身串口模式才是嵌入式调试的灵魂技能。做硬件开发的人都知道很多开发板、路由器、工控机调试都需要串口但并不是每台电脑都自带DB9串口接口。这时候树莓派4B的USB Gadget串口功能就能派上大用场。5.1 配置串口Gadget串口模式同样基于ConfigFS核心是f_serial或f_acm功能。f_serial是通用串口f_acm是符合ACM规范的虚拟串口Windows和Linux对ACM的支持更完善推荐使用f_acm。在脚本中追加以下部分# 串口功能 mkdir -p functions/acm.usb0 # 关联到配置 ln -s functions/acm.usb0 configs/c.1/启用后把树莓派重新插入电脑主机端会新增一个串口设备Linux主机/dev/ttyACM0Windows主机COM3或类似编号想要区分U盘和串口功能可以重新创建gadget配置把两者都加进去这样树莓派插入电脑后会同时识别出U盘和串口两个设备。这种复合设备模式在调试场景中非常实用——一边通过串口下发命令一边用U盘拖入新的固件文件。5.2 主机侧连接与验证在Linux主机上可以用screen或minicom连接sudo apt install -y screen sudo screen /dev/ttyACM0 115200然后我们在树莓派侧做一个环回测试。可以借助socat工具把树莓派本地的一个伪终端桥接到gadget串口sudo apt install -y socat sudo socat -d -d /dev/ttyGS0,raw,echo0 /dev/ttyAMA0,raw,echo0不过更简单的验证方法是在树莓派侧直接往/dev/ttyGS0写数据echo Hello from Pi /dev/ttyGS0在主机侧如果能看到Hello from Pi说明通路正常。5.3 串口参数与权限问题串口通信常见的参数包括波特率、数据位、停止位、校验位。默认情况下g_serial驱动生成的/dev/ttyGS0设备波特率设置可能无效因为USB CDC ACM设备在主机侧会忽略波特率设置统一按USB全速或高速模式传输。这一点和传统UART串口不同很多新手会在这里疑惑半天——其实只要主机和从机两侧的波特率配置一致就行USB虚拟串口并不像物理串口那样严格要求波特率匹配。权限问题是另一个容易踩的坑。默认情况下/dev/ttyACM0的权限属于root用户和dialout组。如果你不想每次都用sudo需要把当前用户加入dialout组sudo usermod -aG dialout $USER修改完重新登录或重启生效。6. 高级玩法U盘和串口同时启用一台树莓派充当两种外设前面我提到复合设备这里展开说说。单功能gadget虽然简单但实际项目中更多需要的是既要又要——既是U盘又是串口有时候还要是网卡。树莓派4B的dwc2控制器支持多个function组合成一个复合设备这背后的机制是USB Composite Device。6.1 复合设备的端点分配逻辑每个USB功能需要占用端点Endpointdwc2作为USB 2.0高速控制器可用的端点数量是有限的。算一下账功能占用端点端点方向mass_storage2个Bulk IN Bulk OUT单向acm_serial2个Bulk IN Bulk OUT 1个中断端点双向中断ecm_network2个Bulk IN Bulk OUT单向所以同时启用U盘串口网卡是完全可行的控制器端点资源足够。不过要注意每个功能的配置文件中都必须在同一个config目录下创建符号链接多个功能链接到同一个configs/c.1才能组合成一个复合设备。6.2 完整三合一配置脚本把之前的内容整合这里给出一份完整的脚本同时启用U盘、串口、网卡ECM网卡虚拟以太网#!/bin/bash modprobe libcomposite mount -t configfs none /sys/kernel/config 2/dev/null GADGET_DIR/sys/kernel/config/usb_gadget/mycomposite if [ -d $GADGET_DIR ]; then echo $GADGET_DIR/UDC 2/dev/null rm -rf $GADGET_DIR fi mkdir -p $GADGET_DIR cd $GADGET_DIR # 基本描述符 echo 0x1d6b idVendor echo 0x0104 idProduct echo 0x0100 bcdDevice echo 0x0200 bcdUSB mkdir -p strings/0x409 echo 20240901 strings/0x409/serialnumber echo RaspberryPi strings/0x409/manufacturer echo Multi-function Composite strings/0x409/product # 配置 mkdir -p configs/c.1/strings/0x409 echo Composite Config configs/c.1/strings/0x409/configuration echo 120 configs/c.1/MaxPower # 功能1: 串口 mkdir -p functions/acm.usb0 # 功能2: 网卡(ECM) mkdir -p functions/ecm.usb0 # 设置主机和设备的MAC地址注意保持唯一 echo 48:6f:73:74:50:43 functions/ecm.usb0/host_addr echo 42:61:64:55:53:42 functions/ecm.usb0/dev_addr # 功能3: U盘 mkdir -p functions/mass_storage.usb0 echo /opt/usb-gadget/usbdisk.img functions/mass_storage.usb0/lun.0/file echo 1 functions/mass_storage.usb0/lun.0/removable echo 0 functions/mass_storage.usb0/lun.0/ro # 关联到同一个配置 ln -s functions/acm.usb0 configs/c.1/ ln -s functions/ecm.usb0 configs/c.1/ ln -s functions/mass_storage.usb0 configs/c.1/ # 启用 echo dwc2 UDC配置完成后树莓派插入电脑会同时看到串口、U盘和一个虚拟网卡。在Linux主机上执行ip addr能看到一个新的usb0网卡接口给它配一个IPsudo ip addr add 192.168.7.2/24 dev usb0 sudo ip link set usb0 up树莓派侧也配置好IPsudo ip addr add 192.168.7.1/24 dev usb0 sudo ip link set usb0 up两边互相ping一下通了就说明虚拟网卡链路正常。这个功能在没有路由器的临时组网场景中非常有用。6.3 复合设备与各系统兼容性我实测下来Windows对复合USB设备的识别有时会慢一些尤其是首次插入需要安装驱动。如果Windows只识别出其中一个功能另一个显示未知设备多半是驱动问题。去设备管理器里更新驱动选择自动搜索即可。Linux和macOS则基本是即插即用体验顺畅得多。7. 避坑实录我把所有踩过的坑一次说清楚玩USB Gadget这段时间踩坑无数。这里把最常见的几个问题整理成排查手册按现象-原因-解决方案的顺序讲方便你直接对照。7.1 电脑完全没反应大概率是线缆或端口问题这是出现频率最高的问题。树莓派插上电脑后电脑一点反应都没有lsusb也看不到任何新设备。排查步骤换一根确定支持数据传输的USB线最好用USB-A to USB-C确认树莓派已经正常启动SSH能连上或者风扇在转、电源灯亮确认dwc2和libcomposite已经加载lsmod | grep dwc2确认/sys/kernel/config/usb_gadget目录存在且非空最后看内核日志dmesg | tail -20。我遇到过一次很诡异的情况lsmod里明明有dwc2但UDC绑定时报错device or resource busy。最后发现是桌面环境下NetworkManager自动把USB网卡功能启用了占用了UDC。解决办法是禁用NetworkManager的USB网卡管理或者直接使用纯命令行系统跑gadget服务。7.2 Windows识别成未知设备树莓派在Windows下被识别为未知USB设备或者黄色感叹号最常见的原因是设备描述符里的PID/VID设置冲突。可以试试修改idProduct的值比如改成0x5702或者0x0100有些PID在Windows的驱动匹配逻辑里优先级更高。另外如果你的Windows系统是精简版可能缺少USB Composite Device驱动去设备管理器手动更新驱动选择从计算机的设备驱动程序列表中选择再选USB Composite Device即可。7.3 U盘模式挂载后无法写入插入电脑后能识别U盘但写入文件报错设备正忙或只读文件系统。多半是因为在树莓派侧没有卸载挂载点或者gadget的lun.0/ro被设成了1。还有一个隐蔽原因FAT32镜像文件在树莓派侧挂载时产生了日志或索引文件拔掉USB线后没有正常卸载导致文件系统处于不一致状态。Windows会把这种U盘标记为有问题的磁盘拒绝写入。解决办法是每次使用完树莓派侧先执行sync并卸载sudo sync sudo umount /mnt/usbdisk如果你需要频繁在U盘模式和树莓派本地访问之间切换建议写一个切换脚本先卸载gadget再挂载镜像到本地避免文件系统损坏。7.4 串口乱码串口连接后输出乱码绝大多数情况是波特率不匹配。检查主机侧和树莓派侧配置的波特率是否一致。如果确认一致还乱码尝试用stty -F /dev/ttyGS0 115200重新设置树莓派侧参数。USB虚拟串口虽然不强制要求波特率匹配但个别驱动实现会参考这个值统一设置更保险。7.5 网卡功能无法ping通ECM网卡配置好后两边都配了IP但还是ping不通先检查防火墙。树莓派Ubuntu系统默认可能启用了ufw或nftables放行规则后就通了sudo ufw allow from 192.168.7.0/24 sudo ufw allow to 192.168.7.0/24另外树莓派的usb0接口默认可能没有启用需要手动ip link set usb0 up。7.6 开机后gadget服务失败如果配置了systemd服务但开机后没生效执行systemctl status usb-gadget能看到具体报错。最常见的失败原因是/opt/usb-gadget/usbdisk.img路径不存在或者挂载点还没准备好。建议在服务配置中加上Afterlocal-fs.target确保文件系统已经挂载脚本里也加上文件存在性检查。8. 进阶思路动态切换Gadget身份一个设备多面手前面讲的都是静态配置——开机后固定是U盘或串口。但在实际项目中我经常需要一会儿当U盘、一会儿当串口甚至要临时切出一个网卡来。USB Gadget支持热切换不需要重启树莓派只要重新配置ConfigFS就可以。8.1 热切换原理UDC同一时间只能绑定一个有效的gadget配置。切换的思路是先解绑当前的gadget清理目录再创建新的gadget并重新绑定UDC。整个过程大约耗时1到2秒。写一个简单的切换脚本/usr/local/bin/switch_gadget.sh#!/bin/bash # 解绑当前gadget GADGET_DIR/sys/kernel/config/usb_gadget for dir in $GADGET_DIR/*; do if [ -f $dir/UDC ]; then echo $dir/UDC fi done # 清理 rm -rf $GADGET_DIR/myusb rm -rf $GADGET_DIR/mycomposite # 根据参数执行不同配置 case $1 in disk) /usr/local/bin/enable_usb_gadget.sh ;; serial) /usr/local/bin/enable_serial_gadget.sh ;; composite) /usr/local/bin/enable_composite_gadget.sh ;; *) echo Usage: $0 {disk|serial|composite} exit 1 ;; esac这样在PC端看到的设备类型会动态变化免去反复插拔的麻烦。8.2 配合udev规则自动切换如果你希望树莓派一插上某个特定主机就自动变成串口插上另一台就自动变成U盘可以利用udev规则监听USB电源事件。虽然这个场景比较小众但对自动化测试的团队来说非常香。还有一个更激进的想法把gadget配置写成嵌入式脚本在树莓派上跑一个服务通过GPIO按钮控制切换。我甚至见过有人用它做了一个硬件调试工具箱五个按键分别模拟U盘、串口、网卡、键盘和鼠标拿到任何一台电脑上都能即插即用。8.3 结合Docker的使用如果你喜欢用Docker管理环境也可以把gadget配置脚本封装到容器里。不过要注意容器要能够访问宿主机的/sys/kernel/config和/sys/class/udc需要在docker run时加--privileged标志或者使用--device挂载指定设备节点。我个人的建议是直接用systemd服务管理脚本就行没必要为了这个功能引入Docker保持系统简洁。9. 写在最后树莓派4B USB Gadget的更多可能性树莓派4B的USB Gadget功能本质上是把Linux强大的USB子系统能力释放了出来。当你理解了ConfigFS的操作逻辑后不只是U盘和串口你还可以模拟键盘、鼠标、游戏手柄、MIDI设备、蓝牙适配器等。这意味着用树莓派模拟HID键盘在自动化测试中批量输入指令把树莓派变成虚拟网卡配合NAT做随身路由器用树莓派模拟U盘 键盘的BadUSB攻击方式但用于合法的渗透测试和硬件安全研究。这里提醒一句模拟键盘鼠标等HID设备具有一定的双面性请确保你的使用场景合法合规只在自有设备和授权测试环境中操作。回到这篇文章的主题。如果你只是为了临时把树莓派变成U盘用5分钟确实足够——前提是你已经看完了前面这些坑尤其是线缆、批次、模块加载这三项。如果你是想长期作为调试工具我强烈建议按systemd服务的方式固化下来把串口、U盘、网卡整合成复合设备用起来才是真香。最后分享一个我个人很受用的习惯每次配置完gadget都会在树莓派上保留一份完整的配置脚本和README记录硬件批次、系统版本、使用的线缆型号。因为这类项目依赖的环境状态太多了三个月后再回来看没有记录基本等于重新踩坑。希望这篇文章能帮你少走这些弯路。
返回列表