资讯动态

Linux板卡变身USB网卡:原理、配置与实战指南

发布时间:2026/8/14 8:43:19 来源:尧图企业网站定制
1. 项目缘起为什么要把Linux板卡变成USB网卡最近在折腾一个嵌入式项目客户要求设备在没有任何外部网络接口比如RJ45网口、Wi-Fi模块的情况下必须能通过USB线让上位机通常是Windows或另一台Linux主机直接访问其内部网络服务。这听起来有点绕简单说就是我的Linux板卡比如树莓派、香橙派或者任何一款跑着Linux的ARM板需要伪装成一个“USB网卡”插到电脑上电脑就能像连接普通有线网络一样获得一个IP地址并直接访问板卡上运行的Web服务器、SSH服务或者自定义的TCP/UDP应用。这个需求在工业数据采集、便携式测试设备、无头Headless设备调试等场景下非常常见。传统的做法是给板卡加个有线网口或者Wi-Fi模块但这会增加BOM成本、功耗和物理尺寸。而几乎所有的嵌入式板和电脑都标配USB接口利用USB Gadget功能将板卡虚拟成一个网络设备就成了一个既经济又高效的方案。在Linux内核中这个功能模块就叫USB Ethernet Gadget 或者更通俗地叫USB CDC Ethernet。很多人第一次接触这个概念会有点懵USB不是用来传文件、接鼠标键盘的吗怎么还能当网卡用其实USB协议本身定义了一大类“通信设备类”CDC其中就包含“以太网控制模型”ECM。Ethernet Gadget驱动就是让Linux设备扮演这个“USB网卡”的角色通过USB线向上位机提供一个标准的、虚拟的以太网接口。对上位机而言它只是识别到了一个新插入的“USB转以太网适配器”并为其安装通用驱动如Windows下的RNDIS或CDC ECM驱动整个过程对用户几乎是透明的。2. 核心原理与驱动选型Linux USB Gadget框架浅析要实现这个功能核心在于Linux内核的USB Gadget框架。这个框架允许一个Linux设备我们称之为“设备端”或“从设备”通过USB接口扮演成各种USB从属设备比如大容量存储U盘、串口、键盘、鼠标当然还有我们需要的网卡。2.1 Gadget框架与Function驱动你可以把USB Gadget框架想象成一个“角色扮演”舞台。内核提供了舞台Gadget框架而具体的“角色”功能则由各种Function驱动来实现。我们需要的就是g_ether这个Function驱动它专门负责实现USB Ethernet功能。当我们在设备端加载g_ether驱动时它会做以下几件事创建虚拟网络接口在设备端的Linux系统中会生成一个全新的网络接口通常命名为usb0。这个接口的行为和eth0、wlan0这类物理接口几乎一样可以配置IP、路由承载网络流量。实现USB协议栈驱动实现了USB CDC Ethernet或RNDIS协议。当设备通过USB线连接到主机时它会响应主机的枚举请求并告诉主机“我是一个USB以太网设备”。建立数据通道驱动会在USB的Bulk端点用于大数据量传输上建立双向数据通道。所有在虚拟接口usb0上收发的网络数据包以太网帧都会被驱动打包通过USB的Bulk端点与主机进行交换。2.2 主机端的视角RNDIS vs. CDC ECM对于主机尤其是Windows来说它需要安装对应的客户端驱动才能识别这个“USB网卡”。这里主要有两种协议RNDIS (Remote Network Driver Interface Specification) 这是微软主导的协议被Windows系统原生支持。也就是说当你将一个配置为RNDIS模式的Linux Gadget设备插入Windows电脑时Windows通常会自动识别并安装内置的“远程NDIS兼容设备”驱动无需用户额外操作。兼容性最好是面向Windows主机的首选。CDC ECM (Ethernet Control Model) 这是USB-IF标准组织定义的通用协议。macOS和现代Linux内核通常原生支持CDC ECM。但在一些旧版本Windows上可能需要手动安装驱动。g_ether驱动默认同时支持这两种协议并会在连接时进行协商。在实际项目中如果目标主机环境确定比如全是Windows 7/10/11那么RNDIS的即插即用体验是最好的。2.3 与USB网络共享的区别这里要澄清一个常见的混淆点USB Ethernet Gadget和USB网络共享例如安卓手机的“USB网络共享”功能在技术原理上是相似的但角色是反的。USB Ethernet Gadget Linux板卡作为“从设备”扮演一个被动的、提供网络接口的网卡。主机是网络的“控制者”。USB网络共享 手机作为“从设备”但它扮演的是一个网络通路将手机自身的移动网络4G/5G或者Wi-Fi网络通过USB共享给主机。此时手机内部的网络栈更复杂包含了路由和NAT。我们的项目属于前者板卡自己就是一个网络终端它通过USB网卡接口将自己融入主机的网络。3. 实战配置从内核到系统的完整步骤理论讲完我们进入实操。假设你手头有一块运行Linux的板卡以树莓派Raspberry Pi OS为例其他发行版原理相通并且已经通过串口或SSH登录。3.1 第一步内核配置检查与驱动加载首先要确保你的内核编译时已经包含了必要的模块。检查当前配置# 查看内核是否支持USB Gadget和Ethernet功能 zcat /proc/config.gz | grep -E “CONFIG_USB_GADGET|CONFIG_USB_ETH”或者查看/boot/config-$(uname -r)文件。你需要看到以下关键配置为y或mCONFIG_USB_GADGETy CONFIG_USB_GADGETFSm CONFIG_USB_CONFIGFSy CONFIG_USB_CONFIGFS_F_FSy CONFIG_USB_CONFIGFS_UEVENTy CONFIG_USB_CONFIGFS_SERIALy CONFIG_USB_CONFIGFS_RNDISy CONFIG_USB_CONFIGFS_ECMy CONFIG_USB_CONFIGFS_ETHERy CONFIG_USB_CONFIGFS_SUBSETy对于树莓派等常见板卡官方内核通常已包含这些模块。如果缺失你需要重新配置和编译内核这是一个大工程此处不展开。动态加载驱动模块最直接的方法是使用g_ether模块。但更现代、更灵活的方式是使用configfs接口它允许在用户空间动态配置Gadget功能。# 1. 加载基础Gadget驱动模块如果尚未加载 sudo modprobe libcomposite sudo modprobe usb_f_rndis sudo modprobe usb_f_ecm sudo modprobe usb_f_eem sudo modprobe usb_f_ecm_subset # 2. 使用 configfs 创建 Gadget这里以创建一个名为“g1”的Gadget为例 sudo mkdir -p /sys/kernel/config/usb_gadget/g1 cd /sys/kernel/config/usb_gadget/g1 # 3. 设置USB Vendor和Product ID (可以自定义但建议使用Linux Foundation的ID以示规范) echo “0x1d6b” idVendor # Linux Foundation echo “0x0104” idProduct # Multifunction Composite Gadget echo “0x0100” bcdDevice # Device version echo “0x0200” bcdUSB # USB 2.0 # 4. 设置字符串描述符主机设备管理器里会显示这些信息 mkdir -p strings/0x409 echo “0123456789” strings/0x409/serialnumber echo “My Manufacturer” strings/0x409/manufacturer echo “Ethernet Gadget” strings/0x409/product # 5. 创建配置 mkdir -p configs/c.1 mkdir -p configs/c.1/strings/0x409 echo “RNDIS” configs/c.1/strings/0x409/configuration # 6. 创建并关联Ethernet Function mkdir -p functions/rndis.usb0 # 创建RNDIS功能关联到usb0接口 # 或者使用CDC ECM: mkdir -p functions/ecm.usb0 # 7. 将Function绑定到配置 ln -s functions/rndis.usb0 configs/c.1/ # 8. 指定USB控制器非常重要树莓派通常是dwc2 echo “dwc2” UDC # 对于树莓派Zero/Zero W/4B控制器是dwc2。其他板卡可能是“musb-hdrc”或“ci_hdrc.0”需查手册。执行完最后一步echo “dwc2” UDC后如果一切正常你应该立刻能在系统里看到一个新的网络接口usb0使用ip link show命令查看并且主机端会发出“检测到新硬件”的提示音。3.2 第二步网络接口配置与IP分配现在设备端有了usb0接口我们需要给它配置IP地址并设置路由。在设备端Linux板卡配置# 给usb0接口分配一个静态IP例如 192.168.7.2 sudo ip addr add 192.168.7.2/24 dev usb0 sudo ip link set usb0 up # 验证接口状态 ip addr show usb0 # 应该显示类似inet 192.168.7.2/24 scope global usb0注意这里选择的IP网段192.168.7.0/24是社区一个约定俗成的常用网段用于USB网络。你可以选择任何私有IP段但要确保不与设备或主机上其他网络冲突。在主机端Windows/Linux/macOS配置Windows: 插入设备后等待驱动自动安装完成。打开“网络连接”你会看到一个名为“远程NDIS兼容设备”或类似的新网络适配器。为其设置静态IP例如192.168.7.1子网掩码255.255.255.0网关可以不设或设为设备端IP192.168.7.2。Linux/macOS: 系统通常会自动识别并创建enx...或usb0接口。同样需要手动配置IP# Linux主机示例 sudo ip addr add 192.168.7.1/24 dev enx001122334455 # 接口名请根据实际情况修改 sudo ip link set dev enx001122334455 up3.3 第三步测试连通性与服务访问配置好IP后就可以测试了。从主机ping设备# 在主机命令行执行 ping 192.168.7.2如果看到回复恭喜你物理链路和IP层已经通了。从设备ping主机# 在设备端执行 ping 192.168.7.1双向ping通是基础。访问设备上的服务假设你的板卡上运行着一个轻量级Web服务器如lighttpd或nginx监听在80端口。现在你可以在主机的浏览器里直接输入http://192.168.7.2来访问它。同样SSH连接也是可以的ssh pi192.168.7.2。4. 生产环境部署自动化脚本与开机自启每次插拔都手动执行一堆命令是不现实的。我们需要让配置在系统启动时自动完成。4.1 创建自动化脚本在设备端/usr/local/bin/下创建一个脚本例如setup_usb_gadget.sh#!/bin/bash # setup_usb_gadget.sh MODULES“libcomposite usb_f_rndis” GADGET_DIR“/sys/kernel/config/usb_gadget/g1” FUNCTION“rndis.usb0” CONTROLLER“dwc2” # 根据你的板卡硬件修改 # 加载内核模块 for module in $MODULES; do modprobe $module done # 检查并创建Gadget配置 if [ ! -d $GADGET_DIR ]; then mkdir -p $GADGET_DIR cd $GADGET_DIR echo 0x1d6b idVendor echo 0x0104 idProduct echo 0x0100 bcdDevice echo 0x0200 bcdUSB mkdir -p strings/0x409 echo “00000001” strings/0x409/serialnumber echo “My Company” strings/0x409/manufacturer echo “USB Ethernet Device” strings/0x409/product mkdir -p configs/c.1 mkdir -p configs/c.1/strings/0x409 echo “Ethernet Config” configs/c.1/strings/0x409/configuration mkdir -p functions/$FUNCTION ln -s functions/$FUNCTION configs/c.1/ # 启用Gadget echo $CONTROLLER UDC fi # 配置网络接口等待接口出现 sleep 2 if ip link show usb0 /dev/null 21; then ip addr flush dev usb0 2/dev/null ip addr add 192.168.7.2/24 dev usb0 ip link set dev usb0 up echo “USB Gadget Ethernet configured on usb0 with IP 192.168.7.2” else echo “Error: usb0 interface not found!” exit 1 fi给脚本执行权限sudo chmod x /usr/local/bin/setup_usb_gadget.sh4.2 配置系统服务Systemd创建Systemd服务文件/etc/systemd/system/usb-gadget-eth.service[Unit] DescriptionUSB Ethernet Gadget Configuration Afterlocal-fs.target network.target Beforenetwork-online.target Wantsnetwork-online.target [Service] Typeoneshot RemainAfterExityes ExecStart/usr/local/bin/setup_usb_gadget.sh ExecStop/bin/bash -c “echo ‘’ /sys/kernel/config/usb_gadget/g1/UDC 2/dev/null; rm -rf /sys/kernel/config/usb_gadget/g1” StandardOutputjournal [Install] WantedBymulti-user.target然后启用并启动服务sudo systemctl daemon-reload sudo systemctl enable usb-gadget-eth.service sudo systemctl start usb-gadget-eth.service这样每次板卡启动USB Ethernet Gadget就会自动配置好。主机一插上USB线网络即刻就绪。5. 进阶调优与排坑指南在实际项目中仅仅“通”了还不够还需要稳定、高效。下面分享几个关键的经验点。5.1 性能优化MTU与USB传输模式USB 2.0的理论带宽是480Mbps但实际网络吞吐量受很多因素影响。usb0接口的默认MTU最大传输单元是1500字节这是标准以太网帧的大小。但在USB传输中有时调整MTU能提升效率。尝试调整MTU# 在设备端尝试设置一个稍小的MTU比如1450或1400 sudo ip link set dev usb0 mtu 1450然后在主机端也进行相应设置。使用iperf3工具进行带宽测试对比不同MTU下的表现# 在设备端运行iperf3服务器 iperf3 -s # 在主机端运行客户端 iperf3 -c 192.168.7.2USB控制器模式确保你的板卡USB硬件工作在正确的模式设备模式而非主机模式。对于树莓派可能需要检查/boot/config.txt中是否设置了dtoverlaydwc2,dr_modeperipheral。5.2 稳定性保障避免接口抖动与IP冲突接口命名持久化有时系统重启后usb0接口可能会因为内核模块加载顺序问题而延迟出现或命名不稳定。可以在Systemd服务的ExecStart中加入重试逻辑或者使用udev规则来确保接口配置在出现后才执行。IP地址管理在生产环境中如果有多台相同设备可能连接到同一台主机固定的IP如192.168.7.2会导致冲突。有两种思路使用链路本地地址Link-Local不配置静态IP让系统通过IPv6的FE80::/10网段或IPv4的169.254.0.0/16网段APIPA自动分配。这需要主机和设备都支持并启用相关协议。在设备端运行微型DHCP服务器在设备端安装dnsmasq或udhcpd让设备在usb0接口上为主机分配IP。这样主机可以设置为自动获取IPDHCP更加灵活。5.3 常见问题排查踩坑记录问题一主机无法识别设备或识别为“未知设备”。排查首先在设备端检查dmesg | tail和lsusb的输出。确保UDC驱动已正确绑定cat /sys/kernel/config/usb_gadget/g1/UDC应有输出。可能原因USB控制器名称不对。树莓派4B和Zero的控制器可能不同。尝试ls /sys/class/udc/查看可用的控制器名称。可能原因主机驱动问题。尝试在Windows设备管理器中手动更新驱动指定为“网络适配器” - “Microsoft” - “远程NDIS兼容设备”。问题二能ping通IP但无法解析域名或访问外网。排查这通常是路由和DNS问题。USB网络是一个独立的二层网络设备端的usb0接口通常不应该设置默认网关。如果你需要设备通过主机上网需要在主机上开启Internet连接共享ICS或配置IP转发和NAT。Windows在主机USB网络适配器的属性中开启“允许其他网络用户通过此计算机的Internet连接来连接”。Linux主机# 开启IP转发 sudo sysctl -w net.ipv4.ip_forward1 # 设置iptables NAT规则假设主机上网接口是eth0 sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE sudo iptables -A FORWARD -i eth0 -o usb0 -m state --state RELATED,ESTABLISHED -j ACCEPT sudo iptables -A FORWARD -i usb0 -o eth0 -j ACCEPT然后在设备端将默认网关设置为主机USB接口的IP如192.168.7.1并配置DNS如8.8.8.8或主机的IP。问题三传输大文件时速度慢或不稳定。排查使用dmesg查看是否有USB传输错误如“babble”、“stall”。可能是USB线质量差、接口接触不良或电源供电不足。尝试更换高质量的短线并确保板卡供电充足。调整可以尝试在configfs中禁用某些USB特性如echo 0 /sys/kernel/config/usb_gadget/g1/configs/c.1/MaxPower来降低功耗要求但这属于高级调试需谨慎。将Linux板卡虚拟为USB网卡是一个极具实用价值的技术它用最简单的硬件接口USB解决了嵌入式设备网络接入的难题。从内核驱动加载、configfs动态配置到网络IP设置、Systemd服务封装再到性能调优和问题排查整个流程涉及了嵌入式Linux开发的多个层面。我个人的体会是初期最大的障碍往往是对USB Gadget框架的理解和控制器UDC的正确指定一旦这个环节打通剩下的就是标准的网络配置问题了。把这个功能稳定地集成到产品中能极大提升设备的易用性和调试便利性。下次如果你的设备缺少网口别再想着飞线焊接了试试给它“注入”一个USB网卡的灵魂吧。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价