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
116 lines
4.7 KiB
Markdown
116 lines
4.7 KiB
Markdown
# USB
|
|
|
|
An xHCI host controller driver, a device-independent core, and three class
|
|
drivers. Written from the specification.
|
|
|
|
---
|
|
|
|
## Why not ported from BSD
|
|
|
|
"Take the USB stack from BSD" is the obvious plan and it does not survive contact
|
|
with the code.
|
|
|
|
FreeBSD's `sys/dev/usb` is roughly 100k lines welded to newbus, devclass,
|
|
`bus_dma`, callouts, taskqueues and FreeBSD's locking primitives, none of which
|
|
exist here. **Porting it means porting FreeBSD's device framework first and then
|
|
discovering the USB stack underneath.** NetBSD's is more portable and the same
|
|
argument applies with a smaller multiplier.
|
|
|
|
The specification is public and the controller is not complicated.
|
|
|
|
> BSD is much more valuable as the reference you check yourself against than as
|
|
> the code you paste.
|
|
|
|
Licensing points the same way: the BSD stacks are BSD-2/3-clause and compatible,
|
|
Linux's drivers are GPL-2.0-only and are not — see
|
|
[../licensing.md](../licensing.md).
|
|
|
|
## What the spec gives you for free
|
|
|
|
Everything in [`include/usb.h`](../../include/usb.h) comes from USB 2.0
|
|
chapter 9, which every device implements regardless of speed, class or vendor.
|
|
|
|
**That is what makes enumeration possible at all:** before you know anything
|
|
about a device you can still ask it what it is, because the question is
|
|
standardised.
|
|
|
|
## The controller
|
|
|
|
`kernel/drivers/bus/xhci/`. The full bring-up path:
|
|
|
|
```
|
|
reset → DCBAA → command ring → event ring → scratchpad
|
|
→ port enumeration → Enable Slot → Address Device → control transfers
|
|
→ configuration and interface descriptors → SET_CONFIGURATION
|
|
→ interrupt and bulk endpoints
|
|
```
|
|
|
|
Rings are producer rings: the driver writes TRBs and the controller reads them,
|
|
with a Link TRB as the last entry pointing back at the start — which is how a
|
|
fixed-size buffer becomes an endless queue. 64 TRBs per ring, 16 device slots.
|
|
|
|
### Enumeration polls; data uses interrupts
|
|
|
|
**And that is not laziness.** `pci_bind_all()` runs from `kmain()` with
|
|
interrupts disabled and no scheduler, so a driver that waited for an interrupt
|
|
during probe would wait forever.
|
|
|
|
Interrupts are registered afterwards, for when a device is actually delivering
|
|
data.
|
|
|
|
## Class drivers
|
|
|
|
| | |
|
|
|---|---|
|
|
| **HID** (`usb/hid/`) | Boot protocol keyboard and mouse, driven from the IRQ handler, feeding the same input paths as PS/2. |
|
|
| **Mass storage** (`usb/storage/`) | Bulk-only transport and SCSI, presenting a block device. A superblock reads back through `bread()`. |
|
|
| **Hub** (`usb/hub/`) | Route strings, hub slot contexts, per-port reset and power. |
|
|
|
|
Class drivers are offered a device by the core once its interface descriptor has
|
|
been read, from the list in `kernel/drivers/registry.c`. Whichever claims it
|
|
publishes a `struct device` in the bus-agnostic list, **with the class the driver
|
|
chose rather than the one the descriptor claimed** — the descriptor says what the
|
|
device says it is, and the driver knows what it made of it.
|
|
|
|
### What proves the hub works
|
|
|
|
A mouse behind a hub enumerating a tier deep. Hubs are the part of USB where
|
|
route strings and slot contexts stop being theoretical, and a hub driver that
|
|
handles only directly-attached devices looks identical to a working one until
|
|
something is plugged into it.
|
|
|
|
### The USB HID grab detail
|
|
|
|
The HID path updates its `prev[]` key state **before returning** even when the
|
|
device is grabbed — otherwise the next report looks like every held key had just
|
|
been pressed again. See [graphics.md](graphics.md#input) for the grab itself.
|
|
|
|
## Testing
|
|
|
|
`bootcheck -Usb` attaches an xHCI controller with a keyboard, a mouse and a
|
|
mass-storage device.
|
|
|
|
**Nothing on the default machine has a USB port**, so without that switch the
|
|
driver is never asked to do anything and a broken one looks exactly like a
|
|
working one.
|
|
|
|
`echo u > /proc/sysrq` and `echo s > /proc/sysrq` drive the storage path.
|
|
|
|
---
|
|
|
|
## What it does not do
|
|
|
|
- **A USB disk cannot be mounted or booted from.** The block device works and
|
|
`bread()` reads a superblock off it; the root device is compiled in rather than
|
|
selectable, so nothing can be told to use it. This is the largest single gap
|
|
and it is item 1 of [../plan/roadmap.md](../plan/roadmap.md).
|
|
- **Boot protocol only** for HID. No report descriptor parsing, so anything that
|
|
is not a plain keyboard or mouse is not understood.
|
|
- **No isochronous transfers**, so no audio and no cameras.
|
|
- **No USB 3 SuperSpeed-specific features** beyond what enumeration needs — the
|
|
USB2 and USB3 descriptor paths both work, the rest of the difference is unused.
|
|
- **16 device slots**, compile-time.
|
|
- **No hot-unplug.** A device removed is not noticed; the device model has no
|
|
removal path either.
|
|
- **One controller.** A second xHCI card would bind and then contend for state
|
|
the driver keeps per-driver rather than per-instance.
|