Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/LOCK-DPMS-LESSONS.md
Fimeg c8acee9579 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).
2026-07-17 07:56:04 -04:00

6 KiB

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.