README: rewritten around what runs (Linux software, Xorg, apk, Linux's drivers), the honest gaps, measured numbers. plan/: 1.0.md is the finish line, where each clause of the acceptance test stands, and the register (one failing clause: a root that survives a reboot); built plans (foundations, hybrid, alpine, xorg) and the decided-against one (wayland) are gone, their content lives in architecture/; roadmap.md is only what is still open. guides: building/layout/testing/adding-a-driver (Linux code may be used). history: four entries (ls -lR, TLB shootdowns, MDIO 0x02460246, netlink MSG_TRUNC). Website: cards, stack, download page, docs index. Code comments that pointed at deleted plan docs point at the architecture docs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
123 lines
6.1 KiB
Markdown
123 lines
6.1 KiB
Markdown
# 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/<class>/<name>/<name>.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/<class>/<name>/`
|
|
- [ ] 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)
|