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

9.2 KiB
Raw Blame History

title date tags categories weight show_comments draft description
为树莓派添加 USB 网卡的记录 2023-09-11T12:35:21+08:00
linux
raspi
usb
udev
tech
50 true false 我给树莓派添加了一张 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)

但是在直接使用 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 为关键字找到了 这个这个,其中得到的下面的命令就能够切换工作模式。

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 负责,而 PID Product ID 就是厂商自己这一款产品的 ID其唯一性由厂商自己负责。

所以可以认为对于一个 USB 设备,其 VID:PID 的组合是唯一的,于是 Linux 通过既定格式组合这两个属性并追加其他属性,得到一个长长的字符串,随后到 /lib/modules/$(uname -r)/modules.alias 中寻找能够匹配这个字符串的模块,匹配的模块会被加载来操控硬件设备。

在我的网卡的例子中,把 /etc/udev/udev.confudev_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 以外的其他设备的描述可以见 这里

关于 udev

udev 能够在每次添加或者移除设备时收到信号,并可以进行如在 /dev 目录下创建额外的符号、更改设备名称等操作。

udev 有规则文件,可以在满足条件时修改设备的属性或运行特定的程序,规则文件细节可以看 udev(7)

在 raspbian 中,/usr/lib/udev/rules.d/40-usb_modeswitch.rules 中有大量的自动切换工作模式的规则,如果我所使用的 USB 网卡在这里也有写好的规则的话,这篇文章就不会出现了。

参考

  1. switch codes for Realtek USB ethernet adapter - USB_ModeSwitch
  2. [SOLVED] [RTL8151] Ethernet not working via multiport adapter - Support / Network - Manjaro Linux Forum
  3. ubuntu - How does Linux know how and what drivers to install in a new installation - Unix & Linux Stack Exchange
  4. usb_modeswitch(1) - Linux man page
  5. networking - How to automate usb_modeswitch? - Ask Ubuntu
  6. udev(7) - Linux manual page