Protocol Research Methodology
How to research and document device protocols for Hypercolor driver development. The vendor's own software + USB captures are the ground truth. Community protocol documentation, wikis, and open-source projects in the RGB ecosystem can provide additional context.
Research Sources (Priority Order)
- USB traffic captures — Capture the vendor's Windows/macOS software communicating with the device. This is ground truth. See the capture workflow below.
- Vendor documentation — Some vendors publish SDK docs or protocol specs (rare but invaluable when available).
- Community protocol documentation — Wikis, blog posts, forum threads where people have documented wire formats. Search for the device name + "protocol" or "reverse engineer."
- Open-source RGB projects — Projects like liquidctl, openrazer, and others in the RGB ecosystem may have documented protocol details for specific device families. Study their docs and protocol descriptions to understand how the device communicates. Always write clean Hypercolor implementations using our own architecture.
- Existing Hypercolor drivers — If a similar device family already has a driver (e.g., adding a new Razer variant when
razer/protocol.rsexists), start from our own code.
USB Traffic Capture Workflow
- Set solid red, capture, then set solid green — diffing these two captures isolates exactly which bytes carry color data and reveals byte ordering (RGB vs RBG vs BGR)
- Identify checksum bytes — bytes that change between the two captures but aren't in color positions. XOR the full packets to spotlight them
- Verify color byte ordering — red capture should show
0xFFin R positions and0x00in G/B; green capture inverts this. If R and B swap, the device uses RBG or BGR - Note inter-packet timing — capture timestamps reveal required
post_delayvalues between commands - Capture the init sequence — record what happens when the vendor software first opens the device (before setting any colors). This reveals firmware queries, mode switches, and topology discovery
- Capture shutdown — what the software sends when it exits (restores hardware control mode)
What to Document
For each protocol, produce a spec in docs/specs/ covering:
- Packet layouts — byte-by-byte diagrams for each command type
- Command vocabulary — init, firmware query, color data, commit, brightness, shutdown
- Color encoding — byte order (RGB/RBG/BGR), max LEDs per packet, packing format
- Checksums — algorithm, which bytes are covered, verification examples
- Timing — inter-packet delays, frame intervals, response timeouts
- Topology — LED counts per zone, addressing scheme (linear/matrix/ring), variant differences
- Firmware variants — which firmware versions use which protocol, how to detect
C++ → Rust Translation Patterns
When studying open-source protocol implementations (in any language), these patterns map to Hypercolor's architecture:
Transport vs Transfer: A protocol reference's transport call determines TWO things in Hypercolor:
TransportType(registry.rs) — device-level transport binding, resolved once perDeviceDescriptor. Determines how the backend opens and talks to the device (e.g.,UsbControl,UsbHidApi,UsbHidRaw,UsbBulk,I2cSmBus). HID descriptors declare a platform-freeTransportIntentand letresolve_current_transportmap it per target OS, because hidraw exists only on Linux.TransferType(protocol.rs) — per-command path hint onProtocolCommand. Allows a single protocol to mix transfer paths within one device session (e.g., HID feature reports for init, bulk for frame data). Variants:Primary,Bulk,HidReport.
| Source Pattern | Hypercolor Equivalent |
|---|---|
| Fixed-size byte buffer with manual offsets | Zerocopy struct; whether it carries a report_id field depends on the resolved transport (see Common Pitfalls) |
| HID feature report send | TransportIntent::Hid(HidTransportIntent { report_mode: FeatureReport, .. }) + TransferType::HidReport |
| USB control transfer | TransportType::UsbControl + TransferType::Primary |
| HID interrupt write | HidAccessMode::Direct (resolves to UsbHid on Linux, UsbHidApi elsewhere) + TransferType::Primary |
| Per-LED color loop with count mismatch | a private per-driver normalize_colors(&self, ..) -> Cow<'a, [[u8; 3]]> method — borrow when the LED count matches, allocate only when padding |
| Sleep/delay between commands | post_delay: Duration::from_millis(N) |
| Read response after command | expects_response: true + parse_response() |
Common Pitfalls
| Pattern | Pitfall | Correct Hypercolor Translation |
|---|---|---|
RGBGetRValue(color) |
Assumes RGB ordering | Check actual byte positions — may be RBG or BGR |
HID write with len+1 |
+1 is the report ID | Under UsbHidApi or UsbHidRaw, pick the report_mode to match: a *WithReportId mode means the struct carries the byte, a plain mode means the transport prepends it. Under every other transport there is no prepend step, so the struct carries it |
usleep(1000) |
Units are microseconds | Duration::from_micros(1000) (= 1ms) |
| HID get feature report | Blocks until response | expects_response: true on preceding command |
sizeof(buf) |
May include the report ID | The size assertion must match the struct, which is the wire size minus any byte the transport prepends, and only UsbHidApi and UsbHidRaw prepend anything |
| Magic numbers at byte offsets | Undocumented, easy to mismap | Define named constants for every offset |
The report ID rule is per transport, not global. report_mode exists on
exactly two of the nine TransportType variants, UsbHidApi and UsbHidRaw,
and the prepend lives only in their encoders. Every other transport sends the
payload verbatim; UsbControl and UsbBulk pass the report ID in the
control-transfer wValue, and UsbHid, UsbMidi, UsbSerial, I2cSmBus,
and UsbVendor add nothing at all, so there the packet struct owns the byte.
Lian Li ENE is the counterexample worth remembering: its descriptors declare
TransportType::UsbHid, which has no report_mode at all, and every ENE
packet struct starts with a report_id field. HidAccessMode::Direct resolves
to UsbHid on Linux, so an intent that names a report_mode can still land on
a transport that ignores it.
Verifying Your Understanding
Always verify your protocol understanding against actual USB traffic from the vendor's software:
- Run the vendor's Windows/macOS software
- Capture with Wireshark + USBPcap (Windows) or usbmon (Linux)
- Compare captured packets byte-by-byte with your spec
- Pay special attention to: byte ordering, checksum algorithms, timing between packets
The vendor's software is always ground truth. Community documentation and open-source implementations may have bugs, be incomplete, or cover different firmware versions.