feat(rapoo): add the VT9 Pro driver for the 0x12xx/0x44xx generation - #147
Merged
Merged
Conversation
The VT9 Pro (0x1205 receiver, 0x4405 cable) answers on its own vendor collection FF00:000E: a 0xBA output frame carries the register address, and the answer to it only ever shows up in an input report. WebHID has no GET_REPORT(Input), so this driver reads its registers through the transport when it exposes receiveInputReport (OpenMouse Bridge) and, when it does not, reports the battery the mouse pushes on 0xBB by itself rather than pretending the registers are readable. Every byte in src/rapoo and src/drivers/rapoo was read off one mouse with HidD_GetInputReport - wired and through the receiver, both links answering byte-for-byte the same - and cross-checked against the vendor driver (RapooGameDevDriver 1.6.29) and pedro3z0/rapoo-software-linux. The frame length is 31 bytes of report data behind the report id; Windows counts the id in the 32-byte buffer, and the browser rejects 32 as oversized. Reads only: the driver has no write path, and it never asks for feature report 0x2B, which drops the link until the cable is pulled. Verified on hardware: polling 4000 Hz, DPI 800 of 7 stages (stage 2), motion sync on, debounce 16 ms, sleep 7200 s, battery 100 %, over both USB paths.
# Conflicts: # src/drivers/mouse-types.ts # src/drivers/registry.ts
|
🎉 This PR is included in version 0.21.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
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.
Device
0x24AE:0x1205(2.4 GHz receiver) and0x24AE:0x4405(cable)usagePage 0xFF00, usage 0x000E0xBA, 31 bytes of report dataGET_REPORT(Input), not delivered as an interrupt IN reportWhy this driver is read-only in a browser
The answer to a read never arrives on its own: it has to be fetched with
GET_REPORT(Input), which isHidD_GetInputReporton Windows,HIDIOCGINPUTon Linux hidraw and
receiveInputReportin OpenMouse Bridge. WebHID has no suchcall, so with WebHID alone the register channel is unreachable - not broken, just
unreadable. The client therefore reads its registers when the transport exposes
receiveInputReportand, when it does not, reports only the battery the mousepushes on its own (
0xBB,FF00:0002, about every 3 s:B0 51 20 03 <link> <percent>). It does not pretend the registers are readable.What this adds
src/rapoo/index.ts- the codec: frame layout, commands, the busy/ok answer,the battery marker, and decoders for the performance, sensor, DPI, stage and
timing blocks plus the
0xBBnotification.src/drivers/rapoo/hid.ts-RapooHidClient: discovery of the configcollection (including inside a child collection), the busy -> ok handshake,
per-read resend, the register walk, and the fallback to the notification.
src/index.ts, README and tsconfigwiring.
dropped frame that is sent again, a register the first pass lost that is read
again, every decoder, and the browser-only path that sends nothing.
docs/rapoo-vt9-pro-testing.mdand the sanitized capture incaptures/rapoo-vt9-pro/.Evidence
(
captures/rapoo-vt9-pro/sweep-2026-09-29.txt);rapoo_vt3prodriver, reverse engineered from Rapoo's ownRapooGameDevDriver1.6.29 - the same installer build the capture wastaken with. Same frame, same offsets, for the VT3 PRO;
rapoo-software-linux'sPROTOCOL.md(from A HUB 1.0.19) describes a thirdRapoo generation whose addresses line up after subtracting the profile-0 base
of
0x600.The three agree on the channel; everything the driver decodes was then measured
on the mouse.
Verified on hardware
readStatus()on this mouse, over the cable and over the receiver, 14 runs withthe same result:
valuesVerified: trueTwo details a reviewer should know
HidP_GetCapsreports 32 because itcounts the report id; the browser rejects a 32-byte payload as oversized and
answers the 31-byte one.
0x2Bis never read. Reading it once drops the link untilthe cable is pulled (
0xC00000E5), so it is refused in the client and excludedfrom the probes. It is a distinct report id from the
0x2Astatus block.Safety
The client has no write path at all - it encodes reads, the battery query and
nothing else - and the register walk is idempotent.
Companion PR
OpenMouse-Project/openmouse#422wiresRapooHidClientinto the controllerso the device is opened on connect. It needs
@openmouse/protocol0.20.0ornewer; the OpenMouse repository bumps that dependency from the published release,
so it should land after this one is released.