Compare commits
5 Commits
098b53744d
...
v0.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
| e364f4edec | |||
| 8a9f31f1b4 | |||
| 0ebecccc1b | |||
| c38996b8a4 | |||
| 914ddf114e |
Generated
+1
-1
@@ -144,7 +144,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "corsairctl"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"alsa",
|
||||
"clap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "corsairctl"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
edition = "2024"
|
||||
description = "CLI tool for Corsair Bragi-protocol devices (HS80, etc.)"
|
||||
license = "MIT"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Decisions
|
||||
|
||||
## 2026-04-09 – License: GPLv3 instead of MIT
|
||||
- **Who**: Dom, F.R.I.D.A.Y.
|
||||
- **Why**: Plan to support multiple headset protocols using HeadsetControl (GPLv3) as reference for protocol implementations. HeadsetControl's code is community-tested — reimplementing blind without hardware to test against is impractical. GPL copyleft is acceptable since the tool is open source on GitHub anyway.
|
||||
- **Tradeoffs**: MIT would allow proprietary use, but we gain access to tested protocol implementations. GPL has no practical downside for a CLI tool distributed as source.
|
||||
- **How**: GPLv3-or-later in Cargo.toml and LICENSE file.
|
||||
|
||||
## 2026-04-09 – Tool name: must avoid "Corsair" trademark
|
||||
- **Who**: Dom, F.R.I.D.A.Y.
|
||||
- **Why**: "Corsair" is a registered trademark. Publishing under `corsairctl` risks trademark issues. "Bragi" is also risky (Bragi GmbH, Danish audio company).
|
||||
- **Tradeoffs**: `headsetctl` is descriptive and follows Linux naming conventions (`bluetoothctl`, `brightnessctl`). Less specific but safer.
|
||||
- **How**: Rename TBD — to be decided as part of the publish plan.
|
||||
|
||||
## 2026-04-09 – Multi-protocol architecture using HeadsetControl as reference
|
||||
- **Who**: Dom, F.R.I.D.A.Y.
|
||||
- **Why**: Each headset vendor uses a proprietary HID protocol. No common standard exists. HeadsetControl already has tested implementations for SteelSeries, Logitech, HyperX, Corsair, etc.
|
||||
- **Tradeoffs**: Porting C code to Rust takes effort, but the protocol logic is the hard part (byte sequences, timing, quirks) — and that's already documented in HeadsetControl's source.
|
||||
- **How**: Modular backend architecture — each protocol as a separate module behind a common trait. Bragi is the first backend; others follow.
|
||||
|
||||
## 2026-06-10 – Scope: private use only, revert to MIT (supersedes the three 2026-04-09 decisions)
|
||||
- **Who**: Dom, F.R.I.D.A.Y.
|
||||
- **Why**: The open-source publish plan (rename away from "Corsair", multi-protocol backend layer, crates.io, upstream to HeadsetControl) was dropped. The tool is for personal use across Dom's own machines, distributed as an Arch package via the private moonarch repo (Gitea registry at gitea.moonarch.de), used primarily as a Waybar widget.
|
||||
- **Tradeoffs**: GPLv3 and the rename were only justified by public distribution and porting HeadsetControl (GPLv3) code. With no public release and no GPL-derived code in the tree, copyleft and the trademark rename buy nothing. MIT is simpler and reversible — switch to GPLv3 only if GPL code is later pulled in.
|
||||
- **How**: Revert license to MIT (Cargo.toml + MIT LICENSE file). Keep the name `corsairctl`. PLAN.md retained as a record of the considered direction, no longer driving work.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Dominik Kressler
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Maintainer: nevaforget <dom@moonarch.de>
|
||||
pkgname=corsairctl
|
||||
pkgver=0.1.0
|
||||
pkgver=0.1.2
|
||||
pkgrel=1
|
||||
pkgdesc='CLI tool for Corsair Bragi-protocol devices (HS80 RGB Wireless, etc.)'
|
||||
arch=('x86_64')
|
||||
@@ -8,7 +8,7 @@ url='https://gitea.moonarch.de/nevaforget/corsairctl'
|
||||
license=('MIT')
|
||||
depends=('hidapi' 'alsa-lib')
|
||||
makedepends=('cargo' 'pkg-config')
|
||||
source=("git+${url}.git")
|
||||
source=("git+${url}.git#tag=v$pkgver")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
prepare() {
|
||||
@@ -29,4 +29,5 @@ package() {
|
||||
install -Dm755 "target/release/$pkgname" "$pkgdir/usr/bin/$pkgname"
|
||||
install -Dm755 "scripts/corsairctl-waybar.sh" "$pkgdir/usr/bin/corsairctl-waybar"
|
||||
install -Dm644 "udev/99-corsair.rules" "$pkgdir/usr/lib/udev/rules.d/99-corsair.rules"
|
||||
install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# Plan: Publish as Multi-Protocol Headset Tool
|
||||
|
||||
## Status: DRAFT — awaiting review
|
||||
|
||||
## Goal
|
||||
|
||||
Publish `corsairctl` as an open-source, multi-headset CLI tool under GPLv3.
|
||||
Rename away from "Corsair" trademark. Support additional headset protocols
|
||||
using HeadsetControl (C, GPLv3) as reference.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Rename & License (no functional changes)
|
||||
|
||||
1. **Pick a new name** — candidates:
|
||||
- `headsetctl` (clear, follows Linux conventions: `brightnessctl`, `bluetoothctl`)
|
||||
- `hsetctl` (shorter, less likely to collide)
|
||||
- `openheadset` (explicit open-source angle)
|
||||
- Decision needed before anything else — name affects crate name, binary, repo, docs
|
||||
|
||||
2. **License file** — ✅ Done (GPLv3 LICENSE + Cargo.toml updated)
|
||||
|
||||
3. **Rename crate & binary** — update `Cargo.toml` name, all references in code,
|
||||
CLAUDE.md, README, udev rules, Waybar wrapper, PKGBUILD
|
||||
|
||||
4. **Add SPDX headers** — GPLv3 requires license notice in each source file
|
||||
|
||||
5. **Clean up repo** — there's a nested `src/corsairctl/` directory that looks like
|
||||
an old copy of the entire repo (has its own `.git/`, `Cargo.toml`, `CLAUDE.md`).
|
||||
Investigate and remove if it's dead weight.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Abstract device layer (architecture)
|
||||
|
||||
Current state: `BragiDevice` directly uses `hidapi` and the Bragi protocol.
|
||||
Everything is Corsair-specific (VID, PIDs, handshake).
|
||||
|
||||
Target state: a trait-based backend system so multiple protocols can coexist.
|
||||
|
||||
```
|
||||
src/
|
||||
├── backend/
|
||||
│ ├── mod.rs // Headset trait + DeviceInfo struct
|
||||
│ ├── bragi.rs // Corsair Bragi (current code, extracted)
|
||||
│ ├── steelseries.rs // future
|
||||
│ └── logitech.rs // future
|
||||
├── hid.rs // generic HID helpers (send/recv/flush)
|
||||
├── cli.rs
|
||||
├── output.rs
|
||||
├── error.rs
|
||||
└── main.rs
|
||||
```
|
||||
|
||||
### The `Headset` trait
|
||||
|
||||
```rust
|
||||
pub trait Headset {
|
||||
fn name(&self) -> &str;
|
||||
fn battery_level(&self) -> Result<f32>;
|
||||
fn battery_status(&self) -> Result<BatteryStatus>;
|
||||
fn brightness(&self) -> Option<Result<u16>>; // not all headsets have LEDs
|
||||
fn set_brightness(&self, value: u16) -> Option<Result<()>>;
|
||||
fn sidetone(&self) -> Option<Result<u8>>; // not all have sidetone
|
||||
fn set_sidetone(&self, value: u8) -> Option<Result<()>>;
|
||||
fn info(&self) -> Result<DeviceInfo>;
|
||||
}
|
||||
```
|
||||
|
||||
### Auto-detection
|
||||
|
||||
Scan HID devices, try each backend's `probe()` function:
|
||||
|
||||
```rust
|
||||
pub fn detect() -> Result<Box<dyn Headset>> {
|
||||
// Try Bragi first (checks Corsair VID + known PIDs)
|
||||
// Then SteelSeries, Logitech, etc.
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Port first additional protocol
|
||||
|
||||
Pick the easiest one from HeadsetControl as proof of concept.
|
||||
Good candidates (simple protocol, popular hardware):
|
||||
|
||||
- **SteelSeries Arctis Nova 7** — straightforward HID, well-documented in HeadsetControl
|
||||
- **Logitech G PRO X** — also relatively simple
|
||||
|
||||
Steps per protocol:
|
||||
1. Study HeadsetControl's C implementation
|
||||
2. Implement as a Rust module behind the `Headset` trait
|
||||
3. Add VID/PID to auto-detection
|
||||
4. Update udev rules generator
|
||||
5. Test (needs hardware or community testers)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Publish
|
||||
|
||||
1. **GitHub repo** — rename or create new repo
|
||||
2. **README** — features, supported devices, install instructions
|
||||
3. **crates.io** — publish crate
|
||||
4. **AUR package** — update PKGBUILD
|
||||
5. **HeadsetControl community** — announce, invite testers for untested protocols
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Upstream contribution to HeadsetControl
|
||||
|
||||
Contribute Bragi protocol support back to [Sapd/HeadsetControl](https://github.com/Sapd/HeadsetControl).
|
||||
|
||||
### Why this is feasible
|
||||
|
||||
HeadsetControl already has `corsair_void_v2w.hpp` which uses a near-identical
|
||||
protocol pattern to Bragi: multi-step handshake (firmware query → software mode
|
||||
→ heartbeat), receiver + headset endpoints, HID buffer flushing with 5ms timeout,
|
||||
sidetone 0-1000 range. The `CorsairDevice` base class provides shared helpers
|
||||
for sidetone mapping, battery parsing, and LED control.
|
||||
|
||||
### What a `corsair_bragi.hpp` would contain
|
||||
|
||||
- New class inheriting from `CorsairDevice`
|
||||
- Bragi-specific PIDs: `0x0A6B` (HS80), potentially HS65, Virtuoso SE
|
||||
- Our Bragi property ID set (from `properties.rs`)
|
||||
- LED brightness support (0-1000, not in existing Corsair implementations)
|
||||
- Init handshake adapted to Bragi's two-phase sequence (receiver → headset)
|
||||
|
||||
### Steps
|
||||
|
||||
1. Study `corsair_device.hpp` and `corsair_void_v2w.hpp` in detail
|
||||
2. Write `corsair_bragi.hpp` in C++20, following their patterns
|
||||
3. Test with our HS80 hardware
|
||||
4. Open PR with protocol documentation (`docs/bragi-protocol.md`)
|
||||
5. List all known Bragi PIDs we can find (community input helps here)
|
||||
|
||||
### Alternative: documentation-only contribution
|
||||
|
||||
If writing C++ is too much effort, contribute just the protocol documentation
|
||||
as a PR. HeadsetControl's community can implement it from there. We already
|
||||
have `docs/bragi-protocol.md` which covers the full protocol.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
- [ ] Final name?
|
||||
- [ ] Should sidetone stay ALSA-based or also go through HID where supported?
|
||||
- [ ] Minimum supported Rust version (MSRV)?
|
||||
- [ ] Do we want a config file for per-device settings?
|
||||
- [ ] The nested `src/corsairctl/` directory — is this an old copy or something active?
|
||||
+75
-75
@@ -1,136 +1,136 @@
|
||||
# Bragi-Protokoll — Reverse-Engineerte Dokumentation
|
||||
# Bragi Protocol — Reverse-Engineered Documentation
|
||||
|
||||
Dieses Dokument beschreibt das Corsair Bragi HID-Protokoll, wie es von neueren
|
||||
Corsair-Wireless-Geräten (HS80 RGB Wireless, etc.) verwendet wird. Reverse-Engineered
|
||||
aus USB-Traces und den Python-Probes in `~/Projects/hs80-battery/`.
|
||||
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.
|
||||
|
||||
## Übersicht
|
||||
## Overview
|
||||
|
||||
Bragi kommuniziert über HID Feature Reports auf **Interface 3** des USB-Receivers.
|
||||
Jedes Paket ist 65 Bytes (1 Byte Report ID + 64 Bytes Daten).
|
||||
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).
|
||||
|
||||
## Paketformat
|
||||
## Packet Format
|
||||
|
||||
### Request (Host → Gerät)
|
||||
### Request (Host → Device)
|
||||
|
||||
```
|
||||
Byte 0: 0x00 — HID Report ID
|
||||
Byte 1: 0x02 — Protokoll-Marker (immer 0x02)
|
||||
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 (siehe unten)
|
||||
Byte 5+: Daten — Bei SET: die zu setzenden Werte
|
||||
Rest: 0x00-gepaddet auf 65 Bytes
|
||||
Byte 4: Property — Property ID (see below)
|
||||
Byte 5+: Data — For SET: the values to write
|
||||
Rest: 0x00-padded to 65 bytes
|
||||
```
|
||||
|
||||
### Response (Gerät → Host)
|
||||
### Response (Device → Host)
|
||||
|
||||
```
|
||||
Byte 0: 0x01 — Report-Typ (immer 0x01, NICHT 0x02 wie im Request)
|
||||
Byte 1: Endpoint — 0x00 = Receiver, 0x01 = Headset (andere IDs als im Request!)
|
||||
Bei Fehler: 0xF0/0xF1 = Error/Not Supported
|
||||
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+: Daten — Property-Wert (typisch uint16 Little-Endian in Bytes 4-5)
|
||||
Byte 4+: Data — Property value (typically uint16 Little-Endian in bytes 4-5)
|
||||
```
|
||||
|
||||
**Achtung:** Das Response-Format weicht vom Request-Format ab:
|
||||
- Byte 0 ist `0x01` (nicht `0x02` wie der Request-Marker)
|
||||
- Endpoint-IDs sind `0x00`/`0x01` (nicht `0x08`/`0x09` wie im Request)
|
||||
**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)
|
||||
|
||||
**Fehler-Erkennung:** Byte 1 == 0xF0 oder 0xF1 bedeutet, dass die Property nicht
|
||||
unterstützt wird oder der Request ungültig war.
|
||||
**Error detection:** Byte 1 == 0xF0 or 0xF1 indicates that the property is not
|
||||
supported or the request was invalid.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| ID | Name | Beschreibung |
|
||||
| ID | Name | Description |
|
||||
|------|----------|---------------------------------|
|
||||
| 0x08 | Receiver | Der USB-Dongle selbst |
|
||||
| 0x09 | Headset | Das verbundene Wireless-Gerät |
|
||||
| 0x08 | Receiver | The USB dongle itself |
|
||||
| 0x09 | Headset | The connected wireless device |
|
||||
|
||||
## Commands
|
||||
|
||||
| ID | Name | Beschreibung |
|
||||
| ID | Name | Description |
|
||||
|------|------|-----------------------|
|
||||
| 0x01 | SET | Property-Wert setzen |
|
||||
| 0x02 | GET | Property-Wert lesen |
|
||||
| 0x01 | SET | Set a property value |
|
||||
| 0x02 | GET | Read a property value |
|
||||
|
||||
## Properties
|
||||
|
||||
| ID | Name | Typ | Beschreibung |
|
||||
| ID | Name | Type | Description |
|
||||
|------|-------------------|---------|-----------------------------------------------|
|
||||
| 0x01 | Polling Rate | uint16 | Polling-Rate in Hz |
|
||||
| 0x02 | Brightness | uint16 | LED-Helligkeit (0-1000) |
|
||||
| 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 | Batterie in Promille (0-1000, /10 = Prozent) |
|
||||
| 0x10 | Battery Status | uint8 | Lade-/Entladestatus (siehe unten) |
|
||||
| 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 | Applikations-Firmware-Version |
|
||||
| 0x14 | Build Firmware | uint16 | Build-Firmware-Version |
|
||||
| 0x15 | Radio App FW | uint16 | Radio-Applikations-Firmware |
|
||||
| 0x16 | Radio Build FW | uint16 | Radio-Build-Firmware |
|
||||
| 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 Werte
|
||||
## Battery Status Values
|
||||
|
||||
Empirisch ermittelt am HS80 RGB Wireless (2026-03-27).
|
||||
Weicht von ckb-next-Quellen ab — dort sind die Werte anders zugeordnet.
|
||||
Empirically determined on the HS80 RGB Wireless.
|
||||
Differs from ckb-next sources where values are mapped differently.
|
||||
|
||||
| Wert | Bedeutung |
|
||||
|------|---------------|
|
||||
| Value | Meaning |
|
||||
|-------|---------------|
|
||||
| 0x00 | Offline |
|
||||
| 0x01 | Laden |
|
||||
| 0x02 | Entladen |
|
||||
| 0x03 | Niedrig |
|
||||
| 0x04 | Voll geladen |
|
||||
| 0x01 | Charging |
|
||||
| 0x02 | Discharging |
|
||||
| 0x03 | Low |
|
||||
| 0x04 | Fully charged |
|
||||
|
||||
## Initialisierungssequenz
|
||||
## Initialization Sequence
|
||||
|
||||
Das Gerät muss in den Software-Modus versetzt werden, bevor Properties gelesen werden
|
||||
können. Die vollständige Sequenz:
|
||||
The device must be switched to software mode before properties can be read.
|
||||
Full sequence:
|
||||
|
||||
### Phase 1: Receiver initialisieren
|
||||
### Phase 1: Initialize Receiver
|
||||
|
||||
1. **Wake-Up:** `GET App Firmware` an Receiver (0x08)
|
||||
- Weckt den Receiver auf und bestätigt Kommunikation
|
||||
2. **Software-Modus:** `SET Mode = 0x02` an Receiver (0x08)
|
||||
- Schaltet Receiver in Software-Modus
|
||||
3. **Heartbeat:** `GET Product ID` an Receiver (0x08)
|
||||
- Bestätigt dass Receiver im Software-Modus antwortet
|
||||
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: Headset initialisieren
|
||||
### Phase 2: Initialize Headset
|
||||
|
||||
4. **Software-Modus:** `SET Mode = 0x02` an Headset (0x09)
|
||||
- Schaltet Headset in Software-Modus (via Receiver)
|
||||
5. **Flush:** HID-Puffer leeren (nonblocking reads bis leer)
|
||||
6. **Heartbeat:** `GET Product ID` an Headset (0x09)
|
||||
- Bestätigt dass Headset erreichbar ist und antwortet
|
||||
- Keine Antwort = Headset ausgeschaltet/nicht verbunden
|
||||
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
|
||||
|
||||
Nach allen Abfragen **müssen** beide Geräte zurück in den Hardware-Modus:
|
||||
After all queries, both devices **must** be switched back to hardware mode:
|
||||
|
||||
7. `SET Mode = 0x01` an Headset (0x09)
|
||||
8. `SET Mode = 0x01` an Receiver (0x08)
|
||||
7. `SET Mode = 0x01` to Headset (0x09)
|
||||
8. `SET Mode = 0x01` to Receiver (0x08)
|
||||
|
||||
**Wichtig:** Ohne Cleanup bleibt das Gerät im Software-Modus und verhält sich
|
||||
möglicherweise nicht normal (z.B. keine automatische Abschaltung).
|
||||
**Important:** Without cleanup the device stays in software mode and may not
|
||||
behave normally (e.g. no automatic power-off).
|
||||
|
||||
## Wert-Extraktion
|
||||
## Value Extraction
|
||||
|
||||
Für uint16-Werte: Little-Endian aus Response-Bytes 4 und 5:
|
||||
For uint16 values: Little-Endian from response bytes 4 and 5:
|
||||
```
|
||||
value = response[4] | (response[5] << 8)
|
||||
```
|
||||
|
||||
Für Batterie: Wert ist in Promille (0-1000), Division durch 10 ergibt Prozent.
|
||||
For battery: value is in per-mille (0-1000), divide by 10 for percent.
|
||||
|
||||
## USB-Identifikation
|
||||
## USB Identification
|
||||
|
||||
- **Vendor ID:** 0x1B1C (Corsair)
|
||||
- **Product ID:** 0x0A6B (HS80 RGB Wireless)
|
||||
- **Interface:** 3 (HID Control Interface)
|
||||
|
||||
Andere Bragi-Geräte verwenden dasselbe Protokoll mit unterschiedlichen Product IDs.
|
||||
Other Bragi devices use the same protocol with different Product IDs.
|
||||
|
||||
+5
-8
@@ -1,16 +1,13 @@
|
||||
// ABOUTME: Einstiegspunkt für corsairctl — CLI-Dispatch zu Subcommands.
|
||||
// ABOUTME: Parst CLI-Argumente und delegiert an die jeweilige Funktionalität.
|
||||
|
||||
mod bragi;
|
||||
mod cli;
|
||||
mod error;
|
||||
mod hid;
|
||||
mod output;
|
||||
mod sidetone;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use cli::{Cli, Command};
|
||||
use corsairctl::bragi;
|
||||
use corsairctl::cli::{Cli, Command};
|
||||
use corsairctl::error;
|
||||
use corsairctl::output;
|
||||
use corsairctl::sidetone;
|
||||
|
||||
fn run() -> error::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
Reference in New Issue
Block a user