SteelSeries Rival 3 (Gen 1, wired) -- HID
Devices: 1038:1824 and 1038:184C (firmware v0.37+ revision, same
protocol; findings below verified on real 184C hardware).
Cross-checked against the rivalcfg
rival3 profile; the ACK behaviour, report size and firmware response format
were confirmed by probing.
USB interfaces
Same four-interface layout as the Gen 2 (mouse input, keyboard, vendor
0xFF00, vendor 0xFFC0), but the vendor reports are 32 bytes, not 64.
Configuration goes to interface 3 as unnumbered HID output reports: write()
= 0x00 report number + 32-byte payload (33 bytes total).
Command acknowledgement: none
Unlike the Gen 2, this firmware does not echo commands on the interrupt IN endpoint. Only the firmware version query answers. Consequence: the driver sends fire-and-forget and cannot detect whether a command was accepted.
Commands (output reports, interface 3)
Every command starts with <cmd> 0x00.
0x0B 0x00 -- DPI presets
0x0B 0x00 <count> <active> <v1> [<v2> ...]
One byte per preset (no separate X/Y), same TrueMove Core table as the Gen 2
(200--8500 DPI, step 100, byte ~= dpi / 43.1). active is 1-based.
As on the Gen 2, the presets are onboard CPI levels only: the CPI button cycles the active CPI value, while colors, effects, polling rate and button bindings are one global configuration shared by every level. Pressing the button flashes a fixed, firmware-chosen indicator color for the new level before the configured lighting returns -- a level indicator, not per-level lighting.
0x04 0x00 -- polling rate
0x04 0x00 <rate> with the same encoding as Gen 2: 1000 Hz -> 0x01,
500 -> 0x02, 250 -> 0x03, 125 -> 0x04.
0x05 0x00 -- zone color (one zone per write)
0x05 0x00 <zone> <R> <G> <B> <brightness>
zone
0x01-- strip topzone
0x02-- strip middlezone
0x03-- strip bottomzone
0x04-- logozone
0x00-- all zones at once
brightness is 0x00-0x64 (0-100 %) and rides in every color write --
there is no separate global brightness command.
Hardware-verified (#38): any 0x05 write cancels the running global
effect on every zone, freezing the animation in place. Effects therefore
have to be applied colors-first, effect-selector-last, and a brightness
change (which is just another round of color writes) must resend the effect
too.
0x06 0x00 -- global light effect
0x06 0x00 <effect>
effect |
byte |
|---|---|
rainbow shift |
0x00 |
breath fast |
0x01 |
breath |
0x02 |
breath slow |
0x03 |
steady |
0x04 |
rainbow breath |
0x05 |
disco |
0x06 |
Global (all zones), unlike the Gen 2 per-zone rainbow mask. The breathing modes pulse the configured zone colors.
0x09 0x00 -- save to onboard flash
Commits the live configuration to persistent memory. One save commits everything: the flash configuration is global (single set of colors, effects, polling and bindings plus the CPI level table); there are no per-level profile slots.
0x10 0x00 -- firmware version
Responds with two raw bytes <major> <minor>; the probed unit answered
0x27 0x00 -> version 39.0.
Not supported by this hardware
Per-zone rainbow masks, reactive click color and the power-up lighting selector
are Gen 2 features; this firmware has the global effect list instead.
Probed on hardware while debugging #24: the Gen 2 reactive command (0x26,
sent both as 0x26 0x00 ... in this firmware's framing and in the raw Gen 2
layout) is silently ignored - no flash, no lighting change. rivalcfg's mature
rival3 profile defines no reactive setting either.
Acceleration / deceleration / angle snapping are not onboard here either, as on the Gen 2: SteelSeries applies them host-side, not in firmware, and alloyctl does the same via its pointer-transform daemon (see Host-side pointer transform (accel / snap)).