mirror of
https://github.com/safijari/openpuck.git
synced 2026-11-03 22:30:22 +00:00
[PR #234] [CLOSED] Add MODE_WAKE: smart-power-on wake personality (Lenovo Alt+P) #250
Labels
No labels
pull-request
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
safijari/openpuck#250
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/safijari/openpuck/pull/234
Author: @Saffsanity
Created: 8/15/2026
Status: ❌ Closed
Base:
main← Head:mode-wake📝 Commits (10+)
4dbe150Add MODE_WAKE: boot-keyboard personality for firmware smart power on95e3cefSurface MODE_WAKE in the panel, README, and CODE_MAP2103639Shape the wake press like a human keystroke (fixes M90q Gen 6 S5 wake)bc90a33Keep the triggering controller alive through the post-fire boot040fbd7Document wake mode: setup guide for smart-power-on hosts46ac644Harden the wake flow against every review finding8b147c9Auto-rearm: tell sleep from shutdown by the host's remote-wakeup arming7b36141Auto-rearm: sample sleep-vs-shutdown once per down-episode, not per edge4b17feeFix second-review findings: dead-bus truths, episode tracking, docs014e617Retry the suspend power-off relay while the controller stays linked📊 Changes
14 files changed (+982 additions, -40 deletions)
View changed files
📝
CODE_MAP.md(+30 -2)📝
OpenPuck/OpenPuck.ino(+35 -15)📝
OpenPuck/config.cpp(+35 -6)📝
OpenPuck/config.h(+28 -1)📝
OpenPuck/controllers.cpp(+3 -0)📝
OpenPuck/haptics.cpp(+147 -3)➕
OpenPuck/mode_wake.cpp(+339 -0)➕
OpenPuck/mode_wake.h(+159 -0)📝
OpenPuck/rf_link.cpp(+39 -7)📝
OpenPuck/rf_link.h(+5 -0)📝
OpenPuck/serial_console.cpp(+1 -1)📝
README.md(+1 -0)➕
docs/WAKE_MODE.md(+151 -0)📝
docs/index.html(+9 -5)📄 Description
Summary
Adds MODE_WAKE (mode 10): a bare boot-protocol keyboard personality that powers on a fully shut-down (S5) host through firmware "smart power on" (Lenovo: Alt+P from the always-on USB port). On the first controller connect after a 3 s link-quiet arming period it types the hotkey and reboots back into the puck — by the time the OS is up, the puck is a normal puck and the controller has reconnected by itself. USB remote wakeup only covers S3; this covers real power-on.
Hardware-validated on SteamOS (Lenovo ThinkCentre M90q Gen 6), including the optional
WAKE_AUTO_REARM=1build: shut down → puck re-arms itself → one controller power-button press → machine boots → controller is connected and working in SteamOS. Not yet exercised on a Windows host. A subsequent adversarial-review hardening pass (46ac644) tightened the arming/trigger/grace paths and was re-validated end-to-end on the same hardware. Should apply to any Lenovo/Dell-class box with a keyboard-hotkey power-on feature — hotkey/press-shaping/return mode are#defines at the top ofmode_wake.h.Default behavior is unchanged for everyone who doesn't use it. Mode 10 is only entered explicitly (panel / console
x10/ assignable chord);WAKE_AUTO_REARMis compile-time off; the new suspend-policy gates are inert unless the puck is in wake mode or booting off a wake fire; the cfg layout is byte-identical (a dead reserved byte is repurposed, oldcfg.binfiles load unchanged).User docs: docs/WAKE_MODE.md — BIOS setup, the designated port, LED diagnostics, auto-rearm trade-offs, adapting to other vendors.
Design notes
bareHidgating insetup(); keeps remote-wakeup in bmAttributes like a real keyboard). Also endpoint budget — the puck uses 6 of 7 IN.anySlotLinkUp()connect up-edge (F-type replies only, so a neighbor puck's shared-address beacon can't false-fire), latched 2 s so an EC that suspends the port between enumeration and the press still fires — but a controller that connected against a dead port can't fire hours later into a manually booted OS. Arming requires ~5 s of observed link silence (the quiet clock runs only while RF is on air) so chording in with a connected controller doesn't type Alt+P into the live session.WAKE_AUTO_REARMre-enters wake mode mid-boot. The grace is a one-shot persisted in cfg (Cfg.wakeHandoff, the deadrsvd1byte — this board class's bootloader wipes.noinitRAM every reset, and the return mode-switch already rewrites cfg, so persistence is free); it is deadline-only (90 s — a POST-time BIOS SET_CONFIGURATION readsmounted()long before the OS is up, so ending on mount would re-expose the kill during the BIOS→kernel gap), and a machine that never boots still gets the battery-saving power-off via an expiry fallback. The grace boot also skipssetup()'s 3 s mount wait and opens the RF connect cooldown, putting beacons on air on the firstloop()pass — the triggering controller relinks in 0.424 s (measured) instead of giving up during POST.WAKE_SEQ_DEADLINE_MS(10 s) bails to settle-and-reboot if the EC deconfigures us mid-sequence, so a booted OS never receives a stray Alt+P.WAKE_AUTO_REARM— compile-time, off by default. Persistent suspend in any other mode reboots into MODE_WAKE so it re-arms itself (the smart-power-on port keeps VBUS in S5, so the MCU never cold-boots). Sleep-aware: the OS armsDEVICE_REMOTE_WAKEUPbefore S3 (that's what makes wake-from-S3 work), a shutdown never does — sampled once at the first suspend edge of a down-episode, so the Smart Power On EC re-arming the feature during its S5 port takeover (a real M90q behavior caught by black-box tracing) can't masquerade as a sleeping OS. Sleeping leaves the puck a puck and controller-wake-from-S3 keeps working; shutdown re-arms. Still off by default: a host with device wakeup disabled in sysfs reads sleep as shutdown (degrades to re-arm-on-sleep). It intentionally bypasses the "no mode switch while suspended" rule the chord/console paths follow — rationale inmode_wake.cpp.Commits
mode_wake.{h,cpp}+ plumbing (config.h,controllers.cpp,OpenPuck.ino,serial_console.cpp); also fixes a hardcoded<= 9bound in theMODE_SUFFIXlookup that theMODE_MAXbump would otherwise have desynced.MODE_NAMES, README row, CODE_MAP section.docs/WAKE_MODE.mduser guide.anySlotLinkUpmillis-wrap guard).Open question for maintainer
Identity is
28DE:574B— Valve VID with an invented PID, following the ReversePuck28DE:1302precedent. Happy to change if you'd prefer something else.Status
WAKE_AUTO_REARMvariants compile clean (arduino-cli, adafruit:nrf52 1.7.0)46ac644(full S5 auto-rearm cycle + arming-race negative test — see the hardware comments)🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.