You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Arctis 9 (0x1038:0x12c2) on Windows: battery BATTERY_UNAVAILABLE / HID communication error (WriteFile ERROR_INVALID_FUNCTION on interface 0) #611
Version / OS
HeadsetControl 4.2.0 (Windows installer) and the continuous portable build (headsetcontrol-windows-x86_64.exe), Windows 11 Home 25H2 (German locale). Headset powered on and connected to the dongle, SteelSeries GG closed. The headset works normally in Windows and in GG.
headsetcontrol -b output
Found 1 supported device(s):
SteelSeries Arctis 9 (SteelSeries Arctis 9) [0x1038:0x12c2]
Battery:
Status: BATTERY_UNAVAILABLE
Error: [battery] HID communication error
The continuous build additionally lists a second entry 0x1038:0x12c4 (only HID interface 5, usage page 0x000c) with the same error.
Manual tests with the dev menu (battery request 0x00, 0x20, padded to 2/8/16/32/64 bytes)
--send on interface 0: Failed to send: WriteFile: (0x00000001) Illegal function (German: "Unzulaessige Funktion") for every length
--send on interface 2: sending works, then No data received (timeout) after 1000 ms
--send-feature (+ --receive-feature 0) on interface 0 and 2: Failed to send feature report: HidD_SetFeature: (0x00000001) Illegal function for every length
Listening on interface 0 (--receive, 6 s) while sending on interface 2: nothing received
Source analysis steelseries_arctis_9.hpp hardcodes interface_id = 0 and sends the request (0x00, 0x20) via hid_write(). On Windows hidapi's hid_write() uses WriteFile(), which needs an output report declared in the interface's report descriptor. On this dongle both WriteFile and HidD_SetFeature fail with ERROR_INVALID_FUNCTION on interface 0 (usage page 0xffc0), so it looks like Windows exposes no writable report there. The Linux kernel driver (hid-steelseries) sends the same request as a control-transfer SET_REPORT, which would explain why it works on Linux. The header comment for 0x12c4 also says it is not verified on hardware. This is my reading of the code and the test results, not a confirmed diagnosis.
Happy to run further tests or provide more output.
Update / correction: the diagnosis in my original description was wrong.
interface_id = 0 in the Arctis 9 device class does not select interface 0. get_hid_path() checks if (!iid || cur_dev->interface_number == iid), so 0 means "first enumerated device". In my --dev -- --list output the first 0x12c2 entry on Windows is MI_01 (usage page 0x000c, consumer control), which cannot be written to. So all my --interface 0 tests (and the normal -b query) went to that interface, not to MI_00 (0xffc0). The explanation about "no writable report on interface 0" is therefore not supported.
Byte 3 is 0x77, which is about 35 % with the existing min/max (0x64/0x9A). The same request on --usage 0xff00:0x0001 (MI_02) is accepted by WriteFile but gets no answer (timeout).
So the likely fix is to pin the usage page in getCapabilityDetail() of steelseries_arctis_9.hpp, e.g.
I have not compiled or tested this change myself, so please treat it as a suggestion. I also do not know how it behaves for 0x12c4, which only shows a single consumer-control interface (interface 5) on my machine.
As a workaround, the --dev command above can be used from a script to read the battery on Windows.
Activity
Scherflon commentedon Oct 25, 2026
Update / correction: the diagnosis in my original description was wrong.
interface_id = 0in the Arctis 9 device class does not select interface 0.get_hid_path()checksif (!iid || cur_dev->interface_number == iid), so0means "first enumerated device". In my--dev -- --listoutput the first0x12c2entry on Windows is MI_01 (usage page0x000c, consumer control), which cannot be written to. So all my--interface 0tests (and the normal-bquery) went to that interface, not to MI_00 (0xffc0). The explanation about "no writable report on interface 0" is therefore not supported.Selecting the interface by usage page works:
Byte 3 is
0x77, which is about 35 % with the existing min/max (0x64/0x9A). The same request on--usage 0xff00:0x0001(MI_02) is accepted byWriteFilebut gets no answer (timeout).So the likely fix is to pin the usage page in
getCapabilityDetail()ofsteelseries_arctis_9.hpp, e.g.I have not compiled or tested this change myself, so please treat it as a suggestion. I also do not know how it behaves for
0x12c4, which only shows a single consumer-control interface (interface 5) on my machine.As a workaround, the
--devcommand above can be used from a script to read the battery on Windows.Sorry for the misleading title and description.
mattenz commentedon Oct 26, 2026
This seems similar to #588 ?