# Curated ioctl-interface registry — `driver_info` data (Part 13 §5.2.3)

One JSON file per curated driver class. The Zig broker reads these
at compile time via `@embedFile` and surfaces them through the
`driver_info` wire op (§5.2.2). Sub-phase 3B of Part 13.

## Format

The spec (§5.2.3) sketches the registry in YAML. We ship JSON
because Zig's stdlib has a built-in JSON parser and zero YAML
support — adding a YAML dependency to ship a ~50-line registry
would be cargo-culted convention over function. The shape is
identical; only the syntax differs. Maintainers edit via the
generator script (below), so the file format is an implementation
detail they don't touch by hand.

## Schema (per file)

```jsonc
{
  "driver": "usbdevfs",                  // curation label (matches filename)
  "module": "usbcore",                   // loaded-module name
  "header": "<linux/usbdevice_fs.h>",    // UAPI header path, with brackets
  "maintainer": "mgf-hrb",               // who maintains this entry
  "last_verified": "2026-05-26",         // ISO date — bump on every regenerate
  "ioctls": [
    {
      "name":      "USBDEVFS_CONTROL",   // C macro name
      "number":    "0xc0185500",         // computed by gcc -E on the header
      "arg":       "struct usbdevfs_ctrltransfer",
      "direction": "rw"                  // r | w | rw | none
    }
    // ...
  ],
  "char_devices": [
    { "pattern": "/dev/bus/usb/*/*", "permissions": "0664" }
  ],
  "uevent_actions": ["bind", "unbind"]
}
```

## Scope

All five federation-maintained driver classes from the §5.2.3
spec ship as of this writing:

* **`usbdevfs.json`** — 31 fixed-number ioctls.
* **`hidraw.json`** — 3 fixed + 9 variable-size ioctls (the
  parameterized `(len)` HIDIOC* macros, via the schema
  extension below).
* **`vfio.json`** — 15 container / group / device / IOMMU
  ioctls (migration + PCI_HOT_RESET deferred as niche).
* **`drm.json`** — 10 generic-DRM ioctls (vendor-specific
  drm/i915_drm.h / drm/amdgpu_drm.h surfaces live in per-
  vendor registry entries when use-cases land).
* **`v4l2.json`** — 12 capture-path ioctls (output / vbi /
  m2m / subdev deferred).

### Variable-size schema extension

Parameterized macros like `HIDIOCGRAWNAME(len)` carry the
runtime size in the encoded number — there's no single static
value. The registry handles them with two new fields:

```jsonc
{
  "name":          "HIDIOCGRAWNAME",
  "variable_size": true,
  "base_number":   "0x80004804",   // value at len=0
  "arg":           "char[len]",
  "direction":     "r"
}
```

The consumer overlays the actual `len` into the size field
(`base | (len << _IOC_SIZESHIFT)`) when calling `ioctl()`.
The conformance gate evaluates the base at size=0 against the
live header. Fixed-number entries keep using `number`
(unchanged); the two shapes are mutually exclusive on the
wire — `number` XOR (`base_number` + `variable_size:true`).

## Regenerating

Use the maintainer tool — never edit JSON files by hand:

```bash
.venv/bin/python scripts/regenerate_ioctl_registry.py
```

The tool:

1. Compiles a tiny C probe that includes `<linux/ioctl.h>` plus
   each driver's UAPI header.
2. Prints every macro's expanded value via `printf("%08lx")`.
3. Writes the JSON files with today's date as `last_verified`.

After regenerating, the conformance gate
(`tests/conformance/test_ioctl_registry_currency.py`) re-runs the
same computation and asserts every JSON entry matches the live
header. The gate runs in the `conformance` test tier — explicit
`pytest -m conformance` invocation.

## How to add a driver

1. Open `scripts/regenerate_ioctl_registry.py`.
2. Append a `CuratedDriver(...)` entry to the `CURATED` tuple
   with the macro list (name + arg + direction per ioctl) and
   the char-device patterns.
3. Run the regenerator. Inspect the resulting `<driver>.json`.
4. Add an `@embedFile` line for the new driver in
   `hrb-code/src/driver_registry.zig`'s `sources` array.
5. Bump `max_entries` in `driver_registry.zig` if approaching
   the cap.
6. `zig build` + `pytest -m conformance` to verify.
7. Commit the JSON, the script change, and the registry change
   in one PR.

## Header-drift gate (`last_verified`)

When a kernel UAPI header changes (e.g. a new ioctl macro is
added upstream, or a struct's sizeof changes), the conformance
gate's next run will FAIL with a diff naming which macros
drifted. The fix is: re-run `regenerate_ioctl_registry.py`,
inspect the diff (any unexpected drift = audit-required), and
commit the result. The new `last_verified` date documents the
re-verification.

The broker NEVER live-probes ioctls (calling `ioctl(fd, X,
NULL)`) — that's UB-adjacent for drivers that interpret the arg
unconditionally. The header is the source of truth.
