Files
corsairctl/PLAN.md
T
nevaforget c38996b8a4 docs: add publish plan and decision log
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.
2026-06-10 16:37:08 +02:00

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)

  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

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:

  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.

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?