#3308·Solaar

Reverse Engineering the Logitech MX Keys Backlight Timeout via HID++ 2.0

Author: Alo3zeraCreated Aug 18, 2026Updated Sep 6, 2026
Labelsenhancement

Reverse Engineering the Logitech MX Keys Backlight Timeout via HID++ 2.0

Abstract

This report documents an investigation into whether the Logitech MX Keys keyboard's backlight inactivity timeout (approximately 5 seconds) can be adjusted via the HID++ 2.0 protocol. Despite systematic enumeration and testing of all discoverable HID++ features — including undocumented/hidden ones — no host-accessible parameter controlling the timeout duration was found. Two previously undocumented (or at least not publicly written up) findings emerged as a byproduct: a proximity-sensor sensitivity control and an LED self-test trigger, both on features gated behind a "hidden features" master switch. The report also identifies the keyboard's main SoC (Nordic nRF52832) and notes a publicly known, unpatched hardware vulnerability that would allow full firmware access — a path outside the scope of this investigation, as it requires voltage-glitching hardware and carries real risk of device damage.

Device Under Test

  • Product: Logitech MX Keys (first-generation wireless keyboard)
  • Firmware version: 12.1.13
  • Connection: Logitech Unifying receiver, USB VID:PID 046d:c52b
  • FCC ID: JNZYR0073 (Logitech Far East Ltd.), granted 2019-06-24
  • Main SoC (identified from FCC internal photos, marking N52832 QFAB80 1719AT): Nordic Semiconductor nRF52832, an ARM Cortex-M4F Bluetooth Low Energy SoC
  • Radio: Bluetooth 5.0 LE, confirmed via FCC BT-LE test report

Methodology

All communication was performed by sending raw HID++ 2.0 long reports (report ID 0x11, 20 bytes) directly to the Unifying receiver's vendor-defined HID interface (usage_page 0xff00, usage 0x2) via Python and the hidapi/hid library on Windows. No third-party software (Solaar, Logi Options+) was used to issue commands; Logi Options+ was only inspected passively (its local SQLite settings database) for reference.

Standard HID++ 2.0 message structure used throughout:

Byte 0: Report ID (0x11 = long report)
Byte 1: Device index (3, for this keyboard on this receiver)
Byte 2: Feature index
Byte 3: (function_id << 4) | software_id
Bytes 4-19: Parameters (16 bytes)

Feature indices were resolved via the standard ROOT (index 0) and FEATURE SET (index 1) features, per the publicly available HID++ 2.0 draft specification and the open-source Solaar and libratbag/logiops driver source code, which document the standard feature IDs, function numbering conventions, and error codes used as reference throughout this investigation.

Safety methodology

Given the risk of writing undocumented values to a personally-owned device, testing followed a strict escalation model:

  1. Read-only enumeration first (function 0, empty/zero parameters) for every feature before any write was attempted.
  2. Single-function, single-value writes, never batch/looped writes with non-zero payloads.
  3. Auto-revert scripts: any write to a previously-unknown function used a script that recorded the baseline value, waited a fixed observation window, and unconditionally wrote the baseline back — including on Ctrl+C interrupt or exception — before exiting.
  4. Explicit exclusion list: known-dangerous documented features were never touched by automated sweeps, specifically RESET (feature 0x0020), DEVICE RESET (0x1802), OOBSTATE (0x1805, governs factory/pairing state), CONFIG DEVICE PROPS (0x1806, persistent configuration storage), and DFUCONTROL SIGNED (0x00C2, firmware update mode entry).

Feature Enumeration

The device exposes 32 HID++ 2.0 features. The table below lists those relevant to this investigation (full raw list omitted for brevity):

Index | Feature ID | Name | Notes -- | -- | -- | -- 0 | 0x0000 | ROOT | Standard 1 | 0x0001 | FEATURE SET | Standard 7 | 0x1000 | BATTERY STATUS | Standard, used to confirm charging state 8 | 0x1b04 | REPROG CONTROLS V4 | Standard, see Section 5 11 | 0x1982 | BACKLIGHT2 | Standard but limited implementation, see Section 4 23 | 0x1830 | (unknown) | Hidden; simple enable flag, no timing data 24 | 0x1890 | (unknown) | Hidden; would not respond to function 0 without parameters 25 | 0x1891 | (unknown) | Hidden; same as above 26 | 0x18a1 | (unknown) | Hidden; LED self-test trigger, see Section 6 27 | 0x1df3 | (unknown) | Hidden; returned zero/no useful data 28 | 0x1e00 | (unknown, standard name "Enable Hidden Features") | Master switch gating indices 23-32, see Section 3 29 | 0x1eb0 | (unknown) | Hidden; proximity sensor sensitivity, see Section 5 30 | 0x1861 | (unknown) | Hidden; returns sequential numeric pairs, likely calibration data 31 | 0x1a20 | (unknown) | Hidden; returns numeric pairs, likely sensor-related 32 | 0x18b0 | (unknown) | Hidden; returned zero/no useful data

Appendix B: Sources

  • Logitech HID++ 2.0 Draft Specification (2012-06-04), published via lekensteyn.nl
  • Logitech HID++ 1.0 Specification for Unifying Receivers, published via lekensteyn.nl
  • Solaar project source code (hidpp20.py, settings_templates.py), pwr-Solaar/Solaar, GitHub
  • PixlOne/logiops project wiki (HIDPP 2.0 protocol reference, CID reference table), GitHub
  • Logitech public HID++ 2.0 feature documentation, Logitech/cpg-docs, GitHub
  • FCC ID JNZYR0073 public filing (internal photographs, BT-LE/GFSK test reports), FCC/Bureau Veritas
  • LimitedResults, "nRF52 Debug Resurrection (APPROTECT Bypass)" Parts 1 & 2 (2020)
  • Nordic Semiconductor customer security notice regarding APPROTECT (2020-06-12)
  • iFixit, "Logitech MX Keys disassembly guide" and associated community comments

Appendix C: Author's Note on Reproducibility

All findings involving physical device behavior (Findings 2-5, 8) were independently, physically verified by me, the author, on the specific unit under test, not inferred from documentation alone. Findings involving the absence of a behavior (Findings 1, 6, 7) are necessarily bounded by the specific firmware version, hardware revision, and testing window described; they should be read as "not found under these conditions" rather than "proven not to exist."