@@ -311,6 +311,8 @@ struct dualsense_output_report {
311
311
312
312
/* Flags for DualShock4 output report. */
313
313
#define DS4_OUTPUT_VALID_FLAG0_MOTOR 0x01
314
+ #define DS4_OUTPUT_VALID_FLAG0_LED 0x02
315
+ #define DS4_OUTPUT_VALID_FLAG0_LED_BLINK 0x04
314
316
315
317
/* DualShock4 hardware limits */
316
318
#define DS4_ACC_RES_PER_G 8192
@@ -339,6 +341,14 @@ struct dualshock4 {
339
341
uint8_t motor_left ;
340
342
uint8_t motor_right ;
341
343
344
+ /* Lightbar leds */
345
+ bool update_lightbar ;
346
+ bool lightbar_enabled ; /* For use by global LED control. */
347
+ uint8_t lightbar_red ;
348
+ uint8_t lightbar_green ;
349
+ uint8_t lightbar_blue ;
350
+ struct led_classdev lightbar_leds [4 ];
351
+
342
352
struct work_struct output_worker ;
343
353
bool output_worker_initialized ;
344
354
void * output_report_dmabuf ;
@@ -697,8 +707,14 @@ static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led,
697
707
{
698
708
int ret ;
699
709
700
- led -> name = devm_kasprintf (& ps_dev -> hdev -> dev , GFP_KERNEL ,
701
- "%s:%s:%s" , ps_dev -> input_dev_name , led_info -> color , led_info -> name );
710
+ if (led_info -> name ) {
711
+ led -> name = devm_kasprintf (& ps_dev -> hdev -> dev , GFP_KERNEL ,
712
+ "%s:%s:%s" , ps_dev -> input_dev_name , led_info -> color , led_info -> name );
713
+ } else {
714
+ /* Backwards compatible mode for hid-sony, but not compliant with LED class spec. */
715
+ led -> name = devm_kasprintf (& ps_dev -> hdev -> dev , GFP_KERNEL ,
716
+ "%s:%s" , ps_dev -> input_dev_name , led_info -> color );
717
+ }
702
718
703
719
if (!led -> name )
704
720
return - ENOMEM ;
@@ -1746,6 +1762,60 @@ static int dualshock4_get_mac_address(struct dualshock4 *ds4)
1746
1762
return ret ;
1747
1763
}
1748
1764
1765
+ static enum led_brightness dualshock4_led_get_brightness (struct led_classdev * led )
1766
+ {
1767
+ struct hid_device * hdev = to_hid_device (led -> dev -> parent );
1768
+ struct dualshock4 * ds4 = hid_get_drvdata (hdev );
1769
+ unsigned int led_index ;
1770
+
1771
+ led_index = led - ds4 -> lightbar_leds ;
1772
+ switch (led_index ) {
1773
+ case 0 :
1774
+ return ds4 -> lightbar_red ;
1775
+ case 1 :
1776
+ return ds4 -> lightbar_green ;
1777
+ case 2 :
1778
+ return ds4 -> lightbar_blue ;
1779
+ case 3 :
1780
+ return ds4 -> lightbar_enabled ;
1781
+ }
1782
+
1783
+ return -1 ;
1784
+ }
1785
+
1786
+ static int dualshock4_led_set_brightness (struct led_classdev * led , enum led_brightness value )
1787
+ {
1788
+ struct hid_device * hdev = to_hid_device (led -> dev -> parent );
1789
+ struct dualshock4 * ds4 = hid_get_drvdata (hdev );
1790
+ unsigned long flags ;
1791
+ unsigned int led_index ;
1792
+
1793
+ spin_lock_irqsave (& ds4 -> base .lock , flags );
1794
+
1795
+ led_index = led - ds4 -> lightbar_leds ;
1796
+ switch (led_index ) {
1797
+ case 0 :
1798
+ ds4 -> lightbar_red = value ;
1799
+ break ;
1800
+ case 1 :
1801
+ ds4 -> lightbar_green = value ;
1802
+ break ;
1803
+ case 2 :
1804
+ ds4 -> lightbar_blue = value ;
1805
+ break ;
1806
+ case 3 :
1807
+ ds4 -> lightbar_enabled = !!value ;
1808
+ }
1809
+
1810
+ ds4 -> update_lightbar = true;
1811
+
1812
+ spin_unlock_irqrestore (& ds4 -> base .lock , flags );
1813
+
1814
+ dualshock4_schedule_work (ds4 );
1815
+
1816
+ return 0 ;
1817
+ }
1818
+
1749
1819
static void dualshock4_init_output_report (struct dualshock4 * ds4 ,
1750
1820
struct dualshock4_output_report * rp , void * buf )
1751
1821
{
@@ -1784,6 +1854,18 @@ static void dualshock4_output_worker(struct work_struct *work)
1784
1854
ds4 -> update_rumble = false;
1785
1855
}
1786
1856
1857
+ if (ds4 -> update_lightbar ) {
1858
+ common -> valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED ;
1859
+ /* Comptabile behavior with hid-sony, which used a dummy global LED to
1860
+ * allow enabling/disabling the lightbar. The global LED maps to
1861
+ * lightbar_enabled.
1862
+ */
1863
+ common -> lightbar_red = ds4 -> lightbar_enabled ? ds4 -> lightbar_red : 0 ;
1864
+ common -> lightbar_green = ds4 -> lightbar_enabled ? ds4 -> lightbar_green : 0 ;
1865
+ common -> lightbar_blue = ds4 -> lightbar_enabled ? ds4 -> lightbar_blue : 0 ;
1866
+ ds4 -> update_lightbar = false;
1867
+ }
1868
+
1787
1869
spin_unlock_irqrestore (& ds4 -> base .lock , flags );
1788
1870
1789
1871
hid_hw_output_report (ds4 -> base .hdev , report .data , report .len );
@@ -1998,12 +2080,52 @@ static inline void dualshock4_schedule_work(struct dualshock4 *ds4)
1998
2080
spin_unlock_irqrestore (& ds4 -> base .lock , flags );
1999
2081
}
2000
2082
2083
+ /* Set default lightbar color based on player. */
2084
+ static void dualshock4_set_default_lightbar_colors (struct dualshock4 * ds4 )
2085
+ {
2086
+ /* Use same player colors as PlayStation 4.
2087
+ * Array of colors is in RGB.
2088
+ */
2089
+ static const int player_colors [4 ][3 ] = {
2090
+ { 0x00 , 0x00 , 0x40 }, /* Blue */
2091
+ { 0x40 , 0x00 , 0x00 }, /* Red */
2092
+ { 0x00 , 0x40 , 0x00 }, /* Green */
2093
+ { 0x20 , 0x00 , 0x20 } /* Pink */
2094
+ };
2095
+
2096
+ uint8_t player_id = ds4 -> base .player_id % ARRAY_SIZE (player_colors );
2097
+
2098
+ ds4 -> lightbar_enabled = true;
2099
+ ds4 -> lightbar_red = player_colors [player_id ][0 ];
2100
+ ds4 -> lightbar_green = player_colors [player_id ][1 ];
2101
+ ds4 -> lightbar_blue = player_colors [player_id ][2 ];
2102
+
2103
+ ds4 -> update_lightbar = true;
2104
+ dualshock4_schedule_work (ds4 );
2105
+ }
2106
+
2001
2107
static struct ps_device * dualshock4_create (struct hid_device * hdev )
2002
2108
{
2003
2109
struct dualshock4 * ds4 ;
2004
2110
struct ps_device * ps_dev ;
2005
2111
uint8_t max_output_report_size ;
2006
- int ret ;
2112
+ int i , ret ;
2113
+
2114
+ /* The DualShock4 has an RGB lightbar, which the original hid-sony driver
2115
+ * exposed as a set of 4 LEDs for the 3 color channels and a global control.
2116
+ * Ideally this should have used the multi-color LED class, which didn't exist
2117
+ * yet. In addition the driver used a naming scheme not compliant with the LED
2118
+ * naming spec by using "<mac_address>:<color>", which contained many colons.
2119
+ * We use a more compliant by using "<device_name>:<color>" name now. Ideally
2120
+ * would have been "<device_name>:<color>:indicator", but that would break
2121
+ * existing applications (e.g. Android). Nothing matches against MAC address.
2122
+ */
2123
+ static const struct ps_led_info lightbar_leds_info [] = {
2124
+ { NULL , "red" , 255 , dualshock4_led_get_brightness , dualshock4_led_set_brightness },
2125
+ { NULL , "green" , 255 , dualshock4_led_get_brightness , dualshock4_led_set_brightness },
2126
+ { NULL , "blue" , 255 , dualshock4_led_get_brightness , dualshock4_led_set_brightness },
2127
+ { NULL , "global" , 1 , dualshock4_led_get_brightness , dualshock4_led_set_brightness },
2128
+ };
2007
2129
2008
2130
ds4 = devm_kzalloc (& hdev -> dev , sizeof (* ds4 ), GFP_KERNEL );
2009
2131
if (!ds4 )
@@ -2060,6 +2182,9 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev)
2060
2182
goto err ;
2061
2183
}
2062
2184
2185
+ /* Use gamepad input device name as primary device name for e.g. LEDs */
2186
+ ps_dev -> input_dev_name = dev_name (& ds4 -> gamepad -> dev );
2187
+
2063
2188
ds4 -> sensors = ps_sensors_create (hdev , DS4_ACC_RANGE , DS4_ACC_RES_PER_G ,
2064
2189
DS4_GYRO_RANGE , DS4_GYRO_RES_PER_DEG_S );
2065
2190
if (IS_ERR (ds4 -> sensors )) {
@@ -2077,12 +2202,22 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev)
2077
2202
if (ret )
2078
2203
goto err ;
2079
2204
2205
+ for (i = 0 ; i < ARRAY_SIZE (lightbar_leds_info ); i ++ ) {
2206
+ const struct ps_led_info * led_info = & lightbar_leds_info [i ];
2207
+
2208
+ ret = ps_led_register (ps_dev , & ds4 -> lightbar_leds [i ], led_info );
2209
+ if (ret < 0 )
2210
+ goto err ;
2211
+ }
2212
+
2080
2213
ret = ps_device_set_player_id (ps_dev );
2081
2214
if (ret ) {
2082
2215
hid_err (hdev , "Failed to assign player id for DualShock4: %d\n" , ret );
2083
2216
goto err ;
2084
2217
}
2085
2218
2219
+ dualshock4_set_default_lightbar_colors (ds4 );
2220
+
2086
2221
/*
2087
2222
* Reporting hardware and firmware is important as there are frequent updates, which
2088
2223
* can change behavior.
0 commit comments