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 ;
@@ -32,6 +33,7 @@ struct steelseries_device {
32
33
struct power_supply * battery ;
33
34
uint8_t battery_capacity ;
34
35
bool headset_connected ;
36
+ bool battery_charging ;
35
37
};
36
38
37
39
#if IS_BUILTIN (CONFIG_LEDS_CLASS ) || \
@@ -368,32 +370,35 @@ static void steelseries_srws1_remove(struct hid_device *hdev)
368
370
369
371
hid_hw_stop (hdev );
370
372
kfree (drv_data );
371
- return ;
372
373
}
373
374
#endif
374
375
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
- static int steelseries_headset_arctis_1_fetch_battery (struct hid_device * hdev )
383
+ static int steelseries_headset_request_battery (struct hid_device * hdev ,
384
+ const char * request , size_t len )
381
385
{
382
386
u8 * write_buf ;
383
387
int ret ;
384
388
385
389
/* Request battery information */
386
- write_buf = kmemdup (arctis_1_battery_request , sizeof ( arctis_1_battery_request ) , GFP_KERNEL );
390
+ write_buf = kmemdup (request , len , GFP_KERNEL );
387
391
if (!write_buf )
388
392
return - ENOMEM ;
389
393
390
- ret = hid_hw_raw_request (hdev , arctis_1_battery_request [ 0 ],
391
- write_buf , sizeof ( arctis_1_battery_request ) ,
394
+ hid_dbg (hdev , "Sending battery request report" );
395
+ ret = hid_hw_raw_request ( hdev , request [ 0 ], write_buf , len ,
392
396
HID_OUTPUT_REPORT , HID_REQ_SET_REPORT );
393
- if (ret < (int )sizeof ( arctis_1_battery_request ) ) {
397
+ if (ret < (int )len ) {
394
398
hid_err (hdev , "hid_hw_raw_request() failed with %d\n" , ret );
395
399
ret = - ENODATA ;
396
400
}
401
+
397
402
kfree (write_buf );
398
403
return ret ;
399
404
}
@@ -404,7 +409,11 @@ static void steelseries_headset_fetch_battery(struct hid_device *hdev)
404
409
int ret = 0 ;
405
410
406
411
if (sd -> quirks & STEELSERIES_ARCTIS_1 )
407
- ret = steelseries_headset_arctis_1_fetch_battery (hdev );
412
+ ret = steelseries_headset_request_battery (hdev ,
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 ));
408
417
409
418
if (ret < 0 )
410
419
hid_dbg (hdev ,
@@ -429,6 +438,9 @@ static void steelseries_headset_battery_timer_tick(struct work_struct *work)
429
438
steelseries_headset_fetch_battery (hdev );
430
439
}
431
440
441
+ #define STEELSERIES_PREFIX "SteelSeries "
442
+ #define STEELSERIES_PREFIX_LEN strlen(STEELSERIES_PREFIX)
443
+
432
444
static int steelseries_headset_battery_get_property (struct power_supply * psy ,
433
445
enum power_supply_property psp ,
434
446
union power_supply_propval * val )
@@ -437,13 +449,24 @@ static int steelseries_headset_battery_get_property(struct power_supply *psy,
437
449
int ret = 0 ;
438
450
439
451
switch (psp ) {
452
+ case POWER_SUPPLY_PROP_MODEL_NAME :
453
+ val -> strval = sd -> hdev -> name ;
454
+ while (!strncmp (val -> strval , STEELSERIES_PREFIX , STEELSERIES_PREFIX_LEN ))
455
+ val -> strval += STEELSERIES_PREFIX_LEN ;
456
+ break ;
457
+ case POWER_SUPPLY_PROP_MANUFACTURER :
458
+ val -> strval = "SteelSeries" ;
459
+ break ;
440
460
case POWER_SUPPLY_PROP_PRESENT :
441
461
val -> intval = 1 ;
442
462
break ;
443
463
case POWER_SUPPLY_PROP_STATUS :
444
- val -> intval = sd -> headset_connected ?
445
- POWER_SUPPLY_STATUS_DISCHARGING :
446
- POWER_SUPPLY_STATUS_UNKNOWN ;
464
+ if (sd -> headset_connected ) {
465
+ val -> intval = sd -> battery_charging ?
466
+ POWER_SUPPLY_STATUS_CHARGING :
467
+ POWER_SUPPLY_STATUS_DISCHARGING ;
468
+ } else
469
+ val -> intval = POWER_SUPPLY_STATUS_UNKNOWN ;
447
470
break ;
448
471
case POWER_SUPPLY_PROP_SCOPE :
449
472
val -> intval = POWER_SUPPLY_SCOPE_DEVICE ;
@@ -477,6 +500,8 @@ steelseries_headset_set_wireless_status(struct hid_device *hdev,
477
500
}
478
501
479
502
static enum power_supply_property steelseries_headset_battery_props [] = {
503
+ POWER_SUPPLY_PROP_MODEL_NAME ,
504
+ POWER_SUPPLY_PROP_MANUFACTURER ,
480
505
POWER_SUPPLY_PROP_PRESENT ,
481
506
POWER_SUPPLY_PROP_STATUS ,
482
507
POWER_SUPPLY_PROP_SCOPE ,
@@ -505,6 +530,7 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd)
505
530
/* avoid the warning of 0% battery while waiting for the first info */
506
531
steelseries_headset_set_wireless_status (sd -> hdev , false);
507
532
sd -> battery_capacity = 100 ;
533
+ sd -> battery_charging = false;
508
534
509
535
sd -> battery = devm_power_supply_register (& sd -> hdev -> dev ,
510
536
& sd -> battery_desc , & battery_cfg );
@@ -520,9 +546,22 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd)
520
546
INIT_DELAYED_WORK (& sd -> battery_work , steelseries_headset_battery_timer_tick );
521
547
steelseries_headset_fetch_battery (sd -> hdev );
522
548
549
+ if (sd -> quirks & STEELSERIES_ARCTIS_9 ) {
550
+ /* The first fetch_battery request can remain unanswered in some cases */
551
+ schedule_delayed_work (& sd -> battery_work ,
552
+ msecs_to_jiffies (STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS ));
553
+ }
554
+
523
555
return 0 ;
524
556
}
525
557
558
+ static bool steelseries_is_vendor_usage_page (struct hid_device * hdev , uint8_t usage_page )
559
+ {
560
+ return hdev -> rdesc [0 ] == 0x06 &&
561
+ hdev -> rdesc [1 ] == usage_page &&
562
+ hdev -> rdesc [2 ] == 0xff ;
563
+ }
564
+
526
565
static int steelseries_probe (struct hid_device * hdev , const struct hid_device_id * id )
527
566
{
528
567
struct steelseries_device * sd ;
@@ -548,12 +587,20 @@ static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id
548
587
if (ret )
549
588
return ret ;
550
589
590
+ if (sd -> quirks & STEELSERIES_ARCTIS_9 &&
591
+ !steelseries_is_vendor_usage_page (hdev , 0xc0 ))
592
+ return - ENODEV ;
593
+
551
594
spin_lock_init (& sd -> lock );
552
595
553
596
ret = hid_hw_start (hdev , HID_CONNECT_DEFAULT );
554
597
if (ret )
555
598
return ret ;
556
599
600
+ ret = hid_hw_open (hdev );
601
+ if (ret )
602
+ return ret ;
603
+
557
604
if (steelseries_headset_battery_register (sd ) < 0 )
558
605
hid_err (sd -> hdev ,
559
606
"Failed to register battery for headset\n" );
@@ -580,6 +627,7 @@ static void steelseries_remove(struct hid_device *hdev)
580
627
581
628
cancel_delayed_work_sync (& sd -> battery_work );
582
629
630
+ hid_hw_close (hdev );
583
631
hid_hw_stop (hdev );
584
632
}
585
633
@@ -599,13 +647,23 @@ static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev,
599
647
return rdesc ;
600
648
}
601
649
650
+ static uint8_t steelseries_headset_map_capacity (uint8_t capacity , uint8_t min_in , uint8_t max_in )
651
+ {
652
+ if (capacity >= max_in )
653
+ return 100 ;
654
+ if (capacity <= min_in )
655
+ return 0 ;
656
+ return (capacity - min_in ) * 100 / (max_in - min_in );
657
+ }
658
+
602
659
static int steelseries_headset_raw_event (struct hid_device * hdev ,
603
660
struct hid_report * report , u8 * read_buf ,
604
661
int size )
605
662
{
606
663
struct steelseries_device * sd = hid_get_drvdata (hdev );
607
664
int capacity = sd -> battery_capacity ;
608
665
bool connected = sd -> headset_connected ;
666
+ bool charging = sd -> battery_charging ;
609
667
unsigned long flags ;
610
668
611
669
/* Not a headset */
@@ -630,6 +688,34 @@ static int steelseries_headset_raw_event(struct hid_device *hdev,
630
688
}
631
689
}
632
690
691
+ if (sd -> quirks & STEELSERIES_ARCTIS_9 ) {
692
+ hid_dbg (sd -> hdev ,
693
+ "Parsing raw event for Arctis 9 headset (%*ph)\n" , size , read_buf );
694
+ if (size < ARCTIS_9_BATTERY_RESPONSE_LEN ) {
695
+ if (!delayed_work_pending (& sd -> battery_work ))
696
+ goto request_battery ;
697
+ return 0 ;
698
+ }
699
+
700
+ if (read_buf [0 ] == 0xaa && read_buf [1 ] == 0x01 ) {
701
+ connected = true;
702
+ charging = read_buf [4 ] == 0x01 ;
703
+
704
+ /*
705
+ * Found no official documentation about min and max.
706
+ * Values defined by testing.
707
+ */
708
+ capacity = steelseries_headset_map_capacity (read_buf [3 ], 0x68 , 0x9d );
709
+ } else {
710
+ /*
711
+ * Device is off and sends the last known status read_buf[1] == 0x03 or
712
+ * there is no known status of the device read_buf[0] == 0x55
713
+ */
714
+ connected = false;
715
+ charging = false;
716
+ }
717
+ }
718
+
633
719
if (connected != sd -> headset_connected ) {
634
720
hid_dbg (sd -> hdev ,
635
721
"Connected status changed from %sconnected to %sconnected\n" ,
@@ -647,6 +733,15 @@ static int steelseries_headset_raw_event(struct hid_device *hdev,
647
733
power_supply_changed (sd -> battery );
648
734
}
649
735
736
+ if (charging != sd -> battery_charging ) {
737
+ hid_dbg (sd -> hdev ,
738
+ "Battery charging status changed from %scharging to %scharging\n" ,
739
+ sd -> battery_charging ? "" : "not " ,
740
+ charging ? "" : "not " );
741
+ sd -> battery_charging = charging ;
742
+ power_supply_changed (sd -> battery );
743
+ }
744
+
650
745
request_battery :
651
746
spin_lock_irqsave (& sd -> lock , flags );
652
747
if (!sd -> removed )
@@ -665,6 +760,10 @@ static const struct hid_device_id steelseries_devices[] = {
665
760
HID_USB_DEVICE (USB_VENDOR_ID_STEELSERIES , 0x12b6 ),
666
761
.driver_data = STEELSERIES_ARCTIS_1 },
667
762
763
+ { /* SteelSeries Arctis 9 Wireless for XBox */
764
+ HID_USB_DEVICE (USB_VENDOR_ID_STEELSERIES , 0x12c2 ),
765
+ .driver_data = STEELSERIES_ARCTIS_9 },
766
+
668
767
{ }
669
768
};
670
769
MODULE_DEVICE_TABLE (hid , steelseries_devices );
@@ -683,3 +782,4 @@ MODULE_DESCRIPTION("HID driver for Steelseries devices");
683
782
MODULE_LICENSE ("GPL" );
684
783
MODULE_AUTHOR ("Bastien Nocera <hadess@hadess.net>" );
685
784
MODULE_AUTHOR ("Simon Wood <simon@mungewell.org>" );
785
+ MODULE_AUTHOR ("Christian Mayer <git@mayer-bgk.de>" );
0 commit comments