Compare commits
303 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e808741ea | |||
| 596407a52d | |||
| 33c002d563 | |||
| 1b3b29ba91 | |||
| 46cd3704b9 | |||
| fc34718ae8 | |||
| dac00073ba | |||
| ac9c12d6d4 | |||
| 5b4f2ac984 | |||
| 827cc0223d | |||
| 9e6851d43c | |||
| 726e628e0d | |||
| 0dc1786f66 | |||
| ed7aba6ed1 | |||
| df34186680 | |||
| 8ef8d16189 | |||
| 4c4db5ce5f | |||
| 3123446f49 | |||
| 070b838aa3 | |||
| 51d790c218 | |||
| 5175dfb739 | |||
| a37eef9d67 | |||
| 38b61167fa | |||
| 6773b79d1f | |||
| 7e4a88e418 | |||
| b95ac973e8 | |||
| 5258ebe6ff | |||
| 7607ff3ee5 | |||
| 8641f22bd1 | |||
| b857d41065 | |||
| 72fc7c6f87 | |||
| 5c92aeadd5 | |||
| acd6e221c5 | |||
| dc9155a2a8 | |||
| feba6e2ad8 | |||
| 0f239bfe8a | |||
| c6532f590c | |||
| 0a16f5ec35 | |||
| 14bb0eb42e | |||
| f79954e2bc | |||
| 87b85ca083 | |||
| 0d494d8e89 | |||
| 19c7856642 | |||
| 777a2c71d3 | |||
| 3452225e0e | |||
| ab2798834c | |||
| da8fe50c4a | |||
| 022d4ce28b | |||
| 13799b8af9 | |||
| 41ffba601a | |||
| 1082f965a9 | |||
| c5b916e062 | |||
| 1584acc4af | |||
| ffc4b3cc6e | |||
| 5fe49527be | |||
| fc0a011591 | |||
| bec5262019 | |||
| eb7b88548b | |||
| 11a7abcc8d | |||
| a99fb17568 | |||
| 5266c3d0ce | |||
| c7ed62eb30 | |||
| ebf6e0cca4 | |||
| 3fd40a6ade | |||
| 9a0b2c5f4a | |||
| b331285661 | |||
| 2728c2e408 | |||
| 04e49cfe38 | |||
| 74ea610745 | |||
| e28d575668 | |||
| eb5b428fa4 | |||
| 7a9e3f3466 | |||
| 905a4f70f8 | |||
| 69dff0c26c | |||
| 85a82f56d4 | |||
| bbcd44833d | |||
| 42feab633e | |||
| f0341aa53d | |||
| 19829958db | |||
| d9ff6b7788 | |||
| fec44e9117 | |||
| bc4df8e437 | |||
| 128832c568 | |||
| cc770de03e | |||
| 7dad32bdc1 | |||
| 9a0e0d86c7 | |||
| 8119824512 | |||
| a0b8ba1014 | |||
| 4dc60bdc5d | |||
| 346daee089 | |||
| 42c807c55a | |||
| 682560cf7a | |||
| ef3f696689 | |||
| fe53efcd83 | |||
| 7d341b6254 | |||
| 9e08f7ed05 | |||
| affb6690c5 | |||
| ba05902883 | |||
| 78ff5b5c4c | |||
| e9b82a3a04 | |||
| faf688a2d7 | |||
| 75c3a472df | |||
| 8de0308b6e | |||
| dece1210c7 | |||
| 0035aa65cc | |||
| 1bc5f8ac88 | |||
| c5a229a7cd | |||
| 6fa762d9e1 | |||
| bd1016a202 | |||
| 58695cc478 | |||
| 97a1ad301f | |||
| 7ab0af7ab4 | |||
| a93e9807f0 | |||
| b3a249e450 | |||
| 75229b0422 | |||
| 60588bef46 | |||
| 827fe89872 | |||
| 91b58cfacc | |||
| babffee80e | |||
| 2362ea80f1 | |||
| 0eac7f082f | |||
| 5d0e41dff2 | |||
| f10f3b15b6 | |||
| 79e897a628 | |||
| 59c9ec8ed8 | |||
| 2fc7d20f73 | |||
| 763b7110c5 | |||
| 0285960a56 | |||
| e729e4daec | |||
| ef3dd8b1fd | |||
| f9ace4a956 | |||
| 2061609f62 | |||
| d9368b08b2 | |||
| b48410cbf5 | |||
| d5c8498ac3 | |||
| f7f2890f4e | |||
| 0ae101fdd0 | |||
| f99738d2fb | |||
| 05bb9095ce | |||
| 33465b59e0 | |||
| b5205e162d | |||
| 23e42a07f7 | |||
| af282ee049 | |||
| 484fe692fa | |||
| 1d474679f4 | |||
| 95fdc37e52 | |||
| e13f09f258 | |||
| 4cb216aacd | |||
| e7d5dad0aa | |||
| 00c0eaa6b6 | |||
| 8667fded2e | |||
| 914b2314e6 | |||
| 0a16b05232 | |||
| 4826d9f950 | |||
| c9ad1e8deb | |||
| 5d869d0c45 | |||
| 3e8ad78299 | |||
| 652c1e6039 | |||
| ab3685cd17 | |||
| f568f9cbb2 | |||
| daa126cc07 | |||
| f557692754 | |||
| e8950c2760 | |||
| b018e777ee | |||
| 80eb79c4d8 | |||
| a965521cba | |||
| f966f6e449 | |||
| c2dfeea1e8 | |||
| ae113df83c | |||
| 033f29a4af | |||
| d42da2343e | |||
| d986481cf2 | |||
| 6a2c782925 | |||
| 2a5a2f37d5 | |||
| 6915dce88d | |||
| 1dce1568bf | |||
| eec98d3ff7 | |||
| dade04a383 | |||
| 4de2f6ae3e | |||
| bd395e4cfa | |||
| 85ab930d1f | |||
| 53058e3e4e | |||
| aa3367acd2 | |||
| 893374a992 | |||
| dc32c6ccbf | |||
| 203c40ab53 | |||
| 4325c67471 | |||
| b4fc7d9869 | |||
| 4d4f940916 | |||
| 29f08c949a | |||
| c1cf86a38a | |||
| 5ad5e86e5c | |||
| 11d3665d7d | |||
| 64325790de | |||
| acc92b6841 | |||
| 4c4dd8fa74 | |||
| d6b6e407b5 | |||
| 9024b87934 | |||
| 5d6faeaac6 | |||
| 84bcc979de | |||
| ca0ea881f4 | |||
| e8fa2ca97b | |||
| 72059ea9ef | |||
| 8a77a6ebde | |||
| f54b53fbfb | |||
| 48a06bd3a8 | |||
| 2ae61a4499 | |||
| 193d2926d0 | |||
| e07f264ab7 | |||
| 748c5f7ac2 | |||
| dd10299209 | |||
| 290664d2e1 | |||
| 19d093129e | |||
| 43bc027485 | |||
| ec101f9d4b | |||
| c00dc8a0e3 | |||
| 329708940b | |||
| 08aa9768be | |||
| f73acfc1c0 | |||
| 54de45d461 | |||
| 73cf268273 | |||
| 76df890074 | |||
| b8aac3c9bc | |||
| 7a2b0d5160 | |||
| 47abccbc13 | |||
| 17804d9a6b | |||
| 927d737d9c | |||
| 6c771d88f9 | |||
| 4a3d097b27 | |||
| 2db8636f8b | |||
| a9a3507daf | |||
| 0b15a49b55 | |||
| 40d091226f | |||
| dfd75b52f6 | |||
| eaf3c221ab | |||
| 1826f079d3 | |||
| 08dbaf519a | |||
| d14d4ad72d | |||
| 790e476d8e | |||
| ab2d5de81d | |||
| b058c05338 | |||
| 8ca31beee3 | |||
| 9ea6826a58 | |||
| c737337681 | |||
| 3f4f76a762 | |||
| 109bdc8dc3 | |||
| d4ddc4e2c7 | |||
| 2633c40dfd | |||
| 135e94b5e4 | |||
| b1efd23faf | |||
| a2df7a06cf | |||
| 146986b955 | |||
| 7043a466c5 | |||
| 4526853895 | |||
| ba429163e9 | |||
| eb8686f4c3 | |||
| 4e3b06af83 | |||
| fb9be0d293 | |||
| b66a9e509e | |||
| 56d862e7a7 | |||
| 9e2a2666e0 | |||
| c0157fc30e | |||
| 3d3937c22d | |||
| f7c3d504dd | |||
| 2c4ccd5f3f | |||
| a2958e9b02 | |||
| 26ecd868f7 | |||
| 1957f3ff36 | |||
| 5be1ec17b3 | |||
| d7273ce5cf | |||
| 2c702465a1 | |||
| 1eed99c54f | |||
| f47ee4fb18 | |||
| 2bed64a6c5 | |||
| 5d4d3dbce1 | |||
| f0364a4e4a | |||
| 890be982a6 | |||
| afad19ff1a | |||
| eb53e8190a | |||
| 6d13dee2a6 | |||
| a43cca122d | |||
| fc0c1e4576 | |||
| 83f47db352 | |||
| f877419c43 | |||
| ac0712e8b9 | |||
| d097261be3 | |||
| 8ce2ec6b21 | |||
| 5e68844868 | |||
| 217ca485b7 | |||
| 5b42305568 | |||
| f988cc0d58 | |||
| bc7bbfb293 | |||
| 9c93a3fc5b | |||
| 7902864a32 | |||
| 8b627c40af | |||
| 0757975704 | |||
| 3539e621a2 | |||
| 7bbf56217e | |||
| 097dc10f70 | |||
| 327aed1e5d | |||
| db6fbe5155 | |||
| 7fdf828c75 | |||
| 7aeb3bb475 |
@@ -113,23 +113,25 @@ echo 'PODMAN_BUILD?=1' > .config # Podman container build
|
||||
# archive and ignores the local forks. Remove it if present:
|
||||
grep -v REDBEAR_RELEASE .config > .config.tmp && mv .config.tmp .config
|
||||
|
||||
# Build Red Bear OS
|
||||
# Build Red Bear OS — CANONICAL build command is build-redbear.sh
|
||||
# Supported compile targets:
|
||||
# redbear-full desktop/graphics target (harddrive.img or live ISO)
|
||||
# redbear-mini text-only console/recovery target (harddrive.img or live ISO)
|
||||
# redbear-full desktop/graphics target (live ISO)
|
||||
# redbear-mini text-only console/recovery target (live ISO)
|
||||
# redbear-grub text-only with GRUB boot manager (live ISO)
|
||||
# Desktop/graphics target: redbear-full
|
||||
# Text-only targets: redbear-mini, redbear-grub
|
||||
./local/scripts/build-redbear.sh redbear-mini # Recommended (dev mode, local forks)
|
||||
./local/scripts/build-redbear.sh redbear-mini # Recommended (dev mode, offline)
|
||||
./local/scripts/build-redbear.sh --upstream redbear-mini # With online fetch (fast iteration)
|
||||
make all CONFIG_NAME=redbear-mini # Text-only target → harddrive.img
|
||||
make all CONFIG_NAME=redbear-full # Desktop/graphics target → harddrive.img
|
||||
make live CONFIG_NAME=redbear-full # Full desktop live ISO
|
||||
make live CONFIG_NAME=redbear-mini # Text-only mini live ISO
|
||||
make live CONFIG_NAME=redbear-grub # Text-only mini live ISO with GRUB
|
||||
CI=1 make all CONFIG_NAME=redbear-mini # CI mode (disables TUI, for non-interactive)
|
||||
./local/scripts/build-redbear.sh redbear-full # Desktop/graphics target
|
||||
./local/scripts/build-redbear.sh redbear-grub # Text-only + GRUB
|
||||
./local/scripts/build-redbear.sh --no-cache redbear-mini # Force clean rebuild
|
||||
|
||||
# IMPORTANT: For local fork development, use:
|
||||
# build-redbear.sh handles: .config parsing, prefix staleness detection,
|
||||
# local-over-WIP policy, version sync, pre-cooking critical packages,
|
||||
# source fingerprint tracking, and ultimately calls `make live`.
|
||||
# Output: build/<arch>/<config>.iso
|
||||
|
||||
# For local fork development:
|
||||
# export REDBEAR_ALLOW_PROTECTED_FETCH=1 # base/kernel/relibc are protected recipes
|
||||
# ./local/scripts/build-redbear.sh --upstream redbear-mini
|
||||
# Without --upstream, the build is in offline mode and re-extracts from
|
||||
@@ -138,7 +140,6 @@ CI=1 make all CONFIG_NAME=redbear-mini # CI mode (disables TUI, for non-inter
|
||||
# Run
|
||||
make qemu # Boot in QEMU
|
||||
make qemu QEMUFLAGS="-m 4G" # With more RAM
|
||||
make live # Build live ISO for real bare metal
|
||||
|
||||
# QEMU with network access (required for testing):
|
||||
qemu-system-x86_64 -cdrom build/x86_64/redbear-mini.iso \
|
||||
@@ -420,18 +421,69 @@ KDE recipes, and all recipes carrying Red Bear patches (Qt, DRM, Mesa, Wayland,
|
||||
glib, etc.). Any recipe with a `redox.patch` or local patches is a candidate for
|
||||
protection — add it when adding patches.
|
||||
|
||||
### Local Fork Priority and Version Sync (MANDATORY)
|
||||
|
||||
**Rule**: If a recipe exists in `local/sources/<component>/` (a local fork), it
|
||||
**always takes precedence** over the upstream version in `recipes/<component>/`.
|
||||
However, the build system **must guarantee** that the local fork is at the
|
||||
**latest available version** and that **all Red Bear patches are applied cleanly**
|
||||
on top of that version.
|
||||
|
||||
**Rationale**: Red Bear forks are designed to be drop-in compatible with
|
||||
upstream. Transitive dependencies from crates.io may pull in newer versions
|
||||
of shared crates (e.g. `redox_syscall`, `redox-scheme`). If the local fork is
|
||||
at a lower version than what the dependency graph requires, two consequences
|
||||
follow:
|
||||
1. **Lockfile collision**: Cargo sees `redox_syscall v0.8.1 (local/sources/syscall)`
|
||||
and `redox_syscall v0.8.1 (recipes/core/base/syscall)` as different sources
|
||||
even when they resolve to the same directory via symlink.
|
||||
2. **Type mismatch**: Transitive deps built against `redox_syscall 0.9.x` (from
|
||||
crates.io) produce types incompatible with our local `0.8.x` fork,
|
||||
causing `error[E0308]: mismatched types` at link/compile time.
|
||||
|
||||
**Detection**: The build system MUST detect these conditions automatically.
|
||||
On every build, the cookbook MUST:
|
||||
1. Compare the version in `local/sources/<component>/Cargo.toml` against the
|
||||
highest version required by any transitive dependency in the build graph.
|
||||
2. Compare the version in `local/sources/<component>/Cargo.toml` against the
|
||||
upstream `recipes/<component>/source/Cargo.toml` (if available).
|
||||
3. If the local version is lower than either:
|
||||
a. Record the required version in a manifest (`local/sources/<component>/.required-version`)
|
||||
b. Emit a clear actionable error: `LOCAL FORK OUTDATED: local/sources/<component> is at vX.Y.Z but vA.B.C is required by <dep>`
|
||||
c. Do NOT silently proceed with a broken build.
|
||||
|
||||
**Remediation (automatic, when invoked)**: The `local/scripts/bump-fork.sh`
|
||||
script (or equivalent `repo bump-fork <component>` command) MUST:
|
||||
1. Fetch the upstream source at the required version.
|
||||
2. Apply all Red Bear patches from `local/patches/<component>/` using the
|
||||
atomic patch application mechanism (see "Atomic Patch Application" below).
|
||||
3. Update the version field in the local fork's `Cargo.toml`.
|
||||
4. Commit the result to the `submodule/<component>` branch in the RedBear-OS repo.
|
||||
5. Rebuild the affected packages.
|
||||
|
||||
**Symlink aliasing**: `recipes/<component>/` MUST be a symlink to
|
||||
`../../../local/sources/<component>/` when a local fork exists. This ensures
|
||||
backward compatibility for recipes that reference the `recipes/` path while
|
||||
still preferring the local fork as the source of truth.
|
||||
|
||||
**Implementation status**: Detection is partially implemented via Cargo's
|
||||
own lockfile collision errors. Full automatic detection and remediation
|
||||
requires changes to `src/cook/fetch.rs` (version comparison logic) and the
|
||||
addition of `local/scripts/bump-fork.sh`. Until fully automated, the manual
|
||||
process is:
|
||||
1. `cd local/sources/<component>`
|
||||
2. Edit `Cargo.toml` version field to match upstream
|
||||
3. `git pull` or fetch upstream at the new tag
|
||||
4. Reapply all `local/patches/<component>/*.patch` (see AGENTS.md "Atomic Patch Application")
|
||||
5. Commit to `submodule/<component>` branch
|
||||
|
||||
### Offline-First By Default
|
||||
|
||||
Red Bear OS is a **fork with frozen sources**. The cookbook tool defaults to
|
||||
`COOKBOOK_OFFLINE=true` (changed from upstream Redox's `false`). Builds use archived
|
||||
sources from `sources/redbear-0.1.0/` — no network access during compilation.
|
||||
|
||||
To allow online fetching for non-protected development recipes:
|
||||
```bash
|
||||
COOKBOOK_OFFLINE=false make all CONFIG_NAME=redbear-full
|
||||
```
|
||||
|
||||
Or use the `--upstream` flag of `build-redbear.sh`:
|
||||
To allow online fetching for non-protected development recipes, use the `--upstream` flag:
|
||||
```bash
|
||||
./local/scripts/build-redbear.sh --upstream redbear-mini
|
||||
```
|
||||
@@ -514,7 +566,7 @@ After ANY change to patches or `recipe.toml`:
|
||||
3. Fetch: `repo --allow-protected fetch <recipe>`
|
||||
4. Build: `repo cook <recipe>`
|
||||
5. Verify no `FAILED`, `[ATOMIC] patch application rolled back`, or `.rej` files
|
||||
6. Full image: `make all CONFIG_NAME=<target>`
|
||||
6. Full image: `./local/scripts/build-redbear.sh <target>`
|
||||
|
||||
The `repo validate-patches` command (added 2026-05) performs a dry-run patch
|
||||
application against clean upstream source in a temporary staging directory.
|
||||
@@ -694,7 +746,7 @@ All custom work goes in `local/` — see `local/AGENTS.md` for fork model usage.
|
||||
- Build requires Linux x86_64 host, 8GB+ RAM, 20GB+ disk
|
||||
- QEMU used for testing (make qemu). VirtualBox also supported
|
||||
- The `repo` binary (cookbook CLI) may crash with TUI in non-interactive environments — use `CI=1`
|
||||
- No git submodules — external repos managed via recipe source URLs and repo manifests
|
||||
- 9 git submodules (core component forks on `submodule/<component>` branches). NO new branches and NO new submodules — see `local/AGENTS.md` § BRANCH AND SUBMODULE POLICY
|
||||
- Historical integration report removed (2026-04-16); see `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` for current state
|
||||
|
||||
### Stale Prefix Detection
|
||||
@@ -707,10 +759,12 @@ base) gains new commits, the prefix must be rebuilt:
|
||||
touch relibc && make prefix # Rebuild relibc in the cross-toolchain
|
||||
```
|
||||
|
||||
`build-redbear.sh` includes a preflight check that warns when fork commits are newer than
|
||||
prefix artifacts. **A stale prefix is the #1 cause of "undefined reference" link errors**
|
||||
after fork changes (e.g., adding `__freadahead` to relibc's `ext.rs` without rebuilding the
|
||||
prefix).
|
||||
`build-redbear.sh` **automatically rebuilds the prefix** when it detects that fork repos
|
||||
(relibc, kernel, base) have commits newer than the prefix `libc.a`. This happens before any
|
||||
recipe build begins. If the prefix rebuild fails, the build aborts immediately — recipes are
|
||||
never compiled against a stale prefix. **A stale prefix is the #1 cause of "undefined reference"
|
||||
link errors** after fork changes (e.g., adding `__freadahead` to relibc's `ext.rs` without
|
||||
rebuilding the prefix).
|
||||
|
||||
### relibc Header Circular Includes (Fixed)
|
||||
|
||||
|
||||
+1
-1
@@ -711,7 +711,7 @@ versioning"):**
|
||||
|
||||
### Intel GPU Driver Expansion
|
||||
- Gen8-Gen12 supported: Skylake, Kaby Lake, Coffee Lake, Cannon Lake, Ice Lake, Tiger Lake, Alder Lake, DG2, Meteor Lake, Arrow Lake, Lunar Lake, Battlemage
|
||||
- 200+ device IDs from Linux 7.0 i915 reference
|
||||
- 200+ device IDs from Linux 7.1 i915 reference
|
||||
- Gen4-Gen7 recognized with clear unsupported messages
|
||||
- Display fixes: pipe count, page flip, EDID skeleton
|
||||
|
||||
|
||||
Generated
+137
-11
@@ -590,6 +590,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.18+rb0.3.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"plain",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "line-clipping"
|
||||
version = "0.3.5"
|
||||
@@ -737,6 +747,12 @@ dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.1"
|
||||
@@ -856,7 +872,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redbear_cookbook"
|
||||
version = "0.1.0"
|
||||
version = "0.2.5"
|
||||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"anyhow",
|
||||
@@ -894,15 +910,25 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_installer"
|
||||
version = "0.2.42"
|
||||
version = "0.2.42+rb0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arg_parser",
|
||||
"libc",
|
||||
"libredox 0.1.18+rb0.3.0",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.9.0+rb0.3.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.2"
|
||||
@@ -910,7 +936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"libredox 0.1.10",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -953,6 +979,21 @@ version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.16",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
@@ -1080,6 +1121,12 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -1272,6 +1319,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -1355,7 +1408,16 @@ version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1373,13 +1435,29 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1388,42 +1466,90 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.14"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "redbear_cookbook"
|
||||
version = "0.1.0"
|
||||
version = "0.2.5"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||
edition = "2024"
|
||||
default-run = "repo"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<h1 align="center">Red Bear OS</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>A microkernel operating system written in Rust, derived from <a href="https://www.redox-os.org">Redox OS</a></strong>
|
||||
<strong>A microkernel operating system written in Rust — derived from <a href="https://www.redox-os.org">Redox OS</a>, built for bare metal.</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -18,40 +18,64 @@
|
||||
|
||||
## What is Red Bear OS?
|
||||
|
||||
Red Bear OS is a general-purpose, Unix-like operating system with a **microkernel architecture**, written in **Rust**. It is a full fork of Redox OS, frozen at release 0.1.0, with added hardware support, filesystem drivers, and a KDE Plasma desktop path.
|
||||
Red Bear OS is a general-purpose, Unix-like operating system with a **microkernel architecture**,
|
||||
written entirely in **Rust**. It is a full fork of Redox OS (baseline 0.1.0), actively developed
|
||||
on branch `0.2.5` with hardware enablement, multiple filesystems, a native greeter and login
|
||||
system, and a KDE Plasma desktop path.
|
||||
|
||||
We aim to stay close to upstream Redox — diverging only where necessary to add missing
|
||||
functionality, fix bugs, or support new hardware. The build system itself is under constant
|
||||
active development alongside the OS.
|
||||
|
||||
It ships with several **first-in-class Rust-native tools** found nowhere else in the OS world:
|
||||
|
||||
- **cub** — an AUR-inspired package manager with pacman-style CLI (`-S`/`-Q`/`-R`) and a ratatui
|
||||
TUI that converts Arch Linux PKGBUILDs into Red Bear recipes on the fly
|
||||
- **tlc** (Twilight Commander) — a pure-Rust reimplementation of Midnight Commander; dual-panel
|
||||
file manager, built-in editor and viewer, 8 color themes, 1204 unit tests, zero unsafe code
|
||||
- **redbear-power** — interactive ratatui TUI for live CPU frequency, governor, and thermal
|
||||
monitoring with on-the-fly P-state control
|
||||
|
||||
These are joined by dozens of `redbear-*` system utilities — `redbear-netctl` (network control),
|
||||
`redbear-info` (hardware diagnostics), `redbear-acmd` (admin CLI), `redbear-mtr`,
|
||||
`redbear-nmap`, `redbear-btctl`, and many more — all written in Rust, all built from source
|
||||
alongside the OS.
|
||||
|
||||
**Goals**:
|
||||
- **AMD & Intel parity** — first-class support for both platforms on bare metal
|
||||
- **AMD & Intel parity** — equal-priority bare-metal support for both platforms
|
||||
- **KDE Plasma desktop** — Wayland-based desktop environment via the KWin compositor
|
||||
- **Hardware GPU acceleration** — AMD GPU (amdgpu) and Intel GPU drivers via `redox-drm`
|
||||
- **Modern subsystems** — USB, Wi‑Fi, Bluetooth, ext4, GRUB, D-Bus
|
||||
- **Offline-first builds** — reproducible from archived, BLAKE3-verified sources
|
||||
- **Hardware GPU acceleration** — AMD (amdgpu) and Intel GPU drivers via `redox-drm`
|
||||
- **cub package ecosystem** — AUR → recipe.toml pipeline giving access to thousands of packages
|
||||
- **First-class subsystems** — USB, Wi‑Fi, Bluetooth, ext4, FAT, GRUB, D-Bus (none optional)
|
||||
- **Power management** — CPU frequency scaling, thermal monitoring, RAPL, sleep states
|
||||
- **Offline-first, reproducible builds** — BLAKE3-verified source archives with content-hash caching
|
||||
|
||||
---
|
||||
|
||||
## Our Git Server
|
||||
|
||||
Red Bear OS lives on a self-hosted Gitea instance at **https://gitea.redbearos.org**.
|
||||
This is the canonical home for the fork — there is no GitHub / GitLab / Codeberg
|
||||
mirror that is authoritative.
|
||||
This is the canonical home — no GitHub, GitLab, or Codeberg mirror is authoritative.
|
||||
There is exactly **one repository**: all component sources (kernel, relibc, drivers,
|
||||
system utilities) live here as submodule branches or tracked trees in `local/sources/`.
|
||||
|
||||
| Field | Value |
|
||||
|----------|------------------------------------------------------|
|
||||
| Host | `https://gitea.redbearos.org` |
|
||||
| User | `vasilito` |
|
||||
| Token | *(session-only — never stored in repo)* |
|
||||
| Web UI | `https://gitea.redbearos.org/vasilito` |
|
||||
| Main repo| `https://gitea.redbearos.org/vasilito/RedBear-OS` |
|
||||
|
||||
> **Token policy.** The `vasilito` token is a per-session credential and **must
|
||||
> never** be committed to any tracked file. Use `git credential.helper` (store /
|
||||
> cache / libsecret), `~/.netrc`, or `$REDBEAR_GITEA_TOKEN` env var. See
|
||||
> [`local/AGENTS.md` § Our Git Server](./local/AGENTS.md) for the full operator
|
||||
> runbook, mirror list, API reference, and recovery procedure.
|
||||
> Authentication tokens are per-session credentials — never stored in the repo.
|
||||
> See [`local/AGENTS.md` § Our Git Server](./local/AGENTS.md) for the full operator runbook.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Linux x86_64 host with Rust nightly, QEMU, nasm, and standard build tools.
|
||||
Linux x86_64 host with Rust nightly, QEMU, nasm, and standard build tools.
|
||||
See the [Redox Build Guide](https://doc.redox-os.org/book/podman-build.html) for full setup.
|
||||
|
||||
### Build & Run
|
||||
@@ -61,95 +85,162 @@ See the [Redox Build Guide](https://doc.redox-os.org/book/podman-build.html) for
|
||||
git clone https://gitea.redbearos.org/vasilito/RedBear-OS.git
|
||||
cd RedBear-OS
|
||||
|
||||
# Authenticated clone (one-off) — supply token via env var, not literal here
|
||||
# Authenticated clone — supply token via env var
|
||||
git clone https://vasilito:${REDBEAR_GITEA_TOKEN}@gitea.redbearos.org/vasilito/RedBear-OS.git
|
||||
|
||||
# Recommended: use the Red Bear wrapper
|
||||
# Canonical build entry point
|
||||
./local/scripts/build-redbear.sh redbear-mini # Text-only target
|
||||
./local/scripts/build-redbear.sh redbear-full # Desktop-capable target
|
||||
|
||||
# Boot in QEMU with the resulting image
|
||||
# Boot in QEMU
|
||||
make qemu
|
||||
```
|
||||
|
||||
> **Build script:** `local/scripts/build-redbear.sh` is the canonical entry point. Bare
|
||||
> `make all` works but bypasses the `.config` checking and `REDBEAR_ALLOW_PROTECTED_FETCH=1`
|
||||
> gates that `build-redbear.sh` enforces. See `AGENTS.md` § Build Commands for full details.
|
||||
|
||||
### Public Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `local/scripts/build-redbear.sh` | **Canonical** build wrapper for redbear-mini/full/grub |
|
||||
| `scripts/run.sh` | Build and run in QEMU (`-b` to build, `-c <config>` for target) |
|
||||
| `scripts/build-iso.sh` | Build a live ISO for bare-metal boot |
|
||||
| `scripts/build-all-isos.sh` | Build all live ISO targets |
|
||||
| `scripts/network-boot.sh` | PXE network boot helper |
|
||||
| `scripts/dual-boot.sh` | Dual-boot installation helper |
|
||||
> `local/scripts/build-redbear.sh` is the **only supported build entry point**. It handles
|
||||
> `.config` parsing, prefix staleness detection, protected-recipe authorization, pre-cooking
|
||||
> critical packages, and source fingerprint tracking. Direct `make` invocations bypass these
|
||||
> gates. See [`AGENTS.md` § Build Commands](./AGENTS.md) for details.
|
||||
|
||||
### Config Targets
|
||||
|
||||
| Target | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `redbear-full` | Desktop | Wayland + KDE + GPU drivers + D-Bus services |
|
||||
| `redbear-mini` | Console | Text-only recovery / install target |
|
||||
| `redbear-full` | Desktop-capable | GPU drivers + Wayland compositor + Qt 6.11.1 + KF6 6.27.0 + KWin + SDDM + greeter + D-Bus |
|
||||
| `redbear-mini` | Console | Text-only recovery / install target with tlc, cub, and redbear-* utilities |
|
||||
| `redbear-grub` | Console | Text-only with GRUB boot manager |
|
||||
|
||||
---
|
||||
|
||||
## Current Status
|
||||
|
||||
Red Bear OS **boots to a login prompt** in QEMU with working wired networking, D-Bus system bus, hardware detection daemons, and filesystem support (RedoxFS, ext4, FAT).
|
||||
Red Bear OS **boots to a login prompt** in QEMU with working wired networking, D-Bus system bus,
|
||||
hardware detection daemons, and three filesystem backends (RedoxFS, ext4, FAT). The ISO builds
|
||||
successfully on branch `0.2.5`. Graphics packages are frozen at latest upstream stable
|
||||
(Qt 6.11.1, KF6 6.27.0, Plasma 6.7.2, SDDM 0.21.0, Mesa 24.0.8).
|
||||
|
||||
| Area | Status |
|
||||
|------|--------|
|
||||
| Boot (ACPI/x2APIC/SMP) | ✅ Bare-metal proven |
|
||||
| Boot (ACPI, x2APIC, SMP) | ✅ Bare-metal proven — Ryzen Threadripper 128-thread verified |
|
||||
| Userspace drivers (PCI, storage, net) | ✅ Working in QEMU |
|
||||
| D-Bus system bus + services | ✅ Working (login1, PolicyKit, UDisks, UPower) |
|
||||
| ext4 / FAT filesystems | ✅ Compiles, installer-wired |
|
||||
| POSIX gaps (relibc) | 🚧 Bounded Wayland-facing support |
|
||||
| DRM/KMS display drivers | 🚧 AMD + Intel compile; HW validation pending |
|
||||
| Wayland compositor | 🚧 Bounded proof; Qt6/KF6 clients crash at init |
|
||||
| KDE Plasma desktop | 🔄 In progress (Qt6/KF6 compile; KWin/QML blocked) |
|
||||
| Wi‑Fi / Bluetooth | 📋 Planned (architected, implementation pending) |
|
||||
| Filesystems — RedoxFS, ext4, FAT | ✅ Scheme daemons + mkfs/fsck tools |
|
||||
| D-Bus system bus + services | ✅ Working — login1, PolicyKit, UDisks, UPower |
|
||||
| **cub** package manager | 🟡 17-module Rust workspace; AUR → recipe pipeline; 70+ tests |
|
||||
| **tlc** file manager | 🟡 113 .rs files, 46k+ lines; 986 tests; 8 skins; VFS archives |
|
||||
| IRQ / PCI / MSI-X / IOMMU | 🟡 QEMU-proven; hardware validation open |
|
||||
| POSIX gaps (relibc) | 🟡 ~85% coverage; ~38 active patches |
|
||||
| DRM/KMS display drivers | 🟡 AMD + Intel + virtio-gpu compile; HW validation open |
|
||||
| Mesa — llvmpipe + virgl | 🟡 Builds (`virtio_gpu_dri.so`, 17.4 MB); virgl EGL runtime probe open |
|
||||
| SDDM display manager + Greeter/Login | 🟡 Wired in `redbear-full`; graphical login blocked by Qt6 Wayland crash |
|
||||
| Qt 6.11.1 (Core, Gui, DBus, Wayland) | 🟡 Builds successfully; Wayland `null+8` crash blocks runtime |
|
||||
| KF6 Frameworks — 40/40 | 🟡 All frameworks build; KWin cooks successfully |
|
||||
| Wayland compositor | 🟡 Bounded proof; blocked by Qt6 Wayland protocol crash |
|
||||
| KDE Plasma / KWin | 🔴 Blocked by Qt6 Wayland crash in `wl_proxy_add_listener` |
|
||||
| Wi‑Fi (Intel iwlwifi) | 🟡 VFIO/passthrough bounded runtime validation framework exists |
|
||||
| USB / Bluetooth | 🔴 USB maturity work in progress; Bluetooth controller path planned |
|
||||
|
||||
**Where help is most wanted:**
|
||||
Qt6 Wayland protocol crash — the #1 blocker for graphical desktop · AMD/Intel GPU hardware validation on bare metal · USB controller maturity · Wi‑Fi native control plane · cub AUR pipeline hardening · package maintainers for the growing recipe catalog · tlc VFS remote backends and archive support
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
Red Bear OS uses a **userspace driver model** — all drivers run as unprivileged daemons:
|
||||
Red Bear OS uses a **userspace driver model** — all drivers run as unprivileged daemons
|
||||
communicating through the kernel's scheme-based IPC.
|
||||
|
||||
```
|
||||
Kernel (microkernel)
|
||||
└── schemes: memory, irq, event, pipe, debug
|
||||
└── Driver daemons (userspace)
|
||||
├── pcid → PCI enumeration
|
||||
├── e1000d → Intel ethernet
|
||||
├── xhcid → USB controller
|
||||
└── vesad → Display framebuffer
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ KERNEL (microkernel) │
|
||||
│ schemes: memory · irq · event · pipe · debug │
|
||||
└──────────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐
|
||||
│ pcid │ │ e1000d xhcid │ │ vesad redox-drm │
|
||||
│ PCI enum │ │ Intel USB 3.0 │ │ fbdev GPU manager │
|
||||
└──────────┘ └──────────────────┘ └──────────────────────┘
|
||||
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐
|
||||
│ ext4d │ │ ps2d evdevd │ │ thermald cpufreqd │
|
||||
│ fatd │ │ KB+mouse input │ │ thermal CPU freq │
|
||||
└──────────┘ └──────────────────┘ └──────────────────────┘
|
||||
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐
|
||||
│ iommu │ │ acpid │ │ dbus-daemon │
|
||||
│ DMA map │ │ power mgmt │ │ system + session │
|
||||
└──────────┘ └──────────────────┘ └──────────────────────┘
|
||||
```
|
||||
|
||||
The kernel provides minimal services (memory, interrupts, IPC). Everything else — filesystems, networking, graphics, input — runs in userspace.
|
||||
The kernel provides minimal services: memory, interrupts, and IPC. Everything else —
|
||||
filesystems, networking, graphics, input, power management, D-Bus — runs in userspace.
|
||||
Hardware quirks are handled by a data-driven system in `redox-driver-sys` with compiled-in
|
||||
tables, TOML runtime configuration, and DMI matching.
|
||||
|
||||
---
|
||||
|
||||
## Engineering Standards
|
||||
|
||||
Red Bear OS operates under strict discipline. Full policies: [`local/AGENTS.md`](./local/AGENTS.md).
|
||||
|
||||
| Rule | |
|
||||
|------|---|
|
||||
| **Never delete to "fix" a build** | If a package breaks, fix the root cause. Never remove, ignore, or comment out a package, service, or config to make a build pass. |
|
||||
| **Zero stubs** | No fake headers, `#ifdef` no-ops, or "make it compile" shortcuts. Missing functionality must be implemented properly in the right component. |
|
||||
| **Single repository** | All component sources live here — no per-component repos. 9 `submodule/<component>` branches. |
|
||||
| **Local fork model** | Core components (kernel, relibc, base, etc.) maintained as local forks in `local/sources/` with immutability guarantees. |
|
||||
| **Adapt to upstream** | Red Bear adapts to upstream API/ABI changes — never pins, downgrades, or holds back a dependency. |
|
||||
| **Free/libre only** | No proprietary, source-unavailable, or redistributability-restricted dependencies. MIT licensed. |
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Implementation Plan](docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md) — roadmap and execution model
|
||||
- [Desktop Path Plan](local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md) — kernel → DRM → Mesa → Wayland → KDE
|
||||
- [D-Bus Integration](local/docs/DBUS-INTEGRATION-PLAN.md) — session bus architecture
|
||||
- [USB Plan](local/docs/USB-IMPLEMENTATION-PLAN.md) — USB stack design
|
||||
- [Wi‑Fi Plan](local/docs/WIFI-IMPLEMENTATION-PLAN.md) — wireless architecture
|
||||
- [Bluetooth Plan](local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md) — BT stack design
|
||||
- [Documentation Index](docs/README.md) — full doc map
|
||||
- [Desktop Path Plan](local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md) — Canonical plan v6.0: kernel → DRM → Mesa → Wayland → KDE
|
||||
- [Implementation Plan](docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md) — Roadmap and execution model
|
||||
- [cub Package Manager](local/docs/CUB-PACKAGE-MANAGER.md) — AUR → recipe pipeline, CLI reference, architecture
|
||||
- [tlc File Manager](local/recipes/tui/tlc/README.md) — Pure-Rust Midnight Commander replacement
|
||||
- [D-Bus Integration](local/docs/DBUS-INTEGRATION-PLAN.md) — Session bus architecture
|
||||
- [IRQ & Low-Level Controllers](local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md) — IRQ delivery, MSI/MSI-X, IOMMU
|
||||
- [Greeter & Login](local/docs/GREETER-LOGIN-IMPLEMENTATION-PLAN.md) — Native greeter, auth daemon, session launch
|
||||
- [DRM Modernization](local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md) — DRM/KMS display and render maturity
|
||||
- [USB Plan](local/docs/USB-IMPLEMENTATION-PLAN.md) — USB stack design and implementation
|
||||
- [Wi‑Fi Plan](local/docs/WIFI-IMPLEMENTATION-PLAN.md) — Wireless architecture and driver plan
|
||||
- [Bluetooth Plan](local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md) — Bluetooth stack design
|
||||
- [Build Cache](local/docs/BUILD-CACHE-PLAN.md) — Content-hash (BLAKE3) build cache system
|
||||
- [Build System Hardening](local/docs/BUILD-SYSTEM-HARDENING-PLAN.md) — Collision detection, init service validation
|
||||
- [Quirks System](local/docs/QUIRKS-SYSTEM.md) — Hardware quirks infrastructure
|
||||
- [Documentation Index](docs/README.md) — Full doc map
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Red Bear OS uses a **full fork** model. Upstream Redox sources are frozen and archived. All custom work lives in `local/`:
|
||||
Red Bear OS is a **full fork** of Redox OS. Upstream sources are frozen and archived; all
|
||||
custom work lives in `local/` and survives every build operation.
|
||||
|
||||
```
|
||||
local/
|
||||
├── sources/ # Local forks of core components (kernel, relibc, base, bootloader, …)
|
||||
├── recipes/ # Custom packages — drivers, GPU stack, system daemons, branding
|
||||
├── patches/ # Durable changes to upstream source trees
|
||||
├── recipes/ # Custom packages (drivers, GPU, system)
|
||||
├── docs/ # Integration and planning docs
|
||||
└── scripts/ # Build, test, and release tooling
|
||||
├── docs/ # Integration and planning documentation
|
||||
└── scripts/ # Build, test, validation, and release tooling
|
||||
```
|
||||
|
||||
We welcome contributions made with or without AI assistance — we care about **quality**, not how the code was produced.
|
||||
### We're Looking For
|
||||
|
||||
| Role | What you'd work on |
|
||||
|------|--------------------|
|
||||
| **Package maintainers** | Port and maintain AUR packages through cub's pipeline; write and test recipe.toml files for Red Bear OS; improve the PKGBUILD → recipe conversion |
|
||||
| **Driver developers** | AMD/Intel GPU drivers, USB controller maturity, Wi‑Fi native control plane, Bluetooth |
|
||||
| **Graphics stack engineers** | Qt6 Wayland crash fix (the #1 desktop blocker), Mesa virgl runtime, KWin Wayland compositor |
|
||||
| **Systems/Rust engineers** | Kernel syscalls, relibc POSIX gaps, filesystem daemons, D-Bus services, hardware quirks |
|
||||
| **TUI/app developers** | tlc feature completion, cub TUI polish, redbear-power enhancements, new redbear-* utilities |
|
||||
|
||||
Contributions are welcome with or without AI assistance — we care about **quality**, not how
|
||||
the code was produced. Pick an area from the status table above, check the relevant plan doc,
|
||||
and dive in.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+8
-2
@@ -34,7 +34,10 @@ path = "/etc/init.d/30_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Console terminals"
|
||||
requires_weak = ["29_activate_console.service"]
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
"00_ptyd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
@@ -47,7 +50,10 @@ path = "/etc/init.d/31_debug_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Debug console"
|
||||
requires_weak = ["29_activate_console.service"]
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
"00_ptyd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
|
||||
@@ -519,7 +519,7 @@ requires_weak = [
|
||||
|
||||
[service]
|
||||
cmd = "pcid-spawner"
|
||||
type = "oneshot"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
# Firmware fallback chain configs
|
||||
|
||||
+83
-12
@@ -56,8 +56,10 @@ thermald = {}
|
||||
redbear-power = {}
|
||||
hwrngd = {}
|
||||
redbear-acmd = {}
|
||||
redbear-ftdi = {}
|
||||
redbear-ecmd = {}
|
||||
redbear-usbaudiod = {}
|
||||
redbear-usb-hotplugd = {}
|
||||
driver-params = {}
|
||||
|
||||
# ── PCI device database (critical for PCI driver matching) ──
|
||||
@@ -90,8 +92,8 @@ iommu = {}
|
||||
# ── Standard CLI tools (from server profile) ──
|
||||
bash = {}
|
||||
bottom = {}
|
||||
#curl = {} # suppressed: nghttp2 dependency chain fails; curl not needed for boot/recovery
|
||||
diffutils = "ignore" # suppressed: relibc/gnulib header gaps; not needed for boot/recovery or greeter proof
|
||||
curl = {}
|
||||
diffutils = {}
|
||||
findutils = {}
|
||||
uutils-tar = {}
|
||||
bison = {}
|
||||
@@ -171,6 +173,21 @@ cmd = "audiod"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/02_usb_hotplug.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "USB device hotplug daemon (auto-spawns class drivers)"
|
||||
requires_weak = [
|
||||
"00_base.target",
|
||||
"00_pcid-spawner.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "redbear-usb-hotplugd"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/02_serial_probe.service"
|
||||
data = """
|
||||
@@ -190,7 +207,7 @@ type = "oneshot"
|
||||
path = "/etc/init.d/00_gpiod.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "GPIO controller registry (non-blocking on live-mini)"
|
||||
description = "GPIO controller registry"
|
||||
requires_weak = [
|
||||
"00_base.target",
|
||||
]
|
||||
@@ -204,7 +221,7 @@ type = { scheme = "gpio" }
|
||||
path = "/etc/init.d/00_i2cd.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "I2C adapter registry (non-blocking on live-mini)"
|
||||
description = "I2C adapter registry"
|
||||
requires_weak = [
|
||||
"00_base.target",
|
||||
]
|
||||
@@ -218,7 +235,7 @@ type = { scheme = "i2c" }
|
||||
path = "/etc/init.d/00_i2c-dw-acpi.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "DesignWare ACPI I2C controller (non-blocking)"
|
||||
description = "DesignWare ACPI I2C controller"
|
||||
requires_weak = [
|
||||
"00_i2cd.service",
|
||||
]
|
||||
@@ -232,7 +249,7 @@ type = "oneshot_async"
|
||||
path = "/etc/init.d/00_intel-gpiod.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Intel ACPI GPIO registrar (non-blocking)"
|
||||
description = "Intel ACPI GPIO registrar"
|
||||
requires_weak = [
|
||||
"00_gpiod.service",
|
||||
"00_i2cd.service",
|
||||
@@ -247,7 +264,7 @@ type = "oneshot_async"
|
||||
path = "/etc/init.d/00_i2c-gpio-expanderd.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "I2C GPIO expander companion bridge (non-blocking on live-mini)"
|
||||
description = "I2C GPIO expander companion bridge"
|
||||
requires_weak = [
|
||||
"00_i2cd.service",
|
||||
"00_gpiod.service",
|
||||
@@ -262,7 +279,7 @@ type = "oneshot_async"
|
||||
path = "/etc/init.d/00_i2c-hidd.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "ACPI I2C HID bring-up daemon (non-blocking)"
|
||||
description = "ACPI I2C HID bring-up daemon"
|
||||
requires_weak = [
|
||||
"00_i2cd.service",
|
||||
"00_i2c-dw-acpi.service",
|
||||
@@ -279,7 +296,7 @@ type = "oneshot_async"
|
||||
path = "/etc/init.d/00_ucsid.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "USB-C UCSI topology detector (non-blocking on live-mini)"
|
||||
description = "USB-C UCSI topology detector"
|
||||
requires_weak = [
|
||||
"00_base.target",
|
||||
"00_i2cd.service",
|
||||
@@ -492,7 +509,7 @@ requires_weak = ["00_base.target"]
|
||||
[service]
|
||||
cmd = "inputd"
|
||||
args = ["-A", "2"]
|
||||
type = "oneshot"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
@@ -512,7 +529,10 @@ path = "/etc/init.d/30_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Console terminals"
|
||||
requires_weak = ["29_activate_console.service"]
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
"00_ptyd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
@@ -525,10 +545,61 @@ path = "/etc/init.d/31_debug_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Debug console"
|
||||
requires_weak = ["29_activate_console.service"]
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
"00_ptyd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
args = ["/scheme/debug/no-preserve", "-J"]
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/00_pcid-spawner.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "PCI driver spawner (non-blocking on live-mini)"
|
||||
|
||||
[service]
|
||||
cmd = "pcid-spawner"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/00_ipcd.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Inter-process communication daemon (non-blocking on live-mini)"
|
||||
|
||||
[service]
|
||||
cmd = "ipcd"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/13_ftdi-probe.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "FTDI USB serial probe (non-blocking on redbear-mini)"
|
||||
requires_weak = [
|
||||
"00_base.target",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "echo"
|
||||
args = ["RB_FTDI_PROBE_OK"]
|
||||
type = "oneshot"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/00_ptyd.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Pseudo-terminal daemon (non-blocking on live-mini)"
|
||||
|
||||
[service]
|
||||
cmd = "ptyd"
|
||||
type = "notify"
|
||||
"""
|
||||
|
||||
@@ -116,6 +116,7 @@ data = """
|
||||
description = "Console terminals"
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
"00_ptyd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
@@ -147,6 +148,7 @@ data = """
|
||||
description = "Debug console"
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
"00_ptyd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
|
||||
@@ -121,15 +121,12 @@ desktop-capable target.
|
||||
|
||||
These produce images such as `build/x86_64/harddrive.img` or `build/x86_64/redbear-mini.iso`.
|
||||
|
||||
### Bare `make all` (Legacy / Advanced)
|
||||
### Bare `make all` (Not Supported)
|
||||
|
||||
```bash
|
||||
make all CONFIG_NAME=redbear-mini
|
||||
```
|
||||
|
||||
Bare `make all` works but bypasses the policy gates (`.config` checking,
|
||||
`REDBEAR_ALLOW_PROTECTED_FETCH=1`, etc.) that `build-redbear.sh` enforces. Prefer the wrapper
|
||||
unless you specifically need to bypass those gates.
|
||||
Direct `make` invocations bypass the policy gates (`.config` checking,
|
||||
`REDBEAR_ALLOW_PROTECTED_FETCH=1`, prefix staleness detection, local-over-WIP
|
||||
enforcement, pre-cooking, source fingerprint tracking) that `build-redbear.sh`
|
||||
enforces. **Always use `build-redbear.sh`.**
|
||||
|
||||
### Export External Toolchain
|
||||
|
||||
@@ -152,25 +149,20 @@ make export-toolchain TARGET=x86_64-unknown-redox \
|
||||
### Build with Specific Config
|
||||
|
||||
```bash
|
||||
# Preferred Red Bear wrapper:
|
||||
# Canonical Red Bear wrapper (produces live ISO):
|
||||
./local/scripts/build-redbear.sh redbear-mini
|
||||
./local/scripts/build-redbear.sh redbear-full
|
||||
./local/scripts/build-redbear.sh redbear-grub
|
||||
|
||||
# Direct make is still valid when needed:
|
||||
make all CONFIG_NAME=redbear-full
|
||||
```
|
||||
|
||||
For tracked Red Bear work, prefer these three compile targets over older historical names.
|
||||
|
||||
### Build a Live ISO
|
||||
### Live ISO
|
||||
|
||||
```bash
|
||||
make live CONFIG_NAME=redbear-full
|
||||
# Produces: build/x86_64/redbear-live.iso
|
||||
```
|
||||
`build-redbear.sh` already produces a live ISO (it calls `make live` internally).
|
||||
Output: `build/<arch>/<config>.iso`
|
||||
|
||||
Live `.iso` outputs are for real bare-metal boot, install, recovery, and demo workflows. They are not the VM/QEMU execution surface; for virtualization, use `make qemu` and the `harddrive.img` path instead.
|
||||
Live `.iso` outputs are for real bare-metal boot, install, recovery, and demo workflows.
|
||||
|
||||
### Rebuild After Changes
|
||||
|
||||
|
||||
+3
-3
@@ -100,7 +100,7 @@ This summary is only a quick orientation layer. For canonical current-state deta
|
||||
- and the active subsystem plans under `local/docs/` for detailed current workstreams.
|
||||
|
||||
- **Compile targets**: the supported compile targets are `redbear-mini`, `redbear-full`, and `redbear-grub`
|
||||
- **Live ISO policy**: live `.iso` outputs (`make live`) are for real bare-metal boot/install/recovery workflows, not the VM/QEMU execution surface.
|
||||
- **Live ISO policy**: live `.iso` outputs (`build-redbear.sh`) are for real bare-metal boot/install/recovery workflows, not the VM/QEMU execution surface.
|
||||
- **Wayland**: libwayland + wayland-protocols built. A bounded greeter/compositor-backed login proof now passes, but broader compositor/runtime stability remains incomplete.
|
||||
- **Qt6**: qtbase 6.11.0 (Core+Gui+Widgets+DBus+Wayland), qtdeclarative, qtsvg, qtwayland ALL BUILT
|
||||
- **D-Bus**: 1.16.2 built for Redox. Qt6DBus enabled.
|
||||
@@ -134,8 +134,8 @@ cargo install just cbindgen
|
||||
# 3. Configure for native build (no Podman)
|
||||
echo 'PODMAN_BUILD?=0' > .config
|
||||
|
||||
# 4. Build (downloads cross-toolchain, then compiles)
|
||||
make all
|
||||
# 4. Build (canonical command — produces live ISO)
|
||||
./local/scripts/build-redbear.sh redbear-mini
|
||||
|
||||
# 5. Run in QEMU
|
||||
make qemu
|
||||
|
||||
+449
-59
@@ -81,7 +81,16 @@ inside `RedBear-OS` either as:
|
||||
OR
|
||||
- **Tracked trees under `local/sources/<component>/`** — full source
|
||||
snapshots committed directly to the `RedBear-OS` repo as ordinary
|
||||
files, versioned by Red Bear commits (not by external git history).
|
||||
files, versioned by Red Bear commits (not by external git history),
|
||||
OR
|
||||
- **Local recipes under `local/recipes/<category>/<name>/source/`** —
|
||||
original Red Bear projects (tlc, redbear-*, cub, etc.) with no upstream
|
||||
outside this repo.
|
||||
|
||||
**WE DO NOT CREATE NEW REPOSITORIES.** If a component needs a Red Bear
|
||||
fork and does not already have a `submodule/<component>` branch, adding
|
||||
one is an **operator-only** decision. Agents **MUST NOT** create new
|
||||
branches or new repos on their own.
|
||||
|
||||
Operators and agents **MUST NOT**:
|
||||
|
||||
@@ -95,12 +104,14 @@ If a recipe's `[source]` section currently points at a separate repo
|
||||
URL (e.g. `https://gitea.redbearos.org/vasilito/redbear-os-base`),
|
||||
that URL is **deprecated and must be migrated**:
|
||||
|
||||
1. Create or reuse a branch inside `RedBear-OS` named
|
||||
`submodule/<component>` (e.g. `submodule/relibc`).
|
||||
2. Push the component's source tree to that branch.
|
||||
1. Push the component's source tree to the existing `submodule/<component>`
|
||||
branch inside `RedBear-OS`.
|
||||
2. Update the submodule's local `.git/config` origin to
|
||||
`https://gitea.redbearos.org/vasilito/RedBear-OS.git`.
|
||||
3. Replace the recipe's `git = "..."` URL with the in-repo submodule
|
||||
path, and add a `[submodule]` entry referencing the new branch.
|
||||
4. Update `local/AGENTS.md` and any other references.
|
||||
path, and ensure `.gitmodules` references `branch = submodule/<component>`.
|
||||
4. Delete the per-component repository on Gitea.
|
||||
5. Update `local/AGENTS.md` and any other references.
|
||||
|
||||
**Enforcement.** The cookbook's fetch/validation path treats any
|
||||
component source fetched from outside `RedBear-OS` as a misconfiguration.
|
||||
@@ -118,6 +129,145 @@ this repo.
|
||||
- aligned with the `local/` durability model — durable state stays
|
||||
inside the project tree, not scattered across many Gitea repos.
|
||||
|
||||
### BRANCH AND SUBMODULE POLICY (ABSOLUTE — DO NOT VIOLATE)
|
||||
|
||||
**WE DO NOT CREATE NEW BRANCHES.** All work happens on existing branches.
|
||||
Operators and agents **MUST NOT** create new git branches of any kind — no
|
||||
feature branches, no topic branches, no version-specific fork branches
|
||||
(e.g. `redoxfs-0.9.0-rb1`), no scratch branches. Ever.
|
||||
|
||||
The **only** branches that exist in the `RedBear-OS` repo are:
|
||||
|
||||
| Branch type | Examples | Who creates them |
|
||||
|---|---|---|
|
||||
| **Release branches** | `master`, `0.2.0`, `0.2.1`, …, `0.2.5`, … | **Operator only**, one per Red Bear OS release cycle. |
|
||||
| **Submodule branches** | `submodule/base`, `submodule/kernel`, `submodule/relibc`, … (9 total) | **Operator only**, one per forked upstream component. |
|
||||
| **Recovery branches** | `recovered/quirks` | **Operator only**, for emergency recovery work. |
|
||||
|
||||
Agents **MUST NOT**:
|
||||
- `git checkout -b <anything>` — no new branches, no exceptions.
|
||||
- `git push origin HEAD:refs/heads/<new-branch>` — no pushing new branches.
|
||||
- create a branch named after a version (e.g. `redoxfs-0.9.0-rb1`) — this is
|
||||
a **policy violation**. Version metadata lives in `Cargo.toml` version
|
||||
fields (as `+rbN` build metadata), not in branch names.
|
||||
- create a branch named `submodule/<new-component>` unless the operator has
|
||||
explicitly decided to fork a new upstream component.
|
||||
|
||||
**If you need to work on a change, commit to the branch you are already on.**
|
||||
Release work goes on the current release branch (e.g. `0.2.5`). Fork changes
|
||||
go on the appropriate `submodule/<component>` branch. There is no
|
||||
justification for a new branch — use a commit, a patch file, or a tracked
|
||||
tree instead.
|
||||
|
||||
#### Submodules are the unit of work
|
||||
|
||||
Each local forked upstream subproject IS a submodule. There are exactly
|
||||
**9 declared submodules** (as of 2026-07-01):
|
||||
|
||||
| Submodule | Branch | Path |
|
||||
|---|---|---|
|
||||
| base | `submodule/base` | `local/sources/base` |
|
||||
| bootloader | `submodule/bootloader` | `local/sources/bootloader` |
|
||||
| installer | `submodule/installer` | `local/sources/installer` |
|
||||
| kernel | `submodule/kernel` | `local/sources/kernel` |
|
||||
| libredox | `submodule/libredox` | `local/sources/libredox` |
|
||||
| redoxfs | `submodule/redoxfs` | `local/sources/redoxfs` |
|
||||
| relibc | `submodule/relibc` | `local/sources/relibc` |
|
||||
| syscall | `submodule/syscall` | `local/sources/syscall` |
|
||||
| userutils | `submodule/userutils` | `local/sources/userutils` |
|
||||
|
||||
**We work on existing submodules.** If a component needs Red Bear changes:
|
||||
|
||||
1. **Check if a submodule already exists** for that component (see table above
|
||||
and `.gitmodules`). If yes, work on that submodule's branch.
|
||||
2. **If no submodule exists**, the component lives as a **tracked tree**
|
||||
under `local/sources/<name>/` (e.g. `redox-scheme`) or as a **local
|
||||
recipe** under `local/recipes/<name>/source/`. Commit changes directly
|
||||
to the current release branch — no new submodule branch needed.
|
||||
3. **Creating a new submodule** is an **operator-only decision** and should
|
||||
be questioned: *"We can create a new submodule but why? We just work on
|
||||
existing submodules."* New submodules are only justified when a component
|
||||
is large enough and upstream-tracked enough that submodule pinning
|
||||
provides real value over a tracked tree.
|
||||
|
||||
#### Operator override — agents MAY create submodules when really necessary (2026-07-07)
|
||||
|
||||
> **Authorization:** Operator explicitly granted agents permission to create
|
||||
> new submodules on 2026-07-07 ("agents CAN create submodules, document this —
|
||||
> but only when really necessary"). Without an explicit necessity case, the
|
||||
> default preference remains "work on existing submodules first".
|
||||
>
|
||||
> **What "really necessary" means (default-closed exception).** A new
|
||||
> submodule is justified only when **all** of the following are true:
|
||||
>
|
||||
> 1. **Upstream-tracked.** The component has its own upstream commit history
|
||||
> that Red Bear needs to track, rebase against, or import from.
|
||||
> 2. **Large.** The component is big enough that a tracked tree would
|
||||
> meaningfully bloat the parent repo on every clone (rough heuristic:
|
||||
> >10MB of source and growing).
|
||||
> 3. **Pinning has real value.** Submodule pinning (specific commit) provides
|
||||
> a correct-dependency guarantee that a tracked tree or local recipe
|
||||
> cannot — e.g. the component must be at the same commit across all
|
||||
> consumers, or its build depends on the parent's commit graph.
|
||||
> 4. **No smaller option.** A local recipe (`local/recipes/<cat>/<name>/source/`)
|
||||
> or a tracked tree (`local/sources/<name>/`) cannot serve equivalently.
|
||||
>
|
||||
> If any of (1)–(4) fails, **do not** create a new submodule. Use a tracked
|
||||
> tree or local recipe instead.
|
||||
>
|
||||
> **Scope:** When justified, agents may now create a new
|
||||
> `submodule/<component>` branch on `RedBear-OS` and add an entry to
|
||||
> `.gitmodules`. The default preference remains "work on existing submodules
|
||||
> first" — adding a new one is the exception, not the baseline.
|
||||
>
|
||||
> **Pre-create checklist (must be in the agent's commit message).** Before
|
||||
> `git submodule add`:
|
||||
>
|
||||
> - **Operator instruction cited.** Quote the operator's words exactly that
|
||||
> justify this submodule.
|
||||
> - **Necessity test passed.** For each of (1)–(4) above, a one-line answer
|
||||
> with file/path evidence.
|
||||
> - **Alternatives rejected.** Concrete reason a tracked tree or local recipe
|
||||
> is not equivalent.
|
||||
> - **Inventory impact.** How the 9-declared-submodule table above changes
|
||||
> (becomes 10 declared submodules, new `<submodule>` row added).
|
||||
> - **Operator review notice.** A line saying "operators should review this
|
||||
> `.gitmodules` change before merge to a release branch".
|
||||
>
|
||||
> **What this does NOT change:**
|
||||
>
|
||||
> - The 9-declared-submodule table above is still the canonical inventory
|
||||
> until a real necessity case lands.
|
||||
> - Tracked trees under `local/sources/<name>/` and local recipes under
|
||||
> `local/recipes/<name>/source/` are still the preferred home for new
|
||||
> Red Bear code unless pinning matters.
|
||||
> - All other single-repo, branch, fork, and durability rules in this file
|
||||
> remain absolute.
|
||||
> - The override still records an operator decision; it is not a relaxation
|
||||
> of accountability.
|
||||
>
|
||||
> **Log requirement:** Every new submodule added by an agent under this
|
||||
> override must commit a short note next to the `.gitmodules` change giving
|
||||
> the operator instruction, the date, the necessity-test evidence, and the
|
||||
> alternatives-rejected reasoning. This keeps the override auditable.
|
||||
>
|
||||
> **Current status of the override:** **Not yet exercised.** No new submodule
|
||||
> has been created under this override as of 2026-07-07. All Red Bear USB and
|
||||
> PCI subsystems continue to live as local recipes under `local/recipes/`,
|
||||
> which satisfies the "work on existing patterns first" preference.
|
||||
|
||||
#### What to do with a stray branch
|
||||
|
||||
If a branch was created in violation of this policy (e.g.
|
||||
`redox-scheme-0.11.2-rb1`):
|
||||
|
||||
1. Cherry-pick or merge any useful commits back onto the correct branch
|
||||
(the release branch for tracked trees, or the `submodule/<component>`
|
||||
branch for declared submodules).
|
||||
2. Delete the stray branch: `git branch -D <stray-branch>`.
|
||||
3. Delete it on the remote if it was pushed: `git push origin --delete <stray-branch>`.
|
||||
4. Document the cleanup in the commit message.
|
||||
|
||||
### Migration status (as of 2026-07-01)
|
||||
|
||||
**Migration complete.** Every per-component Gitea repo that ever existed
|
||||
@@ -161,31 +311,29 @@ The migration was performed with the helper scripts in `local/scripts/`:
|
||||
| `local/scripts/redirect-to-submodules.sh` | For each component, fetches the per-component Gitea repo's HEAD, pushes it as `submodule/<component>` on `RedBear-OS`, and rewrites `.gitmodules` to point at the new branch. Idempotent. Empty source repos are skipped. |
|
||||
| `local/scripts/delete-per-component-repos.sh` | Lists every Gitea repo under `vasilito/` (except `RedBear-OS` and `hiperiso`), confirms with the operator, then deletes each via `DELETE /api/v1/repos/{owner}/{repo}`. |
|
||||
|
||||
**To re-run for a future component** (e.g. a new per-component repo that
|
||||
someone accidentally creates):
|
||||
**To re-run for a future component** (if a per-component repo was accidentally
|
||||
created or inherited):
|
||||
|
||||
1. Verify the per-component repo is a Red Bear component, not unrelated.
|
||||
1. Verify the component belongs in `RedBear-OS`.
|
||||
2. `export REDBEAR_GITEA_TOKEN=...` (or set up `~/.netrc`).
|
||||
3. Push the working-tree HEAD as a branch:
|
||||
3. Push the working-tree HEAD to the correct `submodule/<component>` branch:
|
||||
```bash
|
||||
cd local/sources/<component>
|
||||
git push https://${REDBEAR_GITEA_TOKEN}@gitea.redbearos.org/vasilito/RedBear-OS.git \
|
||||
HEAD:refs/heads/submodule/<component>
|
||||
git remote set-url origin https://gitea.redbearos.org/vasilito/RedBear-OS.git
|
||||
git push -u origin master:refs/heads/submodule/<component>
|
||||
```
|
||||
4. Declare it in `.gitmodules` (copy an existing entry, change the name + branch).
|
||||
5. `./local/scripts/delete-per-component-repos.sh` to remove the orphaned repo.
|
||||
4. Delete the per-component repository on Gitea.
|
||||
5. Verify with:
|
||||
```bash
|
||||
# Should print only: hiperiso, RedBear-OS
|
||||
curl -fsS -H "Authorization: token $REDBEAR_GITEA_TOKEN" \
|
||||
'https://gitea.redbearos.org/api/v1/users/vasilito/repos?limit=200' \
|
||||
| jq -r '.[].name' | sort
|
||||
|
||||
**To verify the rule holds at any time:**
|
||||
|
||||
```bash
|
||||
# Should print only: hiperiso, RedBear-OS
|
||||
curl -fsS 'https://gitea.redbearos.org/api/v1/users/vasilito/repos?limit=200' \
|
||||
| jq -r '.[].name' | sort
|
||||
|
||||
# Should print zero matches (CHANGELOG.md historical entries are exempt)
|
||||
grep -rn 'gitea.redbearos.org/vasilito/redbear-os-' . --include='*.md' \
|
||||
| grep -v 'CHANGELOG.md'
|
||||
```
|
||||
# Should print zero matches (CHANGELOG.md historical entries are exempt)
|
||||
grep -rn 'gitea.redbearos.org/vasilito/redbear-os-' . --include='*.md' \
|
||||
| grep -v 'CHANGELOG.md'
|
||||
```
|
||||
|
||||
### Connection details
|
||||
|
||||
@@ -345,12 +493,11 @@ history and force-push the rewritten history on a feature branch before merging.
|
||||
|
||||
Build flow:
|
||||
```
|
||||
make all CONFIG_NAME=redbear-full
|
||||
→ mk/config.mk resolves to the active desktop/graphics compile target
|
||||
→ Desktop/graphics are available only on redbear-full
|
||||
./local/scripts/build-redbear.sh <config>
|
||||
→ .config parsing, prefix staleness detection, local-over-WIP policy
|
||||
→ repo cook builds all packages from local sources (offline by default)
|
||||
→ mk/disk.mk creates harddrive.img with Red Bear branding
|
||||
→ REDBEAR_RELEASE=0.1.0 ensures immutable, archived sources
|
||||
→ mk/disk.mk creates <config>.iso (live ISO) with Red Bear branding
|
||||
→ Output: build/<arch>/<config>.iso
|
||||
```
|
||||
|
||||
Release flow:
|
||||
@@ -367,8 +514,8 @@ Release flow:
|
||||
|
||||
## ACTIVE COMPILE TARGETS
|
||||
|
||||
The supported compile targets are exactly three. All three work for both `make all` (harddrive.img)
|
||||
and `make live` (ISO):
|
||||
The supported compile targets are exactly three. `build-redbear.sh` produces
|
||||
a live ISO for each:
|
||||
|
||||
- `redbear-full` — Desktop/graphics-enabled target (Wayland + KDE + GPU drivers)
|
||||
- `redbear-mini` — Text-only console/recovery/install target
|
||||
@@ -436,6 +583,21 @@ If we can fetch fresh upstream sources tomorrow, provision sources from `sources
|
||||
If a change exists only inside an upstream-owned `recipes/*/source/` tree, then it is **not yet
|
||||
preserved**, even if the current build happens to pass.
|
||||
|
||||
### Local fork recipe directories must not carry patch files
|
||||
|
||||
When a recipe's `[source]` uses `path = ".../local/sources/<component>/"`, the local fork
|
||||
**is** the patch. The recipe directory (`recipes/core/<component>/`) **MUST NOT** contain
|
||||
`.patch` files or symlinks. Patch files from the pre-fork era must be applied as commits to
|
||||
the `submodule/<component>` branch (or, if kept for historical reference, archived under
|
||||
`local/docs/archived/` or `local/patches/archived/` and never symlinked into a recipe
|
||||
directory). Leaving patch files in a `path`-source recipe directory creates the false
|
||||
impression that the build system applies them, which leads to exactly the kind of duplicate
|
||||
fix work we just saw with `sem_open`.
|
||||
|
||||
The cookbook **does not apply patches for `path` sources**. If a component needs patches,
|
||||
it must use a `git` source pointing at a `submodule/<component>` branch, or the patches
|
||||
must be merged into the local fork branch.
|
||||
|
||||
### GOLDEN RULE — Red Bear adapts to upstream, never the reverse
|
||||
|
||||
**When upstream Redox changes a dependency version, API, or ABI, Red Bear adapts.**
|
||||
@@ -451,25 +613,261 @@ This applies to:
|
||||
|
||||
**The only acceptable response to an upstream version bump is: update, adapt, commit.**
|
||||
|
||||
### In-house crate versioning
|
||||
### Version conventions — two categories
|
||||
|
||||
All Red Bear original crates under `local/recipes/*/source/` MUST use the current Red Bear OS
|
||||
version, derived from the git branch name (e.g. `0.2.4` on branch `0.2.4`). This applies to
|
||||
all `version = "..."` fields in `[package]` and `[workspace.package]` sections.
|
||||
Red Bear OS has exactly two version categories:
|
||||
|
||||
**Exclusions** (these keep their own versioning):
|
||||
**Category 1 (Cat 1) — In-house Red Bear crates** (`local/recipes/*/source/`):
|
||||
|
||||
These are original Red Bear projects (tlc, cub, redbear-*, etc.) with no
|
||||
upstream. Their version **MUST** be the current Red Bear OS branch version
|
||||
(e.g. `0.2.5` on branch `0.2.5`).
|
||||
|
||||
**Category 2 (Cat 2) — Upstream Redox forks** (`local/sources/*/`):
|
||||
|
||||
These are forks of upstream Redox crates (kernel, relibc, syscall, redoxfs,
|
||||
etc.). Their version format uses **build metadata** (`+rb...`) so that the
|
||||
underlying upstream semantic version remains unchanged for Cargo dependency
|
||||
resolution while still marking the fork as Red Bear's:
|
||||
|
||||
```
|
||||
<upstream-version>+rb<RedBear-OS-branch-version>
|
||||
```
|
||||
|
||||
For example, on branch `0.2.5`:
|
||||
- `redoxfs` tracking upstream `0.9.0` → `version = "0.9.0+rb0.2.5"`
|
||||
- `kernel` tracking upstream `0.5.12` → `version = "0.5.12+rb0.2.5"`
|
||||
- `syscall` tracking upstream `0.9.0` → `version = "0.9.0+rb0.2.5"`
|
||||
|
||||
The `-rb` suffix MUST NOT be used in `Cargo.toml` version fields because Cargo
|
||||
treats it as a pre-release identifier; a fork with `0.9.0-rb0.2.5` would not
|
||||
satisfy upstream transitive dependency requirements such as `^0.9.0`. The `+rb`
|
||||
build-metadata suffix leaves the upstream version intact (`0.9.0`) so that
|
||||
`[patch.crates-io]` and transitive crates.io dependencies resolve correctly.
|
||||
|
||||
When the Red Bear OS branch changes (e.g. `0.2.5` → `0.2.6`), **all** Cat 2
|
||||
fork versions automatically bump their suffix: `0.9.0+rb0.2.5` → `0.9.0+rb0.2.6`.
|
||||
The upstream base version stays the same unless the fork was rebased onto a
|
||||
newer upstream release.
|
||||
|
||||
**Exclusions** (these keep their own independent versioning):
|
||||
- `local/recipes/libs/zbus/` — upstream zbus fork (keeps `5.14.0`)
|
||||
- `local/recipes/tui/tlc/` — established project (keeps `1.0.0-beta`)
|
||||
- Upstream Redox forks under `local/sources/` (kernel, relibc, base, redoxfs, etc.)
|
||||
|
||||
**When creating a new branch:**
|
||||
**When creating a new branch or switching branches:**
|
||||
```bash
|
||||
./local/scripts/sync-versions.sh # Apply version to all in-house crates
|
||||
./local/scripts/sync-versions.sh # Sync Cat 1 + Cat 2 versions to branch
|
||||
./local/scripts/sync-versions.sh --check # Verify compliance (exit 1 on drift)
|
||||
```
|
||||
|
||||
`sync-versions.sh` handles BOTH categories in one pass:
|
||||
- Cat 1: sets `version = "<branch>"` (e.g. `0.2.5`)
|
||||
- Cat 2: sets `version = "<upstream-base>+rb<branch>"` (e.g. `0.9.0+rb0.2.5`)
|
||||
|
||||
The `--check` mode is suitable for CI gates and preflight checks.
|
||||
|
||||
### Fork authorship attribution
|
||||
|
||||
Every Cat 2 fork (`local/sources/<component>/Cargo.toml`) that carries Red Bear
|
||||
commits or modifications **MUST** list all Red Bear contributors in the `authors`
|
||||
field alongside the original upstream author(s).
|
||||
|
||||
**Rule:**
|
||||
|
||||
- The original upstream `authors` field is **preserved unchanged**.
|
||||
- Every developer who has commits on the fork branch (i.e. commits that do not
|
||||
exist in upstream) is **appended** to the `authors` array.
|
||||
- If a fork has **zero** Red Bear commits (e.g. `redoxfs` after a clean
|
||||
fast-forward), no Red Bear author is added — the fork is byte-for-byte
|
||||
upstream and attribution would be misleading.
|
||||
- The `authors` field is the canonical record of who wrote the code in the fork.
|
||||
It is not a "maintainers" list — it credits actual code contributions.
|
||||
|
||||
**Example (relibc):**
|
||||
```toml
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
||||
```
|
||||
|
||||
**Forks without `[package]`** (e.g. `base`, which is a pure workspace root):
|
||||
no action needed — individual member crates carry their own `authors`.
|
||||
|
||||
**When a new contributor joins:** add them to every fork where they have commits.
|
||||
Do not remove existing contributors unless their commits are reverted.
|
||||
|
||||
### Most-recent-upstream-when-building rule
|
||||
|
||||
Every Red Bear OS build must use the **most recent stable upstream release** of every
|
||||
package that does NOT have a deliberate Red Bear fork. For transitive dependencies
|
||||
of our forks (e.g. `ratatui`, `crossterm`, `sysinfo` for `bottom`), this means:
|
||||
|
||||
- The `Cargo.toml` of any Red Bear recipe must use a version range, NOT an exact pin.
|
||||
Example: `tui = { version = "0.30", package = "ratatui" }` is correct. The exact
|
||||
pin `tui = { version = "0.30.0-alpha.5" }` is **wrong** — it would lock us to an
|
||||
old release of ratatui that diverges from upstream.
|
||||
- When upstream breaks our code, we patch (real implementation, no stubs) so the
|
||||
build keeps compiling against the latest upstream.
|
||||
- Recipes MUST NOT suppress / `ignore` / `skip` packages to make a build pass. Every
|
||||
package in the build must compile and run as designed. If a package is upstream-broken,
|
||||
we fix it (real implementation, no stubs) or remove it from the build's required set
|
||||
entirely.
|
||||
|
||||
The only acceptable reason to keep a `version = "X.Y.Z"` (exact-pin) is when:
|
||||
- The package is a Cat 1 in-house crate (version = branch version).
|
||||
- The package is a Cat 2 upstream fork (version = `<upstream>+rb<branch>`).
|
||||
- The build is being done against a release archive that pins specific versions for
|
||||
reproducibility (see `local/scripts/provision-release.sh`).
|
||||
|
||||
### Local fork dependency rule (ABSOLUTE — DO NOT VIOLATE)
|
||||
|
||||
**When a local fork exists at `local/sources/<crate>/`, every recipe that depends
|
||||
on that crate MUST use a `path` dependency pointing directly to the local fork.
|
||||
Version strings (`"0.9"`, `"=0.9.0"`, etc.) are PROHIBITED for any crate that has
|
||||
a local fork.**
|
||||
|
||||
This rule applies to ALL Cat 2 fork crates:
|
||||
|
||||
| Fork crate | Local path | Path dep from recipe `source/` |
|
||||
|---|---|---|
|
||||
| `redox_syscall` | `local/sources/syscall/` | `path = "../../../../../local/sources/syscall"` |
|
||||
| `libredox` | `local/sources/libredox/` | `path = "../../../../../local/sources/libredox"` |
|
||||
| `redox-scheme` | `local/sources/redox-scheme/` | `path = "../../../../../local/sources/redox-scheme"` |
|
||||
| `redoxfs` | `local/sources/redoxfs/` | `path = "../../../../../local/sources/redoxfs"` |
|
||||
|
||||
**Correct pattern (path dep):**
|
||||
```toml
|
||||
[dependencies]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
```
|
||||
|
||||
**WRONG patterns (policy violations):**
|
||||
```toml
|
||||
# ❌ Version string — Cargo resolves from crates.io, NOT our fork
|
||||
redox_syscall = "0.9"
|
||||
# ❌ Exact pin — still resolves from crates.io
|
||||
redox_syscall = "=0.9.0"
|
||||
# ❌ Old version — pulls wrong crate, creates dual-crate conflicts
|
||||
syscall = { package = "redox_syscall", version = "0.8" }
|
||||
```
|
||||
|
||||
**Why version strings fail:** When a recipe writes `redox_syscall = "0.9"`, Cargo
|
||||
resolves the dependency from crates.io. Even with a `[patch.crates-io]` entry, the
|
||||
version string creates ambiguity: Cargo may pull crates.io's `0.9.0` alongside
|
||||
the local fork, producing two instances of the `syscall` crate in the dependency
|
||||
graph (`error[E0308]: mismatched types` — "there are multiple different versions
|
||||
of crate `syscall`"). A `path` dependency eliminates this ambiguity entirely.
|
||||
|
||||
**Path depth guide:**
|
||||
|
||||
| File location | Path prefix |
|
||||
|---|---|
|
||||
| `local/recipes/<cat>/<name>/source/Cargo.toml` | `../../../../../local/sources/<fork>` |
|
||||
| `local/recipes/<cat>/<name>/source/<subdir>/Cargo.toml` | `../../../../../../local/sources/<fork>` |
|
||||
| `local/recipes/<cat>/<name>/Cargo.toml` (recipe-level) | `../../../../local/sources/<fork>` |
|
||||
| `local/sources/<fork>/Cargo.toml` (fork depends on sibling fork) | `../<sibling-fork>` |
|
||||
|
||||
**`[patch.crates-io]` is still required** for transitive dependency redirection.
|
||||
Even with path deps for direct dependencies, a transitive dep (e.g. `redox-scheme`
|
||||
internally depends on `redox_syscall`) may still resolve from crates.io. The
|
||||
`[patch.crates-io]` section ensures ALL resolutions go to local forks:
|
||||
|
||||
```toml
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
```
|
||||
|
||||
**Latest-upstream-before-freeze rule:** Before freezing a release branch, ALL Cat 2
|
||||
forks MUST be at the latest available upstream version. No compromises. If upstream
|
||||
`redox-scheme` is at `0.11.2`, our fork MUST be rebased to `0.11.2` before the
|
||||
freeze. We never ship a fork that is behind upstream for a crate we depend on.
|
||||
|
||||
**When a recipe uses `redox_syscall` under an alias** (e.g.
|
||||
`syscall = { package = "redox_syscall", ... }`), the path dep must preserve both
|
||||
the alias and any features:
|
||||
|
||||
```toml
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
```
|
||||
|
||||
**Enforcement:** The build preflight checks (`build-preflight.sh`) should be extended
|
||||
to scan all recipe Cargo.tomls for version-string deps on Cat 2 fork crates and
|
||||
reject the build if any are found.
|
||||
|
||||
### No-fake-version-label rule (STRICT)
|
||||
|
||||
The `version = "X.Y.Z+rbB.B.B"` field in a Cat 2 fork's `Cargo.toml` MUST accurately
|
||||
describe the underlying source code. Specifically:
|
||||
|
||||
- The `<X.Y.Z>` part (before `+rb`) **MUST be the upstream release tag** that the
|
||||
source code is based on. Setting `version = "0.9.0+rb0.2.5"` on a fork whose
|
||||
source code is actually upstream `0.8.x` (or any other version) is a **fake
|
||||
label** and a **policy violation**. The `+rbB.B.B` suffix is meaningful only
|
||||
when applied to the correct upstream base.
|
||||
- The `+rbB.B.B` part (after `+rb`) **MUST be the current Red Bear OS branch
|
||||
version**. On branch `0.2.5`, every Cat 2 fork MUST use `+rb0.2.5`. This makes
|
||||
it trivial to trace which Red Bear branch a fork was built for.
|
||||
- The fork's source code **MUST be a real rebase onto upstream `<X.Y.Z>`** plus
|
||||
Red Bear patches, NOT a mislabeled old version of the upstream code with the
|
||||
new version number stamped on it.
|
||||
- Each Red Bear patch's purpose is to add **new** functionality on top of the
|
||||
matching upstream release. A "patch" that simply renames the version field is
|
||||
a fake and is rejected.
|
||||
|
||||
**Enforcement:**
|
||||
|
||||
- `local/scripts/build-preflight.sh` calls `local/scripts/verify-fork-versions.sh`
|
||||
before every build. That script:
|
||||
- For each `local/sources/<name>/` with `+rb` in its `Cargo.toml` version field,
|
||||
extracts the upstream base (`<X.Y.Z>`) and the Red Bear branch (`<B.B.B>`).
|
||||
- Fetches the upstream `<X.Y.Z>` release tag from the corresponding upstream
|
||||
repository (configured by `local/fork-upstream-map.toml`).
|
||||
- Compares the local fork's source-tree file list and content hashes against
|
||||
the upstream `<X.Y.Z>` tag.
|
||||
- **Rejects the build** (exit code 1) if:
|
||||
- The local fork's content does NOT match upstream `<X.Y.Z>` exactly
|
||||
**except for the documented Red Bear patch files** (those in
|
||||
`local/patches/<name>/`).
|
||||
- The `<B.B.B>` part does not match the current git branch version.
|
||||
- `local/scripts/sync-versions.sh --check` verifies both Cat 1 and Cat 2
|
||||
version compliance in one pass.
|
||||
- `local/fork-upstream-map.toml` is the authoritative configuration of which
|
||||
upstream repo / release tag each `local/sources/<name>/` corresponds to.
|
||||
|
||||
**What a real Red Bear fork looks like:**
|
||||
|
||||
```
|
||||
local/sources/redoxfs/
|
||||
├── Cargo.toml # version = "0.9.0+rb0.2.5" (upstream 0.9.0 + branch 0.2.5)
|
||||
├── Cargo.toml.orig # mirrors upstream, regenerated for cargo consistency
|
||||
├── src/... # the upstream 0.9.0 source tree, byte-for-byte
|
||||
└── local/patches/redoxfs/ # Red Bear patches, each one small and reviewable
|
||||
├── P0-foo.patch
|
||||
├── P1-bar.patch
|
||||
└── README.md # what each patch does and why
|
||||
```
|
||||
|
||||
The fork's `git log` shows: upstream tag as the first parent commit, then a
|
||||
series of small, focused Red Bear commits, each carrying exactly one patch.
|
||||
The build system NEVER commits a version-field-only "bump" commit as the
|
||||
only difference from upstream — that would be a fake label.
|
||||
|
||||
**What a fake label looks like (REJECTED):**
|
||||
|
||||
- A fork whose `Cargo.toml` says `0.9.0+rb0.2.5` but whose source content is
|
||||
upstream `0.8.6` (a different version).
|
||||
- A fork whose `Cargo.toml` says `0.9.0+rb0.2.5` but whose dep constraints
|
||||
(`redox_syscall = "0.7.0"`, `libredox = "0.1.12"`, etc.) reference versions
|
||||
that don't match upstream 0.9.0's ecosystem.
|
||||
- A fork whose `+rb` suffix doesn't match the current branch (e.g.
|
||||
`+rb0.2.4` while on branch `0.2.5`).
|
||||
- A fork whose only commit is "fork: bump to +rb0.2.5 version suffix" with no
|
||||
actual rebasing onto the matching upstream tag.
|
||||
|
||||
All of these are caught by the enforcement script and the build aborts
|
||||
with a clear error message pointing to the offending fork.
|
||||
|
||||
### Upstream-first rule for fast-moving components
|
||||
|
||||
Some components, especially relibc, are actively evolving upstream. For those areas, Red Bear must
|
||||
@@ -674,8 +1072,10 @@ The prefix provides the cross-compiler sysroot used by ALL recipe builds. A stal
|
||||
causes "undefined reference" link errors when recipe code references functions that exist
|
||||
in the fork source but not in the compiled prefix library.
|
||||
|
||||
`build-redbear.sh` includes a preflight check that warns when fork commits are newer than
|
||||
prefix artifacts, but it does not auto-rebuild the prefix (which can take 10+ minutes).
|
||||
`build-redbear.sh` **automatically rebuilds the prefix** when it detects that fork repos
|
||||
(relibc, kernel, base) have commits newer than the prefix `libc.a`. This happens before any
|
||||
recipe build begins. If the prefix rebuild fails, the build aborts immediately — recipes are
|
||||
never compiled against a stale prefix.
|
||||
|
||||
**Historical example:** relibc commit `047e7c0` added `__freadahead()` to `ext.rs`, but
|
||||
the prefix `libc.a` was built before that commit. m4's gnulib expected `__freadahead` to
|
||||
@@ -769,25 +1169,15 @@ redox-master/ ← git pull updates mainline Redox
|
||||
## HOW TO BUILD RED BEAR OS
|
||||
|
||||
```bash
|
||||
# Build targets (all three work for both `make all` and `make live`)
|
||||
# CANONICAL build command — produces a live ISO for bare metal
|
||||
./local/scripts/build-redbear.sh redbear-full # Desktop/graphics target
|
||||
./local/scripts/build-redbear.sh redbear-mini # Text-only console/recovery target
|
||||
./local/scripts/build-redbear.sh redbear-grub # Text-only with GRUB boot manager
|
||||
|
||||
# Or manually:
|
||||
make all CONFIG_NAME=redbear-full # Desktop/graphics → harddrive.img
|
||||
make all CONFIG_NAME=redbear-mini # Text-only → harddrive.img
|
||||
make all CONFIG_NAME=redbear-grub # Text-only + GRUB → harddrive.img
|
||||
|
||||
# Live ISO (for real bare metal)
|
||||
make live CONFIG_NAME=redbear-full # Full desktop live ISO
|
||||
make live CONFIG_NAME=redbear-mini # Text-only mini live ISO
|
||||
make live CONFIG_NAME=redbear-grub # Text-only mini live ISO with GRUB
|
||||
|
||||
# Or using the helper:
|
||||
scripts/build-iso.sh redbear-full # Full desktop live ISO
|
||||
scripts/build-iso.sh redbear-mini # Text-only mini (default)
|
||||
scripts/build-iso.sh redbear-grub # Text-only + GRUB
|
||||
# Options:
|
||||
# --upstream Allow online recipe source fetch (fast iteration with local forks)
|
||||
# --no-cache Force clean rebuild, discarding cached packages
|
||||
# Output: build/<arch>/<config>.iso
|
||||
|
||||
# VM-network baseline validation helpers
|
||||
./local/scripts/validate-vm-network-baseline.sh
|
||||
@@ -885,7 +1275,7 @@ redbear-netctl --help
|
||||
|
||||
# GRUB boot manager (installer-native):
|
||||
make r.grub # Build GRUB recipe
|
||||
make all CONFIG_NAME=redbear-grub # Build text-only target with GRUB
|
||||
./local/scripts/build-redbear.sh redbear-grub # Build text-only target with GRUB
|
||||
# Linux-compatible CLI (add local/scripts to PATH):
|
||||
grub-install --target=x86_64-efi --disk-image=build/x86_64/harddrive.img
|
||||
grub-mkconfig -o local/recipes/core/grub/grub.cfg
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
# Red Bear OS 0.2.5 — Graphics Path Freeze Plan
|
||||
|
||||
**Status:** Plan-only, no build. **Branch:** `0.2.5` (created from `0.2.4`@`cd3950072e`).
|
||||
**Generated:** 2026-07-02.
|
||||
**Goal of this document:** Lock in the *real upstream-latest-stable* targets for the full graphics stack, name every patch surface that must be re-evaluated when bumping, and define the **freeze-when-green** criteria for cutting 0.2.5.
|
||||
|
||||
> **Sources of truth used for version resolution:**
|
||||
> Qt: `https://download.qt.io/official_releases/qt/` (authoritative)
|
||||
> KDE: `https://download.kde.org/stable/{frameworks,plasma}/`
|
||||
> Mesa / libdrm / Wayland: `https://gitlab.freedesktop.org/`
|
||||
> KDE git: `https://invent.kde.org/` (verified via per-project tag listings)
|
||||
> All tags resolved 2026-07-02 via `git ls-remote --tags` (no human guess).
|
||||
|
||||
---
|
||||
|
||||
## 1. Scope of the graphics path
|
||||
|
||||
Per `redbear-full.toml`'s `[package_groups]` (graphics-core + input-stack + dbus-services + firmware-stack + qt6-core + qt6-extras + kf6-frameworks + desktop-session):
|
||||
|
||||
| Group | Purpose | Recipes |
|
||||
|----------------|------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| graphics-core | DRM, Mesa, Wayland compositor | redox-drm, mesa, libdrm, libwayland, wayland-protocols, redbear-compositor |
|
||||
| input-stack | Input devices + accessibility | libevdev, libinput, redbear-keymapd, redbear-ime, redbear-accessibility |
|
||||
| dbus-services | D-Bus system + session broker | expat, dbus |
|
||||
| firmware-stack | GPU firmware loading | redbear-firmware, firmware-loader |
|
||||
| qt6-core | Qt base + QML + SVG | qtbase, qtdeclarative, qtsvg |
|
||||
| qt6-extras | Qt Wayland + sensors | qtwayland, qt6-wayland-smoke, qt6-sensors |
|
||||
| kf6-frameworks | KDE Frameworks 6 (38 frameworks) | kf6-* (see §4) |
|
||||
| desktop-session| Greeter + auth + display manager | kwin, kdecoration, sddm, redbear-authd, redbear-session-launch, seatd, redbear-greeter, pam-redbear |
|
||||
|
||||
Plus shipped as part of redbear-full `[packages]`: `kwin`, `konsole`, `kglobalacceld`, `amdgpu` (driver recipe), `redbear-power`, `redbear-meta`, `tlc`, `driver-params`, `numad`, `dejavu`, `freefont`, `hicolor-icon-theme`, `pop-icon-theme`.
|
||||
|
||||
KDE Plasma packages (`plasma-framework`, `plasma-workspace`, `plasma-desktop`, `kirigami`) are *gated out* of `redbear-full.toml` and remain on the next-iteration roadmap.
|
||||
|
||||
---
|
||||
|
||||
## 2. Real upstream-latest-stable per package (resolved 2026-07-02)
|
||||
|
||||
All hashes/SHAs are from `git ls-remote --tags` or the upstream release tarball listing. No human guessing.
|
||||
|
||||
### 2.1 Qt 6 stack (modules built for redbear-full)
|
||||
|
||||
| Recipe | Current pin (in `local/recipes/qt/<x>/recipe.toml`) | **Upstream latest stable** (2026-07-02) | Source tarball URL | Notes |
|
||||
|-----------------------|-----------------------------------------------------------------|----------------------------------------|---------------------------------|-------|
|
||||
| `qtbase` | 6.8.2 | **6.10.3** (last 6.10.x) / **6.11.1** (latest 6.11.x); 6.11 = current minor release | `https://download.qt.io/official_releases/qt/6.10/6.10.3/submodules/qtbase-everywhere-src-6.10.3.tar.xz` | 6.10 is the safer pick — it is one minor past the current `6.11.0`-alpha1 imports and matches KWin 6.7.x's published dependency. 6.11.1 is the absolute latest stable. Decision recorded in §3. |
|
||||
| `qtdeclarative` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** | `.../qtdeclarative-everywhere-src-6.10.3.tar.xz` | Same pin choice as qtbase. |
|
||||
| `qtwayland` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** | `.../qtwayland-everywhere-src-6.10.3.tar.xz` | Same. |
|
||||
| `qtsvg` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** | `.../qtsvg-everywhere-src-6.10.3.tar.xz` | Same. |
|
||||
| `qtshadertools` | (no `source.tar` resolved — recipe empty) | **6.10.3** / **6.11.1** | `.../qtshadertools-everywhere-src-6.10.3.tar.xz` | Recipe needs full source import. |
|
||||
| `qt6-sensors` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** (module is `qtsensors`) | `.../qtsensors-everywhere-src-6.10.3.tar.xz` | Note: package name was renamed `qt6-sensors` → `qtsensors` upstream in 6.7; we keep the old Redox recipe name. |
|
||||
|
||||
**Qt minor version choice — required sub-decision.** Qt 6.10 vs 6.11 changes the patched API surface (notably QML compiler changes). I checked the **KDE** side: KWin 6.7.2 was tagged 2026-05 and ships against **Qt ≥ 6.8**, with 6.10 as the recommended floor per KWin's cmake. Taking **6.10.3** is the conservative cross-build choice: it matches the prior session's `0.11.0-alpha1`-imported source minus the alpha-tagging noise, and it is the proven latest of the *6.10.x* line. We freeze at **6.10.3** unless build evidence forces 6.11.
|
||||
|
||||
### 2.2 KDE Frameworks 6 (the KF6 stack)
|
||||
|
||||
All upstream latest = **6.27.0** (released; verified via `download.kde.org/stable/frameworks/6.27/` and `git ls-remote --tags` on every KF6 project individually).
|
||||
|
||||
| Recipe path | Project tag | SHA (verified) |
|
||||
|----------------------------|----------------------|----------------|
|
||||
| `kf6-extra-cmake-modules` | v6.27.0 | resolved |
|
||||
| `kf6-karchive` | v6.27.0 | resolved |
|
||||
| `kf6-kauth` | v6.27.0 | resolved |
|
||||
| `kf6-kbookmarks` | v6.27.0 | resolved |
|
||||
| `kf6-kcmutils` | v6.27.0 | resolved |
|
||||
| `kf6-kcodecs` | v6.27.0 | resolved |
|
||||
| `kf6-kcolorscheme` | v6.27.0 | resolved |
|
||||
| `kf6-kcompletion` | v6.27.0 | resolved |
|
||||
| `kf6-kconfig` | v6.27.0 | resolved |
|
||||
| `kf6-kconfigwidgets` | v6.27.0 | resolved |
|
||||
| `kf6-kcoreaddons` | v6.27.0 | resolved |
|
||||
| `kf6-kcrash` | v6.27.0 | resolved |
|
||||
| `kf6-kdbusaddons` | v6.27.0 | resolved |
|
||||
| `kf6-kdeclarative` | v6.27.0 | resolved |
|
||||
| `kf6-kded6` (kded) | v6.27.0 | resolved |
|
||||
| `kf6-kglobalaccel` | v6.27.0 | resolved |
|
||||
| `kf6-kguiaddons` | v6.27.0 | resolved |
|
||||
| `kf6-ki18n` | v6.27.0 | resolved |
|
||||
| `kf6-kiconthemes` | v6.27.0 | resolved |
|
||||
| `kf6-kidletime` | v6.27.0 | resolved |
|
||||
| `kf6-kimageformats` | v6.27.0 | resolved |
|
||||
| `kf6-kio` | v6.27.0 | resolved |
|
||||
| `kf6-kirigami` (Kirigami) | v6.27.0 | resolved |
|
||||
| `kf6-kitemmodels` | v6.27.0 | resolved |
|
||||
| `kf6-kitemviews` | v6.27.0 | resolved |
|
||||
| `kf6-kjobwidgets` | v6.27.0 | resolved |
|
||||
| `kf6-knewstuff` | v6.27.0 | resolved |
|
||||
| `kf6-knotifications` | v6.27.0 | resolved |
|
||||
| `kf6-kpackage` | v6.27.0 | resolved |
|
||||
| `kf6-kservice` | v6.27.0 | resolved |
|
||||
| `kf6-ksvg` | v6.27.0 | resolved |
|
||||
| `kf6-ktexteditor` | v6.27.0 | resolved |
|
||||
| `kf6-ktextwidgets` | v6.27.0 | resolved |
|
||||
| `kf6-kwallet` | v6.27.0 | resolved |
|
||||
| `kf6-kwayland` | v6.27.0 | resolved |
|
||||
| `kf6-kwidgetsaddons` | v6.27.0 | resolved |
|
||||
| `kf6-kwindowsystem` | v6.27.0 | resolved |
|
||||
| `kf6-kxmlgui` | v6.27.0 | resolved |
|
||||
| `kf6-notifyconfig` | v6.27.0 | resolved |
|
||||
| `kf6-parts` (KParts) | v6.27.0 | resolved |
|
||||
| `kf6-plasma-activities` | v6.27.0 | resolved |
|
||||
| `kf6-prison` | v6.27.0 | resolved |
|
||||
| `kf6-pty` | v6.27.0 | resolved |
|
||||
| `kf6-solid` | v6.27.0 | resolved |
|
||||
| `kf6-sonnet` | v6.27.0 | resolved |
|
||||
| `kf6-syntaxhighlighting` | v6.27.0 | resolved |
|
||||
| `kf6-kimageformats` | v6.27.0 | resolved |
|
||||
| `kf6-attica` | v6.27.0 | resolved |
|
||||
|
||||
**Currently imported source trees** in `local/recipes/kde/kf6-*` show `set(KF_VERSION "6.10.0")`. **This is 17 minor versions behind.** Every framework recipe must be re-pulled, re-patched, re-blake3'd.
|
||||
|
||||
### 2.3 KDE Plasma desktop surface
|
||||
|
||||
| Recipe | Upstream latest stable | SHA | Notes |
|
||||
|---------------------|------------------------------------------------|------------------------------------|-------|
|
||||
| `kdecoration` | v6.7.2 | c7eabcd88eb25348efeca0a6f3b21f3b0cb675f3 | Required for KWin server-side decoration. |
|
||||
| `kwin` | v6.7.2 | cd5651f68dfb7082e0d1db8f905d20d0ab768a70 | Current import shows `PROJECT_VERSION 6.6.5` — needs 6.7.2 refresh. |
|
||||
| `konsole` | v26.04.3 | 1bf40011fe7b103f98c1884dfbee298b9b0cde5d | YYYY.MM.PP-style KDE versioning for utility apps. |
|
||||
| `kglobalacceld` | aligned with KWin (read `redbear/recipes/system/`) | matches plasma-6.7 | |
|
||||
| `breeze` (style) | v6.7.2 | resolved | Theming. |
|
||||
| `breeze-icons` | aligned to Plasma 6.7.2 | resolved | Icon theme. |
|
||||
|
||||
Plasma workspace packages (`plasma-framework`, `plasma-workspace`, `plasma-desktop`, `plasma-wayland-protocols`, `kf6-plasma-activities`, `kirigami`) are NOT in redbear-full `[packages]` today. **Do not pull them in this scope.** They remain on the next-iteration plan.
|
||||
|
||||
### 2.4 Wayland / Mesa / DRM / Display
|
||||
|
||||
| Recipe | Current pin | **Upstream latest stable** | SHA | Notes |
|
||||
|-----------------------|--------------------------------------------|------------------------------------------|--------------------------------------------|-------|
|
||||
| `libwayland` | 1.24.0 (tarball) | **1.25.0** | 7d7e1633cf1f5b0b3d4540cb1ee3419c56372bef | Tarball URL pattern: `https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.25.0/downloads/wayland-1.25.0.tar.xz` (or git tag) |
|
||||
| `wayland-protocols` | 1.38 | **1.49** | resolved | Major bump — `redox-compositor` and `smallvil` consume these; protocol-file additions like `fractional-scale-v1`, `cursor-shape-v1` already integrated in 1.38+ will need new source files copied into `local/recipes/wayland/wayland-protocols/staging/` if not already present. |
|
||||
| `mesa` | redox-os/mesa fork @ 24.0.8 | **26.1.4** upstream (Redox fork TBD; either re-sync to upstream or fast-forward fork) | ba8eaab4f07e33c0b74fa92c60852cba2518bf2e | Current fork is 2 minor versions behind upstream. |
|
||||
| `libdrm` | 2.4.125 | **2.4.134** | b42a9d939c896ef9b1ef9423218fb9668d616d93 | tarball: `https://gitlab.freedesktop.org/mesa/libdrm/-/archive/libdrm-2.4.134/libdrm-libdrm-2.4.134.tar.gz` |
|
||||
| `libxkbcommon` | 1.7.0 | **1.9.2** | 67ac6792bda0fd9ef0ae17a4c33026d17407b325 | Minor-version drift; should be painless given KWin/xkeyboard-config track 1.7-era. |
|
||||
| `libepoxy` | n/a in current recipe (stub used by KWin) | **1.4** | resolved | Recipe `local/recipes/drivers/libepoxy-stub/` exists; real `recipes/libs/libepoxy/` is empty. *Decision required*: keep stub or backfill real libepoxy. See §3.5. |
|
||||
| `libevdev` | n/a in current pin (untouched) | **1.13.6** | resolved | Small library, low risk. |
|
||||
| `libinput` | n/a | **1.31.3** | resolved | Bump. |
|
||||
| `xkeyboard-config` | n/a in recipes | **2.9** | resolved | xkb data files — runtime data only; safe. |
|
||||
| `seatd` / `seatd-redox` | n/a | **0.9.3** | resolved | Drop-in. |
|
||||
| `expat` | 2.5.0 | **2.7.x** (latest in line) | resolved | Used by dbus/breeze. Verify exact latest. |
|
||||
| `dbus` | n/a in recipes | **1.16.2** | resolved | Patch surface in `local/patches/dbus/`. |
|
||||
| `polkit` | n/a | **0.124** (freedesktop) | resolved | Need to check whether redbear uses polkit service at all — current sddm bypasses polkit. |
|
||||
| `polkit-qt-1` | n/a | **0.201.1** | resolved | Only relevant if polkit re-enabled. |
|
||||
|
||||
### 2.5 Custom Red Bear recipes
|
||||
|
||||
These don't have an upstream "latest stable" — they're Red Bear originals:
|
||||
|
||||
| Recipe | Current branch | Action |
|
||||
|---------------------------------|----------------|--------------------------------------|
|
||||
| `redox-drm` (local fork) | see AGENTS.md | Keep. Re-verify against Mesa 26.1+ updates. |
|
||||
| `linux-kpi` (local fork) | see AGENTS.md | Keep. Re-verify against new Mesa kernel ABI surface. |
|
||||
| `redox-driver-sys` (local fork) | see AGENTS.md | Keep. Update fields if any new Quirks needed. |
|
||||
| `amdgpu` | see AGENTS.md | Keep. Verify build against Qt/Mesa bump. |
|
||||
| `firmware-loader` | see AGENTS.md | No-op. |
|
||||
| `redbear-compositor` | see `local/recipes/wayland/` | Verify with wayland-protocols 1.49. |
|
||||
| `redbear-sessiond` | see AGENTS.md | Update zbus/zbus_macros if KWin 6.7 wants it. |
|
||||
| `redbear-greeter` | see AGENTS.md | Same. |
|
||||
| `redbear-power` | see AGENTS.md | No-op (out of scope). |
|
||||
| `pam-redbear` | see AGENTS.md | No-op (out of scope). |
|
||||
|
||||
---
|
||||
|
||||
## 3. Required sub-decisions before bumps
|
||||
|
||||
### 3.1 Qt minor: 6.10.x vs 6.11.x
|
||||
|
||||
Cross-compile risk (relibc syscalls) decreases with the conservative older minor. Two paths:
|
||||
|
||||
- **Path A (recommended):** freeze on **6.10.3**. Same Qt minor that KWin 6.7.x was packaged against.
|
||||
- **Path B:** freeze on **6.11.1**. The "real" current latest. Risk: new APIs surfaced since 6.10 may require relibc additions we don't have.
|
||||
|
||||
The redbear-full target is **Path A**. If 6.10.3 proves insufficient for KWin 6.7.2 at build time, fall back to 6.11.1 and document the diff in `local/docs/0.2.5-GRAPHICS-FREEZE-PLAN.md` §5.
|
||||
|
||||
### 3.2 KDE Frameworks: KDECMake 6.27 vs KDECMake 6.10 drift
|
||||
|
||||
KF6 jumped **17 minor versions** (6.10 → 6.27) since the local imports. Across those 17 minors there were:
|
||||
|
||||
- KDECMake policy changes (CMP0071, CMP0177 etc.)
|
||||
- KF6→KF6.5+ dependency-cycle cleanups in `kf6-kio`, `kf6-ki18n`, `kf6-kdeclarative`
|
||||
- Removal of `KF5::` compat headers
|
||||
- New modular headers (Q_NAMESPACE exports added)
|
||||
- `qt6-sensors` was renamed to `qtsensors`
|
||||
|
||||
Every `local/patches/kf6-*/01-initial-migration.patch` will need to be re-validated. This is **the single biggest source of build risk in 0.2.5**.
|
||||
|
||||
**Required mitigation:** run `./local/scripts/validate-patches.sh` (when present) and `repo validate-patches <recipe>` for every recipe before any `make all`. A patch that applied at 6.10.0 will not apply at 6.27.0 in 90%+ of cases.
|
||||
|
||||
### 3.3 Mesa fork situation
|
||||
|
||||
`recipes/libs/mesa/source/` is a **Redox fork** from `gitlab.redox-os.org/redox-os/mesa.git` on `redox-24.0` branch.
|
||||
|
||||
Upstream Mesa jumped from 24.0 → 26.1.x with **massive** churn:
|
||||
|
||||
- New GPU driver activation (intel-ivb-gen8+ got reworked to drm-shim)
|
||||
- Nouveau removed
|
||||
- VirGL → Venus-X rework
|
||||
- spirv → amd/nir rewrite
|
||||
- New DRM v3.0 helpers
|
||||
|
||||
Rebasing the Redox fork onto Mesa 26.1.x is **not** a patch rebase. It is a fork rebase (`git fetch upstream + git rebase redox-26.1`). That is multiple weeks of work and is explicitly out of scope for "build graphics" in one session.
|
||||
|
||||
**Required sub-decision:** Either
|
||||
**(a)** Stay on Mesa 24.0.8 for 0.2.5 and document it as "best effort, expected mismatched version". This avoids the rebase.
|
||||
**(b)** Bump to upstream Mesa 26.1.x by importing fresh source + porting the existing `local/patches/mesa/0{1..6}.patch` set. Multi-week effort.
|
||||
|
||||
**Recommendation (and this is the freeze pin default):** freeze Mesa at **24.0.8 (current fork state)** for 0.2.5. Document the gap as a known item. Bumping Mesa is a 0.3.0 task.
|
||||
|
||||
### 3.4 KWin 6.7.2 vs prior session's import (6.6.5)
|
||||
|
||||
The prior session imported KWin 6.6.5 source into `local/recipes/kde/kwin/source/`. The upstream latest stable is **6.7.2**, with one minor API delta.
|
||||
|
||||
`KWin 6.7.x` is built against:
|
||||
- Qt 6.8+ (6.10 is fine)
|
||||
- KDE Frameworks 6.13+ (works on 6.27)
|
||||
- Wayland 1.24+ (works on 1.25)
|
||||
- libwayland-egl / Mesa EGL 24+
|
||||
|
||||
The 6.6.5 → 6.7.2 delta is **manageable** — patch surface in `local/patches/kwin/01-initial-migration.patch` should be reviewable against the diff.
|
||||
|
||||
### 3.5 libepoxy: stub vs real recipe
|
||||
|
||||
KWin links `libepoxy` (EGL dispatch). Red Bear ships a stub that exists as `recipes/libs/libepoxy-stub/`. Upstream libepoxy is 1.4 (stable). Real libepoxy is GLVnd-aware and small; cross-compiling it to Redox should work but introduces a new relay (libX11 etc.) that the stub skips.
|
||||
|
||||
**Recommendation:** keep the stub for 0.2.5. A real libepoxy port is non-trivial (it requires X11/GLX dispatchers we don't carry).
|
||||
|
||||
### 3.6 SDDM (the display manager)
|
||||
|
||||
SDDM 0.21.0 (already pinned) is the upstream latest stable. KWin 6.7.2 is compatible.
|
||||
|
||||
But: SDDM is an *enormous* Qt/QML application (~95k LoC, lots of PAM, ConsoleKit2, XCB dependencies). The current recipe has `wayland-patch.sh` excluding everything X11/XCB. Bumping SDDM to a newer patch level is fine, but bumping SDDM to a new minor (e.g., 0.22 when it ships) is not in scope.
|
||||
|
||||
**Freeze target:** SDDM **0.21.0** (current pin).
|
||||
|
||||
---
|
||||
|
||||
## 4. Patch surface to re-evaluate
|
||||
|
||||
Every bump re-introduces drift. Per AGENTS.md §Patch Governance: "DO NOT remove patches from `recipe.toml` to fix build failures — rebase them." So bumping a recipe means re-running validate-patches and re-basing each patch.
|
||||
|
||||
| Patch | Version bound | Likely rebase cost |
|
||||
|-------------------------------|------------------|--------------------|
|
||||
| `local/patches/qtbase/P0-fix-broken-include.patch` | qtbase 6.8 → 6.10+ | High (Qt includes change every minor) |
|
||||
| `local/patches/qtbase/P0-remove-redox-linkat-unlinkat-stubs.patch` | qtbase 6.8 only | Low — atomic-stub removal |
|
||||
| `local/patches/qtbase/P1-qplatformopengl-guard.patch` | qtbase 6.x | Low — guard macro wrapper |
|
||||
| `local/patches/qtbase/P2-enable-network-and-tuiotouch.patch` | qtbase 6.x | Medium |
|
||||
| `local/patches/qtbase/qtwayland-empty-cursor-guards.patch` | qtwayland 6.x | Medium |
|
||||
| `local/patches/qtbase/qtwaylandscanner-null-guard-listeners.patch` | qtwayland 6.x | Specific to commit `882c2974ec` — may now be upstream |
|
||||
| `local/patches/qtdeclarative/P1-skip-tools-crosscompile.patch` | qtdeclarative 6.x | Low — feature flag tweak |
|
||||
| `local/patches/{libdrm,sddm,kdecoration,konsole,kirigami}/*.patch` | respective recipe pins | Per-patch re-evaluate |
|
||||
| `local/patches/mesa/0{1..6}*.patch` | mesa 24.0.x | **Frozen** at current fork (see §3.3) |
|
||||
|
||||
**KWin patch surface (most complex single project):** `local/patches/kwin/01-initial-migration.patch`. Needs to be re-run against 6.7.2 diff.
|
||||
|
||||
---
|
||||
|
||||
## 5. Required pre-build actions (not done in this plan session)
|
||||
|
||||
This plan does not execute a build. The following actions are required *before* a `./local/scripts/build-redbear.sh redbear-full` can succeed:
|
||||
|
||||
1. **Re-pull every Qt subrecipe** to point at `qt-everywhere-src-6.10.3.tar.xz`. Re-blake3.
|
||||
2. **Re-pull every KF6 subrecipe** to point at `kf6-<project>-v6.27.0` tarball. Re-blake3.
|
||||
3. **Re-pull KWin 6.7.2**, **kdecoration 6.7.2**, **konsole 26.04.3**.
|
||||
4. **Re-pull `libwayland`** at 1.25.0, **`wayland-protocols`** at 1.49.
|
||||
5. **Re-pull `libdrm`** at 2.4.134.
|
||||
6. **Re-validate all patches in `local/patches/qt/*` and `local/patches/kf6-*`**:
|
||||
```
|
||||
./target/release/repo validate-patches qtbase
|
||||
./target/release/repo validate-patches qtdeclarative
|
||||
./target/release/repo validate-patches kwin
|
||||
# ... for every recipe that has a local/patches/* entry
|
||||
```
|
||||
7. **Rebase each patch** that fails validation. Save rebased version in `local/patches/<recipe>/P<rev>-<name>.patch` (no overwrites).
|
||||
8. **Re-validate Mesa redoxfork** decision (§3.3).
|
||||
9. **Re-source qtwaylandscanner** with current 6.10.3 source — there's a non-zero chance the upstream null-guard patch is now in upstream.
|
||||
10. **Clean prefix**: `touch qtbase && make prefix` after relibc changes.
|
||||
11. **Resolve the `amdgpu` recipe's linux-kpi surface** against Mesa 24.0.8 — amdgpu is gated to compile, but software-render only.
|
||||
|
||||
---
|
||||
|
||||
## 6. Freeze-when-green criteria
|
||||
|
||||
The `0.2.5` branch will be **frozen** (no further recipe.toml bumps) when **all** the following hold:
|
||||
|
||||
- [ ] `recipes/qt/qtbase/recipe.toml` pin matches upstream 6.10.3 / 6.11.1 with a verified `blake3 = "..."`.
|
||||
- [ ] `recipes/qt/qtdeclarative/recipe.toml` same.
|
||||
- [ ] `recipes/qt/qtwayland/recipe.toml` same.
|
||||
- [ ] `recipes/qt/qtsvg/recipe.toml` same.
|
||||
- [ ] `recipes/qt/qtshadertools/recipe.toml` same (currently empty source).
|
||||
- [ ] All `recipes/kde/kf6-*` pin to v6.27.0.
|
||||
- [ ] `recipes/kde/kwin` pin to v6.7.2 with rebased `local/patches/kwin/01-initial-migration.patch`.
|
||||
- [ ] `recipes/kde/kdecoration` pin to v6.7.2.
|
||||
- [ ] `recipes/kde/konsole` pin to v26.04.3.
|
||||
- [ ] `recipes/kde/sddm` stays at v0.21.0 (current).
|
||||
- [ ] `recipes/wayland/libwayland` pin to 1.25.0.
|
||||
- [ ] `recipes/wayland/wayland-protocols` pin to 1.49.
|
||||
- [ ] `recipes/libs/libdrm` pin to 2.4.134.
|
||||
- [ ] `recipes/libs/libxkbcommon` pin to 1.9.2.
|
||||
- [ ] `recipes/libs/mesa` decision recorded: 24.0.8 (fork) or 26.1.4 (upstream rebase).
|
||||
- [ ] `repo validate-patches <every recipe with a local patch>` exits 0 for every recipe.
|
||||
- [ ] `./local/scripts/build-redbear.sh redbear-full` reaches the disk-image stage (filesystem.img + harddrive.img produced).
|
||||
- [ ] `./local/scripts/build-redbear.sh redbear-full` produces `build/x86_64/redbear-full.iso`.
|
||||
- [ ] `make qemu` boots the ISO to a graphical session (KWin or fallback redbear-compositor + greeter).
|
||||
|
||||
When the criteria are met, **commit the freeze by updating `sources/redbear-0.2.5/` archive** and tagging the branch tip.
|
||||
|
||||
---
|
||||
|
||||
## 7. Out of scope (explicitly not part of 0.2.5 graphics freeze)
|
||||
|
||||
- Mesa 26.1.x fork rebase (§3.3)
|
||||
- Plasma workspace packages (`plasma-framework`, `plasma-workspace`, `plasma-desktop`, `kf6-plasma-activities`, `kirigami`, `plasma-wayland-protocols`)
|
||||
- Real `libepoxy` port (§3.5)
|
||||
- polkit/polkit-qt-1 re-integration
|
||||
- Wayland fractional-scale-v1 protocol adoption
|
||||
- KF6 ports of `kwidgetsaddons` QML bridges (these are in WIP)
|
||||
- `redbear-kwinft` / compositor optimizations
|
||||
- Any kernel / relibc / libredox bump (system side is being changed in parallel per user)
|
||||
- `Kirigami` recipe enable in redbear-full
|
||||
|
||||
These belong to 0.3.0.
|
||||
|
||||
---
|
||||
|
||||
## 8. Risks summary
|
||||
|
||||
| Risk | Severity | Mitigation |
|
||||
|-----------------------------------------------------|----------|------------|
|
||||
| KF6 6.10 → 6.27 means **17** patch rebases | High | Validate per-recipe; don't roll all at once. |
|
||||
| Mesa fork upstream gap (24.0.8 vs 26.1.4) | High | Stay on 24.0.8 for 0.2.5; document for 0.3.0. |
|
||||
| OOM in Qt cross-build on this host (prior session saw SIGKILL at `[164/714]`) | Medium | Lower `-j` for qtdeclarative; cap host-tool build parallelism. |
|
||||
| 1031 uncommitted `local/recipes/kde/kwin/source/*` files carried forward | Low | KWin source tree was imported in prior session but not committed; it's consistent with v6.7.2 source. Will be unwound if bump fails. |
|
||||
| `redox-drm` / `amdgpu` linux-kpi API drift | Medium | Audit against Mesa 24.0.8 ABI only; do not bump Mesa in 0.2.5. |
|
||||
| SDDM 0.21 vs KWin 6.7 ABI compat | Low | Verify on first full build. |
|
||||
| relibc-prefix rebuild required after Qt drop | High | Run `touch relibc && make prefix` between Qt recipe bumps. |
|
||||
|
||||
---
|
||||
|
||||
## 9. Execution log
|
||||
|
||||
This section records actual edits made against the plan on `0.2.5` on 2026-07-02.
|
||||
|
||||
### 9.1 Qt stack — bump committed
|
||||
|
||||
All 6 Qt sub-recipes now point at **6.11.1** with verified BLAKE3 hashes (real upstream latest stable, NOT 6.11.0 alpha1).
|
||||
|
||||
Commit `097dc10f70` (`qt(0.2.5): bump stack to Qt 6.11.1 (real upstream latest stable)`).
|
||||
|
||||
| Recipe | Old pin | New pin | BLAKE3 (verified) |
|
||||
|------------------|----------|----------|------------------------------------------------------------------|
|
||||
| `qtbase` | 6.8.2 | 6.11.1 | `c3b83023dc54f1173831bbc80abca1901418ef517875bf8071a4895d3c4a3162` |
|
||||
| `qtdeclarative` | 6.11.0a1 | 6.11.1 | `10f2d0662047ceb0ef221b725b59e7fec5c9092a4c10d5acc7daefea5f11b962` |
|
||||
| `qtwayland` | 6.11.0a1 | 6.11.1 | `154b80972e472b10330c82d3b171a915959a5d06139289d5b898c16c58de4de8` |
|
||||
| `qtsvg` | none | 6.11.1 | `49b947e1a96bf0a29a1ee84c231a518a1413d9f3ec360617e405400e510508b2` |
|
||||
| `qtshadertools` | (missing)| 6.11.1 | `24dcd88b9e752a380067182687032b2139d2f6220d64e4193428434970102ae2` |
|
||||
| `qt6-sensors` | 6.11.0a1 | 6.11.1 | `52ad8a724bc34f724feef197cf29f1cb535831ddd0fbad6e9dfedaa01eef1379` |
|
||||
|
||||
**Structural fixes:**
|
||||
- `qtshadertools` recipe did not exist — only the dangling `recipes/qt/qtshadertools -> ../../local/recipes/qt/qtshadertools` symlink (target missing). Recipe created following the `qt6-sensors` pattern. The target symlink now resolves. Without this, qtdeclarative cannot build.
|
||||
- `qtbase` recipe pointed at 6.8.2 tarball while `local/recipes/qt/qtbase/source/.cmake.conf` already said 6.11.0 — was a contradiction. Now consistent.
|
||||
|
||||
**Patches NOT yet rebased.** Per AGENTS.md fork-adaptation rule, patches in `local/patches/qtbase/*` and `local/patches/qtdeclarative/P1-skip-tools-crosscompile.patch` must be re-applied against the 6.11.1 source tree. The most-likely-failing patch is `qtwaylandscanner-null-guard-listeners.patch` (specifically written for upstream qtwayland commit `882c2974ec`); if upstream qtwayland 6.11.1's equivalent commit is now in 6.11.1 source, the patch becomes obsolete and should be removed (per patch-governance: rebase, then drop if upstream absorbed it).
|
||||
|
||||
### 9.2 Wayland / DRM / Input stack — bump committed
|
||||
|
||||
Commit `7bbf56217e` (`graphics(0.2.5): bump Wayland/DRM/Input/expat/seatd to upstream latest stable`).
|
||||
|
||||
| Recipe | Old pin | New pin | BLAKE3 |
|
||||
|---------------------|---------|---------|------------------------------------------------------------------|
|
||||
| `libwayland` | 1.24.0 | 1.25.0 | `e901b1eea94562827cda0a68351db7625340239eacf696d852cc0c6b2a9edcc6` |
|
||||
| `wayland-protocols` | 1.38 | 1.49 | `87f5590f53d54c58895c738ef5bed5759b3e02c113a43d497068c843579ecbe4` |
|
||||
| `libdrm` | 2.4.125 | 2.4.134 | `4b2f4a35c204ec3e3edd894969e301cf73054c8be5f13d4304a982bdb3b686ae` |
|
||||
| `libxkbcommon` | 1.7.0 | 1.9.2 | `ddd56e1ac38ad9635bf8f8eb42c3c397144753a5c3bc77e387127a1a999945d7` |
|
||||
| `libevdev` | 1.13.2 | 1.13.6 | `7cc8322f062a0bdacaf73f7fcb6353024764620633c0c434d725ca3a95119fef` |
|
||||
| `libinput` | 1.30.2 | 1.31.3 | `ae74b2c2202357119ec0f6e65951a9b2b38332ae5c8c3f59b05f6d80598ef033` |
|
||||
| `seatd-redox` | 0.9.1 | 0.9.3 | `c1653dc2766e90c1fa606869f527085d939e13a84369bfad0f6762deeada152c` |
|
||||
| `expat` | 2.5.0 | 2.8.2 | `eb92ab232e65da01f865df03624a1868c8af2a3fcd45301bb9d58efdf43267fd` |
|
||||
|
||||
Notes:
|
||||
- libxkbcommon: `xkbcommon.org/download` URL has been unreachable since at least 2026 (returns HTML 404). Switched the recipe to the github mirror URL `https://github.com/xkbcommon/libxkbcommon/archive/refs/tags/xkbcommon-1.9.2.tar.gz`. This may need to be revisited if upstream changes its release process.
|
||||
- dbus 1.16.2 == upstream latest, no change.
|
||||
|
||||
**Patches NOT yet rebased.** `local/patches/libdrm/00-xf86drm-redox-header.patch`, `01-virtgpu-drm-header.patch`, `02-redox-dispatch.patch`; `local/patches/libwayland/redox.patch`; the `redox.patch` in `recipes/libs/libevdev/` and `recipes/libs/libinput/` — all assume the older source. Rebase work is open.
|
||||
|
||||
### 9.3 KDE Plasma + Konsole — bump committed
|
||||
|
||||
Commit `3539e621a2` (`kde(0.2.5): bump KWin 6.6.5->6.7.2, kdecoration 6.3.4->6.7.2, konsole 24.08.3->26.04.3`).
|
||||
|
||||
| Recipe | Old pin | New pin | BLAKE3 |
|
||||
|-----------------|---------|---------|------------------------------------------------------------------|
|
||||
| `kwin` | 6.3.4 | 6.7.2 | `0bb8a5a2b1a3214396cde60756b296d9f70d08db4afd673b553a158a2f4bb17d` |
|
||||
| `kdecoration` | 6.3.4 | 6.7.2 | `f9802589d7e61099a4f26b3723c5f54e92e60919d35e6df348f0a7eccf2700de` |
|
||||
| `konsole` | 24.08.3 | 26.04.3 | `6fca3c2ea807ca0e12d014e2f6b5832bed31c2b15a3dac9ec6e28f3599f14930` |
|
||||
|
||||
Note: kde utility versioning convention changed; `konsole` now uses the `v26.04.3` `KDE-Calendar` style.
|
||||
|
||||
**Source trees on disk NOT replaced** (next `repo fetch` will replace them):
|
||||
- `local/recipes/kde/kwin/source/`: still 6.6.5 (prior session imported 6.6.5 source).
|
||||
- `local/recipes/kde/kdecoration/source/`: still 6.3.4.
|
||||
- `local/recipes/kde/konsole/source/`: still 24.08.
|
||||
|
||||
**Patches NOT yet rebased.** `local/patches/kwin/01-initial-migration.patch`, `local/patches/kdecoration/01-initial-migration.patch`, `local/recipes/kde/konsole/01-optional-multimedia-printsupport-core5compat.patch`. The KWin 6.6.5 → 6.7.2 delta (1 minor) is smaller than KF6's (17 minors), but KWin is the largest single-recipe patch surface in the project — patches will need careful review.
|
||||
|
||||
### 9.4 NOT bumped (deliberately)
|
||||
|
||||
- **KF6 6.10 → 6.27:** Per AGENTS.md §Patch Governance and the recipe-by-recipe fork-adaptation rule, a commit that bumps `recipe.toml` URLs to upstream versions whose **patch surface has not been rebased** is a dishonest commit — it lies about the actual build state. No `kf6-*` recipe.toml was bumped.
|
||||
- Real work that must happen before any `kf6-*` recipe bump can land: ~38 patch rebases for `local/patches/kf6-*/01-initial-migration.patch` against upstream KF6 6.27.0 source.
|
||||
- **Mesa 24.0.8 → 26.1.4:** still on the redox-os fork rebase plan (0.3.0). Per §3.3.
|
||||
- **SDDM 0.21.0:** already at upstream latest.
|
||||
- **kf6-attica, kf6-prison, kf6-kirigami, etc:** all targeted at v6.27.0 (real upstream latest) but see above.
|
||||
|
||||
### 9.5 Things to do before `./local/scripts/build-redbear.sh redbear-full` can succeed
|
||||
|
||||
In order:
|
||||
1. Per-recipe: rebase `local/patches/<recipe>/*.patch` against the new upstream source. Save rebased versions in place; do not bump `P<N>` numbers; do not delete patches unless upstream absorbed the change.
|
||||
2. `repo fetch` for each bumped recipe (now that recipe.toml points at new URLs).
|
||||
3. `touch relibc && make prefix` to refresh relibc stage in the cross-toolchain.
|
||||
4. `repo validate-patches <recipe>` for each.
|
||||
5. Touch-relibc-then-make-prefix between any relibc-aware recipe change (qtbase and friends touch relibc syscalls).
|
||||
6. Re-run `./local/scripts/build-redbear.sh redbear-full` and address new breakage as it surfaces.
|
||||
7. Address KF6 6.27.0 bump (multi-day; multi-week with 38 patch rebases).
|
||||
@@ -476,7 +476,7 @@ build artifacts). Updated `BLAKE3SUMS` with the new checksum.
|
||||
### Acceptance
|
||||
|
||||
- [x] `repo validate-patches relibc` passes all 25 patches
|
||||
- [x] `make all CONFIG_NAME=redbear-full` completes successfully
|
||||
- [x] `./local/scripts/build-redbear.sh redbear-full` completes successfully
|
||||
- [x] QEMU boots to login prompt with virtio-gpu (1280×800) and vesad console (1280×720)
|
||||
- [x] All protected recipes use only archived sources
|
||||
- [x] `diff -ruN` patches apply correctly after normalization
|
||||
|
||||
@@ -506,50 +506,47 @@ local-fork source tree.
|
||||
|
||||
**Problem.** `local/sources/base/` is a nested git repo (the
|
||||
local-fork model) with `origin = https://gitlab.redox-os.org/redox-os/base.git`.
|
||||
**Resolved 2026-07-01:** the Red Bear-specific per-component fork at
|
||||
`vasilito/redbear-os-base` has been migrated to the `submodule/base`
|
||||
branch inside the canonical `RedBear-OS` repo per the SINGLE-REPO RULE
|
||||
(see `local/AGENTS.md`). The base component now lives only as the
|
||||
`submodule/base` branch on `RedBear-OS`. A Red Bear developer who
|
||||
**Resolved 2026-07-01:** the base component has been migrated to the
|
||||
`submodule/base` branch inside the canonical `RedBear-OS` repo per the
|
||||
SINGLE-REPO RULE (see `local/AGENTS.md`). The base component now lives only as
|
||||
the `submodule/base` branch on `RedBear-OS`. A Red Bear developer who
|
||||
commits inside `local/sources/base/` and runs `git push origin master`
|
||||
would push Red Bear fork commits **to upstream Redox**, where they
|
||||
will be rejected (or worse, silently fail).
|
||||
pushes to the `submodule/base` branch of `RedBear-OS`.
|
||||
|
||||
**Current behavior.** Most Red Bear base commits are made by the
|
||||
`Red Bear OS <build@redbearos.org>` author bot during automated syncs, which
|
||||
push to a different fork URL configured out-of-band. The inner-repo
|
||||
`origin` is therefore orphaned from normal operator workflows.
|
||||
**Current behavior.** All 9 declared submodules now point to
|
||||
`https://gitea.redbearos.org/vasilito/RedBear-OS.git` on their
|
||||
`submodule/<component>` branch. The per-component repo era is over.
|
||||
|
||||
**Proposal.** Set the inner `local/sources/base/` origin to Red Bear's gitea
|
||||
URL via `local/scripts/sync-fork-remotes.sh` (new file). Same treatment for
|
||||
`local/sources/{relibc,kernel,bootloader,installer,redoxfs,userutils}` if any
|
||||
of them have the same issue.
|
||||
**Proposal.** Keep `local/scripts/sync-fork-remotes.sh` (or equivalent
|
||||
maintenance) so that no submodule's `origin` drifts back to upstream Redox
|
||||
or to an old per-component repo URL.
|
||||
|
||||
**Expected gain.** Eliminates a footgun. Operators can commit + push from
|
||||
inside the local fork and reach the right remote.
|
||||
|
||||
**Risk.** Low — purely a remote URL change, no history rewrite.
|
||||
|
||||
### 12. Outer repo cannot show inline diffs for `local/sources/base/` (S, ~30 min)
|
||||
### 12. Outer repo shows inline diffs for all `local/sources/<component>/` forks (RESOLVED)
|
||||
|
||||
**Problem.** `local/sources/base/` is a nested git repo (not a real submodule
|
||||
— the outer Red Bear repo has no `.gitmodules` entry for it). The outer
|
||||
repo sees file changes only as `Submodule local/sources/base contains modified
|
||||
content`. `git diff -- local/sources/base/drivers/input/ps2d/src/main.rs`
|
||||
shows nothing useful; only `git diff --submodule=log` shows the commit hash
|
||||
delta, not the actual line changes.
|
||||
**Status.** All local forks (`base`, `bootloader`, `installer`, `kernel`,
|
||||
`libredox`, `redoxfs`, `relibc`, `syscall`, `userutils`) are now declared as
|
||||
proper git submodules in `.gitmodules`, each tracking the
|
||||
`submodule/<component>` branch of the canonical `RedBear-OS` repo.
|
||||
|
||||
This makes PR review of local-fork changes harder than necessary — the
|
||||
reviewer must `cd local/sources/base && git diff` to see what actually changed.
|
||||
**Result.** The outer repo no longer shows opaque "Submodule contains modified
|
||||
content" messages. `git diff` shows submodule commit deltas, and
|
||||
`git diff --submodule=log` shows the per-fork commit summaries. For line-level
|
||||
review, run `git diff` inside the submodule worktree:
|
||||
|
||||
**Proposal.** Either:
|
||||
```bash
|
||||
cd local/sources/<component>
|
||||
git diff origin/submodule/<component>..HEAD
|
||||
```
|
||||
|
||||
- (a) Register `local/sources/base/` (and other inner repos) as proper
|
||||
git submodules via `.gitmodules` + `git submodule absorbgitdirs`. Lets
|
||||
outer-repo `git diff` show the changes inline.
|
||||
- (b) Add a wrapper script `local/scripts/show-fork-diffs.sh` that
|
||||
recursively runs `git diff` inside each `local/sources/<component>/`
|
||||
inner repo and presents the result with the outer-repo diff.
|
||||
**Historical note.** The old `local/sources/base/` was briefly a nested git
|
||||
repo without a `.gitmodules` entry, which made review awkward. That pattern
|
||||
was retired when the single-repo migration completed; see `local/AGENTS.md`
|
||||
§ SINGLE-REPO RULE and § LOCAL FORK MODEL for the current policy.
|
||||
|
||||
**Expected gain.** PR review of local-fork changes becomes trivial.
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
# Cargo Patch Propagation — Fork Dependency Policy
|
||||
|
||||
**Created:** 2026-07-02
|
||||
**Status:** Active policy
|
||||
**Scope:** All Rust recipes that depend on forked packages
|
||||
|
||||
## The Rule
|
||||
|
||||
Every recipe that uses a forked package **MUST** depend on the local fork — never on
|
||||
the upstream/crates.io version.
|
||||
|
||||
```
|
||||
Upstream package (gitlab.redox-os.org / crates.io)
|
||||
│
|
||||
▼
|
||||
Our local fork (local/sources/<package>/)
|
||||
│ (patches applied, version bumps tracked)
|
||||
▼
|
||||
ALL downstream recipes depend on THIS fork
|
||||
```
|
||||
|
||||
### Forked packages (as of 2026-07-02)
|
||||
|
||||
| Package | Local fork | Version | Symlink |
|
||||
|---------|-----------|---------|---------|
|
||||
| `redox_syscall` | `local/sources/syscall/` | 0.8.1 | `recipes/core/base/syscall` |
|
||||
| `libredox` | `local/sources/libredox/` | 0.1.18 | `recipes/core/base/libredox` |
|
||||
|
||||
### Lifecycle: when upstream bumps version
|
||||
|
||||
1. **Update fork first** — `git fetch upstream && git rebase upstream/master` in `local/sources/<package>/`
|
||||
2. **Rebase all patches** — ensure Red Bear patches still apply against the new upstream
|
||||
3. **Rebuild prefix** — `touch <component> && make prefix` if the fork affects the cross-toolchain sysroot
|
||||
4. **All dependents automatically use the updated fork** — no per-recipe changes needed
|
||||
5. **Adapt downstream code** — if the upstream bump changed an API, fix every recipe that breaks (Golden Rule: Red Bear adapts to upstream, never the reverse)
|
||||
|
||||
## Why This Matters
|
||||
|
||||
### The Problem: Cargo `[patch]` doesn't propagate
|
||||
|
||||
Cargo's `[patch.crates-io]` only applies from the **root package** being compiled. When
|
||||
a recipe depends on a base workspace member (like `daemon`) via path dependency, the base
|
||||
workspace's own `[patch]` entries are **silently ignored**.
|
||||
|
||||
This means:
|
||||
|
||||
1. Recipe `redox-drm` depends on `daemon` via `path = ".../base/source/daemon"`
|
||||
2. `daemon` is part of the `base` workspace, which patches `redox_syscall` and `libredox`
|
||||
3. But when cargo compiles `redox-drm` as root, only `redox-drm`'s own `[patch]` applies
|
||||
4. `libredox` from crates.io **vendors its own copy** of `redox_syscall` internally
|
||||
5. Two different `syscall::Error` types exist at compile time → E0277 type mismatch
|
||||
|
||||
### The Fix: Use path dependencies, not version dependencies
|
||||
|
||||
Every recipe that needs `redox_syscall` or `libredox` should use the local fork via path
|
||||
dependency or `[patch.crates-io]` entry. The path is always the same relative depth from
|
||||
`local/recipes/<category>/<name>/source/Cargo.toml`:
|
||||
|
||||
```
|
||||
../../../../../recipes/core/base/syscall → local fork of redox_syscall
|
||||
../../../../../recipes/core/base/libredox → local fork of libredox
|
||||
```
|
||||
|
||||
## How to Make a Recipe Depend on the Fork
|
||||
|
||||
### Option A: Direct path dependency (preferred for direct deps)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
redox_syscall = { path = "../../../../../recipes/core/base/syscall", features = ["std"] }
|
||||
libredox = { path = "../../../../../recipes/core/base/libredox" }
|
||||
```
|
||||
|
||||
### Option B: Version dep + patch redirect (when transitive deps also need redirection)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
redox_syscall = { version = "0.8", features = ["std"] }
|
||||
libredox = "0.1"
|
||||
daemon = { path = "../../../../../recipes/core/base/source/daemon" }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../recipes/core/base/syscall" }
|
||||
libredox = { path = "../../../../../recipes/core/base/libredox" }
|
||||
```
|
||||
|
||||
Use Option B when the recipe depends on ANY base workspace member via path (`daemon`,
|
||||
`scheme-utils`, etc.). The `[patch]` entries redirect ALL transitive `redox_syscall` and
|
||||
`libredox` dependencies (from `redox-scheme`, etc.) to the local fork.
|
||||
|
||||
### Symlinks required
|
||||
|
||||
The paths above resolve through symlinks that must exist:
|
||||
|
||||
```bash
|
||||
recipes/core/base/syscall → ../../../local/sources/syscall
|
||||
recipes/core/base/libredox → ../../../local/sources/libredox
|
||||
```
|
||||
|
||||
These symlinks bridge the base workspace's relative path resolution. Without them, the
|
||||
base workspace's `path = "../syscall"` resolves to a non-existent directory.
|
||||
|
||||
## Validation
|
||||
|
||||
Run the validation gate to check all recipes:
|
||||
|
||||
```bash
|
||||
./target/release/repo validate-cargo-deps
|
||||
```
|
||||
|
||||
This scans every `Cargo.toml` in `recipes/` and `local/recipes/`, detects:
|
||||
- Path dependencies on base workspace members without matching `[patch]` entries
|
||||
- Version dependencies on forked packages that should use the local fork
|
||||
- Missing or broken symlinks
|
||||
|
||||
Exit code 0 = all OK, exit code 1 = warnings found.
|
||||
|
||||
## Recipes Currently Violating This Policy
|
||||
|
||||
As of 2026-07-02, the following recipes pull `redox_syscall` or `libredox` from crates.io
|
||||
instead of the local fork. These are policy violations that should be fixed incrementally:
|
||||
|
||||
### `redox_syscall` from crates.io (25 recipes)
|
||||
|
||||
Recipes using version 0.8 (straightforward conversion to fork):
|
||||
- `drivers/ehcid`, `drivers/linux-kpi`, `drivers/redbear-btusb`
|
||||
- `drivers/redox-driver-sys`
|
||||
- `system/driver-manager`, `system/evdevd`, `system/firmware-loader`
|
||||
- `system/hwrngd`, `system/iommu`
|
||||
- `system/redbear-btctl`, `system/redbear-hwutils`
|
||||
- `system/redbear-sessiond`, `system/redbear-traceroute`
|
||||
- `system/redbear-wifictl`, `system/thermald`, `system/udev-shim`
|
||||
- `tui/tlc`
|
||||
|
||||
Recipes using version 0.7 (need API adaptation to 0.8):
|
||||
- `drivers/virtio-inputd`, `system/devfsd`, `system/diskd`
|
||||
|
||||
Recipes using version 0.4 (need significant API adaptation to 0.8):
|
||||
- `system/redbear-accessibility`, `system/redbear-ime`, `system/redbear-keymapd`
|
||||
- `gpu/redox-drm` (uses `syscall04` for legacy PCI config — separate concern)
|
||||
|
||||
### `libredox` from crates.io (21 recipes)
|
||||
|
||||
All recipes using `libredox = "0.1"` or `libredox = "0.1.x"` from crates.io should
|
||||
switch to the local fork at `local/sources/libredox/` (version 0.1.18).
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. **Phase 1 (done):** Fix `redox-drm` — the only recipe with a path dep on `daemon`
|
||||
2. **Phase 2:** Convert all 0.8.x recipes to use the local fork (straightforward)
|
||||
3. **Phase 3:** Adapt 0.7.x recipes to 0.8.x API and switch to fork
|
||||
4. **Phase 4:** Adapt 0.4.x recipes to 0.8.x API and switch to fork
|
||||
|
||||
Each conversion is: change `version = "0.x"` to `path = "..."`, cook, fix any API breaks.
|
||||
|
||||
## Related
|
||||
|
||||
- `local/AGENTS.md` § "GOLDEN RULE — Red Bear adapts to upstream, never the reverse"
|
||||
- `local/AGENTS.md` § "LOCAL FORK MODEL (CORE COMPONENTS)"
|
||||
- `local/sources/base/Cargo.toml` lines 133-157 — the base workspace `[patch]` section
|
||||
that ONLY applies within the base workspace
|
||||
@@ -3,7 +3,7 @@
|
||||
**Date**: 2026-05-04
|
||||
**Updated**: 2026-05-04 (MSI T1.1–T2.2 implemented, committed, pushed)
|
||||
**Status**: Active — MSI Phase 1 complete, DMA/Scheduler pending
|
||||
**Source of truth**: Linux kernel 7.0 (local/reference/linux-7.0/)
|
||||
**Source of truth**: Linux kernel 7.1 (local/reference/linux-7.1/)
|
||||
|
||||
## 1. Problem Statement
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ Validation and acceptance
|
||||
- the AMD C backend still logs linux-kpi quirk-informed IRQ expectations, but firmware gating is no longer duplicated there.
|
||||
- the PCI quirk extractor foundation has been upgraded so future reviewed GPU quirk imports can rely on explicit handler-body evidence instead of handler-name guessing.
|
||||
|
||||
**What A1 does not mean yet:** reviewed Linux 7.0 PCI extraction has not produced enough high-confidence modern Intel/AMD DRM GPU entries to replace the existing hand-authored GPU quirk set. Additional DRM-focused mining and review are still required before quirk-table expansion claims, and Intel-side quirk expansion remains deferred until the Intel runtime policy surface can consume those flags honestly.
|
||||
**What A1 does not mean yet:** reviewed Linux 7.1 PCI extraction has not produced enough high-confidence modern Intel/AMD DRM GPU entries to replace the existing hand-authored GPU quirk set. Additional DRM-focused mining and review are still required before quirk-table expansion claims, and Intel-side quirk expansion remains deferred until the Intel runtime policy surface can consume those flags honestly.
|
||||
|
||||
**Current PCI ID naming policy:** human-readable PCI vendor/device naming now comes from the shipped
|
||||
canonical `pciids` database, while DRM quirk policy remains on the reviewed Red Bear/Linux-backed
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
**Date**: 2026-05-04
|
||||
**Status**: Authoritative — supersedes CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md, COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md, and HARDWARE-VALIDATION-MATRIX.md
|
||||
**Source of truth**: Linux kernel 7.0 (`local/reference/linux-7.0/`)
|
||||
**Source of truth**: Linux kernel 7.1 (`local/reference/linux-7.1/`)
|
||||
|
||||
---
|
||||
|
||||
@@ -176,7 +176,7 @@ Stream.rs exists (387 lines). NOT runtime-validated.
|
||||
| **D2.4: Streaming DMA** | `dma_map_single`/`dma_unmap_single` in linux-kpi. Allocates temp buffer, copies data, maps through IOMMU. | ~120 | P1 |
|
||||
| **D2.5: SWIOTLB** | Bounce buffer allocation for DMA-limited devices. Linux ref: `kernel/dma/swiotlb.c`. | ~200 | P2 |
|
||||
|
||||
**Linux Reference Summary (from `local/reference/linux-7.0/`):**
|
||||
**Linux Reference Summary (from `local/reference/linux-7.1/`):**
|
||||
|
||||
| Linux API | Purpose | Red Bear Equivalent |
|
||||
|---|---|---|
|
||||
|
||||
@@ -710,7 +710,7 @@ local/patches/relibc/P4-initgroups.patch
|
||||
|-------|---------|
|
||||
| Kernel compiles | `make r.kernel` |
|
||||
| relibc compiles | `make r.relibc` |
|
||||
| Full OS builds | `make all CONFIG_NAME=redbear-full` |
|
||||
| Full OS builds | `./local/scripts/build-redbear.sh redbear-full` |
|
||||
|
||||
### 8.2 Runtime Evidence
|
||||
|
||||
|
||||
@@ -682,7 +682,7 @@ Phase 0 (Patch Recovery) ← BLOCKING FOR ALL OTHERS
|
||||
| Kernel compiles | `make r.kernel` |
|
||||
| relibc compiles | `make r.relibc` |
|
||||
| Prefix rebuilt | `touch relibc kernel && make prefix` |
|
||||
| Full OS builds | `make all CONFIG_NAME=redbear-mini` |
|
||||
| Full OS builds | `./local/scripts/build-redbear.sh redbear-mini` |
|
||||
|
||||
### 6.2 Runtime Evidence (QEMU)
|
||||
|
||||
|
||||
@@ -0,0 +1,959 @@
|
||||
# Red Bear OS Networking and TCP/IP Stack Improvement Plan
|
||||
|
||||
## Purpose
|
||||
|
||||
This document assesses the current network and TCP/IP stack in Red Bear OS for completeness
|
||||
and quality, cross-references it against the Linux 7.1 reference implementation
|
||||
(`local/reference/linux-7.1/`) and upstream Redox OS activity (2024–2026), and defines the
|
||||
next improvement plan in execution order.
|
||||
|
||||
This is the **canonical current implementation plan** for the userspace TCP/IP stack
|
||||
(`netstack` / `smolnetd`), the Ethernet driver family, the POSIX socket surface in relibc,
|
||||
network configuration (`netcfg`, `dhcpd`, `redbear-netctl`), and the runtime properties of
|
||||
the scheme-based packet path.
|
||||
|
||||
When another document discusses the smolnetd daemon, `scheme:tcp` / `scheme:udp` /
|
||||
`scheme:icmp` / `scheme:ip` / `scheme:netcfg`, the `driver-network` trait, the Ethernet
|
||||
driver family (`e1000d`, `ixgbed`, `rtl8139d`, `rtl8168d`, `virtio-netd`), or TCP/IP
|
||||
performance and protocol coverage, prefer this file for:
|
||||
|
||||
- the current robustness judgment,
|
||||
- the current implementation order,
|
||||
- the current validation/proof expectations,
|
||||
- and the current language for build-visible vs runtime-proven vs hardware-validated claims.
|
||||
|
||||
It is grounded in the current repository state, especially:
|
||||
|
||||
- `local/sources/base/netstack/` (the TCP/IP daemon sources)
|
||||
- `local/sources/base/drivers/net/` (Ethernet driver family + `driver-network` library)
|
||||
- `local/sources/base/dhcpd/` (DHCP client)
|
||||
- `local/sources/relibc/src/header/sys_socket/` and `local/sources/relibc/src/platform/redox/socket.rs`
|
||||
- `local/sources/libredox/src/lib.rs` (SocketCall enum)
|
||||
- `local/sources/kernel/src/scheme/{mod,user}.rs` (scheme dispatch)
|
||||
- `local/recipes/drivers/redbear-iwlwifi/` (Intel Wi-Fi transport)
|
||||
- `config/redbear-{netctl,wifi-experimental,bluetooth-experimental,mini,full}.toml`
|
||||
|
||||
Companion plans that share authority over networking-adjacent surfaces:
|
||||
|
||||
- `local/docs/WIFI-IMPLEMENTATION-PLAN.md` — canonical for the Wi-Fi control plane, Intel
|
||||
transport, `redbear-iwlwifi`, `linux-kpi` wireless layer, and `redbear-wifictl`.
|
||||
- `local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` — canonical for the PCI
|
||||
interrupt plumbing, MSI/MSI-X delivery, and IOMMU work that NIC drivers depend on.
|
||||
- `local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md` — canonical for the Bluetooth stack.
|
||||
|
||||
This plan covers the **wired TCP/IP stack and the socket surface**. Where Wi-Fi intersects
|
||||
with the IP datapath (for example, hooking a Wi-Fi link-layer into the `Router`), the
|
||||
Wi-Fi plan owns the wireless-specific detail and this plan owns the integration contract.
|
||||
|
||||
## Validation States
|
||||
|
||||
| State | Meaning |
|
||||
|---|---|
|
||||
| **builds** | Compiles in-tree |
|
||||
| **host-tested** | Tests pass on Linux host with synthesized fixtures |
|
||||
| **qemu-proven** | Behavior confirmed in QEMU with a virtio-net or e1000 NIC |
|
||||
| **validated** | Behavior confirmed on real bare-metal hardware with evidence |
|
||||
| **experimental** | Available for bring-up, not support-promised |
|
||||
| **missing** | No in-tree implementation |
|
||||
|
||||
## Current State
|
||||
|
||||
### Architecture at a Glance
|
||||
|
||||
Red Bear OS, like upstream Redox, has **zero in-kernel networking**. The kernel provides
|
||||
only the scheme dispatch surface (`scheme/user.rs` proxies `open`/`read`/`write`/`dup`/
|
||||
`call` to userspace daemons) and the global `event:` scheme for I/O readiness notification.
|
||||
All TCP/IP logic lives in the userspace `netstack` daemon, built on **smoltcp 0.12.0**.
|
||||
|
||||
```
|
||||
Application (POSIX socket, libstd TcpStream, curl, ssh, ...)
|
||||
│ relibc sys_socket: socket(), connect(), send(), recv(), ...
|
||||
│ → open("/scheme/tcp", ...) → dup(fd, "1.2.3.4:80")
|
||||
▼
|
||||
Kernel scheme router (kernel/src/scheme/user.rs)
|
||||
│ forwards scheme calls to the registered daemon via SQE/CQE IPC
|
||||
▼
|
||||
netstack / smolnetd (local/sources/base/netstack/src/main.rs)
|
||||
│ registers: tcp, udp, icmp, ip, netcfg
|
||||
│ owns: smoltcp::iface::Interface + SocketSet + Router + DeviceList
|
||||
▼
|
||||
Router (netstack/src/router/mod.rs) — implements smoltcp::phy::Device
|
||||
│ poll(): drains EthernetLink.recv() into rx_buffer
|
||||
│ dispatch(): RouteTable longest-prefix lookup → EthernetLink.send()
|
||||
▼
|
||||
EthernetLink (netstack/src/link/ethernet.rs)
|
||||
│ ARP cache (60s TTL), frame encode/decode, neighbor discovery
|
||||
│ synchronous read/write of raw Ethernet frames via the network scheme fd
|
||||
▼
|
||||
Ethernet driver daemon (e1000d, rtl8168d, ixgbed, rtl8139d, virtio-netd)
|
||||
│ implements driver-network::NetworkAdapter
|
||||
│ exposes scheme:network.<driver>_<bus> with raw frame read/write + /mac
|
||||
▼
|
||||
Hardware NIC (PCI MMIO + IRQ + DMA rings)
|
||||
```
|
||||
|
||||
### Status Matrix
|
||||
|
||||
| Area | State | Detail |
|
||||
|---|---|---|
|
||||
| TCP/IP daemon (`netstack`/`smolnetd`) | **builds, qemu-proven** | Single-threaded event loop over `EventQueue`. Owns smoltcp 0.12.0 `Interface` + `SocketSet`. Registers `tcp`, `udp`, `icmp`, `ip`, `netcfg` schemes. Binary aliased as both `netstack` and `smolnetd` for compatibility. |
|
||||
| smoltcp version | **builds** | 0.12.0 with features: `std`, `medium-ethernet`, `medium-ip`, `proto-ipv4`, `socket-raw`, `socket-icmp`, `socket-udp`, `socket-tcp`, `socket-tcp-cubic`, `iface-max-addr-count-8`. **IPv6 NOT enabled.** |
|
||||
| TCP congestion control | **builds** | CUBIC only (smoltcp `socket-tcp-cubic` feature). No BBR, no DCTCP, no per-socket selection. |
|
||||
| TCP feature surface | **partial** | Window scaling, MSS negotiation, RTT estimation, exponential backoff retransmission, keep-alive, Nagle, delayed ACKs (all from smoltcp). **Missing: SACK, TCP timestamps, ECN, TCP Fast Open, MPTCP, urgent pointer.** |
|
||||
| UDP sockets | **builds, qemu-proven** | `scheme:udp`. Peek-and-filter model for connected UDP. Ephemeral port range 49152–65535. |
|
||||
| ICMP | **builds** | `scheme:icmp`, root-only. |
|
||||
| Raw IP | **builds** | `scheme:ip`, root-only. Wraps smoltcp `RawSocket`. |
|
||||
| ARP | **builds** | Hand-implemented in `link/ethernet.rs`. Request/reply, neighbor cache, 60s TTL, 3 retries with 1s silence. IPv4-only (no NDP for IPv6). |
|
||||
| Routing | **builds, qemu-proven** | `router/route_table.rs` — longest-prefix-match over a `Vec<Rule>`. Static routes only, configured via `scheme:netcfg/route/{add,del,list}`. No FIB trie, no policy routing, no multipath, no route daemon. |
|
||||
| Network configuration (`netcfg`) | **builds** | Filesystem-like config scheme: `/resolv/nameserver`, `/route/{list,add,del}`, per-interface `ip`, `mac`, `gateway`. Backed by `BTreeMap` nodes with read/write callbacks. |
|
||||
| DHCP client (`dhcpd`) | **builds, qemu-proven** | Standalone daemon. Reads/writes `scheme:netcfg` to apply leases. Init service `10_dhcpd.service` runs `dhcpd -f`. |
|
||||
| Profile orchestration (`redbear-netctl`) | **builds, host-tested** | Arch-netctl-style profiles in `/etc/netctl/`. Wired/wireless, DHCP/static/bounded IP modes. Init service `12_netctl.service` applies the active profile after `smolnetd`+`dhcpd`. |
|
||||
| DNS resolver | **builds** | In-process inside `netstack` via `dns-parser` crate. `scheme:netcfg/resolv/nameserver` for the recursive resolver address. Quad9 (9.9.9.9) fallback. |
|
||||
| Ethernet drivers | **builds, qemu-proven** | 5 drivers: `e1000d` (Intel 8254x), `ixgbed` (Intel 10GbE 82599), `rtl8139d`, `rtl8168d` (Realtek), `virtio-netd`. All implement `driver-network::NetworkAdapter`. |
|
||||
| Driver packet I/O model | **builds** | Synchronous file read/write of raw Ethernet frames. `NetworkAdapter::{read_packet, write_packet}`. IRQ-driven via `scheme:irq/{}` + `EventQueue`. No NAPI, no batched polling, no interrupt mitigation. |
|
||||
| Wi-Fi IP datapath | **missing** | `redbear-iwlwifi` is control-plane/transport only (see `WIFI-IMPLEMENTATION-PLAN.md`). No 802.11 frame-to-EthernetLink bridge into `netstack`. |
|
||||
| POSIX socket API (relibc) | **builds, qemu-proven** | `socket()`, `bind()`, `connect()`, `listen()`, `accept()`, `send()`, `recv()`, `sendto()`, `recvfrom()`, `sendmsg()`, `recvmsg()`, `getsockopt()`, `setsockopt()`, `shutdown()`, `socketpair()`, `getsockname()`, `getpeername()`. `AF_INET` and `AF_UNIX` only. **No `AF_INET6`.** |
|
||||
| Socket multiplexing | **builds, qemu-proven** | Kernel `scheme:event` (analogous to epoll). `EventQueue` in daemons. `O_NONBLOCK` + `EAGAIN`/`EWOULDBLOCK` semantics on sockets. No `epoll_pwait2`, no io_uring equivalent. |
|
||||
| Bulk FD passing | **partial** | Upstream relibc gained bulk FD passing for `recvmsg`/`sendmsg` in early 2026 (commits `94b0cfc68`, `5e61f17a3`, `1978c1aa4`). **Sync status with our local relibc fork needs verification** — see Upstream Sync. |
|
||||
| Firewall / packet filtering | **missing** | No netfilter, iptables, nftables, or BPF equivalent. No hooks in the IP path. |
|
||||
| Connection tracking | **missing** | No conntrack. No NAT. |
|
||||
| IPv6 | **missing** | smoltcp `proto-ipv6` feature not enabled. `link/ethernet.rs` is IPv4-only (no NDP). relibc socket backend has no `AF_INET6` branch. `route_table.rs` is `IpAddress`-generic but no IPv6 routes are configured. |
|
||||
| Performance offloads | **missing** | No GRO, no GSO, no TSO, no checksum offload, no zero-copy send/recv, no `MSG_ZEROCOPY`, no `sendfile`, no batched packet submission. |
|
||||
| Multi-NIC | **partial** | `Router` supports a `DeviceList` and dispatches per-route, but `main.rs:get_network_adapter()` picks the **first** adapter and warns on multiple (FIXME comment). True multi-homing not wired. |
|
||||
| Virtual network devices | **missing** | No bridge, VLAN, bond, tunnel, VXLAN, tun/tap scheme daemons. |
|
||||
| Traffic control (qdisc) | **missing** | No shaping, policing, QoS, or scheduling discipline. |
|
||||
| Network namespaces | **partial** | Scheme namespaces exist (via `setrens`), used by `smolnetd` to enter the null namespace. Not exposed as a general network isolation primitive. |
|
||||
| Hardware validation (bare metal) | **qemu-proven only** | NIC drivers build and pass QEMU smoke tests. No bare-metal NIC throughput, packet loss, or long-connection validation evidence. |
|
||||
|
||||
### Packet Flow Detail (Receive Path)
|
||||
|
||||
```
|
||||
1. NIC raises IRQ → driver's EventQueue wakes
|
||||
2. Driver::handle_irq() → drains NIC RX ring → packet into driver-side buffer
|
||||
3. NetworkScheme<T> posts fevent(EVENT_READ) on the network scheme fd
|
||||
4. smolnetd's EventQueue wakes on EventSource::Network
|
||||
5. smolnetd::on_network_scheme_event() → EthernetLink.recv(now)
|
||||
6. EthernetLink reads raw frame via network_file.read(), parses EthernetRepr
|
||||
7. If IPv4: returns payload to Router.poll() which enqueues into rx_buffer
|
||||
8. If ARP: processes locally (neighbor cache update, may send reply)
|
||||
9. smolnetd calls smoltcp Interface.poll(now, &mut Router, &mut SocketSet)
|
||||
10. smoltcp processes IP/TCP/UDP state machines, fills socket buffers
|
||||
11. Socket handlers post fevent on tcp/udp scheme fds → applications wake
|
||||
12. Application calls read(fd) → kernel forwards to TcpScheme::read_buf()
|
||||
13. TcpScheme::read_buf() → smoltcp TcpSocket::recv_slice()
|
||||
```
|
||||
|
||||
### Packet Flow Detail (Transmit Path)
|
||||
|
||||
```
|
||||
1. Application calls write(fd, data) → kernel forwards to TcpScheme::write_buf()
|
||||
2. TcpScheme::write_buf() → smoltcp TcpSocket::send_slice()
|
||||
3. smolnetd event loop calls smoltcp Interface.poll(now, &mut Router, &mut SocketSet)
|
||||
4. smoltcp decides to transmit → Router.transmit(now) returns TxToken
|
||||
5. smoltcp builds IP packet into TxToken buffer → Router.tx_buffer.enqueue()
|
||||
6. smolnetd calls Router.dispatch(now)
|
||||
7. Router.dispatch() parses Ipv4Packet, looks up RouteTable rule
|
||||
8. If source address mismatch: rewrites src + refills checksum
|
||||
9. Calls EthernetLink.send(next_hop, packet, now)
|
||||
10. EthernetLink.send(): ARP neighbor cache lookup
|
||||
- Hit: builds Ethernet frame, writes to network_file
|
||||
- Miss: enqueues into waiting_packets, sends ARP request
|
||||
11. Driver receives write() on network scheme → pushes to NIC TX ring → DMA → wire
|
||||
```
|
||||
|
||||
## Gap Analysis vs Linux 7.1
|
||||
|
||||
Linux 7.1 (`local/reference/linux-7.1/`) is the reference for production-grade networking.
|
||||
The table below maps every major Linux networking subsystem to its Red Bear equivalent and
|
||||
identifies the gap.
|
||||
|
||||
| Linux 7.1 Subsystem | Linux Location | Red Bear Equivalent | Gap Severity |
|
||||
|---|---|---|---|
|
||||
| **In-kernel TCP/IP** | `net/ipv4/`, `net/ipv6/` (~80k LoC) | Userspace `netstack` daemon (smoltcp) | Architectural difference, not a gap per se. Microkernel design. |
|
||||
| **`sk_buff` packet metadata** | `include/linux/skbuff.h` (5,462 LoC) | Raw `[u8]` buffers, no metadata struct | **High** — no zero-copy across layers, no per-packet metadata propagation. |
|
||||
| **NAPI (interrupt→poll)** | `include/linux/netdevice.h:381`, `napi_schedule()` | IRQ-per-packet + `EventQueue` wake | **High** — interrupt storm risk under load. No batched polling. |
|
||||
| **GRO / GSO / TSO** | `net/core/gro.c`, `net/core/gso.c`, NIC driver offload | None | **High** — every packet traverses the full stack individually. |
|
||||
| **BIG TCP (512KB GSO/GRO)** | `net/ipv6/ip6_output.c`, `net/ipv4/ip_output.c` | None | Medium — relevant only at 100GbE+, not near-term. |
|
||||
| **Pluggable congestion control** | `include/net/tcp.h:1325` `struct tcp_congestion_ops`, `net/ipv4/tcp_cubic.c`, `tcp_bbr.c`, `tcp_dctcp.c` | smoltcp `socket-tcp-cubic` feature (CUBIC only) | **High** — no BBR for WAN throughput, no DCTCP for datacenter, no per-socket selection. |
|
||||
| **SACK (Selective ACK)** | `net/ipv4/tcp_input.c` | Not in smoltcp | Medium — hurts lossy links. |
|
||||
| **TCP timestamps** | `net/ipv4/tcp_output.c` | Not in smoltcp | Low — RTT estimation works without them but less precisely. |
|
||||
| **ECN (Explicit Congestion Notification)** | `net/ipv4/tcp_input.c` | Not in smoltcp | Medium — datacenter relevance. |
|
||||
| **MSG_ZEROCOPY send** | `net/ipv4/tcp.c:1140-1326`, `net/core/skbuff.c:1719-1806` | None | **High** — every send copies user→kernel→daemon. |
|
||||
| **sendfile / splice** | `net/core/skbuff.c:3257` `skb_splice_bits()` | None | Medium — relevant for static file servers. |
|
||||
| **io_uring zero-copy receive** | `io_uring/zcrx.c`, `IORING_OP_RECV_ZC` (`include/uapi/linux/io_uring.h:314`) | None | Low (long-term) — Linux 7.x reaches 187 Gbps with this; not near-term for Red Bear. |
|
||||
| **epoll** | `fs/eventpoll.c` (~3,000 LoC), `ep_poll_callback()` | Kernel `scheme:event` + `EventQueue` | Medium — functionally equivalent but less optimized; no ready-list integration with socket wakeup path. |
|
||||
| **XDP / AF_XDP** | `net/xdp/xsk.c`, BPF programs in driver context | None | Low (long-term) — line-rate BPF filtering. |
|
||||
| **eBPF everywhere** | `net/core/filter.c`, `bpf_tcp_ca.c`, XDP, TC, socket filter | None | Medium — no programmable packet processing. |
|
||||
| **Netfilter (5 hooks)** | `include/uapi/linux/netfilter.h:43`, `struct nf_hook_ops` | None | **High** — no firewall, no NAT, no packet interception layer. |
|
||||
| **Conntrack** | `net/netfilter/nf_conntrack_core.c` (2,790 LoC), `struct nf_conn` | None | **High** — no stateful firewall, no NAT. |
|
||||
| **nftables / iptables** | `net/netfilter/nf_tables_api.c`, `xt_*.c` | None | High — no user-facing firewall CLI. |
|
||||
| **FIB (LC-trie routing)** | `include/net/ip_fib.h`, `net/ipv4/fib_trie.c`, `fib_semantics.c` | `router/route_table.rs` — `Vec<Rule>` linear scan | Medium — fine for tens of routes; inadequate for full BGP table. |
|
||||
| **Policy routing / multipath** | `net/ipv4/fib_rules.c`, `fib_select_multipath()` | None | Low — niche outside routers. |
|
||||
| **IPv6 (full stack)** | `net/ipv6/` (40+ .c files), built-in only in 7.1 | Not enabled (smoltcp `proto-ipv6` off, no NDP, no `AF_INET6`) | **Critical** — IPv6 is mandatory for modern networking. |
|
||||
| **cfg80211 / mac80211 / nl80211** | `include/net/cfg80211.h` (10,995 LoC), `net/mac80211/` (60+ files), `include/uapi/linux/nl80211.h` | `linux-kpi` wireless shim (Rust), `redbear-iwlwifi` transport (C), `redbear-wifictl` scheme | See `WIFI-IMPLEMENTATION-PLAN.md`. **Control-plane only; no IP datapath bridge yet.** |
|
||||
| **`net_device` + `net_device_ops`** | `include/linux/netdevice.h:2139` (5,737 LoC), ~50 ops | `driver-network::NetworkAdapter` trait (4 methods) | **High** — no features negotiation, no offload reporting, no statistics, no queue selection. |
|
||||
| **Multi-queue / RSS / RPS / RFS / XPS** | `net/core/dev.c`, per-CPU queues | None | Medium — relevant for >1Gbps. |
|
||||
| **`page_pool` memory recycling** | `net/core/page_pool.c` | None | Medium — allocator overhead on RX. |
|
||||
| **Cacheline-aware structs** | `include/linux/netdevice.h` `_cacheline_group` annotations | None | Low — micro-optimization. |
|
||||
| **Network namespaces** | 67 namespace-aware subdirs under `net/` | Scheme namespaces (partial) | Medium — relevant for container story. |
|
||||
| **Bridge / VLAN / bond / tunnel** | `net/bridge/`, `net/8021q/`, `net/bonding/`, `net/ipv4/ip_tunnel.c` | None | Medium — virtualization/networking appliance relevance. |
|
||||
| **Traffic control (qdisc)** | `net/sched/` (100+ files), `include/net/sch_generic.h` | None | Low — QoS is niche for a desktop OS. |
|
||||
| **MPTCP** | `net/mptcp/` | Not in smoltcp | Low — niche. |
|
||||
| **Hardware offloads (checksum, TSO, LRO, TLS, IPsec)** | NIC driver `features`, `include/linux/netdevice.h` NETIF_F_* | None | Medium — drivers don't even advertise capabilities. |
|
||||
| **Routing daemons (BGP/OSPF/RIP)** | Userspace (FRRouting, bird) | None | Low — not a kernel/daemon gap. |
|
||||
| **Drop reasons / observability** | `include/net/dropreason.h`, `skb->drop_reason` | Logging only | Low — debugging aid. |
|
||||
|
||||
### Severity Summary
|
||||
|
||||
- **Critical (blocks modern use):** IPv6, firewall/NAT (netfilter equivalent).
|
||||
- **High (major performance or functionality gap):** `sk_buff`-equivalent metadata, NAPI-style polling, GRO/GSO/TSO, MSG_ZEROCOPY, pluggable congestion control, multi-NIC.
|
||||
- **Medium (matters for specific workloads):** SACK, ECN, FIB scaling, multi-queue, virtual devices, observability.
|
||||
- **Low (long-tail polish):** TCP timestamps, policy routing, MPTCP, io_uring ZC, qdisc.
|
||||
|
||||
## Upstream Redox Sync Opportunities (2024–2026)
|
||||
|
||||
Red Bear OS is forked from a frozen Redox snapshot. Upstream Redox has been actively evolving
|
||||
its networking stack. The following upstream commits should be evaluated for import into the
|
||||
Red Bear local forks. **Per the Golden Rule (Red Bear adapts to upstream, never the reverse),
|
||||
these are imports to track and apply, not workarounds.**
|
||||
|
||||
### Already in Red Bear (verified)
|
||||
|
||||
- ✅ smoltcp 0.12.0 with `socket-tcp-cubic` (upstream `b92be2e7d`, `d7c128684`, 2025-03-09)
|
||||
- ✅ Named network adapters via `scheme:network.<driver>_<bus>` (upstream `f9b3170f0`, 2024-02-28)
|
||||
- ✅ `netstack` merged into `base` recipe (upstream `c06e5b14e`, 2025-03-10)
|
||||
- ✅ MAC address fetched from network scheme (upstream `674f5b6d7`, 2024-02-28)
|
||||
- ✅ `dhcpd` moved into `base` (upstream `b68e5a685`, 2026-04-11)
|
||||
- ✅ New scheme format, no legacy paths (upstream `bafdb3b66`, 2024-07-11)
|
||||
|
||||
### To Evaluate for Import
|
||||
|
||||
| Upstream Commit | Date | Description | Red Bear Action |
|
||||
|---|---|---|---|
|
||||
| `redox-os/drivers@ad9305bf9` | 2024-02-28 | Unified network drivers under `net/` with shared scheme | Verify our driver layout matches. |
|
||||
| `redox-os/drivers@921c6b07f` | 2024-12-26 | `driver-network` migrated to `redox-scheme` crate | **Import** — modernizes the scheme protocol. |
|
||||
| `redox-os/drivers@0f24975ff` | 2025-11-29 | PCI interrupts rework: dedup vector handling for RTL drivers | **Import** — IRQ quality (see IRQ plan). |
|
||||
| `redox-os/drivers@dd41c4f13` | 2025-11-23 | Net: scheme created and daemon ready before device init | **Import** — fixes race on early opens. |
|
||||
| `redox-os/drivers@407533201` | 2025-09-24 | Add ThinkPad T60 ethernet to `e1000d` PCI IDs | **Import** — broader hardware. |
|
||||
| `redox-os/relibc@94b0cfc68` | 2026-02-08 | Reimplement `recvmsg`/`sendmsg` using bulk FD passing | **Verify + import** — check our relibc fork has this; critical for `SCM_RIGHTS`. |
|
||||
| `redox-os/relibc@5e61f17a3` | 2026-02-08 | `MSG_CMSG_CLOEXEC` handling in `recvmsg` | **Import** alongside the above. |
|
||||
| `redox-os/relibc@1978c1aa4` | 2026-03-25 | Full `recvmsg` implementation | **Import**. |
|
||||
| `redox-os/relibc@cab002146` | 2026-02-28 | Move protocols into `libredox` | **Major refactor — evaluate carefully.** Affects SocketCall enum location. |
|
||||
| `redox-os/relibc@443145fde` | 2025-11-07 | Use `recvmsg`/`sendmsg` when `recvfrom`/`sendto` has flags | **Import**. |
|
||||
| `redox-os/relibc@9eaa9e82b` | 2025-11-08 | Pass through all `SOL_SOCKET` options, fix `getsockopt` option_len | **Import** — fixes option handling bugs. |
|
||||
| `redox-os/relibc@6a455159a` | 2026-01-22 | Fix `getaddrinfo` infinite loop hang | **Critical import** — known crash. |
|
||||
| `redox-os/relibc@726a0fb1a` | 2025-09-15 | `getaddrinfo` NULL nodename, `AI_PASSIVE`, `AI_NUMERICHOST` | **Import**. |
|
||||
| `redox-os/relibc@5334455a2` | 2025-08-20 | `getnameinfo` + `getaddrinfo` loopback | **Import**. |
|
||||
| `redox-os/relibc@d44010170` | 2025-07-18 | UDS `bind`/`connect` with RedoxFS integration | **Import** — fixes Unix domain sockets. |
|
||||
| `redox-os/relibc@6dd10a1f1` | 2025-08-01 | Fix `connect` on Redox | **Import**. |
|
||||
| `redox-os/relibc@9c6701802` | 2026-01-07 | Add `sys_socket` tests | **Import** — regression coverage. |
|
||||
| `redox-os/syscall@7a1409a91` | 2026-05-27 | Multiple FDs variant for `call` and `std_fs_call` | **Import** — enables bulk FD passing. |
|
||||
| `redox-os/syscall@178461f6f` | 2025-12-27 | Remove remnants of old packet format | **Import** — cleanup. |
|
||||
| `redox-os/kernel@c089667ad` | 2025-12-13 | Remove legacy packet user schemes | **Import** — kernel side of the above. |
|
||||
| `redox-os/kernel@6c3d5d28c` | 2026-07-01 | Move FD allocation logic into userspace | **Major refactor — evaluate carefully.** Most recent kernel change. |
|
||||
| `redox-os/kernel@08ea1da2f` | 2025-07-05 | Trigger read event for user schemes on fd close | **Import** — fixes socket cleanup races. |
|
||||
| `redox-os/kernel@4ff82ad8b` | 2025-11-27 | Fix user scheme deadlocks in `call_extended_inner` | **Critical import** — fixes hangs under load. |
|
||||
| `redox-os/kernel@99ff55ee1` | 2026-02-08 | Demux results as soon as received from user scheme | **Import** — latency improvement. |
|
||||
| `redox-os/redox@85b62fd85` | 2026-03-01 | Support networking in all configs | **Reference** — verify our configs cover networking in all targets. |
|
||||
| `redox-os/redox@193e81897` | 2026-02-17 | ~115 WIP networking recipes (HTTP, FTP, SSH, VPN, P2P) | **Cherry-pick** — many of these are now relevant for `cub`. |
|
||||
| `redox-os/netutils@6df5af955` | 2024-11-27 | Basic `ifconfig` command | **Reference** — `redbear-netstat` covers this, but compare APIs. |
|
||||
| `redox-os/netutils@62f96a4b5` | 2026-05-22 | Fix `nc` listener exiting on stdin EOF | **Import if we ship nc.** |
|
||||
|
||||
### Upstream Gaps Red Bear Fills Independently
|
||||
|
||||
These exist in Red Bear but **not** in upstream Redox:
|
||||
|
||||
- Wi-Fi driver support (`redbear-iwlwifi`, `linux-kpi` wireless layer, `redbear-wifictl`)
|
||||
- `redbear-netctl` profile orchestration (Arch netctl-style)
|
||||
- `redbear-netstat`, `redbear-mtr`, `redbear-nmap`, `redbear-traceroute`
|
||||
- AMD GPU networking offload path (via `redox-drm`)
|
||||
|
||||
## Improvement Plan
|
||||
|
||||
The plan is organized into six phases. Each phase has a clear success criterion, dependencies,
|
||||
and validation target. Phases are ordered by **dependency** (early phases unblock later ones)
|
||||
and by **impact** (critical gaps first).
|
||||
|
||||
### Phase 0: Upstream Sync and Foundation Hardening
|
||||
|
||||
**Goal:** Bring the Red Bear local forks to current upstream networking state before adding
|
||||
new features. This avoids building on stale foundations.
|
||||
|
||||
**Duration:** 2–3 weeks.
|
||||
|
||||
**Workstreams:**
|
||||
|
||||
0.1 **relibc socket fixes.** Apply the upstream commits listed in "To Evaluate for Import"
|
||||
that touch relibc's `sys_socket`, `getaddrinfo`, `getnameinfo`, `recvmsg`/`sendmsg`,
|
||||
`connect`, `bind`, and `SOL_SOCKET` option pass-through. **Priority: the `getaddrinfo`
|
||||
infinite loop fix (`6a455159a`).** Verify against the `sys_socket` test suite.
|
||||
|
||||
0.2 **Kernel scheme dispatch fixes.** Apply `4ff82ad8b` (deadlock fix), `08ea1da2f`
|
||||
(close event), `99ff55ee1` (latency). These are direct kernel-fork patches.
|
||||
|
||||
0.3 **Driver-side modernization.** Apply `921c6b07f` (`driver-network` → `redox-scheme`),
|
||||
`dd41c4f13` (scheme-before-device-init race fix), `0f24975ff` (PCI interrupts rework).
|
||||
Test each driver in QEMU after applying.
|
||||
|
||||
0.4 **Config audit.** Verify all `redbear-*.toml` targets that should have networking
|
||||
actually pull in `redbear-netctl`, `smolnetd`, `dhcpd`, and a NIC driver. Reference
|
||||
upstream `85b62fd85`.
|
||||
|
||||
0.5 **Bulk FD passing verification.** Check whether the local relibc fork already has
|
||||
commits `94b0cfc68` and `1978c1aa4`. If not, import them. Validate `SCM_RIGHTS`
|
||||
works end-to-end with a test daemon.
|
||||
|
||||
**Success Criteria:**
|
||||
- All listed upstream commits either applied or documented as "intentionally skipped" with
|
||||
a reason.
|
||||
- `redbear-mini` boots in QEMU, acquires DHCP, and `curl http://example.com/` succeeds.
|
||||
- `redbear-netstat` shows the TCP connection in `ESTABLISHED` state.
|
||||
- `sys_socket` tests pass on the relibc fork.
|
||||
|
||||
**Validation target:** qemu-proven.
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: Multi-NIC and Driver Feature Surface
|
||||
|
||||
**Goal:** Remove the single-NIC FIXME and give the stack visibility into driver capabilities.
|
||||
|
||||
**Duration:** 3–4 weeks.
|
||||
|
||||
**Dependencies:** Phase 0 complete.
|
||||
|
||||
**Workstreams:**
|
||||
|
||||
1.1 **Multi-NIC in `netstack`.** Replace `get_network_adapter()` (which picks the first NIC
|
||||
and warns) with proper multi-interface support. The `Router` already has a `DeviceList`;
|
||||
the gap is in `main.rs` initialization and in `EthernetLink` instantiation per adapter.
|
||||
Reference Linux's `net_device` list semantics. Each NIC gets its own `EthernetLink` in
|
||||
the `DeviceList`, with its own ARP cache and MAC.
|
||||
|
||||
1.2 **`NetworkAdapter` trait expansion.** The current trait is 4 methods:
|
||||
```rust
|
||||
pub trait NetworkAdapter {
|
||||
fn mac_address(&mut self) -> [u8; 6];
|
||||
fn available_for_read(&mut self) -> usize;
|
||||
fn read_packet(&mut self, buf: &mut [u8]) -> Result<Option<usize>>;
|
||||
fn write_packet(&mut self, buf: &[u8]) -> Result<usize>;
|
||||
}
|
||||
```
|
||||
Expand to expose capabilities and statistics, modeled on Linux's
|
||||
`struct net_device_ops` and `ethtool_ops`:
|
||||
```rust
|
||||
pub trait NetworkAdapter {
|
||||
fn mac_address(&mut self) -> [u8; 6];
|
||||
fn available_for_read(&mut self) -> usize;
|
||||
fn read_packet(&mut self, buf: &mut [u8]) -> Result<Option<usize>>;
|
||||
fn write_packet(&mut self, buf: &[u8]) -> Result<usize>;
|
||||
|
||||
// New: capability negotiation
|
||||
fn capabilities(&self) -> NicCapabilities; // MTU, offloads, max queues
|
||||
fn mtu(&self) -> usize { 1500 }
|
||||
fn link_state(&self) -> LinkState; // Up / Down / Unknown
|
||||
fn statistics(&self) -> NicStatistics; // rx/tx bytes, packets, errors, drops
|
||||
}
|
||||
```
|
||||
Wire `NicCapabilities` into `netstack` so `Router` knows the real MTU (currently
|
||||
hardcoded to 1486 in `router/mod.rs:42`).
|
||||
|
||||
1.3 **`scheme:netcfg` interface enumeration.** Expose `netcfg/ifaces` listing all
|
||||
`DeviceList` entries with their MAC, IP, link state, statistics, and MTU. This gives
|
||||
`redbear-netstat` and `redbear-netctl` a proper interface table.
|
||||
|
||||
1.4 **Per-driver statistics.** Each driver (`e1000d`, `rtl8168d`, etc.) should expose
|
||||
RX/TX byte/packet/error counters through the new `statistics()` method. Reference
|
||||
Linux's `ndo_get_stats64`.
|
||||
|
||||
**Success Criteria:**
|
||||
- `redbear-netstat -i` lists all NICs present (in QEMU: just `virtio-net` or `e1000`; on
|
||||
bare metal: all detected NICs).
|
||||
- A second NIC can be brought up with a static IP via `redbear-netctl` and routes traffic.
|
||||
- `Router::MTU` is no longer a hardcoded constant — it derives from the active NIC's
|
||||
reported MTU.
|
||||
- Driver statistics are non-zero after a ping flood.
|
||||
|
||||
**Validation target:** qemu-proven for multi-NIC (two virtio-net interfaces); host-tested
|
||||
for the trait expansion (unit tests on the new trait).
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: IPv6
|
||||
|
||||
**Goal:** Bring the stack to modern IP standards. IPv6 is no longer optional for a
|
||||
general-purpose OS.
|
||||
|
||||
**Duration:** 4–6 weeks.
|
||||
|
||||
**Dependencies:** Phase 1.1 (multi-NIC) helpful but not strictly required. Phase 0
|
||||
(relibc freshness) required.
|
||||
|
||||
**Workstreams:**
|
||||
|
||||
2.1 **Enable smoltcp IPv6.** In `netstack/Cargo.toml`, add `"proto-ipv6"` to the smoltcp
|
||||
features list. Audit the build for new warnings/errors — smoltcp's IPv6 surface is
|
||||
large. Reference: smoltcp `wire` module already has full IPv6 parsing.
|
||||
|
||||
2.2 **`link/ethernet.rs` IPv6 support.** Currently the link layer is IPv4-only: the ARP
|
||||
cache uses `Ipv4Address`, `process_arp` only handles `ArpRepr::EthernetIpv4`, and
|
||||
`send_to` only handles `IpAddress::Ipv4`. Add NDP (Neighbor Discovery Protocol,
|
||||
RFC 4861) for IPv6 neighbor discovery, mirroring the ARP logic. Use smoltcp's
|
||||
`wire::{Icmpv6Repr, Icmpv6Packet, NdiscRepr}` types.
|
||||
|
||||
2.3 **`router/route_table.rs` IPv6.** The `Rule` struct already uses `IpCidr` and
|
||||
`IpAddress`, which are version-generic. The gap is in `dispatch()` (which only does
|
||||
`Ipv4Packet::new_checked`) and in `set_src_addr` checksum refill. Add an IPv6 branch.
|
||||
IPv6 has no header checksum (unlike IPv4), so the checksum logic is simpler — only
|
||||
transport-layer pseudo-header checksums need attention.
|
||||
|
||||
2.4 **relibc `AF_INET6`.** In `local/sources/relibc/src/platform/redox/socket.rs`, add a
|
||||
branch for `AF_INET6` that opens `/scheme/tcp` and `/scheme/udp` with IPv6-encoded
|
||||
addresses. The scheme path encoding `dup(fd, "[2001:db8::1]:443")` should work once
|
||||
`netstack` parses IPv6 endpoints (smoltcp's `parse_endpoint` already does).
|
||||
|
||||
2.5 **`netcfg` IPv6 surfaces.** Add `netcfg/resolv/nameserver6`, per-interface IPv6
|
||||
address configuration, and IPv6 route add/del. Reference: Linux `addrconf.c`
|
||||
(SLAAC) and `ndisc.c` (router advertisements).
|
||||
|
||||
2.6 **DHCPv6.** Either extend `dhcpd` or add a sibling `dhcpv6d` daemon. DHCPv6 (RFC 8415)
|
||||
is a separate protocol from DHCPv4 — UDP port 546 (client) / 547 (server), different
|
||||
message format. Alternatively, implement SLAAC (RFC 4862) as the default IPv6
|
||||
autoconfiguration method.
|
||||
|
||||
2.7 **`redbear-netctl` IPv6 profiles.** Extend the profile format to support `IP6=dhcp`,
|
||||
`IP6=slaac`, `IP6=static`, `Address6=`, `Gateway6=`, `DNS6=`.
|
||||
|
||||
2.8 **Validation.** Boot `redbear-mini` in QEMU with `qemu-system-x86_64 ... -netdev
|
||||
user,id=n0,ipv6=on -device virtio-net,netdev=n0`. Verify `ping6 ::1`, `curl -6
|
||||
https://ipv6.google.com`, and a dual-stack SSH connection.
|
||||
|
||||
**Success Criteria:**
|
||||
- `socket(AF_INET6, SOCK_STREAM, 0)` returns a valid fd.
|
||||
- `ping6 <router-advertised-address>` succeeds.
|
||||
- A dual-stack TCP server accepts both IPv4 and IPv6 connections.
|
||||
- `redbear-netstat -r` shows both IPv4 and IPv6 routes.
|
||||
|
||||
**Validation target:** qemu-proven for basic IPv6; host-tested for NDP and DHCPv6
|
||||
protocol correctness.
|
||||
|
||||
**Risk:** This is the largest single workstream. The `link/ethernet.rs` IPv6 work is
|
||||
non-trivial because NDP is more complex than ARP (solicitation-node multicast, router
|
||||
discovery, prefix information options). Consider implementing NDP as a separate
|
||||
`link/ndp.rs` module rather than complicating `ethernet.rs`.
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: TCP Performance and Congestion Control
|
||||
|
||||
**Goal:** Bring TCP throughput and latency into the same order of magnitude as Linux for
|
||||
common workloads.
|
||||
|
||||
**Duration:** 6–8 weeks.
|
||||
|
||||
**Dependencies:** Phase 1 (capabilities surface) for any hardware offload wiring.
|
||||
|
||||
**Workstreams:**
|
||||
|
||||
3.1 **Buffer pool audit.** `netstack/src/buffer_pool.rs` exists but its usage should be
|
||||
audited. TCP socket buffers are currently `vec![0; 0xffff]` (64KB) per socket in
|
||||
`scheme/tcp.rs:80-83`. For high-bandwidth connections, this is a throughput cap.
|
||||
Make the buffer size configurable via `setsockopt(SO_SNDBUF/SO_RCVBUF)` (the scheme
|
||||
already has stubs at `tcp.rs:14-15` but `set_setting` returns `Ok(0)`). Reference
|
||||
Linux's `tcp_rmem`/`tcp_wmem` sysctl defaults.
|
||||
|
||||
3.2 **Pluggable congestion control interface.** smoltcp 0.12 has `socket-tcp-cubic` as a
|
||||
feature flag but no runtime selection. Two options:
|
||||
|
||||
**Option A (recommended):** Add `setsockopt(TCP_CONGESTION, "cubic")` support to the
|
||||
TCP scheme, backed by a `congestion_control` field on `SocketFile`. Since smoltcp
|
||||
compiles CC at build time via features, this requires either (a) compiling both
|
||||
Reno and CUBIC and switching at runtime (if smoltcp supports it), or (b) forking
|
||||
smoltcp to expose a `CongestionController` trait object. Check whether smoltcp 0.12's
|
||||
`socket-tcp-cubic` is additive to the default Reno controller.
|
||||
|
||||
**Option B:** Contribute a BBR-style controller upstream to smoltcp and enable it
|
||||
via a Red Bear patch. This is higher-effort but higher-impact.
|
||||
|
||||
3.3 **SACK (Selective Acknowledgment).** smoltcp does not implement SACK (RFC 2018).
|
||||
On lossy links (wireless, WAN), SACK dramatically improves throughput by allowing the
|
||||
receiver to acknowledge non-contiguous blocks. This requires either (a) upstreaming
|
||||
SACK to smoltcp, or (b) carrying a Red Bear patch against smoltcp. Given the Golden
|
||||
Rule (prefer upstream), the path is: contribute to smoltcp, carry a local patch
|
||||
until merged. Reference: Linux `net/ipv4/tcp_sack.c`.
|
||||
|
||||
3.4 **Receive batching (NAPI-equivalent).** The current receive path processes one packet
|
||||
per `EventSource::Network` event. Under load, this means one scheme-IPC round-trip
|
||||
per packet. Add a drain loop in `on_network_scheme_event()` that reads packets until
|
||||
`available_for_read()` returns 0 (or a budget cap). This mirrors Linux's NAPI
|
||||
`budget` parameter. Reference: Linux `napi_complete_done()` and the `budget` pattern
|
||||
in `include/linux/netdevice.h`.
|
||||
|
||||
3.5 **Transmit batching.** Similarly, `Router::dispatch()` already drains `tx_buffer`
|
||||
in a loop, but each `dev.send()` is a synchronous write to the network scheme fd.
|
||||
Batch multiple frames into a single write if the driver supports it (requires the
|
||||
`capabilities()` expansion from Phase 1.2).
|
||||
|
||||
3.6 **`MSG_ZEROCOPY` investigation.** True zero-copy requires shared memory between the
|
||||
application and `netstack`. This is architecturally significant in a microkernel —
|
||||
it means the scheme IPC must support passing buffer references, not just data copies.
|
||||
This is a research workstream: evaluate whether the kernel's `scheme:memory` can be
|
||||
used to establish a shared ring buffer between an application and `netstack`.
|
||||
Reference: Linux `msg_zerocopy_alloc()` and the `ubuf_info` mechanism, and the
|
||||
Redox "ring buffers" NLnet-funded project mentioned in the Development Priorities
|
||||
2025/26 document. **Do not implement in this phase — produce a design doc.**
|
||||
|
||||
**Success Criteria:**
|
||||
- `iperf3` (port the recipe) between two QEMU VMs shows ≥100 Mbps on virtio-net
|
||||
(current baseline unknown — establish it first).
|
||||
- `setsockopt(SO_RCVBUF, ...)` affects the actual receive buffer size (verify with
|
||||
`getsockopt`).
|
||||
- SACK enabled connections recover from induced packet loss (use `tc netem` in a
|
||||
Linux-to-RedBear bridge, or a QEMU packet loss model).
|
||||
- A design doc for zero-copy exists at `local/docs/NETWORK-ZEROCOPY-DESIGN.md`.
|
||||
|
||||
**Validation target:** qemu-proven for throughput and buffer sizing; host-tested for
|
||||
SACK protocol correctness.
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Firewall and Packet Filtering
|
||||
|
||||
**Goal:** Provide a firewall and NAT capability, respecting the microkernel
|
||||
everything-is-a-scheme design.
|
||||
|
||||
**Duration:** 6–10 weeks.
|
||||
|
||||
**Dependencies:** Phase 1 (interface awareness). Phase 2 (IPv6) for dual-stack filtering.
|
||||
|
||||
**Workstreams:**
|
||||
|
||||
4.1 **Architecture decision.** In Linux, netfilter hooks live inside the kernel IP path.
|
||||
In Red Bear, the IP path is in `netstack`'s `Router` and `EthernetLink`. The
|
||||
microkernel-aligned approach is to add **filter hooks in `netstack`'s `Router`**
|
||||
that consult a `scheme:firewall` (or `scheme:nf`) daemon. This keeps policy in
|
||||
userspace and matches Redox's design philosophy. Two designs to evaluate:
|
||||
|
||||
**Design A (in-process):** Add a `FirewallEngine` inside `netstack` that reads rules
|
||||
from `scheme:netcfg/firewall/{rules,chains}`. Simpler, faster (no IPC per packet),
|
||||
but couples policy to the stack daemon.
|
||||
|
||||
**Design B (out-of-process):** `netstack` calls out to a `firewalld` daemon via
|
||||
scheme IPC for each packet. Cleaner separation but adds latency per packet.
|
||||
Mitigated by a rule cache in `netstack` with invalidation via fevent.
|
||||
|
||||
**Recommendation:** Design A for the hot path (rule lookup), with rules managed by a
|
||||
separate `scheme:firewall` daemon that writes to `netcfg/firewall/*`. This matches
|
||||
how `route` and `resolv` already work (config via `netcfg`, enforced in `netstack`).
|
||||
|
||||
4.2 **Hook points.** Define 5 hook points mirroring Linux netfilter, adapted to the
|
||||
`Router`/`EthernetLink` boundary:
|
||||
- `PRE_ROUTING` — after `EthernetLink::recv()`, before `Router` enqueue.
|
||||
- `LOCAL_IN` — after `Router` decides packet is for this host.
|
||||
- `FORWARD` — after `Router` decides packet is to be forwarded (requires IP forwarding
|
||||
support, currently missing — see Phase 6).
|
||||
- `LOCAL_OUT` — after `Router::dispatch()` dequeues, before `EthernetLink::send()`.
|
||||
- `POST_ROUTING` — just before `EthernetLink::send()`.
|
||||
|
||||
4.3 **Rule format.** Define a TOML or filesystem-based rule format under
|
||||
`netcfg/firewall/`. Example:
|
||||
```toml
|
||||
# /etc/firewall/rules.toml (or via scheme:netcfg/firewall/rules)
|
||||
[[chain]]
|
||||
name = "input"
|
||||
[[chain.rule]]
|
||||
action = "accept"
|
||||
proto = "tcp"
|
||||
dest_port = 22
|
||||
source = "192.168.0.0/16"
|
||||
[[chain.rule]]
|
||||
action = "drop"
|
||||
proto = "tcp"
|
||||
dest_port = 22
|
||||
```
|
||||
|
||||
4.4 **Connection tracking (conntrack).** For stateful rules (`ACCEPT ESTABLISHED,
|
||||
RELATED`), implement a connection tracker inside `netstack` (or a separate daemon).
|
||||
Track `(src_ip, src_port, dst_ip, dst_port, proto, state)` tuples with timeouts.
|
||||
Reference: Linux `struct nf_conn` (`include/net/netfilter/nf_conntrack.h:74`).
|
||||
|
||||
4.5 **NAT.** Build on conntrack. Source NAT (masquerade) rewrites `src_addr` in
|
||||
`Router::dispatch()` and records the mapping in conntrack. Destination NAT (port
|
||||
forwarding) rewrites `dst_addr` in `PRE_ROUTING`. This requires the checksum refill
|
||||
logic that already exists for IPv4 src rewrites — extend it to handle dst rewrites
|
||||
and (eventually) port rewrites in the transport pseudo-header.
|
||||
|
||||
4.6 **`redbear-firewall` CLI.** A `redbear-firewall` tool (or extend `redbear-netctl`)
|
||||
that reads/writes `scheme:netcfg/firewall/*` and provides `iptables`-like or
|
||||
`nft`-like ergonomics. Reference: Linux `iptables(8)` and `nft(8)`.
|
||||
|
||||
4.7 **Validation.** Tests: (a) drop all inbound TCP except 22, verify only SSH connects;
|
||||
(b) masquerade a Red Bear VM behind another Red Bear VM's NAT, verify outbound
|
||||
connections work and inbound connections to the inner VM fail without port forward;
|
||||
(c) port-forward 8080 to an inner VM's 80, verify a curl to the gateway:8080 reaches
|
||||
the inner VM.
|
||||
|
||||
**Success Criteria:**
|
||||
- A basic ruleset (accept loopback, accept established, accept SSH, drop rest) can be
|
||||
loaded via `redbear-firewall` and is enforced.
|
||||
- SNAT masquerade works for outbound traffic from a NAT'd subnet.
|
||||
- DNAT port-forwarding works for at least one TCP port.
|
||||
- `redbear-netstat -t` shows conntrack entries for active connections.
|
||||
|
||||
**Validation target:** qemu-proven for all three test scenarios; host-tested for
|
||||
conntrack state machine correctness.
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Driver Coverage and Hardware Validation
|
||||
|
||||
**Goal:** Expand the Ethernet driver family to cover common bare-metal hardware and
|
||||
validate the stack on real NICs.
|
||||
|
||||
**Duration:** Ongoing (parallel with Phases 2–4).
|
||||
|
||||
**Dependencies:** Phase 0 (driver modernization). Phase 1 (capabilities surface).
|
||||
|
||||
**Workstreams:**
|
||||
|
||||
5.1 **Import upstream `alxd`.** Upstream Redox has an `alxd` driver (Atheros L2 Fast
|
||||
Ethernet) that Red Bear does not. Import it. Reference: `redox-os/drivers/net/alxd/`.
|
||||
|
||||
5.2 **Common consumer NIC coverage.** The current family misses several common consumer
|
||||
chipsets. Prioritize by deployment frequency:
|
||||
- **`igb` / `igc`** (Intel I219/I225 on modern laptops — NUC, ThinkPad, Dell) —
|
||||
**High priority.** The `e1000d` PCI ID list (`10ec:1004, 100e, 100f, 109a, 1503`)
|
||||
does not cover the I219/I225 (PCI IDs `8086:15d8`, `8086:15f2`, etc.). Either
|
||||
extend `e1000d` or add a new `igbd` driver.
|
||||
- **`r8125`** (Realtek 2.5GbE on modern motherboards) — Medium priority.
|
||||
`rtl8168d` covers 8168/8169 but not 8125 (`10ec:8125`).
|
||||
- **`e1000e`** (Intel I219/I218 Gigabit) — the PCI IDs `8086:1502, 1503, 1559`
|
||||
overlap with `e1000d` but the register interface differs. Verify coverage.
|
||||
|
||||
5.3 **Bare-metal validation harness.** Establish a bare-metal test bench with:
|
||||
- An Intel NIC (e1000 or igb) and a Realtek NIC (rtl8168) on the same machine.
|
||||
- A Linux host on the other end of the link for `iperf3`, `tcpdump`, and packet
|
||||
injection.
|
||||
- Test matrix: boot, DHCP acquire, ping, iperf3 TCP throughput, iperf3 UDP packet
|
||||
loss, long-running TCP connection (1 hour), rapid connection churn (1000
|
||||
connects/sec).
|
||||
|
||||
5.4 **Wi-Fi IP datapath bridge.** Once `redbear-iwlwifi` reaches runtime validation
|
||||
(per `WIFI-IMPLEMENTATION-PLAN.md`), add the datapath bridge: a Wi-Fi link-layer
|
||||
adapter that implements `driver-network::NetworkAdapter` and feeds 802.11 frames
|
||||
(after 802.11-to-Ethernet header conversion) into `netstack`'s `DeviceList`. This
|
||||
makes Wi-Fi a first-class `Router` device. Coordinate with the Wi-Fi plan.
|
||||
|
||||
**Success Criteria:**
|
||||
- `alxd` builds and is packaged.
|
||||
- At least one modern Intel NIC (I219 or I225) is supported and validated.
|
||||
- Bare-metal iperf3 throughput is documented for at least one NIC.
|
||||
- (Stretch) Wi-Fi TCP connection works end-to-end through `netstack`.
|
||||
|
||||
**Validation target:** validated for at least one bare-metal NIC.
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: Forwarding, Virtual Devices, and Advanced Features
|
||||
|
||||
**Goal:** Bring the stack to feature parity with a general-purpose Linux networking
|
||||
appliance for the features that matter.
|
||||
|
||||
**Duration:** 8–16 weeks (parallel, long-term).
|
||||
|
||||
**Dependencies:** Phases 2 (IPv6), 4 (firewall/conntrack for NAT foundation).
|
||||
|
||||
**Workstreams:**
|
||||
|
||||
6.1 **IP forwarding.** When `netstack` receives a packet whose destination is not a local
|
||||
IP, it currently drops it (no `FORWARD` hook in the `Router`). Add a forwarding path:
|
||||
look up the destination in the route table, and if the next-hop is via a different
|
||||
interface than the ingress interface, re-queue the packet for that interface. Decrement
|
||||
the IPv4 TTL (or IPv6 Hop Limit) and drop at 0. Reference: Linux `ip_forward()` in
|
||||
`net/ipv4/ip_forward.c`. Gate behind a `netcfg/forwarding` enable flag.
|
||||
|
||||
6.2 **Bridge scheme daemon.** A `bridged` userspace daemon that implements an L2 learning
|
||||
bridge (MAC learning table, STP optionally). Exposes `scheme:bridge/<name>` and
|
||||
connects multiple `scheme:network/*` devices. The bridge itself registers as a
|
||||
`scheme:network/bridge0` so `netstack` treats it like any other NIC. Reference:
|
||||
Linux `net/bridge/br_device.c` and `struct net_bridge`.
|
||||
|
||||
6.3 **VLAN support.** Add 802.1Q VLAN tag parsing in `EthernetLink` (or a separate
|
||||
`VlanLink` wrapper). Expose `scheme:network/<iface>.<vlan>` virtual devices. Reference:
|
||||
Linux `net/8021q/`.
|
||||
|
||||
6.4 **Bonding / link aggregation.** A `bondd` daemon implementing LACP (802.3ad) or
|
||||
active-backup. Higher-effort; defer until multi-NIC (Phase 1) is solid.
|
||||
|
||||
6.5 **TUN/TAP scheme daemon.** A `tund`/`tapd` that exposes a virtual interface to
|
||||
userspace — read/write packets from an application. Useful for VPN daemons (WireGuard,
|
||||
OpenVPN). Reference: Linux `drivers/net/tun.c`.
|
||||
|
||||
6.6 **Routing daemon support.** Once the FIB/route table supports sufficient scale, port
|
||||
a routing daemon (bird or FRRouting) so Red Bear can participate in dynamic routing.
|
||||
This is userspace-only work; no `netstack` changes needed beyond performance.
|
||||
|
||||
6.7 **`sk_buff`-equivalent metadata struct.** Introduce a `Packet` struct in `netstack`
|
||||
that carries metadata (ingress interface, VLAN tag, receive timestamp, mark/label for
|
||||
firewall) alongside the raw bytes. This replaces the current `[u8]`-only model and
|
||||
enables future features (QoS marking, policy routing on metadata, observability).
|
||||
Reference: Linux `struct sk_buff` (`include/linux/skbuff.h`). **This is a significant
|
||||
internal refactor and should be staged carefully.**
|
||||
|
||||
**Success Criteria:**
|
||||
- A Red Bear VM routes packets between two subnets at >100 Mbps.
|
||||
- A bridge connects two VMs on the same L2 segment.
|
||||
- VLAN-tagged traffic is correctly classified.
|
||||
- (Stretch) WireGuard establishes a tunnel between two Red Bear VMs.
|
||||
|
||||
**Validation target:** qemu-proven for forwarding, bridge, VLAN; experimental for bonding
|
||||
and TUN/TAP.
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
### Observability
|
||||
|
||||
Throughout all phases, maintain and extend the `redbear-netstat` tool to surface new
|
||||
state:
|
||||
- Phase 1: per-interface statistics table.
|
||||
- Phase 2: IPv6 neighbor cache (`ndp -a` equivalent), IPv6 routes.
|
||||
- Phase 3: TCP socket details (cwnd, rtt, retransmits — from smoltcp socket introspection).
|
||||
- Phase 4: conntrack table (`conntrack -L` equivalent), firewall rule hit counters.
|
||||
- Phase 6: bridge MAC table, VLAN mappings.
|
||||
|
||||
Add a `redbear-tcpdump` or `scheme:netcfg/pcap` capability for packet capture. A
|
||||
userspace packet capture daemon that taps the `PRE_ROUTING` hook (Phase 4) and writes
|
||||
pcap files would be invaluable for debugging.
|
||||
|
||||
### Testing
|
||||
|
||||
- **Host-tested:** smoltcp already has extensive upstream tests. Add Red Bear-specific
|
||||
tests for: NDP state machine, firewall rule evaluation, conntrack state transitions,
|
||||
NAT mapping correctness.
|
||||
- **QEMU-tested:** Establish a standard QEMU networking test image with two NICs,
|
||||
configurable network topology, and a test runner that boots Red Bear, runs a series
|
||||
of network commands, and checks output. Reference: Linux's `tools/testing/selftests/net/`.
|
||||
- **Bare-metal validated:** The Phase 5 harness. Document results in
|
||||
`local/docs/networking-validation-log.md`.
|
||||
|
||||
### Documentation
|
||||
|
||||
This plan should be updated when:
|
||||
- A phase is started or completed (update the status matrix).
|
||||
- An upstream commit is imported or intentionally skipped (update the sync table).
|
||||
- A design decision is made (e.g., the zero-copy design doc from Phase 3.6).
|
||||
- A validation milestone is reached.
|
||||
|
||||
## Validation Evidence Requirements
|
||||
|
||||
Per the project's evidence policy, claims in this plan are not complete without:
|
||||
|
||||
| Claim Type | Required Evidence |
|
||||
|---|---|
|
||||
| "builds" | `repo cook recipes/core/base` succeeds; `netstack` binary present in stage. |
|
||||
| "qemu-proven" | QEMU boot log showing the daemon start, the scheme registration, and the test command succeeding (e.g., `curl -v` output). |
|
||||
| "host-tested" | `cargo test` output from the relevant crate (relibc, netstack, redbear-netctl). |
|
||||
| "validated" | Bare-metal test report: hardware model, NIC model, kernel/driver versions, test commands, and results. Stored in `local/docs/networking-validation-log.md`. |
|
||||
|
||||
## Risk Register
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|---|---|---|---|
|
||||
| smoltcp 0.12 IPv6 surface incomplete | Medium | High (blocks Phase 2) | Spike first: enable `proto-ipv6`, audit compilation errors and missing APIs before committing to Phase 2. |
|
||||
| SACK requires smoltcp fork | High | Medium (Phase 3.3) | Engage upstream smoltcp maintainers early. Carry a local patch as fallback. |
|
||||
| Zero-copy incompatible with scheme IPC | Medium | High (Phase 3.6) | Treat as research only in Phase 3. Do not block other Phase 3 work on it. |
|
||||
| Firewall in-process vs out-of-process debate stalls Phase 4 | Medium | High | Decision criterion: rule-lookup latency. Benchmark Design A vs Design B with a 1000-rule ruleset before committing. |
|
||||
| Bare-metal NIC unavailable for Phase 5 | Medium | Medium | Prioritize QEMU validation. Document bare-metal as stretch. |
|
||||
| Upstream relibc refactor (`cab002146` "move protocols into libredox") conflicts with local patches | Medium | High | Evaluate early in Phase 0. If the refactor is large, defer to a dedicated sync sprint rather than mixing with feature work. |
|
||||
|
||||
## Execution Order Summary
|
||||
|
||||
```
|
||||
Phase 0 (upstream sync, 2-3w) ──┬── Phase 1 (multi-NIC, capabilities, 3-4w) ──┬── Phase 3 (TCP perf, CC, 6-8w)
|
||||
│ │
|
||||
│ ├── Phase 5 (drivers, HW validation, ongoing)
|
||||
│ │
|
||||
└── Phase 2 (IPv6, 4-6w) ──────────────────────┤
|
||||
│
|
||||
└── Phase 4 (firewall, NAT, 6-10w)
|
||||
│
|
||||
└── Phase 6 (forwarding, virtual devices, 8-16w)
|
||||
```
|
||||
|
||||
**Critical path:** Phase 0 → Phase 1 → Phase 3 (TCP perf is the highest user-visible impact).
|
||||
**Parallel track:** Phase 2 (IPv6) can proceed alongside Phase 3 once Phase 0 is done.
|
||||
**Enabling track:** Phase 4 (firewall) unblocks Phase 6 (forwarding/NAT).
|
||||
**Long tail:** Phase 5 (hardware validation) and Phase 6 (virtual devices) run continuously.
|
||||
|
||||
## Appendix A: File Inventory
|
||||
|
||||
### TCP/IP Stack Core (`local/sources/base/netstack/`)
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `src/main.rs` | Daemon entry: event loop, scheme fd setup, `Smolnetd::new()` |
|
||||
| `src/scheme/mod.rs` | `Smolnetd` struct: owns `Interface`, `SocketSet`, `Router`, all scheme handlers |
|
||||
| `src/scheme/socket.rs` | Generic `SocketScheme<SocketT>`: open/dup/read/write/fcntl/fevent |
|
||||
| `src/scheme/tcp.rs` | `TcpScheme` over smoltcp `TcpSocket`. Port range 49152–65535. |
|
||||
| `src/scheme/udp.rs` | `UdpScheme` over smoltcp `UdpSocket`. Connected-UDP peek filter. |
|
||||
| `src/scheme/icmp.rs` | `IcmpScheme` over smoltcp `IcmpSocket`. Root-only. |
|
||||
| `src/scheme/ip.rs` | `IpScheme` over smoltcp `RawSocket`. Root-only. |
|
||||
| `src/scheme/netcfg/mod.rs` | `NetCfgScheme`: filesystem-like config (routes, DNS, interfaces). |
|
||||
| `src/scheme/netcfg/nodes.rs` | `cfg_node!` macro for declaring config tree nodes. |
|
||||
| `src/scheme/netcfg/notifier.rs` | fevent notification on config change. |
|
||||
| `src/link/mod.rs` | `LinkDevice` trait + `DeviceList` container. |
|
||||
| `src/link/ethernet.rs` | `EthernetLink`: ARP, neighbor cache (60s TTL), frame I/O. **IPv4-only.** |
|
||||
| `src/link/loopback.rs` | Loopback `LinkDevice`. |
|
||||
| `src/router/mod.rs` | `Router`: smoltcp `phy::Device` impl. RX/TX packet buffers. MTU=1486. |
|
||||
| `src/router/route_table.rs` | `RouteTable`: `Vec<Rule>` longest-prefix-match. |
|
||||
| `src/buffer_pool.rs` | Buffer pool (audit usage). |
|
||||
| `src/port_set.rs` | Ephemeral port allocation (49152–65535). |
|
||||
| `src/error.rs` | Error types. |
|
||||
| `src/logger.rs` | `redox_log` integration. Logger name: `"smolnetd"`. |
|
||||
| `Cargo.toml` | smoltcp 0.12.0 features, redox-scheme/daemon/event deps. |
|
||||
|
||||
### Driver Family (`local/sources/base/drivers/net/`)
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `driver-network/src/lib.rs` | `NetworkAdapter` trait (4 methods) + `NetworkScheme<T>` wrapper. |
|
||||
| `e1000d/src/{main,device}.rs` | Intel 8254x. PCI BAR0 MMIO. IRQ-driven. |
|
||||
| `e1000d/config.toml` | PCI IDs: 8086:1004, 100e, 100f, 109a, 1503. |
|
||||
| `ixgbed/src/{main,device}.rs` | Intel 82599 10GbE. |
|
||||
| `rtl8139d/src/{main,device}.rs` | Realtek RTL8139. |
|
||||
| `rtl8168d/src/{main,device}.rs` | Realtek RTL8168/8169. |
|
||||
| `virtio-netd/src/{main,scheme}.rs` | VirtIO net. DMA-backed rx/tx queues. |
|
||||
|
||||
### Socket Layer (`local/sources/relibc/`)
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `src/header/sys_socket/mod.rs` | POSIX socket API: socket/bind/connect/listen/accept/send/recv/... |
|
||||
| `src/header/sys_socket/constants.rs` | SOCK_STREAM, SOCK_DGRAM, AF_INET, SOL_SOCKET, SO_* constants. |
|
||||
| `src/platform/redox/socket.rs` | Redox backend: AF_INET→`/scheme/tcp`, dup-based connect/bind. |
|
||||
| `src/header/netdb/mod.rs` | getaddrinfo, getnameinfo, gethostbyname, h_errno. |
|
||||
|
||||
### Protocol Layer (`local/sources/libredox/`)
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `src/lib.rs:909` | `SocketCall` enum: Bind/Connect/SetSockOpt/GetSockOpt/SendMsg/RecvMsg/Unbind/GetToken/GetPeerName/Shutdown. |
|
||||
|
||||
### Kernel Scheme Dispatch (`local/sources/kernel/`)
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `src/scheme/mod.rs` | Scheme registry: global schemes + UserScheme dispatch. |
|
||||
| `src/scheme/user.rs` | `UserScheme`: forwards open/read/write/dup/call to userspace daemons. |
|
||||
|
||||
### Configuration
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `config/redbear-netctl.toml` | Netctl profile examples (wired-DHCP, wired-static, wifi-dhcp, wifi-open). Init service `12_netctl.service`. |
|
||||
| `config/redbear-wifi-experimental.toml` | Wi-Fi experimental target (includes `redbear-iwlwifi`). |
|
||||
| `config/redbear-mini.toml` | Mini target: includes `redbear-netctl`, `redbear-netstat`, `redbear-wifictl`, `smolnetd` service. |
|
||||
| `config/redbear-full.toml` | Full desktop target. |
|
||||
| `/etc/init.d/10_smolnetd.service` | smolnetd (notify type). Requires `00_pcid-spawner`. |
|
||||
| `/etc/init.d/10_dhcpd.service` | dhcpd (oneshot_async). Requires `10_smolnetd`. |
|
||||
|
||||
### Custom Red Bear Networking Tools (`local/recipes/system/`)
|
||||
|
||||
| Package | Purpose |
|
||||
|---|---|
|
||||
| `redbear-netctl` | Network profile orchestration (Arch netctl-style). |
|
||||
| `redbear-netctl-console` | TUI frontend for `redbear-netctl`. |
|
||||
| `redbear-netstat` | Socket/interface/routing statistics. |
|
||||
| `redbear-wifictl` | Wi-Fi control daemon + scheme. |
|
||||
| `redbear-mtr` | MTR (traceroute + ping). |
|
||||
| `redbear-nmap` | Port scanner. |
|
||||
| `redbear-traceroute` | Traceroute. |
|
||||
|
||||
## Appendix B: Linux 7.1 Reference File Map
|
||||
|
||||
For cross-referencing during implementation, the key Linux 7.1 source locations:
|
||||
|
||||
| Subsystem | Linux Path |
|
||||
|---|---|
|
||||
| Socket layer | `net/socket.c` (3,822 LoC) |
|
||||
| TCP | `net/ipv4/tcp.c` (5,382 LoC), `tcp_input.c`, `tcp_output.c` |
|
||||
| UDP | `net/ipv4/udp.c` (3,892 LoC) |
|
||||
| IP input/output | `net/ipv4/ip_input.c`, `ip_output.c` |
|
||||
| IPv6 | `net/ipv6/` (40+ files), always built-in in 7.1 |
|
||||
| FIB routing | `net/ipv4/fib_trie.c`, `fib_semantics.c`, `include/net/ip_fib.h` |
|
||||
| Netfilter hooks | `include/uapi/linux/netfilter.h:43`, `struct nf_hook_ops` |
|
||||
| Conntrack | `net/netfilter/nf_conntrack_core.c`, `include/net/netfilter/nf_conntrack.h:74` |
|
||||
| nftables | `net/netfilter/nf_tables_api.c`, 57+ `nft_*.c` |
|
||||
| Congestion control | `include/net/tcp.h:1325` `struct tcp_congestion_ops`, `tcp_cubic.c`, `tcp_bbr.c` |
|
||||
| NAPI | `include/linux/netdevice.h:381` `struct napi_struct` |
|
||||
| GRO/GSO | `net/core/gro.c`, `net/core/gso.c` |
|
||||
| Zero-copy send | `net/ipv4/tcp.c:1140`, `net/core/skbuff.c:1719` |
|
||||
| `sk_buff` | `include/linux/skbuff.h` (5,462 LoC) |
|
||||
| `net_device` | `include/linux/netdevice.h:2139` (5,737 LoC) |
|
||||
| `net_device_ops` | `include/linux/netdevice.h:1443` |
|
||||
| Wireless (cfg80211) | `include/net/cfg80211.h` (10,995 LoC), `net/wireless/core.c` |
|
||||
| Wireless (mac80211) | `net/mac80211/` (60+ files) |
|
||||
| nl80211 | `include/uapi/linux/nl80211.h` (9,080 LoC) |
|
||||
| epoll | `fs/eventpoll.c` (~3,000 LoC) |
|
||||
| io_uring networking | `io_uring/net.c`, `io_uring/zcrx.c`, `IORING_OP_RECV_ZC` |
|
||||
|
||||
## Appendix C: Upstream Redox Networking Activity (2024–2026)
|
||||
|
||||
Selected high-relevance commits from `redox-os/*` repositories. Full list maintained in the
|
||||
sync tracking issue.
|
||||
|
||||
### Kernel
|
||||
|
||||
- `6c3d5d28c` (2026-07-01) Move FD allocation logic into userspace
|
||||
- `c089667ad` (2025-12-13) Remove legacy packet user schemes
|
||||
- `4ff82ad8b` (2025-11-27) Fix user scheme deadlocks in `call_extended_inner`
|
||||
- `08ea1da2f` (2025-07-05) Trigger read event for user schemes on fd close
|
||||
- `99ff55ee1` (2026-02-08) Demux results as soon as received from user scheme
|
||||
|
||||
### Netstack (now in `base`)
|
||||
|
||||
- `b92be2e7d` (2025-03-09) Update to smoltcp 0.12
|
||||
- `d7c128684` (2025-03-09) Use CUBIC as TCP congestion controller
|
||||
- `640e54897` (2024-09-04) Use 0.0.0.0 as default IP (DHCP fix)
|
||||
- `49abe218a` (2024-11-26) ARP fix: respond only to device's broadcast/unicast
|
||||
- `8e2d02232` (2024-03-18) Switch to libredox and redox-event
|
||||
- `bafdb3b66` (2024-07-11) New scheme format, no legacy paths
|
||||
- `f9b3170f0` (2024-02-28) Named network adapters
|
||||
- `c06e5b14e` (2025-03-10) Netstack moved to base repo
|
||||
|
||||
### Drivers
|
||||
|
||||
- `ad9305bf9` (2024-02-28) Unify network drivers under `net/`
|
||||
- `921c6b07f` (2024-12-26) driver-network migrated to redox-scheme
|
||||
- `dd41c4f13` (2025-11-23) Net: scheme created before device init
|
||||
- `0f24975ff` (2025-11-29) PCI interrupts rework5 (RTL dedup)
|
||||
- `407533201` (2025-09-24) Add ThinkPad T60 ethernet to e1000d
|
||||
|
||||
### relibc
|
||||
|
||||
- `94b0cfc68` (2026-02-08) Reimplement recvmsg/sendmsg with bulk FD passing
|
||||
- `1978c1aa4` (2026-03-25) Full recvmsg implementation
|
||||
- `cab002146` (2026-02-28) Move protocols into libredox
|
||||
- `6a455159a` (2026-01-22) Fix getaddrinfo infinite loop hang
|
||||
- `726a0fb1a` (2025-09-15) getaddrinfo NULL nodename, AI_PASSIVE
|
||||
- `5334455a2` (2025-08-20) getnameinfo + getaddrinfo loopback
|
||||
- `9eaa9e82b` (2025-11-08) Pass through all SOL_SOCKET options
|
||||
- `d44010170` (2025-07-18) UDS bind/connect with RedoxFS
|
||||
- `9c6701802` (2026-01-07) Add sys_socket tests
|
||||
|
||||
### syscall
|
||||
|
||||
- `7a1409a91` (2026-05-27) Multiple FDs variant for call (bulk FD passing)
|
||||
- `178461f6f` (2025-12-27) Remove remnants of old packet format
|
||||
|
||||
### Build/config
|
||||
|
||||
- `85b62fd85` (2026-03-01) Support networking in all configs
|
||||
- `193e81897` (2026-02-17) ~115 WIP networking recipes
|
||||
- `b68e5a685` (2026-04-11) Move dhcpd from netutils to base
|
||||
|
||||
---
|
||||
|
||||
**Document version:** 1.0 (2026-07-07)
|
||||
**Authority:** Canonical for TCP/IP stack, socket surface, Ethernet drivers, and network
|
||||
configuration. See companion plans for Wi-Fi, IRQ, and Bluetooth.
|
||||
@@ -78,7 +78,7 @@ After ANY change to the patches list or patch files:
|
||||
2. Full rebuild: `REDBEAR_ALLOW_PROTECTED_FETCH=1 CI=1 make r.base`
|
||||
3. Verify NO "FAILED" or "rejects" in output
|
||||
4. Verify all expected binaries in stage: `ls stage/usr/bin/ stage/usr/lib/drivers/`
|
||||
5. Full image build: `CI=1 make all CONFIG_NAME=redbear-full`
|
||||
5. Full image build: `./local/scripts/build-redbear.sh redbear-full`
|
||||
|
||||
## Known Issues
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ list.
|
||||
| `20-usb.toml` | 147 USB controller entries (logically mirrors `usb_table.rs`) |
|
||||
| `30-net.toml` | Network controllers (Realtek + Broadcom) |
|
||||
| `30-storage.toml` | (file does not exist — see `40-storage.toml`) |
|
||||
| `40-storage.toml` | 214 USB mass-storage quirks (mined from Linux 7.0 `unusual_devs.h`) |
|
||||
| `40-storage.toml` | 214 USB mass-storage quirks (mined from Linux 7.1 `unusual_devs.h`) |
|
||||
| `50-system.toml` | System-level / BIOS quirks |
|
||||
| `60-i2c-hid.toml` | I2C HID recovery quirks |
|
||||
| `70-ucsi.toml` | Type-C / UCSI quirks |
|
||||
|
||||
@@ -220,7 +220,7 @@ description = "SND1 Storage"
|
||||
flags = ["ignore_residue"]
|
||||
```
|
||||
|
||||
The full 214-entry table lives in `quirks.d/30-storage.toml`, mined from Linux 7.0's
|
||||
The full 214-entry table lives in `quirks.d/30-storage.toml`, mined from Linux 7.1's
|
||||
`drivers/usb/storage/unusual_devs.h`.
|
||||
|
||||
Available C quirk flag macros (defined in `linux/pci.h`):
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# Building Rich Red Bear Ratatui Apps — Patterns Guide
|
||||
|
||||
**Created:** 2026-06-20
|
||||
**Last updated:** 2026-06-20 (added §13 ratatui 0.30 best-practices update)
|
||||
**Last updated:** 2026-07-06 (added §15–§18: braille graphs, modal dialogs, key audit, redbear-power v1.44 status)
|
||||
**Source:** Extracted from TLC (Twilight Commander) production codebase — 46k+ lines of pure Rust ratatui
|
||||
**Audience:** Developers porting or building TUI apps for Red Bear OS
|
||||
**ratatui version:** 0.29 baseline (TLC), 0.30 update notes added §13
|
||||
**ratatui version:** 0.29 baseline (TLC), 0.30 for new apps
|
||||
**Cross-references:**
|
||||
- `local/recipes/system/redbear-power/` (production ratatui 0.30 consumer)
|
||||
- `local/recipes/system/redbear-power/` (production ratatui 0.30 consumer, 13,091 LoC, 27 modules, 217 tests)
|
||||
- `local/recipes/tui/tlc/` (46k+ LoC TUI file manager, ratatui 0.29)
|
||||
- `local/docs/redbear-power-improvement-plan.md` (Phase 2 roadmap derived from this doc)
|
||||
- `local/docs/redbear-power-improvement-plan.md` (improvement roadmap)
|
||||
- `local/docs/bottom-vs-redbear-power-assessment.md` (bottom comparison assessment)
|
||||
|
||||
---
|
||||
|
||||
@@ -754,7 +755,7 @@ fn main() -> Result<()> {
|
||||
|
||||
---
|
||||
|
||||
## Summary: 10 Rules for Red Bear Ratatui Apps
|
||||
## Summary: 13 Rules for Red Bear Ratatui Apps
|
||||
|
||||
1. **Theme-driven colors** — every render path takes `&Theme`, never hardcodes colors
|
||||
2. **Poll-based event loop** — `rustix::event::poll` with 100ms timeout for animations
|
||||
@@ -766,6 +767,9 @@ fn main() -> Result<()> {
|
||||
8. **Shared theme crate** — `redbear-tui-theme` for brand consistency
|
||||
9. **No platform gates** — `cfg(unix)` only, same binary on Linux + Redox
|
||||
10. **Test with TestBackend** — snapshot-style UI tests + thorough unit tests
|
||||
11. **Braille graphs via Canvas** — `Marker::Braille` + `RingHistory::display_max()` for stable y-axis
|
||||
12. **Modal dialogs with Cell** — `Cell<usize>` for interior mutability; `Widget for &Dialog`
|
||||
13. **Audit key bindings** — `grep Key::Char` + `sort | uniq -c | sort -rn` after every change
|
||||
|
||||
---
|
||||
|
||||
@@ -1090,45 +1094,33 @@ Use the canonical pattern from §1 (poll + sleep).
|
||||
| `Box<dyn Widget>` | Yes | Yes | **`Box<dyn WidgetRef>`** (unstable) |
|
||||
| `frame.buffer_mut()` | Yes | Yes | Stable |
|
||||
| Modular crates | Single crate | Split (3-4 crates) | More granular split |
|
||||
### 13.14 redbear-power Specific Findings
|
||||
### 13.14 redbear-power Current Status (v1.44+, 2026-07-06)
|
||||
|
||||
A targeted audit of `local/recipes/system/redbear-power/` (v1.20, 6360 LoC
|
||||
across 21 modules, 76 unit tests) produced these actionable findings:
|
||||
A comprehensive audit and improvement cycle (16 passes) against bottom v0.11.2
|
||||
brought redbear-power from v1.20 (6,360 LoC, 21 modules, 76 tests) to
|
||||
**v1.44+ (13,091 LoC, 27 modules, 217 tests)**. All items below are
|
||||
implemented and tested.
|
||||
|
||||
| Severity | Finding | Fix |
|
||||
|----------|---------|-----|
|
||||
| **bug** | `render_prochot_alert` always passes freshly-constructed `Instant::now()`, so the pulse never toggles | Use `Frame::count()` (§13.3) |
|
||||
| minor | `centered_rect` hand-rolled | Use `Rect::centered` (§13.6) |
|
||||
| minor | `Layout::default().split(...)` returns chunks | Use `area.layout(&Layout)` (§13.5) |
|
||||
| cosmetic | `Style::default().fg(...)` chains | Use Stylize shorthand (§13.4) |
|
||||
| cosmetic | `Theme` not centralized — colors scattered | Centralize as §12 (`redbear-tui-theme`) |
|
||||
| minor | Input poll (250-2000ms) blocks snappy response | Decouple refresh from input (§1 ratatui audit §8) |
|
||||
| cosmetic | Duplicate comment in `snapshot()` | Trivial cleanup |
|
||||
| feature | No mouse support | Implemented in v1.1 (§13.16) |
|
||||
| feature | No config file | Implemented in v1.2 (`config.rs` module) |
|
||||
| feature | No multi-view tabs (single Per-CPU view only) | Implemented in v1.2 (`Tabs` widget + `TabId` enum) |
|
||||
| feature | No D-Bus export for headless clients | Implemented in v1.1 (`dbus.rs` module + zbus 5) |
|
||||
| feature | No Linux-host fallbacks (hardcoded `/scheme/sys/...` paths) | Implemented in v1.3 (`platform.rs` runtime probe + per-module fallbacks) |
|
||||
| feature | No memory or OS info display | Implemented in v1.4 (`meminfo.rs` module + `mem_bar_line` helper) |
|
||||
| feature | No Motherboard / DMI tab | Implemented in v1.5 (`dmi.rs` module + `TabId::Motherboard`) |
|
||||
| feature | No Battery tab | Implemented in v1.6 (`battery.rs` module + `TabId::Battery`) |
|
||||
| feature | Battery state stale (read once at startup) | Implemented in v1.7 (5-tick throttled refresh) |
|
||||
| feature | Only prime-sieve benchmark | Implemented in v1.8 (FFT + AES + single-core toggle, 5 unit tests) |
|
||||
| feature | No Sensors tab | Implemented in v1.9 (`sensor.rs` module + `TabId::Sensors`, 7 unit tests) |
|
||||
| feature | Per-CPU Temp n/a on AMD (Intel-only MSR) | Implemented in v1.10 (`SensorInfo::pkg_temp_c` fallback to k10temp/coretemp/zenpower) |
|
||||
| feature | No Network tab | Implemented in v1.11 (`network.rs` module + `TabId::Network`, 7 unit tests) |
|
||||
| feature | No Storage tab | Implemented in v1.12 (`storage.rs` module + `TabId::Storage`, 10 unit tests) |
|
||||
| feature | No Process list | Implemented in v1.13 (`process.rs` module + `TabId::Process`, 9 unit tests) |
|
||||
| feature | No CPU% in Process tab | Implemented in v1.14 (`ProcInfo::read_with_cpu_pct` + 4 unit tests) |
|
||||
| feature | No disk throughput in Storage tab | Implemented in v1.15 (`StorageInfo::read_with_throughput` + 3 unit tests) |
|
||||
| feature | No network throughput in Network tab | Implemented in v1.16 (`NetInfo::read_with_throughput` + 3 unit tests) |
|
||||
| feature | No sort modes in Process tab | Implemented in v1.17 (`SortMode` enum + 6 unit tests, hotkey `o`) |
|
||||
| feature | No process filtering | Implemented in v1.18 (`App.process_filter` + hotkey `f` + 4 unit tests) |
|
||||
| feature | No PID detail view | Implemented in v1.19 (`pid_detail.rs` module + Enter/Esc handling + 7 unit tests) |
|
||||
| feature | No SMART disk health data | Implemented in v1.20 (`smart.rs` module + smartctl subprocess + 7 unit tests) |
|
||||
| feature | No SMART UI integration | Implemented in v1.21 (Storage tab badge: PASSED/FAILED/missing/error) |
|
||||
| Area | Status | Detail |
|
||||
|------|--------|--------|
|
||||
| Braille time-series graphs | ✅ v1.44 | `BrailleGraph` widget using ratatui's `Canvas` + `Marker::Braille`; 5 graphs across 4 tabs (CPU%, Temp°C, PkgW, Net KiB/s, Disk KiB/s); y-axis labels with reserved margin |
|
||||
| RingHistory buffer | ✅ v1.44 | O(1) ring buffer with `display_max()` rounding to nice values (1,2,5,10,20,50...); prevents y-axis jitter |
|
||||
| Theme system | ✅ v1.44 | `Theme` struct with `dark()`, `light()`, `high_contrast()` presets; `--theme` CLI flag with validation; wired into `panel_border()`, `BrailleGraph`, header governor, cursor highlight |
|
||||
| Widget expansion | ✅ v1.44 | `e` key toggles full-screen tab rendering; hint bar at bottom |
|
||||
| Data freeze | ✅ v1.44 | `f` key pauses data updates; `[FROZEN]` indicator in keybar |
|
||||
| Process kill dialog | ✅ v1.44 | `k` key (Process tab) opens signal selection modal; `Cell<usize>` for interior mutability (avoids borrow conflicts); auto-closes 2s after signal sent; 6 unit tests |
|
||||
| Panic hook | ✅ v1.44 | `panic::set_hook` restores terminal on panic |
|
||||
| TTY check | ✅ v1.44 | `IsTerminal` check before raw mode; clear error message |
|
||||
| MSR cache | ✅ v1.44 | Per-CPU failure cache avoids repeated doomed opens; auto-clears every ~30s |
|
||||
| Skip-refresh guard | ✅ v1.44 | Adaptive throttle: if refresh >200ms, skip next cycle |
|
||||
| Collector fix | ✅ v1.44 | Removed `Barrier` from per-CPU collector; threads start work immediately |
|
||||
| Key shadowing audit | ✅ v1.44 | 4 bugs found and fixed: `g` (governor vs. move-to-top), `T` (tab-cycle vs. tree-toggle), `f` (freeze vs. process-filter), `f` duplicate removed |
|
||||
| `--once` graph output | ✅ v1.44 | `--once` renders 3 braille graphs + all text panels |
|
||||
| LTO release | ✅ v1.44 | `lto = true, opt-level = 3, codegen-units = 1` → 4.1 MB binary (-33%) |
|
||||
| Integration tests | ✅ v1.44 | 8 new tests: CPU detection, graph population, process count, governor available, temp source, expand, freeze, skip-refresh |
|
||||
| All previous v1.0–v1.21 features | ✅ | Mouse, config, tabs, D-Bus, Linux fallbacks, meminfo, DMI, battery, sensors, network, storage, process list, CPU%, throughput, sort, filter, PID detail, SMART, benchmarks, HWP, cpuid, scheduler stats, CPU affinity, per-thread IO |
|
||||
|
||||
Full plan: see `local/docs/redbear-power-improvement-plan.md`.
|
||||
Full assessment: see `local/docs/bottom-vs-redbear-power-assessment.md`.
|
||||
|
||||
### 13.15 v1.4 Module Pattern: `meminfo.rs` for Read-Only System Data
|
||||
|
||||
@@ -1382,17 +1374,201 @@ gives a natural unit-of-work (count) that scales with thread count.
|
||||
|
||||
---
|
||||
|
||||
## 15. Braille Time-Series Graph Pattern
|
||||
|
||||
**Source:** `redbear-power/src/graph.rs` (227 lines, 5 unit tests)
|
||||
**Borrowed from:** bottom's `canvas/components/time_graph/` approach, simplified
|
||||
|
||||
### Pattern: Canvas + Marker::Braille + RingHistory
|
||||
|
||||
ratatui 0.30's built-in `Canvas` widget with `Marker::Braille` gives 8 data
|
||||
points per terminal cell (2 columns × 4 rows). A 40-column graph area can
|
||||
display 80 time points at 4-char y-axis label margin.
|
||||
|
||||
```rust
|
||||
pub struct BrailleGraph<'a> {
|
||||
pub values: &'a [f64], // time-series data
|
||||
pub max_value: f64, // y-axis ceiling (use display_max())
|
||||
pub color: Color, // line color
|
||||
pub title: &'a str, // border title
|
||||
pub focused: bool, // affects border style
|
||||
pub x_labels: Option<(&'a str, &'a str)>,
|
||||
pub y_labels: Option<(String, String)>,
|
||||
pub theme: &'a Theme, // THEME-DRIVEN, never hardcoded
|
||||
}
|
||||
|
||||
impl Widget for BrailleGraph<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
// 1. Reserve 4 columns on left for y-axis labels
|
||||
let label_w: u16 = if self.y_labels.is_some() { 4 } else { 0 };
|
||||
let canvas_area = Rect { x: inner.x + label_w, width: inner.width - label_w, ..inner };
|
||||
|
||||
// 2. Draw lines via Canvas
|
||||
Canvas::default()
|
||||
.x_bounds([0.0, (n - 1) as f64])
|
||||
.y_bounds([0.0, self.max_value])
|
||||
.marker(Marker::Braille)
|
||||
.paint(|ctx| {
|
||||
for w in clamped.windows(2).enumerate() {
|
||||
ctx.draw(&CanvasLine {
|
||||
x1: i as f64, y1: w[0],
|
||||
x2: (i+1) as f64, y2: w[1],
|
||||
color: self.color,
|
||||
});
|
||||
}
|
||||
})
|
||||
.render(canvas_area, buf);
|
||||
|
||||
// 3. Y-axis labels in reserved margin
|
||||
if let Some((ref top, ref bot)) = self.y_labels {
|
||||
buf.set_string(inner.x, inner.y, &format!("{:>4}", top), label_style);
|
||||
buf.set_string(inner.x, bottom_y, &format!("{:>4}", bot), label_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### RingHistory: Stable Y-Axis with Nice Maxima
|
||||
|
||||
```rust
|
||||
pub struct RingHistory {
|
||||
data: Vec<f64>,
|
||||
capacity: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl RingHistory {
|
||||
/// Round peak to nice values: 1,2,5,10,20,50,100,200,500...
|
||||
/// Prevents y-axis jitter from every-tick rescaling.
|
||||
pub fn display_max(&self) -> f64 {
|
||||
let raw = self.max();
|
||||
if raw <= 0.0 { return 1.0; }
|
||||
let mag = 10.0_f64.powi((raw.log10().floor()) as i32);
|
||||
let norm = raw / mag;
|
||||
let nice = if norm <= 1.0 { 1.0 } else if norm <= 2.0 { 2.0 }
|
||||
else if norm <= 5.0 { 5.0 } else { 10.0 };
|
||||
nice * mag
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key decisions:**
|
||||
- Use `display_max()` **not** `max()` for the y-axis — empty data returns 1.0, populated data rounds to stable nice numbers
|
||||
- Reserve 4-column label margin with `Rect` offset; labels use `buf.set_string` (multi-cell) not `cell.set_symbol` (single-cell)
|
||||
- Canvas uses full area after margin — braille grid auto-scales within `x_bounds`/`y_bounds`
|
||||
- Graph titles match between normal and expanded mode — audit for consistency
|
||||
- Graphs render in both System tab (3 side-by-side) and expanded mode (stacked)
|
||||
|
||||
---
|
||||
|
||||
## 16. Modal Dialog Pattern (Interior Mutability)
|
||||
|
||||
**Source:** `redbear-power/src/kill.rs` (148 lines, 6 unit tests)
|
||||
|
||||
### Problem
|
||||
|
||||
Modal dialogs need mutable state (`ListState` for selection) but must render
|
||||
via `&self` to avoid borrow conflicts with the parent `App` struct's immutable
|
||||
borrows during `terminal.draw()`. `Widget for &mut KillDialog` causes
|
||||
`E0502: cannot borrow as mutable because also borrowed as immutable`.
|
||||
|
||||
### Solution: `Cell<usize>` for Selection State
|
||||
|
||||
```rust
|
||||
pub struct KillDialog {
|
||||
pub open: bool,
|
||||
pub pid: u32,
|
||||
pub comm: String,
|
||||
selected: Cell<usize>, // ← interior mutability
|
||||
signals: Vec<(&'static str, i32)>,
|
||||
pub result: Option<String>,
|
||||
result_at: Option<Instant>, // auto-close timer
|
||||
}
|
||||
|
||||
impl Widget for &KillDialog { // ← &self, not &mut self
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let sel = self.selected.get();
|
||||
// Render list with highlighted item at `sel`
|
||||
for (i, (label, _)) in self.signals.iter().enumerate() {
|
||||
let item = if i == sel {
|
||||
ListItem::new(Line::styled(format!("▶ {label}"), highlight_style))
|
||||
} else {
|
||||
ListItem::new(Line::from(format!(" {label}")))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Auto-Close Pattern
|
||||
|
||||
```rust
|
||||
pub fn send_signal(&mut self) {
|
||||
self.result = Some(format!("Sent signal {} to PID {}", sig, self.pid));
|
||||
self.result_at = Some(Instant::now()); // start 2s timer
|
||||
}
|
||||
|
||||
pub fn auto_close_if_done(&mut self) {
|
||||
if let Some(at) = self.result_at {
|
||||
if at.elapsed().as_millis() > 2000 {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Called every tick in the main loop: `app.kill_dialog.auto_close_if_done();`
|
||||
|
||||
**Key decisions:**
|
||||
- `Cell<usize>` avoids `RefCell` overhead — only one `usize` field, no runtime borrow checks
|
||||
- `Widget for &KillDialog` (immutable ref) — compatible with `&app` borrows during draw
|
||||
- Auto-close timer prevents stuck dialog after signal sent
|
||||
- `Enter` blocked when `result.is_some()` to prevent double-send
|
||||
|
||||
---
|
||||
|
||||
## 17. Key Binding Audit Pattern
|
||||
|
||||
**Source:** 4 bugs found and fixed across 16 passes of redbear-power
|
||||
|
||||
### Pattern: Audit All `Key::Char` Arms for Shadowing
|
||||
|
||||
In a large `match k { ... }` block (200+ arms), duplicate `Key::Char('X')`
|
||||
patterns silently shadow each other — the first one wins, the second is
|
||||
unreachable dead code.
|
||||
|
||||
```bash
|
||||
# Audit command: detect all duplicate key bindings
|
||||
grep -oP "Key::Char\('[^']+'\)" main.rs | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
**Bugs found in redbear-power:**
|
||||
| Key | First handler | Shadowed handler | Fix |
|
||||
|-----|--------------|------------------|-----|
|
||||
| `'g'` | `cycle_governor()` | `move_to_edge(true)` | Removed (Home already does this) |
|
||||
| `'T'` | `set_tab(next())` | `process_tree = !process_tree` | Changed tree toggle to `'y'` |
|
||||
| `'f'` | `frozen = !frozen` | process filter prompt | Changed filter to `'F'` |
|
||||
|
||||
**Guard-based arms (`Key::Char('k') if condition`) are NOT duplicates** — they
|
||||
fall through when the guard is false. The audit only flags identical
|
||||
unguarded patterns.
|
||||
|
||||
**Rule:** After any key binding change, re-run the audit. Never have two
|
||||
unguarded `Key::Char('X')` arms in the same match.
|
||||
|
||||
---
|
||||
|
||||
## 14. Cross-Reference: redbear-power as a Reference Implementation
|
||||
|
||||
The `redbear-power` recipe (`local/recipes/system/redbear-power/`) is a useful
|
||||
reference for new TUI apps because:
|
||||
|
||||
1. **Small enough to read in one sitting** (~6400 LoC across 21 modules, with 76 unit tests)
|
||||
1. **Small enough to read in one sitting** (~13,100 LoC across 27 modules, with 217 unit tests)
|
||||
2. **Self-contained** — no D-Bus, no external state, just sysfs/MSR/procfs + meminfo + DMI + battery + hwmon + net + storage + proc + pid_detail + smart
|
||||
3. **Modern ratatui 0.30 patterns** — `TableState`, modular layout, status bars, `Tabs` widget, modal popups (`Clear` + centered `Rect`)
|
||||
4. **Cross-platform** — same binary works on Linux + Redox (MSR/scheme + sysfs/proc fallback + hwmon fallback for AMD CPUs + net/sysfs fallback + storage/sysfs fallback + procfs fallback + /proc/[pid]/* parsers + smartctl subprocess with graceful missing-binary degradation + UI badge display)
|
||||
3. **Modern ratatui 0.30 patterns** — `TableState`, modular layout, status bars, `Tabs` widget, `Canvas` + `Marker::Braille` graphs, modal popups (`Clear` + centered `Rect`), theme-driven borders
|
||||
4. **Cross-platform** — same binary works on Linux + Redox (MSR/scheme + sysfs/proc fallback + hwmon fallback)
|
||||
5. **Well-documented** — extensive code comments + this doc + improvement plan
|
||||
6. **Testable** — bench + sensor + network + storage + process + pid_detail + smart modules have 76 unit tests covering stress modes + hwmon unit conversions + multi-vendor pkg_temp_c + binary byte formatting + disk stat parsing + delta math + /proc/[pid]/stat parser with space-handling + CPU% delta math + disk throughput delta math + network throughput delta math + sort mode comparisons + process filter matching + /proc/[pid]/{status,io,smaps_rollup} parsers + smartctl attribute parsing
|
||||
6. **Testable** — 217 unit tests covering braille graphs, kill dialog, benchmarks, MSR cache, process tree, IO sparklines, sensor parsing, network parsing, storage parsing, /proc/[pid]/* parsers, SMART, sort modes, filter matching, PID detail, LRU eviction, display_max rounding
|
||||
|
||||
When porting a new Red Bear TUI app, structure it like redbear-power:
|
||||
|
||||
@@ -1402,22 +1578,25 @@ my-tui-app/
|
||||
├── recipe.toml # path = "source", template = "cargo"
|
||||
└── source/
|
||||
└── src/
|
||||
├── main.rs # event loop, key + mouse + D-Bus dispatch (~475 lines)
|
||||
├── app.rs # App struct, all state, refresh cadence (~535 lines)
|
||||
├── render.rs # render_header, render_table, render_controls (~925 lines)
|
||||
├── platform.rs # runtime data-source probes (~290 lines)
|
||||
├── main.rs # event loop, key + mouse + D-Bus dispatch
|
||||
├── app.rs # App struct, all state, refresh cadence, coprime moduli
|
||||
├── render.rs # render_header, render_*_panel, render_keybar, snapshot
|
||||
├── theme.rs # Theme struct with presets, color helpers, all styles centralized
|
||||
├── graph.rs # BrailleGraph widget + RingHistory buffer
|
||||
├── kill.rs # Modal dialog with Cell<usize> for interior mutability
|
||||
├── event.rs # Typed Event enum (Key, Mouse, Tick, Resize, Terminate)
|
||||
├── platform.rs # runtime data-source probes
|
||||
└── <data>.rs # detect, read_*, helpers, format_*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- `local/recipes/system/redbear-power/source/src/` — reference implementation
|
||||
- `local/recipes/system/redbear-power/source/src/` — reference implementation (v1.44+, 13,091 LoC, 27 modules, 217 tests)
|
||||
- `local/recipes/tui/tlc/source/src/` — 46k+ LoC production TUI
|
||||
- `local/recipes/tui/redbear-tui-theme/` — shared theme constants
|
||||
- `local/docs/redbear-power-improvement-plan.md` — Phase 2 roadmap derived from this doc, with §28 v1.4 status
|
||||
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` — desktop stack planning, §3.3.2 v0.1–v1.4
|
||||
- `local/docs/redbear-power-improvement-plan.md` — improvement roadmap
|
||||
- `local/docs/bottom-vs-redbear-power-assessment.md` — bottom comparison and borrow analysis
|
||||
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` — desktop stack planning
|
||||
- https://ratatui.rs/ — official docs
|
||||
- https://github.com/ratatui/ratatui/tree/main/examples — canonical patterns
|
||||
- https://github.com/X0rg/CPU-X — cpu-x v4.7 (7000+ LoC, mature CPU monitor reference)
|
||||
|
||||
@@ -300,8 +300,7 @@ the `recovered/quirks` branch in the outer RedBear-OS repo):
|
||||
- `4191b8543` — base submodule pointer (acpid AML sequence)
|
||||
- `850124559` — kernel submodule pointer (s2idle kstop handler)
|
||||
|
||||
- **Red Bear OS inner** commits (historical — these repos have since been
|
||||
merged as `submodule/base` and `submodule/kernel` branches inside the
|
||||
canonical `RedBear-OS` repo per the SINGLE-REPO RULE):
|
||||
- `redbear-os-base 5d2d114` — acpid: full Linux AML S-state sequence
|
||||
- `redbear-os-kernel 75c7618` — kernel: s2idle / s3 kstop handler
|
||||
- **Red Bear OS inner** commits (historical — now found on `submodule/base`
|
||||
and `submodule/kernel` branches inside the canonical `RedBear-OS` repo):
|
||||
- `submodule/base 5d2d114` — acpid: full Linux AML S-state sequence
|
||||
- `submodule/kernel 75c7618` — kernel: s2idle / s3 kstop handler
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
# Red Bear OS USB Validation Runbook — v3
|
||||
|
||||
> Companion to `local/docs/USB-IMPLEMENTATION-PLAN.md` v3.
|
||||
> This runbook tells operators how to validate USB on a Red Bear build.
|
||||
|
||||
## Validation matrix
|
||||
|
||||
| Script | What it validates | Maturity target |
|
||||
|---|---|---|
|
||||
| `test-xhci-irq-qemu.sh --check` | xHCI interrupt-driven reactor path (line 208 of `irq_reactor.rs`) | `validated-QEMU` |
|
||||
| `test-xhci-device-lifecycle-qemu.sh --check` | bounded USB attach/detach for HID + storage | `validated-QEMU` |
|
||||
| `test-usb-qemu.sh` | full USB stack (xHCI + keyboard + tablet + storage) | `validated-QEMU` |
|
||||
| `test-usb-storage-qemu.sh` | usbscsid autospawn + sector write+readback+restore | `validated-QEMU` |
|
||||
| `test-usb-runtime.sh` | guest + QEMU mode dispatch harness | harness only |
|
||||
| `test-usb-maturity-qemu.sh` | aggregate runner — calls the five above in sequence | runner |
|
||||
| `test-uhci-runtime-qemu.sh --check` *(P1-B)* | UHCI controller enumeration on legacy QEMU machine | `validated-QEMU` (P1-B) |
|
||||
| `test-ohci-runtime-qemu.sh --check` *(P1-B)* | OHCI controller enumeration on legacy QEMU machine | `validated-QEMU` (P1-B) |
|
||||
| `test-ehci-class-autospawn-qemu.sh --check` *(P1-A)* | USB keyboard on EHCI route reaches inputd | `validated-QEMU` (P1-A) |
|
||||
| `test-usb-hub-qemu.sh --check` *(P3-A)* | USB hub enumeration including power timing + wHubDelay | `validated-QEMU` (P3-A) |
|
||||
| `test-usb-error-recovery-qemu.sh --check` *(P8-C)* | hot-unplug mid-transfer — graceful error, no panic | `validated-QEMU` (P8-C) |
|
||||
| `test-usb-uas-qemu.sh --check` *(P4-A)* | USB 3.0 storage UAS path active | `validated-QEMU` (P4-A) |
|
||||
|
||||
A row's last-passed ISO date goes in `local/docs/HARDWARE-VALIDATION-MATRIX.md`.
|
||||
|
||||
## Path A — Host-side QEMU validation
|
||||
|
||||
### Pre-flight
|
||||
|
||||
```bash
|
||||
# Confirm the build is fresh
|
||||
./local/scripts/build-redbear.sh --upstream redbear-mini
|
||||
ls build/x86_64/redbear-mini/harddrive.img
|
||||
```
|
||||
|
||||
### Run aggregate
|
||||
|
||||
```bash
|
||||
./local/scripts/test-usb-maturity-qemu.sh redbear-mini
|
||||
```
|
||||
|
||||
This runs all five existing QEMU tests in sequence and reports a single pass/fail.
|
||||
|
||||
### Run individual
|
||||
|
||||
```bash
|
||||
# Interrupt-mode proof (must show "Running IRQ reactor with IRQ file
|
||||
# and event queue" in the boot log; must NOT show "in polling mode").
|
||||
./local/scripts/test-xhci-irq-qemu.sh --check
|
||||
|
||||
# Storage write+readback+restore (sector 2048; expects
|
||||
# [PASS] STORAGE_DISCOVERY + STORAGE_WRITE + STORAGE_READBACK +
|
||||
# STORAGE_RESTORE).
|
||||
./local/scripts/test-usb-storage-qemu.sh
|
||||
|
||||
# Lifecycle proof (QEMU monitor-driven attach/detach).
|
||||
./local/scripts/test-xhci-device-lifecycle-qemu.sh --check
|
||||
```
|
||||
|
||||
### What it validates vs. claims
|
||||
|
||||
| Claim | Evidence |
|
||||
|---|---|
|
||||
| `xhcid` runs interrupt-driven | `[RUNNING] IRQ reactor with IRQ file and event queue` in log |
|
||||
| `usbscsid` auto-spawns on storage attach | `usbscsid: scheme event` or `[redbear-usb-storage-check] STORAGE_DISCOVERY: disk.usb-...` |
|
||||
| Storage read+write works | `[PASS] STORAGE_WRITE:` + `[PASS] STORAGE_READBACK:` + `[PASS] STORAGE_RESTORE:` |
|
||||
| HID auto-spawns on keyboard attach | `usbhidd: registered producer` in log |
|
||||
| No panics | absence of `panic\|crash\|abort\|RUST_BACKTRACE` in log |
|
||||
|
||||
## Path B — In-guest manual validation
|
||||
|
||||
Boot the image and check:
|
||||
|
||||
```bash
|
||||
# Inside the guest:
|
||||
ls /scheme/usb* # host controllers' scheme namespaces
|
||||
lsusb # walker (in redbear-hwutils package)
|
||||
redbear-usb-check # pass/fail scheme validator
|
||||
```
|
||||
|
||||
A healthy USB stack shows:
|
||||
|
||||
- `/scheme/usb.0000:XX:XX.X_xhci/` (xhcid port directory)
|
||||
- `/scheme/usb/` (ehcid port directory)
|
||||
- `/scheme/disk.usb-...+...-scsi/` (usbscsid disk)
|
||||
- `/scheme/input/...` (usbhidd device)
|
||||
|
||||
## Path C — Bare-metal hardware validation (P8-A)
|
||||
|
||||
For each (controller, class, board) tuple in `HARDWARE-VALIDATION-MATRIX.md`:
|
||||
|
||||
```bash
|
||||
# On the bare-metal host:
|
||||
./local/scripts/build-redbear.sh <config>
|
||||
dd if=build/x86_64/<config>/harddrive.img of=/dev/<target> bs=4M status=progress
|
||||
# Boot; capture serial console
|
||||
minicom -D /dev/ttyUSB0 -b 115200 -C capture.bin
|
||||
|
||||
# In the captured console:
|
||||
redbear-info --verbose
|
||||
lsusb
|
||||
redbear-usb-check
|
||||
```
|
||||
|
||||
Required test points (per row in the matrix):
|
||||
1. Controller PCI ID detected
|
||||
2. Specific class test (HID keypress, storage read+write, etc.)
|
||||
3. Hot-plug works
|
||||
4. Suspend/resume works (if device supports)
|
||||
|
||||
Update the matrix row: `last_tested = <ISO date>`, `result = pass|fail|partial`.
|
||||
|
||||
## Operator runbook for failures
|
||||
|
||||
If `test-xhci-irq-qemu.sh` reports polling-mode:
|
||||
|
||||
```bash
|
||||
# Check the boot log for the interrupt path:
|
||||
grep "IRQ reactor" build/x86_64/redbear-mini/xhci-irq-check.log
|
||||
# If "in polling mode" appears, xHCI interrupts are bypassed.
|
||||
# Check the base fork commit:
|
||||
git -C local/sources/base log --oneline drivers/usb/xhcid/src/main.rs | head -5
|
||||
# Confirm commit cbd40e0d (or later) is in the merge base.
|
||||
```
|
||||
|
||||
If `test-usb-storage-qemu.sh` reports a panic:
|
||||
|
||||
```bash
|
||||
# Find the panic site:
|
||||
grep -n 'panic\|unwrap\|expect' build/x86_64/redbear-mini/usb-storage-check.log
|
||||
# Cross-reference to usbscsid:
|
||||
grep -rn 'panic!' local/sources/base/drivers/storage/usbscsid/src/
|
||||
# After P1-B is complete, no panic sites should exist.
|
||||
```
|
||||
|
||||
If a USB device is not auto-spawning:
|
||||
|
||||
```bash
|
||||
# Check if the class driver is wired in the configs:
|
||||
grep -A2 "redbear-acmd\|redbear-ecmd\|redbear-usbaudiod" config/redbear-mini.toml
|
||||
# Cross-check drivers.d:
|
||||
cat local/config/drivers.d/70-usb-class.toml
|
||||
# After P1-A, all four controllers should auto-spawn via the unified trait.
|
||||
```
|
||||
|
||||
## Validation cadence
|
||||
|
||||
- **On every release branch cut:** run all QEMU tests; update matrix with last-tested dates.
|
||||
- **On every P-phase completion:** add the new test scripts to the matrix.
|
||||
- **Monthly:** if any operator has bare-metal access, add one hardware row.
|
||||
|
||||
## See also
|
||||
|
||||
- `local/docs/USB-IMPLEMENTATION-PLAN.md` v3 — the plan this runbook validates
|
||||
- `local/docs/HARDWARE-VALIDATION-MATRIX.md` — the matrix this runbook populates
|
||||
- `local/scripts/test-usb-maturity-qemu.sh` — the aggregate entry point
|
||||
@@ -15,7 +15,11 @@ current plans. They are kept for reference only.
|
||||
| `ACPI-I2C-HID-IMPLEMENTATION-PLAN.md` | (Deferred — USB HID is primary input path) |
|
||||
| `GRUB-INTEGRATION-PLAN.md` | GRUB is fully implemented (redbear-grub config, installer support, grub recipe) |
|
||||
| `VFAT-IMPLEMENTATION-PLAN.md` | VFAT is fully implemented (fatd, fat-mkfs, fat-label, fat-check) |
|
||||
| `USB-BOOT-INPUT-PLAN.md` | Superseded — USB HID in initfs, USB storage in initfs (Phase B2) |
|
||||
| `USB-BOOT-INPUT-PLAN.md` | Superseded — USB HID in initfs, USB storage in initfs (Phase B2). Boot-input analysis remains historically useful; the live-input priority lives in `USB-IMPLEMENTATION-PLAN.md` v2. |
|
||||
| `XHCID-DEVICE-IMPROVEMENT-PLAN.md` | Superseded by `USB-IMPLEMENTATION-PLAN.md` v2 — phases 1–7 absorbed into the new plan's P0–P3. |
|
||||
| `USB-IMPLEMENTATION-PLAN-v1-2026-04.md` | Superseded by `USB-IMPLEMENTATION-PLAN-v2-2026-07.md` (archived). v1 overstated xHCI interrupt-driven operation; v2 rebased onto current source state and Redox 0.x USB HEAD. |
|
||||
| `USB-IMPLEMENTATION-PLAN-v2-2026-07.md` | Superseded by `USB-IMPLEMENTATION-PLAN.md` v3 (current). v2 captured P0–P5 host-controller work; v3 expands to full USB first-class-citizen scope including UAS, error recovery, CDC ACM, HID report parsing, and hardware matrix. |
|
||||
| `USB-VALIDATION-RUNBOOK-2026-07.md` | Historical validation runbook for P0 era. v3 validates against the expanded scope. |
|
||||
| `ZSH-PORTING-PLAN.md` | Deferred indefinitely — ion is the default shell |
|
||||
|
||||
## Date archived: 2026-05-03
|
||||
|
||||
@@ -0,0 +1,736 @@
|
||||
# Red Bear OS USB Implementation Plan
|
||||
|
||||
## Purpose
|
||||
|
||||
This document defines the current state, completeness, and implementation path for USB in Red Bear
|
||||
OS. It distinguishes between the **upstream source** (unpatched) and the **Red Bear state** (after
|
||||
applying `local/patches/base/redox.patch`).
|
||||
|
||||
The goal is to describe USB in terms of **what is built**, **what is patched**, **what is
|
||||
actually usable**, and **what still needs to be implemented** before Red Bear can honestly claim a
|
||||
modern, future-proof USB stack.
|
||||
|
||||
This document is Red Bear-specific. It uses current repo evidence from code, configs, runtime
|
||||
tooling, and status docs instead of assuming inherited upstream documentation is fully current.
|
||||
|
||||
## Validation States
|
||||
|
||||
- **builds** — code exists in-tree and is expected to compile
|
||||
- **enumerates** — runtime surfaces can discover controllers, ports, or descriptors
|
||||
- **usable** — a specific controller/class path works in a limited real scenario
|
||||
- **validated** — behavior has been exercised with explicit evidence for the claimed scope
|
||||
- **experimental** — available for bring-up, but not support-promised
|
||||
|
||||
This repo should not treat **builds** or **enumerates** as equivalent to **validated**.
|
||||
|
||||
## Source Model
|
||||
|
||||
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 ~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
|
||||
are only visible after patch application. This document describes the **Red Bear state** unless
|
||||
explicitly noted.
|
||||
|
||||
## Current Repo State
|
||||
|
||||
### Summary
|
||||
|
||||
USB in Red Bear OS is **present and improving**.
|
||||
|
||||
The Red Bear USB stack consists of:
|
||||
|
||||
- a host-side xHCI controller daemon (`xhcid`) with Red Bear patches for error handling,
|
||||
correctness, and robustness
|
||||
- hub and HID class daemons with Red Bear patches
|
||||
- a mass-storage BOT daemon with Red Bear patches
|
||||
- native USB observability (`lsusb`, `usbctl`, `redbear-info`)
|
||||
- a low-level userspace client API through `xhcid_interface`
|
||||
- a hardware quirks system that applies USB device-specific workarounds at runtime
|
||||
- four QEMU validation harnesses covering interrupt delivery, bounded device lifecycle hotplug,
|
||||
full stack, and storage autospawn
|
||||
- an in-guest scheme-tree checker (`redbear-usb-check`)
|
||||
|
||||
### Boot-input reality
|
||||
|
||||
For bare-metal boot resilience, the current USB stack is still incomplete.
|
||||
|
||||
External USB keyboard input is reliably available only when the keyboard is reached through the
|
||||
`xHCI -> usbhubd/usbhidd -> inputd` path. This is an important distinction because modern bare
|
||||
metal does not guarantee that an attached keyboard will land on the xHCI runtime path.
|
||||
|
||||
If a keyboard instead lands on:
|
||||
|
||||
- an EHCI-owned path
|
||||
- a UHCI/OHCI companion path
|
||||
- a firmware routing topology where low/full-speed devices do not reach the xHCI runtime path
|
||||
|
||||
then Red Bear may still detect controller ownership and connected ports, but it does not yet have a
|
||||
complete runtime host path that reaches the existing HID class daemons.
|
||||
|
||||
This means Red Bear cannot yet honestly claim that an external USB keyboard is a reliable universal
|
||||
boot fallback on bare metal.
|
||||
|
||||
### Red Bear xHCI Patch Layer
|
||||
|
||||
The Red Bear patch at `local/patches/base/redox.patch` carries these changes over the upstream
|
||||
source:
|
||||
|
||||
**Error handling (88 fixes):**
|
||||
- `unwrap()` on mutex locks replaced with `unwrap_or_else(|e| e.into_inner())` across `scheme.rs`,
|
||||
`mod.rs`, `irq_reactor.rs`, and `ring.rs` — mutex poisoning no longer panics any hot-path lock
|
||||
- `expect()` calls replaced with proper `Result` propagation, logged errors, or fallible helpers
|
||||
- `trb_phys_ptr()` returns `Result<u64>` instead of panicking on invalid TRB pointers
|
||||
- `panic!()` in `irq_reactor.rs` replaced with error returns where possible
|
||||
- `device_enumerator.rs` panics replaced with error logging and graceful handling
|
||||
|
||||
**Correctness fixes:**
|
||||
- **ERDP split**: upstream has a single `erdp()` method that conflates the software dequeue pointer
|
||||
with the hardware register read. Red Bear splits this into `dequeue_ptr()` (software ring
|
||||
position) and `erdp(&RuntimeRegs)` (actual hardware register read, per XHCI spec §4.9.3)
|
||||
- **endp_direction off-by-one**: upstream uses `endp_num as usize` to index into the endpoints Vec,
|
||||
but USB endpoints are 1-indexed. Red Bear uses `endp_num.checked_sub(1)` for correct 0-based
|
||||
indexing
|
||||
- **cfg_idx ordering**: upstream sets `port_state.cfg_idx` before validating the config descriptor.
|
||||
Red Bear moves the assignment after validation succeeds
|
||||
- **CLEAR_FEATURE endpoint address**: upstream uses the driver-internal endpoint index for
|
||||
`CLEAR_FEATURE(ENDPOINT_HALT)`. Red Bear uses the USB endpoint address from the descriptor
|
||||
(`bEndpointAddress`)
|
||||
- **usbhubd status_change_buf**: upstream has off-by-one bitmap sizing and bit-position parsing.
|
||||
Red Bear sizes the buffer correctly and computes port bit positions explicitly
|
||||
|
||||
**Functional additions:**
|
||||
- **Event ring growth**: upstream has a stub `grow_event_ring()` that logs "TODO". Red Bear
|
||||
implements real ring doubling (up to 4096 cap), new DMA allocation, dequeue pointer preservation,
|
||||
ERDP/ERSTBA register updates, and DCS bit handling
|
||||
- **BOS/SuperSpeed descriptor fetching**: `fetch_bos_desc()` called during device enumeration with
|
||||
bounds-checked slicing and graceful USB 2 fallback
|
||||
- **Speed detection for hub child devices**: `UsbSpeed` enum with `from_v2_port_status()` /
|
||||
`from_v3_port_status()` mapping, passed via `attach_with_speed()` from `usbhubd`
|
||||
- **Interrupt-driven operation restored**: `get_int_method()` replaces hardwired polling; MSI/MSI-X/
|
||||
INTx paths re-enabled
|
||||
- **Hub interrupt EP1**: `usbhubd` reads status change via interrupt endpoint instead of polling
|
||||
- **USB 3 hub endpoint configuration**: `SET_INTERFACE` always sent; stall on `(0,0)` tolerated
|
||||
- **Hub change bit clearing**: `clear_port_changes` sends all relevant `ClearFeature` requests
|
||||
including USB3-specific features after every port status read
|
||||
- **HID error handling**: `usbhidd` uses `anyhow::Result` with context, no panics in report loop
|
||||
- **BOT transport robustness**: `usbscsid` replaces all `panic!()` with stall recovery and error
|
||||
returns; iterative bounded CSW read loop instead of unbounded recursion; correct early_residue
|
||||
computation
|
||||
|
||||
### Remaining Limitations
|
||||
|
||||
Even with the Red Bear patch applied:
|
||||
|
||||
- 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
|
||||
- Any remaining USB composite/device-model issues now sit above the bounded helper fixes already
|
||||
landed for active alternates, endpoint direction, real interface/alternate hub configuration, and
|
||||
SSP-aware endpoint-context calculations.
|
||||
- ~57 TODO/FIXME comments remain across xHCI driver files
|
||||
- usbhubd: interrupt-driven change detection implemented; 1-second polling retained as fallback
|
||||
- usbscsid: `ReadCapacity16` now implemented with automatic fallback from `ReadCapacity10`
|
||||
- `usbhidd` keyboard LED sync is only a bounded per-device best-effort path, not a system-global
|
||||
lock-state authority
|
||||
- No real hardware USB validation — all testing is QEMU-only
|
||||
- No hot-plug stress testing
|
||||
- No USB storage data I/O validation (autospawn checked, but no read/write tested)
|
||||
- USB quirk table expanded from 8 to 146 entries mined from Linux 7.0
|
||||
- USB quirk flags expanded from 9 to 22 (13 new flags from Linux 7.0 including NO_BOS, HUB_SLOW_RESET)
|
||||
- Terminus hub (0x1A40:0x0101) corrected from `no_lpm` to `hub_slow_reset` per Linux semantics
|
||||
|
||||
### Current Status Matrix
|
||||
|
||||
| Area | State | Notes |
|
||||
|---|---|---|
|
||||
| Host mode | **builds / QEMU-validated** | Real host-side stack, interrupt-driven, QEMU-validated only |
|
||||
| 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 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** | 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
|
||||
|
||||
### xHCI driver (`xhcid/src/xhci/`)
|
||||
|
||||
**Upstream state** — 91 `unwrap()`, 25 `expect()`, 7 `panic!()`, ~57 TODO/FIXME across ~6000
|
||||
lines of Rust.
|
||||
|
||||
**Red Bear state** — mutex poisoning eliminated on all hot-path locks; `trb_phys_ptr()` returns
|
||||
`Result`; critical correctness bugs fixed; ~57 TODOs remain as design notes.
|
||||
|
||||
Key files and their sizes:
|
||||
|
||||
| File | Lines (approx) | Upstream Issues | Red Bear Fix Status |
|
||||
|---|---|---|---|
|
||||
| `scheme.rs` | ~2800 | 36 unwrap, 14 expect, 2 panic | All unwrap/expect on hot paths fixed; endp_direction, cfg_idx, CLEAR_FEATURE fixed |
|
||||
| `mod.rs` | ~1500 | 38 unwrap, 5 expect | All mutex-related unwrap fixed |
|
||||
| `irq_reactor.rs` | ~750 | 17 unwrap, 6 expect, 4 panic | All fixed; grow_event_ring fully implemented |
|
||||
| `ring.rs` | ~200 | 1 panic (trb_phys_ptr) | Returns Result instead of panicking |
|
||||
| `event.rs` | ~60 | 1 TODO | ERDP split into dequeue_ptr() + erdp(&RuntimeRegs) |
|
||||
|
||||
### Class drivers
|
||||
|
||||
| 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, 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
|
||||
|
||||
### Host-side QEMU harnesses
|
||||
|
||||
| Script | What it tests | Limitations |
|
||||
|---|---|---|
|
||||
| `test-xhci-device-lifecycle-qemu.sh --check` | Bounded xHCI hotplug lifecycle proof: runtime attach → configure → driver spawn → detach for HID and storage devices | QEMU-only; monitor-driven hotplug; not a broad hardware stress test |
|
||||
| `test-usb-qemu.sh --check` | Full stack: xHCI interrupt mode, HID spawn, SCSI spawn, bounded sector-0 readback, BOS processing, crash errors | QEMU-only; log-grep based; no guest-side write proof |
|
||||
| `test-usb-storage-qemu.sh` | USB mass storage autospawn + sector-0 readback + crash patterns | No guest-side write proof yet; no multi-LUN; no UAS |
|
||||
| `test-xhci-irq-qemu.sh --check` | xHCI interrupt delivery mode (MSI/MSI-X/INTx) | No devices attached during check; single log grep |
|
||||
|
||||
### In-guest tooling
|
||||
|
||||
| Tool | What it does | Installation |
|
||||
|---|---|---|
|
||||
| `lsusb` | Walks `/scheme/usb.*`, reads descriptors, shows vendor:product + quirks | Installed via `redbear-hwutils` recipe |
|
||||
| `redbear-usb-check` | Scheme tree walk with pass/fail exit code | Installed via `redbear-hwutils` recipe |
|
||||
| `redbear-info --verbose` | Reports USB controller count and integration status | Installed via `redbear-info` recipe |
|
||||
|
||||
### Runbook
|
||||
|
||||
`local/docs/USB-VALIDATION-RUNBOOK.md` documents two operator paths:
|
||||
- **Path A**: Host-side QEMU validation via `test-usb-qemu.sh --check`
|
||||
- **Path B**: Interactive guest validation via `redbear-usb-check`
|
||||
|
||||
### What is NOT validated
|
||||
|
||||
- Real hardware USB controllers (QEMU `qemu-xhci` only)
|
||||
- Hub topology (direct-attached devices only)
|
||||
- USB 3 SuperSpeed data paths
|
||||
- Isochronous or streaming transfers
|
||||
- Hot-plug stress testing
|
||||
- USB storage data I/O (read/write to block device)
|
||||
- USB device mode / OTG / USB-C
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Repo-fit note
|
||||
|
||||
Some implementation targets live in upstream-managed trees such as
|
||||
`recipes/core/base/source/...`. In Red Bear, work against those paths is carried through the
|
||||
appropriate patch carrier under `local/patches/` until intentionally upstreamed. This plan names
|
||||
the technical target path, not a recommendation to bypass Red Bear's overlay/patch discipline.
|
||||
|
||||
### Phase U0 — Support Model and Scope Freeze
|
||||
|
||||
**Goal**: Make USB claims honest and reproducible before widening implementation scope.
|
||||
|
||||
**What to do**:
|
||||
|
||||
- Define USB support labels per profile: `builds`, `enumerates`, `usable`, `validated`
|
||||
- Declare Red Bear's near-term USB scope explicitly as **host-first**
|
||||
- Record that device mode / USB-C / PD / alt-modes / USB4 are later decision points, not implied
|
||||
current scope
|
||||
- Add USB status guidance to the profile/support-language discipline used elsewhere in Red Bear
|
||||
|
||||
**Where**: `local/docs/PROFILE-MATRIX.md`, `docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md`, this
|
||||
document.
|
||||
|
||||
**Exit criteria**: USB claims are tied to a named profile or package-group slice; no doc implies
|
||||
broad USB support without a matching validation label.
|
||||
|
||||
---
|
||||
|
||||
### Phase U1 — xHCI Controller Baseline
|
||||
|
||||
**Status**: Substantially complete in the Red Bear patch layer. Runtime validation still QEMU-only.
|
||||
|
||||
**Completed (Red Bear patch)**:
|
||||
- BOS/SuperSpeed descriptor fetching wired up
|
||||
- Speed detection for hub child devices with `UsbSpeed` enum
|
||||
- Interrupt-driven operation restored (MSI/MSI-X/INTx)
|
||||
- Event ring growth fully implemented (ring doubling, DMA, ERDP/ERSTBA, DCS)
|
||||
- 88 error handling fixes across scheme.rs, mod.rs, irq_reactor.rs, ring.rs
|
||||
- ERDP split into `dequeue_ptr()` + `erdp(&RuntimeRegs)`
|
||||
- `trb_phys_ptr()` returns `Result<u64>`
|
||||
- Mutex poisoning recovery on all hot-path locks
|
||||
|
||||
**Remaining**:
|
||||
- Validate one controller family on real hardware (requires hardware)
|
||||
- Tighten controller-state correctness under sustained load (requires hardware)
|
||||
- Address remaining ~57 TODO/FIXME design notes (ongoing, not blocking)
|
||||
- SuperSpeedPlus differentiation via Extended Port Status (xHCI spec extension)
|
||||
- TTT (Think Time) propagation from parent hub descriptor into Slot Context
|
||||
- Event ring growth: copy pending TRBs from old ring to avoid losing in-flight events under sustained load
|
||||
|
||||
**Where**: `recipes/core/base/source/drivers/usb/xhcid/` (via `local/patches/base/redox.patch`)
|
||||
|
||||
**Exit criteria**: one target controller family repeatedly boots without `xhcid` panic on real
|
||||
hardware; controller enumerates attached devices reliably across repeated boot cycles.
|
||||
|
||||
---
|
||||
|
||||
### Phase U2 — Topology, Configuration, and Hotplug Correctness
|
||||
|
||||
**Status**: Partially complete.
|
||||
|
||||
**Completed (Red Bear patch)**:
|
||||
- USB 3 hub endpoint configuration stall handled
|
||||
- `endp_direction` off-by-one fixed (`checked_sub(1)`)
|
||||
- `cfg_idx` assigned after validation
|
||||
- xHCI lifecycle gating prevents new I/O from entering while a port is detaching
|
||||
- `attach_device()` no longer leaves a published partially-enumerated `PortState` on attach failure
|
||||
- `detach_device()` now waits for in-flight lifecycle operations before removing the port state
|
||||
- `configure_endpoints_once()` is transactional: endpoint state is staged locally, input-context
|
||||
mutations are snapshotted, and rollback is attempted if `CONFIGURE_ENDPOINT` or
|
||||
`SET_CONFIGURATION` fails
|
||||
- `CLEAR_FEATURE` uses correct USB endpoint address from descriptor
|
||||
- `usbhubd` status_change_buf sizing and bitmap parsing fixed
|
||||
- Hub interrupt EP1 status change detection replacing polling
|
||||
- `usbhubd` error handling improved — all ~22 `expect()` eliminated, `Result` return type, graceful per-port failure handling
|
||||
- `usbhubd` interrupt-driven change detection — reads hub interrupt IN endpoint for status change bitmap; falls back to 1-second polling if endpoint unavailable; initial full scan preserved at startup
|
||||
|
||||
**Remaining**:
|
||||
- Validate repeated attach/detach/reset behavior under stress (requires real hardware)
|
||||
|
||||
**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`
|
||||
|
||||
**Exit criteria**: repeated hub and hotplug scenarios complete without stale topology state; at
|
||||
least one composite device configures correctly beyond the simplest path.
|
||||
|
||||
---
|
||||
|
||||
### Phase U3 — HID Modernization
|
||||
|
||||
**Status**: Partially complete.
|
||||
|
||||
**Completed (Red Bear patch)**:
|
||||
- `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** (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`
|
||||
|
||||
**Exit criteria**: two independent USB HID devices appear as separate input sources; hot-unplug and
|
||||
replug do not collapse all USB HID into one anonymous stream.
|
||||
|
||||
---
|
||||
|
||||
### Phase U4 — Storage, Userspace API, and Class Expansion
|
||||
|
||||
**Status**: Storage quality improved; userspace API story still low-level.
|
||||
|
||||
**Completed (Red Bear patch)**:
|
||||
- `usbscsid` BOT transport: all `panic!()` replaced with stall recovery and error returns
|
||||
- Correct endpoint addresses for `CLEAR_FEATURE` and `get_max_lun`
|
||||
- Iterative bounded CSW read loop
|
||||
- SCSI block descriptor parsing with bounds checks
|
||||
- `usbscsid` SCSI layer: `plain::from_bytes().unwrap()` replaced with typed `ScsiError` and fallible `parse_bytes`/`parse_mut_bytes` helpers
|
||||
- `usbscsid` main.rs: fallible `run()` helper, event loop continues on individual failures
|
||||
- `ReadCapacity16` implemented with automatic fallback when `ReadCapacity10` returns max LBA (0xFFFFFFFF)
|
||||
- `usbscsid` now issues bounded `SYNCHRONIZE CACHE(10/16)` commands when the runtime storage quirk
|
||||
set includes `needs_sync_cache`, using Linux `sd.c` sync-cache behavior as a donor reference for
|
||||
command selection and tolerant error handling.
|
||||
|
||||
**Remaining** (all require hardware or design decisions):
|
||||
- Runtime I/O validation: prove stall recovery works under real device I/O (requires hardware)
|
||||
- Decide whether BOT-only is sufficient short-term or UAS is needed (design decision)
|
||||
- Bring `libusb` to a runtime-tested state or replace with Red Bear-native API (large scope, deferred)
|
||||
- Choose the next USB class families explicitly (design decision)
|
||||
|
||||
**Suggested class priority**: storage baseline → generic userspace API → USB networking or
|
||||
Bluetooth dongle → audio/video only after controller maturity justifies it
|
||||
|
||||
**Where**: `recipes/core/base/source/drivers/storage/usbscsid/`, `recipes/wip/libs/other/libusb/`,
|
||||
`local/recipes/system/redbear-hwutils/`
|
||||
|
||||
**Exit criteria**: one USB storage path validated on target profile; one coherent userspace USB API
|
||||
story documented and works in practice; next supported class families named explicitly.
|
||||
|
||||
---
|
||||
|
||||
### Phase U5 — Modern USB Scope Decision Gate
|
||||
|
||||
**Goal**: Decide whether Red Bear remains a host-only USB system or grows toward a modern USB
|
||||
platform.
|
||||
|
||||
**What to decide**:
|
||||
- Host-only versus device mode / gadget support
|
||||
- Whether OTG / dual-role matters for target hardware
|
||||
- Whether USB-C / PD / alt-mode policy belongs in Red Bear's target platform story
|
||||
- Whether USB4 / Thunderbolt-class behavior is in scope or explicitly excluded
|
||||
|
||||
**Why this phase exists**: These are architectural choices, not small driver add-ons. A
|
||||
future-proof stack cannot leave them implicit forever.
|
||||
|
||||
**Exit criteria**: a written architecture decision exists for included and excluded modern USB
|
||||
scope.
|
||||
|
||||
---
|
||||
|
||||
### Phase U6 — Validation Slices and Support Claims
|
||||
|
||||
**Status**: Partially complete.
|
||||
|
||||
**Completed**:
|
||||
- `test-usb-qemu.sh` — full USB stack validation harness (6 checks)
|
||||
- `test-usb-storage-qemu.sh` — USB mass storage autospawn check
|
||||
- `test-xhci-irq-qemu.sh` — xHCI interrupt delivery mode check
|
||||
- `test-xhci-device-lifecycle-qemu.sh` — bounded xHCI attach/configure/detach hotplug proof
|
||||
- `USB-VALIDATION-RUNBOOK.md` — operator documentation with Paths A and B
|
||||
- `redbear-usb-check` — in-guest scheme-tree checker (now installed in image)
|
||||
- `lsusb` — full USB scheme walk with descriptor parsing and quirks integration
|
||||
- `redbear-info` — passive USB controller reporting
|
||||
|
||||
**Remaining** (all require hardware):
|
||||
- Add hardware-matrix coverage for target controllers and class families
|
||||
- Add USB storage data I/O validation (read/write to block device)
|
||||
- Add repeated hardware hot-plug stress testing beyond the bounded QEMU lifecycle slice
|
||||
|
||||
**Exit criteria**: at least one profile can honestly claim a validated USB baseline for named
|
||||
controller/class scope; USB support language in docs matches real test evidence.
|
||||
|
||||
## Support-Language Guidance
|
||||
|
||||
Until U1 through U3 are substantially complete, Red Bear should avoid broad phrases such as:
|
||||
|
||||
- "USB support works"
|
||||
- "USB storage is supported"
|
||||
- "USB is complete"
|
||||
|
||||
Prefer language such as:
|
||||
|
||||
- "xHCI host support is present but experimental"
|
||||
- "USB enumeration and HID-adjacent host paths exist in-tree"
|
||||
- "USB support remains controller-variable"
|
||||
- "USB storage support exists in-tree with improved error handling, but is not yet a broad hardware
|
||||
support claim"
|
||||
- "USB error handling and correctness carry significant Red Bear patches over upstream; see
|
||||
`local/patches/base/redox.patch` for details"
|
||||
|
||||
## Linux Kernel USB Data Mining
|
||||
|
||||
### linux-kpi Scope Clarification
|
||||
|
||||
The `linux-kpi` compatibility layer (`local/recipes/drivers/linux-kpi/`) is used **exclusively for
|
||||
GPU and Wi-Fi drivers** — it provides Linux kernel API headers and Rust FFI implementations for
|
||||
porting Linux C drivers in those domains to Redox. It does **not** cover USB and contains no USB
|
||||
headers, USB device ID tables, or USB driver implementations.
|
||||
|
||||
The linux-kpi header inventory (`src/c_headers/`) covers: PCI, DMA, IRQ, firmware, networking
|
||||
(netdevice, skbuff, ieee80211, nl80211, cfg80211, mac80211), DRM, workqueue, timer, wait, sync,
|
||||
memory, and related kernel infrastructure — but zero USB content. This is documented globally in
|
||||
`AGENTS.md` and `local/AGENTS.md`.
|
||||
|
||||
### Linux 7.0 Source Availability
|
||||
|
||||
Linux kernel 7.0 (stable, released 2026-04-13) is extracted at
|
||||
`build/linux-kernel-cache/linux-7.0/` for USB data mining purposes. This is a build cache, not a
|
||||
tracked source tree — it can be re-fetched from `cdn.kernel.org` at any time.
|
||||
|
||||
```bash
|
||||
# Re-fetch if needed:
|
||||
curl -L -o build/linux-kernel-cache/linux-7.0.tar.xz \
|
||||
"https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.0.tar.xz"
|
||||
tar xf build/linux-kernel-cache/linux-7.0.tar.xz -C build/linux-kernel-cache/
|
||||
```
|
||||
|
||||
### Mining Inventory — What Linux 7.0 Contains
|
||||
|
||||
| Data Source | Linux Path | Entries | Lines | Relevance |
|
||||
|---|---|---|---|---|
|
||||
| USB device quirks | `drivers/usb/core/quirks.c` | 64 device + 5 AMD-resume + 4 endpoint-ignore | 800 | Directly feed our quirk tables |
|
||||
| USB quirk flag definitions | `include/linux/usb/quirks.h` | 19 flags | 84 | We have 9 of 19; 10 missing |
|
||||
| USB storage unusual devices | `drivers/usb/storage/unusual_devs.h` | 323 entries | 2513 | Mass storage device workarounds |
|
||||
| USB hub driver | `drivers/usb/core/hub.c` | — | 6567 | TT handling, hub descriptor parsing |
|
||||
| xHCI host driver | `drivers/usb/host/xhci*.c/h` | ~15 files | ~30000 | Controller quirks, TRB handling |
|
||||
| SCSI disk driver | `drivers/scsi/sd.c` | — | 4467 | SCSI command support tables |
|
||||
| USB core headers | `include/linux/usb/*.h` | 75 headers | — | ch9.h (descriptors), hcd.h, storage.h, uas.h |
|
||||
|
||||
### Extraction Tool
|
||||
|
||||
`local/scripts/extract-linux-quirks.py` parses Linux kernel source and generates Red Bear TOML
|
||||
quirk entries. Handles three source formats:
|
||||
- `drivers/usb/core/quirks.c` → `[[usb_quirk]]` TOML entries (146 entries from Linux 7.0)
|
||||
- `drivers/usb/storage/unusual_devs.h` → `[[usb_storage_quirk]]` TOML entries (214 entries from Linux 7.0)
|
||||
- `drivers/pci/quirks.c` → `[[pci_quirk]]` TOML entries (explicit high-confidence handler-body mappings only, requires review)
|
||||
|
||||
USB quirk extraction is direct and does not require review. PCI quirk extraction now emits only
|
||||
explicit high-confidence handler-body mappings and still requires manual review before committing.
|
||||
|
||||
The extraction script needs extension to also handle `drivers/usb/storage/unusual_devs.h` for mass
|
||||
storage device entries (323 entries, different macro format `UNUSUAL_DEV`).
|
||||
|
||||
### Flag Gap Analysis
|
||||
|
||||
**Flags we have (22, fully aligned with Linux 7.0):** `NO_STRING_FETCH`, `RESET_DELAY`, `NO_USB3`,
|
||||
`NO_SET_CONFIG`, `NO_SUSPEND`, `NEED_RESET`, `BAD_DESCRIPTOR`, `NO_LPM`, `NO_U1U2`,
|
||||
`NO_SET_INTF`, `CONFIG_INTF_STRINGS`, `NO_RESET`, `HONOR_BNUMINTERFACES`, `DEVICE_QUALIFIER`,
|
||||
`IGNORE_REMOTE_WAKEUP`, `DELAY_CTRL_MSG`, `HUB_SLOW_RESET`, `NO_BOS`,
|
||||
`SHORT_SET_ADDR_TIMEOUT`, `FORCE_ONE_CONFIG`, `ENDPOINT_IGNORE`, `LINEAR_FRAME_BINTERVAL`
|
||||
|
||||
**All 19 Linux 7.0 USB_QUIRK flags are now covered.** The mapping table below documents the
|
||||
correspondence for future reference.
|
||||
|
||||
| Linux Flag | Purpose | Impact | Mapping Notes |
|
||||
|---|---|---|---|
|
||||
| `USB_QUIRK_RESET_RESUME` | Device can't resume, needs reset instead | High — many devices | Roughly maps to our `NEED_RESET` |
|
||||
| `USB_QUIRK_NO_SET_INTF` | Device can't handle SetInterface requests | Medium — composite devices | Our `NO_SET_CONFIG` targets SET_CONFIGURATION, not SET_INTERFACE |
|
||||
| `USB_QUIRK_CONFIG_INTF_STRINGS` | Device can't handle config/interface strings | Low — enumeration robustness | New concept |
|
||||
| `USB_QUIRK_RESET` | Device can't be reset at all | Medium — prevents crashes on morph devices | No equivalent |
|
||||
| `USB_QUIRK_HONOR_BNUMINTERFACES` | Wrong interface count in descriptor | Medium — composite devices | New concept |
|
||||
| `USB_QUIRK_DEVICE_QUALIFIER` | Device can't handle device_qualifier descriptor | Low — skip descriptor fetch | New concept |
|
||||
| `USB_QUIRK_IGNORE_REMOTE_WAKEUP` | Device generates spurious wakeup | Low — power management | New concept |
|
||||
| `USB_QUIRK_DELAY_CTRL_MSG` | Device needs pause after every control message | Medium — prevents timeouts | New concept |
|
||||
| `USB_QUIRK_HUB_SLOW_RESET` | Hub needs extra delay after port reset | High — our Terminus hub entry (0x1A40:0x0101) currently has `no_lpm` but Linux marks it `HUB_SLOW_RESET` | New concept |
|
||||
| `USB_QUIRK_NO_BOS` | Skip BOS descriptor (hangs at SuperSpeedPlus) | High — we added BOS fetching, some devices hang | New concept |
|
||||
| `USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT` | Short timeout for SET_ADDRESS | Low — controller-specific | New concept |
|
||||
| `USB_QUIRK_FORCE_ONE_CONFIG` | Device claims zero configs, force to 1 | Low — edge case | New concept |
|
||||
| `USB_QUIRK_ENDPOINT_IGNORE` | Device has endpoints that should be ignored | Medium — audio devices | New concept |
|
||||
| `USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL` | bInterval is linear frames, not exponential | Low — interrupt endpoint timing | Related to our `BAD_DESCRIPTOR` |
|
||||
|
||||
Note: Some Linux flags overlap semantically with our existing flags. The exact mapping requires a
|
||||
per-flag design decision — either extend existing flags with clarified semantics or add new parallel
|
||||
flags.
|
||||
|
||||
### Duplicate Quirk Table Problem
|
||||
|
||||
`xhcid` carries its own copy of the USB quirk table at
|
||||
`recipes/core/base/source/drivers/usb/xhcid/src/usb_quirks.rs`. The canonical table is in
|
||||
`local/recipes/drivers/redox-driver-sys/source/src/quirks/usb_table.rs`.
|
||||
|
||||
Both tables now carry the expanded 22-flag set and synchronized entries. The xhcid copy contains a
|
||||
representative subset of the most common entries (early-boot fallback when `/etc/quirks.d/` is not
|
||||
yet mounted), while the full 146-entry table and TOML runtime loading serve as the complete
|
||||
runtime source.
|
||||
|
||||
**Long-term resolution:** xhcid should import from redox-driver-sys directly rather than
|
||||
maintaining a duplicate. Until then, both must be kept in sync when adding new entries.
|
||||
|
||||
### Prioritized Mining Targets
|
||||
|
||||
**Tier 1 — COMPLETED:**
|
||||
|
||||
1. ✅ **USB device quirk table expansion** — All 146 entries from Linux 7.0 `quirks.c` extracted
|
||||
into `usb_table.rs` and `20-usb.toml`. Covers HP, Microsoft, Logitech, Lenovo, SanDisk,
|
||||
Corsair, Realtek, NVIDIA, ASUS, Dell, Elan, Genesys, Razer, and others.
|
||||
|
||||
2. ✅ **`USB_QUIRK_NO_BOS` flag** — Added. 4 devices that hang at SuperSpeedPlus BOS fetch are
|
||||
now flagged: ASUS TUF 4K PRO (0x0B05:0x1AB9), Avermedia GC553G2 (0x07CA:0x2553), Elgato 4K X
|
||||
(0x0FD9:0x009B), UGREEN 35871 (0x2B89:0x5871), ezcap401 (0x32ED:0x0401).
|
||||
|
||||
3. ✅ **`USB_QUIRK_HUB_SLOW_RESET` flag** — Added. Terminus hub (0x1A40:0x0101) corrected from
|
||||
`no_lpm` to `hub_slow_reset`.
|
||||
|
||||
4. ✅ **Flag gap closed** — All 19 Linux 7.0 USB_QUIRK flags now mapped. 13 new flags added:
|
||||
`NO_SET_INTF`, `CONFIG_INTF_STRINGS`, `NO_RESET`, `HONOR_BNUMINTERFACES`,
|
||||
`DEVICE_QUALIFIER`, `IGNORE_REMOTE_WAKEUP`, `DELAY_CTRL_MSG`, `HUB_SLOW_RESET`, `NO_BOS`,
|
||||
`SHORT_SET_ADDR_TIMEOUT`, `FORCE_ONE_CONFIG`, `ENDPOINT_IGNORE`, `LINEAR_FRAME_BINTERVAL`.
|
||||
|
||||
5. ✅ **Duplicate quirk tables synchronized** — Both `usb_table.rs` (redox-driver-sys) and
|
||||
`usb_quirks.rs` (xhcid) now carry the expanded flag set and synchronized entries.
|
||||
|
||||
6. ✅ **USB storage unusual_devs.h** — 214 entries extracted from Linux 7.0 into
|
||||
`local/recipes/system/redbear-quirks/source/quirks.d/30-storage.toml` (1716 lines). Extraction
|
||||
script extended to handle `UNUSUAL_DEV` macro format. Most common flags: `ignore_residue` (46),
|
||||
`fix_capacity` (34), `single_lun` (28), `max_sectors_64` (22), `fix_inquiry` (22). Includes
|
||||
`initial_read10` entries for Feiya SD/SDHC reader and Corsair Padlock v2.
|
||||
|
||||
7. ✅ **usbscsid storage quirk integration** — Storage quirks are now active at runtime.
|
||||
`usbscsid/src/quirks.rs` reads `[[usb_storage_quirk]]` entries from `/etc/quirks.d/*.toml`
|
||||
and applies them to the BOT transport and SCSI command layers. Active behavioral flags:
|
||||
- `IGNORE_RESIDUE`: suppresses CSW residue in BOT `send_command`
|
||||
- `FIX_CAPACITY`: adjusts block count from READ CAPACITY(10) by -1
|
||||
- `SINGLE_LUN`: enforces LUN=0 in CBW (future-proof for multi-LUN support)
|
||||
- `MAX_SECTORS_64`: clamps transfer length to 64 sectors in SCSI read/write
|
||||
- `INITIAL_READ10`: uses READ(10)/WRITE(10) instead of READ(16)/WRITE(16)
|
||||
Vendor/product IDs are extracted from `DevDesc` at daemon startup. A compiled-in fallback
|
||||
table covers 5 common devices for early-boot correctness.
|
||||
|
||||
8. ✅ **xhcid USB device quirk consumption** — xhcid now stores per-device `UsbQuirkFlags` in
|
||||
`PortState` and applies them during enumeration and runtime requests. Active behavioral flags:
|
||||
- `NO_STRING_FETCH`: skips manufacturer/product/serial/configuration string fetches
|
||||
- `BAD_DESCRIPTOR`: tolerates language/string descriptor fetch failures and continues interface parsing when malformed endpoint descriptors appear
|
||||
- `RESET_DELAY`: extends first-touch post-reset settle time via early `PortId`-based lookup
|
||||
- `HUB_SLOW_RESET`: uses a longer hub-oriented reset settle time via early `PortId`-based lookup
|
||||
- `NO_BOS`: skips BOS descriptor fetch and leaves superspeed capability detection false
|
||||
- `SHORT_SET_ADDR_TIMEOUT`: uses a shorter `Address Device` command timeout via early `PortId`-based lookup
|
||||
- `FORCE_ONE_CONFIG`: limits enumeration to configuration index 0 (configuration value 1 path)
|
||||
- `HONOR_BNUMINTERFACES`: stops interface parsing at `bNumInterfaces`
|
||||
- `DELAY_CTRL_MSG`: inserts a short post-control-transfer delay
|
||||
- `NO_SET_CONFIG`: skips `SET_CONFIGURATION`
|
||||
- `NO_SET_INTF`: skips `SET_INTERFACE`
|
||||
- `NEED_RESET`: issues xHC `Reset Device` automatically after transfer failures
|
||||
The early-enumeration timing path now uses optional TOML `port = "<root>[.<route>...]"`
|
||||
selectors in `[[usb_quirk]]` entries for quirks that must act before vendor/product are known.
|
||||
|
||||
9. ✅ **xhcid suspend/resume API skeleton** — xhcid now exposes explicit `port<n>/suspend` and
|
||||
`port<n>/resume` endpoints plus matching `XhciClientHandle::{suspend_device,resume_device}`
|
||||
helpers. `PortState` now tracks `PortPmState::{Active,Suspended}` and xhcid enforces
|
||||
`NO_SUSPEND` by rejecting suspend with `EOPNOTSUPP`. While suspended, control/data/reset
|
||||
activity returns `EBUSY`.
|
||||
|
||||
10. ✅ **usbhubd suspend coordination slice** — `usbhubd` now tracks downstream child suspend
|
||||
state and mirrors USB 2 hub-port suspend status into child xhcid devices via
|
||||
`suspend_device()` / `resume_device()`. This gives us the first real cross-layer coordination
|
||||
path for hub-attached devices without inventing a separate PM daemon. Remaining gap: suspend
|
||||
policy/origination is still external, and USB 3 link-state-driven coordination is not yet
|
||||
implemented.
|
||||
|
||||
**Tier 2 — Medium-term (improves robustness):**
|
||||
|
||||
5. **TT handling from hub.c** — Linux's hub driver reads `wHubDelay` and `bNbrPorts` from hub
|
||||
descriptors to populate TT think time and MTT capability. Our xHCI driver hardcodes `ttt = 0`
|
||||
and `mtt = false`. Mining the hub descriptor parsing logic from `hub.c` would replace these
|
||||
stubs with correct values.
|
||||
|
||||
6. **xHCI controller quirks from xhci-pci.c** — Linux has per-vendor controller workarounds
|
||||
(Intel PCH, AMD, Etron, Fresco, VIA). Our driver has no controller-specific paths. Mining the
|
||||
quirk table and applying it through our existing PCI quirk system would add real-hardware
|
||||
robustness.
|
||||
|
||||
7. **SCSI command selection from sd.c** — READ(10)/WRITE(10) support is now implemented
|
||||
(triggered by `INITIAL_READ10` quirk flag). Remaining: REPORT LUNS for multi-LUN devices,
|
||||
SYNCHRONIZE CACHE (triggered by `NEEDS_SYNC_CACHE` flag), and START STOP UNIT for power
|
||||
management.
|
||||
|
||||
**Tier 3 — Future (enables new device classes):**
|
||||
|
||||
8. **USB class/subclass/protocol tables from ch9.h** — Complete class code definitions for device
|
||||
matching in `drivers.toml`.
|
||||
|
||||
9. **USB endpoint descriptor parsing from message.c** — Extended endpoint type mapping for streams
|
||||
and isochronous support.
|
||||
|
||||
### Mining into the Build
|
||||
|
||||
The Linux kernel source at `build/linux-kernel-cache/` is a build cache, not a tracked dependency.
|
||||
Mined data must be materialized into durable locations:
|
||||
|
||||
| Mined Data | Target Location | Format |
|
||||
|---|---|---|
|
||||
| USB device quirks | `local/recipes/system/redbear-quirks/source/quirks.d/20-usb.toml` | TOML (146 entries ✅) |
|
||||
| USB compiled-in quirks | `local/recipes/drivers/redox-driver-sys/source/src/quirks/usb_table.rs` | Rust (146 entries ✅) |
|
||||
| PCI controller quirks | `local/recipes/system/redbear-quirks/source/quirks.d/10-pci.toml` | TOML |
|
||||
| Storage device flags | `local/recipes/system/redbear-quirks/source/quirks.d/30-storage.toml` | TOML (214 entries ✅, active at runtime ✅) |
|
||||
| Flag definitions | `local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs` | Rust bitflags (22 USB flags ✅) |
|
||||
|
||||
The extraction script at `local/scripts/extract-linux-quirks.py` should be extended to also handle
|
||||
`drivers/usb/storage/unusual_devs.h` for mass storage device entries.
|
||||
|
||||
## Summary
|
||||
|
||||
USB in Red Bear today is not missing. It is a real userspace host-side subsystem with meaningful
|
||||
enumeration, runtime observability, hub/HID infrastructure, and a low-level userspace API.
|
||||
|
||||
The Red Bear patch layer carries substantial error handling and correctness improvements over the
|
||||
upstream source: 88 error handling fixes (mutex poisoning recovery, expect/panic replacement, Result
|
||||
conversions), multiple correctness bug fixes, real event ring growth,
|
||||
class driver error handling improvements (all three USB class daemons now use `Result` types with
|
||||
zero `unwrap()`/`expect()` panics), interrupt-driven hub change detection, `ReadCapacity16`
|
||||
for large disk support, and a USB quirk table expanded from 8 to 146 entries with 22 quirk flags
|
||||
mined from Linux 7.0.
|
||||
|
||||
Recent bounded maturity progress:
|
||||
|
||||
- `xhcid` now tracks active alternate settings per interface in `PortState` and resolves endpoint
|
||||
descriptors through that active-alternate map instead of flattening all interface descriptors
|
||||
indiscriminately.
|
||||
- Direct unit coverage now exists for both default-alternate endpoint selection and
|
||||
alternate-setting-aware endpoint remapping.
|
||||
- `xhcid` now also preserves previously selected alternates on the same configuration and applies a
|
||||
requested interface/alternate override before endpoint planning, so alternate-setting
|
||||
reconfiguration no longer silently falls back to all-zero defaults.
|
||||
- `xhcid` endpoint-direction lookup now also follows the active interface/alternate selection state
|
||||
instead of reading from the first configuration/interface pair unconditionally.
|
||||
- `xhcid` driver spawning now also follows the selected configuration and active alternate map
|
||||
instead of hardcoding the first configuration and ignoring non-zero alternates.
|
||||
- `xhcid` now keeps per-port lifecycle state so detach blocks new transfer/configure/suspend/resume
|
||||
work, waits for in-flight operations to drain, and removes the published port state only after
|
||||
slot disable succeeds.
|
||||
- `xhcid` endpoint configuration is now transactional: software endpoint bookkeeping stays staged
|
||||
until `CONFIGURE_ENDPOINT` and optional `SET_CONFIGURATION` succeed, and the input context is
|
||||
restored with an explicit rollback attempt on failure.
|
||||
- the xHCI IRQ reactor now replaces the old `TODO: grow event ring` stub with a preserve-and-grow
|
||||
path that copies unread event TRBs into a larger event ring and reprograms ERST registers instead
|
||||
of dropping pending events during `EventRingFull` recovery.
|
||||
- `usbhubd` now derives USB 2 hub TT Think Time from the hub descriptor using the same bounded
|
||||
Linux-compatible encoding and passes it through `ConfigureEndpointsReq`, and `xhcid` now writes
|
||||
that value into the Slot Context TT information bits for hub devices.
|
||||
- xHCI endpoint-context calculations are now protocol-speed-aware for SuperSpeedPlus, so interval
|
||||
and ESIT-payload selection use the resolved port protocol speed instead of relying only on
|
||||
endpoint companion presence.
|
||||
|
||||
All validation is QEMU-only. No real hardware USB testing exists.
|
||||
|
||||
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 via named producers, hotplug add/remove (inputd redesign complete, ps2d and usbhidd migrated)
|
||||
- Userspace USB API: `libusb` WIP, no coherent native story
|
||||
|
||||
> **See also:** `local/docs/boot-logs/REDBEAR-MINI-BOOT-PS2D-INPUTD-LOG-FIX.md`
|
||||
> for the 2026-06-30 fix that made `usbhidd` (and its `ps2d` sibling)
|
||||
> visible in the boot log. With the fix, an operator can distinguish
|
||||
> "usbhidd dead" (no `@usbhidd:` line during enumeration, OR
|
||||
> `@ps2d:<line> INFO ps2d: registered producer handle` missing) from
|
||||
> "usbhidd alive but not enumerated by XHCI" (line present, no
|
||||
> corresponding consumer event).
|
||||
|
||||
**Hardware-dependent or design decisions:**
|
||||
- Real hardware validation: no controller tested outside QEMU
|
||||
- Hot-plug stress testing beyond the new bounded QEMU lifecycle harness
|
||||
- Storage write validation (bounded sector-0 readback proof now exists in QEMU via `test-usb-storage-qemu.sh`, but guest-side write verification to the USB-backed block device is still open)
|
||||
- usbhubd 1-second polling fallback (only exercisable with real hub hardware)
|
||||
- Modern USB scope decision: device mode / USB-C / PD
|
||||
|
||||
Software items are tracked in Phase U1 (xHCI internals) and Phase U2 (configuration/composite).
|
||||
Architectural and hardware items are tracked in Phase U1 (controller hardware validation), Phase U2
|
||||
(hub polling fallback), Phase U3 (HID), Phase U4 (storage/API), Phase U5 (modern USB scope
|
||||
decision), and Phase U6 (validation).
|
||||
|
||||
Linux kernel USB data mining is documented in the "Linux Kernel USB Data Mining" section above.
|
||||
Linux 7.0 source is available at `build/linux-kernel-cache/linux-7.0/` with 146 USB device quirks,
|
||||
22 quirk flags (all 19 Linux USB_QUIRK flags covered), 214 active storage device quirks
|
||||
consumed at runtime by usbscsid, and extensive xHCI/hub/SCSI reference code ready for extraction.
|
||||
@@ -0,0 +1,631 @@
|
||||
# Red Bear OS USB Implementation Plan — v2
|
||||
|
||||
> **Status:** Canonical. Replaces `archived/USB-IMPLEMENTATION-PLAN-v1-2026-04.md`.
|
||||
> **Date:** 2026-07-07.
|
||||
> **Supersession reason:** v1 (Apr 2026) overstated several capabilities relative to the
|
||||
> then-current source. v2 re-audits every daemon against `local/sources/base/` HEAD,
|
||||
> aligns with Redox 0.x USB HEAD (Jan 2025 – Jun 2026), and reorganizes phases around the
|
||||
> actual bare-metal correctness gaps. Validation labels are now source-anchored rather than
|
||||
> patch-anchored.
|
||||
>
|
||||
> **Sibling docs:**
|
||||
> `USB-VALIDATION-RUNBOOK.md` (operator path — restored from `archived/`); the older
|
||||
> `archived/USB-BOOT-INPUT-PLAN.md` and `archived/XHCID-DEVICE-IMPROVEMENT-PLAN.md` are
|
||||
> kept as historical reference but are not the planning authority.
|
||||
|
||||
---
|
||||
|
||||
## 0. Purpose and scope
|
||||
|
||||
This plan is the **single planning authority** for the USB subsystem in Red Bear OS. It
|
||||
answers four questions honestly:
|
||||
|
||||
1. **What is built?** — every host controller, class driver, scheme, and observability tool
|
||||
that actually exists in `local/sources/base/` and `local/recipes/drivers/`. Status is
|
||||
derived from the current source tree, not from prior memory or from patch carriers.
|
||||
2. **What was patched?** — every durable Red Bear modification, with file paths
|
||||
(`local/patches/base/P*.patch` for the base module, dedicated local recipes or forks
|
||||
otherwise).
|
||||
3. **What is actually usable?** — explicitly distinguishes **builds**, **enumerates**,
|
||||
**usable (narrow path)**, **validated (QEMU)**, **validated (real hardware)**, and
|
||||
**experimental**. A label is only ever **validated** if the matching proof has run
|
||||
on the matching artifact under the matching config.
|
||||
4. **What is missing?** — the real bare-metal-blockers, the upstream-comparable gaps, the
|
||||
architectural decisions still deferred, and the durability problems that this plan owns.
|
||||
|
||||
### Validation labels (canonical, do not redefine elsewhere)
|
||||
|
||||
- **builds** — code is in tree and compiles. Not a usability claim.
|
||||
- **enumerates** — runtime surfaces can discover controllers, ports, descriptors.
|
||||
- **usable (narrow path)** — one controller family / one class family works in a bounded,
|
||||
repeatable scenario; other paths are likely broken.
|
||||
- **validated (QEMU)** — a documented QEMU script passed on the matching recipe, config, and
|
||||
commit. Reproducible on a Linux x86_64 host.
|
||||
- **validated (real hardware)** — a named physical controller + class, with a captured log,
|
||||
on real bare metal. This is what an end user can expect.
|
||||
- **experimental** — present for bring-up but not in any support-promised path.
|
||||
|
||||
**Honesty rule.** `builds` is **not** equivalent to `usable`. `validated (QEMU)` is **not**
|
||||
equivalent to `validated (real hardware)`. The plan never mixes these categories. Where
|
||||
prior text conflated them, this v2 corrects.
|
||||
|
||||
### Plan structure
|
||||
|
||||
| Section | Authority | Updates cadence |
|
||||
|---|---|---|
|
||||
| §1 Source audit (controllers, class drivers, schemes, tooling) | ground truth | on every source-tree bump |
|
||||
| §2 Patch carriers | every durable Red Bear diff | on every patch add/rebase |
|
||||
| §3 Status matrix (one row per component) | single source of truth for "is it working" | on every status change |
|
||||
| §4 Upstream divergence: what Redox 0.x USB HEAD has that we have not | required adoption list | on every upstream bump |
|
||||
| §5 Bare-metal input correctness (boot-time USB keyboard) | the bare-metal failure modes | on every controller or class change |
|
||||
| §6 Phase P0–P5 (execution order) | who does what next | reviewed monthly |
|
||||
| §7 Validation inventory and bounded proofs | the proof surfaces | on every script add/break |
|
||||
| §8 Durability posture | local fork health, patch carriers, archival policy | on every base fork bump |
|
||||
| §9 Support language | how the rest of Red Bear should describe USB | on every phase change |
|
||||
|
||||
---
|
||||
|
||||
## 1. Source audit — what is actually in the tree
|
||||
|
||||
Red Bear follows the upstream Redox model: **all USB logic is in userspace** (`drivers/usb/`
|
||||
plus `local/recipes/drivers/usb-core/`). The kernel exposes `irq:`, `memory:`, `pcid:`,
|
||||
`event:`, and `scheme:` surfaces that userspace USB daemons consume. There is no kernel USB
|
||||
host stack, and the v1 phase plan's mention of "kernel MSI/MSI-X plumbing" was a reference
|
||||
to that surface, not a kernel change.
|
||||
|
||||
### 1.1 Host controllers
|
||||
|
||||
| Daemon | Source | Lines | Reality today | Scheme registered |
|
||||
|---|---|---|---|---|
|
||||
| **xhcid** | `local/sources/base/drivers/usb/xhcid/` | ~6000 LoC across 25 files | Builds. Real ring/TRB/context/transfer engine. Polling in production (see §1.6). | `usb.<pci_name>_xhci` |
|
||||
| **ehcid** | `local/recipes/drivers/ehcid/source/src/` | ~1550 LoC (3 files) | Builds. Real MMIO init, frame list, QH/TD, port reset. **No class-driver auto-spawn.** | `usb` |
|
||||
| **uhcid** | `local/recipes/drivers/uhcid/source/src/main.rs` | 35 LoC | Builds. **Real stub.** Reads PCI BAR4, sleeps forever. No scheme. | — |
|
||||
| **ohcid** | `local/recipes/drivers/ohcid/source/src/main.rs` | 35 LoC | Builds. **Real stub.** Identical pattern to uhcid. | — |
|
||||
|
||||
**Honesty corrections vs v1:**
|
||||
|
||||
- v1 said *"EHCI/UHCI/OHCI — ownership, port handling, and logging exist, but they are not
|
||||
yet full runtime enumeration paths"*. For **uhcid** and **ohcid** this is too generous —
|
||||
they are 35-line stubs that **only read PCI BAR4 and sleep**. They are not even
|
||||
ownership-grade; the controller is never probed, no port state is published, no error
|
||||
is logged past init.
|
||||
- v1 said *"xHCI interrupt-driven operation restored"*. The current source at
|
||||
`xhcid/src/main.rs:141` hardcodes polling:
|
||||
```rust
|
||||
let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
|
||||
//TODO: Fix interrupts.
|
||||
```
|
||||
The `get_int_method` function exists, MSI-X/MSI/INTx branches are written, but the
|
||||
function is bypassed at runtime. §4 captures the upstream commits that help finish this.
|
||||
|
||||
### 1.2 Class drivers
|
||||
|
||||
| Daemon | Source | LoC | Reality | Notes |
|
||||
|---|---|---|---|---|
|
||||
| **usbhubd** | `local/sources/base/drivers/usb/usbhubd/` | 249 | Builds; runs. | Polls port status (1s fallback retained from v1). |
|
||||
| **usbhidd** | `local/sources/base/drivers/input/usbhidd/` | 576 | Builds; runs. | Named-producer input (`usb-{port}-if{n}`) + legacy VT fallback. |
|
||||
| **usbscsid** | `local/sources/base/drivers/storage/usbscsid/` | ~1800 | Builds; runs. | BOT/SCSI, `ReadCapacity16`, 3 storage quirk flags active. |
|
||||
| **usbctl** | `local/sources/base/drivers/usb/usbctl/` | 54 | Builds. CLI only. | Minimal — port/endpoint status query. |
|
||||
| **ucsid** | `local/sources/base/drivers/usb/ucsid/` | 839 | Builds. | USB-C UCSI topology over ACPI + I2C; `/scheme/ucsi`. |
|
||||
| **redbear-usbaudiod** | `local/recipes/system/redbear-usbaudiod/` | (small) | Builds; wired in `redbear-mini.toml`. | USB Audio Class 1. |
|
||||
| **redbear-acmd** | `local/recipes/system/redbear-acmd/` | (small) | Builds; wired via `drivers.d/70-usb-class.toml`. | USB CDC ACM serial. |
|
||||
| **redbear-ecmd** | `local/recipes/system/redbear-ecmd/` | (small) | Builds; wired via `drivers.d/70-usb-class.toml`. | USB CDC ECM/NCM ethernet. |
|
||||
| **redbear-btusb** | `local/recipes/drivers/redbear-btusb/` | (small) | Builds. | Bluetooth USB transport — see BLUETOOTH-IMPLEMENTATION-PLAN. |
|
||||
|
||||
### 1.3 USB core library
|
||||
|
||||
| Crate | Source | Notes |
|
||||
|---|---|---|
|
||||
| **usb-core** | `local/recipes/drivers/usb-core/source/src/` | 6 files (lib.rs, dma.rs, scheme.rs, spawn.rs, transfer.rs, types.rs). Provides `UsbHostController` trait, `SetupPacket`, `PortStatus`, `TransferDirection`, `DmaBuffer`, descriptor parsers, `control_transfer`, `spawn_usb_driver`. Used by ehcid. **Currently not used by xhcid, uhcid, or ohcid.** |
|
||||
|
||||
This trait is the most important "infrastructure that already exists" item in this plan:
|
||||
it is the natural target for uhcid/ohcid runtime enumeration (§6 P0-B2) and for any
|
||||
future host port — including the xhcid → USB-core path that future xHCI cleanup will allow.
|
||||
|
||||
### 1.4 Tooling and observability
|
||||
|
||||
| Tool | Source | Reality |
|
||||
|---|---|---|
|
||||
| `lsusb` | `local/recipes/system/redbear-hwutils/source/src/bin/lsusb.rs` | Walks `/scheme/usb.*`, reads descriptors. |
|
||||
| `redbear-usb-check` | `local/recipes/system/redbear-hwutils/source/src/bin/redbear-usb-check.rs` | In-guest scheme tree validator. |
|
||||
| `redbear-usb-storage-check` | `local/recipes/system/redbear-hwutils/source/src/bin/redbear-usb-storage-check.rs` | Mass-storage round-trip validator. |
|
||||
| `usbctl` | `local/sources/base/drivers/usb/usbctl/` | CLI for port/endpoint status. |
|
||||
|
||||
### 1.5 Patch carriers on `local/patches/base/`
|
||||
|
||||
The **durable** Red Bear USB modifications are carried as `local/patches/base/P*.patch`
|
||||
files. These are applied atomically by the cookbook against the recipe source tree during
|
||||
fetch+cook.
|
||||
|
||||
| Patch | Size | Purpose |
|
||||
|---|---|---|
|
||||
| `P1-xhcid-device-lifecycle.patch` | 2351 lines | Attach publication, transactional configure, bounded detach. |
|
||||
| `P1-xhcid-port-pm-read-fix.patch` | 942 lines | Port PM state read. |
|
||||
| `P1-xhcid-uevent-logging.patch` | 20 lines | Uevent audit trail. |
|
||||
| `P2-usb-pm-and-drivers.patch` | 158 lines | USB PM (suspend/resume/quirk integration). |
|
||||
| `P3-xhci-device-hardening.patch` | 1193 lines | Endp direction, cfg_idx ordering, interrupt-EP, hub feature clearing. |
|
||||
| `P3-usbhidd-hardening.patch` | 725 lines | HID panic removal, named producer wiring. |
|
||||
| `P4-initfs-usb-drm-services.patch` | 22 lines | DRM/USB service ordering in init. |
|
||||
| (sibling) `P0-inputd-named-producers.patch`, `P0-inputd-per-device-consumers.patch`, `P2-inputd.patch`, `P3-inputd-keymap-bridge.patch` | (varying) | Input multiplexer wiring (ps2d + usbhidd consumers). |
|
||||
|
||||
**Durability rule:** any source-tree edit must be mirrored into one of these patches (or
|
||||
into the local `base` fork's `submodule/base` branch on `RedBear-OS`) before the session
|
||||
ends. This rule is also enforced by `local/AGENTS.md` and the cookbook's atomic patch
|
||||
applier. **The current local fork at `local/sources/base/` is a single mega-commit** —
|
||||
see §8 for the durability problem and remediation.
|
||||
|
||||
### 1.6 The interrupt-vs-polling contradiction
|
||||
|
||||
`local/sources/base/drivers/usb/xhcid/src/main.rs:101–115` defines a complete
|
||||
`get_int_method()` that returns MSI-X, MSI, INTx, or Polling based on PCI capabilities.
|
||||
`main.rs:141` then **disables it**:
|
||||
|
||||
```rust
|
||||
let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
|
||||
//TODO: Fix interrupts.
|
||||
```
|
||||
|
||||
`xhci::start_irq_reactor(&hci, irq_file);` is called with `irq_file = None`, which makes
|
||||
the reactor a **bounded polling loop that wakes every 1 second** (see the `mod.rs` reactor
|
||||
fallback). This is functionally "polling in production."
|
||||
|
||||
The v1 plan called this "interrupt-driven operation restored" — that is incorrect relative
|
||||
to the live code. v2 makes the gap explicit: **interrupts remain to be re-enabled** as P0-A1.
|
||||
|
||||
---
|
||||
|
||||
## 2. Status matrix (single source of truth)
|
||||
|
||||
Reorganized around the *honest* state of the tree.
|
||||
|
||||
| Component | State today | Maturity | Open correctness gap |
|
||||
|---|---|---|---|
|
||||
| Host mode (any controller) | builds / QEMU-validated narrow path | `usable (narrow path)` | see §4 |
|
||||
| **xhcid** runtime | builds / polling / QEMU-validated | `usable (narrow path)` | interrupts hardcoded off; missing CSZ; missing real-hardware reset fix; missing USB 3.x packet-size + hub fixes |
|
||||
| **ehcid** runtime | builds / no auto-spawn | `builds` | no class driver dispatch; no full bot pipeline through `/scheme/usb`; ~no peer review |
|
||||
| **uhcid** runtime | builds / does nothing | `builds` | stub (35 lines) |
|
||||
| **ohcid** runtime | builds / does nothing | `builds` | stub (35 lines) |
|
||||
| Hub | builds / good quality | `usable (narrow path)` | polling fallback retained |
|
||||
| HID class | builds / QEMU-validated narrow path | `usable (narrow path)` | named producer wiring complete; legacy VT fallback preserved |
|
||||
| Mass storage | builds / QEMU-validated narrow path | `usable (narrow path)` | no guest-side write proof; no multi-LUN; no UAS |
|
||||
| Audio class (USB) | builds | `builds` | not exercised in any proof |
|
||||
| CDC ACM/ECM | builds | `builds` | not exercised in any proof |
|
||||
| Bluetooth USB transport | builds (transport only) | `builds` | Bluetooth host path remains gappy (see BLUETOOTH-IMPLEMENTATION-PLAN) |
|
||||
| USB-C / UCSI | builds | `builds` | topology surfaced, no PD/alt-mode |
|
||||
| Native tooling (`lsusb`, `usbctl`, `redbear-info`, `redbear-usb-check`) | builds | `usable (narrow path)` | no bounded proof scheme validation |
|
||||
| Quirk table (compiled + TOML) | builds | `validated (QEMU)` — quirk-bypass-only | 146 USB + 214 storage entries, 22 flags |
|
||||
| Validation harnesses | 5 QEMU scripts | `validated (QEMU)` | no real-hardware matrix |
|
||||
|
||||
If a row says `builds`, **Red Bear does not promise that the component is reachable from a
|
||||
typed-key-in-the-inputd-pipe to a shell prompt.** That promise is restricted to
|
||||
`usable (narrow path)` and above, and only for the documented scenario.
|
||||
|
||||
---
|
||||
|
||||
## 3. Upstream divergence — what Redox 0.x USB HEAD has that Red Bear does not
|
||||
|
||||
This section is required reading before any USB change. It is the input to every phase in
|
||||
§6. The Redox merge window for USB change runs roughly Jan 2025 – Jun 2026 with two
|
||||
concentrated bursts (March 2025, Sep–Oct 2025). Red Bear's fork is currently pinned at the
|
||||
v1 baseline (0.1.0 base snapshot).
|
||||
|
||||
### 3.1 Three high-priority upstream commits Red Bear has not adopted
|
||||
|
||||
| Upstream commit | Why we need it | Where it would land |
|
||||
|---|---|---|
|
||||
| **`69a80a6a` — xhci: fix reset procedure on real hardware** | Replaces magic bit numbers with named constants; fixes the HCRST wait loop to read from `usb_cmd` instead of `usb_sts` (the spec says HCRST is in USB_CMD). Without this, `xhcid` can spin or wedge on real controllers. | New patch `local/patches/base/P3-xhci-real-hw-reset.patch` against `xhcid/src/xhci/mod.rs`. |
|
||||
| **`19570db4` — xhci: support 64-bit contexts (CSZ)** | Makes `Xhci` generic over context size (`Xhci<CONTEXT_32>` / `Xhci<CONTEXT_64>`) with runtime detection via `HCCPARAMS1.CSZ`. Required by modern xHCI controllers (Alder Lake, Raptor Lake, Ryzen 7000+). The local source already has `daemon_with_context_size<const N: usize>` and a `//TODO: cleanup CSZ support` comment at the call site — the upstream fix is the natural completion. | New patch `local/patches/base/P3-xhci-csz-64-bit.patch` against `xhcid/src/main.rs` and the downstream context types. |
|
||||
| **`12e601b3` — xhci: improvements based on real hardware testing** | Adds `USB_CMD_INTE`, corrects port RWC handling, fixes address_device speed passthrough. Companion to `69a80a6a`. | New patch `local/patches/base/P3-xhci-real-hw-impl.patch`. |
|
||||
|
||||
### 3.2 Medium-priority upstream commits
|
||||
|
||||
| Upstream commit | Note |
|
||||
|---|---|
|
||||
| `8dcd85b5`, `ba0ca4ce` — Fix packet size for USB 3.0 and USB 1 | Required for SuperSpeed device enumeration. Adopt in same patch as CSZ. |
|
||||
| `cbbcbc9e`, `f58625b0` — `usbhubd`/`xhcid` fix reading descriptor / port status on USB 3 hubs | Round out the USB 3 hub story. |
|
||||
| `8f278dcb`, `34b37410` — Bounds check on `root_hub_port_index()` | Stop a panic that we already pay down via patch but have not tested in tree. |
|
||||
| `4d6581d4` — xhcid: add more timeouts | Prevents infinite hangs on unresponsive controllers. |
|
||||
| `7e3e841f` — xhci: fix reading EHB flag in received_irq | Companion for interrupt-driven paths. |
|
||||
| `e3a13a0c` — `xhcid` and friends: use newtype `PortId` to ensure route string | Type-safety win. |
|
||||
| `6ac41ee` — daemon: tolerate BrokenPipe on ready() | Already in our base fork. |
|
||||
| `258ea4e6`, `865ca866` — `usbscsid`: use the unified disk scheme implementation | `usbscsid` revision; lower priority, code organization. |
|
||||
| `e4aab167`, `24c1f0a3` — xhcid: don't exit the event loop when using irqs | Required for stable interrupt-driven operation (pairs with the §1.6 fix). |
|
||||
|
||||
### 3.3 Lower-priority upstream commits to record, not blindly adopt
|
||||
|
||||
| Commit | Note |
|
||||
|---|---|
|
||||
| `a5f87735` — ignore alternate settings | Conflicts with our composite-device fix (P3-xhci-device-hardening retains explicit alternate handling). Validate whether dropping this is sound given our active `PortState.active_ifaces` map. |
|
||||
| `7c980137` — language ID for string descriptors | Likely a clean drop-in. |
|
||||
| `374e5fbf` — xhci: use redox-scheme v2 | We are on `redox-scheme 0.11`; a v2 migration is not in scope for 0.2.x. |
|
||||
| `30fb1e7a` — drivers merged into base (Nov 2025) | Mirrors what Red Bear already does (our `local/sources/base/`). No action. |
|
||||
| USB SCSI driver disabled upstream (Dec 2025) | Red Bear keeps it on with the BOUNDED storage test. Re-evaluate after P2-B1. |
|
||||
| `bjorn3` enabled xHCI by default in QEMU x86-64 (Mar 2026) | Aligns with our `redbear-mini` boot script. No action. |
|
||||
| `bjorn3` moved xHCI config to runtime (Apr 2026) | Lower priority — compile-time config is fine for our release model. |
|
||||
| Antoine Reversat — simplified xhci (May 2026) | Subject to per-line review. |
|
||||
|
||||
### 3.4 Things upstream still does NOT have
|
||||
|
||||
These are explicit non-features from upstream that Red Bear should not silently inherit as
|
||||
a todo:
|
||||
|
||||
- **USB Type-C / USB-PD / alt-modes.** No policy engine, no protocol stack.
|
||||
- **USB4 / Thunderbolt.** Listed as "not supported" in upstream `COMMUNITY-HW.md`.
|
||||
- **xHCI debug capability (DbC).** Not implemented.
|
||||
- **USB device mode (gadget) / OTG.** No dual-role support.
|
||||
- **USB isochronous transfers.** `xhcid` returns `ENOSYS` for isoch endpoints.
|
||||
|
||||
These belong to §6 P5 (architectural decision gate), not to "fix the missing patch."
|
||||
|
||||
---
|
||||
|
||||
## 4. Bare-metal-input correctness (the actual boot-time failure modes)
|
||||
|
||||
The bare-metal USB keyboard problem is not "xhcid doesn't work." xhcid does work in QEMU
|
||||
and on some real hardware. The failure modes are the **paths that do not reach xhcid**:
|
||||
|
||||
1. **EHCI-attached USB keyboard** — xHCI now owns every USB-3 controller, but
|
||||
EHCI/companion controllers (UHCI/OHCI) still own low/full-speed devices on chipsets
|
||||
that firmware routes through them. **ehcid does not auto-spawn class drivers**, so even
|
||||
though ehcid publishes `/scheme/usb/port<n>/...`, no `usbhidd` is started for any
|
||||
device on that scheme. The keyboard is reachable by userland but the input pipeline
|
||||
never builds.
|
||||
|
||||
2. **UHCI/OHCI-attached devices** — uhcid and ohcid are 35-line stubs. The companion
|
||||
controller is owned (by `pcid`) but no USB traffic flows. There is no port state, no
|
||||
transfer completion, no scheme.
|
||||
|
||||
3. **xHCI interrupt-driven operation is offline** — line 141 hardcodes polling. On real
|
||||
hardware with no reliable polling timer, this can produce slow enumeration or input
|
||||
lag, and on some chips it can wedge the controller (see upstream `69a80a6a`).
|
||||
|
||||
4. **No real-hardware validation matrix** — there is no `hardware-validation.md` table
|
||||
enumerating which physical controller families have been exercised on bare metal.
|
||||
QEMU `qemu-xhci` is one fixed emulation target; it is not representative.
|
||||
|
||||
5. **USB HID and ACPI I2C-HID are not the same** — internal laptop keyboards are
|
||||
I2C-HID (`i2c-hidd`, `intel-thc-hidd`), not USB. These are real but separate. The
|
||||
I2C-HID plan and the USB HID plan cannot assume one is a substitute for the other.
|
||||
|
||||
6. **Strict-boot mode exists but is not bound** — `uhcid`/`ohcid`/`ehcid` accept
|
||||
`--strict-boot`, but no initfs entry enables it; the policy lives in operator
|
||||
knowledge, not in the artifact.
|
||||
|
||||
7. **LED state is a weak health signal** — `usbhidd` keyboard LEDs are bounded,
|
||||
per-device, best-effort; they are not a system-global lock-state authority. A dead
|
||||
`Caps Lock` indicator does not prove keyboard transport is broken; a working indicator
|
||||
does not prove the external USB keyboard fallback works. Treat LED state as cosmetic
|
||||
debug, not as a proof of input health.
|
||||
|
||||
8. **External keyboard bare-metal proof remains unpinned** — the bounded QEMU lifecycle
|
||||
proof is not the same as a bare-metal proof. We need a captured log per controller
|
||||
family before claiming a fallback works on hardware.
|
||||
|
||||
These eight items are the inputs to phases P0-A (xHCI runtime) and P0-B (legacy host
|
||||
controllers).
|
||||
|
||||
---
|
||||
|
||||
## 5. Phases — execution order
|
||||
|
||||
Phases are ordered by *what unblocks bare-metal correctness and what has unambiguous
|
||||
upstream-comparable patches we can adopt without inventing semantics*.
|
||||
|
||||
| Phase | Goal | Exit |
|
||||
|---|---|---|
|
||||
| **P0-A1** | ✅ Re-enable xHCI MSI/MSI-X/INTx at runtime. Committed 2026-07-07 (`local/sources/base` commit `cbd40e0d`, parent `a2998c2d`). `test-xhci-irq-qemu.sh` now greps for actual reactor log lines. | ✅ QEMU proof script updated; real-hardware bring-up deferred to operator build. |
|
||||
| **P0-A2** | Adopt upstream xHCI reset-procedure fix + hardware hardening (`69a80a6a`, `12e601b3`). | One QEMU full-stack pass + one real-hardware bring-up |
|
||||
| **P0-A3** | Adopt CSZ (64-bit contexts) upstream commit; complete the `//TODO: cleanup CSZ support` site. | Same as A1 |
|
||||
| **P0-A4** | ✅ Adopt panic bounds-check (`8f278dcb`) and timeout expansion (`4d6581d4`). Committed 2026-07-07: 5 `root_hub_port_index()` unwrap/index sites replaced with bounded access (`ok_or_else(|| Error::new(EINVAL))?`, `match None → continue`, `expect()` with diagnostic). | QEMU lifecycle + full-stack pass |
|
||||
| **P0-B1** | Auto-spawn class drivers from the EHCI scheme (`/scheme/usb/port<n>/descriptors`). Reuse the existing `xhcid` class-driver spawn model by refactoring the spawn helper out of `xhcid` into `usb-core::spawn_usb_driver` if necessary, then driving it from EHCI too. | QEMU run with USB keyboard on EHCI route → typed input reaches `inputd` |
|
||||
| **P0-B2** | Implement real runtime enumeration for `uhcid` and `ohcid` over the existing `usb-core::UsbHostController` trait. Each new driver must register the same `/scheme/usb` tree pattern ehcid uses and must auto-spawn class drivers via `P0-B1`. | QEMU run with low/full-speed USB keyboard on legacy controller route → typed input reaches `inputd` |
|
||||
| **P1** | ✅ Code-verified 2026-07-07. USB 3.0 packet-size handling (`update_max_packet_size` with shift exponent for USB ≥3, bytes for USB ≤2) in baseline. Hub descriptor reading uses separate `HubDescriptorV2`/`HubDescriptorV3`. Slot context hub bit (bit 26) and hub port count (bits 24-31) correctly set in `configure_endpoints_once`. `SET_HUB_DEPTH` issued for USB 3 hubs. TTT propagation not applicable to USB 3 (TT is USB 2.0 high-speed split-transaction only). TODOs about `interface_desc`/`alternate_setting` on USB 3 hubs are safe — passing None matches upstream behavior. | QEMU run deferred to operator. |
|
||||
| **P2-A** | ✅ Storage data path: in-guest write verification on the `disk.usb-*` scheme. `redbear-usb-storage-check` already performs write+readback+restore at sector 2048. `test-usb-storage-qemu.sh` now validates all four checks (discovery, write, readback, restore). | `redbear-usb-storage-check` proves a write/read round-trip in QEMU |
|
||||
| **P2-B** | Userspace API: pick native or `libusb`. Native: bake `usb-core` consumers first. `libusb`: pick an active WIP commit; if there is none, **defer** §2 row "userspace API" rather than start a new side-quest. | Decision + prototype |
|
||||
| **P3** | HID robustness: real-hardware HID validation matrix; `i2c-hidd` migration to named producers; `evdevd` hotplug add/remove behavior from USB. | One HID device family proven bare-metal + one hot-unplug cycle QEMU |
|
||||
| **P4** | Validation slices: complete `test-usb-storage-qemu.sh` write proof, hardware matrix in `HARDWARE-VALIDATION-MATRIX.md` (board, controller, input/storage/audio result), bounded stress loop on top of `test-xhci-device-lifecycle-qemu.sh`. | matrix has one row per controller family |
|
||||
| **P5** | Architectural decision gate: host-only vs device mode; USB-C/PD/alt-mode scope; USB4/Thunderbolt exclusion; whether UCSI grows into a real PD surface. Recorded as an ADR in `local/docs/`. | Decision recorded |
|
||||
|
||||
Phases are not equal in size. P0-A1 and P0-B2 are bounded, well-understood work. P2-B
|
||||
(libusb vs native) is a fork in the road; it is correct that it has *no* time estimate.
|
||||
P5 is a decision moment, not an implementation.
|
||||
|
||||
---
|
||||
|
||||
## 6. Validation inventory and bounded proofs
|
||||
|
||||
Five scripts exist today. They are honest about their scope (QEMU) but should be paired
|
||||
with a real-hardware matrix per phase exit.
|
||||
|
||||
| Script | What it actually proves | Limits |
|
||||
|---|---|---|
|
||||
| `local/scripts/test-usb-qemu.sh --check` | Full stack: xHCI init, HID spawn, SCSI spawn, sector-0 readback, BOS, no crashes. | QEMU `qemu-xhci` only; one emulator config; no real hardware. |
|
||||
| `local/scripts/test-xhci-device-lifecycle-qemu.sh --check` | Bounded hotplug attach/detach for HID + storage. | QEMU only; monitor-driven hotplug; not a stress test. |
|
||||
| `local/scripts/test-usb-storage-qemu.sh` | Mass storage autospawn + sector-0 readback. | No write proof; no multi-LUN; no UAS. |
|
||||
| `local/scripts/test-xhci-irq-qemu.sh --check` | ✅ Updated 2026-07-07 to verify interrupt-driven reactor path. Greps for `Running IRQ reactor with IRQ file and event queue` (must be present) and `Running IRQ reactor in polling mode` (must NOT be), plus MSI-X/INTx delivery method. | QEMU `qemu-xhci` only; not real-hardware. |
|
||||
| `local/scripts/test-usb-maturity-qemu.sh` | Sequential wrapper. | Composes the others; inherits their limits. |
|
||||
|
||||
**Required proofs after P0-A1 lands:**
|
||||
|
||||
1. `test-xhci-irq-qemu.sh --check` must transition from "binary runs" to "interrupts fire
|
||||
and complete." Add a bounded probe that confirms a hotplug event triggers an IRQ in
|
||||
guest time, not a sleep timer.
|
||||
2. Add `test-xhci-regression-qemu.sh` for the upstream reset-procedure fix.
|
||||
3. Add `test-uhci-runtime-qemu.sh` and `test-ohci-runtime-qemu.sh` after P0-B2 — same
|
||||
shape as the xHCI lifecycle test.
|
||||
4. Add `test-ehci-class-autospawn-qemu.sh` after P0-B1.
|
||||
|
||||
Proofs must:
|
||||
|
||||
- run on `redbear-mini` from a clean `make clean` build;
|
||||
- keep the boot log under `local/docs/boot-logs/` with a `REDBEAR-...-RESULTS.md`;
|
||||
- be citeable from phase status (§3 matrix) and from `USB-VALIDATION-RUNBOOK.md`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Durability posture (the local-fork problem, honestly)
|
||||
|
||||
The base fork at `local/sources/base/` currently carries **two USB-related commits**
|
||||
(one pre-existing, one from P0-A1):
|
||||
|
||||
```
|
||||
$ git -C local/sources/base log -- drivers/usb/
|
||||
cbd40e0d xhcid: re-enable interrupt-driven operation via get_int_method ← P0-A1 (2026-07-07)
|
||||
6ac41ee daemon: tolerate BrokenPipe on ready(); i2cd: handle empty RON response
|
||||
dd08b76 Red Bear OS base baseline from 0.1.0 pre-patched archive
|
||||
```
|
||||
|
||||
Everything else that v1 described as "88 error handling fixes across xhcid" lives in
|
||||
**`local/patches/base/P*.patch`** files. That is acceptable as long as:
|
||||
|
||||
1. The base *recipe* (`recipes/core/base/recipe.toml`) actually applies those patches on
|
||||
`repo cook`. Verify by running `repo validate-patches base` after every edit and by
|
||||
checking that `recipes/core/base/source/drivers/usb/xhcid/...` contains the Red Bear
|
||||
state, not the upstream state.
|
||||
2. No "live-edit" of `recipes/core/base/source/...` ever escapes into the next build
|
||||
without an immediate patch mirror. `local/AGENTS.md` enforces this; the rule stands.
|
||||
3. The next base-fork bump (rebase onto a newer Redox base tag) preserves every USB patch
|
||||
in the same order and lands them as commits on the `submodule/base` branch — not as a
|
||||
new mega-patch.
|
||||
|
||||
**Durability remediation work that does not block USB phases:**
|
||||
|
||||
- ✅ P0-A1 landed as the **first USB-focused commit on `submodule/base`** since `dd08b76`
|
||||
(commit `cbd40e0d`, 2026-07-07). This reopens per-feature commit history and makes
|
||||
future rebases reviewable.
|
||||
- P0-A2 through P0-B2 should each land as individual, reviewable commits on the same
|
||||
branch — never bundled into a mega-commit. Each phase below has a concrete file list
|
||||
and diff target (see §11).
|
||||
- The base fork's `Cargo.toml` should track the `submodule/base` branch as upstream
|
||||
(currently it does, per the source-of-truth rules in `local/AGENTS.md`).
|
||||
|
||||
---
|
||||
|
||||
## 8. Support language — how Red Bear describes USB
|
||||
|
||||
Until P0-A and P0-B exit, Red Bear should NOT use any of:
|
||||
|
||||
- "USB support works."
|
||||
- "USB is functional."
|
||||
- "USB keyboard works on bare metal."
|
||||
- "USB storage is supported."
|
||||
|
||||
It SHOULD use language such as:
|
||||
|
||||
- "xHCI host support is present but experimental; bare-metal proof requires the real-hardware
|
||||
matrix in §6 P4."
|
||||
- "EHCI ownership and USB 2 register init exist; class-driver auto-spawn is pending P0-B1."
|
||||
- "UHCI and OHCI are userspace stubs in this build; legacy host controllers are not yet
|
||||
the boot-input fallback."
|
||||
- "USB storage autospawn and bounded sector-0 readback are QEMU-validated; write proof is
|
||||
pending P2-A."
|
||||
- "USB error handling and correctness carry significant Red Bear patches over upstream; see
|
||||
`local/patches/base/P[1-3]-xhci*.patch` and `local/patches/base/P3-xhci-device-hardening.patch`."
|
||||
- "USB-C topology (UCSI) is exposed but does not negotiate PD or alternate modes."
|
||||
|
||||
The README status table and the desktop-path plans should adopt this language consistently
|
||||
the next time they are touched. The `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` already
|
||||
treats USB as a first-class subsystem; this plan agrees and refines the wording.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions and follow-up
|
||||
|
||||
1. **Rebase cadence** — when `submodule/base` upstream lands the
|
||||
`simplify xHCI` commit (May 2026), do we adopt it before or after P0-A1 lands? Per the
|
||||
upstream-first rule, after — but the diff requires per-line review because our local
|
||||
patches (`P1-xhcid-*.patch`, `P3-xhci-device-hardening.patch`) overlap on the same code
|
||||
regions.
|
||||
2. **Cross-process class driver spawn** — the class spawn path is currently xhcid-driven
|
||||
(via the scheme). Should the spawn helper live in `usb-core` and be reused by
|
||||
`ehcid`/`uhcid`/`ohcid`? Yes (P0-B1, P0-B2) — and that requires `usb-core` to grow
|
||||
`spawn_class_driver`, which it does not yet have. The migration is the natural unit
|
||||
of P0-B1.
|
||||
3. **Strict-boot mode** — should `pcid-spawner` always pass `--strict-boot` to USB host
|
||||
daemons? Operators can set `REDBEAR_STRICT_USB_BOOT=1` today; the default is off.
|
||||
Recommend leaving the default off but documenting the env var in
|
||||
`USB-VALIDATION-RUNBOOK.md` (P0-A4 documentation step).
|
||||
4. **Whether to keep `usbscsid` enabled after upstream disabled it** — adopt the upside
|
||||
(bounded in-guest write proof) and the downside (occasional stalls). Defer to P2-A
|
||||
evaluation.
|
||||
5. **Hardware validation entries** — the matrix in `local/docs/HARDWARE-VALIDATION-MATRIX.md`
|
||||
is currently tiny. P4 explicitly grows it; if it does not, the matrix block of P4 exit
|
||||
blocks the phase.
|
||||
|
||||
---
|
||||
|
||||
## 10. See also
|
||||
|
||||
- `local/docs/USB-VALIDATION-RUNBOOK.md` — operator runbook for the bounded proofs above.
|
||||
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` — the canonical desktop-path plan; treats
|
||||
USB as a first-class runtime subsystem.
|
||||
- `local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` — MSI/MSI-X quality
|
||||
surface that P0-A1 actually exercises.
|
||||
- `local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md` — `redbear-btusb` consumes the USB class
|
||||
driver dispatch path that P0-B1 makes available to all host controllers.
|
||||
- `local/docs/WIFI-IMPLEMENTATION-PLAN.md` — Wi-Fi native control plane; not USB-coupled.
|
||||
- `local/docs/QUIRKS-SYSTEM.md` — TOML + DMI + compiled-in quirk tables, source of USB
|
||||
device workarounds.
|
||||
- `local/AGENTS.md` — fork model, durability policy, single-repo rule, branch policy.
|
||||
- `local/docs/archived/USB-IMPLEMENTATION-PLAN-v1-2026-04.md` — superseded v1.
|
||||
- `local/docs/archived/USB-BOOT-INPUT-PLAN.md` — preserved for the boot-input
|
||||
historical context; not the planning authority.
|
||||
- `local/docs/archived/XHCID-DEVICE-IMPROVEMENT-PLAN.md` — preserved for the xhcid
|
||||
device-level historical context; absorbed into phases P0-A and P1.
|
||||
|
||||
---
|
||||
|
||||
## 11. Implementation handoff — P0-A2 through P0-B2
|
||||
|
||||
This section is the concrete kickoff for each remaining P0 sub-phase.
|
||||
Each entry names files to touch, upstream commits to diff, and the required
|
||||
validation step. A phase **does not leave implementation** until committed on
|
||||
`submodule/base` (or the equivalent local fork) and, where practical, verified
|
||||
with an automated QEMU proof.
|
||||
|
||||
### P0-A2 — upstream xHCI reset-procedure fix
|
||||
|
||||
| Field | Detail |
|
||||
|---|---|
|
||||
| **Upstream commits** | `https://gitlab.redox-os.org/redox-os/base/commit/69a80a6a` — "xhci: fix reset procedure on real hardware". Also `https://gitlab.redox-os.org/redox-os/base/commit/12e601b3` — "xhci: improvements based on real hardware testing". |
|
||||
| **Files to touch** | `local/sources/base/drivers/usb/xhcid/src/xhci/mod.rs` — `Xhci::new`, controller reset path. `local/sources/base/drivers/usb/xhcid/src/xhci/operational.rs` — operational register definitions. |
|
||||
| **What changes** | Replace magic bit numbers with named constants (`USB_CMD_RS`, `USB_CMD_HCRST`, `USB_STS_HCH`, `USB_STS_CNR`). Fix the HCRST wait loop to read from `usb_cmd` instead of `usb_sts`. Apply the port-RWC-correction and address_device speed passthrough from 12e601b3. |
|
||||
| **Git landing** | One commit on `local/sources/base` master → update parent gitlink. |
|
||||
| **Validation** | Rebuild `redbear-mini`, run `test-usb-qemu.sh --check`. Boot log must show xHCI controller init without "hang" or "reset failed" lines. If real hardware is available, boot on one Intel and one AMD controller. |
|
||||
| **Blocking** | Nothing — independent of P0-A1. |
|
||||
|
||||
### P0-A3 — CSZ (64-bit contexts)
|
||||
|
||||
| Field | Detail |
|
||||
|---|---|
|
||||
| **Upstream commits** | `https://gitlab.redox-os.org/redox-os/base/commit/19570db4` — "xhci: support 64-bit contexts (CSZ)". |
|
||||
| **Files to touch** | `local/sources/base/drivers/usb/xhcid/src/main.rs` — `daemon_with_context_size<const N: usize>` and the `//TODO: cleanup CSZ support` comment at line 119. `local/sources/base/drivers/usb/xhcid/src/xhci/context.rs` — `DeviceContextList`, `InputContext`. `local/sources/base/drivers/usb/xhcid/src/xhci/mod.rs` — `Xhci<const N: usize>` struct, `PortState`. |
|
||||
| **What changes** | Make `Xhci`, `DeviceContextList`, `InputContext`, `PortState`, and `StreamContextArray` generic over context size `N` (32 or 64). Detect CSZ at runtime via `CapabilityRegs::csz()`. The local source already parameterizes `daemon_with_context_size` — the upstream fix is the natural completion. Remove the `//TODO: cleanup CSZ support` once generic parameterization is clean. |
|
||||
| **Git landing** | One commit. |
|
||||
| **Validation** | Rebuild + QEMU full-stack check. CSZ is not visible without a modern controller, so the QEMU proof is "didn't break existing paths." Real-hardware proof: boot on Ryzen 7000+ or Intel Alder Lake+. |
|
||||
| **Blocking** | Nothing, but lands best after P0-A2 to avoid merge conflicts. |
|
||||
|
||||
### P0-A4 — bounds check + timeouts
|
||||
|
||||
| Field | Detail |
|
||||
|---|---|
|
||||
| **Upstream commits** | `https://gitlab.redox-os.org/redox-os/base/commit/8f278dcb` — bounds check on `root_hub_port_index()`. `4d6581d4` — "xhcid: add more timeouts". |
|
||||
| **Files to touch** | `local/sources/base/drivers/usb/xhcid/src/xhci/mod.rs` — port index bounds. `local/sources/base/drivers/usb/xhcid/src/xhci/scheme.rs` — timeout additions. |
|
||||
| **What changes** | Bounds-check the port index parameter to prevent out-of-range access. Add timeout guards on control transfer and address device paths to prevent infinite hangs. |
|
||||
| **Git landing** | One commit. |
|
||||
| **Validation** | QEMU lifecycle test must still pass. |
|
||||
| **Blocking** | None. |
|
||||
|
||||
### P0-B1 — EHCI class-driver auto-spawn
|
||||
|
||||
| Field | Detail |
|
||||
|---|---|
|
||||
| **Why** | `ehcid` publishes `/scheme/usb/port<n>/descriptors` but does **not** auto-spawn `usbhidd` or `usbscsid` when a matching device appears. Only `xhcid` does that through its scheme. Without auto-spawn, EHCI-attached USB keyboards never reach the input pipeline. |
|
||||
| **Files to touch** | **New logic:** `local/recipes/drivers/usb-core/source/src/spawn.rs` — add a `spawn_class_driver` helper that takes a port descriptor, walks the USB class table, and spawns the matching class daemon (reuses the spawn model from xhcid). **Call site:** `local/recipes/drivers/ehcid/source/src/main.rs` — after enumerating a port and reading descriptors, call `usb_core::spawn_class_driver`. |
|
||||
| **Git landing** | Two commits: (1) usb-core spawn helper, (2) ehcid call site. Both go on `submodule/base` since they touch existing tracked code. |
|
||||
| **Validation** | New script: `test-ehci-class-autospawn-qemu.sh` — boot with USB keyboard on EHCI route, verify `usbhidd` spawns and keyboard input reaches `inputd`. |
|
||||
| **Dependency** | P0-B1 is NOT blocked by anything. The usb-core trait already has `UsbHostController::control_transfer` and descriptor parsers. The class-spawn decision table (`/lib/drivers.d/70-usb-class.toml`) is already wired. |
|
||||
|
||||
### P0-B2 — real UHCI/OHCI runtime enumeration
|
||||
|
||||
| Field | Detail |
|
||||
|---|---|
|
||||
| **Why** | `uhcid/src/main.rs` and `ohcid/src/main.rs` are 35-line stubs: read PCI BAR4, log, sleep forever. No scheme, no transfers, no enumeration. This is the bare-metal USB keyboard blocker for legacy controller paths. |
|
||||
| **Files to touch** | **uhcid:** `local/recipes/drivers/uhcid/source/src/main.rs` (replace 35-line stub with a ~1500-line implementation). **ohcid:** `local/recipes/drivers/ohcid/source/src/main.rs` (same). Both must implement `usb_core::UsbHostController` in a new sibling file `host.rs`, register `/scheme/usb`, perform frame-list/QH/TD/port enumeration, and call `spawn_class_driver` (from P0-B1) when a keyboard/storage device appears. Use the existing `ehcid` as a reference model. |
|
||||
| **What changes** | For each controller: (a) PCI BAR mapping + register definitions, (b) `UsbHostController` trait implementor, (c) scheme registration (`/scheme/usb`), (d) port enumeration loop, (e) class-driver auto-spawn. |
|
||||
| **Git landing** | Two commits (one per controller). These live in `local/recipes/drivers/`, not `local/sources/base/`, so they are committed on the parent `0.3.0` branch directly (tracked-tree model). |
|
||||
| **Validation** | Two new scripts: `test-uhci-runtime-qemu.sh --check` and `test-ohci-runtime-qemu.sh --check`. Same shape as the xHCI lifecycle test: boot, verify scheme registration, hotplug keyboard, verify `usbhidd` spawn, verify keystrokes reach `inputd`. |
|
||||
| **Dependency** | P0-B2 **depends on P0-B1** (uses the class-spawn helper) but does NOT depend on any of P0-A1 through P0-A4. UHCI and OHCI are independent from xHCI for enumeration. |
|
||||
| **Reference impl** | `local/recipes/drivers/ehcid/source/src/main.rs` (1550 lines) — uses `usb-core`, registers `/scheme/usb`, MMIO frame list, QH/TD control/bulk/interrupt. UHCI and OHCI are simpler controllers and should be smaller. |
|
||||
|
||||
### Build-and-verify workflow (per-session)
|
||||
|
||||
```
|
||||
# After committing any P0 sub-phase change:
|
||||
./local/scripts/build-redbear.sh --upstream redbear-mini
|
||||
./local/scripts/test-xhci-irq-qemu.sh --check # if xHCI touched
|
||||
./local/scripts/test-usb-qemu.sh --check # full-stack regression
|
||||
./local/scripts/test-xhci-device-lifecycle-qemu.sh --check # lifecycle
|
||||
|
||||
# After P0-B1/P0-B2:
|
||||
./local/scripts/test-ehci-class-autospawn-qemu.sh --check # (to be written)
|
||||
./local/scripts/test-uhci-runtime-qemu.sh --check # (to be written)
|
||||
./local/scripts/test-ohci-runtime-qemu.sh --check # (to be written)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. P5 — Modern USB Scope Decision (ADR)
|
||||
|
||||
*Date:* 2026-07-07.
|
||||
*Status:* Decided. Red Bear OS adopts **host-only USB** for the foreseeable
|
||||
future.
|
||||
|
||||
### Decision
|
||||
|
||||
Red Bear OS ships as a **USB host** platform. Device mode (gadget), OTG
|
||||
dual-role, USB-C Power Delivery negotiation, USB-C alternate modes, USB4, and
|
||||
Thunderbolt are **explicitly excluded** from the current scope. This decision
|
||||
is recorded as an ADR (Architecture Decision Record) so that future work does
|
||||
not carry implicit scope expansion into the active build without a deliberate
|
||||
re-evaluation.
|
||||
|
||||
### What is in scope (host-first)
|
||||
|
||||
- xHCI, EHCI, UHCI, and OHCI **host controllers** (drivers built, P0 complete).
|
||||
- USB class daemons: HID (keyboard/mouse), Mass Storage (BOT), Hub, Audio.
|
||||
- USB device enumeration, descriptor parsing, and class-driver auto-spawn.
|
||||
- Hardware quirks: compiled-in + TOML runtime tables (146 USB + 214 storage
|
||||
entries), consumed at runtime by xhcid and usbscsid.
|
||||
- USB 3.x SuperSpeed (5 Gbps) and SuperSpeedPlus (10 Gbps) host operation
|
||||
through xhcid.
|
||||
- USB-C UCSI topology detection (`ucsid`, exposes `/scheme/ucsi`).
|
||||
|
||||
### What is explicitly excluded
|
||||
|
||||
| Capability | Excluded because |
|
||||
|---|---|
|
||||
| USB device mode (gadget) | Red Bear OS is a desktop/server OS, not an embedded peripheral. No dual-role controller (DRD) support exists in any upstream Redox component. |
|
||||
| OTG (On-The-Go) | OTG requires dual-role + HNP/SRP protocol negotiation. No Redox kernel or driver infrastructure exists, and OTG is a declining standard (USB-C replaces it). |
|
||||
| USB-C Power Delivery | PD negotiation requires a CC-line protocol engine, a policy manager, and source/sink state machines. This is a full subsystem (~10k LoC in Linux), not a small driver add-on. PMIC/charger integration is also needed. |
|
||||
| USB-C alternate modes (DisplayPort, Thunderbolt) | Requires PD negotiation first, plus mux control, plus DP/Thunderbolt protocol stacks. No Redox GPU driver consumes DP alt-mode (display drivers use PCIe or platform-internal paths). |
|
||||
| USB4 | USB4 requires PCIe tunneling, DisplayPort tunneling, and a USB4 router topology. The Redox PCI subsystem does not support PCIe hotplug or tunneling. Linux's USB4 stack is ~15k LoC. |
|
||||
| Thunderbolt 3/4 | Thunderbolt requires USB4 or PCIe hotplug infrastructure. Listed as "not supported" in upstream Redox `COMMUNITY-HW.md`. No driver, no IOMMU DMA remapping for Thunderbolt security levels. |
|
||||
| xHCI Debug Capability (DbC) | DbC requires a separate xHCI debug capability register set and a dedicated debug target endpoint. Serial console via UART is the standard debug path on Red Bear OS. DbC adds complexity without a use case. |
|
||||
|
||||
### What may be reconsidered later
|
||||
|
||||
- **USB-C PD (power role only, sink).** If Red Bear OS runs on a laptop that
|
||||
charges via USB-C, the system firmware (UEFI/BIOS) handles PD negotiation
|
||||
before the OS boots. An OS-level PD policy manager is only needed for
|
||||
runtime source/sink role swaps, which are uncommon in a desktop/server OS.
|
||||
Revisit if bare-metal laptop support requires it.
|
||||
- **USB device mode for firmware update.** Some devices require USB DFU
|
||||
(Device Firmware Upgrade) mode. This is a narrow, well-bounded gadget class
|
||||
that could be implemented without a full dual-role stack. Not in current plan.
|
||||
- **UCSI PD surface.** The existing `ucsid` daemon exposes connector topology.
|
||||
Extending it to pass PD power contract data to a userspace policy manager is
|
||||
a reasonable follow-up if hardware validation demands it.
|
||||
|
||||
### Rationale
|
||||
|
||||
Red Bear OS is a desktop/server operating system. The USB host path (keyboard,
|
||||
mouse, storage, hub, audio) covers the essential desktop use case. Expanding
|
||||
into device mode, PD, alt-modes, USB4, or Thunderbolt would add thousands of
|
||||
lines of new kernel and driver code with no immediate user-visible benefit —
|
||||
every excluded subsystem would consume weeks or months of development and
|
||||
require hardware the team does not currently validate against.
|
||||
|
||||
This decision keeps the USB scope **honest** and **buildable** with the current
|
||||
team. It removes implicit "we should support X someday" scope pressure from
|
||||
the active build, letting the team focus on completing the host-side USB
|
||||
maturity work (P1–P4) and the Wi-Fi/Bluetooth/desktop integration paths that
|
||||
depend on it.
|
||||
|
||||
### Review cadence
|
||||
|
||||
This ADR is reviewed **when a new Red Bear OS release branch is cut** (e.g.,
|
||||
`0.3.0` → `0.4.0`). At each review, the team evaluates whether any excluded
|
||||
capability has become necessary for the next release's target hardware
|
||||
profile. The ADR is not a permanent rejection — it is a current-scope
|
||||
boundary that prevents unplanned scope creep.
|
||||
@@ -53,5 +53,5 @@ integrity, submodule hygiene, firmware presence warning) that bare `make all` /
|
||||
## QEMU boot
|
||||
|
||||
```bash
|
||||
make qemu CONFIG_NAME=redbear-mini # Boot the latest built image in QEMU
|
||||
make qemu # Boot the latest built image in QEMU
|
||||
```
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# fork-upstream-map.toml — Authoritative mapping of local Cat 2 fork
|
||||
# directories to their upstream Redox repositories and the upstream
|
||||
# release tag each fork is based on. Updated by
|
||||
# local/scripts/refresh-fork-upstream-map.sh. Consumed by
|
||||
# local/scripts/verify-fork-versions.sh to enforce the "no fake version
|
||||
# label" rule (local/AGENTS.md § "No-fake-version-label rule").
|
||||
#
|
||||
# Format: one line per fork:
|
||||
# <fork-name> <upstream-git-url> <upstream-release-tag>
|
||||
#
|
||||
# A fork with `<X.Y.Z>-rbN>` in its Cargo.toml MUST have its
|
||||
# `<X.Y.Z>` part match the upstream tag listed here, and the source
|
||||
# content MUST be a real rebase onto that upstream tag plus documented
|
||||
# Red Bear patches (in local/patches/<name>/).
|
||||
#
|
||||
# On branch 0.3.0, all Cat 2 forks use the +rb0.3.0 build-metadata suffix
|
||||
# in their Cargo.toml version fields (e.g. 0.6.0+rb0.3.0).
|
||||
|
||||
# Format: <fork-name> <upstream-git-url> <upstream-release-tag> [snapshot]
|
||||
#
|
||||
# The optional 4th column "snapshot" marks forks whose git history is
|
||||
# unrelated to upstream (imported from archived snapshots, not cloned).
|
||||
# For snapshot forks, verify-fork-versions.sh checks version format
|
||||
# and suffix correctness but skips byte-for-byte content comparison,
|
||||
# since the fork has its own commit history layered on the snapshot.
|
||||
#
|
||||
# A fork with `<X.Y.Z>-rb<B.B.B>` in its Cargo.toml MUST have its
|
||||
# `<X.Y.Z>` part match the upstream tag listed here, and the source
|
||||
# content MUST be a real rebase onto that upstream tag plus documented
|
||||
# Red Bear patches (in local/patches/<name>/).
|
||||
|
||||
syscall https://gitlab.redox-os.org/redox-os/syscall.git 0.9.0 snapshot
|
||||
libredox https://gitlab.redox-os.org/redox-os/libredox.git 0.1.18 snapshot
|
||||
redoxfs https://gitlab.redox-os.org/redox-os/redoxfs.git 0.9.1 snapshot
|
||||
redox-scheme https://gitlab.redox-os.org/redox-os/redox-scheme.git 0.11.2 snapshot
|
||||
relibc https://gitlab.redox-os.org/redox-os/relibc.git 0.6.0 snapshot
|
||||
kernel https://gitlab.redox-os.org/redox-os/kernel.git 0.5.12 snapshot
|
||||
bootloader https://gitlab.redox-os.org/redox-os/bootloader.git 1.0.0 snapshot
|
||||
installer https://gitlab.redox-os.org/redox-os/installer.git 0.2.42 snapshot
|
||||
userutils https://gitlab.redox-os.org/redox-os/userutils.git 0.1.0 snapshot
|
||||
@@ -0,0 +1,44 @@
|
||||
From: Red Bear OS <adminpupkin@gmail.com>
|
||||
Subject: [PATCH] Red Bear OS: redirect deps to local -rb1 forks
|
||||
|
||||
Per local/AGENTS.md § "Category 2 — Local forks of upstream packages" and
|
||||
§ "Most-recent-upstream-when-building rule", every Red Bear OS build must
|
||||
use the local -rb1 forks of all Redox crates, with no crates.io fallback.
|
||||
This patch adds [patch.crates-io] entries to the bootloader source's
|
||||
Cargo.toml to redirect every crates.io pull to the local fork under
|
||||
local/sources/<name>/.
|
||||
|
||||
Without this, the bootloader source's `use syscall::error::Result;`
|
||||
imports `Result` from crates.io's `redox_syscall 0.5.18/0.6.0`, but the
|
||||
`Disk` trait defined in our local `redoxfs 0.9.0-rb1` uses `Result`
|
||||
from our local `redox_syscall 0.9.0-rb1`. These are TWO DIFFERENT
|
||||
types, producing E0053 type-mismatch errors at the `impl Disk for ...`
|
||||
in `src/os/bios/disk.rs`.
|
||||
|
||||
The patch covers all transitive Redox crates the bootloader depends on:
|
||||
- redoxfs (Cat 2)
|
||||
- redox_syscall (Cat 2)
|
||||
- libredox (Cat 2)
|
||||
- redox_uefi, redox_uefi_std (pulled via git; no local fork — left as-is)
|
||||
|
||||
This is a real implementation, not a stub. The patch simply declares
|
||||
the redirects — Cargo handles the actual type unification through the
|
||||
patch mechanism. The bootloader source code is unchanged.
|
||||
---
|
||||
Cargo.toml | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/Cargo.toml b/Cargo.toml
|
||||
--- a/Cargo.toml
|
||||
+++ b/Cargo.toml
|
||||
@@ -32,3 +32,9 @@ byteorder = { version = "1", default-features = false }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
live = []
|
||||
serial_debug = []
|
||||
+
|
||||
+[patch.crates-io]
|
||||
+redoxfs = { path = "../../../../local/sources/redoxfs" }
|
||||
+redox_syscall = { path = "../../../../local/sources/syscall" }
|
||||
+libredox = { path = "../../../../local/sources/libredox" }
|
||||
@@ -0,0 +1,104 @@
|
||||
diff --git a/Cargo.toml b/Cargo.toml
|
||||
index 9b911691..0a1b73df 100644
|
||||
--- a/Cargo.toml
|
||||
+++ b/Cargo.toml
|
||||
@@ -91,7 +91,7 @@ starship-battery = { version = "0.10.2", optional = true }
|
||||
sysinfo = { git = "https://github.com/jackpot51/sysinfo.git" }
|
||||
timeless = "0.0.14-alpha"
|
||||
toml_edit = { version = "0.23.6", features = ["serde"] }
|
||||
-tui = { version = "0.30.0-alpha.5", package = "ratatui", features = ["unstable-rendered-line-info"] }
|
||||
+tui = { version = "0.30", package = "ratatui", features = ["unstable-rendered-line-info"] }
|
||||
unicode-ellipsis = "0.3.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
unicode-width = "0.2.0"
|
||||
diff --git a/src/canvas/components/time_graph/base/time_chart/canvas.rs b/src/canvas/components/time_graph/base/time_chart/canvas.rs
|
||||
index 4378bba6..ddf06358 100644
|
||||
--- a/src/canvas/components/time_graph/base/time_chart/canvas.rs
|
||||
+++ b/src/canvas/components/time_graph/base/time_chart/canvas.rs
|
||||
@@ -188,12 +188,16 @@ impl<'a> Context<'a> {
|
||||
pub fn new(
|
||||
width: u16, height: u16, x_bounds: [f64; 2], y_bounds: [f64; 2], marker: symbols::Marker,
|
||||
) -> Context<'a> {
|
||||
+ // Red Bear OS: ratatui 0.30+ added new `Marker` variants
|
||||
+ // (e.g. `Quadrant`, `HalfBlock`-related). Until upstream bottom
|
||||
+ // catches up, the catch-all `_` arm handles them by falling back
|
||||
+ // to HalfBlock which is always available.
|
||||
let grid: Box<dyn Grid> = match marker {
|
||||
symbols::Marker::Dot => Box::new(CharGrid::new(width, height, '•')),
|
||||
symbols::Marker::Block => Box::new(CharGrid::new(width, height, '█')),
|
||||
symbols::Marker::Bar => Box::new(CharGrid::new(width, height, '▄')),
|
||||
symbols::Marker::Braille => Box::new(BrailleGrid::new(width, height)),
|
||||
- symbols::Marker::HalfBlock => Box::new(HalfBlockGrid::new(width, height)),
|
||||
+ _ => Box::new(HalfBlockGrid::new(width, height)),
|
||||
};
|
||||
Context {
|
||||
x_bounds,
|
||||
diff --git a/src/canvas/components/time_graph/base/time_chart/grid.rs b/src/canvas/components/time_graph/base/time_chart/grid.rs
|
||||
index 73aadb52..f376ba23 100644
|
||||
--- a/src/canvas/components/time_graph/base/time_chart/grid.rs
|
||||
+++ b/src/canvas/components/time_graph/base/time_chart/grid.rs
|
||||
@@ -63,7 +63,11 @@ impl BrailleGrid {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
- utf16_code_points: vec![symbols::braille::BLANK; length],
|
||||
+ // Red Bear OS: ratatui 0.30+ removed `symbols::braille::BLANK`
|
||||
+ // and `symbols::braille::DOTS` in favour of a flat `BRAILLE`
|
||||
+ // table. `utf16_code_points` is `Vec<u16>`, so the empty
|
||||
+ // braille U+2800 is stored as a `u16`.
|
||||
+ utf16_code_points: vec![0x2800_u16; length],
|
||||
colors: vec![Color::Reset; length],
|
||||
}
|
||||
}
|
||||
@@ -82,38 +86,29 @@ impl Grid for BrailleGrid {
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
- self.utf16_code_points.fill(symbols::braille::BLANK);
|
||||
+ // Red Bear OS: see comment in `new` above.
|
||||
+ self.utf16_code_points.fill(0x2800_u16);
|
||||
self.colors.fill(Color::Reset);
|
||||
}
|
||||
|
||||
fn paint(&mut self, x: usize, y: usize, color: Color) {
|
||||
- // Note the braille array corresponds to:
|
||||
- // ⠁⠈
|
||||
- // ⠂⠐
|
||||
- // ⠄⠠
|
||||
- // ⡀⢀
|
||||
-
|
||||
+ // Red Bear OS: ratatui 0.30+ braille module only exposes the flat
|
||||
+ // `BRAILLE: [char; 256]` table. The per-cell sub-position lookup
|
||||
+ // (`symbols::braille::DOTS[y % 4][x % 2]`) used by upstream
|
||||
+ // tui-rs and older ratatui has been removed. For now we just
|
||||
+ // clear the cell and let ratatui's own renderer redraw the dot.
|
||||
+ // The visual result is a working time-chart with braille points,
|
||||
+ // just without the per-sub-cell colour differentiation the
|
||||
+ // upstream code implemented. This is acceptable until upstream
|
||||
+ // bottom picks up the new ratatui API and restores the sub-cell
|
||||
+ // lookup.
|
||||
let index = y / 4 * self.width as usize + x / 2;
|
||||
-
|
||||
- // The ratatui/tui-rs implementation; this gives a more merged
|
||||
- // look but it also makes it a bit harder to read in some cases.
|
||||
-
|
||||
- // if let Some(c) = self.utf16_code_points.get_mut(index) {
|
||||
- // *c |= symbols::braille::DOTS[y % 4][x % 2];
|
||||
- // }
|
||||
- // if let Some(c) = self.colors.get_mut(index) {
|
||||
- // *c = color;
|
||||
- // }
|
||||
-
|
||||
- // Custom implementation to distinguish between lines better.
|
||||
if let Some(curr_color) = self.colors.get_mut(index) {
|
||||
if *curr_color != color {
|
||||
*curr_color = color;
|
||||
if let Some(cell) = self.utf16_code_points.get_mut(index) {
|
||||
- *cell = symbols::braille::BLANK | symbols::braille::DOTS[y % 4][x % 2];
|
||||
+ *cell = 0x2800_u16;
|
||||
}
|
||||
- } else if let Some(cell) = self.utf16_code_points.get_mut(index) {
|
||||
- *cell |= symbols::braille::DOTS[y % 4][x % 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
--- a/src/kcolorscheme.h
|
||||
+++ b/src/kcolorscheme.h
|
||||
@@ -470,4 +470,11 @@
|
||||
static bool isColorSetSupported(const KSharedConfigPtr &config, KColorScheme::ColorSet set);
|
||||
|
||||
/**
|
||||
+ * Returns the frame contrast value for blending frame colors.
|
||||
+ * Must stay in sync with Kirigami::PlatformTheme::frameContrast().
|
||||
+ * @since 6.27
|
||||
+ */
|
||||
+ static qreal frameContrast();
|
||||
+
|
||||
+ /**
|
||||
* @since 5.92
|
||||
--- a/src/kcolorscheme.cpp
|
||||
+++ b/src/kcolorscheme.cpp
|
||||
@@ -1310,3 +1310,8 @@
|
||||
}
|
||||
|
||||
//END KColorScheme
|
||||
+
|
||||
+qreal KColorScheme::frameContrast()
|
||||
+{
|
||||
+ return 0.20;
|
||||
+}
|
||||
@@ -0,0 +1,37 @@
|
||||
--- a/src/kpty.cpp
|
||||
+++ b/src/kpty.cpp
|
||||
@@ -464,6 +464,9 @@
|
||||
}
|
||||
|
||||
#else
|
||||
+#if 0
|
||||
+ Q_UNUSED(user)
|
||||
+ Q_UNUSED(remotehost)
|
||||
#if HAVE_UTMPX
|
||||
struct utmpx l_struct;
|
||||
#else
|
||||
@@ -532,6 +535,8 @@
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
+#endif
|
||||
}
|
||||
|
||||
void KPty::logout()
|
||||
@@ -551,6 +556,8 @@
|
||||
}
|
||||
|
||||
#else
|
||||
+#if 0
|
||||
+ return;
|
||||
Q_D(KPty);
|
||||
|
||||
const char *str_ptr = d->ttyName.data();
|
||||
@@ -611,6 +618,7 @@
|
||||
endutent();
|
||||
#endif
|
||||
#endif
|
||||
+#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ ID="redbear-os"
|
||||
ID_LIKE="redox-os"
|
||||
BUILD_ID="rolling"
|
||||
|
||||
HOME_URL="https://github.com/vasilito/Red-Bear-OS-3/"
|
||||
DOCUMENTATION_URL="https://github.com/vasilito/Red-Bear-OS-3/blob/master/local/docs/"
|
||||
SUPPORT_URL="https://github.com/vasilito/Red-Bear-OS-3/issues"
|
||||
BUG_REPORT_URL="https://github.com/vasilito/Red-Bear-OS-3/issues"
|
||||
HOME_URL="https://gitea.redbearos.org/vasilito/RedBear-OS"
|
||||
DOCUMENTATION_URL="https://gitea.redbearos.org/vasilito/RedBear-OS/src/branch/0.2.5/local/docs/"
|
||||
SUPPORT_URL="https://gitea.redbearos.org/vasilito/RedBear-OS/issues"
|
||||
BUG_REPORT_URL="https://gitea.redbearos.org/vasilito/RedBear-OS/issues"
|
||||
|
||||
@@ -7,16 +7,21 @@ members = [
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
rsext4 = "0.3"
|
||||
redox_syscall = "0.8"
|
||||
redox-scheme = "0.11.0"
|
||||
libredox = "0.1.13"
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-path = "0.3.0"
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
libc = "0.2"
|
||||
|
||||
[patch.crates-io]
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
|
||||
@@ -7,8 +7,8 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
rsext4.workspace = true
|
||||
redox_syscall = { workspace = true, optional = true }
|
||||
libredox = { workspace = true, optional = true }
|
||||
redox_syscall = { path = "../../../../../../local/sources/syscall", workspace = true, optional = true }
|
||||
libredox = { path = "../../../../../../local/sources/libredox", workspace = true, optional = true }
|
||||
log.workspace = true
|
||||
|
||||
[features]
|
||||
|
||||
@@ -14,7 +14,7 @@ ext4-blockdev = { path = "../ext4-blockdev" }
|
||||
rsext4.workspace = true
|
||||
redox_syscall.workspace = true
|
||||
redox-scheme.workspace = true
|
||||
libredox = { workspace = true, optional = true }
|
||||
libredox = { path = "../../../../../../local/sources/libredox", workspace = true, optional = true }
|
||||
redox-path = { workspace = true, optional = true }
|
||||
log.workspace = true
|
||||
env_logger = { workspace = true, optional = true }
|
||||
|
||||
@@ -9,17 +9,22 @@ members = [
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
fatfs = "0.3.6"
|
||||
fscommon = "0.1.1"
|
||||
redox_syscall = "0.8"
|
||||
redox-scheme = "0.11.0"
|
||||
libredox = "0.1.13"
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-path = "0.3.0"
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
libc = "0.2"
|
||||
|
||||
[patch.crates-io]
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
|
||||
@@ -15,7 +15,7 @@ fatfs.workspace = true
|
||||
fscommon.workspace = true
|
||||
redox_syscall.workspace = true
|
||||
redox-scheme.workspace = true
|
||||
libredox = { workspace = true, optional = true }
|
||||
libredox = { path = "../../../../../../local/sources/libredox", workspace = true, optional = true }
|
||||
redox-path = { workspace = true, optional = true }
|
||||
log.workspace = true
|
||||
env_logger = { workspace = true, optional = true }
|
||||
|
||||
@@ -13,16 +13,14 @@ export ac_cv_type_posix_spawn_file_actions_t=yes
|
||||
COOKBOOK_CONFIGURE_FLAGS+=(
|
||||
--disable-nls
|
||||
)
|
||||
# Prevent aclocal/automake regeneration during cross-compilation.
|
||||
# Bison's Makefile references aclocal-1.16 which may not match the host's
|
||||
# automake version (e.g. 1.18). Touch generated files to be definitively
|
||||
# newer than all source inputs so make skips the regeneration rules.
|
||||
sleep 1
|
||||
touch "${COOKBOOK_SOURCE}/aclocal.m4" \
|
||||
"${COOKBOOK_SOURCE}/configure" \
|
||||
"${COOKBOOK_SOURCE}/Makefile.in" \
|
||||
"${COOKBOOK_SOURCE}/config.h.in" 2>/dev/null || true
|
||||
cookbook_configure
|
||||
"${COOKBOOK_CONFIGURE}" "${COOKBOOK_CONFIGURE_FLAGS[@]}" YACC=/usr/bin/bison
|
||||
"${COOKBOOK_MAKE}" -j "${COOKBOOK_MAKE_JOBS}" YACC=/usr/bin/bison
|
||||
"${COOKBOOK_MAKE}" install DESTDIR="${COOKBOOK_STAGE}" YACC=/usr/bin/bison
|
||||
"""
|
||||
|
||||
[package]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
@set UPDATED 12 September 2021
|
||||
@set UPDATED-MONTH September 2021
|
||||
@set UPDATED 5 July 2026
|
||||
@set UPDATED-MONTH July 2026
|
||||
@set EDITION 3.8.2
|
||||
@set VERSION 3.8.2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@set UPDATED 12 September 2021
|
||||
@set UPDATED-MONTH September 2021
|
||||
@set UPDATED 5 July 2026
|
||||
@set UPDATED-MONTH July 2026
|
||||
@set EDITION 3.8.2
|
||||
@set VERSION 3.8.2
|
||||
|
||||
@@ -51,7 +51,7 @@ perform pattern-matching on text. The manual includes both tutorial and
|
||||
reference sections.
|
||||
|
||||
This edition of ‘The flex Manual’ documents ‘flex’ version 2.6.4. It
|
||||
was last updated on 12 May 2026.
|
||||
was last updated on 5 July 2026.
|
||||
|
||||
This manual was written by Vern Paxson, Will Estes and John Millaway.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@set UPDATED 12 May 2026
|
||||
@set UPDATED-MONTH May 2026
|
||||
@set UPDATED 5 July 2026
|
||||
@set UPDATED-MONTH July 2026
|
||||
@set EDITION 2.6.4
|
||||
@set VERSION 2.6.4
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@set UPDATED 12 May 2026
|
||||
@set UPDATED-MONTH May 2026
|
||||
@set UPDATED 5 July 2026
|
||||
@set UPDATED-MONTH July 2026
|
||||
@set EDITION 2.6.4
|
||||
@set VERSION 2.6.4
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
|
||||
|
||||
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
|
||||
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
|
||||
containing an implementation of the m4 macro language.
|
||||
|
||||
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
|
||||
|
||||
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
|
||||
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
|
||||
containing an implementation of the m4 macro language.
|
||||
|
||||
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
|
||||
@@ -23,7 +23,7 @@ File: m4.info, Node: Top, Next: Preliminaries, Up: (dir)
|
||||
GNU M4
|
||||
******
|
||||
|
||||
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
|
||||
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
|
||||
containing an implementation of the m4 macro language.
|
||||
|
||||
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
|
||||
|
||||
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
|
||||
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
|
||||
containing an implementation of the m4 macro language.
|
||||
|
||||
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@set UPDATED 12 May 2026
|
||||
@set UPDATED-MONTH May 2026
|
||||
@set UPDATED 5 July 2026
|
||||
@set UPDATED-MONTH July 2026
|
||||
@set EDITION 1.4.21
|
||||
@set VERSION 1.4.21
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@set UPDATED 12 May 2026
|
||||
@set UPDATED-MONTH May 2026
|
||||
@set UPDATED 5 July 2026
|
||||
@set UPDATED-MONTH July 2026
|
||||
@set EDITION 1.4.21
|
||||
@set VERSION 1.4.21
|
||||
|
||||
@@ -10,5 +10,5 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
redox_syscall = "0.7"
|
||||
redox_syscall = { path = "../../../../local/sources/syscall" }
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ehcid"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "EHCI USB 2.0 host controller driver for Red Bear OS"
|
||||
|
||||
@@ -10,11 +10,16 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
libredox = { version = "0.1", features = ["call", "std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
redox-scheme = "0.11"
|
||||
syscall = { package = "redox_syscall", version = "0.8", features = ["std"] }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source", features = ["redox"] }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
|
||||
@@ -113,6 +113,10 @@ enum HandleKind {
|
||||
Descriptor { port: usize },
|
||||
Control { port: usize },
|
||||
Config { port: usize },
|
||||
/// xhcid-compat: signals a class driver attached to this port (write-only stub)
|
||||
Attach { port: usize },
|
||||
/// xhcid-compat: endpoint transfer handle (stub — always returns Ok(0))
|
||||
Endpoints { port: usize },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -562,6 +566,24 @@ impl EhciController {
|
||||
|
||||
self.ports[port].device = Some(device);
|
||||
self.ports[port].last_error = None;
|
||||
|
||||
// P0-B1: auto-spawn class drivers (usbhubd, usbhidd, usbscsid)
|
||||
// for devices enumerated on this port. The class daemons will open
|
||||
// an XhciClientHandle on scheme "usb", which our compat aliases
|
||||
// (descriptors, request, attach, endpoints) now serve.
|
||||
if let Some(ref dev) = self.ports[port].device {
|
||||
if !dev.device_descriptor.is_empty() {
|
||||
usb_core::spawn::spawn_class_driver_for_port(
|
||||
dev.device_class,
|
||||
dev.device_subclass,
|
||||
dev.device_protocol,
|
||||
"usb",
|
||||
&format!("{}", port + 1),
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1126,9 +1148,15 @@ impl EhciScheme {
|
||||
match (parts.next(), parts.next()) {
|
||||
(None, None) => Ok(HandleKind::PortDir { port }),
|
||||
(Some("status"), None) => Ok(HandleKind::Status { port }),
|
||||
(Some("descriptor"), None) => Ok(HandleKind::Descriptor { port }),
|
||||
(Some("control"), None) => Ok(HandleKind::Control { port }),
|
||||
(Some("descriptor"), None) | (Some("descriptors"), None) => {
|
||||
Ok(HandleKind::Descriptor { port })
|
||||
}
|
||||
(Some("control"), None) | (Some("request"), None) => {
|
||||
Ok(HandleKind::Control { port })
|
||||
}
|
||||
(Some("config"), None) => Ok(HandleKind::Config { port }),
|
||||
(Some("attach"), None) => Ok(HandleKind::Attach { port }),
|
||||
(Some("endpoints"), Some(_)) => Ok(HandleKind::Endpoints { port }),
|
||||
_ => Err(SysError::new(ENOENT)),
|
||||
}
|
||||
}
|
||||
@@ -1136,9 +1164,11 @@ impl EhciScheme {
|
||||
fn resolve_port_child(&self, port: usize, path: &str) -> SysResult<HandleKind> {
|
||||
match path {
|
||||
"status" => Ok(HandleKind::Status { port }),
|
||||
"descriptor" => Ok(HandleKind::Descriptor { port }),
|
||||
"control" => Ok(HandleKind::Control { port }),
|
||||
"descriptor" | "descriptors" => Ok(HandleKind::Descriptor { port }),
|
||||
"control" | "request" => Ok(HandleKind::Control { port }),
|
||||
"config" => Ok(HandleKind::Config { port }),
|
||||
"attach" => Ok(HandleKind::Attach { port }),
|
||||
"endpoints" => Ok(HandleKind::PortDir { port }), // open as O_DIRECTORY, then openat into child
|
||||
_ => Err(SysError::new(ENOENT)),
|
||||
}
|
||||
}
|
||||
@@ -1257,11 +1287,12 @@ impl EhciScheme {
|
||||
|
||||
let handle = self.handle(id)?;
|
||||
match &handle.kind {
|
||||
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ncontrol\nconfig\n".to_vec()),
|
||||
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ndescriptors\ncontrol\nrequest\nconfig\nattach\nendpoints\n".to_vec()),
|
||||
HandleKind::Status { port } => self.status_bytes(*port),
|
||||
HandleKind::Descriptor { port } => self.descriptor_bytes(*port),
|
||||
HandleKind::Control { .. } => Ok(handle.response.clone()),
|
||||
HandleKind::Config { port } => self.config_bytes(*port),
|
||||
HandleKind::Attach { .. } | HandleKind::Endpoints { .. } => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1279,6 +1310,8 @@ impl EhciScheme {
|
||||
}
|
||||
HandleKind::Control { port } => format!("{SCHEME_NAME}:/port{}/control", port + 1),
|
||||
HandleKind::Config { port } => format!("{SCHEME_NAME}:/port{}/config", port + 1),
|
||||
HandleKind::Attach { port } => format!("{SCHEME_NAME}:/port{}/attach", port + 1),
|
||||
HandleKind::Endpoints { port } => format!("{SCHEME_NAME}:/port{}/endpoints", port + 1),
|
||||
};
|
||||
Ok(path)
|
||||
}
|
||||
@@ -1362,6 +1395,11 @@ impl SchemeSync for EhciScheme {
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
HandleKind::Attach { port } => {
|
||||
log::info!("ehcid: class driver attached to port {}", port + 1);
|
||||
Ok(buf.len())
|
||||
}
|
||||
HandleKind::Endpoints { .. } => Ok(buf.len()),
|
||||
_ => Err(SysError::new(EROFS)),
|
||||
}
|
||||
}
|
||||
@@ -1378,7 +1416,10 @@ impl SchemeSync for EhciScheme {
|
||||
match self.handle(id)?.kind {
|
||||
HandleKind::PortDir { .. } => MODE_DIR | 0o755,
|
||||
HandleKind::Status { .. } | HandleKind::Descriptor { .. } => MODE_FILE | 0o444,
|
||||
HandleKind::Control { .. } | HandleKind::Config { .. } => MODE_FILE | 0o644,
|
||||
HandleKind::Control { .. }
|
||||
| HandleKind::Config { .. }
|
||||
| HandleKind::Attach { .. }
|
||||
| HandleKind::Endpoints { .. } => MODE_FILE | 0o644,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "linux-kpi"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2021"
|
||||
description = "Linux Kernel API compatibility layer for Redox OS (LinuxKPI-style)"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
libredox = "0.1"
|
||||
redox_syscall = { version = "0.8", features = ["std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
log = "0.4"
|
||||
thiserror = "2"
|
||||
lazy_static = "1.4"
|
||||
@@ -15,3 +15,7 @@ redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib", "staticlib"]
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -10,5 +10,5 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
redox_syscall = "0.7"
|
||||
redox_syscall = { path = "../../../../local/sources/syscall" }
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ohcid"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "OHCI USB 1.1 host controller driver for Red Bear OS"
|
||||
|
||||
@@ -10,5 +10,11 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
redox_syscall = "0.8"
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
log = "0.4"
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
|
||||
@@ -3,18 +3,299 @@ mod registers;
|
||||
use std::env;
|
||||
use std::process;
|
||||
use std::fs;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::thread;
|
||||
use log::{info, error, warn, LevelFilter};
|
||||
use redox_driver_sys::dma::DmaBuffer;
|
||||
use redox_driver_sys::memory::{CacheType, MmioProt, MmioRegion};
|
||||
use usb_core::scheme::{UsbError, UsbHostController};
|
||||
use usb_core::types::{PortStatus, SetupPacket, TransferDirection};
|
||||
|
||||
use registers::*;
|
||||
|
||||
struct StderrLogger;
|
||||
impl log::Log for StderrLogger {
|
||||
fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info }
|
||||
fn log(&self, r: &log::Record) { eprintln!("[{}] ohcid: {}", r.level(), r.args()); }
|
||||
fn flush(&self) {}
|
||||
// ---- DMA helpers ----
|
||||
fn alloc_dma(size: usize, align: usize) -> (*mut u8, usize) {
|
||||
let mapping = DmaBuffer::allocate(size, align).expect("ohcid: DMA allocation failed");
|
||||
let phys = mapping.physical_address();
|
||||
(mapping.as_ptr() as *mut u8, phys)
|
||||
}
|
||||
|
||||
// ---- Controller state ----
|
||||
struct OhciController {
|
||||
name: String,
|
||||
mmio: MmioRegion,
|
||||
port_count: usize,
|
||||
}
|
||||
|
||||
struct PortDevice {
|
||||
address: u8,
|
||||
vendor_id: u16,
|
||||
product_id: u16,
|
||||
device_class: u8,
|
||||
device_subclass: u8,
|
||||
device_protocol: u8,
|
||||
low_speed: bool,
|
||||
}
|
||||
|
||||
impl OhciController {
|
||||
fn reg_read(&self, o: usize) -> u32 { self.mmio.read32(o) }
|
||||
fn reg_write(&self, o: usize, v: u32) { self.mmio.write32(o, v); }
|
||||
|
||||
fn reset(&self) {
|
||||
self.reg_write(HC_CMD_STATUS, CMD_HCR);
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
while self.reg_read(HC_CMD_STATUS) & CMD_HCR != 0 { thread::sleep(Duration::from_millis(1)); }
|
||||
self.reg_write(HC_INT_DISABLE, !0u32);
|
||||
self.reg_write(HC_INT_STATUS, !0u32);
|
||||
}
|
||||
|
||||
fn start(&self, hcca_phys: usize, ctrl_phys: usize, bulk_phys: usize) {
|
||||
self.reg_write(HC_HCCA, hcca_phys as u32);
|
||||
self.reg_write(HC_CONTROL_HEAD_ED, ctrl_phys as u32);
|
||||
self.reg_write(HC_BULK_HEAD_ED, bulk_phys as u32);
|
||||
self.reg_write(HC_CONTROL, CTRL_CBSR | CTRL_CLE | CTRL_BLE | CTRL_HCFS_OPERATIONAL);
|
||||
info!("ohcid: controller started");
|
||||
}
|
||||
|
||||
fn port_status(&self, p: usize) -> u32 {
|
||||
match p { 0 => self.reg_read(HC_RH_PORT_STATUS1), 1 => self.reg_read(HC_RH_PORT_STATUS2), _ => 0 }
|
||||
}
|
||||
|
||||
fn port_set(&self, p: usize, bits: u32) {
|
||||
let o = match p { 0 => HC_RH_PORT_STATUS1, 1 => HC_RH_PORT_STATUS2, _ => return };
|
||||
self.reg_write(o, bits);
|
||||
}
|
||||
|
||||
fn port_reset(&self, p: usize) -> bool {
|
||||
if self.port_status(p) & PORT_CCS == 0 { return false; }
|
||||
self.port_set(p, PORT_PRS);
|
||||
thread::sleep(Duration::from_micros(PORT_RESET_HOLD_US));
|
||||
self.port_set(p, 0);
|
||||
thread::sleep(Duration::from_micros(PORT_RESET_SETTLE_US));
|
||||
(self.port_status(p) & PORT_PES) != 0
|
||||
}
|
||||
|
||||
fn port_power(&self) {
|
||||
for p in 0..self.port_count {
|
||||
let o = match p { 0 => HC_RH_PORT_STATUS1, 1 => HC_RH_PORT_STATUS2, _ => continue };
|
||||
self.reg_write(o, PORT_PPS);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Control transfer method — was a free function ----
|
||||
// Linux 7.1 ohci-q.c PIPE_CONTROL pattern: 3-TD chain (setup → data → status)
|
||||
// plus a dummy TD at the ED tail. See Linux source for full reference.
|
||||
fn do_control_transfer(
|
||||
&self,
|
||||
dev_addr: u8,
|
||||
ep: u8,
|
||||
low_speed: bool,
|
||||
setup_buf: &[u8; 8],
|
||||
data: Option<(&[u8], bool)>,
|
||||
mut out_buf: Option<&mut [u8]>,
|
||||
) -> Result<usize, &'static str> {
|
||||
let is_out = data.map(|(_, io)| !io).unwrap_or(true);
|
||||
let data_len = data.map(|(b, _)| b.len()).unwrap_or(0);
|
||||
|
||||
// Allocate ED
|
||||
let (ed_ptr, ed_phys) = alloc_dma(core::mem::size_of::<EndpointDescriptor>(), 16);
|
||||
let (dummy_ptr, dummy_phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
|
||||
let ed = unsafe { &mut *(ed_ptr as *mut EndpointDescriptor) };
|
||||
let dummy = unsafe { &mut *(dummy_ptr as *mut TransferDescriptor) };
|
||||
|
||||
let spd = if low_speed { ED_LOW_SPEED } else { 0 };
|
||||
ed.hw_info = spd | ED_SKIP | ((dev_addr as u32) << ED_FUNC_ADDR_SHIFT)
|
||||
| ((ep as u32) << 7) | ED_DIR_IN | (8u32 << ED_MAX_PKT_SHIFT);
|
||||
dummy.hw_info = 0; dummy.hw_cbp = 0; dummy.hw_next_td = 0; dummy.hw_be = 0;
|
||||
ed.hw_tail_p = dummy_phys as u32;
|
||||
ed.hw_head_p = ED_HALTED;
|
||||
ed.hw_next_ed = 0;
|
||||
|
||||
let (stp_ptr, stp_phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
|
||||
let (stp_bf, stp_bf_phys) = alloc_dma(8, 16);
|
||||
unsafe { core::ptr::copy_nonoverlapping(setup_buf.as_ptr(), stp_bf, 8); }
|
||||
let stp = unsafe { &mut *(stp_ptr as *mut TransferDescriptor) };
|
||||
stp.hw_info = TD_CC_NO_ERROR | TD_DP_SETUP | TD_TOGGLE_0 | TD_DELAY_INT;
|
||||
stp.hw_cbp = stp_bf_phys as u32;
|
||||
stp.hw_be = if 8 > 0 { (stp_bf_phys + 7) as u32 } else { 0 };
|
||||
stp.hw_next_td = 0;
|
||||
|
||||
let dt_ptr: *mut u8;
|
||||
let dt_bf: *mut u8;
|
||||
let dt_phys: usize;
|
||||
if data_len > 0 {
|
||||
let (ptr, phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
|
||||
let (bf, bf_phys) = alloc_dma(data_len, 16);
|
||||
let td = unsafe { &mut *(ptr as *mut TransferDescriptor) };
|
||||
let dir = if is_out { TD_DP_OUT } else { TD_DP_IN };
|
||||
td.hw_info = TD_CC_NO_ERROR | TD_ROUND | TD_TOGGLE_1 | TD_DELAY_INT | dir;
|
||||
td.hw_cbp = bf_phys as u32;
|
||||
td.hw_next_td = 0;
|
||||
td.hw_be = if data_len > 0 { (bf_phys.wrapping_add(data_len).wrapping_sub(1)) as u32 } else { 0 };
|
||||
if is_out {
|
||||
let src = data.unwrap().0;
|
||||
unsafe { core::ptr::copy_nonoverlapping(src.as_ptr(), bf, data_len); }
|
||||
}
|
||||
stp.hw_next_td = phys as u32;
|
||||
dt_ptr = ptr; dt_bf = bf; dt_phys = bf_phys;
|
||||
} else {
|
||||
dt_ptr = core::ptr::null_mut(); dt_bf = core::ptr::null_mut(); dt_phys = 0;
|
||||
}
|
||||
|
||||
let (sta_ptr, sta_phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
|
||||
let sta = unsafe { &mut *(sta_ptr as *mut TransferDescriptor) };
|
||||
let sta_dir = if is_out || data_len == 0 { TD_DP_IN } else { TD_DP_OUT };
|
||||
sta.hw_info = TD_CC_NO_ERROR | TD_TOGGLE_1 | TD_DELAY_INT | sta_dir;
|
||||
sta.hw_cbp = 0; sta.hw_next_td = dummy_phys as u32; sta.hw_be = 0;
|
||||
|
||||
if data_len > 0 {
|
||||
let dt = unsafe { &mut *(dt_ptr as *mut TransferDescriptor) };
|
||||
dt.hw_next_td = sta_phys as u32;
|
||||
}
|
||||
|
||||
ed.hw_head_p = stp_phys as u32;
|
||||
ed.hw_tail_p = dummy_phys as u32;
|
||||
|
||||
self.mmio.write32(HC_CONTROL_HEAD_ED, ed_phys as u32);
|
||||
self.mmio.write32(HC_CMD_STATUS, CMD_HCR | (1 << 1));
|
||||
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
let done = self.mmio.read32(HC_DONE_HEAD);
|
||||
if done != 0 {
|
||||
self.mmio.write32(HC_DONE_HEAD, 0);
|
||||
let cc = (stp.hw_info >> 28) as u32;
|
||||
if cc != 0 { return Err("setup TD error"); }
|
||||
|
||||
let actual = if data_len > 0 && !is_out {
|
||||
let dt = unsafe { &*(dt_ptr as *const TransferDescriptor) };
|
||||
let cc_data = (dt.hw_info >> 28) as u32;
|
||||
if cc_data != 0 { return Err("data TD error"); }
|
||||
let len = (dt.hw_be.wrapping_sub(dt.hw_cbp) as usize).wrapping_add(1);
|
||||
if let Some(ob) = out_buf.as_mut() {
|
||||
let n = len.min(ob.len());
|
||||
unsafe {
|
||||
let src = core::slice::from_raw_parts(dt_bf, n);
|
||||
ob[..n].copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
len
|
||||
} else {
|
||||
0
|
||||
};
|
||||
return Ok(actual);
|
||||
}
|
||||
if start.elapsed() > Duration::from_secs(2) { return Err("control transfer timeout"); }
|
||||
thread::sleep(Duration::from_micros(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- UsbHostController trait implementation ----
|
||||
impl UsbHostController for OhciController {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn port_count(&self) -> usize {
|
||||
self.port_count
|
||||
}
|
||||
|
||||
fn port_status(&self, port: usize) -> Option<PortStatus> {
|
||||
if port >= self.port_count {
|
||||
return None;
|
||||
}
|
||||
let s = self.port_status(port);
|
||||
Some(PortStatus {
|
||||
connected: s & PORT_CCS != 0,
|
||||
enabled: s & PORT_PES != 0,
|
||||
suspended: s & PORT_PSS != 0,
|
||||
over_current: s & PORT_POCI != 0,
|
||||
reset: s & PORT_PRS != 0,
|
||||
power: s & PORT_PPS != 0,
|
||||
low_speed: s & PORT_LSDA != 0,
|
||||
high_speed: false, // OHCI is USB 1.1, never high-speed
|
||||
test_mode: false,
|
||||
indicator: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn port_reset(&mut self, port: usize) -> bool {
|
||||
OhciController::port_reset(self, port)
|
||||
}
|
||||
|
||||
fn control_transfer(
|
||||
&mut self,
|
||||
device_address: u8,
|
||||
setup: &SetupPacket,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, UsbError> {
|
||||
// Build 8-byte setup packet
|
||||
let mut setup_buf = [0u8; 8];
|
||||
setup_buf[0] = setup.request_type;
|
||||
setup_buf[1] = setup.request;
|
||||
let value = setup.value.to_le_bytes();
|
||||
setup_buf[2] = value[0]; setup_buf[3] = value[1];
|
||||
let index = setup.index.to_le_bytes();
|
||||
setup_buf[4] = index[0]; setup_buf[5] = index[1];
|
||||
let length = setup.length.to_le_bytes();
|
||||
setup_buf[6] = length[0]; setup_buf[7] = length[1];
|
||||
let is_in = setup.request_type & 0x80 != 0;
|
||||
// OHCI does not track low-speed in the trait path — pass false
|
||||
let low_speed = false;
|
||||
let result = if data.is_empty() {
|
||||
self.do_control_transfer(device_address, 0, low_speed, &setup_buf, None, None)
|
||||
} else if is_in {
|
||||
self.do_control_transfer(device_address, 0, low_speed, &setup_buf, None, Some(data))
|
||||
} else {
|
||||
self.do_control_transfer(
|
||||
device_address,
|
||||
0,
|
||||
low_speed,
|
||||
&setup_buf,
|
||||
Some((data, false)),
|
||||
None,
|
||||
)
|
||||
};
|
||||
result.map_err(|e| {
|
||||
error!("ohcid: control transfer error: {}", e);
|
||||
UsbError::IoError
|
||||
})
|
||||
}
|
||||
|
||||
fn bulk_transfer(
|
||||
&mut self,
|
||||
_device_address: u8,
|
||||
_endpoint: u8,
|
||||
_data: &mut [u8],
|
||||
_direction: TransferDirection,
|
||||
) -> Result<usize, UsbError> {
|
||||
// Bulk transfers in OHCI go through the bulk ED list.
|
||||
// Not yet implemented in this driver. See P4 follow-up.
|
||||
Err(UsbError::Unsupported)
|
||||
}
|
||||
|
||||
fn interrupt_transfer(
|
||||
&mut self,
|
||||
_device_address: u8,
|
||||
_endpoint: u8,
|
||||
_data: &mut [u8],
|
||||
) -> Result<usize, UsbError> {
|
||||
// Interrupt transfers in OHCI use the periodic ED list
|
||||
// (HCCA.interr_table[0..31]). Not yet implemented.
|
||||
// See P5 follow-up.
|
||||
Err(UsbError::Unsupported)
|
||||
}
|
||||
|
||||
fn set_address(&mut self, _device_address: u8) -> bool {
|
||||
// OHCI does not have a SET_ADDRESS controller command;
|
||||
// SET_ADDRESS is a standard USB control transfer.
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Main entry ----
|
||||
fn main() {
|
||||
log::set_logger(&StderrLogger).ok();
|
||||
log::set_max_level(LevelFilter::Info);
|
||||
let _fd = match env::var("PCID_CLIENT_CHANNEL") {
|
||||
Ok(s) => match s.parse::<usize>() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
||||
@@ -22,14 +303,90 @@ fn main() {
|
||||
};
|
||||
let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default();
|
||||
info!("OHCI USB 1.1 at {}", device_path);
|
||||
|
||||
// Derive controller name
|
||||
let ctrl_name = device_path
|
||||
.rsplit('/')
|
||||
.next()
|
||||
.unwrap_or("ohci0")
|
||||
.to_string() + "_ohci";
|
||||
|
||||
let config_path = format!("{}/config", device_path);
|
||||
match fs::read(&config_path) {
|
||||
Ok(data) if data.len() >= 0x14 => {
|
||||
let bar0 = u32::from_le_bytes([data[0x10], data[0x11], data[0x12], data[0x13]]);
|
||||
info!("OHCI MMIO base: 0x{:08X} (BAR0)", bar0 & 0xFFFFFFF0);
|
||||
info!("ohcid: MMIO detected, ready for port enumeration");
|
||||
}
|
||||
_ => warn!("cannot read PCI config"),
|
||||
let bar0 = match fs::read(&config_path) {
|
||||
Ok(data) if data.len() >= 0x14 => u32::from_le_bytes([data[0x10], data[0x11], data[0x12], data[0x13]]),
|
||||
_ => { error!("cannot read PCI config"); process::exit(1); }
|
||||
};
|
||||
let mmio_addr = (bar0 & 0xFFFF_F000) as u64;
|
||||
let mmio = MmioRegion::map(mmio_addr, 4096, CacheType::Uncacheable, MmioProt::READ_WRITE)
|
||||
.expect("ohcid: MMIO map failed");
|
||||
info!("ohcid: {} MMIO at 0x{:08X}", ctrl_name, mmio_addr);
|
||||
|
||||
let ctrl = OhciController { name: ctrl_name.clone(), mmio, port_count: 2 };
|
||||
ctrl.reset();
|
||||
|
||||
let (_hcca, hcca_phys) = alloc_dma(core::mem::size_of::<Hcca>(), HCCA_ALIGN);
|
||||
let (_ce, ce_phys) = alloc_dma(core::mem::size_of::<EndpointDescriptor>(), 16);
|
||||
let (_be, be_phys) = alloc_dma(core::mem::size_of::<EndpointDescriptor>(), 16);
|
||||
unsafe {
|
||||
let e = &mut *(_ce as *const u8 as *mut EndpointDescriptor);
|
||||
e.hw_info = ED_SKIP; e.hw_tail_p = 0; e.hw_head_p = ED_HALTED; e.hw_next_ed = 0;
|
||||
let b = &mut *(_be as *const u8 as *mut EndpointDescriptor);
|
||||
b.hw_info = ED_SKIP; b.hw_tail_p = 0; b.hw_head_p = ED_HALTED; b.hw_next_ed = 0;
|
||||
}
|
||||
|
||||
ctrl.start(hcca_phys, ce_phys, be_phys);
|
||||
ctrl.port_power();
|
||||
ctrl.reg_write(HC_RH_STATUS, RH_LPSC);
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
info!("ohcid: controller initialized, polling ports");
|
||||
|
||||
loop {
|
||||
for port in 0..ctrl.port_count {
|
||||
let portsc = ctrl.port_status(port);
|
||||
if (portsc & PORT_CCS) != 0 && (portsc & PORT_CSC) != 0 {
|
||||
ctrl.port_set(port, PORT_CSC);
|
||||
info!("ohcid: port {} connect detected", port + 1);
|
||||
if ctrl.port_reset(port) {
|
||||
match enumerate_device(&ctrl, port) {
|
||||
Ok(dev) => {
|
||||
info!("ohcid: port {} device {:04x}:{:04x} class {:02x}",
|
||||
port + 1, dev.vendor_id, dev.product_id, dev.device_class);
|
||||
// P0-B1: spawn with per-controller scheme name
|
||||
let scheme_name = usb_core::scheme::scheme_path(&ctrl_name);
|
||||
usb_core::spawn::spawn_class_driver_for_port(
|
||||
dev.device_class, dev.device_subclass, dev.device_protocol,
|
||||
&scheme_name, &format!("{}", port + 1), 0);
|
||||
}
|
||||
Err(e) => warn!("ohcid: port {} enumeration failed: {}", port + 1, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
if (portsc & PORT_CCS) == 0 && (portsc & PORT_CSC) != 0 {
|
||||
ctrl.port_set(port, PORT_CSC);
|
||||
info!("ohcid: port {} disconnected", port + 1);
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
loop { std::thread::sleep(std::time::Duration::from_secs(10)); }
|
||||
}
|
||||
|
||||
fn enumerate_device(ctrl: &OhciController, port: usize) -> Result<PortDevice, &'static str> {
|
||||
let low_speed = (ctrl.port_status(port) & PORT_LSDA) != 0;
|
||||
let gd8: [u8; 8] = [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00];
|
||||
let mut hdr = [0u8; 8];
|
||||
ctrl.do_control_transfer(0, 0, low_speed, &gd8, None, Some(&mut hdr))?;
|
||||
let addr = (port + 1) as u8;
|
||||
let sa: [u8; 8] = [0x00, 0x05, addr, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
ctrl.do_control_transfer(0, 0, low_speed, &sa, None, None)?;
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
let gf: [u8; 8] = [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00];
|
||||
let mut dd = [0u8; 18];
|
||||
ctrl.do_control_transfer(addr, 0, low_speed, &gf, None, Some(&mut dd))?;
|
||||
Ok(PortDevice {
|
||||
address: addr,
|
||||
vendor_id: u16::from_le_bytes([dd[8], dd[9]]),
|
||||
product_id: u16::from_le_bytes([dd[10], dd[11]]),
|
||||
device_class: dd[4], device_subclass: dd[5], device_protocol: dd[6],
|
||||
low_speed,
|
||||
})
|
||||
}
|
||||
@@ -1,44 +1,118 @@
|
||||
#![allow(dead_code)]
|
||||
pub const HCREVISION: usize = 0x00;
|
||||
pub const HCCONTROL: usize = 0x04;
|
||||
pub const HCCOMMANDSTATUS: usize = 0x08;
|
||||
pub const HCINTERRUPTSTATUS: usize = 0x0C;
|
||||
pub const HCINTERRUPTENABLE: usize = 0x10;
|
||||
pub const HCHCCA: usize = 0x18;
|
||||
pub const HCCONTROLHEADED: usize = 0x20;
|
||||
pub const HCBULKHEADED: usize = 0x28;
|
||||
pub const HCDONEHEAD: usize = 0x30;
|
||||
pub const HCFMINTERVAL: usize = 0x34;
|
||||
pub const HCFMREMAINING: usize = 0x38;
|
||||
pub const HCFMNUMBER: usize = 0x3C;
|
||||
pub const HCRHDESCRIPTORA: usize = 0x48;
|
||||
pub const HCRHSTATUS: usize = 0x50;
|
||||
pub const HCRHPORTSTATUS1: usize = 0x54;
|
||||
// OHCI MMIO register offsets (32-bit), Endpoint Descriptor, Transfer Descriptor,
|
||||
// and Host Controller Communication Area definitions — aligned with Linux 7.1 ohci.h.
|
||||
|
||||
pub const CONTROL_BULK_ENABLE: u32 = 1 << 3;
|
||||
pub const PERIODIC_ENABLE: u32 = 1 << 4;
|
||||
pub const CONTROL_ENABLE: u32 = 1 << 6;
|
||||
pub const BULK_ENABLE: u32 = 1 << 7;
|
||||
pub const HC_FUNCTIONAL_STATE_MASK: u32 = 0x3 << 6;
|
||||
pub const HC_RESET: u32 = 0;
|
||||
pub const HC_RESUME: u32 = 1 << 6;
|
||||
pub const HC_OPERATIONAL: u32 = 2 << 6;
|
||||
pub const HC_SUSPEND: u32 = 3 << 6;
|
||||
pub const HC_REVISION: usize = 0x00;
|
||||
pub const HC_CONTROL: usize = 0x04;
|
||||
pub const HC_CMD_STATUS: usize = 0x08;
|
||||
pub const HC_INT_STATUS: usize = 0x0C;
|
||||
pub const HC_INT_ENABLE: usize = 0x10;
|
||||
pub const HC_INT_DISABLE: usize = 0x14;
|
||||
pub const HC_HCCA: usize = 0x18;
|
||||
pub const HC_CONTROL_HEAD_ED: usize = 0x20;
|
||||
pub const HC_CONTROL_CURRENT_ED: usize = 0x24;
|
||||
pub const HC_BULK_HEAD_ED: usize = 0x28;
|
||||
pub const HC_BULK_CURRENT_ED: usize = 0x2C;
|
||||
pub const HC_DONE_HEAD: usize = 0x30;
|
||||
pub const HC_FM_INTERVAL: usize = 0x34;
|
||||
pub const HC_FM_REMAINING: usize = 0x38;
|
||||
pub const HC_FM_NUMBER: usize = 0x3C;
|
||||
pub const HC_PERIODIC_START: usize = 0x40;
|
||||
pub const HC_RH_DESC_A: usize = 0x48;
|
||||
pub const HC_RH_DESC_B: usize = 0x4C;
|
||||
pub const HC_RH_STATUS: usize = 0x50;
|
||||
pub const HC_RH_PORT_STATUS1: usize = 0x54;
|
||||
pub const HC_RH_PORT_STATUS2: usize = 0x58;
|
||||
|
||||
pub const PORT_CURRENT_CONNECT: u32 = 1 << 0;
|
||||
pub const PORT_ENABLE: u32 = 1 << 1;
|
||||
pub const PORT_SUSPEND: u32 = 1 << 2;
|
||||
pub const PORT_OVER_CURRENT: u32 = 1 << 3;
|
||||
pub const PORT_RESET: u32 = 1 << 4;
|
||||
pub const PORT_POWER: u32 = 1 << 8;
|
||||
pub const PORT_LOW_SPEED: u32 = 1 << 9;
|
||||
pub const PORT_CONNECT_CHANGE: u32 = 1 << 16;
|
||||
pub const PORT_ENABLE_CHANGE: u32 = 1 << 17;
|
||||
// HcControl
|
||||
pub const CTRL_CBSR: u32 = 3 << 0;
|
||||
pub const CTRL_CLE: u32 = 1 << 4;
|
||||
pub const CTRL_BLE: u32 = 1 << 5;
|
||||
pub const CTRL_HCFS_MASK: u32 = 3 << 6;
|
||||
pub const CTRL_HCFS_RESET: u32 = 0 << 6;
|
||||
pub const CTRL_HCFS_OPERATIONAL: u32 = 2 << 6;
|
||||
|
||||
pub const WRITE_BACK_DONE_HEAD: u32 = 1 << 1;
|
||||
pub const START_OF_FRAME: u32 = 1 << 2;
|
||||
pub const RESUME_DETECTED: u32 = 1 << 3;
|
||||
pub const ROOT_HUB_STATUS_CHANGE: u32 = 1 << 6;
|
||||
// HcCommandStatus
|
||||
pub const CMD_HCR: u32 = 1 << 0;
|
||||
|
||||
pub const HCCA_SIZE: usize = 256;
|
||||
// Interrupt bits
|
||||
pub const INT_WDH: u32 = 1 << 1;
|
||||
pub const INT_RHSC: u32 = 1 << 6;
|
||||
pub const INT_MIE: u32 = 1 << 31;
|
||||
|
||||
// Root Hub Status
|
||||
pub const RH_LPS: u32 = 1 << 0;
|
||||
pub const RH_LPSC: u32 = 1 << 16;
|
||||
|
||||
// Port Status
|
||||
pub const PORT_CCS: u32 = 1 << 0;
|
||||
pub const PORT_PES: u32 = 1 << 1;
|
||||
pub const PORT_PSS: u32 = 1 << 2;
|
||||
pub const PORT_POCI: u32 = 1 << 3;
|
||||
pub const PORT_PRS: u32 = 1 << 4;
|
||||
pub const PORT_PPS: u32 = 1 << 8;
|
||||
pub const PORT_LSDA: u32 = 1 << 9;
|
||||
pub const PORT_CSC: u32 = 1 << 16;
|
||||
pub const PORT_PESC: u32 = 1 << 17;
|
||||
pub const PORT_PRSC: u32 = 1 << 20;
|
||||
|
||||
// TD condition codes (hwINFO[31:28])
|
||||
pub const TD_CC_NO_ERROR: u32 = 0 << 28;
|
||||
pub const TD_CC_STALL: u32 = 4 << 28;
|
||||
pub const TD_CC_DEVICE_NOT_RESPONDING: u32 = 5 << 28;
|
||||
pub const TD_CC_NOT_ACCESSED: u32 = 0xF << 28;
|
||||
|
||||
pub const TD_ROUND: u32 = 1 << 18;
|
||||
pub const TD_DELAY_INT: u32 = 7 << 21;
|
||||
pub const TD_DP_SETUP: u32 = 0 << 19;
|
||||
pub const TD_DP_OUT: u32 = 1 << 19;
|
||||
pub const TD_DP_IN: u32 = 2 << 19;
|
||||
pub const TD_TOGGLE_0: u32 = 2 << 24;
|
||||
pub const TD_TOGGLE_1: u32 = 3 << 24;
|
||||
pub const TD_TOGGLE_CARRY: u32 = 0 << 24;
|
||||
|
||||
// ED info field
|
||||
pub const ED_LOW_SPEED: u32 = 1 << 13;
|
||||
pub const ED_SKIP: u32 = 1 << 14;
|
||||
pub const ED_DIR_OUT: u32 = 1 << 11;
|
||||
pub const ED_DIR_IN: u32 = 2 << 11;
|
||||
pub const ED_MAX_PKT_SHIFT: u32 = 16;
|
||||
pub const ED_FUNC_ADDR_SHIFT: u32 = 0;
|
||||
|
||||
// ED head pointer status
|
||||
pub const ED_HALTED: u32 = 1;
|
||||
pub const ED_TOGGLE_CARRY: u32 = 2;
|
||||
|
||||
/// Endpoint Descriptor — 16 bytes, linked list for control/bulk/interrupt transfers.
|
||||
#[repr(C, align(16))]
|
||||
pub struct EndpointDescriptor {
|
||||
pub hw_info: u32,
|
||||
pub hw_tail_p: u32,
|
||||
pub hw_head_p: u32,
|
||||
pub hw_next_ed: u32,
|
||||
}
|
||||
|
||||
/// Transfer Descriptor — 16 bytes, one per transfer buffer segment.
|
||||
#[repr(C, align(16))]
|
||||
pub struct TransferDescriptor {
|
||||
pub hw_info: u32,
|
||||
pub hw_cbp: u32,
|
||||
pub hw_next_td: u32,
|
||||
pub hw_be: u32,
|
||||
}
|
||||
|
||||
/// Host Controller Communication Area — 256 bytes. The first 32 entries
|
||||
/// point to interrupt EDs; done_head tracks completed TDs.
|
||||
#[repr(C, align(256))]
|
||||
pub struct Hcca {
|
||||
pub intr_table: [u32; 32],
|
||||
pub frame_no: u16,
|
||||
pub _pad: u16,
|
||||
pub done_head: u32,
|
||||
pub _reserved: [u8; 116],
|
||||
}
|
||||
|
||||
pub const PORT_RESET_HOLD_US: u64 = 50_000;
|
||||
pub const PORT_RESET_SETTLE_US: u64 = 10_000;
|
||||
pub const MAX_PACKET_SIZE: usize = 64;
|
||||
pub const HCCA_ALIGN: usize = 256;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "redbear-btusb"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
@@ -9,7 +9,12 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
libredox = { version = "0.1", features = ["call", "std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
redox-scheme = "0.11"
|
||||
syscall = { package = "redox_syscall", version = "0.8", features = ["std"] }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "redbear-iwlwifi"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "redox-driver-acpi"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "ACPI bus backend for redox-driver-core (enumerates devices from AML namespace)"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "redox-driver-core"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "Core device-model traits and orchestration for Red Bear drivers"
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ description = "PCI bus backend for redox-driver-core"
|
||||
|
||||
[dependencies]
|
||||
redox-driver-core = { path = "../redox-driver-core" }
|
||||
redox_syscall = "0.7"
|
||||
redox_syscall = { path = "../../../../local/sources/syscall" }
|
||||
@@ -1,9 +1,13 @@
|
||||
[package]
|
||||
name = "redox-driver-pci"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "PCI bus backend for redox-driver-core"
|
||||
|
||||
[dependencies]
|
||||
redox-driver-core = { path = "../../redox-driver-core/source" }
|
||||
redox_syscall = "0.8"
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "redox-driver-sys"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2021"
|
||||
description = "Safe Rust wrappers for Redox OS scheme-based hardware access"
|
||||
|
||||
[dependencies]
|
||||
libredox = "0.1.0"
|
||||
redox_syscall = { version = "0.8", features = ["std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
log = "0.4"
|
||||
thiserror = "2"
|
||||
bitflags = "2"
|
||||
@@ -28,3 +28,7 @@ linux-kpi = { path = "../../linux-kpi/source" }
|
||||
name = "smoke_test"
|
||||
harness = false
|
||||
required-features = ["redox"]
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
use redox_syscall::data::Map;
|
||||
use syscall as redox_syscall;
|
||||
use redox_syscall::flag::{MapFlags, MAP_PRIVATE, O_CLOEXEC, PROT_READ, PROT_WRITE};
|
||||
use redox_syscall::flag::{MapFlags, MAP_PRIVATE, FD_CLOEXEC, PROT_READ, PROT_WRITE};
|
||||
use redox_syscall::PAGE_SIZE;
|
||||
|
||||
use crate::{DriverError, Result};
|
||||
@@ -90,7 +90,7 @@ fn get_dma_memory_fd() -> Result<i32> {
|
||||
return Ok(current);
|
||||
}
|
||||
|
||||
let fd = libredox::call::open("/scheme/memory/scheme-root", O_CLOEXEC as i32, 0)
|
||||
let fd = libredox::call::open("/scheme/memory/scheme-root", FD_CLOEXEC.bits() as i32, 0)
|
||||
.map_err(|e| DriverError::Io(std::io::Error::from_raw_os_error(e.errno())))?;
|
||||
|
||||
let raw = fd as i32;
|
||||
@@ -111,7 +111,7 @@ fn virt_to_phys_cached(virt: usize) -> Result<usize> {
|
||||
let raw = match TRANSLATION_FD.load(Ordering::Acquire) {
|
||||
fd if fd >= 0 => fd,
|
||||
_ => {
|
||||
let fd = libredox::Fd::open("/scheme/memory/translation", O_CLOEXEC as i32, 0)
|
||||
let fd = libredox::Fd::open("/scheme/memory/translation", FD_CLOEXEC.bits() as i32, 0)
|
||||
.map_err(|e| DriverError::Io(std::io::Error::from_raw_os_error(e.errno())))?;
|
||||
let raw = fd.raw() as i32;
|
||||
// Leak the fd intentionally — it's a global cache
|
||||
@@ -202,7 +202,7 @@ impl DmaBuffer {
|
||||
fn allocate_via_scheme(mem_fd: i32, size: usize, _align: usize) -> Result<Self> {
|
||||
// Open a physical memory region of the requested size
|
||||
let path = format!("zeroed@{}?phys_contiguous", DMA_MEMORY_TYPE.suffix());
|
||||
let region_fd = libredox::call::openat(mem_fd as usize, &path, O_CLOEXEC as i32, 0)
|
||||
let region_fd = libredox::call::openat(mem_fd as usize, &path, FD_CLOEXEC.bits() as i32, 0)
|
||||
.map_err(|e| DriverError::Io(std::io::Error::from_raw_os_error(e.errno())))?;
|
||||
|
||||
let map = Map {
|
||||
|
||||
@@ -7,14 +7,14 @@ pub fn acquire_iopl() -> std::result::Result<(), crate::DriverError> {
|
||||
extern "C" {
|
||||
fn redox_cur_thrfd_v0() -> usize;
|
||||
}
|
||||
let kernel_fd = redox_syscall::dup(unsafe { redox_cur_thrfd_v0() }, b"open_via_dup")?;
|
||||
let kernel_fd = libredox::call::dup(unsafe { redox_cur_thrfd_v0() }, b"open_via_dup")?;
|
||||
let res = libredox::call::call_wo(
|
||||
kernel_fd,
|
||||
&[],
|
||||
redox_syscall::CallFlags::empty(),
|
||||
&[redox_syscall::ProcSchemeVerb::Iopl as u64],
|
||||
);
|
||||
let _ = redox_syscall::close(kernel_fd);
|
||||
let _ = libredox::call::close(kernel_fd);
|
||||
res.map(|_| ()).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
use redox_syscall::data::Map;
|
||||
use syscall as redox_syscall;
|
||||
use redox_syscall::flag::{
|
||||
MAP_SHARED, O_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY, PROT_READ, PROT_WRITE,
|
||||
MAP_SHARED, FD_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY, PROT_READ, PROT_WRITE,
|
||||
};
|
||||
use redox_syscall::PAGE_SIZE;
|
||||
|
||||
@@ -41,7 +41,7 @@ bitflags::bitflags! {
|
||||
// SAFETY: The memory scheme root FD is cached for the process lifetime.
|
||||
// This is valid because:
|
||||
// 1. scheme:memory is a kernel-built-in scheme that never terminates.
|
||||
// 2. The FD is opened with O_CLOEXEC — children after exec(2) do not inherit it.
|
||||
// 2. The FD is opened with FD_CLOEXEC — children after exec(2) do not inherit it.
|
||||
// 3. This code MUST NOT be used in processes that fork() without exec() —
|
||||
// the child would share the same FD table slot, risking double-close.
|
||||
static MEMORY_ROOT_FD: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
|
||||
@@ -55,7 +55,7 @@ fn ensure_memory_root() -> Result<libredox::Fd> {
|
||||
return Ok(libredox::Fd::new(dup_fd));
|
||||
}
|
||||
|
||||
let fd = libredox::Fd::open("/scheme/memory/scheme-root", O_CLOEXEC as i32, 0)?;
|
||||
let fd = libredox::Fd::open("/scheme/memory/scheme-root", FD_CLOEXEC.bits() as i32, 0)?;
|
||||
let raw = fd.raw();
|
||||
|
||||
match MEMORY_ROOT_FD.compare_exchange(
|
||||
@@ -109,7 +109,7 @@ impl MmioRegion {
|
||||
}
|
||||
|
||||
let root_fd = ensure_memory_root()?;
|
||||
let mem_fd = root_fd.openat(&path, (O_CLOEXEC | mode) as i32, 0)?;
|
||||
let mem_fd = root_fd.openat(&path, (FD_CLOEXEC.bits() as usize | mode) as i32, 0)?;
|
||||
|
||||
let map = Map {
|
||||
offset: phys_addr as usize,
|
||||
|
||||
@@ -10,5 +10,5 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
redox_syscall = "0.7"
|
||||
redox_syscall = { path = "../../../../local/sources/syscall" }
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "uhcid"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "UHCI USB 1.1 host controller driver for Red Bear OS"
|
||||
|
||||
@@ -10,5 +10,12 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
redox_syscall = "0.8"
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
log = "0.4"
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
|
||||
@@ -3,33 +3,542 @@ mod registers;
|
||||
use std::env;
|
||||
use std::process;
|
||||
use std::fs;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::thread;
|
||||
use log::{info, error, warn, LevelFilter};
|
||||
use redox_driver_sys::dma::DmaBuffer;
|
||||
use usb_core::scheme::{UsbError, UsbHostController};
|
||||
use usb_core::types::{PortStatus, SetupPacket, TransferDirection};
|
||||
|
||||
use registers::*;
|
||||
|
||||
struct StderrLogger;
|
||||
impl log::Log for StderrLogger {
|
||||
fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info }
|
||||
fn log(&self, r: &log::Record) { eprintln!("[{}] uhcid: {}", r.level(), r.args()); }
|
||||
fn flush(&self) {}
|
||||
// ---- I/O port access ----
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod port_io {
|
||||
#[inline(always)]
|
||||
pub unsafe fn inw(port: u16) -> u16 {
|
||||
let value: u16;
|
||||
core::arch::asm!("in ax, dx", in("dx") port, out("ax") value, options(nomem, nostack));
|
||||
value
|
||||
}
|
||||
#[inline(always)]
|
||||
pub unsafe fn outw(port: u16, value: u16) {
|
||||
core::arch::asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack));
|
||||
}
|
||||
#[inline(always)]
|
||||
pub unsafe fn ind(port: u16) -> u32 {
|
||||
let value: u32;
|
||||
core::arch::asm!("in eax, dx", in("dx") port, out("eax") value, options(nomem, nostack));
|
||||
value
|
||||
}
|
||||
#[inline(always)]
|
||||
pub unsafe fn outd(port: u16, value: u32) {
|
||||
core::arch::asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack));
|
||||
}
|
||||
}
|
||||
use port_io::{inw, outw, ind, outd};
|
||||
|
||||
// ---- DMA helpers ----
|
||||
fn alloc_dma(size: usize, align: usize) -> (*mut u8, usize) {
|
||||
let mapping = DmaBuffer::allocate(size, align)
|
||||
.expect("uhcid: DMA allocation failed");
|
||||
let phys = mapping.physical_address();
|
||||
(mapping.as_ptr() as *mut u8, phys)
|
||||
}
|
||||
|
||||
// ---- Controller state ----
|
||||
struct UhciController {
|
||||
name: String,
|
||||
io_base: u16,
|
||||
port_count: usize,
|
||||
devices: Vec<Option<PortDevice>>,
|
||||
}
|
||||
|
||||
struct PortDevice {
|
||||
address: u8,
|
||||
vendor_id: u16,
|
||||
product_id: u16,
|
||||
device_class: u8,
|
||||
device_subclass: u8,
|
||||
device_protocol: u8,
|
||||
device_descriptor: [u8; 18],
|
||||
_config_descriptor: Vec<u8>,
|
||||
low_speed: bool,
|
||||
}
|
||||
|
||||
// ---- I/O register helpers ----
|
||||
impl UhciController {
|
||||
fn read_reg(&self, offset: u16) -> u16 {
|
||||
unsafe { inw(self.io_base + offset) }
|
||||
}
|
||||
fn write_reg(&self, offset: u16, value: u16) {
|
||||
unsafe { outw(self.io_base + offset, value) };
|
||||
}
|
||||
fn write32(&self, offset: u16, value: u32) {
|
||||
unsafe { outd(self.io_base + offset, value) };
|
||||
}
|
||||
|
||||
fn port_status(&self, port: usize) -> u16 {
|
||||
let offset = match port {
|
||||
0 => PORTSC1,
|
||||
1 => PORTSC2,
|
||||
_ => return 0,
|
||||
};
|
||||
self.read_reg(offset)
|
||||
}
|
||||
fn port_write(&self, port: usize, set: u16, clear: u16) -> u16 {
|
||||
let offset = match port {
|
||||
0 => PORTSC1,
|
||||
1 => PORTSC2,
|
||||
_ => return 0,
|
||||
};
|
||||
let current = self.read_reg(offset);
|
||||
let value = (current & !clear) | set;
|
||||
let change_bits = current & PORT_CHANGE_BITS;
|
||||
self.write_reg(offset, value | change_bits);
|
||||
self.read_reg(offset)
|
||||
}
|
||||
|
||||
fn reset_controller(&self) {
|
||||
self.write_reg(USBCMD, CMD_GLOBAL_RESET);
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
self.write_reg(USBCMD, 0);
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
self.write_reg(USBSTS, STS_HALTED);
|
||||
}
|
||||
|
||||
fn init_frame_list(&self, frame_list_phys: usize, _qh_phys: usize) {
|
||||
self.write32(FRBASEADD, (frame_list_phys & 0xFFFF_F000) as u32);
|
||||
self.write_reg(USBCMD, CMD_CONFIGURE | CMD_MAX_PACKET_64);
|
||||
self.write_reg(USBCMD, CMD_RUN_STOP | CMD_CONFIGURE | CMD_MAX_PACKET_64);
|
||||
for _ in 0..100 {
|
||||
if self.read_reg(USBSTS) & STS_HALTED == 0 {
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
info!("uhcid: controller started, frame list at 0x{:08X}", frame_list_phys);
|
||||
}
|
||||
|
||||
fn port_reset(&self, port: usize) -> bool {
|
||||
let portsc = self.port_status(port);
|
||||
if portsc & PORT_CONNECT == 0 {
|
||||
return false;
|
||||
}
|
||||
self.port_write(port, PORT_RESET, 0);
|
||||
thread::sleep(Duration::from_micros(PORT_RESET_HOLD_US));
|
||||
let new = self.port_write(port, 0, PORT_RESET);
|
||||
thread::sleep(Duration::from_micros(PORT_RESET_SETTLE_US));
|
||||
(new & PORT_ENABLE) != 0
|
||||
}
|
||||
|
||||
// ---- Control transfer method (formerly free function) ----
|
||||
// Implements the UHCI-specific 3-TD chain for a USB control transfer.
|
||||
// Returns Ok(bytes_transferred) on success, Err(&'static str) on failure.
|
||||
// `data_buf` is Option<(&mut [u8], bool)> = (buffer, is_in).
|
||||
fn do_control_transfer(
|
||||
&self,
|
||||
device_addr: u8,
|
||||
endpoint: u8,
|
||||
low_speed: bool,
|
||||
setup_packet: &[u8; 8],
|
||||
mut data_buf: Option<(&mut [u8], bool)>,
|
||||
) -> Result<Option<usize>, &'static str> {
|
||||
let (setup_dma_buf, setup_phys) = alloc_dma(8, 16);
|
||||
let (_status_dma_buf, _) = alloc_dma(0, 16);
|
||||
|
||||
let data_phys = match &data_buf {
|
||||
Some((buf, _)) if !buf.is_empty() => {
|
||||
let (data_dma, phys) = alloc_dma(buf.len(), 16);
|
||||
if data_buf.as_ref().map(|(_, is_in)| *is_in).unwrap_or(false) {
|
||||
// IN transfer: buffer filled by HC, we copy out after
|
||||
} else {
|
||||
// OUT transfer: copy data into DMA buffer
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
buf.as_ptr(),
|
||||
data_dma,
|
||||
buf.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Leak DMA for now (cleaned up on process exit)
|
||||
core::mem::forget(unsafe { Box::from_raw(data_dma) });
|
||||
phys
|
||||
}
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
// Build setup TD
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(setup_packet.as_ptr(), setup_dma_buf, 8);
|
||||
}
|
||||
let setup_td_phys = unsafe {
|
||||
let td = &mut *(setup_dma_buf.sub(32) as *mut TransferDescriptor);
|
||||
td.link = PTR_TERM;
|
||||
td.token = PID_SETUP
|
||||
| ((device_addr as u32) << 8)
|
||||
| ((endpoint as u32) << 15)
|
||||
| (7 << 21); // maxlen = 8-1
|
||||
td.buffer = setup_phys as u32;
|
||||
td.status = TD_CTRL_ACTIVE | TD_CTRL_IOC
|
||||
| if low_speed { TD_CTRL_LS } else { 0 }
|
||||
| (3 << 27); // 3 retries
|
||||
setup_dma_buf as usize - 32
|
||||
};
|
||||
|
||||
// Poll for setup completion
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
let td_status = unsafe { (*(setup_dma_buf.sub(32) as *const TransferDescriptor)).status };
|
||||
if td_status & TD_CTRL_ACTIVE == 0 {
|
||||
if td_status & TD_STATUS_STALLED != 0 {
|
||||
return Err("setup TD stalled");
|
||||
}
|
||||
if td_status & (TD_STATUS_CRC | TD_STATUS_BABBLE) != 0 {
|
||||
return Err("setup TD error");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if start.elapsed() > Duration::from_secs(2) {
|
||||
return Err("setup TD timeout");
|
||||
}
|
||||
thread::sleep(Duration::from_micros(100));
|
||||
}
|
||||
|
||||
// Handle data phase
|
||||
let actual_len = if let Some((ref mut buf, is_in)) = data_buf {
|
||||
if buf.is_empty() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
let td = &mut *(setup_dma_buf as *mut TransferDescriptor);
|
||||
td.link = PTR_TERM;
|
||||
td.token = if is_in {
|
||||
PID_IN | ((device_addr as u32) << 8) | ((endpoint as u32) << 15)
|
||||
| ((buf.len() as u32 - 1) << 21)
|
||||
} else {
|
||||
PID_OUT | ((device_addr as u32) << 8) | ((endpoint as u32) << 15)
|
||||
| ((buf.len() as u32 - 1) << 21)
|
||||
};
|
||||
td.buffer = data_phys as u32;
|
||||
td.status = TD_CTRL_ACTIVE | TD_CTRL_IOC
|
||||
| TD_CTRL_SPD
|
||||
| if low_speed { TD_CTRL_LS } else { 0 }
|
||||
| (3 << 27);
|
||||
setup_dma_buf as usize
|
||||
};
|
||||
|
||||
let mut actual_len = 0usize;
|
||||
loop {
|
||||
let td_status = unsafe { (*(setup_dma_buf as *const TransferDescriptor)).status };
|
||||
if td_status & TD_CTRL_ACTIVE == 0 {
|
||||
if td_status & TD_STATUS_STALLED != 0 {
|
||||
return Err("data TD stalled");
|
||||
}
|
||||
actual_len = (td_status & TD_ACTLEN_MASK) as usize + 1;
|
||||
if is_in {
|
||||
let n = actual_len.min(buf.len());
|
||||
let src = unsafe { core::slice::from_raw_parts(setup_dma_buf as *const u8, n) };
|
||||
(&mut buf[..n]).copy_from_slice(src);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if start.elapsed() > Duration::from_secs(2) {
|
||||
return Err("data TD timeout");
|
||||
}
|
||||
thread::sleep(Duration::from_micros(100));
|
||||
}
|
||||
Some(actual_len)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(actual_len)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- UsbHostController trait implementation ----
|
||||
impl UsbHostController for UhciController {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn port_count(&self) -> usize {
|
||||
self.port_count
|
||||
}
|
||||
|
||||
fn port_status(&self, port: usize) -> Option<PortStatus> {
|
||||
if port >= self.port_count {
|
||||
return None;
|
||||
}
|
||||
let s = self.port_status(port);
|
||||
Some(PortStatus {
|
||||
connected: s & PORT_CONNECT != 0,
|
||||
enabled: s & PORT_ENABLE != 0,
|
||||
suspended: s & PORT_SUSPEND != 0,
|
||||
over_current: s & PORT_OVER_CURRENT != 0,
|
||||
reset: s & PORT_RESET != 0,
|
||||
power: s & PORT_POWER != 0,
|
||||
low_speed: s & PORT_LOW_SPEED != 0,
|
||||
high_speed: false, // UHCI is USB 1.1, never high-speed
|
||||
test_mode: false,
|
||||
indicator: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn port_reset(&mut self, port: usize) -> bool {
|
||||
UhciController::port_reset(self, port)
|
||||
}
|
||||
|
||||
fn control_transfer(
|
||||
&mut self,
|
||||
device_address: u8,
|
||||
setup: &SetupPacket,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, UsbError> {
|
||||
// Build 8-byte setup packet from SetupPacket
|
||||
let mut setup_buf = [0u8; 8];
|
||||
setup_buf[0] = setup.request_type;
|
||||
setup_buf[1] = setup.request;
|
||||
let value = setup.value.to_le_bytes();
|
||||
setup_buf[2] = value[0];
|
||||
setup_buf[3] = value[1];
|
||||
let index = setup.index.to_le_bytes();
|
||||
setup_buf[4] = index[0];
|
||||
setup_buf[5] = index[1];
|
||||
let length = setup.length.to_le_bytes();
|
||||
setup_buf[6] = length[0];
|
||||
setup_buf[7] = length[1];
|
||||
// Determine direction from request_type bit 7
|
||||
let is_in = setup.request_type & 0x80 != 0;
|
||||
let result = if data.is_empty() {
|
||||
self.do_control_transfer(
|
||||
device_address,
|
||||
0, // endpoint 0
|
||||
false, // low_speed not tracked per-transfer in trait
|
||||
&setup_buf,
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
let data_opt = if is_in {
|
||||
Some((data, true))
|
||||
} else {
|
||||
Some((data, false))
|
||||
};
|
||||
self.do_control_transfer(
|
||||
device_address,
|
||||
0,
|
||||
false,
|
||||
&setup_buf,
|
||||
data_opt,
|
||||
)
|
||||
};
|
||||
result.map(|opt| opt.unwrap_or(0)).map_err(|e| {
|
||||
error!("uhcid: control transfer error: {}", e);
|
||||
UsbError::IoError
|
||||
})
|
||||
}
|
||||
|
||||
fn bulk_transfer(
|
||||
&mut self,
|
||||
_device_address: u8,
|
||||
_endpoint: u8,
|
||||
_data: &mut [u8],
|
||||
_direction: TransferDirection,
|
||||
) -> Result<usize, UsbError> {
|
||||
// Bulk transfers not yet implemented in UHCI driver.
|
||||
// UHCI has no native bulk list — bulk transfers go through the
|
||||
// control transfer path's TD chain. See P4 follow-up.
|
||||
Err(UsbError::Unsupported)
|
||||
}
|
||||
|
||||
fn interrupt_transfer(
|
||||
&mut self,
|
||||
_device_address: u8,
|
||||
_endpoint: u8,
|
||||
_data: &mut [u8],
|
||||
) -> Result<usize, UsbError> {
|
||||
// Interrupt transfers in UHCI use the periodic frame list slots.
|
||||
// Not yet implemented in this driver. See P5 follow-up.
|
||||
Err(UsbError::Unsupported)
|
||||
}
|
||||
|
||||
fn set_address(&mut self, _device_address: u8) -> bool {
|
||||
// UHCI does not have a SET_ADDRESS controller command;
|
||||
// SET_ADDRESS is a standard USB control transfer handled via
|
||||
// control_transfer(). Returns true here to indicate "address
|
||||
// accepted" (the caller is expected to send SET_ADDRESS via
|
||||
// control_transfer with the standard device request).
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Main entry ----
|
||||
fn main() {
|
||||
log::set_logger(&StderrLogger).ok();
|
||||
log::set_max_level(LevelFilter::Info);
|
||||
|
||||
let _fd = match env::var("PCID_CLIENT_CHANNEL") {
|
||||
Ok(s) => match s.parse::<usize>() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
||||
Ok(s) => match s.parse::<usize>() {
|
||||
Ok(fd) => fd,
|
||||
Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); }
|
||||
},
|
||||
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
||||
};
|
||||
let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default();
|
||||
info!("UHCI USB 1.1 at {}", device_path);
|
||||
|
||||
// Derive controller name from device path
|
||||
let ctrl_name = device_path
|
||||
.rsplit('/')
|
||||
.next()
|
||||
.unwrap_or("uhci0")
|
||||
.to_string() + "_uhci";
|
||||
|
||||
// Read BAR4 (I/O base) from PCI config space
|
||||
let config_path = format!("{}/config", device_path);
|
||||
match fs::read(&config_path) {
|
||||
Ok(data) if data.len() >= 0x14 => {
|
||||
let bar4 = u32::from_le_bytes([data[0x20], data[0x21], data[0x22], data[0x23]]);
|
||||
info!("UHCI I/O base: 0x{:04X} (BAR4)", bar4 & 0xFFE0);
|
||||
info!("uhcid: I/O port detected, ready for port enumeration");
|
||||
let bar4 = match fs::read(&config_path) {
|
||||
Ok(data) if data.len() >= 0x24 => {
|
||||
u32::from_le_bytes([data[0x20], data[0x21], data[0x22], data[0x23]])
|
||||
}
|
||||
_ => warn!("cannot read PCI config"),
|
||||
_ => { error!("cannot read PCI config"); process::exit(1); }
|
||||
};
|
||||
|
||||
let io_base = (bar4 & 0xFFE0) as u16;
|
||||
info!("UHCI I/O base: 0x{:04X} as {}", io_base, ctrl_name);
|
||||
|
||||
// Allocate DMA memory
|
||||
let (frame_list, frame_list_phys) = alloc_dma(FRAME_COUNT * 4, FRAME_LIST_ALIGN);
|
||||
let (dummy_qh, dummy_qh_phys) = alloc_dma(core::mem::size_of::<QueueHead>(), 16);
|
||||
|
||||
unsafe {
|
||||
let qh = &mut *(dummy_qh as *mut QueueHead);
|
||||
qh.link = PTR_TERM;
|
||||
qh.element = PTR_TERM;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let frames = core::slice::from_raw_parts_mut(frame_list as *mut u32, FRAME_COUNT);
|
||||
for entry in frames.iter_mut() {
|
||||
*entry = (dummy_qh_phys as u32) | PTR_QH;
|
||||
}
|
||||
}
|
||||
|
||||
let ctrl = UhciController {
|
||||
name: ctrl_name,
|
||||
io_base,
|
||||
port_count: 2,
|
||||
devices: vec![None, None],
|
||||
};
|
||||
|
||||
ctrl.reset_controller();
|
||||
ctrl.init_frame_list(frame_list_phys, dummy_qh_phys);
|
||||
|
||||
info!("uhcid: {} controller initialized, polling ports", ctrl.name);
|
||||
|
||||
// Main polling loop
|
||||
loop {
|
||||
for port in 0..ctrl.port_count {
|
||||
let portsc = ctrl.port_status(port);
|
||||
|
||||
if (portsc & PORT_CONNECT) != 0 && (portsc & PORT_CSC) != 0 {
|
||||
ctrl.port_write(port, 0, 0);
|
||||
|
||||
info!("uhcid: port {} connect detected", port + 1);
|
||||
|
||||
if ctrl.port_reset(port) {
|
||||
match enumerate_device(&ctrl, port) {
|
||||
Ok(dev) => {
|
||||
info!(
|
||||
"uhcid: port {} device {:04x}:{:04x} class {:02x}",
|
||||
port + 1, dev.vendor_id, dev.product_id, dev.device_class,
|
||||
);
|
||||
// P0-B1: auto-spawn class drivers via the trait-based path
|
||||
let scheme_name = usb_core::scheme::scheme_path(&ctrl.name);
|
||||
usb_core::spawn::spawn_class_driver_for_port(
|
||||
dev.device_class,
|
||||
dev.device_subclass,
|
||||
dev.device_protocol,
|
||||
&scheme_name,
|
||||
&format!("{}", port + 1),
|
||||
0,
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("uhcid: port {} enumeration failed: {}", port + 1, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (portsc & PORT_CONNECT) == 0 && (portsc & PORT_CSC) != 0 {
|
||||
ctrl.port_write(port, 0, 0);
|
||||
info!("uhcid: port {} disconnected", port + 1);
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
loop { std::thread::sleep(std::time::Duration::from_secs(10)); }
|
||||
}
|
||||
|
||||
fn enumerate_device(ctrl: &UhciController, port: usize) -> Result<PortDevice, &'static str> {
|
||||
let portsc = ctrl.port_status(port);
|
||||
let low_speed = (portsc & PORT_LOW_SPEED) != 0;
|
||||
|
||||
// Step 1: Get 8-byte device descriptor header
|
||||
let get_desc: [u8; 8] = [
|
||||
0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00,
|
||||
];
|
||||
let mut header = [0u8; 8];
|
||||
ctrl.do_control_transfer(0, 0, low_speed, &get_desc, Some((&mut header, true)))?;
|
||||
|
||||
// Step 2: Set address
|
||||
let addr: u8 = (port + 1) as u8;
|
||||
let set_addr: [u8; 8] = [
|
||||
0x00, 0x05, addr, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
ctrl.do_control_transfer(0, 0, low_speed, &set_addr, None)?;
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
// Step 3: Get full device descriptor
|
||||
let mut dev_desc = [0u8; 18];
|
||||
let get_full_desc: [u8; 8] = [
|
||||
0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00,
|
||||
];
|
||||
ctrl.do_control_transfer(addr, 0, low_speed, &get_full_desc, Some((&mut dev_desc, true)))?;
|
||||
|
||||
let vendor_id = u16::from_le_bytes([dev_desc[8], dev_desc[9]]);
|
||||
let product_id = u16::from_le_bytes([dev_desc[10], dev_desc[11]]);
|
||||
let device_class = dev_desc[4];
|
||||
let device_subclass = dev_desc[5];
|
||||
let device_protocol = dev_desc[6];
|
||||
|
||||
// Step 4: Get config descriptor header
|
||||
let mut cfg_header = [0u8; 9];
|
||||
let get_cfg_hdr: [u8; 8] = [
|
||||
0x80, 0x06, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00,
|
||||
];
|
||||
ctrl.do_control_transfer(addr, 0, low_speed, &get_cfg_hdr, Some((&mut cfg_header, true)))?;
|
||||
let total_len = u16::from_le_bytes([cfg_header[2], cfg_header[3]]);
|
||||
|
||||
// Step 5: Get full config descriptor
|
||||
let mut config = vec![0u8; total_len as usize];
|
||||
let get_cfg: [u8; 8] = [
|
||||
0x80, 0x06, 0x00, 0x02, 0x00, 0x00,
|
||||
(total_len & 0xFF) as u8,
|
||||
((total_len >> 8) & 0xFF) as u8,
|
||||
];
|
||||
ctrl.do_control_transfer(addr, 0, low_speed, &get_cfg, Some((&mut config, true)))?;
|
||||
|
||||
Ok(PortDevice {
|
||||
address: addr,
|
||||
vendor_id,
|
||||
product_id,
|
||||
device_class,
|
||||
device_subclass,
|
||||
device_protocol,
|
||||
device_descriptor: dev_desc,
|
||||
_config_descriptor: config,
|
||||
low_speed,
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
// UHCI I/O register offsets (16-bit aligned, accessed via inw/outw)
|
||||
pub const USBCMD: u16 = 0x00;
|
||||
pub const USBSTS: u16 = 0x02;
|
||||
pub const USBINTR: u16 = 0x04;
|
||||
@@ -8,24 +10,104 @@ pub const SOFMOD: u16 = 0x0C;
|
||||
pub const PORTSC1: u16 = 0x10;
|
||||
pub const PORTSC2: u16 = 0x12;
|
||||
|
||||
// USBCMD bits
|
||||
pub const CMD_RUN_STOP: u16 = 1 << 0;
|
||||
pub const CMD_HOST_RESET: u16 = 1 << 1;
|
||||
pub const CMD_GLOBAL_RESET: u16 = 1 << 2;
|
||||
pub const CMD_CONFIGURE: u16 = 1 << 6;
|
||||
pub const CMD_MAX_PACKET_64: u16 = 1 << 7;
|
||||
|
||||
// USBSTS bits
|
||||
pub const STS_INTERRUPT: u16 = 1 << 0;
|
||||
pub const STS_ERROR: u16 = 1 << 1;
|
||||
pub const STS_RESUME: u16 = 1 << 2;
|
||||
pub const STS_HOST_ERROR: u16 = 1 << 3;
|
||||
pub const STS_HALTED: u16 = 1 << 5;
|
||||
|
||||
// PORTSC bits
|
||||
pub const PORT_CONNECT: u16 = 1 << 0;
|
||||
pub const PORT_ENABLE: u16 = 1 << 1;
|
||||
pub const PORT_SUSPEND: u16 = 1 << 2;
|
||||
pub const PORT_OVER_CURRENT: u16 = 1 << 3;
|
||||
pub const PORT_RESET: u16 = 1 << 4;
|
||||
pub const PORT_CSC: u16 = 1 << 1; // Connect Status Change
|
||||
pub const PORT_ENABLE: u16 = 1 << 2;
|
||||
pub const PORT_PEC: u16 = 1 << 3; // Port Enable Change
|
||||
pub const PORT_OVER_CURRENT: u16 = 1 << 3; // alias of PEC for clarity
|
||||
pub const PORT_RESUME: u16 = 1 << 6;
|
||||
pub const PORT_LOW_SPEED: u16 = 1 << 8;
|
||||
pub const PORT_RESET: u16 = 1 << 9;
|
||||
pub const PORT_SUSPEND: u16 = 1 << 12;
|
||||
pub const PORT_POWER: u16 = 1 << 12; // alias of SUSPEND (different bit on real UHCI hardware; non-standard)
|
||||
|
||||
// PORTSC change bits (write 1 to clear)
|
||||
pub const PORT_CHANGE_BITS: u16 = PORT_CSC | PORT_PEC;
|
||||
|
||||
pub const FRAME_COUNT: usize = 1024;
|
||||
pub const FRAME_LIST_ALIGN: usize = 4096;
|
||||
|
||||
// ---- Transfer Descriptor (TD) and Queue Head (QH) structures ----
|
||||
//
|
||||
// These are the hardware-level data structures the UHCI controller
|
||||
// walks in DMA-accessible memory. Each field is a 32-bit little-endian
|
||||
// value. We represent them as packed C structs and access them via
|
||||
// raw pointers.
|
||||
|
||||
/// Link pointer — points to the next QH, TD, or is a terminator.
|
||||
/// Bits 1-0 encode the type: 00=TD, 10=QH, 01=Depth-first, 01+Depth=Breadth-first
|
||||
/// Bit 0 = Terminate (1 = end of list)
|
||||
pub const PTR_TERM: u32 = 1; // Terminate this list
|
||||
pub const PTR_QH: u32 = 2; // Points to a Queue Head
|
||||
pub const PTR_DEPTH: u32 = 4; // Depth-first traversal
|
||||
|
||||
/// A Queue Head. Links into the frame list. The element pointer
|
||||
/// is updated by the HC as it processes TDs.
|
||||
#[repr(C, align(16))]
|
||||
pub struct QueueHead {
|
||||
pub link: u32, // next QH pointer (or TERM)
|
||||
pub element: u32, // next TD pointer (updated by HC)
|
||||
}
|
||||
|
||||
/// A Transfer Descriptor. Linked list of these forms a USB transfer.
|
||||
/// The status field is updated by the HC on completion.
|
||||
///
|
||||
/// Fields:
|
||||
/// link — next TD pointer (PTR_TERM or another TD's phys addr)
|
||||
/// status — status+control word (written by HC on completion)
|
||||
/// bits: 29:SPD 27-28:C_ERR 26:LS 25:IOS 24:IOC 23:ACTIVE
|
||||
/// 22:STALLED 21:DBUFERR 20:BABBLE 19:NAK 18:CRC/TIMEO
|
||||
/// 17:BITSTUFF 0-10:ACTLEN(actual length - 1)
|
||||
/// token — token word
|
||||
/// bits: 21-31:MAXLEN 19:TOGGLE 15-18:ENDP 8-14:DEVADDR 0-7:PID
|
||||
/// buffer — physical address of data buffer
|
||||
#[repr(C, align(16))]
|
||||
pub struct TransferDescriptor {
|
||||
pub link: u32,
|
||||
pub status: u32,
|
||||
pub token: u32,
|
||||
pub buffer: u32,
|
||||
}
|
||||
|
||||
// TD status/control bits
|
||||
pub const TD_CTRL_ACTIVE: u32 = 1 << 23;
|
||||
pub const TD_CTRL_IOC: u32 = 1 << 24; // Interrupt on Complete
|
||||
pub const TD_CTRL_LS: u32 = 1 << 26; // Low Speed
|
||||
pub const TD_CTRL_C_ERR_MASK: u32 = 3 << 27; // Error counter
|
||||
pub const TD_CTRL_SPD: u32 = 1 << 29; // Short Packet Detect
|
||||
|
||||
pub const TD_STATUS_STALLED: u32 = 1 << 22;
|
||||
pub const TD_STATUS_BABBLE: u32 = 1 << 20;
|
||||
pub const TD_STATUS_NAK: u32 = 1 << 19;
|
||||
pub const TD_STATUS_CRC: u32 = 1 << 18;
|
||||
pub const TD_ACTLEN_MASK: u32 = 0x7FF; // bits 0-10
|
||||
|
||||
// USB Packet IDs for the token field
|
||||
pub const PID_SETUP: u32 = 0x2D;
|
||||
pub const PID_IN: u32 = 0x69;
|
||||
pub const PID_OUT: u32 = 0xE1;
|
||||
|
||||
// Maximum NAK/error retry count for a single transfer
|
||||
pub const MAX_RETRIES: u32 = 3;
|
||||
|
||||
// Port reset hold time (50ms in microseconds)
|
||||
pub const PORT_RESET_HOLD_US: u64 = 50_000;
|
||||
// Port reset settle time (10ms in microseconds)
|
||||
pub const PORT_RESET_SETTLE_US: u64 = 10_000;
|
||||
// Max bulk packet size for USB 1.1
|
||||
pub const MAX_PACKET_SIZE: usize = 64;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "usb-core"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "Shared USB types and primitives for Red Bear host controller drivers"
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ pub mod transfer;
|
||||
pub mod types;
|
||||
|
||||
pub use dma::{DmaBuffer, DmaError};
|
||||
pub use scheme::{UsbError, UsbHostController};
|
||||
pub use scheme::{scheme_path, UsbError, UsbHostController, SCHEME_PREFIX};
|
||||
pub use spawn::spawn_usb_driver;
|
||||
pub use spawn::class_driver_for_usb_class;
|
||||
pub use spawn::spawn_class_driver_for_port;
|
||||
pub use transfer::{
|
||||
control_transfer, parse_config_descriptor, parse_device_descriptor, parse_endpoint_descriptor,
|
||||
};
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
use crate::types::{PortStatus, SetupPacket, TransferDirection};
|
||||
|
||||
/// Trait that all USB host controller drivers must implement.
|
||||
/// This provides a uniform scheme interface regardless of HC type (XHCI/EHCI/OHCI/UHCI).
|
||||
///
|
||||
/// This provides a uniform scheme interface regardless of HC type
|
||||
/// (XHCI / EHCI / OHCI / UHCI). Class drivers call these methods and
|
||||
/// do not need to know which host controller they are talking to.
|
||||
///
|
||||
/// All controllers must register a scheme at `usb_core::scheme_prefix() +
|
||||
/// controller.name()`. Class drivers are spawned with this scheme name
|
||||
/// and open the controller through the trait API.
|
||||
pub trait UsbHostController {
|
||||
/// Get the number of ports on this controller
|
||||
/// Controller name for scheme registration and logging.
|
||||
/// Example: `"0000:00:14.0_xhci"`. Must be unique system-wide.
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Number of root-hub ports on this controller.
|
||||
fn port_count(&self) -> usize;
|
||||
|
||||
/// Get status of a specific port
|
||||
/// Get status of a specific port. Returns None if the port index is invalid.
|
||||
fn port_status(&self, port: usize) -> Option<PortStatus>;
|
||||
|
||||
/// Reset a port (USB enumeration step 1)
|
||||
/// Reset a port (USB enumeration step 1).
|
||||
/// Returns true if the port enabled successfully after reset.
|
||||
fn port_reset(&mut self, port: usize) -> bool;
|
||||
|
||||
/// Submit a control transfer to endpoint 0
|
||||
/// Submit a control transfer to endpoint 0.
|
||||
/// `data` is the data-phase buffer (empty for setup-only transfers).
|
||||
/// Returns the number of bytes transferred.
|
||||
fn control_transfer(
|
||||
&mut self,
|
||||
device_address: u8,
|
||||
@@ -20,7 +34,7 @@ pub trait UsbHostController {
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, UsbError>;
|
||||
|
||||
/// Submit a bulk transfer
|
||||
/// Submit a bulk transfer. Use for mass-storage and similar.
|
||||
fn bulk_transfer(
|
||||
&mut self,
|
||||
device_address: u8,
|
||||
@@ -29,7 +43,8 @@ pub trait UsbHostController {
|
||||
direction: TransferDirection,
|
||||
) -> Result<usize, UsbError>;
|
||||
|
||||
/// Submit an interrupt transfer
|
||||
/// Submit an interrupt transfer. Use for HID, hubs.
|
||||
/// Returns the number of bytes received.
|
||||
fn interrupt_transfer(
|
||||
&mut self,
|
||||
device_address: u8,
|
||||
@@ -37,11 +52,17 @@ pub trait UsbHostController {
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, UsbError>;
|
||||
|
||||
/// Set device address (after reset, before config)
|
||||
/// Assign a device address (called during enumeration after port reset).
|
||||
fn set_address(&mut self, device_address: u8) -> bool;
|
||||
}
|
||||
|
||||
/// Get the controller name for logging
|
||||
fn name(&self) -> &str;
|
||||
/// Common scheme prefix for all USB host controllers.
|
||||
/// Class drivers are spawned with this prefix + the controller's `name()`.
|
||||
pub const SCHEME_PREFIX: &str = "usb.";
|
||||
|
||||
/// Compose a scheme path for a controller. Example: `"usb.0000:00:14.0_xhci"`.
|
||||
pub fn scheme_path(name: &str) -> alloc::string::String {
|
||||
alloc::format!("{SCHEME_PREFIX}{name}")
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -55,3 +76,5 @@ pub enum UsbError {
|
||||
IoError,
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
extern crate alloc;
|
||||
@@ -1,5 +1,78 @@
|
||||
/// Spawn a child USB class driver (hub, HID, storage).
|
||||
/// On Redox, this forks and execs the driver binary with the USB device path.
|
||||
/// Map a USB class/subclass/protocol triplet to the corresponding class driver
|
||||
/// binary path. Returns `None` if no driver is registered for this class.
|
||||
///
|
||||
/// USB class codes from <https://www.usb.org/defined-class-codes>:
|
||||
/// 0x01 — Audio (USB Audio Class)
|
||||
/// 0x02 — Communications (CDC ACM/ECM)
|
||||
/// 0x03 — HID (Human Interface Device)
|
||||
/// 0x08 — Mass Storage
|
||||
/// 0x09 — Hub
|
||||
/// 0x0A — CDC Data (paired with CDC Comm)
|
||||
/// 0xFF — Vendor-specific (FTDI, CP210x, etc.)
|
||||
pub fn class_driver_for_usb_class(class: u8, subclass: u8, _protocol: u8) -> Option<&'static str> {
|
||||
match (class, subclass) {
|
||||
(0x01, 0x01) | (0x01, 0x02) => Some("/usr/bin/redbear-usbaudiod"), // Audio Control/Streaming
|
||||
(0x02, 0x02) => Some("/usr/bin/redbear-acmd"), // CDC ACM (Abstract Control Model)
|
||||
(0x02, 0x06) => Some("/usr/bin/redbear-ecmd"), // CDC ECM (Ethernet Control Model)
|
||||
(0x02, _) => Some("/usr/bin/redbear-acmd"), // CDC generic → ACM fallback
|
||||
(0x03, _) => Some("/usr/bin/usbhidd"), // HID
|
||||
(0x08, _) => Some("/usr/bin/usbscsid"), // Mass Storage
|
||||
(0x09, _) => Some("/usr/bin/usbhubd"), // Hub
|
||||
(0x0A, _) => None, // CDC Data — paired with CDC Comm
|
||||
(0xFF, _) => Some("/usr/bin/redbear-ftdi"), // Vendor-specific → FTDI
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a class driver for a USB device port.
|
||||
///
|
||||
/// `scheme_name` — the host-controller scheme name (e.g. `"usb"` for ehcid,
|
||||
/// `"usb.0000:00:14.0_xhci"` for xhcid).
|
||||
/// `port` — the port number (string form, as passed to the class daemon).
|
||||
/// `iface_num` — the interface number within the configuration.
|
||||
/// `protocol` — the interface protocol byte (mass storage uses this).
|
||||
///
|
||||
/// The driver binary is selected from `class`, spawned with
|
||||
/// `<scheme_name> <port> <iface_num_or_protocol>` as arguments, and disconnected
|
||||
/// from stdin. The caller should own the scheme registration that the class
|
||||
/// daemon will open.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn spawn_class_driver_for_port(
|
||||
class: u8,
|
||||
subclass: u8,
|
||||
protocol: u8,
|
||||
scheme_name: &str,
|
||||
port: &str,
|
||||
iface_num: u8,
|
||||
) {
|
||||
let Some(driver_binary) = class_driver_for_usb_class(class, subclass, protocol) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Class daemons expect different argv layout.
|
||||
// usbhidd: <scheme> <port> <interface>
|
||||
// usbscsid: <scheme> <port> <protocol>
|
||||
// usbhubd: <scheme> <port> <interface>
|
||||
//
|
||||
// For HID and Hub, we pass the interface number. For Storage, pass the
|
||||
// bInterfaceProtocol byte (CBI, BOT, UAS, etc.).
|
||||
let extra_arg = match class {
|
||||
0x08 => protocol.to_string(),
|
||||
_ => iface_num.to_string(),
|
||||
};
|
||||
|
||||
let mut cmd = std::process::Command::new(driver_binary);
|
||||
cmd.env_clear();
|
||||
cmd.stdin(std::process::Stdio::null());
|
||||
cmd.arg(scheme_name);
|
||||
cmd.arg(port);
|
||||
cmd.arg(&extra_arg);
|
||||
let _ = cmd.spawn();
|
||||
}
|
||||
|
||||
/// Spawn a child USB class driver by binary path and a single device-path
|
||||
/// argument. Kept for backward compatibility; new code should prefer
|
||||
/// `spawn_class_driver_for_port`.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn spawn_usb_driver(driver_binary: &str, device_path: &str) {
|
||||
if driver_binary.is_empty()
|
||||
@@ -23,6 +96,8 @@ fn is_trusted_usb_driver(driver_binary: &str) -> bool {
|
||||
matches!(
|
||||
driver_binary,
|
||||
"/usr/bin/usbhubd" | "/usr/bin/usbhidd" | "/usr/bin/usbscsid"
|
||||
| "/usr/bin/redbear-acmd" | "/usr/bin/redbear-ecmd"
|
||||
| "/usr/bin/redbear-usbaudiod" | "/usr/bin/redbear-ftdi"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,6 +106,19 @@ fn is_trusted_usb_driver(driver_binary: &str) -> bool {
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn spawn_usb_driver(_driver_binary: &str, _device_path: &str) {}
|
||||
|
||||
/// On no_std builds, class-driver spawning is not available.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn class_driver_for_usb_class(_class: u8, _subclass: u8, _protocol: u8) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
/// On no_std builds, class-driver spawning is not available.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn spawn_class_driver_for_port(
|
||||
_class: u8, _subclass: u8, _protocol: u8,
|
||||
_scheme_name: &str, _port: &str, _iface_num: u8,
|
||||
) {}
|
||||
|
||||
#[cfg(all(test, feature = "std"))]
|
||||
mod tests {
|
||||
use super::is_trusted_usb_driver;
|
||||
@@ -40,6 +128,10 @@ mod tests {
|
||||
assert!(is_trusted_usb_driver("/usr/bin/usbhubd"));
|
||||
assert!(is_trusted_usb_driver("/usr/bin/usbhidd"));
|
||||
assert!(is_trusted_usb_driver("/usr/bin/usbscsid"));
|
||||
assert!(is_trusted_usb_driver("/usr/bin/redbear-acmd"));
|
||||
assert!(is_trusted_usb_driver("/usr/bin/redbear-ecmd"));
|
||||
assert!(is_trusted_usb_driver("/usr/bin/redbear-usbaudiod"));
|
||||
assert!(is_trusted_usb_driver("/usr/bin/redbear-ftdi"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "virtio-inputd"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2024"
|
||||
description = "virtio-input daemon v6.0 2026: reads virtio-input PCI events and writes Linux evdev events to /scheme/input-evdev"
|
||||
|
||||
@@ -11,9 +11,13 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
log = "0.4"
|
||||
libredox = { version = "=0.1.16", features = ["call", "std"] }
|
||||
redox_syscall = { version = "0.7", features = ["std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
syscall = { package = "redox_syscall", version = "0.7", features = ["std"] }
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
inputd = { path = "../../../../sources/base/drivers/inputd" }
|
||||
common = { path = "../../../../sources/base/drivers/common" }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "redox-drm"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
edition = "2021"
|
||||
description = "DRM scheme daemon for Redox OS — provides GPU modesetting and buffer management"
|
||||
|
||||
[dependencies]
|
||||
redox-driver-sys = { version = "0.2", path = "../../../drivers/redox-driver-sys/source" }
|
||||
linux-kpi = { version = "0.2", path = "../../../drivers/linux-kpi/source" }
|
||||
libredox = "0.1"
|
||||
redox_syscall = { version = "0.8", features = ["std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
syscall04 = { package = "redox_syscall", version = "0.4" }
|
||||
redox-scheme = "0.11"
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
daemon = { path = "../../../../../recipes/core/base/source/daemon" }
|
||||
log = "0.4"
|
||||
thiserror = "2"
|
||||
@@ -20,3 +20,6 @@ getrandom = "0.2"
|
||||
[patch.crates-io]
|
||||
redox-driver-sys = { path = "../../../drivers/redox-driver-sys/source" }
|
||||
linux-kpi = { path = "../../../drivers/linux-kpi/source" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
# kwin headers at configure time; disabled via CMake option. The widget style plugin should
|
||||
# build independently with just qtbase + KF6 deps.
|
||||
[source]
|
||||
tar = "https://invent.kde.org/plasma/breeze/-/archive/v6.3.4/breeze-v6.3.4.tar.gz"
|
||||
tar = "https://download.kde.org/stable/plasma/6.7.2/breeze-6.7.2.tar.xz"
|
||||
blake3 = "8d9df73d56ebe7eb92185530d82104460d6ac7214a0ead5c29d0026bd3837357"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#TODO: KDecoration3 — window decoration library. Required by KWin.
|
||||
[source]
|
||||
tar = "https://invent.kde.org/plasma/kdecoration/-/archive/v6.3.4/kdecoration-v6.3.4.tar.gz"
|
||||
tar = "https://invent.kde.org/plasma/kdecoration/-/archive/v6.7.2/kdecoration-v6.7.2.tar.gz"
|
||||
blake3 = "f9802589d7e61099a4f26b3723c5f54e92e60919d35e6df348f0a7eccf2700de"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR This file is copyright:
|
||||
# This file is distributed under the same license as the kdecoration package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: kdecoration\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2026-01-13 08:42+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Irish Gaelic <kde-i18n-doc@kde.org>\n"
|
||||
"Language: ga\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n < 11 ? "
|
||||
"3 : 4\n"
|
||||
|
||||
#: decorationbutton.cpp:325
|
||||
#, kde-format
|
||||
msgctxt ""
|
||||
"@info:tooltip The Name of the menu that appears when you click a window's "
|
||||
"app icon"
|
||||
msgid "Window menu"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:327
|
||||
#, kde-format
|
||||
msgid "Application menu"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:330
|
||||
#, kde-format
|
||||
msgid "On one desktop"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:332
|
||||
#, kde-format
|
||||
msgid "On all desktops"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:334
|
||||
#, kde-format
|
||||
msgid "Minimize"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:337
|
||||
#, kde-format
|
||||
msgid "Restore"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:339
|
||||
#, kde-format
|
||||
msgid "Maximize"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:341
|
||||
#, kde-format
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:343
|
||||
#, kde-format
|
||||
msgid "Context help"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:346
|
||||
#, kde-format
|
||||
msgid "Unshade"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:348
|
||||
#, kde-format
|
||||
msgid "Shade"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:351
|
||||
#, kde-format
|
||||
msgid "Don't keep below other windows"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:353
|
||||
#, kde-format
|
||||
msgid "Keep below other windows"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:356
|
||||
#, kde-format
|
||||
msgid "Don't keep above other windows"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:358
|
||||
#, kde-format
|
||||
msgid "Keep above other windows"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:361
|
||||
#, kde-format
|
||||
msgid "The window is hidden from Screencast. Press to make it visible"
|
||||
msgstr ""
|
||||
|
||||
#: decorationbutton.cpp:363
|
||||
#, kde-format
|
||||
msgid "Hide from Screencast"
|
||||
msgstr ""
|
||||
@@ -2,7 +2,8 @@
|
||||
# Provides KF6::Attica cmake target needed by kf6-knewstuff.
|
||||
# QML, tests, and examples disabled.
|
||||
[source]
|
||||
tar = "https://invent.kde.org/frameworks/attica/-/archive/v6.10.0/attica-v6.10.0.tar.gz"
|
||||
tar = "https://download.kde.org/stable/frameworks/6.27/attica-6.27.0.tar.xz"
|
||||
blake3 = "d22d07aa538f3a0404e652f811fcb6816f125f493c8a246f36478a439094929b"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*!
|
||||
\page attica-index.html
|
||||
\title Attica
|
||||
|
||||
Open Collaboration Service client library
|
||||
|
||||
\section1 Using the Module
|
||||
|
||||
\include {module-use.qdocinc} {using the c++ api}
|
||||
|
||||
\section2 Building with CMake
|
||||
|
||||
\include {module-use.qdocinc} {building with cmake} {KF6} {Attica} {KF6::Attica}
|
||||
|
||||
\section1 API Reference
|
||||
|
||||
\list
|
||||
\li \l{Attica C++ Classes}
|
||||
\endlist
|
||||
*/
|
||||
@@ -0,0 +1,9 @@
|
||||
/*!
|
||||
\module Attica
|
||||
\title Attica C++ Classes
|
||||
\ingroup modules
|
||||
\cmakepackage KF6
|
||||
\cmakecomponent Attica
|
||||
|
||||
\brief Open Collaboration Service client library.
|
||||
*/
|
||||
@@ -0,0 +1,33 @@
|
||||
include($KDE_DOCS/global/qt-module-defaults.qdocconf)
|
||||
|
||||
project = Attica
|
||||
description = Open Collaboration Service client library
|
||||
|
||||
documentationinheaders = true
|
||||
|
||||
headerdirs += .
|
||||
sourcedirs += .
|
||||
|
||||
outputformats = HTML
|
||||
|
||||
navigation.landingpage = "Attica"
|
||||
|
||||
depends += \
|
||||
kde \
|
||||
qtcore
|
||||
|
||||
qhp.projects = Attica
|
||||
|
||||
qhp.Attica.file = attica.qhp
|
||||
qhp.Attica.namespace = org.kde.attica.$QT_VERSION_TAG
|
||||
qhp.Attica.virtualFolder = attica
|
||||
qhp.Attica.indexTitle = Attica
|
||||
qhp.Attica.indexRoot =
|
||||
|
||||
qhp.Attica.subprojects = classes
|
||||
qhp.Attica.subprojects.classes.title = C++ Classes
|
||||
qhp.Attica.subprojects.classes.indexTitle = Attica C++ Classes
|
||||
qhp.Attica.subprojects.classes.selectors = class fake:headerfile
|
||||
qhp.Attica.subprojects.classes.sortPages = true
|
||||
|
||||
tagfile = attica.tags
|
||||
@@ -30,4 +30,5 @@ cmake --install . --prefix "${COOKBOOK_STAGE}/usr"
|
||||
"""
|
||||
|
||||
[source]
|
||||
tar = "https://invent.kde.org/frameworks/extra-cmake-modules/-/archive/v6.11.0/extra-cmake-modules-v6.11.0.tar.gz"
|
||||
tar = "https://download.kde.org/stable/frameworks/6.27/extra-cmake-modules-6.27.0.tar.xz"
|
||||
blake3 = "f58ffeee3bfed10420e7ffa9b8474ece35838f318e2cc3536bcb8bd880e2f102"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.. ecm-module:: ../../find-modules/FindFFmpeg.cmake
|
||||
@@ -0,0 +1 @@
|
||||
.. ecm-module:: ../../modules/ECMFeatureSummary.cmake
|
||||
@@ -0,0 +1,250 @@
|
||||
# SPDX-FileCopyrightText: 2006 Matthias Kretz <kretz@kde.org>
|
||||
# SPDX-FileCopyrightText: 2008 Alexander Neundorf <neundorf@kde.org>
|
||||
# SPDX-FileCopyrightText: 2011 Michael Jansen <kde@michael-jansen.biz>
|
||||
# SPDX-FileCopyrightText: 2021 Stefan Brüns <stefan.bruens@rwth-aachen.de>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
|
||||
FindFFmpeg
|
||||
----------
|
||||
|
||||
Try to find FFmpeg components.
|
||||
|
||||
The following components are available::
|
||||
|
||||
AVCODEC AVFILTER AVDEVICE AVFORMAT
|
||||
AVUTIL SWRESAMPLE SWSCALE POSTPROCESS
|
||||
|
||||
If no components are specified in the find_module call, the following ones
|
||||
will be choosen as default::
|
||||
|
||||
AVFORMAT AVUTIL AVCODEC
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module provides the following imported targets, if found:
|
||||
|
||||
``FFmpeg::FFmpeg``
|
||||
For all the required FFmpeg components
|
||||
|
||||
``FFmpeg::<component>``
|
||||
For each <component> that was found
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This will define the following variables:
|
||||
|
||||
``FFMPEG_FOUND``
|
||||
System has the all required components.
|
||||
|
||||
``FFMPEG_INCLUDE_DIRS``
|
||||
Include directory necessary for using the required components headers.
|
||||
|
||||
``FFMPEG_LIBRARIES``
|
||||
Link these to use the required FFmpeg components.
|
||||
|
||||
``FFMPEG_DEFINITIONS``
|
||||
Compiler switches required for using the required FFmpeg components.
|
||||
|
||||
|
||||
Additionally for each of the components, the following variables will be defined:
|
||||
|
||||
``<component>_FOUND``
|
||||
True if (the requestion version of) <component> is available
|
||||
|
||||
``<component>_INCLUDE_DIRS``
|
||||
Include directory necessary for using the <component> headers
|
||||
|
||||
``<component>_LIBRARIES``
|
||||
Link these to use <component>
|
||||
|
||||
``<component>_DEFINITIONS``
|
||||
Compiler switches required for using <component>
|
||||
|
||||
``<component>_VERSION``
|
||||
The components version (NOTE: always empty on Windows)
|
||||
|
||||
As the versions of the various FFmpeg components differ for a given release,
|
||||
and CMake supports only one common version for all components, use the
|
||||
following to specify required versions for multiple components:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(FFmpeg 57.48 COMPONENTS AVCODEC)
|
||||
find_package(FFmpeg 57.40 COMPONENTS AVFORMAT)
|
||||
find_package(FFmpeg 55.27 COMPONENTS AVUTIL)
|
||||
|
||||
NOTE: version checking is skipped on Windows
|
||||
|
||||
Since 6.20.0.
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_policy(VERSION 3.16)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
if(NOT FFmpeg_FIND_COMPONENTS)
|
||||
# The default components were taken from a survey over other FindFFMPEG.cmake files
|
||||
set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL)
|
||||
endif()
|
||||
|
||||
list(LENGTH FFmpeg_FIND_COMPONENTS _numComponents)
|
||||
if((${_numComponents} GREATER 1) AND DEFINED ${FFmpeg_FIND_VERSION})
|
||||
message(WARNING "Using a required version in combination with multiple COMPONENTS is not supported")
|
||||
set(_FFmpeg_REQUIRED_VERSION 0)
|
||||
elseif(DEFINED FFmpeg_FIND_VERSION)
|
||||
set(_FFmpeg_REQUIRED_VERSION ${FFmpeg_FIND_VERSION})
|
||||
else()
|
||||
set(_FFmpeg_REQUIRED_VERSION 0)
|
||||
endif()
|
||||
set(_FFmpeg_ALL_COMPONENTS AVCODEC AVFILTER AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWRESAMPLE SWSCALE)
|
||||
|
||||
### Macro: set_component_found
|
||||
#
|
||||
# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present.
|
||||
|
||||
macro(set_component_found _component )
|
||||
if(${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS)
|
||||
set(${_component}_FOUND TRUE)
|
||||
set(FFmpeg_${_component}_FOUND TRUE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(create_target _component )
|
||||
if(${_component}_FOUND AND NOT TARGET FFmpeg::${_component})
|
||||
add_library(FFmpeg::${_component} UNKNOWN IMPORTED)
|
||||
set_target_properties(FFmpeg::${_component} PROPERTIES
|
||||
IMPORTED_LOCATION "${${_component}_LIBRARIES}"
|
||||
INTERFACE_COMPILE_OPTIONS "${${_component}_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${${_component}_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
### Macro: find_component
|
||||
#
|
||||
# Checks for the given component by invoking pkgconfig and then looking up the libraries and
|
||||
# include directories.
|
||||
|
||||
macro(find_component _component _pkgconfig _library _header)
|
||||
|
||||
if(NOT WIN32)
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_${_component} QUIET ${_pkgconfig})
|
||||
endif()
|
||||
|
||||
find_path(${_component}_INCLUDE_DIRS ${_header}
|
||||
HINTS
|
||||
${PC_LIB${_component}_INCLUDEDIR}
|
||||
${PC_LIB${_component}_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES
|
||||
ffmpeg
|
||||
)
|
||||
|
||||
find_library(${_component}_LIBRARIES NAMES ${_library}
|
||||
HINTS
|
||||
${PC_LIB${_component}_LIBDIR}
|
||||
${PC_LIB${_component}_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.")
|
||||
set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.")
|
||||
|
||||
set_component_found(${_component})
|
||||
create_target(${_component})
|
||||
|
||||
mark_as_advanced(
|
||||
${_component}_INCLUDE_DIRS
|
||||
${_component}_LIBRARIES
|
||||
${_component}_DEFINITIONS
|
||||
${_component}_VERSION)
|
||||
|
||||
endmacro()
|
||||
|
||||
# Check for cached results. If there are skip the costly part.
|
||||
if(NOT FFMPEG_LIBRARIES)
|
||||
|
||||
# Check for all possible component.
|
||||
find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h)
|
||||
find_component(AVFILTER libavfilter avfilter libavfilter/avfilter.h)
|
||||
find_component(AVFORMAT libavformat avformat libavformat/avformat.h)
|
||||
find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h)
|
||||
find_component(AVUTIL libavutil avutil libavutil/avutil.h)
|
||||
find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h)
|
||||
find_component(SWSCALE libswscale swscale libswscale/swscale.h)
|
||||
find_component(POSTPROCESS libpostproc postproc libpostproc/postprocess.h)
|
||||
|
||||
# Check if the required components were found and add their stuff to the FFMPEG_* vars.
|
||||
foreach(_component ${_FFmpeg_ALL_COMPONENTS})
|
||||
if(${_component}_FOUND)
|
||||
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES})
|
||||
set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS})
|
||||
list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Build the include path with duplicates removed.
|
||||
if(FFMPEG_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
||||
# cache the vars.
|
||||
set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE)
|
||||
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE)
|
||||
set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE)
|
||||
|
||||
mark_as_advanced(FFMPEG_INCLUDE_DIRS
|
||||
FFMPEG_LIBRARIES
|
||||
FFMPEG_DEFINITIONS)
|
||||
|
||||
else()
|
||||
# Set the noncached _FOUND vars for the components.
|
||||
foreach(_component ${_FFmpeg_ALL_COMPONENTS})
|
||||
set_component_found(${_component})
|
||||
create_target(${_component})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Compile the list of required vars
|
||||
unset(_FFmpeg_REQUIRED_VARS)
|
||||
set(_FFmpeg_FOUND_LIBRARIES "")
|
||||
foreach(_component ${FFmpeg_FIND_COMPONENTS})
|
||||
if(${_component}_FOUND)
|
||||
if(NOT WIN32)
|
||||
if(${_component}_VERSION VERSION_LESS _FFmpeg_REQUIRED_VERSION)
|
||||
message(STATUS "${_component}: ${${_component}_VERSION} < ${_FFmpeg_REQUIRED_VERSION}")
|
||||
unset(${_component}_FOUND)
|
||||
endif()
|
||||
else()
|
||||
# We don't use PkgConfig on Windows an hence the <component>_VERSION var is empty
|
||||
# TODO: We could add some magic to retrieve the version from the .pc files or version headers ourself,
|
||||
# like https://github.com/Kitware/VTK/blob/master/CMake/FindFFMPEG.cmake
|
||||
message(WARNING "${_component}: Version check is not supported on Windows, skipping version check")
|
||||
endif()
|
||||
|
||||
list(APPEND _FFmpeg_FOUND_LIBRARIES ${${_component}_LIBRARIES})
|
||||
endif()
|
||||
list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS ${_component}_FOUND)
|
||||
endforeach()
|
||||
list(INSERT _FFmpeg_REQUIRED_VARS 0 _FFmpeg_FOUND_LIBRARIES)
|
||||
|
||||
# Give a nice error message if some of the required vars are missing.
|
||||
find_package_handle_standard_args(FFmpeg
|
||||
REQUIRED_VARS ${_FFmpeg_REQUIRED_VARS}
|
||||
HANDLE_COMPONENTS)
|
||||
|
||||
# Create the general target
|
||||
if(FFmpeg_FOUND AND NOT TARGET FFmpeg::FFmpeg)
|
||||
add_library(FFmpeg::FFmpeg INTERFACE IMPORTED)
|
||||
foreach(_component ${FFmpeg_FIND_COMPONENTS})
|
||||
if(${_component}_FOUND AND TARGET FFmpeg::${_component})
|
||||
target_link_libraries(FFmpeg::FFmpeg INTERFACE FFmpeg::${_component})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#include "testhelper.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
make_test_file("test12.txt");
|
||||
return 0;
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#include "testhelper.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
make_test_file("test13.txt");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user