// Native Watchman flash config read — M3 step 6 (locked decision 3). // // Reads the per-unit calibration config over Tundra HID feature reports // (libsurvive driver_vive protocol): // GET 0x10 (CONFIG_READMODE): bytes [1]<<8|[2] = compressed payload length // GET 0x11 (CONFIG_READ): byte [1] = chunk size, data at [2..]; 0 = end // Payload is a zlib stream; inflated (vendored puff) it is the config JSON. // // Protocol facts burned in (M3, LHR-1F8E25F1): // - Our device handles are FILE_FLAG_OVERLAPPED; synchronous HidD_GetFeature // fails on them (ERROR_GEN_FAILURE) — reads go through overlapped // DeviceIoControl(IOCTL_HID_GET_FEATURE), hidapi's shape. // - The Tundra stalls GET_FEATURE transiently (~1 in 3) — every read // retries (20x / 20 ms), mirroring libsurvive's resubmit-on-stall. // - Backup discipline: the read runs TWICE and must be byte-identical // before anything is returned (docs/m3-product-shape.md step 6). // - The SteamVR lighthouse-cache JSON is LOSSY vs flash (ipd block); flash // is the authoritative source. #pragma once #include #include #include namespace sauna { // Reads + inflates the config from the Beyond's Watchman (serial filter "" // = MCU-paired auto-pick, same rules as the IMU reader). On success fills // *jsonText (and *rawZ, *outSerial if given). False + *err on failure. bool ReadWatchmanConfig(const char* serial, std::string* jsonText, std::string* err, std::string* outSerial = nullptr, std::vector* rawZ = nullptr); } // namespace sauna