docs: four directories, and a rule for when a thing leaves the roadmap
docs/ was ten files where a plan, a description, a guide and a bug report all
sat next to each other. It is now architecture/ (what SchOS is), plan/ (what it
will be), guides/ (how to work on it) and history/ (why it is like this), with
an index that states the one rule holding it together: when something is
finished it moves from plan/ to architecture/ and is rewritten as a description
of the thing rather than a record of building it.
A ticked checkbox says the work happened. It does not say what the work
*produced*, and six months later the checkbox is all that is left. The network
stack proved the point: finished, verified end to end, and for a month the only
description of it anywhere was a checkbox four levels of indentation deep.
So roadmap.md went from 534 lines to 181 -- nothing deleted, the done work
graduated. Seven subsystems that were finished and undescribed now have
descriptions: boot, kernel, syscalls, filesystem, networking, graphics, usb,
init. Every one ends with "What it does not do", which is the section that pays;
a document listing only strengths is marketing, and the reader who needs it most
is the one about to trip over a limitation nobody wrote down.
schfs.md still opened with "phases 1 through 3 1/2 are done, phase 4 is next"
long after all five had landed. It is a description now, with the phase record
and the hard-won bugs moved to history/.
Three corrections found on the way, all load-bearing:
* projects/schcom/README.md claimed SCM_RIGHTS was done. There is no
sendmsg/recvmsg in the kernel at all, and an entire GUI plan rested on it.
* The root README said "No TCP/IP" months after the stack landed.
* history/kernel.md sec.7 was marked "in progress" on a problem that process
groups solved.
Also adds plan/1.0.md, which draws a finish line so the open list stops being
infinite, and marks schwm/ and schde/ superseded: in Wayland the compositor is
the window manager, so that split dissolves rather than being abandoned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
parent
817d666068
commit
57aff40228
59 changed files with 3981 additions and 1124 deletions
112
docs/guides/adding-a-driver.md
Normal file
112
docs/guides/adding-a-driver.md
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# 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/`.
|
||||
|
||||
**BSD: yes. Linux: no.** This is a licence incompatibility rather than a
|
||||
preference, and it decides the whole strategy — see
|
||||
[../licensing.md](../licensing.md). BSD-2 and BSD-3 code can be used directly
|
||||
with a `NOTICE` entry; GPL-2.0-only code can never be, at any distance. Reading
|
||||
Linux to understand *hardware* is fine and normal; translating a Linux driver
|
||||
function by function produces a derivative work whatever the identifiers say.
|
||||
|
||||
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/
|
||||
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/display.h)) | the compositor |
|
||||
| `DEVCLASS_NET` | `struct netdev_ops` ([netdev.h](../../include/netdev.h)) | the TCP/IP stack |
|
||||
|
||||
A display driver that owns a linear framebuffer also publishes it with
|
||||
`gfx_attach_scanout()` ([gfx.h](../../include/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)
|
||||
Loading…
Reference in a new issue