Skip to content

docs(core): correct the model_ids transport ordering, and fix the MX Keys mock - #668

Open
yuzi-co wants to merge 1 commit into
AprilNEA:masterfrom
yuzi-co:fix/model-ids-transport-order
Open

docs(core): correct the model_ids transport ordering, and fix the MX Keys mock#668
yuzi-co wants to merge 1 commit into
AprilNEA:masterfrom
yuzi-co:fix/model-ids-transport-order

Conversation

@yuzi-co

@yuzi-co yuzi-co commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The bug

DeviceModelInfo::model_ids (crates/openlogi-core/src/device.rs) was documented as:

Per-transport PIDs ordered to match Self::transports (USB, eQuad, BTLE, Bluetooth); slots for disabled transports stay 0.

Both halves are wrong.

1. The order is reversed

DeviceTransport in openlogi-hidpp declares USB as the high bit:

const USB       = 1 << 3;
const E_QUAD    = 1 << 2;
const BTLE      = 1 << 1;
const BLUETOOTH = 1 << 0;

PIDs are packed in ascending bit order, so the wire order is Bluetooth, BTLE, eQuad, USB — the reverse of the doc.

The documented order looks like it was copied from the DeviceTransports struct field order, which happens to list usb first. Understandable, but it does not match the wire.

2. The array is packed, not positional

openlogi-hidpp's doc for the same field is already correct:

The 16-bit PID for every supported transport protocol will be appended into this array, limiting the total amount of supported transport protocols to three.

Disabled transports are skipped, not zero-filled — which is also why three slots are enough for four transports. "Slots for disabled transports stay 0" would require four.

Hardware confirmation

Two devices, two different transport pairs, same rule:

device transports model_ids reading
G502 LIGHTSPEED usb+equad [407f, c08d, 0000] c08d is the USB PID — it enumerates as 046d:c08d when cabled. 407f is the wireless PID (wpid=407f). eQuad precedes USB.
MX Master equad+btle [b012, 4041, 0000] b012 is the BLE PID — Windows enumerates it as BTHLEDEVICE\…_PID&B012_…. 4041 is the eQuad PID. BTLE precedes eQuad.

Both packed from the front with a trailing zero. Under the old doc, model_ids[0] would be "the USB PID" — on the G502 that reads 407f, the wireless PID, and on the MX Master it reads a PID for a transport the device says it does not support (usb = false).

The ambiguity already caused a bug

mock_agent.rs contains two fixtures with the identical transport set usb=false, equad=true, btle=true, bluetooth=false and opposite orderings:

// MX Master 3S — BTLE first. Correct.
model_ids: [0xb034, 0x4082, 0],

// MX Keys — eQuad first. Wrong.
model_ids: [0x408a, 0xb35b, 0],

This PR swaps the MX Keys pair to [0xb35b, 0x408a, 0].

No test asserts on these values. Independently, channel_registry.rs's tests use direct(0xb35b) — treating 0xb35b as MX Keys' direct-attach PID, consistent with it being the BTLE entry.

Scope

Documentation and mock data only — no functional change.

I grepped every consumer of model_ids: openlogi-cli/src/cmd/list.rs:144 and openlogi-core/src/diagnostics.rs:362 both format or iterate all three slots. Nothing indexes the array by transport, so nothing was reading the wrong PID at runtime.

It still matters, because the doc is what the next implementer follows. In particular #667 proposes using model_ids to correlate one physical device across transports — doing that against the documented order would pick the wrong PID.

Local gate

Per AGENTS.md, Windows 11 / x86_64-pc-windows-msvc:

command result
cargo fmt --all -- --check exit 0
cargo clippy --workspace --all-targets -- -D warnings exit 0
cargo test --workspace exit 0
RUSTDOCFLAGS="-D warnings" cargo doc -p openlogi-hid … exit 101 — pre-existing

The rustdoc failure is the AsyncHidChannel::supports_short_long_hidpp link error that reproduces on a clean master on Windows and is fixed by #661; CI does not see it because the docs job runs on ubuntu.

I also built docs for the crate this PR actually touches. cargo doc -p openlogi-core reports 5 unresolved-link errors on this branch and the same 5 on clean master (ButtonId, Action::category, Binding::fill_gesture_defaults, …) — all pre-existing, none added here, and openlogi-core is not part of the AGENTS.md doc gate. The new [DeviceTransports] link resolves cleanly.

Related: #667.

…Keys mock

DeviceModelInfo::model_ids was documented as "ordered to match transports
(USB, eQuad, BTLE, Bluetooth); slots for disabled transports stay 0".
Both halves of that are wrong.

Order. The PIDs are packed in ascending HID++ transport-bit order, and
DeviceTransport declares USB as the *high* bit: USB = 1 << 3, E_QUAD =
1 << 2, BTLE = 1 << 1, BLUETOOTH = 1 << 0. So the wire order is
Bluetooth, BTLE, eQuad, USB — the reverse of what was documented. The
documented order appears to have been copied from the DeviceTransports
struct field order, which lists usb first.

Packing. openlogi-hidpp's own doc for the same field is right: the PID
"will be appended into this array". Disabled transports are skipped, not
zero-filled, which is also why three slots suffice for four transports.

Confirmed on two devices with different transport pairs:

  G502 LIGHTSPEED  transports=usb+equad   model_ids=[407f,c08d,0000]
    c08d is the USB PID (it enumerates as 046d:c08d when cabled);
    407f is the wireless PID (reported as wpid=407f). eQuad precedes USB.

  MX Master        transports=equad+btle  model_ids=[b012,4041,0000]
    b012 is the BLE PID (Windows enumerates it as BTHLEDEVICE ...PID&B012);
    4041 is the eQuad PID. BTLE precedes eQuad.

Both are packed, ascending-bit, with a trailing zero.

The ambiguity has already produced contradictory fixtures in mock_agent:
MX Master 3S declares equad+btle as [0xb034, 0x4082, 0] (BTLE first,
correct) while MX Keys declares the identical transport set as
[0x408a, 0xb35b, 0] (eQuad first). Swap the MX Keys pair. No test asserts
on these values; channel_registry's tests independently treat 0xb35b as a
direct-attach PID, consistent with it being the BTLE entry.

No functional change: every consumer of model_ids formats or iterates all
three slots, none indexes by transport.
@yuzi-co
yuzi-co requested a review from AprilNEA as a code owner August 18, 2026 22:16
@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown

Greptile Summary

Corrects the documented HID++ model_ids wire ordering and packing semantics, and aligns the MX Keys mock fixture with that contract.

  • Documents ascending transport-bit order: Bluetooth, BTLE, eQuad, then USB.
  • Clarifies that enabled transports are packed from the front rather than assigned fixed slots.
  • Reorders the MX Keys mock PIDs so its BTLE PID precedes its eQuad PID.

Confidence Score: 5/5

The PR appears safe to merge; the documentation and mock-data correction are consistent with the HID++ transport representation.

The changed ordering matches the protocol’s ascending transport-bit packing, and the reviewed consumers provide no concrete path where correcting this mock fixture causes an observable failure.

Important Files Changed

Filename Overview
crates/openlogi-core/src/device.rs The expanded documentation accurately describes the HID++ transport bits, packed PID ordering, skipped disabled transports, and three-entry limit.
crates/openlogi-agent/src/bin/mock_agent.rs The MX Keys fixture now places its BTLE PID before its eQuad PID, consistently with its enabled transports and the documented wire ordering.

Reviews (1): Last reviewed commit: "docs(core): correct the model_ids transp..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant