Repository navigation
fix(devices): identify receiver mice by WPID and full name; add MX Master 3S For Business - #176
Merged
Merged
Conversation
…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.
This was referenced Nov 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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::readNamenow works like Solaar'shidpp20.get_name(): each request starts where the previous reply ended, until the name is complete.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 newhidpp::ReceiverInforeads the paired device's WPID from receiver register0x2B5, as Solaar'sdevice_pairing_information()does:0x50 + slot, WPID little-endian.0x20 + slot − 1, WPID big-endian.DeviceSession::resolveDescriptoruses that WPID on receivers and the hidraw PID on Bluetooth. The receiver's own PID is never used for the lookup.findByNametook the firstcontains()hit in folder order, somx-master-3beatmx-master-3s. It now prefers an exact (case-insensitive) match, then the longest contained descriptor name.Two related changes the fix needed:
0xB020, so a plain WPID lookup would show the Business model as the retail one. The newDeviceRegistry::findByPidAndNamelets the reported name pick between descriptors that share a PID. This fixes the same mix-up over Bluetooth too.mx-master-3IDs: 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 PID0xB023and Unifying WPID0x4082(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(WPID0xB035): 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:
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 aDeviceSpecfor MX Master 3S For Business.logitune-tests811/811,logitune-tray-tests9/9,logitune-qml-tests72/72, andgenerate-readme-devices.py --checkall pass on top ofa50311f.🤖 Generated with Claude Code