Prompted by a user report of a 0bda:c811 (RTL8811CU) dongle not working. That
particular case turned out to be fine on current stock — driver, firmware and USB ID all
check out — but chasing it surfaced a whole class of dongle this image cannot bring up at
all, and the reason is not a missing driver.
The gap
Many cheap WiFi dongles ship in ZeroCD mode: on first plug-in they enumerate as a USB
mass-storage device presenting a virtual CD-ROM full of Windows drivers, and only
re-enumerate as a WiFi NIC once the host sends a specific command. Until that happens they
never present the USB ID their driver is waiting for, so no amount of driver coverage helps.
For the Realtek case the switch is trivial — from usb_modeswitch.d/0bda:1a2b:
# D-Link DWA-171 Wifi Dongle
TargetVendor=0x2001
TargetProduct=0x331d
StandardEject=1
StandardEject=1 is a SCSI START STOP UNIT over the bulk endpoint. The device detaches
and returns as 2001:331d, which is in rtw8821cu.c's device table, so
rtw88_8821cu binds and the dongle works. Eject the fake CD; the device reboots into its
real identity.
Why the kernel will not do this for us
It half does, and the half it does makes userspace mandatory —
drivers/usb/storage/unusual_devs.h:1516:
/*
* Reported by Zenm Chen <zenmchen@gmail.com>
* Ignore driver CD mode, otherwise usb_modeswitch may fail to switch
* the device into Wi-Fi mode.
*/
UNUSUAL_DEV( 0x0bda, 0x1a2b, 0x0000, 0xffff,
"Realtek", "DISK",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_IGNORE_DEVICE ),
Mainline deliberately refuses to claim the device so that usb_modeswitch can reach
it, and says so in the comment. Two things follow:
- There is no kernel-only fix coming. Upstream has explicitly delegated this to userspace.
eject cannot substitute. US_FL_IGNORE_DEVICE means no /dev/sr0 is created, so
the eject applet we already ship (busybox, symlinked at /bin/eject and
/usr/bin/eject) has nothing to act on. On this image such a dongle enumerates and then
nothing at all happens.
Neither we nor stock ship usb_modeswitch — confirmed against stock's addon.tar at
9d9ff03a (Release 20260912), which has no modeswitch member.
This is a blind spot in docs/wifi-parity.md §12, structurally. That audit's method
was "enumerate every chip family under drivers/net/wireless/ with a USB bus file" — it
asked which chips have drivers, never whether the dongle ever reaches its driver's USB
ID. A ZeroCD device fails the second test while passing the first, so the audit could not
have seen it.
Proposal: ship usb_modeswitch, not usb_modeswitch_data
Buildroot has both, and the split matters:
| Package |
Contents |
Cost |
BR2_PACKAGE_USB_MODESWITCH (2.6.2) |
the binary + dispatcher |
small; deps are libusb and jimtcl, both already in the image (libusb-1.0.so.0, libjim.so.0.83) |
BR2_PACKAGE_USB_MODESWITCH_DATA (20251207) |
513 device configs + a 44 KB udev rules file |
2.1 MB, of which ~509 configs are 3G/4G modems, printers and WAN sticks |
Take the first, skip the second, and vendor the handful of configs that matter into the
board overlay — the same pattern already used for 60-jms583-phantom.rules and
70-persistent-net.rules.
Skipping the data package is not only about the 2.1 MB. Its rules file opens with:
KERNEL=="ttyUSB*", ATTRS{bNumConfigurations}=="*", PROGRAM="usb_modeswitch --symlink-name %p ...
which forks usb_modeswitch on every ttyUSB* add event. This is a board where USB
serial is a first-class feature (uartmode, MIDI), so that is a real per-hotplug cost paid
for modems nobody will ever plug into a MiSTer. (The same file also carries a malformed
token: SUBSYSTEM!="usb", ACTION!="add",, GOTO=..., double comma included.)
What this actually buys
Every ZeroCD entry in the data package under a WiFi vendor ID, cross-checked against
whether we build a driver for the post-switch ID:
| ZeroCD ID |
switches to |
driver in our image |
0bda:1a2b |
2001:331d (RTL8811CU/8821CU) |
rtw88_8821cu ✔ CONFIG_RTW88_8821CU=m |
0bda:a192 |
0bda:f192 (RTL8192FU) |
rtl8xxxu ✔ CONFIG_RTL8XXXU=m |
0e8d:2870 |
0e8d:7612 (MT7612U) |
mt76x2u ✔ CONFIG_MT76x2U=m |
148f:2878 |
148f:7601 (MT7601U) |
mt7601u ✔ CONFIG_MT7601U=m |
Four configs, four drivers already built and installed. Everything else in the 513 is out
of scope. The first row is the common AC600/AC1300 dongle sold under a dozen brand names,
which is why this is worth doing at all.
Work items
Scope note
configs/mister_de25nano_defconfig has no BR2_ROOTFS_OVERLAY, so an overlay-delivered
rule reaches the DE10 only. Given the DE25's current scope is a bare developer OS that is
probably fine, but it is a reason to consider whether these files belong in a shared
location rather than the DE10 overlay.
Caveats
- Divergence from stock, which ships nothing here. Same class as ADR 0016: strictly
more hardware works, nothing regresses.
- Risk is low — our rules would be exact VID:PID matches, so there is no path to
mis-firing an eject at a real USB drive.
Prompted by a user report of a
0bda:c811(RTL8811CU) dongle not working. Thatparticular case turned out to be fine on current stock — driver, firmware and USB ID all
check out — but chasing it surfaced a whole class of dongle this image cannot bring up at
all, and the reason is not a missing driver.
The gap
Many cheap WiFi dongles ship in ZeroCD mode: on first plug-in they enumerate as a USB
mass-storage device presenting a virtual CD-ROM full of Windows drivers, and only
re-enumerate as a WiFi NIC once the host sends a specific command. Until that happens they
never present the USB ID their driver is waiting for, so no amount of driver coverage helps.
For the Realtek case the switch is trivial — from
usb_modeswitch.d/0bda:1a2b:StandardEject=1is a SCSISTART STOP UNITover the bulk endpoint. The device detachesand returns as
2001:331d, which is inrtw8821cu.c's device table, sortw88_8821cubinds and the dongle works. Eject the fake CD; the device reboots into itsreal identity.
Why the kernel will not do this for us
It half does, and the half it does makes userspace mandatory —
drivers/usb/storage/unusual_devs.h:1516:Mainline deliberately refuses to claim the device so that
usb_modeswitchcan reachit, and says so in the comment. Two things follow:
ejectcannot substitute.US_FL_IGNORE_DEVICEmeans no/dev/sr0is created, sothe
ejectapplet we already ship (busybox, symlinked at/bin/ejectand/usr/bin/eject) has nothing to act on. On this image such a dongle enumerates and thennothing at all happens.
Neither we nor stock ship
usb_modeswitch— confirmed against stock'saddon.tarat9d9ff03a(Release 20260912), which has nomodeswitchmember.This is a blind spot in
docs/wifi-parity.md§12, structurally. That audit's methodwas "enumerate every chip family under
drivers/net/wireless/with a USB bus file" — itasked which chips have drivers, never whether the dongle ever reaches its driver's USB
ID. A ZeroCD device fails the second test while passing the first, so the audit could not
have seen it.
Proposal: ship
usb_modeswitch, notusb_modeswitch_dataBuildroot has both, and the split matters:
BR2_PACKAGE_USB_MODESWITCH(2.6.2)libusbandjimtcl, both already in the image (libusb-1.0.so.0,libjim.so.0.83)BR2_PACKAGE_USB_MODESWITCH_DATA(20251207)Take the first, skip the second, and vendor the handful of configs that matter into the
board overlay — the same pattern already used for
60-jms583-phantom.rulesand70-persistent-net.rules.Skipping the data package is not only about the 2.1 MB. Its rules file opens with:
which forks
usb_modeswitchon everyttyUSB*add event. This is a board where USBserial is a first-class feature (
uartmode, MIDI), so that is a real per-hotplug cost paidfor modems nobody will ever plug into a MiSTer. (The same file also carries a malformed
token:
SUBSYSTEM!="usb", ACTION!="add",, GOTO=..., double comma included.)What this actually buys
Every ZeroCD entry in the data package under a WiFi vendor ID, cross-checked against
whether we build a driver for the post-switch ID:
0bda:1a2b2001:331d(RTL8811CU/8821CU)rtw88_8821cu✔CONFIG_RTW88_8821CU=m0bda:a1920bda:f192(RTL8192FU)rtl8xxxu✔CONFIG_RTL8XXXU=m0e8d:28700e8d:7612(MT7612U)mt76x2u✔CONFIG_MT76x2U=m148f:2878148f:7601(MT7601U)mt7601u✔CONFIG_MT7601U=mFour configs, four drivers already built and installed. Everything else in the 513 is out
of scope. The first row is the common AC600/AC1300 dongle sold under a dozen brand names,
which is why this is worth doing at all.
Work items
select BR2_PACKAGE_USB_MODESWITCHinpackage/mister-userspace/Config.in(§5.12USB/input), deliberately without
_DATA; docs/buildroot-config.md anddocs/package-manifest.md entries.
board/mister/de10nano/rootfs-overlay/etc/usb_modeswitch.d/and a narrow exact-VID:PID rules file into
.../etc/udev/rules.d/.and the packed DB at
/usr/share/usb_modeswitch/, with/etc/usb_modeswitch.d/asthe override directory; verify the dispatcher's actual lookup order and the exact
RUN+=invocation rather than assuming it from the Makefile(
RULESDIR=/lib/udev/rules.d,PREFIX=/usr).none was available. This should not land on source inspection alone, which is the
same caveat
wifi-parity.md§8 records against8852cu.ko.docs/wifi-parity.mdas a new section, and note in §12 that its methoddoes not cover enumeration state.
Scope note
configs/mister_de25nano_defconfighas noBR2_ROOTFS_OVERLAY, so an overlay-deliveredrule reaches the DE10 only. Given the DE25's current scope is a bare developer OS that is
probably fine, but it is a reason to consider whether these files belong in a shared
location rather than the DE10 overlay.
Caveats
more hardware works, nothing regresses.
mis-firing an eject at a real USB drive.