LOCK-DPMS-LESSONS: distilled lock/wake/DPMS operating knowledge
Plus queue item 11: suspend-resume FTS calibration race (dt2w wake from s2idle wedges the touch controller; dpms serialization didn't cover it).
This commit is contained in:
parent
8ceca51717
commit
c8acee9579
2 changed files with 120 additions and 0 deletions
113
docs/LOCK-DPMS-LESSONS.md
Normal file
113
docs/LOCK-DPMS-LESSONS.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Lock / DPMS / wake — the lessons doc
|
||||
|
||||
The hard-won rules for everything between "screen goes dark" and "user is
|
||||
back in." Doctrine lives in SESSION-AUTHORITY-DOCTRINE.md; the boot
|
||||
choreography lives in session-authority-boot-order.md; the raw archaeology
|
||||
lives in DUMP-power-idle-lock-2026-07-15.md. This page is the distilled
|
||||
operating knowledge — what we know is true on blueline, with the receipts.
|
||||
|
||||
## 1. Ordering: lock, then off
|
||||
|
||||
Blanking an unlocked session leaks the desktop for a frame on wake, and a
|
||||
wake that races the lock can land input on an unlocked surface.
|
||||
|
||||
- Power button (`blueline-power-button`): screen on → `session lock` IPC →
|
||||
wait ≤2s for the compositor-acked `"locked": true` → blank. If the lock
|
||||
IPC fails, blank anyway — dark-but-unlocked is recoverable, lit-and-
|
||||
unlocked in a pocket is not.
|
||||
- Suspend: `PrepareForSleep` → lock secure → release the delay inhibitor.
|
||||
Fail-closed, ~5s budget.
|
||||
- Idle: the native IdleCoordinator locks (300s) well before hypridle's
|
||||
dpms-off (600s) and suspend (900s), so idle blanking always finds the
|
||||
session already locked.
|
||||
|
||||
## 2. One DPMS authority
|
||||
|
||||
`blueline-screen-toggle` serializes every panel power transition. Nothing
|
||||
else calls `hyprctl dispatch dpms` — when hypridle still had a raw dpms
|
||||
call after the toggle became authority, double-tap wake raced `on-resume`
|
||||
and the touch controller calibrated against a half-ramped panel
|
||||
(Pixel3Arch `73d0cd0`). Any new wake source (power button, dt2w, proximity
|
||||
someday) goes through the toggle, or it will reintroduce the race.
|
||||
|
||||
## 3. The FTS touch controller has opinions
|
||||
|
||||
- On panel-unprepare it drops to gesture mode (that is what makes dt2w
|
||||
work while the screen is off).
|
||||
- On sense-on it self-calibrates against the panel. If the panel is still
|
||||
ramping, calibration is garbage and touch goes deaf or erratic. The
|
||||
kernel signature is a continuous spew of
|
||||
`fts_status_event_handler: … invalid … Strength soft Force cal`.
|
||||
A single line after a wake is noise; a stream that keeps flowing minutes
|
||||
later is a wedged controller.
|
||||
- The serialized DPMS path fixed this for screen-off/on cycles. **The
|
||||
suspend-resume path still races** (found 2026-07-17): s2idle exit
|
||||
re-runs panel-prepare → FTS reset → Sense ON with nobody pacing it, and
|
||||
a dt2w wake from suspend produced exactly the wedged-controller spew and
|
||||
a dead swipe-to-unlock on the lockscreen. Open gap; candidate fixes are
|
||||
kernel-side (pace sense-on after panel ramp — check what Android does,
|
||||
Android is the reference) or a post-resume rebind hook, which needs care
|
||||
because rebinding while the panel is off may drop the dt2w feature flags.
|
||||
- Recovery without reboot:
|
||||
`echo 2-0049 > /sys/bus/i2c/drivers/fts/unbind && sleep 1 && echo 2-0049 > /sys/bus/i2c/drivers/fts/bind`
|
||||
(root). Firmware re-verifies, sense restarts. Expect a line or two of
|
||||
invalid-cal immediately after; continuous spew means it did not take.
|
||||
|
||||
## 4. Suspend truths
|
||||
|
||||
- Suspend is s2idle. The USB gadget (usb0 NCM/ACM) dies with it — an ssh
|
||||
session dropping and `usb0: Lost carrier` in the journal during suspend
|
||||
entry is *normal*, not a crash. Do not chase gadget "flaps" without
|
||||
first checking `journalctl` for `PM: suspend entry`.
|
||||
- dt2w wakes the phone from s2idle (gesture mode survives suspend).
|
||||
- NetworkManager/ModemManager sleep-monitor churn around suspend entry is
|
||||
choreography, not failure.
|
||||
- Warm reboots poison TZ/remoteproc state on blueline — recovery from a
|
||||
bad state is a true cold boot (unplugged power-off), never `reboot`.
|
||||
|
||||
## 5. Lock architecture (who holds what)
|
||||
|
||||
- souveraine-sessiond takes ext-session-lock at boot *before* the shell
|
||||
exists; no desktop surface can precede the lock. The shell inherits the
|
||||
lock later over the bridge socket (`misc.allow_session_lock_restore`).
|
||||
- The sessiond↔shell handoff necessarily drops one lock client for ~1.05s.
|
||||
Hyprland's `lockdead_screen_delay` default (1000ms) painted a red
|
||||
"lockscreen crashed" flash in that gap; it is 4000ms in the tracked
|
||||
hyprland.lua. The session stays locked throughout — the flash was
|
||||
cosmetic.
|
||||
- Shell heartbeat loss → sessiond fail-closes: re-acquires the lock and
|
||||
presents its bare fallback PIN surface. Proven in anger 2026-07-17 when
|
||||
the shell died silently post-unlock. PAM at the fallback surface is a
|
||||
real unlock.
|
||||
- `screenLocked` (request) and `screenLockSecure` (compositor ack) are
|
||||
different facts. Personal-tier gates key off *secure*. The compositor
|
||||
can end a lock unilaterally (`ext_session_lock_v1_finished`); the shell
|
||||
resyncs `screenLocked` when that happens (LockScreen.qml
|
||||
onLockStateChanged) — before that fix, a stale `lockRequested:true`
|
||||
both lied to the gates and blocked re-lock.
|
||||
- Unlock is never an agent verb.
|
||||
|
||||
## 6. Shell surface rules
|
||||
|
||||
- Lock-path QML deploys only while the phone is UNLOCKED.
|
||||
`WlSessionLock.surfaceComponent` cannot change while the lock is active;
|
||||
hot-deploying over an active lock wedges the lockscreen until reboot.
|
||||
- Two-stage lock: glance first, PIN pad on swipe-up (60px) / tap / any
|
||||
key; retreats on swipe-down (80px, empty PIN only) or 25s idle.
|
||||
- hypridle's `after_sleep_cmd` fires the `lockFocus` global shortcut on
|
||||
EVERY wake. It must only refocus the (hidden) PIN field — refocus must
|
||||
never imply PIN reveal, or every wake would skip the glance stage.
|
||||
- The shell runs under `souveraine-shell.service` (journal keeps stderr,
|
||||
restart on failure, non-clean exits append to
|
||||
`~/.local/state/souveraine/crashes.log`, gives up after 8 tries and
|
||||
leaves the fail-closed lock in charge). It was a raw exec once; the
|
||||
crash reason died with it and unlock landed on a black desktop.
|
||||
|
||||
## 7. Open gaps
|
||||
|
||||
- Suspend-resume FTS calibration race (§3) — the one live regression.
|
||||
- Crash *reporting* (queue item 10): crashes.log + coredumps + failed
|
||||
units surfaced as banner/sensoria, not just survived.
|
||||
- Proximity wake must never touch lock state
|
||||
(DUMP-power-idle-lock-2026-07-15.md §2) — same doctrine as dt2w: wake
|
||||
sources move the panel, only sessiond moves the lock.
|
||||
|
|
@ -138,6 +138,13 @@ authority. Screen off → wake (to lockscreen). Tap-to-wake unchanged.
|
|||
shell banner / sensoria event on new crashes.log entries + new
|
||||
coredumpctl entries and --failed user units, so a crash is *reported*,
|
||||
not just survived. Pairs with queue item 5's notification plumbing.
|
||||
11. **Suspend-resume FTS calibration race** (found 2026-07-17): s2idle
|
||||
exit re-runs panel-prepare → FTS reset → Sense ON unpaced; dt2w wake
|
||||
from suspend wedged the controller (continuous invalid Force cal spew,
|
||||
dead swipe on the lockscreen). The 73d0cd0 serialization only covered
|
||||
dpms off/on. Candidates: kernel-side pacing (Android is the reference)
|
||||
or a post-resume rebind hook (careful: rebind with panel off may drop
|
||||
dt2w flags). Lessons doc: LOCK-DPMS-LESSONS.md §3.
|
||||
|
||||
## Boot password prompt (seen post-reboot, patched live)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue