1111#include <linux/hid.h>
1212#include <linux/idr.h>
1313#include <linux/input/mt.h>
14+ #include <linux/leds.h>
15+ #include <linux/led-class-multicolor.h>
1416#include <linux/module.h>
1517
1618#include <asm/unaligned.h>
@@ -38,6 +40,7 @@ struct ps_device {
3840 uint8_t battery_capacity ;
3941 int battery_status ;
4042
43+ const char * input_dev_name ; /* Name of primary input device. */
4144 uint8_t mac_address [6 ]; /* Note: stored in little endian order. */
4245 uint32_t hw_version ;
4346 uint32_t fw_version ;
@@ -53,6 +56,13 @@ struct ps_calibration_data {
5356 int sens_denom ;
5457};
5558
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+
5666/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
5767#define PS_INPUT_CRC32_SEED 0xA1
5868#define PS_OUTPUT_CRC32_SEED 0xA2
@@ -147,6 +157,7 @@ struct dualsense {
147157 uint8_t motor_right ;
148158
149159 /* RGB lightbar */
160+ struct led_classdev_mc lightbar ;
150161 bool update_lightbar ;
151162 uint8_t lightbar_red ;
152163 uint8_t lightbar_green ;
@@ -288,6 +299,8 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
288299 {0 , 0 },
289300};
290301
302+ static void dualsense_set_lightbar (struct dualsense * ds , uint8_t red , uint8_t green , uint8_t blue );
303+
291304/*
292305 * Add a new ps_device to ps_devices if it doesn't exist.
293306 * 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
525538 return 0 ;
526539}
527540
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+
528606static struct input_dev * ps_sensors_create (struct hid_device * hdev , int accel_range , int accel_res ,
529607 int gyro_range , int gyro_res )
530608{
@@ -761,6 +839,53 @@ static int dualsense_get_mac_address(struct dualsense *ds)
761839 return ret ;
762840}
763841
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+
764889static void dualsense_init_output_report (struct dualsense * ds , struct dualsense_output_report * rp ,
765890 void * buf )
766891{
@@ -1106,10 +1231,14 @@ static int dualsense_reset_leds(struct dualsense *ds)
11061231
11071232static void dualsense_set_lightbar (struct dualsense * ds , uint8_t red , uint8_t green , uint8_t blue )
11081233{
1234+ unsigned long flags ;
1235+
1236+ spin_lock_irqsave (& ds -> base .lock , flags );
11091237 ds -> update_lightbar = true;
11101238 ds -> lightbar_red = red ;
11111239 ds -> lightbar_green = green ;
11121240 ds -> lightbar_blue = blue ;
1241+ spin_unlock_irqrestore (& ds -> base .lock , flags );
11131242
11141243 schedule_work (& ds -> output_worker );
11151244}
@@ -1142,7 +1271,20 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
11421271 struct dualsense * ds ;
11431272 struct ps_device * ps_dev ;
11441273 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+ };
11461288
11471289 ds = devm_kzalloc (& hdev -> dev , sizeof (* ds ), GFP_KERNEL );
11481290 if (!ds )
@@ -1196,6 +1338,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
11961338 ret = PTR_ERR (ds -> gamepad );
11971339 goto err ;
11981340 }
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 );
11991343
12001344 ds -> sensors = ps_sensors_create (hdev , DS_ACC_RANGE , DS_ACC_RES_PER_G ,
12011345 DS_GYRO_RANGE , DS_GYRO_RES_PER_DEG_S );
@@ -1223,8 +1367,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
12231367 if (ret )
12241368 goto err ;
12251369
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. */
12261375 dualsense_set_lightbar (ds , 0 , 0 , 128 ); /* blue */
12271376
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+
12281385 ret = ps_device_set_player_id (ps_dev );
12291386 if (ret ) {
12301387 hid_err (hdev , "Failed to assign player id for DualSense: %d\n" , ret );
0 commit comments