Skip to content

feat(corsair): read-only Bragi driver for the IRONCLAW RGB WIRELESS - #178

Merged
snekxs merged 1 commit into
OpenMouse-Project:mainfrom
ydw1904:feat/corsair-bragi
Oct 8, 2026
Merged

snekxs merged 1 commit into
OpenMouse-Project:mainfrom
ydw1904:feat/corsair-bragi

Conversation

@ydw1904

@ydw1904 ydw1904 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

For OpenMouse-Project/openmouse#496 (capture from Discord ticket 0159). App wiring: OpenMouse-Project/openmouse#664 (draft). Catalog: OpenMouse-Project/openmouse-landing-page#36.

What the mouse is

The IRONCLAW RGB WIRELESS speaks Corsair's Bragi protocol (ckb-next's name; OpenRGB calls it Corsair Peripheral V2), not the NXP feature-report protocol the NIGHTSWORD driver uses. A request is a 64-byte output report with no report id on the 0xFF42 interface (interface 1, interrupt endpoints 0x04/0x84): [0x08 | slot] [command] [property] [0x00] [value LE]. The answer comes back on the same interface as [slot] [command] [status] [value LE]. Slot 0 is the device on the cable; a SLIPSTREAM receiver relays slots 1-7 to the devices paired to it.

The capture went through receiver 0x1b1c:0x1bdc, which OpenRGB names after the M75 Wireless and OpenLinkHub lists as a generic SLIPSTREAM receiver. The mouse behind it is the Ironclaw: iCUE's slider values only fit a 100 to 18,000 scale (208 steps of 86.06 DPI), and the M75 Wireless goes to 26,000.

What this adds

  • src/corsair/bragi.ts, re-exported from @openmouse/protocol/corsair: constants, corsairBragiEncode.get / .setDpi, and decoders for replies, firmware, polling rate, battery and the receiver's slot bitfield. Tests re-encode all 202 iCUE frames from the capture byte for byte and check every ack.
  • src/drivers/corsair/bragi-hid.ts: read-only CorsairBragiHidClient for the mouse on the cable (0x1b4c) and two receivers (0x1b66 from ckb-next, 0x1bdc from the capture). Through a receiver it reads the connected-slot bitfield (0x36) first and talks to the lowest connected slot, or slot 1 (the one iCUE used) if that read fails. It then reads product id, firmware, mode, polling rate, battery and live DPI X/Y, each best-effort. settingsReady: false, getDpiOptions() returns [], and it never sends a SET.
  • Registry entry, CORSAIR_BRAGI_HID_FILTERS (VID, PID, usage page 0xFF42), and 0xFF42 in the registry probe matrix.
  • captures/corsair-ironclaw-rgb-wireless/: the vendor-channel frames, the receiver's USB descriptors, and a README with each file's trust level.

What is deliberately missing

  • Writes. setDpi is encoded and matches iCUE's frames, but iCUE was driving the mouse (software mode) during the capture. ckb-next and OpenLinkHub both switch Bragi mice to software mode before touching them, and nothing shows yet whether DPI X/Y read back and stick in hardware mode with iCUE closed. The driver does not change the mode either: a mouse left in software mode stops applying its onboard settings.
  • Any GET reply from this mouse. The capture starts after iCUE's setup, so the reply layout and every property other than 0x21/0x22 come from ckb-next, OpenRGB and OpenLinkHub, which agree with each other, not from hardware.
  • The HID report descriptors, so the picker filter cannot narrow by collection usage. Interface 2 is 0xFF42 as well (input only), so Chrome may list two receiver entries; isSupported only accepts the collection that has output reports.
  • Wired mode and receiver 0x1b66 are included on ckb-next and OpenRGB evidence, with no traffic seen. DPI stages, polling writes, lift-off, angle snap, sleep and lighting were not captured.

Verification

npm run check: build clean, 2179 pass, 0 fail (registry probe matrix included), pack dry-run OK. Not tested on hardware. Next from the owner: a capture that includes a receiver replug (report descriptors), iCUE starting and quitting (mode switches and reads), and one DPI stage and one polling-rate change.

Corsair's newer mice speak Bragi (ckb-next's name, OpenRGB's Corsair
Peripheral V2): 64-byte output reports on the 0xFF42 interface, byte 0 is
0x08 | slot, where slot 0 is the device on the cable and slots 1-7 are the
devices paired to a SLIPSTREAM receiver.

The codec (src/corsair/bragi.ts, re-exported from the corsair entry point)
re-encodes all 202 frames of a community iCUE capture byte for byte: DPI X
and Y writes to slot 1 through receiver 0x1bdc, Discord ticket 0159. GET
replies were not captured; their layout and the property ids follow
ckb-next, OpenRGB and OpenLinkHub, which agree with each other.

CorsairBragiHidClient reads identity, firmware, mode, polling rate, battery
and live DPI over the cable (0x1b4c) or a receiver (0x1b66, 0x1bdc) and
reports settingsReady false. It never writes and never changes the
hardware/software mode: the DPI write is encoded and tested, but stays out
of the driver until a capture shows it sticks with iCUE closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@snekxs
snekxs force-pushed the feat/corsair-bragi branch from 82f8872 to ac1b6d8 Compare October 8, 2026 06:55
snekxs added a commit to ydw1904/mouse-protocol that referenced this pull request Oct 8, 2026
@snekxs
snekxs force-pushed the feat/corsair-bragi branch from 8f5ec97 to ac1b6d8 Compare October 8, 2026 07:59
@snekxs
snekxs merged commit 9e18680 into OpenMouse-Project:main Oct 8, 2026
18 of 20 checks passed
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.29.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants