Fix boot-to-login: override pcid-spawner to oneshot_async, add U3 input producers, Intel HDA phases A-D

- Override 00_pcid-spawner.service to oneshot_async in redbear-legacy-base.toml:
  rootfs phase no longer blocks on PCI driver init; getty/login starts immediately.
  Confirmed working on both QEMU and bare metal (redbear-live-mini).
- Clean up 00_base legacy script: remove dead notify ipcd/ptyd calls, keep sudo --daemon.
- Add U3 named input producers: inputd supports per-device named producers with
  fan-out to both device-specific consumers and legacy VT consumers. Migrate ps2d
  and usbhidd to InputProducer trait. RESERVED_DEVICE_NAMES deduplicated.
- Add Intel HDA audio driver phases A-D: ihdad error handling (37 fixes), audio
  quirks, codec path enumeration, mixer/volume control.
- Add init service start/readiness logging (always visible, not debug-gated).
- Update BOOT-PROCESS-ASSESSMENT.md: Phase 6 complete, boot procedure documented,
  validation matrix updated with confirmed boot status.
- Update USB-IMPLEMENTATION-PLAN.md and INPUT-SCHEME-ENHANCEMENT.md for U2/U3 status.
This commit is contained in:
2026-04-24 20:25:00 +01:00
parent 55af4d097b
commit 08bea46575
7 changed files with 1803 additions and 138 deletions
+209 -11
View File
@@ -1,8 +1,8 @@
# Red Bear OS Boot Process Assessment & Improvement Plan
**Generated:** 2026-04-23
**Updated:** 2026-04-23
**Status:** Phase 1 ✅, Phase 2 ✅, Phase 3 ✅, Phase 4 ✅ (docs + known gaps), Phase 5 ✅
**Updated:** 2026-04-24
**Status:** Phase 1 ✅, Phase 2 ✅, Phase 3 ✅, Phase 4 ✅ (docs + known gaps), Phase 5 ✅, Phase 6 ✅ (boot to login confirmed)
**Scope:** Comprehensive assessment of boot completeness, mistakes, robustness, resilience, and quality
## Boot Chain Overview
@@ -128,12 +128,12 @@ Bare-metal testing requires physical hardware. Current validation is:
## Phase 5: Validation Matrix ✅
### Build Verification
| Target | Build | QEMU Boot | Notes |
|--------|-------|-----------|-------|
| redbear-minimal | ✅ harddrive.img (2 GB) | ✅ Stage 2 (kernel loaded) | Login renders to framebuffer, not serial |
| redbear-full | ✅ harddrive.img (4 GB) | ✅ (prior session) | Greeter services load |
| redbear-live-mini | ✅ ISO (384 MB) | — | ISO for bare-metal boot |
| redbear-live | ✅ ISO (3.0 GB) | — | ISO for bare-metal boot |
| Target | Build | QEMU Boot | Bare-Metal Boot | Notes |
|--------|-------|-----------|-----------------|-------|
| redbear-mini | ✅ harddrive.img (2 GB) | ✅ Login prompt | — | Framebuffer console login |
| redbear-full | ✅ harddrive.img (4 GB) | ✅ Login prompt | — | Desktop packages included |
| redbear-live-mini | ✅ ISO (384 MB) | — | ✅ Login prompt | ISO for bare-metal boot |
| redbear-live-full | ✅ ISO (3.0 GB) | — | — | ISO for bare-metal boot |
### Compilation Verification
- `cargo check --workspace` in base source: **0 errors**
@@ -179,6 +179,82 @@ CI=1 make all CONFIG_NAME=redbear-minimal ARCH=x86_64
## Key Technical Findings
### Bare-Metal Boot Log Analysis (2026-04-24)
AMD machine boot log shows initfs phase starts but never completes:
- Kernel boots: ACPI, IOAPIC, timer, memory all OK
- vesad initializes: 1280x1024 at 0xA0000000 (FRAMEBUFFER_* from UEFI bootloader)
- fbbootlogd maps display
- ps2d: keyboard works, mouse BAT fails (no PS/2 mouse port — expected on modern hardware)
- pcid begins PCI enumeration
- acpid starts, AML interpreter initializes
- **MISSING**: "init: initfs drivers target step() complete" — scheduler.step() never returns
- **MISSING**: "init: phase 2 — switchroot to /usr" — rootfs phase never starts
- **MISSING**: any getty or login output
Root cause hypothesis (unproven): a service with `type = "notify"`, `type = { scheme = "..." }`,
or `type = "oneshot"` in the initfs phase does not signal readiness or does not exit,
causing init's scheduler.step() to block forever. All three service types wait synchronously
in `service.rs`. Possible blockers include:
- A `notify` service that hangs before calling `daemon::Daemon::ready()`
- A `scheme` service that hangs before calling `daemon::SchemeDaemon::ready_*()`
- An `oneshot` service like `pcid-spawner --initfs` that hangs during PCI enumeration
With the new per-service logging (Phase 6A + 6C), the next boot will show exactly which
service blocks — the last `init: starting ...` line before the hang identifies the blocker.
### Bare-Metal/QEMU Boot Log Analysis (2026-04-24, second test with Phase 6 logging)
The enhanced logging proved the initfs phase completes successfully. The actual blocker is
in the rootfs phase:
- Initfs phase: ✅ all services start and signal readiness/exit correctly
- `init: phase 2 - switchroot to /usr`
- `init: scheduling 22 rootfs units`
- `init: starting PCI driver spawner (pcid-spawner)`**BLOCKS HERE**
- pcid-spawner (rootfs, `type = "oneshot"`) spawns e1000d (ok), ihdad (fails with RIRB timeout)
- Then hangs — no further output for 30+ seconds while system is alive (keyboard works)
- Init never reaches `30_console` → getty → login
Root cause (confirmed): rootfs `00_pcid-spawner.service` uses `type = "oneshot"`, which
causes init to block until pcid-spawner exits. On real hardware and QEMU, pcid-spawner
can hang waiting for a PCI device driver that never responds, blocking the entire rootfs
phase including getty/login.
Fix: override `00_pcid-spawner.service` to `type = "oneshot_async"` in
`config/redbear-legacy-base.toml`. Drivers spawn in the background while init proceeds
to start console services. Network services that depend on specific drivers handle their
own timing (they connect to driver schemes when ready).
**Confirmed working**: Both QEMU and bare-metal boot to login prompt after this fix.
### Phase 6: Boot Visibility & Service Cleanup ✅
**Status: Confirmed working — system boots to login prompt on both QEMU and bare metal.**
**6A: Init service start logging (always visible)**
`init/src/scheduler.rs`: Service and target start messages promoted from DEBUG to always-visible.
Every service now logs `init: starting <description> (<cmd>)` before spawning and
`init: started <description> (pid <N>)` after a respawnable process is created.
**6B: Legacy init script cleanup**
`config/redbear-legacy-base.toml`:
- `00_base`: Removed dead `notify ipcd` / `notify ptyd` calls.
The `notify` binary does not exist anywhere in the build tree — these calls always failed
silently. ipcd and ptyd are started by the base recipe's systemd-style services
(`00_ipcd.service`, `00_ptyd.service`). sudo --daemon is kept because `00_sudo.service`
exists in the base recipe but is not wired into any target that gets scheduled.
The script now does tmpdir setup + sudo --daemon.
- `00_drivers`: Blanked (was redundant — pcid-spawner starts via `00_pcid-spawner.service`).
**6C: Service readiness completion logging**
`init/src/service.rs`: Added success log after each blocking wait completes:
- `notify` services: `init: <cmd> ready (notify)` after readiness byte received
- `scheme` services: `init: <cmd> ready (scheme <name>)` after scheme registered
- `oneshot` services: `init: <cmd> done (oneshot)` after process exits successfully
Combined with 6A's `init: starting ...` before spawn, the boot log now shows the full
lifecycle of every blocking service — any gap between "starting" and "ready/done" pinpoints
the blocker.
### Serde `deny_unknown_fields` Behavior
`UnitInfo` and `Service` structs use `#[serde(deny_unknown_fields)]`. Any unrecognized field in `[unit]` or `[service]` sections causes the ENTIRE service file to fail deserialization. The init system logs the error and skips the service — it never starts.
@@ -192,10 +268,18 @@ Services with `type = "oneshot_async"` are fire-and-forget by default. Init spaw
### Config Include Chain
```
redbear-minimal.toml → minimal.toml, redbear-legacy-base.toml, redbear-device-services.toml, redbear-netctl.toml
redbear-full.toml → desktop.toml, redbear-desktop.toml, redbear-greeter-services.toml, ...
redbear-live-full.toml redbear-live.toml
redbear-live.toml → redbear-full.toml
redbear-full.toml → desktop.toml, redbear-legacy-base.toml, redbear-legacy-desktop.toml,
redbear-device-services.toml, redbear-netctl.toml, redbear-greeter-services.toml
desktop.toml → desktop-minimal.toml, server.toml
desktop-minimal.toml → minimal.toml
server.toml → minimal.toml
minimal.toml → base.toml
redbear-live-mini.toml → minimal.toml, redbear-legacy-base.toml, redbear-netctl.toml
redbear-live.toml → redbear-full.toml, ...
redbear-mini → redbear-minimal.toml → minimal.toml, redbear-legacy-base.toml,
redbear-device-services.toml, redbear-netctl.toml
```
### Upstream Targets (not Red Bear defined)
@@ -266,3 +350,117 @@ redbear-live.toml → redbear-full.toml, ...
- `local/scripts/validate-service-files.sh` — manual service schema validation (redbear-*.toml only)
- `local/docs/BOOT-PROCESS-ASSESSMENT.md` — this document
- `recipes/core/base/source/init.initfs.d/41_acpid.service` — acpid in initfs (boot race fix)
## Boot Procedure
### Supported compile targets
| Target | Purpose | Output |
|--------|---------|--------|
| `redbear-mini` | Minimal non-desktop (QEMU + bare metal) | `build/x86_64/harddrive.img` |
| `redbear-live-mini` | Minimal live ISO (bare metal only) | `build/x86_64/redbear-live-mini.iso` |
| `redbear-full` | Desktop/graphics (QEMU + bare metal) | `build/x86_64/harddrive.img` |
| `redbear-live-full` / `redbear-live` | Desktop/graphics live ISO (bare metal only) | `build/x86_64/redbear-live-full.iso` |
### Build commands
```bash
# Minimal target (QEMU testing)
CI=1 make all CONFIG_NAME=redbear-mini ARCH=x86_64
# Minimal live ISO (bare-metal boot)
CI=1 make live CONFIG_NAME=redbear-live-mini ARCH=x86_64
# Desktop/graphics target (QEMU testing)
CI=1 make all CONFIG_NAME=redbear-full ARCH=x86_64
# Desktop/graphics live ISO (bare-metal boot)
CI=1 make live CONFIG_NAME=redbear-live-full ARCH=x86_64
```
### QEMU boot (harddrive.img)
```bash
# Boot the minimal target in QEMU
make qemu CONFIG_NAME=redbear-mini
# Boot with more RAM
make qemu CONFIG_NAME=redbear-mini QEMUFLAGS="-m 4G"
# Boot desktop target
make qemu CONFIG_NAME=redbear-full
```
QEMU boots from `harddrive.img` (not the live ISO). The `-serial mon:stdio` flag provides
the serial console, but Red Bear uses the framebuffer console for login — type at the
graphical console, not serial.
### Bare-metal boot (live ISO)
1. **Build the ISO:**
```bash
CI=1 make live CONFIG_NAME=redbear-live-mini ARCH=x86_64
```
2. **Write ISO to USB drive:**
```bash
sudo dd if=build/x86_64/redbear-live-mini.iso of=/dev/sdX bs=4M status=progress && sync
```
Replace `/dev/sdX` with your USB device. Use `lsblk` to identify it.
3. **Boot from USB:**
- Insert USB into target machine
- Power on, enter UEFI boot menu (typically F12, F8, or Esc)
- Select the USB device as boot target
- Red Bear OS boots from UEFI → bootloader → kernel → init → login prompt
4. **Login:**
- Default user: `root`, no password
- The framebuffer console displays the login prompt after boot completes
### What happens during boot
```
UEFI firmware
→ Red Bear bootloader (loaded from EFI system partition)
→ Kernel (kstart → start → kmain)
→ userspace_init → bootstrap (forks initfs/procmgr/initnsmgr)
→ Initfs phase:
logd, inputd, vesad (framebuffer), fbcond, fbbootlogd,
ps2d (keyboard), acpid, pcid-spawner-initfs (initfs PCI drivers), lived, redoxfs
→ switchroot /usr
→ Rootfs phase:
00_base (tmpdir + sudo --daemon)
00_ipcd.service, 00_ptyd.service
00_pcid-spawner.service (async — spawns PCI drivers in background)
30_console (getty with respawn)
→ Login prompt on framebuffer console
```
### Boot log markers
The init system logs the following always-visible markers. If boot hangs, the last visible
marker identifies the blocker:
```
init: phase 1 — initfs boot
init: starting <description> (<cmd>) # before each service spawn
init: <cmd> ready (notify) # notify-type service ready
init: <cmd> ready (scheme <name>) # scheme-type service ready
init: <cmd> done (oneshot) # oneshot service exited
init: phase 2 — switchroot to /usr
init: scheduling N rootfs units
init: reached target <description>
init: phase 3 — rootfs services started
init: boot complete — entering waitpid loop
```
### Troubleshooting
| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| No display output | UEFI framebuffer not provided | Try different USB port or disable CSM in UEFI settings |
| Boot hangs after "scheduling N rootfs units" | A blocking service hangs | Check last "starting" line; `pcid-spawner` was previously the blocker |
| Keyboard not working | PS/2 unavailable, USB not ready | Modern hardware uses USB — ensure xHCI controller is functional |
| No login prompt | Getty not starting | Check `30_console` service in config; verify getty respawn is set |
| "missing field `unit`" parse error | Invalid service TOML | Run `./local/scripts/validate-service-files.sh config/` |
+31 -27
View File
@@ -26,11 +26,11 @@ The current `inputd` implementation in `recipes/core/base/source/drivers/inputd/
- `SchemeRoot` exists, but it is not a real directory yet: it does not enumerate entries.
- `lib.rs` only exposes `ProducerHandle`, `ConsumerHandle`, `DisplayHandle`, and `ControlHandle`.
Current callers confirm the limitation:
Current callers after migration:
- `ps2d` opens one `ProducerHandle` and sends both keyboard and mouse events into the same stream.
- `usbhidd` also opens one `ProducerHandle` and sends keyboard/mouse/button/scroll data into the same stream.
- local `evdevd` reads `/scheme/input/consumer`, receives anonymous mixed `orbclient::Event` values, and manually translates them.
- `ps2d` opens two `InputProducer` instances (`ps2-keyboard`, `ps2-mouse`) with legacy fallback, routing keyboard scancodes to the keyboard producer and mouse events to the mouse producer.
- `usbhidd` opens one `InputProducer` per interface instance (`usb-{port}-if{n}`) with legacy fallback.
- local `evdevd` reads `/scheme/input/consumer`, receives anonymous mixed `orbclient::Event` values, and manually translates them (not yet migrated to per-device streams).
## 3. Design Principles
@@ -454,23 +454,20 @@ This keeps `DeviceConsumer` simple and avoids introducing a second handle teardo
## 13. Migration Path
### 13.1 `ps2d`
### 13.1 `ps2d` — MIGRATED
`ps2d` is the first caller that should adopt the new API because it already has a clean split between keyboard and mouse sources.
`ps2d` now uses two `InputProducer` instances with named-first, legacy-fallback strategy:
Recommended startup logic:
1. Try `NamedProducerHandle::new("ps2-keyboard")`
2. Try `NamedProducerHandle::new("ps2-mouse")`
3. If both succeed, run in named mode
4. If either fails, close any partially opened named handle and fall back to one legacy `ProducerHandle::new()`
1. Try `InputProducer::new_named_or_fallback("ps2-keyboard")` → falls back to legacy on error
2. Try `InputProducer::new_named_or_fallback("ps2-mouse")` → falls back to legacy on error
3. `Ps2d` struct holds `keyboard_input: InputProducer` + `mouse_input: InputProducer`
Routing:
- keyboard scancodes → `ps2-keyboard`
- mouse move / absolute move / button / scroll events → `ps2-mouse`
- keyboard scancodes → `self.keyboard_input`
- mouse move / absolute move / button / scroll events → `self.mouse_input`
This preserves compatibility with old `inputd` while immediately enabling per-device consumers on new `inputd`.
This preserves compatibility with old `inputd` while enabling per-device consumers on new `inputd`.
### 13.2 `evdevd`
@@ -482,9 +479,15 @@ Once the scheme exists, local `evdevd` can move from `/scheme/input/consumer` to
It can keep the legacy consumer path as a fallback for older systems.
### 13.3 `usbhidd`
### 13.3 `usbhidd` — MIGRATED
`usbhidd` can remain legacy initially, then later migrate to named producers such as `usb-hid0`, `usb-hid1`, or more specific per-interface names.
`usbhidd` now uses one `InputProducer` per interface instance with named-first, legacy-fallback strategy:
1. Opens `InputProducer::new_named_or_fallback(&format!("usb-{}-if{}", port, interface_num))`
2. Falls back to legacy on error
3. All event writes go through the same `write_event()` method
Producer names: `usb-{port}-if{interface_num}` (e.g., `usb-1-if0`, `usb-1-if1`).
## 14. Backward Compatibility Requirements
@@ -520,16 +523,17 @@ This design does **not** include:
Another developer implementing this design should be able to proceed in this order:
1. extend `Handle` and `InputScheme` state
2. teach `openat()` to parse `producer/{name}`, `events`, and dynamic device names
3. add root `getdents()` support for `SchemeRoot`
4. refactor `write()` so producer type is detected before routing
5. fan out named-producer events to matching `DeviceConsumer` handles and the existing legacy path
6. add hotplug queue serialization helpers
7. extend `fevent()` and daemon notification loop for `DeviceConsumer` and `HotplugEvents`
8. add cleanup in `on_close()` for `NamedProducer`
9. extend `lib.rs` with the new handle types and directory lister
10. migrate `ps2d` with a named-producer-first, legacy-fallback strategy
1. extend `Handle` and `InputScheme` state
2. teach `openat()` to parse `producer/{name}`, `events`, and dynamic device names
3. add root `getdents()` support for `SchemeRoot`
4. refactor `write()` so producer type is detected before routing
5. fan out named-producer events to matching `DeviceConsumer` handles and the existing legacy path
6. add hotplug queue serialization helpers
7. extend `fevent()` and daemon notification loop for `DeviceConsumer` and `HotplugEvents`
8. add cleanup in `on_close()` for `NamedProducer`
9. extend `lib.rs` with the new handle types and directory lister
10. migrate `ps2d` with a named-producer-first, legacy-fallback strategy
11. migrate `usbhidd` with a named-producer-first, legacy-fallback strategy ✅
## 17. Final Outcome
+417
View File
@@ -0,0 +1,417 @@
# Intel HDA Implementation Plan
**Version:** 1.0 (2026-04-24)
**Status:** Draft execution plan
**Scope owner:** Audio subsystem (legacy HDA + Intel DSP decision path)
## Purpose
This document defines the concrete execution plan for implementing full Intel audio support in Red Bear OS, using Linux 7.0 source code in-tree as donor reference material.
"Full Intel support" is split into three tracks:
1. Legacy PCI HDA controller + analog codecs (current `ihdad` path)
2. HDMI/DP digital audio over HDA links
3. Modern Intel DSP-class platforms (SOF/AVS-class routing, not legacy-only HDA)
## Why This Plan Is Needed
Current in-tree evidence shows `ihdad` is an early implementation, not a complete Intel audio stack:
- Single-codec assumption in enumeration logic (`device.rs`)
- Unimplemented controller interrupt handler (`handle_controller_interrupt`)
- Fixed-format playback setup (44.1kHz / 16-bit / stereo)
- Incomplete scheme surface (`Handle::Todo`-centric behavior)
- No complete capture path integration in `audiod` (`TODO: audio input`)
- Historical hardware report: "No audio, HDA driver cannot find output pins"
## Current Stack Snapshot
### Driver and daemon surface
- `ihdad` registers `audiohw`
- `audiod` opens `/scheme/audiohw` and exposes `/scheme/audio`
- SDL backends use `/scheme/audio`
### Known contract constraints
- `audiod` mixes fixed-size buffers (`HW_BUFFER_SIZE = 512`)
- `ihdad` stream writes currently assume strict block sizing
- `ihdad` currently hardcodes one primary output format on setup
## Canonical Donor Sources (Linux 7.0 in-tree)
- Controller policy and quirks:
- `build/linux-kernel-cache/linux-7.0/sound/hda/controllers/intel.c`
- Generic parser and fixup engine:
- `build/linux-kernel-cache/linux-7.0/sound/hda/common/auto_parser.c`
- Core codec/controller plumbing:
- `build/linux-kernel-cache/linux-7.0/sound/hda/common/`
- Vendor codec implementations:
- `build/linux-kernel-cache/linux-7.0/sound/hda/codecs/`
- Intel DSP route-selection policy:
- `build/linux-kernel-cache/linux-7.0/sound/hda/core/intel-dsp-config.c`
- Modern Intel DSP implementations:
- `build/linux-kernel-cache/linux-7.0/sound/soc/sof/intel/`
- `build/linux-kernel-cache/linux-7.0/sound/soc/intel/avs/`
## Execution Model
The plan is organized as issue-sized work packages (`HDA-001`..`HDA-012`).
### Phase A: Legacy HDA correctness (must complete first)
#### HDA-001 — Multi-codec and function-group support
**Goal:** Remove single-codec assumptions and support real codec topology.
**Files:**
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/device.rs`
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/node.rs`
**Acceptance criteria:**
- Codec enumeration includes all detected codecs
- Bring-up does not assume first codec is the audio path
- `audiohw:codec` dump reflects multi-codec topology
#### HDA-002 — Controller interrupts and unsolicited events
**Goal:** Implement real controller interrupt handling and unsol event dispatch.
**Files:**
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/device.rs`
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/cmdbuff.rs`
**Acceptance criteria:**
- `handle_controller_interrupt()` is non-stub
- Jack-related unsol events are observable and processed
- No interrupt-ack regressions under continuous playback
#### HDA-003 — Format/rate/channel negotiation
**Goal:** Replace fixed-format startup with negotiated stream format.
**Files:**
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/device.rs`
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/stream.rs`
**Acceptance criteria:**
- Driver selects supported stream format from capabilities
- Unsupported format requests fail deterministically
- Startup no longer assumes 44.1kHz/16-bit/stereo only
#### HDA-004 — Real scheme endpoint model (`pcmout`/`pcmin`)
**Goal:** Replace `Handle::Todo` behavior with structured stream handles.
**Files:**
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/device.rs`
- `recipes/core/base/source/audiod/src/scheme.rs`
**Acceptance criteria:**
- Distinct playback and capture endpoints exist
- Handle lifecycle and permissions are explicit
- Multiple clients can be supported without implicit index-0 fallback
#### HDA-005 — Capture and duplex path
**Goal:** Implement and validate simultaneous input/output.
**Files:**
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/device.rs`
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/stream.rs`
- `recipes/core/base/source/audiod/src/main.rs`
- `recipes/core/base/source/audiod/src/scheme.rs`
**Acceptance criteria:**
- Capture endpoint is functional
- Duplex playback/capture runs stably for bounded runtime tests
- `audiod` input TODO is removed
### Phase B: Parser, fixups, and quirk-driven stability
#### HDA-006 — Generic parser + fixup framework
**Goal:** Add parser/fixup framework equivalent to Linux generic HDA model.
**Files:**
- New parser/fixup module(s) under `ihdad/src/hda/`
- Integration in `device.rs`
**Acceptance criteria:**
- Pin/path selection is parser-driven, not heuristic-only
- Fixups can be applied by device identity and pin/config criteria
- Targeted fixup can resolve known "no output pins" class failures
#### HDA-007 — Audio quirk data pipeline
**Goal:** Add audio quirk extraction and runtime loading pattern aligned with current quirks system.
**Files:**
- `local/scripts/extract-linux-quirks.py` (extend for HDA tables)
- `local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs` (add audio quirk model)
- `local/recipes/system/redbear-quirks/source/quirks.d/` (add audio quirk TOML)
**Acceptance criteria:**
- Audio quirk entries load from `/etc/quirks.d`
- Driver behavior can be changed by data without code edits
- At least MSI/probe/position/power policy classes represented
#### HDA-008 — Controller policy parity slice
**Goal:** Add minimum policy knobs parity with Linux HDA controller behavior.
**Files:**
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/device.rs`
- `recipes/core/base/source/drivers/audio/ihdad/src/main.rs`
**Initial parity targets:**
- MSI policy
- single-command fallback policy
- codec probe mask
- DMA position-fix policy
- jack poll fallback policy
**Acceptance criteria:**
- Policies are configurable and observable
- Policy defaults can be influenced by quirk data
### Phase C: Digital audio completeness
#### HDA-009 — HDMI/DP audio path
**Goal:** Implement digital codec path handling including ELD and sink constraints.
**Files:**
- New digital-audio module(s) in `ihdad/src/hda/`
- Integration points in `device.rs`
**Acceptance criteria:**
- HDMI/DP codec path is detected and usable on supported hardware/VMs
- ELD-informed format/channel limitations are honored
### Phase D: Modern Intel audio (DSP-class)
#### HDA-010 — Intel audio route dispatcher
**Goal:** Add driver-selection logic equivalent to Linux `intel-dsp-config` principles.
**Files:**
- New dispatcher logic in audio/pcid integration path
- `recipes/core/base/source/drivers/audio/ihdad/config.toml` and related registration surfaces
**Acceptance criteria:**
- cAVS/SOF-class devices are not incorrectly routed to legacy-only behavior
- Route decision uses bounded platform traits (PCI class/prog-if + board traits)
#### HDA-011 — SOF/AVS-class implementation track
**Goal:** Provide a modern Intel DSP-capable driver path separate from legacy `ihdad`.
**Donor roots:**
- `sound/soc/sof/intel`
- `sound/soc/intel/avs`
**Acceptance criteria:**
- At least one Intel cAVS/SOF-class machine can produce bounded playback
- Legacy HDA path remains intact on legacy devices
### Phase E: Desktop ecosystem compatibility
#### HDA-012 — PipeWire/PulseAudio compatibility bridge
**Goal:** Bridge Redox native audio to desktop software expecting PipeWire/PulseAudio APIs.
**Acceptance criteria:**
- KDE desktop audio consumers can produce sound through compatibility layer
- Scope and claim language remains bounded (no overclaim)
## Validation Gates
### G1 — Legacy HDA playback stability
- Environment: QEMU HDA and at least one bare-metal Intel HDA device
- Criteria:
- Sustained playback duration threshold met
- No IRQ storm, no driver lockup
- No repeated timeout errors from CORB/RIRB paths
### G2 — Jack event behavior
- Environment: bare metal with physical jack
- Criteria:
- Plug/unplug detected
- Route switches correctly
- Speaker mute policy behaves as expected
### G3 — Duplex stability
- Environment: bare metal preferred; QEMU for baseline
- Criteria:
- Capture + playback concurrently
- No starvation/deadlock in scheme processing
### G4 — Quirk efficacy
- Criteria:
- At least 3 hardware-specific issues fixed by data-driven quirks
- Fixes do not require permanent ad hoc branches in main flow
### G5 — Modern Intel path
- Environment: Intel cAVS/SOF-class system
- Criteria:
- Route dispatcher selects modern path
- Bounded playback success via DSP-capable path
## Risk and Dependency Notes
1. **Main risk:** Treating SOF/AVS systems as legacy HDA-only.
2. **Main technical debt risk:** Hardcoded policy instead of quirk-backed data.
3. **Integration dependency:** `audiod` contract must evolve in lockstep with `ihdad` stream model.
4. **Desktop dependency:** KDE audio integration remains blocked without compatibility bridge even if kernel/driver path works.
## Initial Prioritization (strict order)
1. HDA-001 through HDA-005
2. HDA-006 through HDA-008
3. HDA-009
4. HDA-010 and HDA-011
5. HDA-012
## HDA-001 Implementation Blueprint
This section defines the concrete first code slice for `HDA-001` (multi-codec + function-group support).
### Objective
Remove hardcoded codec `0` traversal and make codec/AFG/widget discovery data-driven from `STATESTS`.
### Current hotspots
- `ihdad` codec discovery currently hardcodes `let codec: u8 = 0` during enumeration.
- Widget addressing and lists (`outputs`, `inputs`, pins) are global vectors not grouped by codec/function group.
- The scheme dump path (`audiohw:codec`) assumes a single codec payload.
### Files to edit
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/device.rs`
- `recipes/core/base/source/drivers/audio/ihdad/src/hda/node.rs` (only if helper fields/methods are needed)
### Step-by-step patch plan
1. Introduce per-codec topology container in `device.rs`.
Add internal structures:
- `CodecTopology`
- `codec_addr: CodecAddr`
- `afgs: Vec<NodeAddr>`
- `widget_map: HashMap<WidgetAddr, HDANode>`
- `outputs: Vec<WidgetAddr>`
- `inputs: Vec<WidgetAddr>`
- `output_pins: Vec<WidgetAddr>`
- `input_pins: Vec<WidgetAddr>`
- `beep_addr: Option<WidgetAddr>`
- `IntelHDA` field:
- `codecs_topology: HashMap<CodecAddr, CodecTopology>`
2. Replace global widget collections with codec-scoped accessors.
Keep existing fields temporarily for migration safety, but make enumeration write to `codecs_topology` first.
After compile + smoke pass, remove stale globals (`outputs`, `inputs`, `widget_map`, pin vectors, `beep_addr`).
3. Refactor `enumerate()` to iterate all detected codecs.
- Use `self.codecs` as source (populated in `reset_controller()` from `STATESTS`).
- For each codec:
- Read root node `(codec, 0)`.
- Iterate all function groups in root range.
- Filter audio function groups (`function_group_type` audio class).
- Enumerate widgets and classify into per-codec topology lists.
4. Add safe codec selection helper for playback bring-up.
Add helper:
- `fn pick_primary_codec_for_output(&self) -> Option<CodecAddr>`
Selection policy v1:
- First codec with at least one `output_pin` and one `AudioOutput` widget.
- Stable tie-breaker: lowest codec address.
5. Make `find_best_output_pin()` codec-aware.
Change signature from global behavior to:
- `fn find_best_output_pin(&mut self, codec: CodecAddr) -> Result<WidgetAddr>`
Ensure all widget lookups use the selected codec topology map.
6. Update path walk helpers to consume codec-scoped maps.
- `find_path_to_dac()` should use the selected codec topology `widget_map`.
- Avoid `.unwrap()` on map lookups in traversal; return `None`/`Err(ENODEV)` on missing nodes.
7. Update `configure()` to use selected codec.
- Choose codec via `pick_primary_codec_for_output()`.
- Call `find_best_output_pin(codec)`.
- Resolve DAC path only within that codec.
8. Update codec dump endpoint to expose all codecs.
- Keep `openat("codec")` behavior, but include per-codec sections in output.
- Optional follow-up: add `codec/<n>` path support; not required for first slice.
9. Guard rails for no-audio cases.
- If codecs are present but no valid output topology found, return structured `ENODEV`.
- Do not panic on `No output pins`.
### Non-goals for HDA-001
- Jack unsolicited handling (`HDA-002`)
- Capture stream enablement (`HDA-005`)
- Policy quirks (`HDA-008`)
- HDMI/DP ELD path (`HDA-009`)
### Compile + runtime checks for this slice
1. Build driver package:
- `./target/release/repo cook recipes/core/base`
- or full base target flow already used by this tree
2. Boot in QEMU with HDA enabled:
- validate `ihdad` starts without panic
- read codec dump from `audiohw:codec`
3. Verify acceptance:
- Multiple codec entries are shown when available
- Single-codec machines still work
- No regression in existing playback path on QEMU ICH9 HDA
### Exit criteria for closing HDA-001
- Enumeration is no longer hardcoded to codec 0.
- Playback path can choose a valid codec deterministically.
- Codec dump includes all detected codecs.
- `ihdad` no longer panics when output pins are missing on codec 0 but present on another codec.
## Claim Language Policy
Until G1-G5 gates are met, support claims must remain bounded:
- Use: "builds", "enumerates", "bounded playback proof"
- Avoid: "full Intel audio support" or broad compatibility claims
## Related Documents
- `local/docs/LINUX-BORROWING-RUST-IMPLEMENTATION-PLAN.md`
- `local/docs/QUIRKS-SYSTEM.md`
- `local/docs/QUIRKS-IMPROVEMENT-PLAN.md`
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md`
- `docs/05-KDE-PLASMA-ON-REDOX.md`
- `recipes/core/base/source/drivers/COMMUNITY-HW.md`
+36 -14
View File
@@ -27,7 +27,7 @@ This repo should not treat **builds** or **enumerates** as equivalent to **valid
USB driver code lives in `recipes/core/base/source/drivers/usb/`, which is an upstream-managed git
working copy. Red Bear carries all USB modifications through `local/patches/base/redox.patch`
(currently 5029 lines, 23 diff sections, 16 USB/HID/storage-related).
(currently ~17000 lines across ~100 diff sections).
**Upstream state** — the unpatched source snapshot that `make fetch` produces — has significant
error handling gaps and several correctness bugs. Red Bear's patch layer fixes these, but the fixes
@@ -124,7 +124,7 @@ source:
Even with the Red Bear patch applied:
- HID is still wired through the legacy mixed-stream `inputd` path
- HID is now wired through named producers (`ps2-keyboard`, `ps2-mouse`, `usb-{port}-if{n}`); named producers always fan out to both per-device consumers and the legacy VT consumer path; the `InputProducer` wrapper falls back to an anonymous legacy `ProducerHandle` if the named path is unavailable (e.g., older `inputd` build)
- external USB keyboard fallback is not guaranteed on bare metal unless the keyboard reaches the
xHCI runtime path
- EHCI/UHCI/OHCI are not yet full runtime host-controller implementations
@@ -151,11 +151,11 @@ Even with the Red Bear patch applied:
| xHCI controller | **builds / QEMU-validated** | Red Bear patch: 88 error handling fixes, ERDP split, endp_direction fix, cfg_idx fix, real grow_event_ring, mutex poison recovery on all hot-path locks; no real hardware validation yet |
| EHCI/UHCI/OHCI | **builds / enumerates** | Ownership, port handling, and logging exist, but they are not yet full runtime enumeration paths |
| Hub handling | **builds / good quality** | `usbhubd`: all `expect()` eliminated, interrupt-driven change detection with polling fallback, graceful per-port error handling |
| HID | **builds / QEMU-validated in narrow path** | `usbhidd` handles keyboard/mouse/button/scroll via legacy input path, no panics in report loop; keyboard LED sync exists as a bounded per-device best-effort path |
| HID | **builds / QEMU-validated in narrow path** | `usbhidd` handles keyboard/mouse/button/scroll via named producer path (`usb-{port}-if{n}`) with legacy fallback, no panics in report loop; keyboard LED sync exists as a bounded per-device best-effort path |
| Mass storage | **builds / good quality** | `usbscsid`: typed `ScsiError`, fallible parsing, `ReadCapacity16` for >2TB, stall recovery, resilient event loop |
| Native tooling | **builds / enumerates** | `lsusb`, `usbctl`, `redbear-info`, `redbear-usb-check` provide observability |
| Low-level userspace API | **builds** | `xhcid_interface` with `UsbSpeed` enum, `attach_with_speed()` |
| Validation | **builds / QEMU-only** | 3 harness scripts + in-guest checker; no real hardware validation scripts |
| Validation | **builds / QEMU-only** | 4 harness scripts + in-guest checker; no real hardware validation scripts |
| Hardware quirks | **builds** | `redox-driver-sys` quirk tables with 146 compiled-in USB quirk entries (mined from Linux 7.0) + 22 USB quirk flags; runtime TOML loading for `/etc/quirks.d/` |
## Code Quality by Daemon
@@ -183,7 +183,7 @@ Key files and their sizes:
| Daemon | Lines | Error Handling Quality | Remaining unwrap/expect | Key Gaps |
|---|---|---|---|---|
| `usbhubd` | ~430 | **Good**`Result<(), Box<dyn Error>>`, all `expect()` eliminated, interrupt-driven change detection | 0 | 1-second polling fallback if interrupt EP unavailable |
| `usbhidd` | 576 | **Good**`anyhow::Result` with context, zero `unwrap()`/`expect()` | 0 | Hardcoded 1ms poll rate; mouse ×2 multiplier workaround; X scroll missing |
| `usbhidd` | 576 | **Good**`anyhow::Result` with context, no panics in report loop; `expect()` remains in arg parsing and descriptor setup (pre-existing) | 7 `expect()` + 1 `assert_eq!` (pre-existing, arg parsing/descriptor setup) | Hardcoded 1ms poll rate; mouse ×2 multiplier workaround; X scroll missing |
| `usbscsid` | ~1800 | **Good**`ScsiError` typed errors, fallible `parse_bytes`/`parse_mut_bytes` helpers, resilient event loop, `ReadCapacity16` | 0 | — |
## Validation Infrastructure
@@ -301,8 +301,19 @@ hardware; controller enumerates attached devices reliably across repeated boot c
**Remaining**:
- Validate repeated attach/detach/reset behavior under stress (requires real hardware)
- Support non-default configurations and alternate settings (requires xHCI config logic in scheme.rs)
- Improve composite-device handling and endpoint selection across interfaces (requires xHCI config logic in scheme.rs)
**Completed (Red Bear patch, this session)**:
- `configure_endpoints_once()` now filters endpoints by specific interface+alternate when
`req.interface_desc` is set, enabling composite-device drivers to claim individual interfaces
without programming endpoints from other interfaces
- When `interface_desc` is `None` (initial device setup), endpoints are collected from all
default-alternate (alt 0) interfaces, preserving backward compatibility
- `PortState.active_ifaces: BTreeMap<u8, u8>` tracks which interface numbers are active and
which alternate setting each is using
- `set_interface()` now updates `active_ifaces` after a successful SET_INTERFACE control request
- `spawn_drivers()` logs non-default alternates at debug level instead of warning, documenting
that non-default alternates are selected by drivers via SET_INTERFACE rather than auto-spawn
- Initial configuration populates `active_ifaces` with all default-alternate interfaces
**Where**: `recipes/core/base/source/drivers/usb/usbhubd/`, `xhcid/src/xhci/scheme.rs`
@@ -316,14 +327,25 @@ least one composite device configures correctly beyond the simplest path.
**Status**: Partially complete.
**Completed (Red Bear patch)**:
- `usbhidd` error handling improved — `anyhow::Result` with context, no panics in report loop, zero `unwrap()`/`expect()` calls
- `assert_eq!` replaced with `anyhow::bail!`
- `usbhidd` error handling improved — `anyhow::Result` with context, no panics in report loop; `expect()`/`assert_eq!` remain in arg parsing and descriptor setup (pre-existing)
- Display write failures logged as warnings instead of panicking
- `inputd` scheme enhancement: named producers (`/scheme/input/producer/{name}`), per-device
consumer streams (`/scheme/input/{device_name}`), hotplug event stream (`/scheme/input/events`),
root directory enumeration (static entries + dynamic device names)
- Named producer events fan out to both matching DeviceConsumers and the legacy VT consumer path
- Hotplug binary format: 16-byte header (kind, device_id, name_len, reserved) + UTF-8 name
- Device IDs allocated monotonically, never reused
- Public API: `NamedProducerHandle`, `DeviceConsumerHandle`, `HotplugHandle`, `InputDeviceLister`,
`InputProducer` (named-first, legacy-fallback convenience wrapper)
- All legacy paths, event payloads, VT behavior, and display/control behavior preserved unchanged
- `ps2d` migrated: two `InputProducer` instances (`ps2-keyboard`, `ps2-mouse`), keyboard events
route to `keyboard_input`, mouse events to `mouse_input`, named-first with legacy fallback
- `usbhidd` migrated: one `InputProducer` per interface instance (`usb-{port}-if{n}`), named-first
with legacy fallback
**Remaining** (all require architectural changes to `inputd`, not USB-internal code):
- Migrate `usbhidd` toward named producers and per-device streams (requires inputd redesign)
- Expose hotplug add/remove behavior cleanly to downstream consumers (requires inputd redesign)
- Align USB HID with the `inputd` enhancement design already documented in-tree (cross-cutting)
**Remaining** (requires downstream consumer/driver migration, not inputd scheme changes):
- Migrate `i2c-hidd` to named producers (still uses legacy `ProducerHandle`)
- Expose hotplug add/remove behavior to downstream consumers via `evdevd` migration
**Where**: `recipes/core/base/source/drivers/input/usbhidd/`, `inputd/`,
`local/docs/INPUT-SCHEME-ENHANCEMENT.md`
@@ -685,7 +707,7 @@ The remaining gaps now fall into two categories:
**Broader architectural work (cross-cutting, not a small bounded USB-only fix):**
- Any remaining USB composite/device-model issues now belong to wider device-model/design cleanup
rather than one more isolated helper patch.
- HID producer modernization: per-device streams, hotplug add/remove (requires inputd redesign)
- HID producer modernization: per-device streams via named producers, hotplug add/remove (inputd redesign complete, ps2d and usbhidd migrated)
- Userspace USB API: `libusb` WIP, no coherent native story
**Hardware-dependent or design decisions:**
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,44 @@
# Audio controller and codec quirks.
# These apply to HDA controllers and codec devices.
# Intel ICH8 HDA — force EAPD on outputs
[[pci_quirk]]
vendor = 0x8086
device = 0x284b
flags = ["audio_force_eapd"]
# Intel ICH9 HDA (QEMU) — use immediate command interface
[[pci_quirk]]
vendor = 0x8086
device = 0x293e
flags = ["audio_single_cmd"]
# Intel 6-series PCH HDA — position fix LPIB
[[pci_quirk]]
vendor = 0x8086
device = 0x1c20
flags = ["audio_position_fix_lpib"]
# Intel 7-series PCH HDA — position fix LPIB
[[pci_quirk]]
vendor = 0x8086
device = 0x1e20
flags = ["audio_position_fix_lpib"]
# Intel Sunrise Point HDA — position fix LPIB
[[pci_quirk]]
vendor = 0x8086
device = 0xa170
flags = ["audio_position_fix_lpib"]
# Intel Cannon Point HDA — position fix LPIB
[[pci_quirk]]
vendor = 0x8086
device = 0x9dc8
flags = ["audio_position_fix_lpib"]
# AMD FCH HDA — single command fallback
[[pci_quirk]]
vendor = 0x1022
device = 0x1457
flags = ["audio_single_cmd"]