atk: decode PAW3395 DPI correctly and identify VXE mice by CID/MID - #44
Open
SakethKanchi wants to merge 1 commit into
Open
atk: decode PAW3395 DPI correctly and identify VXE mice by CID/MID#44SakethKanchi wants to merge 1 commit into
SakethKanchi wants to merge 1 commit into
Conversation
The ATK driver applied the A9's PAW3950Ultra DPI encoding to every device behind vendor id 0x373b. ATK HUB Web 3.2.21 branches that decoder per sensor: PAW3950/PAW3950DM/PAW3395/PAW3395Ultra pack a plain 10-bit count of 50-DPI steps instead. A PAW3395 stage read with the A9 encoding is a fifth of the real value. A receiver's product id cannot say which sensor a mouse has: 0x373b:0x1085 is a generic "Wireless mouse 1k dongle" reused across models, and the R1 alone ships with PAW3311, PAW3395, PAW3395SE, PAW3395Ultra, PAW3950Ultra and CORE26K. So identify the mouse the way the vendor's own HUB does, with GetMouseCIDMID (command 0x10), and look the pair up in a catalog that carries the sensor. - add per-sensor profiles and the step-50 codec to src/atk - add src/drivers/atk/products.ts, keyed "<cid>,<mid>" - pick the DPI encoding, ceiling and DPI ladder from the sensor, for reads and writes alike; an unidentified mouse keeps the A9 behaviour, matching the HUB's own fallback - report VXE as its own brand via deviceBrand(), as Lamzu/CRDRAKO already do - claim VXE's wired transport (0x3554:0xf58f, "Compx VXE R1"), which speaks the same 0xff02/report-0x08 channel. Product-id gated: 0x3554 is shared with the VGN Dragonfly F2, so the id also joins the Pulsar fallback's CLAIMED_VGN_PRODUCT_IDS - decode the rest of GetBatteryLevel: the reply carries a charge flag and a cell voltage next to the percentage, so report batteryState and batteryVoltageMv instead of a hardcoded "Unknown" - treat an unprogrammed angle register as unsupported. It reads 0xff and fails the (value, 0x55-value) pair, but decoded as -1 degrees - retry identification instead of caching a timeout, so a mouse that was asleep is not left misnamed and mis-decoded for the session Sensors whose DPI mapping is a lookup table (PAW3395SE, PAW3315, PAW3311, PAW3320) are deliberately not implemented; those tables are not captured here and a guessed step would misreport DPI silently. Verified on a VXE R1 (CID/MID 2,12, PAW3395, firmware Mouse 3.13) over both transports: DPI now reads 1600 rather than 320, ceiling 30000 rather than 42000, and the mouse names itself "VXE R1". Battery was captured across a charge cycle: flag 0 with a steady voltage on battery, flag 1 with the voltage climbing on the cable. Writes were not exercised. See docs/atk-testing.md.
SakethKanchi
force-pushed
the
feat/vxe-r1-paw3395
branch
from
September 4, 2026 19:21
dc0a321 to
3152e56
Compare
Author
|
App-side counterpart: OpenMouse-Project/openmouse#156 (adds the supported-devices row). This one should land first — until it does, that row is only true against a local build. |
This was referenced Sep 5, 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.
What this fixes
The ATK driver applied the A9's PAW3950Ultra DPI encoding to every device behind
vendor id
0x373b. ATK HUB Web 3.2.21 branches that decoder per sensor:A PAW3395 stage read with the A9 encoding is a fifth of the real value: my
1,600 DPI stage reported as 320.
Why a product-id map would have been wrong
A receiver's product id cannot tell you the sensor.
0x373b:0x1085is a generic"Wireless mouse 1k dongle"reused across models, and the R1 alone ships withsix different sensors across revisions (PAW3311, PAW3395, PAW3395SE,
PAW3395Ultra, PAW3950Ultra, CORE26K) — with different encodings and ceilings.
Even the model name is not enough.
So this identifies the mouse the way the vendor's own HUB does:
GetMouseCIDMID(command
0x10, from the HUB's command enum, which also confirms this repo'sexisting
0x04/0x08/0x12), looked up in a catalog keyed"<cid>,<mid>"thatcarries the sensor.
Changes
src/atksrc/drivers/atk/products.ts, keyed"<cid>,<mid>"alike — a sensor-aware decode with a legacy encode would corrupt
setDpiso no existing ATK device changes behaviour
deviceBrand(), as Lamzu/CRDRAKO already do0x3554:0xf58f, "Compx VXE R1") claimed — same0xff02/report-0x08channel, previously unsupported becauseisSupported()gated on
vendorId === atk. Product-id gated, since0x3554is shared with theVGN Dragonfly F2; the id also joins the Pulsar fallback's
CLAIMED_VGN_PRODUCT_IDS(the registry overlap test caught this immediately —vid 0x3554 pid 0xf58f: Pulsar + ATK)0x00bdreadsff ff ff ffand fails the(value, 0x55-value)pair, but decoded as -1degrees of angle tuning
answers nothing, and caching that left a woken mouse misnamed and mis-decoded
for the rest of the session
Battery
GetBatteryLevelcarries a charge flag and a cell voltage next to thepercentage, so
batteryStateandbatteryVoltageMvare now reported instead ofa hardcoded
"Unknown". Captured across a charge cycle: flag0with a steady3,786 mV on battery, flag
1with the voltage climbing 3,893 → 3,938 mV on thecable, and flag
0again at 3,875 mV after unplugging. A non-zero flag meanscharging; the HUB tests
=== 2on some families, which is also non-zero.Note the percentage legitimately drops on unplug (70% at 3,969 mV charging vs
50% at 3,875 mV resting): the mouse derives percent from cell voltage, and a
charging cell reads high. That is the mouse's own reporting, documented so it is
not mistaken for a decode bug.
Deliberately not implemented
Sensors whose DPI mapping is a lookup table (PAW3395SE, PAW3315, PAW3311,
PAW3320). Those tables are not captured here, and a guessed step would misreport
DPI silently. Mice with those sensors keep the existing fallback.
Polling rates are left at the full 125-8,000 Hz ladder. The R1 ships with a 1K
receiver but an 8K receiver is sold separately and works with the same mouse, so
the ceiling belongs to the receiver in use, not the model. Capping by product id
would be wrong.
Hardware verification
VXE R1, CID/MID
2,12, sensor PAW3395, firmwareMouse 3.13, on Linux via/dev/hidraw*and in Chrome via WebHID. Verified over both transports(
0x373b:0x1085receiver and0x3554:0xf58fwired), reporting the same identity,DPI and firmware through each:
ATKVXEATK Wireless mouse -1k dongleVXE R132016004200030000-1null(unsupported)DPI stages at EEPROM
0x000c..0x001bread0f 0f 00 37/17 17 00 27/1f 1f 00 17/3f 3f 00 d7→ 800 / 1200 / 1600 / 3200 DPI, which the mouse'sown DPI button cycles through. Cycling to stage 1 on hardware moved the UI to
1,200 DPI, matching
(0x17 + 1) * 50.Firmware, lift-off, debounce, motion sync, ripple control and sleep all read
correctly; battery is covered above. Writes were not exercised, so
verified: truein the catalogrecords the identity and read path only. Details in
docs/atk-testing.md.npm run checkpasses (818 tests). OpenMouse builds and its full suite passesagainst this package locally (112 tests); the matching app-side PR is linked below.
Unknowns left documented
GetMouseCIDMIDreply (0x47on this unit) is unidentified.0xf58fis reused by the vendor for the R1SE/R1SE+ (PAW3395SE), which is why thesensor must come from CID/MID and not that id.
device-images.tsmatches by display-name regex and has no R1asset, so
VXE R1falls back tounknown-device.png. I did not point it atanother model's render.