[PATCH v2 0/4] HID: steelseries: Add support for Arctis headset lineup

Sriman Achanta posted 4 patches 3 weeks, 5 days ago
.../ABI/testing/sysfs-driver-hid-srws1        |   21 -
.../ABI/testing/sysfs-driver-hid-steelseries  |  131 ++
drivers/hid/hid-ids.h                         |   33 +-
drivers/hid/hid-quirks.c                      |   25 +
drivers/hid/hid-steelseries.c                 | 2061 ++++++++++++++---
5 files changed, 1925 insertions(+), 346 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-driver-hid-srws1
create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-steelseries
[PATCH v2 0/4] HID: steelseries: Add support for Arctis headset lineup
Posted by Sriman Achanta 3 weeks, 5 days ago
This patch series adds comprehensive support for the SteelSeries Arctis
wireless gaming headset lineup to the hid-steelseries driver.

The current driver provides only basic battery monitoring for Arctis 1
and Arctis 9. This series extends support to 25+ Arctis models with
full feature control including sidetone, auto-sleep, microphone
controls, volume limiting, and Bluetooth settings.

The driver restructure uses a capability-based device info system to
cleanly handle the varying feature sets across different Arctis
generations while maintaining support for the legacy SRW-S1 racing
wheel.

Patch 1: Add 27 new device IDs to hid-ids.h
Patch 2: Add HID quirks for proper device initialization 
Patch 3: Update ABI documentation for new sysfs attributes
Patch 4: Complete driver implementation with all features

Tested on Arctis Nova 7 (0x2202). All other implementation details are
based on the reverse engineering done in the HeadsetControl library
(abe3ac8).

V2:
- Fix Documentation formatting issues

Sriman Achanta (4):
  HID: hid-ids: Add SteelSeries Arctis headset device IDs
  HID: quirks: Add INPUT_CONFIGURED quirk for SteelSeries Arctis
    headsets
  Documentation: ABI: Document SteelSeries headset sysfs attributes
  HID: steelseries: Add support for Arctis headset lineup

 .../ABI/testing/sysfs-driver-hid-srws1        |   21 -
 .../ABI/testing/sysfs-driver-hid-steelseries  |  131 ++
 drivers/hid/hid-ids.h                         |   33 +-
 drivers/hid/hid-quirks.c                      |   25 +
 drivers/hid/hid-steelseries.c                 | 2061 ++++++++++++++---
 5 files changed, 1925 insertions(+), 346 deletions(-)
 delete mode 100644 Documentation/ABI/testing/sysfs-driver-hid-srws1
 create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-steelseries

-- 
2.52.0
[PATCH 0/3] HID: steelseries: clean up functions, move battery request data to structs
Posted by Benjamin Wheeler 2 weeks, 3 days ago
Hi Sriman,
As requested, here are my patches for your v2 of the new driver.

They fix an issue where the Arctis 7 incorrectly shows 100% battery due
to submitting an invalid report. They also include some cleanups of a
couple functions.

I've tested these by loading the module in a VM, and seeing that the
Arctis 7 reads the correct battery % through the Ubuntu desktop.

If these don't fit nicely into your v3, feel free to take what you need
out of them, or, once you submit v3, I can re-do my patches and
re-submit.

Thanks!

Benjamin Wheeler (3):
  HID: steelseries: Clean up hid send_report functions
  HID: steelseries: Add battery request info (byte flags) to device
    info.
  HID: steelseries: Use device data for battery requests

 drivers/hid/hid-steelseries.c | 217 +++++++++++++++++-----------------
 1 file changed, 111 insertions(+), 106 deletions(-)

-- 
2.52.0
[PATCH 1/3] HID: steelseries: Clean up hid send_report functions
Posted by Benjamin Wheeler 2 weeks, 3 days ago
These functions do the same thing, so combine them into one with a
parameter.
Use helper functions to keep callsites the same.

Signed-off-by: Benjamin Wheeler <benjaminwheeler0510@gmail.com>
---
 drivers/hid/hid-steelseries.c | 45 ++++++++++++++++-------------------
 1 file changed, 21 insertions(+), 24 deletions(-)

diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c
index a0046fbc830b..63b086fa6df0 100644
--- a/drivers/hid/hid-steelseries.c
+++ b/drivers/hid/hid-steelseries.c
@@ -829,9 +829,13 @@ static int steelseries_battery_register(struct steelseries_device *sd)
 	return 0;
 }
 
-/* Helper function to send feature reports */
-static int steelseries_send_feature_report(struct hid_device *hdev,
-					   const u8 *data, size_t len)
+/**
+ * Send an hid report to the device.
+ * Supported types are output reports and feature reports.
+ */
+static int __steelseries_send_report(struct hid_device *const hdev,
+				     const u8 *const data, const size_t len,
+				     const enum hid_report_type type)
 {
 	u8 *buf;
 	int ret;
@@ -840,8 +844,9 @@ static int steelseries_send_feature_report(struct hid_device *hdev,
 	if (!buf)
 		return -ENOMEM;
 
-	ret = hid_hw_raw_request(hdev, data[0], buf, len, HID_FEATURE_REPORT,
+	ret = hid_hw_raw_request(hdev, data[0], buf, len, type,
 				 HID_REQ_SET_REPORT);
+
 	kfree(buf);
 
 	if (ret < 0)
@@ -852,28 +857,19 @@ static int steelseries_send_feature_report(struct hid_device *hdev,
 	return 0;
 }
 
-/* Helper function to send output reports */
-static int steelseries_send_output_report(struct hid_device *hdev,
-					  const u8 *data, size_t len)
+static inline int steelseries_send_feature_report(struct hid_device *const hdev,
+						  const u8 *const data,
+						  const size_t len)
 {
-	u8 *buf;
-	int ret;
-
-	buf = kmemdup(data, len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	/* Use raw_request with OUTPUT_REPORT type for devices without Interrupt OUT */
-	ret = hid_hw_raw_request(hdev, data[0], buf, len, HID_OUTPUT_REPORT,
-				 HID_REQ_SET_REPORT);
-	kfree(buf);
-
-	if (ret < 0)
-		return ret;
-	if (ret < len)
-		return -EIO;
+	return __steelseries_send_report(hdev, data, len, HID_FEATURE_REPORT);
+}
 
-	return 0;
+static inline int steelseries_send_output_report(struct hid_device *const hdev,
+						 const u8 *const data,
+						 const size_t len)
+{
+	// NOTE: Output report (HID_OUTPUT_REPORT) is for devices without Interrupt OUT
+	return __steelseries_send_report(hdev, data, len, HID_OUTPUT_REPORT);
 }
 
 /* Sidetone level attribute */
@@ -2170,3 +2166,4 @@ MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
 MODULE_AUTHOR("Simon Wood <simon@mungewell.org>");
 MODULE_AUTHOR("Christian Mayer <git@mayer-bgk.de>");
 MODULE_AUTHOR("Sriman Achanta <srimanachanta@gmail.com>");
+MODULE_AUTHOR("Benjamin Wheeler <benjaminwheeler0510@gmail.com>");
-- 
2.52.0
[PATCH 2/3] HID: steelseries: Add battery request info (byte flags) to device info.
Posted by Benjamin Wheeler 2 weeks, 3 days ago
This way, the device info is decoupled from the functions that call it.
This will allow for simplification of calling these functions.

Signed-off-by: Benjamin Wheeler <benjaminwheeler0510@gmail.com>
---
 drivers/hid/hid-steelseries.c | 79 +++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c
index 63b086fa6df0..dabc4763f072 100644
--- a/drivers/hid/hid-steelseries.c
+++ b/drivers/hid/hid-steelseries.c
@@ -31,12 +31,22 @@
 /* Legacy quirk flag for SRW-S1 */
 #define STEELSERIES_SRWS1 BIT(0)
 
+/**
+ * Stores info about how to send a report to this device.
+ */
+struct steelseries_report_data {
+	const u8 data[2];
+	const u8 len; // Only ever 1 or 2, no need for size_t.
+	const enum hid_report_type type;
+};
+
 struct steelseries_device_info {
 	u16 product_id;
 	const char *name;
 	u8 interface_binding_mode; /* 0 = first enumerated, 1 = specific interface(s) */
 	u16 valid_interfaces; /* Bitmask when mode = 1, ignored when mode = 0 */
 	unsigned long capabilities;
+	const struct steelseries_report_data *const report_data;
 };
 
 struct steelseries_device {
@@ -382,12 +392,55 @@ static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev,
 	return rdesc;
 }
 
+// Report data for: arctis 1, 1x, 7p, 7x
+static const struct steelseries_report_data report_data__1_1x_7p_7x = {
+	.data = { 0x06, 0x12 },
+	.len = 2,
+	.type = HID_FEATURE_REPORT
+};
+
+// Report data for: arctis 7, 7 v2 (2019 ed.)
+static const struct steelseries_report_data report_data__7_7v2 = {
+	.data = { 0x06, 0x18 },
+	.len = 2,
+	.type = HID_OUTPUT_REPORT
+};
+
+// Report data for: 7 plus, 7 plus P, 7 plus X, 7 plus Destiny, all novas besides nova 3p, nova 3x
+static const struct steelseries_report_data report_data__7plus_nova = {
+	.data = { 0x00, 0xb0 },
+	.len = 2,
+	.type = HID_OUTPUT_REPORT
+};
+
+// Report data for: arctis 9
+static const struct steelseries_report_data report_data__9 = {
+	.data = { 0x00, 0x20 },
+	.len = 2,
+	.type = HID_FEATURE_REPORT
+};
+
+// Report data for: Pro
+static const struct steelseries_report_data report_data__pro = {
+	.data = { 0x40, 0xAA },
+	.len = 2,
+	.type = HID_OUTPUT_REPORT
+};
+
+// Report data for: Nova 3p, 3x
+static const struct steelseries_report_data report_data__nova_3p_3x = {
+	.data = { 0xb0 },
+	.len = 1,
+	.type = HID_OUTPUT_REPORT
+};
+
 static const struct steelseries_device_info arctis_1_info = {
 	.product_id = USB_DEVICE_ID_STEELSERIES_ARCTIS_1,
 	.name = "Arctis 1 Wireless",
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__1_1x_7p_7x,
 };
 
 static const struct steelseries_device_info arctis_1_x_info = {
@@ -396,6 +449,7 @@ static const struct steelseries_device_info arctis_1_x_info = {
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__1_1x_7p_7x,
 };
 
 static const struct steelseries_device_info arctis_7_info = {
@@ -404,6 +458,7 @@ static const struct steelseries_device_info arctis_7_info = {
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(5),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__7_7v2,
 };
 
 static const struct steelseries_device_info arctis_7_p_info = {
@@ -412,6 +467,7 @@ static const struct steelseries_device_info arctis_7_p_info = {
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__1_1x_7p_7x,
 };
 
 static const struct steelseries_device_info arctis_7_x_info = {
@@ -420,6 +476,7 @@ static const struct steelseries_device_info arctis_7_x_info = {
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__1_1x_7p_7x,
 };
 
 static const struct steelseries_device_info arctis_7_gen2_info = {
@@ -428,6 +485,7 @@ static const struct steelseries_device_info arctis_7_gen2_info = {
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(5),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__7_7v2,
 };
 
 static const struct steelseries_device_info arctis_7_plus_info = {
@@ -437,6 +495,7 @@ static const struct steelseries_device_info arctis_7_plus_info = {
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY |
 			SS_CAP_INACTIVE_TIME | SS_CAP_CHATMIX,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_7_plus_p_info = {
@@ -446,6 +505,7 @@ static const struct steelseries_device_info arctis_7_plus_p_info = {
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY |
 			SS_CAP_INACTIVE_TIME | SS_CAP_CHATMIX,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_7_plus_x_info = {
@@ -455,6 +515,7 @@ static const struct steelseries_device_info arctis_7_plus_x_info = {
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY |
 			SS_CAP_INACTIVE_TIME | SS_CAP_CHATMIX,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_7_plus_destiny_info = {
@@ -464,6 +525,7 @@ static const struct steelseries_device_info arctis_7_plus_destiny_info = {
 	.valid_interfaces = BIT(3),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY |
 			SS_CAP_INACTIVE_TIME | SS_CAP_CHATMIX,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_9_info = {
@@ -473,6 +535,7 @@ static const struct steelseries_device_info arctis_9_info = {
 	.valid_interfaces = 0,
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY |
 			SS_CAP_INACTIVE_TIME | SS_CAP_CHATMIX,
+	.report_data = &report_data__9,
 };
 
 static const struct steelseries_device_info arctis_pro_info = {
@@ -481,6 +544,7 @@ static const struct steelseries_device_info arctis_pro_info = {
 	.interface_binding_mode = 0,
 	.valid_interfaces = 0,
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__pro,
 };
 
 static const struct steelseries_device_info arctis_nova_3_info = {
@@ -490,6 +554,7 @@ static const struct steelseries_device_info arctis_nova_3_info = {
 	.valid_interfaces = BIT(4),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_3_p_info = {
@@ -499,6 +564,7 @@ static const struct steelseries_device_info arctis_nova_3_p_info = {
 	.valid_interfaces = BIT(0),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY |
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_VOLUME,
+	.report_data = &report_data__nova_3p_3x,
 };
 
 static const struct steelseries_device_info arctis_nova_3_x_info = {
@@ -508,6 +574,7 @@ static const struct steelseries_device_info arctis_nova_3_x_info = {
 	.valid_interfaces = BIT(0),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY |
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_VOLUME,
+	.report_data = &report_data__nova_3p_3x,
 };
 
 static const struct steelseries_device_info arctis_nova_5_info = {
@@ -518,6 +585,7 @@ static const struct steelseries_device_info arctis_nova_5_info = {
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_CHATMIX |
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_5_x_info = {
@@ -528,6 +596,7 @@ static const struct steelseries_device_info arctis_nova_5_x_info = {
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_CHATMIX |
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_info = {
@@ -539,6 +608,7 @@ static const struct steelseries_device_info arctis_nova_7_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_x_info = {
@@ -550,6 +620,7 @@ static const struct steelseries_device_info arctis_nova_7_x_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_p_info = {
@@ -561,6 +632,7 @@ static const struct steelseries_device_info arctis_nova_7_p_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_x_rev2_info = {
@@ -572,6 +644,7 @@ static const struct steelseries_device_info arctis_nova_7_x_rev2_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_diablo_info = {
@@ -583,6 +656,7 @@ static const struct steelseries_device_info arctis_nova_7_diablo_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_wow_info = {
@@ -594,6 +668,7 @@ static const struct steelseries_device_info arctis_nova_7_wow_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_gen2_info = {
@@ -605,6 +680,7 @@ static const struct steelseries_device_info arctis_nova_7_gen2_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_7_x_gen2_info = {
@@ -616,6 +692,7 @@ static const struct steelseries_device_info arctis_nova_7_x_gen2_info = {
 			SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_LED |
 			SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER |
 			SS_CAP_BT_POWER_ON | SS_CAP_BT_CALL_VOL,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_pro_info = {
@@ -624,6 +701,7 @@ static const struct steelseries_device_info arctis_nova_pro_info = {
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(4),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__7plus_nova,
 };
 
 static const struct steelseries_device_info arctis_nova_pro_x_info = {
@@ -632,6 +710,7 @@ static const struct steelseries_device_info arctis_nova_pro_x_info = {
 	.interface_binding_mode = 1,
 	.valid_interfaces = BIT(4),
 	.capabilities = SS_CAP_SIDETONE | SS_CAP_BATTERY | SS_CAP_INACTIVE_TIME,
+	.report_data = &report_data__7plus_nova,
 };
 
 #define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000
-- 
2.52.0
[PATCH 3/3] HID: steelseries: Use device data for battery requests
Posted by Benjamin Wheeler 2 weeks, 3 days ago
This eliminates several functions that are no longer needed.
When making a battery request, the required data to do so is simply
pulled from the device struct.

Signed-off-by: Benjamin Wheeler <benjaminwheeler0510@gmail.com>
---
 drivers/hid/hid-steelseries.c | 93 +++++------------------------------
 1 file changed, 11 insertions(+), 82 deletions(-)

diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c
index dabc4763f072..1200213bc8f3 100644
--- a/drivers/hid/hid-steelseries.c
+++ b/drivers/hid/hid-steelseries.c
@@ -809,44 +809,17 @@ static enum power_supply_property steelseries_battery_props[] = {
 	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
 };
 
-/* Forward declarations for battery request functions */
-static int steelseries_arctis_1_request_battery(struct hid_device *hdev);
-static int steelseries_arctis_7_plus_request_battery(struct hid_device *hdev);
-static int steelseries_arctis_9_request_battery(struct hid_device *hdev);
-static int steelseries_arctis_nova_request_battery(struct hid_device *hdev);
-static int steelseries_arctis_nova_3p_request_battery(struct hid_device *hdev);
-static int
-steelseries_arctis_pro_wireless_request_battery(struct hid_device *hdev);
+static int __steelseries_send_report(struct hid_device *const hdev,
+				     const u8 *const data, const size_t len,
+				     const enum hid_report_type type);
 
-static int steelseries_request_battery(struct hid_device *hdev)
+static int
+steelseries_request_battery(const struct steelseries_device *const sd)
 {
-	u16 product = hdev->product;
-
-	/* Route to device-specific battery request handler */
-	if (product == USB_DEVICE_ID_STEELSERIES_ARCTIS_1 ||
-	    product == USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X ||
-	    product == USB_DEVICE_ID_STEELSERIES_ARCTIS_7_P ||
-	    product == USB_DEVICE_ID_STEELSERIES_ARCTIS_7_X)
-		return steelseries_arctis_1_request_battery(hdev);
-
-	if (product == USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS ||
-	    product == USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_P ||
-	    product == USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_X ||
-	    product == USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_DESTINY)
-		return steelseries_arctis_7_plus_request_battery(hdev);
-
-	if (product == USB_DEVICE_ID_STEELSERIES_ARCTIS_9)
-		return steelseries_arctis_9_request_battery(hdev);
-
-	if (product == USB_DEVICE_ID_STEELSERIES_ARCTIS_PRO)
-		return steelseries_arctis_pro_wireless_request_battery(hdev);
-
-	if (product == USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_3_P ||
-	    product == USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_3_X)
-		return steelseries_arctis_nova_3p_request_battery(hdev);
-
-	/* All other Nova series use the same battery request */
-	return steelseries_arctis_nova_request_battery(hdev);
+	const struct steelseries_report_data *const report_data =
+		sd->info->report_data; // Alias
+	return __steelseries_send_report(sd->hdev, report_data->data,
+					 report_data->len, report_data->type);
 }
 
 static void steelseries_battery_timer_tick(struct work_struct *work)
@@ -854,7 +827,7 @@ static void steelseries_battery_timer_tick(struct work_struct *work)
 	struct steelseries_device *sd = container_of(
 		work, struct steelseries_device, battery_work.work);
 
-	steelseries_request_battery(sd->hdev);
+	steelseries_request_battery(sd);
 }
 
 static int steelseries_battery_register(struct steelseries_device *sd)
@@ -895,7 +868,7 @@ static int steelseries_battery_register(struct steelseries_device *sd)
 	power_supply_powers(sd->battery, &sd->hdev->dev);
 
 	INIT_DELAYED_WORK(&sd->battery_work, steelseries_battery_timer_tick);
-	steelseries_request_battery(sd->hdev);
+	steelseries_request_battery(sd);
 
 	/* Arctis 9 may need a retry */
 	if (sd->hdev->product == USB_DEVICE_ID_STEELSERIES_ARCTIS_9) {
@@ -1685,50 +1658,6 @@ static const struct attribute_group steelseries_attr_group = {
 	.is_visible = steelseries_attr_is_visible,
 };
 
-static int steelseries_arctis_1_request_battery(struct hid_device *hdev)
-{
-	const u8 data[] = { 0x06, 0x12 };
-
-	return steelseries_send_feature_report(hdev, data, sizeof(data));
-}
-
-static int steelseries_arctis_7_plus_request_battery(struct hid_device *hdev)
-{
-	const u8 data[] = { 0x00, 0xb0 };
-
-	return steelseries_send_output_report(hdev, data, sizeof(data));
-}
-
-static int steelseries_arctis_9_request_battery(struct hid_device *hdev)
-{
-	const u8 data[] = { 0x00, 0x20 };
-
-	return steelseries_send_feature_report(hdev, data, sizeof(data));
-}
-
-static int steelseries_arctis_nova_request_battery(struct hid_device *hdev)
-{
-	const u8 data[] = { 0x00, 0xb0 };
-
-	return steelseries_send_output_report(hdev, data, sizeof(data));
-}
-
-static int steelseries_arctis_nova_3p_request_battery(struct hid_device *hdev)
-{
-	const u8 data[] = { 0xb0 };
-
-	return steelseries_send_output_report(hdev, data, sizeof(data));
-}
-
-static int
-steelseries_arctis_pro_wireless_request_battery(struct hid_device *hdev)
-{
-	/* Request battery - response will arrive asynchronously via raw_event */
-	const u8 data[] = { 0x40, 0xAA };
-
-	return steelseries_send_output_report(hdev, data, sizeof(data));
-}
-
 static int steelseries_raw_event(struct hid_device *hdev,
 				 struct hid_report *report, u8 *data, int size)
 {
-- 
2.52.0
Re: [PATCH v2 0/4] HID: steelseries: Add support for Arctis headset lineup
Posted by Benjamin Wheeler 2 weeks, 3 days ago
Hello,

I'm Benjamin, and I coincidentally have also been looking into this 
driver. Sriman, if you would like, I'd be happy to work together on this 
driver with you! In reviewing your patches, I have found and fixed a bug 
in my local tree regarding battery requests.

The Steelseries Arctis 7 and Arctis 7 (2019 ed.) are not implemented in 
the battery request logic, and simply report 100% (the default value 
during battery registration). I discovered this bug while testing your 
driver with my Arctis 7 :-)

Would you like me to send a formal patch for this? I also have some 
patches I can send that clean up the battery logic in general that make 
the code less complex.

I am also happy to look into creating ALSA mixers/switches as well. 
Please let me know how I can help! Excellent work so far, and smart 
choice in using HeadsetControl, they've done most of the heavy lifting 
for us.


Sincerely,

Benjamin Wheeler


On 1/11/26 11:19 PM, Sriman Achanta wrote:
> This patch series adds comprehensive support for the SteelSeries Arctis
> wireless gaming headset lineup to the hid-steelseries driver.
>
> The current driver provides only basic battery monitoring for Arctis 1
> and Arctis 9. This series extends support to 25+ Arctis models with
> full feature control including sidetone, auto-sleep, microphone
> controls, volume limiting, and Bluetooth settings.
>
> The driver restructure uses a capability-based device info system to
> cleanly handle the varying feature sets across different Arctis
> generations while maintaining support for the legacy SRW-S1 racing
> wheel.
>
> Patch 1: Add 27 new device IDs to hid-ids.h
> Patch 2: Add HID quirks for proper device initialization
> Patch 3: Update ABI documentation for new sysfs attributes
> Patch 4: Complete driver implementation with all features
>
> Tested on Arctis Nova 7 (0x2202). All other implementation details are
> based on the reverse engineering done in the HeadsetControl library
> (abe3ac8).
>
> V2:
> - Fix Documentation formatting issues
>
> Sriman Achanta (4):
>    HID: hid-ids: Add SteelSeries Arctis headset device IDs
>    HID: quirks: Add INPUT_CONFIGURED quirk for SteelSeries Arctis
>      headsets
>    Documentation: ABI: Document SteelSeries headset sysfs attributes
>    HID: steelseries: Add support for Arctis headset lineup
>
>   .../ABI/testing/sysfs-driver-hid-srws1        |   21 -
>   .../ABI/testing/sysfs-driver-hid-steelseries  |  131 ++
>   drivers/hid/hid-ids.h                         |   33 +-
>   drivers/hid/hid-quirks.c                      |   25 +
>   drivers/hid/hid-steelseries.c                 | 2061 ++++++++++++++---
>   5 files changed, 1925 insertions(+), 346 deletions(-)
>   delete mode 100644 Documentation/ABI/testing/sysfs-driver-hid-srws1
>   create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-steelseries
>