# 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](../guides/adding-a-driver.md). Moving drivers out of the kernel entirely is [../plan/hybrid.md](../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](../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_use` flag is the hook a hot-unplug path would need, and nothing sets it for that purpose. - **`NDEVICE` is 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_ops` and `netdev_ops`. Storage and input devices appear in the list with `ops = 0` and are reached through their own subsystems instead.