Skip to content

Support the Lingbao M5 Pro (PAW3395), and stop the Fantech driver inventing readings - #59

Merged
snekxs merged 2 commits into
mainfrom
pr49-fix
Sep 6, 2026
Merged

Support the Lingbao M5 Pro (PAW3395), and stop the Fantech driver inventing readings#59
snekxs merged 2 commits into
mainfrom
pr49-fix

Conversation

@snekxs

Copy link
Copy Markdown
Member

Rebase/merge of #49 onto current main, resolving additive brand-union conflicts in mouse-types.ts/registry.ts against #50/#53/#54/#55. Closes#49.

AIand others added 2 commits September 5, 2026 07:57
The Lingbao M5 Pro (PixArt PAW3395) enumerates under VID 0x3151 on the
very same 0xFFFF/0x02 vendor interface FantechHidClient claims, so the
registry handed it to that driver — which could not read a single value
from it, and reported fabricated ones rather than failing.
0x3151 belongs to MicLink/mlzn, the ODM, not to Fantech. The mouse is
configured by GearHub-V5 (qmk.top); its protocol was read out of that
bundle and then verified byte for byte against real hardware.
LingbaoHidClient implements the three things the Fantech driver lacks:
- The Bit7 checksum, byte[7] = 255 - sum(bytes 0..6). Without it the
device ACKs the write and answers 64 zero bytes, silently.
- The 2.4G relay. A receiver does not forward a command just because
one was written to it: select the target (0xF6 0x05), poll status
until ready (0xF7), send, then notice-read (0xFC) before reading
back. 0xF7 also carries link state and battery.
- The mouse-class command set (GET_DPI 0xD4 / SET_DPI 0x54) and its
seven-entry report-rate table, in which code 5 means 250 Hz. The
Fantech driver carries the six-entry keyboard table, where code 5
means 125 Hz instead.
Commands are serialised through a queue. The relay is stateful, so two
exchanges in flight at once interleave and corrupt each other —
readStatus() ran into this on its first concurrent read.
Scoped to the M5 Pro's two product ids (0x402D receiver, 0x4026 wired) so
it cannot shadow Fantech's own hardware, and FantechHidClient now leaves
those two ids alone; the registry's one-driver-per-device test enforces it.
Verified live on a Lingbao M5 Pro receiver: DPI stages 400/800/1600/3200/
6400/26000 with stage 2 active, 8000 Hz, battery 44%, firmware v3.03,
device id 2285. Bluetooth mode is not covered — over BLE the mouse moves
to a different usage page and a separate read path.
The same investigation showed FantechHidClient inventing data on any
device that does not answer, so this also fixes:
- readStatus() throws instead of returning 1600 DPI / 8000 Hz decoded
from an all-zero reply, letting the caller try the next driver
- setDpiForSlot() no longer zeroes every DPI slot it is not writing
- 250 Hz is no longer advertised when it cannot be encoded, so the UI
cannot render a button that throws on click
- the hardcoded "Wired (USB)" and the placeholder firmware string are
gone rather than mislabelling wireless units
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/drivers/mouse-types.ts
#	src/drivers/registry.ts
@snekxs
snekxs merged commit b93aa20 into mainSep 6, 2026
2 checks passed
@snekxs
snekxs deleted the pr49-fix branch September 6, 2026 08:10
Sign up for freeto 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

@snekxs