19
19
20
20
#define STEELSERIES_SRWS1 BIT(0)
21
21
#define STEELSERIES_ARCTIS_1 BIT(1)
22
+ #define STEELSERIES_ARCTIS_9 BIT(2)
22
23
23
24
struct steelseries_device {
24
25
struct hid_device * hdev ;
@@ -375,7 +376,9 @@ static void steelseries_srws1_remove(struct hid_device *hdev)
375
376
#define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000
376
377
377
378
#define ARCTIS_1_BATTERY_RESPONSE_LEN 8
379
+ #define ARCTIS_9_BATTERY_RESPONSE_LEN 64
378
380
static const char arctis_1_battery_request [] = { 0x06 , 0x12 };
381
+ static const char arctis_9_battery_request [] = { 0x00 , 0x20 };
379
382
380
383
static int steelseries_headset_request_battery (struct hid_device * hdev ,
381
384
const char * request , size_t len )
@@ -395,6 +398,7 @@ static int steelseries_headset_request_battery(struct hid_device *hdev,
395
398
hid_err (hdev , "hid_hw_raw_request() failed with %d\n" , ret );
396
399
ret = - ENODATA ;
397
400
}
401
+
398
402
kfree (write_buf );
399
403
return ret ;
400
404
}
@@ -407,6 +411,9 @@ static void steelseries_headset_fetch_battery(struct hid_device *hdev)
407
411
if (sd -> quirks & STEELSERIES_ARCTIS_1 )
408
412
ret = steelseries_headset_request_battery (hdev ,
409
413
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 ));
410
417
411
418
if (ret < 0 )
412
419
hid_dbg (hdev ,
@@ -522,9 +529,22 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd)
522
529
INIT_DELAYED_WORK (& sd -> battery_work , steelseries_headset_battery_timer_tick );
523
530
steelseries_headset_fetch_battery (sd -> hdev );
524
531
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
+
525
538
return 0 ;
526
539
}
527
540
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
+
528
548
static int steelseries_probe (struct hid_device * hdev , const struct hid_device_id * id )
529
549
{
530
550
struct steelseries_device * sd ;
@@ -550,6 +570,10 @@ static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id
550
570
if (ret )
551
571
return ret ;
552
572
573
+ if (sd -> quirks & STEELSERIES_ARCTIS_9 &&
574
+ !steelseries_is_vendor_usage_page (hdev , 0xc0 ))
575
+ return - ENODEV ;
576
+
553
577
spin_lock_init (& sd -> lock );
554
578
555
579
ret = hid_hw_start (hdev , HID_CONNECT_DEFAULT );
@@ -606,6 +630,15 @@ static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev,
606
630
return rdesc ;
607
631
}
608
632
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
+
609
642
static int steelseries_headset_raw_event (struct hid_device * hdev ,
610
643
struct hid_report * report , u8 * read_buf ,
611
644
int size )
@@ -637,6 +670,32 @@ static int steelseries_headset_raw_event(struct hid_device *hdev,
637
670
}
638
671
}
639
672
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
+
640
699
if (connected != sd -> headset_connected ) {
641
700
hid_dbg (sd -> hdev ,
642
701
"Connected status changed from %sconnected to %sconnected\n" ,
@@ -672,6 +731,10 @@ static const struct hid_device_id steelseries_devices[] = {
672
731
HID_USB_DEVICE (USB_VENDOR_ID_STEELSERIES , 0x12b6 ),
673
732
.driver_data = STEELSERIES_ARCTIS_1 },
674
733
734
+ { /* SteelSeries Arctis 9 Wireless for XBox */
735
+ HID_USB_DEVICE (USB_VENDOR_ID_STEELSERIES , 0x12c2 ),
736
+ .driver_data = STEELSERIES_ARCTIS_9 },
737
+
675
738
{ }
676
739
};
677
740
MODULE_DEVICE_TABLE (hid , steelseries_devices );
@@ -690,3 +753,4 @@ MODULE_DESCRIPTION("HID driver for Steelseries devices");
690
753
MODULE_LICENSE ("GPL" );
691
754
MODULE_AUTHOR ("Bastien Nocera <hadess@hadess.net>" );
692
755
MODULE_AUTHOR ("Simon Wood <simon@mungewell.org>" );
756
+ MODULE_AUTHOR ("Christian Mayer <git@mayer-bgk.de>" );
0 commit comments