# Adding a driver The device model itself is described in [../architecture/devices.md](../architecture/devices.md). This is the procedure. --- ## Where to get one from **The best source is the specification, not another kernel.** xHCI, AHCI, virtio, NVMe and the Intel datasheets are public documents with no licence on what you write from them. `kernel/drivers/bus/xhci/xhci.c` came from the spec, and so did `net/e1000/` and `gpu/virtio_gpu/`. **Three sources, in the order to try them:** 1. **A native driver, written from the specification.** The default, and what every driver in the kernel is. 2. **Linux's own driver, unmodified, through LDLS** ([ldls.md](../architecture/ldls.md)), when the device is one Linux supports and SchOS cannot afford to write a driver for — a NIC with a PHY behind it, a GPU, a Wi-Fi card. It runs in a process, so a crash costs the interface and not the machine. This is how the laptop's Realtek NIC is driven. 3. **BSD or Linux code taken into the tree.** Licensing no longer forbids either: SchOS is GPL-2.0-or-later, so GPL-2.0-only code combines with it, and BSD-2/3 code can be used directly with a `NOTICE` entry — see [../licensing.md](../licensing.md). The reason to prefer 1 and 2 is maintenance, not law: a driver copied in is a driver that has to be tracked. Reading another kernel to understand *hardware* is fine and normal. One caveat about "port it from BSD" as a plan, learned by looking: FreeBSD's `sys/dev/usb` is roughly 100k lines welded to newbus, devclass, `bus_dma`, callouts, taskqueues and FreeBSD's locking. Porting it means porting FreeBSD's device framework first and finding the USB stack somewhere underneath. > **BSD is far more valuable as the reference you check yourself against than as > the code you paste.** Read it when the spec is ambiguous — which it is, often — > and write your own. Where a *table* of constants is genuinely faster to transcribe than to retype from a PDF — device IDs, register offsets — taking it from a BSD tree is fine after the licence check, and goes in `NOTICE`. That is data; the logic stays ours. --- ## The procedure 1. Write `kernel/drivers///.c` exporting a `struct pci_driver` or `struct usb_driver`. One directory per driver, so a driver that grows a register header, a second source file or a firmware blob has somewhere to put it: kernel/drivers/ registry.c gpu/vga/ gpu/virtio_gpu/ net/e1000/ net/rtl8139/ net/r8169/ storage/ahci/ storage/nvme/ audio/ac97.c bus/xhci/ usb/core/ usb/hid/ usb/storage/ usb/hub/ 2. Declare it in `kernel/drivers/registry.c` and add it to the array. 3. Add the object to `KERN_DRIVERS` in the `Makefile`. 4. Fill in the **class vtable** for what it is, and point `.ops` at it. That is the whole of what the rest of the kernel sees: | class | vtable | consumer | |---|---|---| | `DEVCLASS_DISPLAY` | `struct display_ops` ([display.h](../../include/gfx/display.h)) | none today — Xorg draws through `/dev/fb0` | | `DEVCLASS_NET` | `struct netdev_ops` ([netdev.h](../../include/dev/netdev.h)) | the TCP/IP stack | | `DEVCLASS_AUDIO` | `struct audio_ops` | `/dev/dsp` | A display driver that owns a linear framebuffer also publishes it with `gfx_attach_scanout()` ([gfx.h](../../include/gfx/gfx.h)). That is what the text console and the crash screen draw on, and it is separate from the display class on purpose: the class is about *frames* — acquire, present, vblank — and the crash screen has to work when nothing else in the system does. A driver that fills these in never mentions its consumer, and its consumer never mentions it. Getting this wrong has a characteristic shape: the e1000 shipped with `.ops = 0` and the network stack called `e1000_transmit()` directly, so `kernel/net/` was full of one driver's name and the second NIC driver would have had to edit the protocol stack to add itself. Private headers go **next to the driver**, not in `include/`. The build adds the driver's own directory to the include path, so `e1000.h` — a register map that exactly one file has any use for — lives in `kernel/drivers/net/e1000/`. `include/` is for headers the rest of the kernel needs. Registration in the device model is automatic — `pci_bind()` and `usb_bind_all()` publish whatever they claim, so a driver cannot forget to. Set `devclass` on a USB driver, or the device lands in `other`. A driver that can drive **more than one card at once** keeps its instances in an array and implements `.attached`, which the bus calls once the `struct device` exists; hang the instance on `dev->priv` there and read it back in every op. It cannot be done inside `probe()`, because the device is published afterwards. Skipping this is how a driver ends up refusing the second card, as the e1000 did until it was asked to drive two. The best source for a new driver is the **specification**, not another kernel: xHCI, AHCI, virtio, NVMe and the Intel datasheets are public documents with no licence on what you write from them. `kernel/drivers/bus/xhci/xhci.c` came from the spec, and so did `net/e1000/` and `gpu/virtio_gpu/`. Where a *table* of constants is genuinely faster to transcribe than to retype from a PDF -- device IDs, register offsets -- taking it from a BSD tree is fine after the licence check, and goes in `NOTICE`. That is data; the logic stays ours. --- ## Checklist - [ ] One directory under `kernel/drivers///` - [ ] Exports a `struct pci_driver` or `struct usb_driver` - [ ] Declared in `kernel/drivers/registry.c` **and** added to its array - [ ] Object added to `KERN_DRIVERS` in the `Makefile` - [ ] Class vtable filled in and `.ops` pointed at it - [ ] `.attached` implemented if it can drive more than one card - [ ] `devclass` set, if it is a USB driver - [ ] Private headers next to the driver, not in `include/` - [ ] A `NOTICE` entry, if anything was copied — **before** the commit, not after - [ ] A selftest reachable from `/proc/sysrq`, if the class has one (`netdev_selftest` is the model)