puck_hid.h 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. // puck_hid.h -- the Steam Controller puck personality (MODE_STEAM + MODE_LIZARD).
  2. //
  3. // This is the controller that wears Valve's Proteus puck identity (28DE:1304) and exposes the four puck HID slot interfaces
  4. // (interface N owns bond slot N). It implements:
  5. // - the puck feature COMMAND CHANNEL (handleSet/handleGet): reports 0x83/0xAE/0xB4/0xAD/0xA2/0xA3 the host
  6. // uses to read attributes/serials and read/write/clear bond slots, plus the host->controller haptic relay
  7. // ride-along on OUTPUT reports 0x80-0x86 and feature passthrough (report 0x01).
  8. // - the seamless LIZARD decision: when Steam is driving the gamepad (recent OUTPUT/heartbeat) we forward the
  9. // gamepad report 0x45; when Steam is closed we present keyboard+mouse on the SAME interface. MODE_LIZARD
  10. // forces lizard always. lizardActive() is the single source of truth, shared with the haptic gate so we
  11. // never relay haptics while presenting lizard (Steam isn't reading 0x45 back -> would buzz-loop).
  12. // - the USB connection-state presentation (reports 0x79 / 0x7B) Steam reads to mark the controller connected.
  13. #pragma once
  14. #include "controllers.h"
  15. #include <stdint.h>
  16. // Forward report 0x45 only when the seq advanced (dedupe like the real puck); sending stale
  17. // repeats makes Steam's trackpad smoothing stair-step. (WebUSB field 14)
  18. extern uint8_t g_fwdNewOnly;
  19. // Content dedup: forward a 0x45/0x42 report only when its body EXCLUDING the free-running counter byte
  20. // changed, so the delivered rate tracks the controller's real distinct-report rate (not the poll rate).
  21. // On by default; console "CD" toggles for A/B. (see puck_hid.cpp for the why)
  22. extern uint8_t g_fwdContentDedup;
  23. // True while we are presenting the desktop keyboard/mouse instead of forwarding the gamepad to Steam
  24. // (MODE_LIZARD always, or MODE_STEAM once Steam's heartbeat has stopped). Exposed so the haptic layer can
  25. // tell "lizard is active" without duplicating the heartbeat logic -- the lizard-suppression keepalive must
  26. // NOT run while lizard is active, or it disables the controller's autonomous touchpad haptic ticks.
  27. bool puckLizardActive();
  28. // Feature-command capture (diagnostic): when g_cmdCapture is on, Steam's USB feature SET/GET commands are
  29. // logged to serial (# FC lines) from loop context and the high-rate I45 input stream is suppressed, so the
  30. // connect handshake is readable. puckCmdLogDrain() must be called once per loop(). Console "FC" toggles it.
  31. extern bool g_cmdCapture;
  32. void puckCmdLogDrain(void);
  33. // Note that a controller power-off (0x9F "off!") was just relayed to `slot` (or ALL used slots for the 0xFF
  34. // broadcast). The puck then presents that slot as cleanly DISCONNECTED to Steam and HOLDS it there through the
  35. // controller's messy shutdown -- a real controller keeps streaming F1 for up to ~1s after the off command, and
  36. // without this the puck faithfully reports "still connected" -> conn bounces -> Steam sees a phantom reconnect
  37. // (the "reappears for a split second") and re-runs its connect config, or never sees a clean disconnect and
  38. // leaves the controller in its list. Called from hapticSendShutdown() so every power-off path is covered.
  39. void puckNotePowerOff(uint8_t slot);
  40. class SteamPuckController : public IController {
  41. public:
  42. void begin() override;
  43. void onReport45(int slot, const uint8_t *rep, bool fresh,
  44. uint8_t bodyTlen) override;
  45. void onAuxReport(int slot, uint8_t rid, const uint8_t *data, uint8_t n)
  46. override; // forward controller 0x43/0x44 status to Steam
  47. void task() override;
  48. void wakeEvent() override;
  49. bool isPuck() const override
  50. {
  51. return true;
  52. }
  53. };
  54. extern SteamPuckController g_steamPuck;