If you’re running OpenWrt on a Raspberry Pi 5 with a USB-to-Ethernet adapter based on the Realtek RTL8153B chipset, you may have noticed that the adapter stops working after a power outage. It works fine after a manual unplug/replug, but never comes up on its own after a cold boot. Here’s why, and how to fix it permanently.

The Problem

Many RTL8153B-based USB Ethernet adapters (including models from TP-Link, Ugreen, and others) use a dual-mode design. When first plugged in, they present themselves as a USB CD-ROM drive (product ID 0bda:8151) containing a Windows driver installer. A special kernel driver called r8152-cfgselector is supposed to intercept this, switch the device to Ethernet mode (product ID 0bda:8153), and hand it off to the r8152 network driver.

The problem is a boot-time race condition. On the Raspberry Pi 5 running OpenWrt, the USB device enumerates at around t=4s during boot, but r8152-cfgselector doesn’t load until t=8-25s (it varies between boots). In that gap, the usb-storage driver claims the device as a CD-ROM, and the Ethernet driver never gets a chance.

You can confirm this by checking dmesg after a cold boot:

1
2
3
4
[    4.3s] usb 4-1: New USB device found, idVendor=0bda, idProduct=8151
[    4.3s] usb-storage 4-1:1.0: USB Mass Storage device detected
[    5.4s] scsi 0:0:0:0: CD-ROM            USB CD-ROM       2.00
[    8.2s] usbcore: registered new device driver r8152-cfgselector  <-- too late!

When you manually unplug and replug the adapter, r8152-cfgselector is already loaded and intercepts the device before usb-storage can grab it. That’s why a replug always fixes it.

What Doesn’t Work

Before finding the real fix, I tried several approaches that all failed:

1. OpenWrt Hotplug Script (/etc/hotplug.d/usb/)

I wrote a script to detect 0bda:8151 and trigger a USB reset. It never fired. OpenWrt’s hotplug.json (procd) short-circuits USB device events – when a USB device has MAJOR/MINOR set (which all USB devices do), it takes the makedev code path and returns before reaching the generic hotplug-call dispatcher. Scripts in /etc/hotplug.d/usb/ are simply never executed for USB device add events on OpenWrt.

2. Init.d Script with USB Deauthorize/Reauthorize

I created an init.d service that waited for the device, detected 8151 mode, unbound it from usb-storage, then deauthorized/reauthorized the USB port to trigger re-enumeration. The script ran correctly (confirmed by syslog), but the deauthorize/reauthorize cycle doesn’t actually power-cycle the USB hardware on the Raspberry Pi 5. The device kept its internal state and re-enumerated in CD-ROM mode again, with usb-storage immediately reclaiming it.

3. Kernel Quirk Alone (usb-storage.quirks=0bda:8151:i)

Adding usb-storage.quirks=0bda:8151:i to /boot/cmdline.txt successfully prevents usb-storage from claiming the device (dmesg shows “device ignored”). However, this alone is not enough. When r8152-cfgselector loads later, it does not probe already-present unclaimed devices – it only acts on new USB attach events. So the device just sits there unclaimed in CD-ROM mode, and nothing ever switches it to Ethernet.

The Fix (Two Parts)

The complete solution requires two things working together:

Part 1: Kernel Boot Parameter

Edit /boot/cmdline.txt and append:

1
usb-storage.quirks=0bda:8151:i

The i flag means ignore – the kernel will prevent usb-storage from ever binding to 0bda:8151. The full line should look something like:

1
console=tty1 console=serial0,115200 root=PARTUUID=xxxxx-02 rootfstype=ext4 rootwait usb-storage.quirks=0bda:8151:i

Important: Everything must stay on a single line.

This prevents usb-storage from grabbing the device, but we still need to force a re-enumeration after r8152-cfgselector loads.

Part 2: Init.d Script with USB Port Disable/Enable

The key insight: the RPi5’s xHCI controller exposes a disable sysfs entry for each USB hub port. Writing 1 to it physically disconnects the device from the bus. Writing 0 back re-enables the port and triggers a fresh enumeration – equivalent to a physical unplug/replug.

Create /etc/init.d/fix-rtl8153:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/bin/sh /etc/rc.common

START=19
STOP=99

start() {
    (
        USB3PORT="/sys/bus/usb/devices/usb3/3-0:1.0/usb3-port1/disable"
        USB4PORT="/sys/bus/usb/devices/usb4/4-0:1.0/usb4-port1/disable"

        # Wait for USB device to appear on either bus
        TRIES=0
        USBDEV=""
        while [ $TRIES -lt 30 ]; do
            if [ -f /sys/bus/usb/devices/4-1/idProduct ]; then
                USBDEV="/sys/bus/usb/devices/4-1"
                break
            elif [ -f /sys/bus/usb/devices/3-1/idProduct ]; then
                USBDEV="/sys/bus/usb/devices/3-1"
                break
            fi
            sleep 0.5
            TRIES=$((TRIES + 1))
        done

        [ -n "$USBDEV" ] || { logger -t rtl8153-fix "USB device never appeared"; exit 0; }

        PRODUCT=$(cat "$USBDEV/idProduct")
        if [ "$PRODUCT" = "8151" ]; then
            logger -t rtl8153-fix "Adapter stuck in CD-ROM mode (8151) at $USBDEV, fixing..."

            # Wait for r8152-cfgselector to load
            TRIES=0
            while ! lsmod | grep -q r8152 && [ $TRIES -lt 40 ]; do
                sleep 0.5
                TRIES=$((TRIES + 1))
            done
            logger -t rtl8153-fix "r8152 module loaded after $TRIES attempts"

            ATTEMPT=1
            while [ $ATTEMPT -le 2 ]; do
                logger -t rtl8153-fix "Port cycle attempt $ATTEMPT"

                # Disable both USB port sides (USB 2.0 + USB 3.0)
                echo 1 > "$USB4PORT" 2>/dev/null
                echo 1 > "$USB3PORT" 2>/dev/null
                logger -t rtl8153-fix "Disabled USB ports"

                sleep 2

                # Re-enable both USB port sides
                echo 0 > "$USB4PORT" 2>/dev/null
                echo 0 > "$USB3PORT" 2>/dev/null
                logger -t rtl8153-fix "Re-enabled USB ports"

                # Wait up to 15s for eth1 carrier
                TRIES=0
                while [ $TRIES -lt 30 ]; do
                    if cat /sys/class/net/eth1/carrier 2>/dev/null | grep -q 1; then
                        logger -t rtl8153-fix "eth1 is up with carrier (attempt $ATTEMPT)"
                        exit 0
                    fi
                    sleep 0.5
                    TRIES=$((TRIES + 1))
                done

                logger -t rtl8153-fix "eth1 did not come up after attempt $ATTEMPT"
                ATTEMPT=$((ATTEMPT + 1))
            done

            logger -t rtl8153-fix "FAILED: eth1 did not come up after 2 attempts"
        else
            logger -t rtl8153-fix "Adapter already in Ethernet mode ($PRODUCT) at $USBDEV, no fix needed"
        fi
    ) &
}

Then make it executable and enable it:

1
2
chmod +x /etc/init.d/fix-rtl8153
/etc/init.d/fix-rtl8153 enable

The script runs at START=19 (before network at 20), backgrounds itself so it doesn’t block boot, and:

  1. Waits for the USB device to appear on bus 3 or bus 4
  2. Checks if it’s stuck in CD-ROM mode (idProduct = 8151)
  3. Waits for r8152-cfgselector to load
  4. Disables both USB 2.0 and USB 3.0 sides of the port (the RPi5’s xHCI controller uses paired buses for the same physical port)
  5. Waits 2 seconds for the device firmware to reset
  6. Re-enables both port sides, triggering fresh enumeration
  7. Verifies eth1 comes up with carrier within 15 seconds
  8. If it doesn’t, retries the full cycle once more

Why This Works

The RTL8153B’s dual-mode behavior is identical to how old 3G USB modems worked – they’d present a virtual CD-ROM with Windows drivers, and usb_modeswitch would switch them to modem mode.

The kernel quirk prevents usb-storage from claiming the device, so it sits unclaimed. The init.d script then does the equivalent of a physical unplug/replug by disabling and re-enabling the USB port through sysfs. When the device re-enumerates, r8152-cfgselector is already loaded and intercepts it, switches the USB configuration, and the r8152 Ethernet driver takes over.

The reason we need both parts: the kernel quirk alone leaves the device unclaimed (cfgselector doesn’t probe existing devices), and the port cycle alone doesn’t help if usb-storage reclaims the device on re-enumeration.

Debugging

After a power cycle, check if the fix ran:

1
logread | grep rtl8153-fix

You should see something like:

1
2
3
4
5
6
rtl8153-fix: Adapter stuck in CD-ROM mode (8151) at /sys/bus/usb/devices/4-1, fixing...
rtl8153-fix: r8152 module loaded after 12 attempts
rtl8153-fix: Port cycle attempt 1
rtl8153-fix: Disabled USB ports
rtl8153-fix: Re-enabled USB ports
rtl8153-fix: eth1 is up with carrier (attempt 1)

Environment

  • Raspberry Pi 5
  • OpenWrt 24.10.4 (bcm27xx/bcm2712)
  • Kernel 6.6.110
  • USB Ethernet adapter: Realtek RTL8153B (0bda:8151 / 0bda:8153)
  • Connected via the RPi5’s USB 3.0 xHCI controller (xhci-hcd.1, bus 3/4)

TL;DR

  1. Add usb-storage.quirks=0bda:8151:i to /boot/cmdline.txt
  2. Create the init.d script above that does a USB port disable/enable cycle after r8152-cfgselector loads
  3. Reboot. Your RTL8153B USB Ethernet adapter will survive power outages without needing a manual replug.