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
4.8 KiB
The device model
How hardware gets found, claimed and used, and what the rest of the kernel sees of it.
Writing a driver is ../guides/adding-a-driver.md. Moving drivers out of the kernel entirely is ../plan/hybrid.md.
One list of everything attached
There is one list of everything attached, whatever bus found it:
include/device.h and kernel/dev/device.c, readable at /proc/devices.
name bus class driver device
kbd0 platform input ps2kbd PS/2 keyboard
mouse0 platform input ps2mouse PS/2 mouse
uart0 platform other uart8250 16550 serial port
ide0 platform storage ide ATA disk controller
vga0 pci display vga display controller
Before this there were two registries in kernel/drivers/registry.c — an array
of PCI drivers and an array of USB drivers — with nothing that knew about both.
That is sufficient for binding and useless for the three things wanted next.
Enumeration. "What is in this machine, and what claimed it" had no answer covering a USB disk and a PCI display at once. A device manager is exactly that question.
Classes. A compositor should ask for the display and not care whether that is the VBE framebuffer or virtio-gpu. Without a class every consumer hardcodes a driver, and adding the second driver means editing the consumers — which is how a framebuffer address ended up hardcoded in three places here once already.
A boundary for drivers that are not compiled in. They have to attach somewhere that is not an array fixed at link time.
Two vocabularies are kept deliberately separate: a device's bus is
provenance (platform, pci, usb), its class is capability
(display, input, storage, net, bus). A USB disk and a SATA disk are
both storage despite sharing no numbering at all. Where a driver claims a
device, the driver's opinion wins over the descriptor's: struct usb_driver has
a devclass field separate from the USB interface class, because the descriptor
says what the device claims to be and the driver knows what it made of it.
It is a flat list with a bus tag, not a device tree. Nothing yet needs to
walk a hierarchy, and a tree nobody traverses is just a harder list. Hot-unplug
would need more; the in_use flag is the hook.
Platform devices are registered in one place
(platform_register_devices() in registry.c) rather than inside each driver's
init, for the same reason the driver arrays are written out by hand: a list you
can read beats a list assembled by side effects.
Linux drivers
Decided 2026-08-08. Linux drivers are not shipped with SchOS and never will be. The user may install one on their own machine, through a device manager or equivalent, and SchOS will load it.
The licensing reasoning matters and is worth stating exactly, because it is easy to get wrong in both directions:
- Distribution is what triggers the GPL. Shipping a GPL-2.0-only driver in an Apache-2.0 image is what cannot be done — see ../licensing.md §2. A user obtaining a driver themselves and loading it on their own machine is their act, not ours, and is not a distribution by us.
- Implementing a loader is not copying. Writing support for a module format and a driver ABI from published documentation produces our own work. It is a clean-room implementation, and it stays one only if it is written from documentation rather than transcribed from Linux source. Reading Linux to understand hardware is fine; translating its code is not, whatever the identifiers say.
So the rule for this subsystem is the same as everywhere else in the tree: the
loader is ours and Apache-2.0, what it loads is the user's business, and nothing
GPL ever enters an image we build. RAMDISK_EXCLUDE and NOTPORTS are the
existing mechanisms for keeping that true mechanically rather than by intention.
What it does not do
- It is a flat list, not a device tree. There is no parent/child nesting, only a bus tag. Nothing yet needs to walk a hierarchy, and a tree nobody traverses is just a harder list.
- No hot-plug. Devices are registered at boot and never removed; the
in_useflag is the hook a hot-unplug path would need, and nothing sets it for that purpose. NDEVICEis 32, compile-time, like every other table in the kernel.- Drivers are fixed at link time. There is no module loader, and nothing can be installed after the build — the class vtable is the attachment point a loader would use, and it is the only part that exists.
- Only two class vtables are defined:
display_opsandnetdev_ops. Storage and input devices appear in the list withops = 0and are reached through their own subsystems instead.