11
11
#include <linux/hid.h>
12
12
#include <linux/idr.h>
13
13
#include <linux/input/mt.h>
14
+ #include <linux/leds.h>
15
+ #include <linux/led-class-multicolor.h>
14
16
#include <linux/module.h>
15
17
16
18
#include <asm/unaligned.h>
@@ -38,6 +40,7 @@ struct ps_device {
38
40
uint8_t battery_capacity ;
39
41
int battery_status ;
40
42
43
+ const char * input_dev_name ; /* Name of primary input device. */
41
44
uint8_t mac_address [6 ]; /* Note: stored in little endian order. */
42
45
uint32_t hw_version ;
43
46
uint32_t fw_version ;
@@ -53,6 +56,13 @@ struct ps_calibration_data {
53
56
int sens_denom ;
54
57
};
55
58
59
+ struct ps_led_info {
60
+ const char * name ;
61
+ const char * color ;
62
+ enum led_brightness (* brightness_get )(struct led_classdev * cdev );
63
+ int (* brightness_set )(struct led_classdev * cdev , enum led_brightness );
64
+ };
65
+
56
66
/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
57
67
#define PS_INPUT_CRC32_SEED 0xA1
58
68
#define PS_OUTPUT_CRC32_SEED 0xA2
@@ -147,6 +157,7 @@ struct dualsense {
147
157
uint8_t motor_right ;
148
158
149
159
/* RGB lightbar */
160
+ struct led_classdev_mc lightbar ;
150
161
bool update_lightbar ;
151
162
uint8_t lightbar_red ;
152
163
uint8_t lightbar_green ;
@@ -288,6 +299,8 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
288
299
{0 , 0 },
289
300
};
290
301
302
+ static void dualsense_set_lightbar (struct dualsense * ds , uint8_t red , uint8_t green , uint8_t blue );
303
+
291
304
/*
292
305
* Add a new ps_device to ps_devices if it doesn't exist.
293
306
* Return error on duplicate device, which can happen if the same
@@ -525,6 +538,71 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu
525
538
return 0 ;
526
539
}
527
540
541
+ static int ps_led_register (struct ps_device * ps_dev , struct led_classdev * led ,
542
+ const struct ps_led_info * led_info )
543
+ {
544
+ int ret ;
545
+
546
+ led -> name = devm_kasprintf (& ps_dev -> hdev -> dev , GFP_KERNEL ,
547
+ "%s:%s:%s" , ps_dev -> input_dev_name , led_info -> color , led_info -> name );
548
+
549
+ if (!led -> name )
550
+ return - ENOMEM ;
551
+
552
+ led -> brightness = 0 ;
553
+ led -> max_brightness = 1 ;
554
+ led -> flags = LED_CORE_SUSPENDRESUME ;
555
+ led -> brightness_get = led_info -> brightness_get ;
556
+ led -> brightness_set_blocking = led_info -> brightness_set ;
557
+
558
+ ret = devm_led_classdev_register (& ps_dev -> hdev -> dev , led );
559
+ if (ret ) {
560
+ hid_err (ps_dev -> hdev , "Failed to register LED %s: %d\n" , led_info -> name , ret );
561
+ return ret ;
562
+ }
563
+
564
+ return 0 ;
565
+ }
566
+
567
+ /* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */
568
+ static int ps_lightbar_register (struct ps_device * ps_dev , struct led_classdev_mc * lightbar_mc_dev ,
569
+ int (* brightness_set )(struct led_classdev * , enum led_brightness ))
570
+ {
571
+ struct hid_device * hdev = ps_dev -> hdev ;
572
+ struct mc_subled * mc_led_info ;
573
+ struct led_classdev * led_cdev ;
574
+ int ret ;
575
+
576
+ mc_led_info = devm_kmalloc_array (& hdev -> dev , 3 , sizeof (* mc_led_info ),
577
+ GFP_KERNEL | __GFP_ZERO );
578
+ if (!mc_led_info )
579
+ return - ENOMEM ;
580
+
581
+ mc_led_info [0 ].color_index = LED_COLOR_ID_RED ;
582
+ mc_led_info [1 ].color_index = LED_COLOR_ID_GREEN ;
583
+ mc_led_info [2 ].color_index = LED_COLOR_ID_BLUE ;
584
+
585
+ lightbar_mc_dev -> subled_info = mc_led_info ;
586
+ lightbar_mc_dev -> num_colors = 3 ;
587
+
588
+ led_cdev = & lightbar_mc_dev -> led_cdev ;
589
+ led_cdev -> name = devm_kasprintf (& hdev -> dev , GFP_KERNEL , "%s:rgb:indicator" ,
590
+ ps_dev -> input_dev_name );
591
+ if (!led_cdev -> name )
592
+ return - ENOMEM ;
593
+ led_cdev -> brightness = 255 ;
594
+ led_cdev -> max_brightness = 255 ;
595
+ led_cdev -> brightness_set_blocking = brightness_set ;
596
+
597
+ ret = devm_led_classdev_multicolor_register (& hdev -> dev , lightbar_mc_dev );
598
+ if (ret < 0 ) {
599
+ hid_err (hdev , "Cannot register multicolor LED device\n" );
600
+ return ret ;
601
+ }
602
+
603
+ return 0 ;
604
+ }
605
+
528
606
static struct input_dev * ps_sensors_create (struct hid_device * hdev , int accel_range , int accel_res ,
529
607
int gyro_range , int gyro_res )
530
608
{
@@ -761,6 +839,53 @@ static int dualsense_get_mac_address(struct dualsense *ds)
761
839
return ret ;
762
840
}
763
841
842
+ static int dualsense_lightbar_set_brightness (struct led_classdev * cdev ,
843
+ enum led_brightness brightness )
844
+ {
845
+ struct led_classdev_mc * mc_cdev = lcdev_to_mccdev (cdev );
846
+ struct dualsense * ds = container_of (mc_cdev , struct dualsense , lightbar );
847
+ uint8_t red , green , blue ;
848
+
849
+ led_mc_calc_color_components (mc_cdev , brightness );
850
+ red = mc_cdev -> subled_info [0 ].brightness ;
851
+ green = mc_cdev -> subled_info [1 ].brightness ;
852
+ blue = mc_cdev -> subled_info [2 ].brightness ;
853
+
854
+ dualsense_set_lightbar (ds , red , green , blue );
855
+ return 0 ;
856
+ }
857
+
858
+ static enum led_brightness dualsense_player_led_get_brightness (struct led_classdev * led )
859
+ {
860
+ struct hid_device * hdev = to_hid_device (led -> dev -> parent );
861
+ struct dualsense * ds = hid_get_drvdata (hdev );
862
+
863
+ return !!(ds -> player_leds_state & BIT (led - ds -> player_leds ));
864
+ }
865
+
866
+ static int dualsense_player_led_set_brightness (struct led_classdev * led , enum led_brightness value )
867
+ {
868
+ struct hid_device * hdev = to_hid_device (led -> dev -> parent );
869
+ struct dualsense * ds = hid_get_drvdata (hdev );
870
+ unsigned long flags ;
871
+ unsigned int led_index ;
872
+
873
+ spin_lock_irqsave (& ds -> base .lock , flags );
874
+
875
+ led_index = led - ds -> player_leds ;
876
+ if (value == LED_OFF )
877
+ ds -> player_leds_state &= ~BIT (led_index );
878
+ else
879
+ ds -> player_leds_state |= BIT (led_index );
880
+
881
+ ds -> update_player_leds = true;
882
+ spin_unlock_irqrestore (& ds -> base .lock , flags );
883
+
884
+ schedule_work (& ds -> output_worker );
885
+
886
+ return 0 ;
887
+ }
888
+
764
889
static void dualsense_init_output_report (struct dualsense * ds , struct dualsense_output_report * rp ,
765
890
void * buf )
766
891
{
@@ -1106,10 +1231,14 @@ static int dualsense_reset_leds(struct dualsense *ds)
1106
1231
1107
1232
static void dualsense_set_lightbar (struct dualsense * ds , uint8_t red , uint8_t green , uint8_t blue )
1108
1233
{
1234
+ unsigned long flags ;
1235
+
1236
+ spin_lock_irqsave (& ds -> base .lock , flags );
1109
1237
ds -> update_lightbar = true;
1110
1238
ds -> lightbar_red = red ;
1111
1239
ds -> lightbar_green = green ;
1112
1240
ds -> lightbar_blue = blue ;
1241
+ spin_unlock_irqrestore (& ds -> base .lock , flags );
1113
1242
1114
1243
schedule_work (& ds -> output_worker );
1115
1244
}
@@ -1142,7 +1271,20 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
1142
1271
struct dualsense * ds ;
1143
1272
struct ps_device * ps_dev ;
1144
1273
uint8_t max_output_report_size ;
1145
- int ret ;
1274
+ int i , ret ;
1275
+
1276
+ static const struct ps_led_info player_leds_info [] = {
1277
+ { LED_FUNCTION_PLAYER1 , "white" , dualsense_player_led_get_brightness ,
1278
+ dualsense_player_led_set_brightness },
1279
+ { LED_FUNCTION_PLAYER2 , "white" , dualsense_player_led_get_brightness ,
1280
+ dualsense_player_led_set_brightness },
1281
+ { LED_FUNCTION_PLAYER3 , "white" , dualsense_player_led_get_brightness ,
1282
+ dualsense_player_led_set_brightness },
1283
+ { LED_FUNCTION_PLAYER4 , "white" , dualsense_player_led_get_brightness ,
1284
+ dualsense_player_led_set_brightness },
1285
+ { LED_FUNCTION_PLAYER5 , "white" , dualsense_player_led_get_brightness ,
1286
+ dualsense_player_led_set_brightness }
1287
+ };
1146
1288
1147
1289
ds = devm_kzalloc (& hdev -> dev , sizeof (* ds ), GFP_KERNEL );
1148
1290
if (!ds )
@@ -1196,6 +1338,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
1196
1338
ret = PTR_ERR (ds -> gamepad );
1197
1339
goto err ;
1198
1340
}
1341
+ /* Use gamepad input device name as primary device name for e.g. LEDs */
1342
+ ps_dev -> input_dev_name = dev_name (& ds -> gamepad -> dev );
1199
1343
1200
1344
ds -> sensors = ps_sensors_create (hdev , DS_ACC_RANGE , DS_ACC_RES_PER_G ,
1201
1345
DS_GYRO_RANGE , DS_GYRO_RES_PER_DEG_S );
@@ -1223,8 +1367,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
1223
1367
if (ret )
1224
1368
goto err ;
1225
1369
1370
+ ret = ps_lightbar_register (ps_dev , & ds -> lightbar , dualsense_lightbar_set_brightness );
1371
+ if (ret )
1372
+ goto err ;
1373
+
1374
+ /* Set default lightbar color. */
1226
1375
dualsense_set_lightbar (ds , 0 , 0 , 128 ); /* blue */
1227
1376
1377
+ for (i = 0 ; i < ARRAY_SIZE (player_leds_info ); i ++ ) {
1378
+ const struct ps_led_info * led_info = & player_leds_info [i ];
1379
+
1380
+ ret = ps_led_register (ps_dev , & ds -> player_leds [i ], led_info );
1381
+ if (ret < 0 )
1382
+ goto err ;
1383
+ }
1384
+
1228
1385
ret = ps_device_set_player_id (ps_dev );
1229
1386
if (ret ) {
1230
1387
hid_err (hdev , "Failed to assign player id for DualSense: %d\n" , ret );
0 commit comments