Skip to content

fix(devices): identify receiver mice by WPID and full name; add MX Master 3S For Business - #176

Merged
mmaher88 merged 1 commit into
masterfrom
claude/loving-cerf-dd834b
Nov 9, 2026
Merged

mmaher88 merged 1 commit into
masterfrom
claude/loving-cerf-dd834b

Conversation

@mmaher88

@mmaher88 mmaher88 commented Nov 9, 2026

Copy link
Copy Markdown
Owner

Summary

Mice on a receiver could be matched to the wrong device descriptor. An MX Master 3S For Business on a Bolt receiver showed up as MX Master 3: DPI capped at 4000 instead of 8000, and the wrong back/forward defaults. Three bugs combined to cause this:

  1. Device names over 16 characters were garbled. getName (feature 0x0005, fn 1) takes a single byte, the index of the first character to return. We sent two bytes ({0, offset}) and stepped by 13, so the device always answered from index 0 and logged "MX Master 3S ForMX Master". DeviceName::readName now works like Solaar's hidpp20.get_name(): each request starts where the previous reply ended, until the name is complete.

  2. Receiver mice were never looked up by their own ID. The lookup used the hidraw node's PID, which on a receiver is the receiver's (Bolt 0xC548), so every receiver mouse fell back to name matching. The new hidpp::ReceiverInfo reads the paired device's WPID from receiver register 0x2B5, as Solaar's device_pairing_information() does:

    • Bolt: sub-register 0x50 + slot, WPID little-endian.
    • Unifying: sub-register 0x20 + slot − 1, WPID big-endian.

    DeviceSession::resolveDescriptor uses that WPID on receivers and the hidraw PID on Bluetooth. The receiver's own PID is never used for the lookup.

  3. findByName took the first contains() hit in folder order, so mx-master-3 beat mx-master-3s. It now prefers an exact (case-insensitive) match, then the longest contained descriptor name.

Two related changes the fix needed:

  • Shared PIDs: MX Vertical and MX Vertical for Business both use 0xB020, so a plain WPID lookup would show the Business model as the retail one. The new DeviceRegistry::findByPidAndName lets the reported name pick between descriptors that share a PID. This fixes the same mix-up over Bluetooth too.
  • mx-master-3 IDs: the descriptor listed the Unifying receiver PID (0xc52b) as its product ID. That only worked because of the old receiver-PID lookup, and it matched any mouse on a Unifying receiver. It now lists the MX Master 3's Bluetooth PID 0xB023 and Unifying WPID 0x4082 (both from Solaar's descriptors). A new test checks that no shipped descriptor lists a receiver PID.

Also adds devices/mx-master-3s-for-business (WPID 0xB035): the 3S descriptor and images, matching how MX Anywhere 3S for Business relates to MX Anywhere 3S. It's marked beta. The README table is regenerated, and the wiki pages Adding-a-Device and Architecture now describe WPID-based matching.

Hardware check

Two mice, each on its own Bolt receiver. App log from this branch's build:

device name: "MX Master 3S"
receiver slot 1 WPID: b034
matched device descriptor: "MX Master 3S"

device name: "MX Master 3S For Business"
DPI range: 200 - 8000 step: 50
receiver slot 2 WPID: b035
matched device descriptor: "MX Master 3S For Business"

Before (master): device name: "MX Master 3S ForMX Master" → matched device descriptor: "MX Master 3".

Tests

The new tests were written first and failed against the old behaviour:

  • DeviceName.ReadName*: one-byte character index per fragment, full 25-character name, stops when a fragment fails.
  • ReceiverInfo.*: Bolt and Unifying request bytes, WPID byte order, wrong-slot, wrong-register and empty-slot replies.
  • DeviceSessionTest.*Resolve*, ReceiverPidNeverSelectsADescriptor, SharedPidResolvedByName: WPID-based lookup and the name tie-break.
  • DeviceRegistry.FindByNamePrefers*, NoDescriptorClaimsAReceiverPid, and a DeviceSpec for MX Master 3S For Business.

logitune-tests 811/811, logitune-tray-tests 9/9, logitune-qml-tests 72/72, and generate-readme-devices.py --check all pass on top of a50311f.

🤖 Generated with Claude Code

…ster 3S For Business

Receiver-attached mice were matched to the wrong descriptor. An MX Master
3S For Business on Bolt showed up as "MX Master 3" (DPI capped at 4000,
wrong back/forward defaults). Three bugs combined:

- getName (0x0005 fn 1) takes a one-byte character index. We sent two
  bytes ({0, offset}) and stepped by 13, so the device always answered
  from index 0 and any name over 16 chars came back garbled
  ("MX Master 3S ForMX Master"). Now reads like Solaar's get_name():
  request from the end of the previous fragment until the name is whole.

- Descriptors were looked up by the hidraw node's PID, which on a
  receiver is the receiver's own (Bolt 0xC548), so receiver mice always
  fell back to name matching. Now the paired device's WPID is read from
  receiver register 0x2B5 (Bolt sub 0x50+slot, little-endian; Unifying
  sub 0x20+slot-1, big-endian), as Solaar does, and used for the lookup.
  When several descriptors share a PID (MX Vertical and MX Vertical for
  Business, both 0xB020), the reported name picks between them.

- findByName returned the first contains() hit in folder order, so
  "MX Master 3" won over "MX Master 3S". It now prefers an exact match,
  then the longest contained descriptor name.

The MX Master 3 descriptor listed the Unifying receiver PID (0xC52B) as
its product ID, which only worked because of the receiver-PID lookup and
would match any Unifying mouse. It now lists its Bluetooth PID 0xB023
and Unifying WPID 0x4082 (from Solaar).

Adds devices/mx-master-3s-for-business (WPID 0xB035), a copy of the 3S
descriptor and images, mirroring how MX Anywhere 3S for Business relates
to MX Anywhere 3S. Marked beta until verified on hardware.
@mmaher88
mmaher88 merged commit f6b1431 into master Nov 9, 2026
6 checks passed
@mmaher88
mmaher88 deleted the claude/loving-cerf-dd834b branch November 9, 2026 13:03
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