# Bragi Protocol — Reverse-Engineered Documentation This document describes the Corsair Bragi HID protocol used by newer Corsair wireless devices (HS80 RGB Wireless, HS65, Virtuoso SE, etc.). Reverse-engineered from USB traces and protocol analysis. ## Overview Bragi communicates via HID Feature Reports on **Interface 3** of the USB receiver. Each packet is 65 bytes (1 byte Report ID + 64 bytes data). ## Packet Format ### Request (Host → Device) ``` Byte 0: 0x00 — HID Report ID Byte 1: 0x02 — Protocol marker (always 0x02) Byte 2: Endpoint — 0x08 = Receiver, 0x09 = Headset (via Receiver) Byte 3: Command — 0x01 = SET, 0x02 = GET Byte 4: Property — Property ID (see below) Byte 5+: Data — For SET: the values to write Rest: 0x00-padded to 65 bytes ``` ### Response (Device → Host) ``` Byte 0: 0x01 — Report type (always 0x01, NOT 0x02 like the request marker) Byte 1: Endpoint — 0x00 = Receiver, 0x01 = Headset (different IDs than request!) On error: 0xF0/0xF1 = Error/Not Supported Byte 2: Command — Echo: 0x01 = SET, 0x02 = GET Byte 3: Status — 0x00 = OK Byte 4+: Data — Property value (typically uint16 Little-Endian in bytes 4-5) ``` **Note:** The response format differs from the request format: - Byte 0 is `0x01` (not `0x02` like the request marker) - Endpoint IDs are `0x00`/`0x01` (not `0x08`/`0x09` like in the request) **Error detection:** Byte 1 == 0xF0 or 0xF1 indicates that the property is not supported or the request was invalid. ## Endpoints | ID | Name | Description | |------|----------|---------------------------------| | 0x08 | Receiver | The USB dongle itself | | 0x09 | Headset | The connected wireless device | ## Commands | ID | Name | Description | |------|------|-----------------------| | 0x01 | SET | Set a property value | | 0x02 | GET | Read a property value | ## Properties | ID | Name | Type | Description | |------|-------------------|---------|-----------------------------------------------| | 0x01 | Polling Rate | uint16 | Polling rate in Hz | | 0x02 | Brightness | uint16 | LED brightness (0-1000) | | 0x03 | Mode | uint8 | 0x01 = Hardware, 0x02 = Software | | 0x09 | Sidetone | uint16 | Sidetone level | | 0x0F | Battery Level | uint16 | Battery in per-mille (0-1000, /10 = percent) | | 0x10 | Battery Status | uint8 | Charging/discharging status (see below) | | 0x11 | Vendor ID | uint16 | USB Vendor ID | | 0x12 | Product ID | uint16 | USB Product ID | | 0x13 | App Firmware | uint16 | Application firmware version | | 0x14 | Build Firmware | uint16 | Build firmware version | | 0x15 | Radio App FW | uint16 | Radio application firmware | | 0x16 | Radio Build FW | uint16 | Radio build firmware | ## Battery Status Values Empirically determined on the HS80 RGB Wireless. Differs from ckb-next sources where values are mapped differently. | Value | Meaning | |-------|---------------| | 0x00 | Offline | | 0x01 | Charging | | 0x02 | Discharging | | 0x03 | Low | | 0x04 | Fully charged | ## Initialization Sequence The device must be switched to software mode before properties can be read. Full sequence: ### Phase 1: Initialize Receiver 1. **Wake-up:** `GET App Firmware` to Receiver (0x08) - Wakes the receiver and confirms communication 2. **Software mode:** `SET Mode = 0x02` to Receiver (0x08) - Switches receiver to software mode 3. **Heartbeat:** `GET Product ID` to Receiver (0x08) - Confirms receiver responds in software mode ### Phase 2: Initialize Headset 4. **Software mode:** `SET Mode = 0x02` to Headset (0x09) - Switches headset to software mode (via receiver) 5. **Flush:** Clear HID buffer (non-blocking reads until empty) 6. **Heartbeat:** `GET Product ID` to Headset (0x09) - Confirms headset is reachable and responding - No response = headset powered off / not connected ### Cleanup After all queries, both devices **must** be switched back to hardware mode: 7. `SET Mode = 0x01` to Headset (0x09) 8. `SET Mode = 0x01` to Receiver (0x08) **Important:** Without cleanup the device stays in software mode and may not behave normally (e.g. no automatic power-off). ## Value Extraction For uint16 values: Little-Endian from response bytes 4 and 5: ``` value = response[4] | (response[5] << 8) ``` For battery: value is in per-mille (0-1000), divide by 10 for percent. ## USB Identification - **Vendor ID:** 0x1B1C (Corsair) - **Product ID:** 0x0A6B (HS80 RGB Wireless) - **Interface:** 3 (HID Control Interface) Other Bragi devices use the same protocol with different Product IDs.