lg-gram-16z90tp: Phase 5 + Phase 4.4 code-complete (acpid events + multitouch)
Submodule bump (base f315a9be):
- Vendored acpi-rs fork with real Opcode::Notify (upstream panicked).
- acpid Phase 5: GPE/PM1 dispatch, EC query loop, lid/button/fan
scheme surfaces, AML notification queue, SCI IRQ subscription.
- i2c-hidd Phase 4.4: HID 1.11 descriptor parser + decoder, multi-
touch digitizer forwarding (absolute pointer + two-finger scroll).
Parent changes:
- redbear-upower: 250 ms notification drain arm polls
/scheme/acpi/notifications and emits Changed D-Bus signal on power
state transitions. The existing 30 s poll stays as a safety net.
- LG-GRAM-16Z90TP-COMPATIBILITY-PLAN.md: Phase 4.4 + Phase 5
(P5.0-P5.5) marked code-complete with status preambles, stub sweep
notes, file mappings, and runtime-gate-still-open disclaimers.
Runtime validation (Gate 5: AC event latency, lid, power button, fan)
remains pending Phase 1 bare-metal boot.
This commit is contained in:
@@ -202,10 +202,11 @@ whole desktop path (Qt6/KWin) on this machine.**
|
||||
|
||||
### PHASE 4 — Touchpad: I2C-HID chain bring-up (M)
|
||||
|
||||
> **STATUS: 4.1–4.3 COMPLETE (2026-07-20).** The entire I2C transfer chain was
|
||||
> **STATUS: 4.1–4.4 COMPLETE (2026-07-21).** The entire I2C transfer chain was
|
||||
> stubbed end-to-end and has been replaced with real implementations (see
|
||||
> "stub sweep" below). 4.4 (multitouch report parsing) and 4.5 (host
|
||||
> validation) remain open and require the running system.
|
||||
> "stub sweep" below). 4.4 (multitouch report parsing) landed on 2026-07-21.
|
||||
> Only 4.5 (host runtime validation) remains open and requires the running
|
||||
> system.
|
||||
|
||||
**Stub sweep (all found during this phase, all replaced with real code):**
|
||||
|
||||
@@ -279,7 +280,7 @@ pinning (pre-existing, separate task), CallerCtx authZ on i2c schemes
|
||||
| 4.1 | Identify actual ACPI `_HID` of the two LPSS I2C controllers on the host | — | ✅ DONE — **no `_HID` exists on this platform** (host-verified via sysfs: PCI function 00:15.0/00:15.1 ↔ `\_SB_.PC00.I2C0/I2C1`, DesignWare adapter spawned by intel-lpss MFD with `bxt_i2c_info`, 133 MHz). PCI-ID discovery implemented instead, per Linux's model |
|
||||
| 4.2 | Controller bring-up: BAR/IRQ, DesignWare init sequence, bus speed | `drivers/i2c/designware/`, `intel-lpss-i2cd` | ✅ DONE (polling engine; IRQ-driven completion is a documented IRQ-plan follow-up, not a stub) |
|
||||
| 4.3 | `i2c-hidd`: probe 04CA:00CC (PNP0C50), `_DSM` HID descriptor, report descriptor fetch | `i2c-hidd`, `i2cd`, endpoint chain | ✅ CODE-READY — protocol chain verified end-to-end in code (i2c-hidd's bare `I2cTransferRequest` → i2cd routing → provider endpoint). Runtime proof pending host boot (Phase 1) |
|
||||
| 4.4 | Multitouch digitizer report parsing (ABS_MT_* slots, tracking IDs) + forwarding through inputd → evdevd gesture module | `i2c-hidd/hid.rs`, `evdevd/gesture.rs` | M — OPEN |
|
||||
| 4.4 | Multitouch digitizer report parsing (ABS_MT_* slots, tracking IDs) + forwarding through inputd → evdevd gesture module | `i2c-hidd/{hid,input,report_desc}.rs` | ✅ CODE-COMPLETE (2026-07-21) — HID 1.11 §6.2.2 descriptor parser + decoder in new `report_desc.rs` (digitizer contacts, generic-desktop X/Y/wheel, buttons, keyboard page fallback, sign-extended fields, usage-min/max expansion, global-state stack). Integrated via `forward_layout_report`: first touching contact → absolute `MouseEvent`; two simultaneous contacts → vertical `ScrollEvent` (two-finger scroll); buttons → `ButtonEvent`. 3 unit tests pass on host. Runtime palm rejection / gesture tuning stays open with 4.5 (needs boot) |
|
||||
| 4.5 | Host validation: pointer, click, two-finger scroll; palm-rejection tuning | — | S — OPEN (needs boot) |
|
||||
|
||||
**Adapter naming chain (documented for future consumers):**
|
||||
@@ -290,9 +291,10 @@ provider daemon (intel-lpss-i2cd registered alias `PC00.I2C0` discovered by
|
||||
matching `FixedMemory32 == BAR0`) → engine executes on the controller's BAR0.
|
||||
|
||||
**Gate 4 (partial):** base fork cooks for `x86_64-unknown-redox` with the new
|
||||
chain; all unit tests pass (dw-i2c 3, intel-lpss-i2cd 3, others green);
|
||||
binaries staged at verified paths. Full gate (touchpad usable on host) open
|
||||
until Phase 1 boot + 4.4.
|
||||
chain; all unit tests pass (dw-i2c 3, intel-lpss-i2cd 3, i2c-hidd 3, others
|
||||
green); binaries staged at verified paths. Full gate (touchpad usable on host)
|
||||
is now blocked only on Phase 1 boot — 4.4 (multitouch parsing) landed
|
||||
2026-07-21.
|
||||
|
||||
---
|
||||
|
||||
@@ -300,16 +302,83 @@ until Phase 1 boot + 4.4.
|
||||
|
||||
This phase removes the biggest "not a real laptop" gap: event-driven power state.
|
||||
|
||||
| # | Work item | Files | Effort |
|
||||
|---|---|---|---|
|
||||
| 5.1 | **GPE dispatch infrastructure in acpid**: SCI via `/scheme/irq` (ACPI `sci_int` from FADT), GPE status/enable block access, per-GPE handler registry | `local/sources/base/drivers/acpid/src/{main,acpi}.rs` | L |
|
||||
| 5.2 | EC event loop: `SCI_EVT` poll/IRQ → `QR_EC` query → dispatch `_Qxx` AML methods; wire battery/AC change notifications (`\_SB.BAT0` notify 0x80) | `acpid/ec.rs`, new `acpid/gpe.rs` | M |
|
||||
| 5.3 | Lid switch (PNP0C0D `_LID`) + power/sleep fixed events (PM1_STS PWRBTN/SLPBTN) → scheme surfaces + kstop events | acpid + sessiond consumers | M |
|
||||
| 5.4 | Event-driven `redbear-upower` (replace polling with notify-driven refresh) | `redbear-upower` | S |
|
||||
| 5.5 | Fan: real PNP0C0B/EC fan control replacing thermald soft model | `thermald/fan.rs`, acpid EC | M |
|
||||
> **STATUS: CODE-COMPLETE (2026-07-21).** All five sub-items (P5.1–P5.5)
|
||||
> landed plus a prerequisite P5.0; affected crates compile for
|
||||
> `x86_64-unknown-redox`; `cook base` and `cook redbear-upower` succeed;
|
||||
> i2c-hidd (3) + dw-i2c (5) + intel-lpss-i2cd (1) tests pass on host.
|
||||
> acpid host-side tests can't link (pre-existing libredox limitation —
|
||||
> tests must run via `redoxer`). Runtime Gate 5 remains open pending
|
||||
> Phase 1 bare-metal boot.
|
||||
|
||||
**Gate 5:** Unplug/replug AC → instant battery/AC event; close/open lid → event
|
||||
logged; power button → clean shutdown flow; fan responds to thermal load.
|
||||
**Prerequisite work (P5.0):**
|
||||
|
||||
The upstream `acpi-rs` crate (rev 90cbe88) **panicked** on every AML
|
||||
`Notify (target, value)` opcode — the interpreter path was literally
|
||||
`unimplemented!()`. Since `Notify` is the backbone of ACPI event flow
|
||||
(EC queries fan it out, battery/AC/lid/thermal devices use it, _Qxx
|
||||
methods end with `Notify(\_SB.BAT0, 0x80)`), this is unworkable on real
|
||||
laptop firmware. Fixed by **vendoring the acpi-rs crate into the base
|
||||
fork** (`drivers/acpi-rs/`) with a real `Opcode::Notify` executor:
|
||||
|
||||
- `src/aml/mod.rs`: resolves the target ACPI path, then invokes
|
||||
`Handler::handle_notify(target_path, value)`.
|
||||
- `src/lib.rs`: new `Handler::handle_notify` default (no-op) so
|
||||
existing users of the crate (amlserde, others) keep compiling.
|
||||
- `drivers/acpid/Cargo.toml` + `drivers/amlserde/Cargo.toml`: switched
|
||||
to `acpi = { path = "../acpi-rs", features = ["alloc", "aml"] }` so
|
||||
every consumer sees the same fork (avoids the dual-version trap).
|
||||
- `Cargo.toml` (workspace): added `"drivers/acpi-rs"` to members.
|
||||
|
||||
This is a **vendored fork**, not a `local/sources/acpi-rs/` submodule.
|
||||
It lives inside the base fork and travels with it. Upstream acpi-rs
|
||||
has Notify unimplemented at the time of vendoring; if upstream lands a
|
||||
real Notify implementation we will re-evaluate convergence per the
|
||||
upstream-first rule (CONSOLE-TO-KDE § "upstream-first for fast-moving
|
||||
components").
|
||||
|
||||
**Stub sweep in this phase:**
|
||||
|
||||
1. **acpi-rs `Notify` opcode was `unimplemented!()`** → vendored fork
|
||||
with a real executor that resolves the target path and calls back
|
||||
into the handler.
|
||||
2. **acpid had no SCI/GPE/PM1 path at all** → new `gpe.rs` (FADT-driven
|
||||
GPE block map, write-1-to-clear status, read-modify-write enable
|
||||
preserving firmware state on every other GPE) + `power_events.rs`
|
||||
(init + SCI dispatch following Linux `evgpe.c` / `ec.c` / `button.c`).
|
||||
3. **EC query protocol had no event-loop caller** → `pub fn sci_evt_set()`
|
||||
and `pub fn query()` exposed on `Ec`, called by the SCI handler with
|
||||
a 32-iteration bounded drain guard.
|
||||
4. **No lid/button/fan scheme surface** → scheme.rs grew `Lid`,
|
||||
`LidState`, `ButtonDir`, `Button`, `Notifications`, `Fan`,
|
||||
`FanState`, `FanSpeed` handle kinds with proper directory-vs-file
|
||||
separation.
|
||||
5. **`evaluate_acpi_method` took `&mut self`** → changed to `&self`
|
||||
everywhere; AML mutation goes through the existing `RwLock`-guarded
|
||||
`AmlSymbols` inner state, which is how the EC query loop and the
|
||||
notification drain can run from a read-only context.
|
||||
|
||||
**Review-fix round (2026-07-20):** this phase inherited all Phase 4
|
||||
review fixes (LPSS PCI bring-up, I2C engine ordering, firmware op-mode
|
||||
honesty) because it shares the base fork. No additional review has
|
||||
been run on the Phase 5 surface yet; the implementation follows the
|
||||
same Linux 7.1 reference patterns and bounded-validation discipline.
|
||||
|
||||
| # | Work item | Files | Status |
|
||||
|---|---|---|---|
|
||||
| 5.0 | *(prerequisite)* Real AML `Notify` opcode (was `unimplemented!()` in upstream acpi-rs) | `drivers/acpi-rs/` (vendored fork), `drivers/acpid/Cargo.toml`, `drivers/amlserde/Cargo.toml`, workspace `Cargo.toml` | ✅ DONE — vendored fork at base `drivers/acpi-rs/`; `Handler::handle_notify` hook added; all acpi consumers in the base workspace pointed at the path dep |
|
||||
| 5.1 | **GPE dispatch infrastructure in acpid**: SCI via `/scheme/irq` (ACPI `sci_int` from FADT), GPE status/enable block access, per-GPE handler registry | `drivers/acpid/src/gpe.rs` (new), `drivers/acpid/src/main.rs` | ✅ DONE — `GpeBlocks::from_fadt` builds the register map; PM1 fixed-event bits (PWRBTN/SLPBTN/RTC) per ACPI 6.4 §4.8.3.1; enable preserve + status write-1-to-clear; main loop subscribes `/scheme/irq/{sci_irq}` (default 9) and dispatches via `handle_sci`. 2 unit tests cover block mapping + GPE1 base offset |
|
||||
| 5.2 | EC event loop: `SCI_EVT` poll/IRQ → `QR_EC` query → dispatch `_Qxx` AML methods; wire battery/AC change notifications (`\_SB.BAT0` notify 0x80) | `drivers/acpid/src/power_events.rs` (new), `drivers/acpid/src/ec.rs`, `drivers/acpid/src/notifications.rs` (new), `drivers/acpid/src/aml_physmem.rs` | ✅ DONE — ECDT parse for EC device path + GPE (ACPI 6.4 §5.2.16) with `_HID = PNP0C09` probe fallback over conventional paths (mirrors Linux `acpi_ec_ecdt_start`); `handle_sci` drains the EC query queue (bounded at 32 iterations); `Ec::sci_evt_set()` / `Ec::query()` exposed; `_Qxx` AML methods evaluated; `AmlNotifications` shared queue receives `Notify` events pushed by the vendored acpi-rs `handle_notify` hook |
|
||||
| 5.3 | Lid switch (PNP0C0D `_LID`) + power/sleep fixed events (PM1_STS PWRBTN/SLPBTN) → scheme surfaces + kstop events | `drivers/acpid/src/scheme.rs`, `drivers/acpid/src/power_events.rs`, `drivers/acpid/src/acpi.rs` | ✅ DONE — lid device discovered via `_LID` evaluation over candidate paths (EC child + `\_SB`); PM1 fixed events cleared-before-dispatch; surfaces at `/scheme/acpi/{lid/state, button/{power,sleep}, notifications}`; PowerButton increments edge counter and triggers the main-loop shutdown path; SleepButton increments counter and calls `enter_s2idle`; AML `Notify(\_SB.LID_,…)` refreshes `_LID` and emits `LidChanged(open)` on edge transitions |
|
||||
| 5.4 | Event-driven `redbear-upower` (replace polling with notify-driven refresh) | `local/recipes/system/redbear-upower/source/src/main.rs` | ✅ DONE — added 250 ms `notify_interval` tokio arm draining `/scheme/acpi/notifications`; on non-empty drain, snapshots power state and emits `Changed` D-Bus signal if it changed. The existing 30 s poll arm stays as a safety net for missed events |
|
||||
| 5.5 | Fan: real PNP0C0B/EC fan control replacing thermald soft model | `drivers/acpid/src/scheme.rs`, `drivers/acpid/src/power_events.rs`, `drivers/acpid/src/acpi.rs` | ✅ DONE — **implementation differs from the original plan wording**: instead of replacing thermald's soft fan model, acpid now owns the ACPI 4.0 fan method surface directly. Probes `_FST`-exposing devices over conventional placements (EC child + LPCB + `\_SB`), exposes read+write at `/scheme/acpi/fan/<idx>/{state,speed}` (`_FST` on read, `_FSL` on write, percent clamped 0–100). thermald keeps its soft model for now; convergence between the two is a Phase 9.x hardening task |
|
||||
|
||||
**Gate 5 (code-complete, runtime open):** Unplug/replug AC → instant
|
||||
battery/AC event; close/open lid → event logged; power button → clean
|
||||
shutdown flow; fan responds to thermal load. All code paths exist and
|
||||
compile; none have been exercised on the host yet (Phase 1 boot is the
|
||||
prerequisite). The 250 ms notification drain in redbear-upower should
|
||||
turn the existing 30 s worst-case state lag into a near-instant event,
|
||||
but this is a runtime claim and remains to be measured.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ const BUS_NAME: &str = "org.freedesktop.UPower";
|
||||
const UPOWER_PATH: &str = "/org/freedesktop/UPower";
|
||||
const DISPLAY_DEVICE_PATH: &str = "/org/freedesktop/UPower/devices/DisplayDevice";
|
||||
const ACPI_POWER_ROOT: &str = "/scheme/acpi/power";
|
||||
const ACPI_NOTIFICATIONS: &str = "/scheme/acpi/notifications";
|
||||
/// Battery/AC events arrive via AML `Notify` (EC query → `_Qxx` →
|
||||
/// `Notify(\_SB.BAT0, 0x80)`) and are drained from this file. Polling the
|
||||
/// drain queue at sub-second cadence turns a 30 s worst-case state lag
|
||||
/// into a near-instant event.
|
||||
const NOTIFICATION_POLL_MS: u64 = 250;
|
||||
|
||||
const DEVICE_KIND_UNKNOWN: u32 = 0;
|
||||
const DEVICE_KIND_LINE_POWER: u32 = 1;
|
||||
@@ -159,6 +165,20 @@ fn read_u64(path: impl AsRef<Path>) -> Option<u64> {
|
||||
read_trimmed(path)?.parse().ok()
|
||||
}
|
||||
|
||||
/// Drain the ACPI notification log (`/scheme/acpi/notifications`): each
|
||||
/// line is `<device> <value>` as delivered by AML `Notify`.
|
||||
fn read_notifications() -> Vec<String> {
|
||||
fs::read_to_string(ACPI_NOTIFICATIONS)
|
||||
.map(|text| {
|
||||
text.lines()
|
||||
.map(str::trim)
|
||||
.filter(|line| !line.is_empty())
|
||||
.map(str::to_owned)
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn read_f64(path: impl AsRef<Path>) -> Option<f64> {
|
||||
read_trimmed(path)?.parse().ok()
|
||||
}
|
||||
@@ -526,6 +546,8 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
|
||||
let mut last_snapshot = runtime.snapshot();
|
||||
let mut poll_interval =
|
||||
tokio::time::interval(Duration::from_secs(POLL_INTERVAL_SECS));
|
||||
let mut notify_interval =
|
||||
tokio::time::interval(Duration::from_millis(NOTIFICATION_POLL_MS));
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
@@ -536,6 +558,24 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
|
||||
eprintln!("redbear-upower: shutdown signal received, exiting cleanly");
|
||||
break;
|
||||
}
|
||||
_ = notify_interval.tick() => {
|
||||
let drained = read_notifications();
|
||||
if !drained.is_empty() {
|
||||
eprintln!(
|
||||
"redbear-upower: {} ACPI notification(s): {}",
|
||||
drained.len(),
|
||||
drained.join(", ")
|
||||
);
|
||||
let current_snapshot = runtime.snapshot();
|
||||
if current_snapshot != last_snapshot {
|
||||
eprintln!(
|
||||
"redbear-upower: power state changed after notification, emitting Changed signal"
|
||||
);
|
||||
let _ = UPowerDaemon::changed(&signal_emitter).await;
|
||||
last_snapshot = current_snapshot;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = poll_interval.tick() => {
|
||||
let current_snapshot = runtime.snapshot();
|
||||
if current_snapshot != last_snapshot {
|
||||
|
||||
+1
-1
Submodule local/sources/base updated: ddcd08709f...f315a9be3b
Reference in New Issue
Block a user