Source: https://mayphus.org/nanopi-r2s-freebsd-usb-ethernet/ Title: An experimental FreeBSD USB Ethernet workaround for the NanoPi R2S Metadata: {"date":"2026-10-01T00:00:00Z","kind":"article","language":"en","locale":"en","route":"/nanopi-r2s-freebsd-usb-ethernet/","slug":"nanopi-r2s-freebsd-usb-ethernet","tags":["freebsd","nanopi r2s","usb","ethernet","embedded"],"type":"article"} # An experimental FreeBSD USB Ethernet workaround for the NanoPi R2S The USB Ethernet port on my NanoPi R2S failed during its first FreeBSD initialization. An experimental patch changed when USB3 link power management was configured, and the recorded tests then passed attachment, DHCP, traffic, interface cycling, and a cold power-on. That is a useful workaround record for one board. It is not an accepted FreeBSD fix or proof that USB low-power transitions work correctly. The [public experiment](https://github.com/mayphus/freebsd-src/pull/1) remains a draft in my fork. Codex generated and revised the patch and ran the software tests; independent human USB-maintainer review has not been completed. ## The failure and the tested system The recorded system was a NanoPi R2S with an RK3328 DWC3/xHCI controller and its integrated RTL8153B USB Ethernet chip, identified as version `6010`. On FreeBSD `15.1-RELEASE-p3`, the first register control read failed with `USB_ERR_IOERROR` and xHCI completion code 4. The failed read left a zeroed buffer, leading to the misleading message `unknown version 0x0000`. A later software USB reset recovered the device. The release-based test used source revision [`88e7371d9dc26f85dfc1b008cbe59ebc7e4a33da`](https://github.com/freebsd/freebsd-src/commit/88e7371d9dc26f85dfc1b008cbe59ebc7e4a33da), an arm64 `GENERIC` kernel, and a cross-build using LLVM 19 in a Debian 13 container. These details describe the reported experiment, not a promise that another image or board will behave the same way. A same-kernel comparison, with the startup reset script skipped, produced success with U1/U2 disabled, failure with them enabled, then success with them disabled again. Disabling U1 alone allowed attachment but left interface initialization blocked while U2 remained enabled. These comparisons motivated investigating startup power-management ordering. They did not identify the complete controller or device defect. ## What the patch changes This article refers to the exact six-file candidate at [`80848864583942aa14aaecab74206e736628fcef`](https://github.com/mayphus/freebsd-src/commit/80848864583942aa14aaecab74206e736628fcef). Pin that revision when comparing results; the draft branch may change later. The patch keeps the parent port's U1/U2 timeouts disabled during enumeration. After driver probing and attachment, the hub chooses the default policy unless a driver has selected one. A new `usbd_set_usb3_lpm()` helper lets a driver select both timeout settings and attempts to disable both again if enabling them partially fails. The `ure(4)` changes disable the parent-port timeouts before asynchronous post-attach setup. `ure_stop()` also attempts to disable them before stopping transfers; detach does not use that path. A later tick attempts to restore the timed policy when the interface is running, carrier is up, and the receive-start flag is set. There is no added startup reset script, reset-retry loop, or fixed startup delay. The candidate also caps the timed U1 value at 127. The old root-hub calculation produced 128; U1 timeout values 128 through 254 are reserved. That correction alone did **not** fix this R2S. It should be assessed separately from the broader lifecycle changes. ## What passed The public record reports these results for the release-based patched kernel: - First attachment identified the chip, obtained DHCP, and negotiated a `1000baseT` full-duplex link. - Three interface down/up cycles passed register reads and 20/20 ping packets with 1400-byte payloads per cycle, with zero interface errors. - A one-time test boot and an ordinary persistent boot each attached once and passed 20/20 traffic packets without reset recovery. - On September 24, 2026, unplugging and reconnecting board power produced another successful attach, DHCP, link, register read, and 20/20 traffic result. A 1 Gbps negotiated link is not a measured 1 Gbps throughput result. No throughput benchmark is reported here. Physical USB unplug/replug, suspend/resume, and module unload/reload were not validated. The patch was also reported to apply to FreeBSD main at [`10a3562a019b4eaaa0f5c50a15d739c43e41e8e7`](https://github.com/freebsd/freebsd-src/commit/10a3562a019b4eaaa0f5c50a15d739c43e41e8e7). That tree was not built or boot-tested in this record. ## Working traffic does not establish working low-power states The follow-up deliberately looked for actual U1/U2 residence. A separate diagnostic kernel sampled xHCI port registers directly, without downstream Ethernet control transfers during each sampling burst. It is not part of the six-file candidate. | Condition | Samples | U0 | U1 | U2 | | --- | ---: | ---: | ---: | ---: | | Normal interface operation | 500,000 | 500,000 | 0 | 0 | | Device U1/U2 permissions enabled | 500,000 | 500,000 | 0 | 0 | | Receive transfers stopped; short idle timers | 500,000 | 500,000 | 0 | 0 | Timer readback confirmed U1=127/U2=128 in the normal configuration and U1=1/U2=1 in the short-timer trial. Device status also reflected enabled U1/U2 permissions. Every sampled link state nevertheless remained U0. Each condition used 50 short bursts, so these are correlated samples, not continuous monitoring or a residence-time measurement. Brief transitions outside those bursts and measurement effects remain possible. There was no known non-U0 positive control. Stopping receive transfers also failed to produce observed low-power residence, so the idea that receive activity alone explains U0 remains a hypothesis. The evidence supports successful timer programming and the reported traffic results. It does not demonstrate U1/U2 entry, exit, power savings, or recovery from those states. Stronger evidence needs controller/device investigation, transition tracing, or a USB3 protocol analyzer. ## Remaining engineering risks Source inspection adds reasons to keep this experimental. In `ure_start()`, `sc_rxstarted` is set before the calls that request receive-transfer startup. The later flag check therefore does not establish that receive transfers have actually been submitted or become ready. The successful board tests do not prove this ordering safe on other timings or controllers. The new attach path also aborts `ure` attachment when disabling LPM fails, affecting SuperSpeed devices beyond this board. Managed-policy ownership is not fully cleaned up across detach or reset. These are source-level risks, not reproduced crashes in the reported tests, and they need maintainer review before general use. Linux's [`r8152` driver](https://github.com/torvalds/linux/blob/d24e8ac715de2e16a53c144005b1863660a5fbea/drivers/net/usb/r8152.c) explicitly disables hub-initiated LPM while also enabling USB LPM in device setup paths. That distinction is useful context, not validation of this FreeBSD policy. A fuller separation of hub- and device-initiated behavior may be needed. See the pinned [receive-start path](https://github.com/mayphus/freebsd-src/blob/80848864583942aa14aaecab74206e736628fcef/sys/dev/usb/net/if_ure.c#L1333) and [deferred USB transfer start](https://github.com/mayphus/freebsd-src/blob/80848864583942aa14aaecab74206e736628fcef/sys/dev/usb/usb_transfer.c#L1949) for the ordering concern. The same candidate appeared in [upstream PR 2444](https://github.com/freebsd/freebsd-src/pull/2444), which I withdrew. It was not merged upstream. This article does not present that withdrawal as a technical rejection by a maintainer. ## Trying the candidate safely Changing a kernel can remove network access or prevent a normal boot. Arrange a working serial/local console and known-good spare boot media first. Keep the original kernel, modules, boot configuration, and source changes available for recovery. Do not make a remotely managed router depend on this experiment as its only recovery path. Start with a separate source checkout and review the [pinned commit and diff](https://github.com/mayphus/freebsd-src/commit/80848864583942aa14aaecab74206e736628fcef). Compare its six files with the release-based source revision above. Check patch applicability on your exact source tree before building; a clean application is only a source compatibility check. Do not assume the fork's current main is the tested release tree. Use the [FreeBSD Handbook's kernel build procedure](https://docs.freebsd.org/en/books/handbook/kernelconfig/) appropriate to your source and target, together with the tree's `UPDATING` instructions. This record identifies the cross-build toolchain and target, but does not publish a complete reproducible build command sequence or container recipe. I am not filling that gap with commands labeled as tested. Keep matching kernel/modules together and try the result through your established one-time boot/recovery procedure before making it persistent. For comparison, record chip identification, first attachment without automatic reset recovery, DHCP, negotiated carrier, register access, packet delivery, and interface errors. Wait for carrier before evaluating traffic: the public follow-up includes an early trial that lost five packets before carrier returned, which was not evidence of a USB power-transition failure. Keep any reset workaround documented so it cannot silently mask the original failure. If the candidate fails, use the console or spare media to boot the known-good kernel and matching modules, restore the previous boot configuration, and remove the candidate only from the experimental source checkout. Preserve unrelated source edits. If network access is already lost, recovery must not depend on fetching another image over that interface. ## References - [Fork PR 1: experiment, hardware results, and diagnostic limits](https://github.com/mayphus/freebsd-src/pull/1) - [Exact six-file candidate, 80848864583942aa14aaecab74206e736628fcef](https://github.com/mayphus/freebsd-src/commit/80848864583942aa14aaecab74206e736628fcef) - [Withdrawn upstream PR 2444](https://github.com/freebsd/freebsd-src/pull/2444) - [Linux r8152 source](https://github.com/torvalds/linux/blob/d24e8ac715de2e16a53c144005b1863660a5fbea/drivers/net/usb/r8152.c) - [FreeBSD Handbook: configuring and building a kernel](https://docs.freebsd.org/en/books/handbook/kernelconfig/)