kernel (threads, demand paging, TLB shootdowns, dynamic linking, RAM direct map), syscalls, filesystem (/proc, /sys, /dev), terminals (12, VT handshake, ptys), networking (r8169 through LDLS), LDLS (module sets; Linux code may ship), devices (what stays in the kernel; hybrid.md's lasting content), boot (the loader's placement of the kernel), graphics (fbdev, GMMOS, QR 1-40), audio (ALSA over OSS), debugging, usb. New: packages.md (the apk root) and desktop.md (Xorg on the framebuffer). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
141 lines
6.5 KiB
Markdown
141 lines
6.5 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 does not decide it: the BSD stacks are BSD-2/3-clause and compatible, and
|
|
since SchOS became GPL-2.0-or-later Linux's GPL-2.0-only drivers are compatible too
|
|
— see [../licensing.md](../licensing.md). The argument against porting is the
|
|
framework underneath, not the licence.
|
|
|
|
## What the spec gives you for free
|
|
|
|
Everything in [`include/dev/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. From then on the interrupt handler is the event ring's only reader:
|
|
command completions and endpoint-0 completions are filed per slot, and whoever
|
|
issued them is woken to collect them. That is what lets enumeration happen
|
|
again later, from a thread, while keyboards are interrupting.
|
|
|
|
Before reset the driver takes the controller from the firmware (USB Legacy
|
|
Support: OS Owned, wait for BIOS Owned to clear, SMIs off), and on Intel 7/8/9
|
|
series chipsets switches the ports over from the EHCI controllers, which is
|
|
where the firmware leaves them.
|
|
|
|
### Hot-plug
|
|
|
|
The `usbhotplug` kernel thread owns every enumeration after boot. It looks at
|
|
the root ports whenever the controller reports a port change and every quarter
|
|
second regardless, and at hubs whenever their status-change endpoint reports
|
|
one. A device that arrives is debounced for 100 ms and enumerated exactly as at
|
|
boot; one that leaves has its endpoints closed (failing any transfer in flight),
|
|
its drivers' `disconnect` called, its slot disabled and its memory freed, and
|
|
the same for everything behind it if it was a hub. A device that will not
|
|
enumerate is left alone until it is unplugged, not retried for ever.
|
|
|
|
The thread also drives key repeat for USB keyboards, which do not repeat by
|
|
themselves: 500 ms, then about 33 a second, as the PS/2 keyboard's firmware
|
|
does.
|
|
|
|
## Class drivers
|
|
|
|
| | |
|
|
|---|---|
|
|
| **HID** (`usb/hid/`) | Keyboards in boot protocol. Pointers in report protocol, from their report descriptor: buttons, X, Y and wheel, relative (mice) or absolute (tablets, VM pointers), with report IDs. A boot mouse whose descriptor cannot be used falls back to boot protocol. Feeds the same input paths as PS/2. |
|
|
| **Mass storage** (`usb/storage/`) | Bulk-only transport and SCSI. Up to four sticks, `usb0`..`usb3`, each registered with the block layer with its partitions; pulling one out unregisters it. |
|
|
| **Hub** (`usb/hub/`) | Route strings, hub slot contexts, per-port reset and power, and the status-change endpoint for hot-plug behind the hub. |
|
|
|
|
Class drivers are offered **every interface** of a device, each as its own
|
|
`usb_dev`, once the configuration has been read, from the list in
|
|
`kernel/drivers/registry.c` — a wireless receiver's keyboard and mouse are
|
|
interfaces 0 and 1 and get a driver each. 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
|
|
|
|
- **Pulling out a stick a filesystem is mounted from panics** on that
|
|
filesystem's next read, by the rule in `blkdev_rw()`: a filesystem that went on
|
|
with missing blocks would be guessing.
|
|
- **HID is keyboards and pointers.** Consumer-control interfaces (volume keys),
|
|
game pads and the rest are declined. Caps, Num and Scroll Lock light their LEDs
|
|
on USB and PS/2 keyboards, one state shared between keyboards.
|
|
- **USB 3 hubs** are not driven: the hub driver speaks the USB 2 hub protocol.
|
|
- **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.
|
|
- **One controller.** A second xHCI card would bind and then contend for state
|
|
the driver keeps per-driver rather than per-instance.
|