1919
2020#define STEELSERIES_SRWS1 BIT(0)
2121#define STEELSERIES_ARCTIS_1 BIT(1)
22+ #define STEELSERIES_ARCTIS_9 BIT(2)
2223
2324struct steelseries_device {
2425 struct hid_device * hdev ;
@@ -375,7 +376,9 @@ static void steelseries_srws1_remove(struct hid_device *hdev)
375376#define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000
376377
377378#define ARCTIS_1_BATTERY_RESPONSE_LEN 8
379+ #define ARCTIS_9_BATTERY_RESPONSE_LEN 64
378380static const char arctis_1_battery_request [] = { 0x06 , 0x12 };
381+ static const char arctis_9_battery_request [] = { 0x00 , 0x20 };
379382
380383static int steelseries_headset_request_battery (struct hid_device * hdev ,
381384 const char * request , size_t len )
@@ -395,6 +398,7 @@ static int steelseries_headset_request_battery(struct hid_device *hdev,
395398 hid_err (hdev , "hid_hw_raw_request() failed with %d\n" , ret );
396399 ret = - ENODATA ;
397400 }
401+
398402 kfree (write_buf );
399403 return ret ;
400404}
@@ -407,6 +411,9 @@ static void steelseries_headset_fetch_battery(struct hid_device *hdev)
407411 if (sd -> quirks & STEELSERIES_ARCTIS_1 )
408412 ret = steelseries_headset_request_battery (hdev ,
409413 arctis_1_battery_request , sizeof (arctis_1_battery_request ));
414+ else if (sd -> quirks & STEELSERIES_ARCTIS_9 )
415+ ret = steelseries_headset_request_battery (hdev ,
416+ arctis_9_battery_request , sizeof (arctis_9_battery_request ));
410417
411418 if (ret < 0 )
412419 hid_dbg (hdev ,
@@ -522,9 +529,22 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd)
522529 INIT_DELAYED_WORK (& sd -> battery_work , steelseries_headset_battery_timer_tick );
523530 steelseries_headset_fetch_battery (sd -> hdev );
524531
532+ if (sd -> quirks & STEELSERIES_ARCTIS_9 ) {
533+ /* The first fetch_battery request can remain unanswered in some cases */
534+ schedule_delayed_work (& sd -> battery_work ,
535+ msecs_to_jiffies (STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS ));
536+ }
537+
525538 return 0 ;
526539}
527540
541+ static bool steelseries_is_vendor_usage_page (struct hid_device * hdev , uint8_t usage_page )
542+ {
543+ return hdev -> rdesc [0 ] == 0x06 &&
544+ hdev -> rdesc [1 ] == usage_page &&
545+ hdev -> rdesc [2 ] == 0xff ;
546+ }
547+
528548static int steelseries_probe (struct hid_device * hdev , const struct hid_device_id * id )
529549{
530550 struct steelseries_device * sd ;
@@ -550,6 +570,10 @@ static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id
550570 if (ret )
551571 return ret ;
552572
573+ if (sd -> quirks & STEELSERIES_ARCTIS_9 &&
574+ !steelseries_is_vendor_usage_page (hdev , 0xc0 ))
575+ return - ENODEV ;
576+
553577 spin_lock_init (& sd -> lock );
554578
555579 ret = hid_hw_start (hdev , HID_CONNECT_DEFAULT );
@@ -606,6 +630,15 @@ static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev,
606630 return rdesc ;
607631}
608632
633+ static uint8_t steelseries_headset_map_capacity (uint8_t capacity , uint8_t min_in , uint8_t max_in )
634+ {
635+ if (capacity >= max_in )
636+ return 100 ;
637+ if (capacity <= min_in )
638+ return 0 ;
639+ return (capacity - min_in ) * 100 / (max_in - min_in );
640+ }
641+
609642static int steelseries_headset_raw_event (struct hid_device * hdev ,
610643 struct hid_report * report , u8 * read_buf ,
611644 int size )
@@ -637,6 +670,32 @@ static int steelseries_headset_raw_event(struct hid_device *hdev,
637670 }
638671 }
639672
673+ if (sd -> quirks & STEELSERIES_ARCTIS_9 ) {
674+ hid_dbg (sd -> hdev ,
675+ "Parsing raw event for Arctis 9 headset (%*ph)\n" , size , read_buf );
676+ if (size < ARCTIS_9_BATTERY_RESPONSE_LEN ) {
677+ if (!delayed_work_pending (& sd -> battery_work ))
678+ goto request_battery ;
679+ return 0 ;
680+ }
681+
682+ if (read_buf [0 ] == 0xaa && read_buf [1 ] == 0x01 ) {
683+ connected = true;
684+
685+ /*
686+ * Found no official documentation about min and max.
687+ * Values defined by testing.
688+ */
689+ capacity = steelseries_headset_map_capacity (read_buf [3 ], 0x68 , 0x9d );
690+ } else {
691+ /*
692+ * Device is off and sends the last known status read_buf[1] == 0x03 or
693+ * there is no known status of the device read_buf[0] == 0x55
694+ */
695+ connected = false;
696+ }
697+ }
698+
640699 if (connected != sd -> headset_connected ) {
641700 hid_dbg (sd -> hdev ,
642701 "Connected status changed from %sconnected to %sconnected\n" ,
@@ -672,6 +731,10 @@ static const struct hid_device_id steelseries_devices[] = {
672731 HID_USB_DEVICE (USB_VENDOR_ID_STEELSERIES , 0x12b6 ),
673732 .driver_data = STEELSERIES_ARCTIS_1 },
674733
734+ { /* SteelSeries Arctis 9 Wireless for XBox */
735+ HID_USB_DEVICE (USB_VENDOR_ID_STEELSERIES , 0x12c2 ),
736+ .driver_data = STEELSERIES_ARCTIS_9 },
737+
675738 { }
676739};
677740MODULE_DEVICE_TABLE (hid , steelseries_devices );
@@ -690,3 +753,4 @@ MODULE_DESCRIPTION("HID driver for Steelseries devices");
690753MODULE_LICENSE ("GPL" );
691754MODULE_AUTHOR (
"Bastien Nocera <[email protected] >" );
692755MODULE_AUTHOR (
"Simon Wood <[email protected] >" );
756+ MODULE_AUTHOR (
"Christian Mayer <[email protected] >" );
0 commit comments