HID: steelseries: Add support for Arctis Nova 5X and Nova 7 families

The Arctis Nova 5X, Nova 7 Gen2, and Nova 7 2026 refresh headsets answer
status polls, but also send unsolicited battery and connection updates
on a second HID interface (interface 5). Use that interface through
async_interface so the driver does not have to poll. Add request and
parse helpers for the Nova status format (0xb0/0xb7/0xb9/0xbb opcodes).

The original (pre-Gen2) Arctis Nova 7 family answers the same status
opcodes on the same interfaces, but reports battery capacity as a
discrete 0-4 level instead of a raw percentage. Add a second parse
function that maps that level through steelseries_map_capacity(), and a
separate device_info so the two capacity encodings cannot get mixed up.

Add the fourteen USB product IDs that share these protocols:

  0x2253  Arctis Nova 5X
  0x2202  Arctis Nova 7
  0x2206  Arctis Nova 7X
  0x22a4  Arctis Nova 7X (alternate PID)
  0x223a  Arctis Nova 7 Diablo
  0x227a  Arctis Nova 7 World of Warcraft Edition
  0x22a1  Arctis Nova 7 2026
  0x22a7  Arctis Nova 7P 2026
  0x22a5  Arctis Nova 7X 2026
  0x22a9  Arctis Nova 7 Diablo 2026
  0x227e  Arctis Nova 7 Gen 2
  0x2258  Arctis Nova 7X Gen 2
  0x229e  Arctis Nova 7X Gen 2 (alternate PID)
  0x22ad  Arctis Nova 7X Gen 2 (alternate PID)

Signed-off-by: Sriman Achanta <srimanachanta@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
Sriman Achanta
2026-08-03 15:15:53 -04:00
committed by Jiri Kosina
parent bebeca20ab
commit 83e70ced0d
3 changed files with 145 additions and 0 deletions

View File

@@ -1379,6 +1379,20 @@
#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X 0x12b6
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_9 0x12c2
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X 0x2253
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7 0x2202
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X 0x2206
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2 0x22a4
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO 0x223a
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_WOW 0x227a
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_2026 0x22a1
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_P_2026 0x22a7
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2026 0x22a5
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO_2026 0x22a9
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_GEN2 0x227e
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2 0x2258
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_2 0x229e
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_3 0x22ad
#define USB_DEVICE_ID_STEELSERIES_MSI_KLC 0x1122
#define USB_DEVICE_ID_STEELSERIES_MSI_ALC 0x1161

View File

@@ -749,6 +749,20 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_9) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_WOW) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_2026) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_P_2026) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2026) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO_2026) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_GEN2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_3) },
#endif
#if IS_ENABLED(CONFIG_HID_SUNPLUS)
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },

View File

@@ -108,6 +108,13 @@ static int steelseries_arctis_9_request_status(struct hid_device *hdev)
return steelseries_send_output_report(hdev, data, sizeof(data));
}
static int steelseries_arctis_nova_request_status(struct hid_device *hdev)
{
const u8 data[] = { 0x00, 0xb0 };
return steelseries_send_output_report(hdev, data, sizeof(data));
}
/*
* Headset battery helpers
*/
@@ -162,6 +169,58 @@ static void steelseries_arctis_9_parse_status(struct steelseries_device *sd,
}
}
static void steelseries_arctis_nova_parse_status(struct steelseries_device *sd,
u8 *data, int size)
{
if (size < 2)
return;
switch (data[0]) {
case 0xb0:
if (size < 4)
return;
sd->headset_connected = (data[1] == 0x03);
sd->battery_capacity = data[2];
sd->battery_charging = (data[3] == 0x01);
break;
case 0xb7:
sd->battery_capacity = data[1];
break;
case 0xb9:
sd->headset_connected = (data[1] == 0x03);
break;
case 0xbb:
sd->battery_charging = (data[1] == 0x01);
break;
}
}
static void steelseries_arctis_nova_7_parse_status(struct steelseries_device *sd,
u8 *data, int size)
{
if (size < 2)
return;
switch (data[0]) {
case 0xb0:
if (size < 4)
return;
sd->headset_connected = (data[1] == 0x03);
sd->battery_capacity = steelseries_map_capacity(data[2], 0, 4);
sd->battery_charging = (data[3] == 0x01);
break;
case 0xb7:
sd->battery_capacity = steelseries_map_capacity(data[1], 0, 4);
break;
case 0xb9:
sd->headset_connected = (data[1] == 0x03);
break;
case 0xbb:
sd->battery_charging = (data[1] == 0x01);
break;
}
}
/*
* Device info definitions
*/
@@ -180,6 +239,22 @@ static const struct steelseries_device_info arctis_9_info = {
.parse_status = steelseries_arctis_9_parse_status,
};
static const struct steelseries_device_info arctis_nova_info = {
.sync_interface = 3,
.async_interface = 5,
.capabilities = SS_CAP_BATTERY,
.request_status = steelseries_arctis_nova_request_status,
.parse_status = steelseries_arctis_nova_parse_status,
};
static const struct steelseries_device_info arctis_nova_7_info = {
.sync_interface = 3,
.async_interface = 5,
.capabilities = SS_CAP_BATTERY,
.request_status = steelseries_arctis_nova_request_status,
.parse_status = steelseries_arctis_nova_7_parse_status,
};
/*
* Headset wireless status and battery infrastructure
*/
@@ -581,6 +656,48 @@ static const struct hid_device_id steelseries_arctis_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_9),
.driver_data = (unsigned long)&arctis_9_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7),
.driver_data = (unsigned long)&arctis_nova_7_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X),
.driver_data = (unsigned long)&arctis_nova_7_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2),
.driver_data = (unsigned long)&arctis_nova_7_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO),
.driver_data = (unsigned long)&arctis_nova_7_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_WOW),
.driver_data = (unsigned long)&arctis_nova_7_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_2026),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_P_2026),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2026),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO_2026),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_GEN2),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_2),
.driver_data = (unsigned long)&arctis_nova_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_3),
.driver_data = (unsigned long)&arctis_nova_info },
{}
};
MODULE_DEVICE_TABLE(hid, steelseries_arctis_devices);