diff --git a/content/posts/add-usb-ethernet-adaptor-for-raspberry.md b/content/posts/add-usb-ethernet-adaptor-for-raspberry.md new file mode 100644 index 0000000..397136c --- /dev/null +++ b/content/posts/add-usb-ethernet-adaptor-for-raspberry.md @@ -0,0 +1,143 @@ +--- +title: "为树莓派添加 USB 网卡的记录" +date: 2023-09-11T12:35:21+08:00 +tags: [] +categories: [] +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"