--- title: "为树莓派添加 USB 网卡的记录" date: 2023-09-11T12:35:21+08:00 tags: [ linux, raspi, usb, udev ] categories: [ tech ] weight: 50 show_comments: true draft: false description: "我给树莓派添加了一张 USB 网卡,但是它只工作一半,在尝试修理的过程中了解到了 usb_modeswitch 的机制并对 Linux 选择驱动的方式和 udev 有了一定的了解。" --- 我一直在用树莓派连接有线网络,然后令树莓派放出 WiFi 供台式电脑连接,这个流程是完全可以工作的,但美中不足的是树莓派放出的 WiFi 只有大概 50 Mbit/s 的速度,而树莓派所连接的有线网络是 100 Mbits/s,相当于是白白给自己的网络体验打了个对折。 思虑良久(约一年),终于下定决心买下了一款 USB 转 RJ45 接口的 USB 网卡,我所购买的是裕合联的 USB3.0 有线网卡千兆铝合金,型号 YHL-U5503BK。 把网线、网卡、树莓派、台式电脑都接上,🤔,基本上是工作的……具体情况大概是: - 在树莓派开机时,把 USB 网卡插上,能正确识别到新的网卡,也可以进行 IP 等的配置。 - 在树莓派关机时,把 USB 网卡插上,再开机,网卡被识别为一个大容量存储设备,网络配置自然也不能进行。 在一系列的尝试过后,发现在网卡被识别为大容量存储设备时,使用 `usbreset 0bda:8151`,可以使其重新被识别为网卡,此时在 journalctl 的系统日志中也可以发现 USB 设备断开连接并且同一个接口中又新添加了一个设备,同时要注意到 Vendor ID 和 Product ID 也发生了改变。 ``` $ lsusb -tv /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M ID 1d6b:0003 Linux Foundation 3.0 root hub |__ Port 1: Dev 2, If 0, Class=Mass Storage, Driver=usb-storage, 5000M ID 152d:1561 JMicron Technology Corp. / JMicron USA Technology Corp. JMS561U two ports SATA 6Gb/s bridge |__ Port 2: Dev 3, If 0, Class=Mass Storage, Driver=usb-storage, 5000M ID 0bda:8151 Realtek Semiconductor Corp. RTL8151 Adapteon Business Mobile Networks BV /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M ID 1d6b:0002 Linux Foundation 2.0 root hub |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M ID 2109:3431 VIA Labs, Inc. Hub $ sudo usbreset 0bda:8151 Resetting USB 10/100/1000 LAN ... failed [No such device] $ lsusb -tv /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M ID 1d6b:0003 Linux Foundation 3.0 root hub |__ Port 1: Dev 2, If 0, Class=Mass Storage, Driver=usb-storage, 5000M ID 152d:1561 JMicron Technology Corp. / JMicron USA Technology Corp. JMS561U two ports SATA 6Gb/s bridge |__ Port 2: Dev 4, If 0, Class=Vendor Specific Class, Driver=r8152, 5000M ID 0bda:8153 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M ID 1d6b:0002 Linux Foundation 2.0 root hub |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M ID 2109:3431 VIA Labs, Inc. Hub ``` 在发现 usbreset 可以使其正常工作以后,便在 `/etc/udev/rules.d/90-my-usbrj45.rules` 中使用下面这一行配置,在 `0bda:8151` 添加时,直接对其 usbreset,然后就能够正常使用了, ``` ACTION=="add", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8151" RUN+="/usr/bin/usbreset 0bda:8151" ``` 不过虽然解决了,依然感觉不是很优雅,询问群友得知,在连接时先被识别为大容量存储设备、然后可以切换为其他工作模式而被识别为另一种设备的行为,是所谓的免驱硬件常见的工作方式,一般在 Windows 下,设备被识别为大容量存储设备后,系统可以从此设备中拿到驱动安装文件,安装以后再切换到实际工作的模式。在 Linux 下 usb_modeswitch 可以触发硬件工作模式的切换。 > Several new USB devices have their proprietary Windows drivers onboard, most of them WAN dongles. When plugged in for the first time, they act like a flash storage and start installing the Windows driver from there. If the driver is already installed, it makes the storage device disappear and a new device, mainly composite with modem ports, shows up. > > -- [usb_modeswitch(1)][3] 但是在直接使用 usb_modeswitch 时,一条“没有给定切换方法”的警告有点令人奇怪,并且重新 lsusb 查看发现并没有什么效果,大容量存储设备还是大容量存储设备。 ``` $ sudo usb_modeswitch -v 0bda -p 8151 Look for default devices ... Found devices in default mode (1) Access device 004 on bus 002 Get the current device configuration ... Current configuration number is 1 Use interface number 0 with class 255 Warning: no switching method given. See documentation -> Run lsusb to note any changes. Bye! ``` 不过既然裕合联是个小品牌,但是它可能用的东西是通用模块或者是其他大厂的下游也说不定,印象里面 VID:PID 是唯一的所以以之作为关键字应该可以找到有用的信息。 果不其然以 `usb_modeswitch 0bda 8151` 为关键字找到了 [这个][0] 和 [这个][1],其中得到的下面的命令就能够切换工作模式。 ``` sudo usb_modeswitch -v 0bda -p 8151 -V 0bda -P 8152 -M 555342430860d9a9c0000000800006e0000000000000000000000000000000 ``` 此外为了得到一个较有意义的网卡名称在 udev 中添加了一条配置将网卡名称改为 usbrj45,同时在符合期望 VID 和 PID 的情况下执行上面的切换运行状态的命令,于是最后的结果就是创建了一个 `/etc/udev/rules.d/90-my-usbrj45.rules`,文件内容如下,其中 MAC 地址被抹去: ``` # persistent link nane of usbrj45 SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="xx:xx:xx:xx:xx:xx", NAME="usbrj45" ACTION=="add", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8151" RUN+="/sbin/usb_modeswitch -v 0bda -p 8151 -M 555342430860d9a9c0000000800006e0000000000000000000000000000000" ``` ## 收获 ### Linux 选择驱动的方式 经过这一大圈的调查,了解到 VID (Vendor ID)是 USB 厂商识别码,每个厂商都会有一个 ID,其唯一性由 [USB-IF](https://www.usb.org) 负责,而 PID (Product ID) 就是厂商自己这一款产品的 ID,其唯一性由厂商自己负责。 所以可以认为对于一个 USB 设备,其 VID:PID 的组合是唯一的,于是 Linux 通过既定格式组合这两个属性并追加其他属性,得到一个长长的字符串,随后到 `/lib/modules/$(uname -r)/modules.alias` 中寻找能够匹配这个字符串的模块,匹配的模块会被加载来操控硬件设备。 在我的网卡的例子中,把 `/etc/udev/udev.conf` 中 `udev_log` 设置为 `debug` 后可以从 journalctl 中看到被组合得到的字符串,像这样: ``` 2-2:1.0: hwdb modalias key: "usb:v0BDAp8151d3100dc00dsc00dp00ic08isc06ip50in00" 2-2:2.0: hwdb modalias key: "usb:v0BDAp8153d3100dc00dsc00dp00ic02isc06ip00in00" 2-2:2.1: hwdb modalias key: "usb:v0BDAp8153d3100dc00dsc00dp00ic0Aisc00ip00in01" ``` 然后这个字符串能够匹配到 modules.alias 中的这两个模块,于是模块被加载并用来操控硬件: ``` $ grep -e v0BDAp8151 -e v0BDAp8153 /lib/modules/$(uname -r)/modules.alias alias usb:v0BDAp8153d*dc*dsc*dp*ic02isc06ip00in* cdc_ether alias usb:v0BDAp8153d*dc*dsc*dp*ic02isc06ip00in* r8153_ecm ``` 字符串拼接的细节和 USB 以外的其他设备的描述可以见 [这里][2] ### 关于 udev udev 能够在每次添加或者移除设备时收到信号,并可以进行如在 /dev 目录下创建额外的符号、更改设备名称等操作。 udev 有规则文件,可以在满足条件时修改设备的属性或运行特定的程序,规则文件细节可以看 [udev(7)][5] 。 在 raspbian 中,`/usr/lib/udev/rules.d/40-usb_modeswitch.rules` 中有大量的自动切换工作模式的规则,如果我所使用的 USB 网卡在这里也有写好的规则的话,这篇文章就不会出现了。 ## 参考 1. [switch codes for Realtek USB ethernet adapter - USB_ModeSwitch][0] 2. [[SOLVED] [RTL8151] Ethernet not working via multiport adapter - Support / Network - Manjaro Linux Forum][1] 3. [ubuntu - How does Linux know how and what drivers to install in a new installation - Unix & Linux Stack Exchange][2] 4. [usb_modeswitch(1) - Linux man page][3] 5. [networking - How to automate usb_modeswitch? - Ask Ubuntu][4] 6. [udev(7) - Linux manual page][5] [0]: https://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=2972 "switch codes for Realtek USB ethernet adapter - USB_ModeSwitch" [1]: https://forum.manjaro.org/t/solved-rtl8151-ethernet-not-working-via-multiport-adapter/41773/9 "[SOLVED] [RTL8151] Ethernet not working via multiport adapter - Support / Network - Manjaro Linux Forum" [2]: https://unix.stackexchange.com/questions/499716/how-does-linux-know-how-and-what-drivers-to-install-in-a-new-installation/499771#499771 "ubuntu - How does Linux know how and what drivers to install in a new installation - Unix & Linux Stack Exchange" [3]: https://linux.die.net/man/1/usb_modeswitch "usb_modeswitch(1) - Linux man page" [4]: https://askubuntu.com/questions/1247572/how-to-automate-usb-modeswitch "networking - How to automate usb_modeswitch? - Ask Ubuntu" [5]: https://man7.org/linux/man-pages/man7/udev.7.html "udev(7) - Linux manual page"