Files
RedBear-OS/local/docs/INPUT-STACK-LINUX-ALIGNMENT-PLAN.md
T
vasilito 78aca1bb45 docs: Linux-aligned input-stack consolidation plan
Cross-referenced the console-input path against Linux/CachyOS
(local/reference/linux-7.1/drivers/{input,tty}/). Linux applies the
keymap once (vt/keyboard.c) and runs one line discipline (n_tty.c)
shared by the console tty and ptys; RedBear duplicates the keymap
(inputd + fbcond) and CR/LF (fbcond + ptyd) and runs two disciplines
bridged by getty — which is where typed characters are lost (echoed but
not executed). Plan: inputd owns the keymap and emits the full byte
stream (incl. escape sequences); fbcond becomes display-only passthrough
(no keymap, no CR/LF, no cooking); ptyd is the sole line discipline.
Staged, each stage independently bootable; validation needs the build
green (acpi-rs currently blocks it).
2026-07-24 11:04:01 +09:00

106 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Input Stack — Linux-Aligned Consolidation Plan
**Status:** Design (implementation staged below). Goal: make the console-input path match the
Linux/CachyOS model — one keymap owner, one line discipline shared by console and pty — instead of
the current duplicated, two-discipline chain that loses typed characters (echoed but not executed).
CachyOS's input/tty is stock upstream Linux (the `local/reference/cachyos/` tree is packaging +
kernel *performance* patches; the input subsystem is unmodified). So the reference model below is
`local/reference/linux-7.1/drivers/{input,tty}/`.
## The Linux model (single owner per stage)
| Stage | Linux file | Property |
|---|---|---|
| Raw input pipeline | `drivers/input/input.c` (`input_pass_values`) | one driver→core→handler pipeline |
| **Keymap → bytes** | `drivers/tty/vt/keyboard.c` (`put_queue`, `to_utf8`, `applkey`) | **applied ONCE**; emits the full byte stream incl. escape sequences for special keys |
| **Line discipline** | `drivers/tty/n_tty.c` (`read_buf` cooked, `echo_buf`, ICRNL, ISIG) | **ONE module**: canonical, echo, cooked buffer, CR/LF, signal keys |
| Console vs pty | `drivers/tty/pty.c` (`tty_port`, shared ldisc) | pty is a thin bridge that **reuses `n_tty`** — no second discipline |
Key invariants Linux guarantees and we currently violate:
1. The keymap is applied **exactly once**.
2. CR/LF (`ICRNL`/`ONLCR`) is one `termios` policy in **one** place.
3. Canonical/echo/cooked lives in **one** buffer pair, and the **console tty and ptys share the
same line discipline**. "Echoed but not delivered" is structurally impossible because the reader
drains the very buffer the discipline cooked into.
## Current RedBear path (what to fix)
```
ps2d ─▶ inputd ─▶ fbcond ─▶ getty(bridge) ─▶ ptyd ─▶ pty-slave ─▶ shell
keymap keymap+ byte pump FULL line discipline
(get_char) CR→LF (canonical/echo/cooked/CR→LF/signals)
+ own input
queue
```
Confirmed wiring (`local/sources/userutils/src/bin/getty.rs`):
- getty opens `/scheme/fbcon/{vt}` (fbcond) **and** `/scheme/pty/ptmx` (ptyd), then byte-pumps
console↔pty; login/shell stdin = pty **slave**.
Duplication:
- **Keymap twice**: inputd `active_keymap.get_char` (`drivers/inputd/src/main.rs:452`) **and** fbcond
`self.keymap.lookup` (`drivers/graphics/fbcond/src/text.rs:140`).
- **CR→LF twice**: fbcond (`text.rs:164`) **and** ptyd (`ICRNL`, `drivers/ptyd/src/pty.rs:76`).
- **Two disciplines**: fbcond has its own input queue/cooking; ptyd has the real one. The bridge
between them is where typed characters are lost while bare `\n` survives (the reported bug:
`tlc` echoes but never executes; manual Enters produce empty prompts).
## Target (Linux-aligned)
```
ps2d ─▶ inputd ─▶ fbcond(display only) ─▶ getty(dumb pipe) ─▶ ptyd ─▶ slave ─▶ shell
keymap→ passthrough of the THE single line
BYTES already-decoded byte discipline (n_tty)
(incl. stream; renders output;
escapes) NO keymap, NO CR/LF,
NO input cooking
```
Rules:
1. **inputd owns the keymap** and emits the **full byte stream** for each key, including escape
sequences for special keys (arrows, F-keys, Home/End) — the `keyboard.c`/`applkey` role. Today it
only sets a single `character`; extend it to emit bytes.
2. **fbcond is display-only + a raw byte passthrough.** Remove `self.keymap.lookup` and the
`text.rs:164` CR→LF. It forwards inputd's decoded bytes verbatim into the console read stream and
renders output. It performs **no** cooking, echo, or CR/LF.
3. **ptyd is the sole line discipline** (the `n_tty`). Canonical/echo/cooked/CR→LF/signals stay here
and **only** here. This is already implemented (`pty.rs:41-249`); it just becomes the single owner.
4. **getty stays a dumb byte pipe** (no processing) — matches a terminal emulator feeding a pty.
Result: keymap once (inputd), CR/LF once (ptyd `termios`), one cooked buffer (ptyd) that the shell
drains directly. Structurally identical to Linux → the "echoed but not delivered" class is gone.
## Staged implementation (each stage builds + boots independently; validate on an idle host)
1. **inputd: emit full byte sequences.** Add a keymap `get_bytes(scancode, mods) -> &[u8]` that
returns the char OR the escape sequence for special keys (model on `keyboard.c`). Deliver bytes on
the consumer stream (or keep events but carry the full byte payload). Keep behavior identical for
plain chars first; add escapes next.
2. **fbcond: drop keymap + CR/LF, become passthrough.** Remove `self.keymap.lookup` and the CR→LF at
`text.rs:164`; forward inputd's bytes verbatim. fbcond keeps only rendering + scrollback + the
serial mirror (which should also stop translating CR/LF — leave that to ptyd).
3. **Verify one discipline.** Confirm ptyd is the only place doing canonical/echo/cooked/CR→LF; delete
fbcond's residual input-cooking paths (`push_input_bytes` CR→LF at `pty.rs`-style sites in fbcond).
4. **Regression pass.** Console login on framebuffer AND serial: type a command, confirm execution;
test backspace, Ctrl-C (ISIG), arrows (escape sequences), and the serial mirror.
Immediate stopgap (independent of this refactor, already partly done): the brush minimal backend
reads raw and accumulates a persistent line buffer; extend it to treat **both** `\r` and `\n` as
submit so the shell is robust even before the stack is consolidated. This is a shell-side band-aid,
not the fix — the fix is stages 13.
## Validation
- Framebuffer screendump (QMP) is ground truth. **One boot proves nothing** (input races); compare
across boots on an idle host — external load (opencode) contaminates timing.
- Success: type `echo RB=$((21*2))=OK` on the framebuffer console → `RB=42=OK` executes; backspace,
Ctrl-C, and arrows behave; serial-console login works the same way.
## Risk / rollback
- Boot-critical path; each stage is independently bootable and revertable (one component per stage).
- Do not land any stage validated only under external load.
- Build is currently blocked on `acpi-rs` (`aml/mod.rs:258` `connect_op_regions` undefined) — none of
this can be verified until that compiles.