08-20-2026, 06:25 AM
CKB-NEXT: M65 RGB ULTRA
Linux driver support for Corsair M65 RGB Ultra — contributed upstream to ckb-next
C • Linux • Bragi Protocol • Reverse Engineering • Open Source
WHAT THIS IS
The Corsair M65 RGB Ultra is a wired gaming mouse (USB ID 1b1c:1b9e) that had zero Linux support. Every other M65 variant uses Corsair's legacy NXP protocol. The Ultra doesn't — it uses Bragi, a newer protocol that shares nothing with NXP at the wire level. Nobody knew this. The existing device request sat open on GitHub since 2022.
This contribution adds full support: cursor, all 8 buttons including sniper, scroll, DPI control, and RGB lighting across all three zones. It was submitted as PR #1332 to the ckb-next project.
4 files changed · +72 / -12 lines · USB ID 1b9e · issue open since 2022
THE DISCOVERY
The M65 RGB Ultra's firmware does something deceptive: it ACKs legacy NXP commands without applying them. You send an NXP lighting command, the device responds with a proper acknowledgment on the command endpoint (07 22 00 00 ...), and nothing happens. No error, no rejection — just silence on the hardware. This is why treating it as an NXP clone of the M65 RGB Elite never worked, and why the issue sat open for years.
The answer came from USB traffic captures with usbmon. NXP writes completed successfully with zero hardware effect. The Bragi handshake returned real data — firmware version, poll rate, brightness — where NXP returned all zeros or failures. The device is Bragi. Once that was established, the actual code changes were surgical.
DEVICE SPECIFICS
THE CODE
Five files, each handling a different layer of device support.
src/daemon/usb.h — device definition, macros, protocol registration
src/daemon/usb.c — device table, DPI range, product string
src/daemon/keymap.c — input translation, button LUT, HID parsing
src/daemon/led_bragi.c — RGB lighting, zone mapping, LED offset
TESTING
Developed and tested with an M65 RGB Elite and the Ultra connected simultaneously. Verified on hardware: cursor tracking, all 8 buttons including sniper hold with DPI shift and light change, scroll wheel, DPI stage switching, all three lighting zones with per-zone independent colors, GUI key rebinding — across daemon restarts and USB replugs. The NXP code path is completely untouched, so existing devices are unaffected by construction.
TRANSPARENCY
This was a human-driven hardware debugging effort done in collaboration with an AI assistant (Anthropic Claude). Every change was motivated by wire-level evidence — usbmon captures, daemon debug logs, button-by-button calibration — and verified on the physical hardware before the next step. The AI disclosure is included in the commit and PR description. This is how AI-assisted open source contribution should work: full transparency, real engineering, no black boxes.
VIEW PR #1332 ON GITHUB ↗
Linux driver support for Corsair M65 RGB Ultra — contributed upstream to ckb-next
C • Linux • Bragi Protocol • Reverse Engineering • Open Source
WHAT THIS IS
The Corsair M65 RGB Ultra is a wired gaming mouse (USB ID 1b1c:1b9e) that had zero Linux support. Every other M65 variant uses Corsair's legacy NXP protocol. The Ultra doesn't — it uses Bragi, a newer protocol that shares nothing with NXP at the wire level. Nobody knew this. The existing device request sat open on GitHub since 2022.
This contribution adds full support: cursor, all 8 buttons including sniper, scroll, DPI control, and RGB lighting across all three zones. It was submitted as PR #1332 to the ckb-next project.
4 files changed · +72 / -12 lines · USB ID 1b9e · issue open since 2022
THE DISCOVERY
The M65 RGB Ultra's firmware does something deceptive: it ACKs legacy NXP commands without applying them. You send an NXP lighting command, the device responds with a proper acknowledgment on the command endpoint (07 22 00 00 ...), and nothing happens. No error, no rejection — just silence on the hardware. This is why treating it as an NXP clone of the M65 RGB Elite never worked, and why the issue sat open for years.
The answer came from USB traffic captures with usbmon. NXP writes completed successfully with zero hardware effect. The Bragi handshake returned real data — firmware version, poll rate, brightness — where NXP returned all zeros or failures. The device is Bragi. Once that was established, the actual code changes were surgical.
DEVICE SPECIFICS
Quote:No Report ID prefix — HID mouse reports arrive raw: byte 0 is buttons, bytes 1–4 are X/Y as int16 little-endian, byte 5 is wheel. Every other mouse in the codebase has a Report ID prefix byte. New translation function required.
3 lighting zones — Logo, scroll wheel, DPI indicator. The device sends them in that order, but the shared M65E GUI layout expects a different mapping. Zone reorder array {0, 2, 1} handles the translation. Zone count cross-checked against OpenRGB's CorsairPeripheralV2 table.
Button mapping — All 8 buttons arrive in one byte in straight bit order. Calibrated by capture: 0x08 back, 0x10 forward, 0x20 DPI up, 0x40 DPI down, 0x80 sniper.
Wheel data — Only exists in the HID packet, not in the Bragi software packet. Device added to SW_PKT_HAS_NO_WHEEL macro.
THE CODE
Five files, each handling a different layer of device support.
src/daemon/usb.h — device definition, macros, protocol registration
Code:
// Product ID definition
#define P_M65_RGB_ELITE 0x1b5a
#define P_M65_RGB_ULTRA 0x1b9e // <-- NEW
#define IS_M65(kb) ((kb)->vendor == V_CORSAIR && ...)
// Added to IS_MOUSE macro
(product) == (P_M65_RGB_ULTRA) ||
// Added to SUPPORTS_ADJRATE macro
(kb)->product == P_M65_RGB_ULTRA ||
// New macro — devices with no HID Report ID prefix
#define HAS_HID_NO_REPORTID(kb) ((kb)->product == P_M65_RGB_ULTRA)
// Added to USES_BRAGI macro
(product) == (P_M65_RGB_ULTRA)
// Added to SW_PKT_HAS_NO_WHEEL macro (wheel only in HID packet)
(kb)->product == P_M65_RGB_ULTRAsrc/daemon/usb.c — device table, DPI range, product string
Code:
// DPI list — Ultra supports up to 26000
{ P_M65_RGB_ELITE, 18000 },
{ P_M65_RGB_ULTRA, 26000 }, // <-- NEW
// Device model table
{ V_CORSAIR, P_M65_RGB_ELITE, },
{ V_CORSAIR, P_M65_RGB_ULTRA, }, // <-- NEW
// Product string — shares "m65e" layout with the Elite
- if(product == P_M65_RGB_ELITE)
+ if(product == P_M65_RGB_ELITE || product == P_M65_RGB_ULTRA)
return "m65e";src/daemon/keymap.c — input translation, button LUT, HID parsing
Code:
// New function declaration
static void hid_mouse_translate_noreportid(usbinput* input, int length,
const unsigned char* urbinput);
// Bragi input routing — added no-reportid branch
if(buffer[1] == BRAGI_INPUT_HID && urblen == 64) {
corsair_bragi_mousecopy(targetkb, &targetkb->input, buffer);
} else if(HAS_HID_NO_REPORTID(targetkb)) {
// No Report ID prefix: buttons at byte 0, XY at bytes 1-4
hid_mouse_translate_noreportid(&targetkb->input, urblen, buffer);
} else {
// New translation function — raw HID with no Report ID
static void hid_mouse_translate_noreportid(usbinput* input,
int length, const unsigned char* urbinput){
if(length < 6){
ckb_err("Invalid length %d", length);
return;
}
for(int bit = 0; bit < BUTTON_HID_COUNT; bit++){
if(urbinput[0] & (1 << bit))
SET_KEYBIT(input->keys, MOUSE_BUTTON_FIRST + bit);
else
CLEAR_KEYBIT(input->keys, MOUSE_BUTTON_FIRST + bit);
}
input->rel_x += (urbinput[2] << 8) | urbinput[1];
input->rel_y += (urbinput[4] << 8) | urbinput[3];
input->whl_rel_y = (signed char)urbinput[5];
}
// Button lookup table — all 8 in straight bit order
const unsigned char m65_ultra_lut[BRAGI_ONE_BYTE_MOUSE_BUTTONS] = {
0x00,
0x01,
0x02,
0x03, // back
0x04, // forward
0x05, // dpi up
0x06, // dpi down
0x07, // sniper
};
// Added M65 Ultra to one-byte button device list + LUT routing
|| kb->product == P_M65_RGB_ULTRA))
else if(kb->vendor == V_CORSAIR && kb->product == P_M65_RGB_ULTRA)
lut = m65_ultra_lut;src/daemon/led_bragi.c — RGB lighting, zone mapping, LED offset
Code:
// LED zone count — 3 zones: logo, scroll wheel, DPI indicator
LED_CASE_M(P_M65_RGB_ULTRA, 3);
// Zone reorder — device order doesn't match GUI layout
if(kb->product == P_M65_RGB_ULTRA){
// Device zone order: logo, scroll wheel, dpi indicator
// GUI order: back, dpi, wheel — swap last two
static const uchar zmap[3] = {0, 2, 1};
bytes *= 3;
for(size_t i = 0; i < zones; i++){
pkt[7 + i] = newlight->r[led_offset + zmap[i]];
pkt[7 + zones + i] = newlight->g[led_offset + zmap[i]];
pkt[7 + zones * 2 + i] = newlight->b[led_offset + zmap[i]];
}
}
// LED offset — M65E layout has no "front" zone, start at back
- return updatergb_bragi(kb, force, LED_MOUSE);
+ const size_t led_offset = LED_MOUSE +
+ (kb->product == P_M65_RGB_ULTRA ? 1 : 0);
+ return updatergb_bragi(kb, force, led_offset);TESTING
Developed and tested with an M65 RGB Elite and the Ultra connected simultaneously. Verified on hardware: cursor tracking, all 8 buttons including sniper hold with DPI shift and light change, scroll wheel, DPI stage switching, all three lighting zones with per-zone independent colors, GUI key rebinding — across daemon restarts and USB replugs. The NXP code path is completely untouched, so existing devices are unaffected by construction.
TRANSPARENCY
This was a human-driven hardware debugging effort done in collaboration with an AI assistant (Anthropic Claude). Every change was motivated by wire-level evidence — usbmon captures, daemon debug logs, button-by-button calibration — and verified on the physical hardware before the next step. The AI disclosure is included in the commit and PR description. This is how AI-assisted open source contribution should work: full transparency, real engineering, no black boxes.
VIEW PR #1332 ON GITHUB ↗
— Z E R O S T O H E A V E N ! —
Photonamus Industries • Founder
Photonamus Industries • Founder
.png)