leafee98-blog/content/posts/add-usb-ethernet-adaptor-for-raspberry.md
leafee98 16979b186c
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
edit: add tag and category to add-usb-ethernet-adaptor-for-raspberry
2024-01-19 16:25:58 +08:00

144 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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