leafee98-blog/content/posts/add-usb-ethernet-adaptor-for-raspberry.md

144 lines
9.2 KiB
Markdown
Raw Normal View History

---
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"