The Setup
I had a D-Link DWA-131 rev H1 USB WiFi adapter and a Linux laptop. The adapter didn't work. Not "doesn't work great" — completely unrecognized. lsusb showed the device, dmesg showed nothing useful.
A quick search confirmed what I suspected: no one had written a Linux driver for this specific revision of the chip. The H1 uses a different chipset from the earlier revisions that do have support.
So I wrote one.
Understanding USB Device Drivers
I started by reading the Linux kernel driver documentation and a lot of the rtl8188eu driver source (a similar Realtek chip). The basics of a USB network driver are:
- Match your device — register a USB device ID table with your vendor ID and product ID
- Bind on plug — implement
probe()which the kernel calls when a matching device appears - Set up the network interface — create a
net_device, implementndo_open,ndo_stop,ndo_start_xmit - Handle USB transfers — use URBs (USB Request Blocks) for async TX/RX
- Clean up — implement
disconnect()for when the device is unplugged
The hardest part wasn't the USB plumbing. It was understanding the chip's firmware initialization sequence.
Reverse Engineering the Initialization Sequence
The DWA-131 H1 uses a Realtek RTL8192EU chipset. Realtek provides out-of-tree drivers for Windows, and there's a semi-functional Android driver floating around. Neither was designed for clean upstreaming.
I spent a few evenings with Wireshark's USBPCap on a Windows VM, capturing the exact sequence of control transfers that the official Windows driver sends on device connect. This gave me the initialization sequence: which registers to write, in what order, with what values.
From there I could implement rtl8192eu_init_device() in C.
The actual driver
The driver registers a usb_driver struct:
static struct usb_driver rtl8192eu_driver = {
.name = "rtl8192eu",
.id_table = rtl8192eu_id_table,
.probe = rtl8192eu_probe,
.disconnect = rtl8192eu_disconnect,
.suspend = rtl8192eu_suspend,
.resume = rtl8192eu_resume,
};
On probe, it allocates a net_device, sets up the private data structure, initializes the chipset firmware, and registers the net device with the kernel. TX/RX use async URBs submitted to the USB core.
The trickiest part was power management. The H1 chip has a hardware power save mode that requires a specific heartbeat sequence to keep the radio awake. Getting that wrong means the device silently stops responding after a few minutes.
Submitting Upstream
After the driver worked reliably for a week on my laptop, I cleaned up the code, added proper kernel-style comments, and submitted it to the linux-wireless mailing list.
The review process involved a few rounds of feedback — mostly style issues and one real bug I'd missed in the error path of probe(). After two revisions, it was accepted.
Seeing git log --oneline drivers/net/wireless/realtek/ and finding my commit in there is a feeling I'll remember for a while.
What It Took
About three weeks of evenings. I didn't know C well at the start of this — I knew Python and JavaScript. The kernel's coding style, the subsystem APIs, the build system (Kconfig/Kbuild) — all new.
The single most useful thing I did was read existing drivers in the same family and understand why they were written the way they were, rather than just copying patterns blindly.
If you have a device that doesn't work on Linux, it might just need someone willing to dig in.