Skip to content

ATK ZERO (0x373b:0x1154 / 0x1155): a second ATK protocol the 0xff02 filter cannot reach #48

Description

@itsnttt

The device

USB id Firmware Path
ATK ZERO, wired 0x373b:0x1154 mouse 3.0.3 direct
ATK ZERO, 8K receiver 0x373b:0x1155 mouse 3.0.3, receiver 3.0.2 via receiver

Sensor PAW3950 Ultra. Both report cid,mid = 1,1.

Why it is invisible today

SUPPORTED_HID_FILTERS asks ATK for { 0x373b, usagePage 0xff02, usage 2 }, and
AtkHidClient.isSupported requires the same collection. An ATK ZERO exposes no
0xff02 at all. Its collections, read from the device:

0xff00 / 0x0001   feature #3, 63 B
0xff01 / 0x0001   feature #4, 63 B
0xff05 / 0x0001   input #8 + output #8, 63 B     <- the config channel

So the mouse never reaches the browser's device picker, and vendor id alone was
never enough to conclude coverage of this family.

The protocol

ATK's current configurator calls the family BITMOUSE. It is unrelated to the
16-byte EEPROM protocol in src/atk/ — 63-byte frames on report 0x08:

request  [0] checksum = Σ bytes 1..62 & 0xff
         [1] 0x72   [2] paramLen  [3] cmdSn  [4] target
         [5] commandId            [6] cmdLen [7..] payload

reply    no checksum byte, so fields sit one earlier:
         [0] 0x72   [1] status (0xff = error)  [2] cmdSn  [3] target
         [4] commandId            [5] payload length      [6..] payload

A receiver answers its own commands on target 0 and relays mouse commands on
target 1.

Two firmware behaviours that bit me, both now pinned by tests:

  • Replies are not zero-padded. Bytes past the reported length are the
    previous exchange's leftovers, e.g. GetDeviceType answering
    72 00 3a 00 1a 01 | 03 | ff ff 00 00 00 01 01 01 08 07 04 where only 03
    is valid.
  • The DPI table's count byte is the table size, not the enabled count. It
    reads 8 on a mouse holding two configured stages; empty slots read 0 DPI while
    keeping stale colour bytes.

Verified on hardware

Reads on both transports: version, cid/mid, device type, battery, charging
state, config block, DPI stage table.

Writes, each confirmed by reading back and then restored: polling rate
2000 → 1000 Hz, DPI 800 → 1600, debounce 4 → 8 ms, sleep 1800 → 600 s, motion
sync and ripple control on → off.

What I could not establish

  • Lift-off distance. The config byte reads 0 on both transports, and the
    vendor's own accessors overlap a 16-bit DPI value at +3 with an 8-bit lift-off
    byte at +4. I left it unreported rather than guess.
  • Angle snapping. A SetSensorAngle command exists; not exercised.
  • GetChipId (71) and GetMouseOnlineStatus (35) both answer zero on
    hardware that is plainly online.
  • cmdSn is a constant 0x3a in every reply captured; meaning unknown.

Evidence

Framing, command ids, field offsets and request lengths came from the vendor's
own WebHID configurator (ATK HUB v3.2.21, served publicly from hub.atk.pro),
and every value used was then confirmed against the device. No vendor code is
copied.

The mouseCidMid command (74) is the same identification route as the open
#44, approached from this protocol's side.

Proposed shape, before I open a PR

A codec under src/bitmouse/ (framing, command table, config and DPI decoders,
sensor DPI ranges, product table), a driver at
src/drivers/atk/bitmouse-hid.ts, registry entry, and PID-scoped filters. Only
the two verified products are claimed; the same protocol drives much of the
current ATK/VXE range, but each model wants its own hardware check.

Branch, if it is easier to read than prose:
https://github.com/itsnttt/mouse-protocol/tree/bitmouse-atk-zero

npm run check is clean there (843 tests), and openmouse builds and passes
its 112 tests against the local package. registry.test.ts needed 0xff05
added to USAGE_PAGES, exactly as its own assertion message instructs.

Two things I would rather agree before opening the PR:

  1. Where the codec belongs. I put it at src/bitmouse/ on the reading that
    this is a shared OEM family like src/compx/, not an ATK-only thing. If you
    would rather it live under src/atk/ as a second module, that is an easy
    move.
  2. How far to claim. I scoped the product table to the two ids I own. If you
    would prefer the filter to be vendor-wide on 0xff05 and let the driver
    sort it out at runtime, say so — I deliberately avoided claiming models I
    cannot test.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions