PLAN.md: 5-phase plan to publish as a multi-protocol headset tool (rename away from Corsair trademark, abstract device layer, port additional protocols, publish, upstream to HeadsetControl). DECISIONS.md: rationale for GPLv3 relicense, naming constraints, and the multi-protocol architecture.
5.1 KiB
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)
-
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
-
License file — ✅ Done (GPLv3 LICENSE + Cargo.toml updated)
-
Rename crate & binary — update
Cargo.tomlname, all references in code, CLAUDE.md, README, udev rules, Waybar wrapper, PKGBUILD -
Add SPDX headers — GPLv3 requires license notice in each source file
-
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
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:
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:
- Study HeadsetControl's C implementation
- Implement as a Rust module behind the
Headsettrait - Add VID/PID to auto-detection
- Update udev rules generator
- Test (needs hardware or community testers)
Phase 4: Publish
- GitHub repo — rename or create new repo
- README — features, supported devices, install instructions
- crates.io — publish crate
- AUR package — update PKGBUILD
- HeadsetControl community — announce, invite testers for untested protocols
Phase 5: Upstream contribution to HeadsetControl
Contribute Bragi protocol support back to 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
- Study
corsair_device.hppandcorsair_void_v2w.hppin detail - Write
corsair_bragi.hppin C++20, following their patterns - Test with our HS80 hardware
- Open PR with protocol documentation (
docs/bragi-protocol.md) - 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?