|
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; |
@@ -147,6 +150,7 @@ struct dualsense { |
147 | 150 | uint8_t motor_right; |
148 | 151 |
|
149 | 152 | /* RGB lightbar */ |
| 153 | + struct led_classdev_mc lightbar; |
150 | 154 | bool update_lightbar; |
151 | 155 | uint8_t lightbar_red; |
152 | 156 | uint8_t lightbar_green; |
@@ -288,6 +292,8 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = { |
288 | 292 | {0, 0}, |
289 | 293 | }; |
290 | 294 |
|
| 295 | +static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue); |
| 296 | + |
291 | 297 | /* |
292 | 298 | * Add a new ps_device to ps_devices if it doesn't exist. |
293 | 299 | * Return error on duplicate device, which can happen if the same |
@@ -525,6 +531,45 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu |
525 | 531 | return 0; |
526 | 532 | } |
527 | 533 |
|
| 534 | +/* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */ |
| 535 | +static int ps_lightbar_register(struct ps_device *ps_dev, struct led_classdev_mc *lightbar_mc_dev, |
| 536 | + int (*brightness_set)(struct led_classdev *, enum led_brightness)) |
| 537 | +{ |
| 538 | + struct hid_device *hdev = ps_dev->hdev; |
| 539 | + struct mc_subled *mc_led_info; |
| 540 | + struct led_classdev *led_cdev; |
| 541 | + int ret; |
| 542 | + |
| 543 | + mc_led_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_led_info), |
| 544 | + GFP_KERNEL | __GFP_ZERO); |
| 545 | + if (!mc_led_info) |
| 546 | + return -ENOMEM; |
| 547 | + |
| 548 | + mc_led_info[0].color_index = LED_COLOR_ID_RED; |
| 549 | + mc_led_info[1].color_index = LED_COLOR_ID_GREEN; |
| 550 | + mc_led_info[2].color_index = LED_COLOR_ID_BLUE; |
| 551 | + |
| 552 | + lightbar_mc_dev->subled_info = mc_led_info; |
| 553 | + lightbar_mc_dev->num_colors = 3; |
| 554 | + |
| 555 | + led_cdev = &lightbar_mc_dev->led_cdev; |
| 556 | + led_cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s:rgb:indicator", |
| 557 | + ps_dev->input_dev_name); |
| 558 | + if (!led_cdev->name) |
| 559 | + return -ENOMEM; |
| 560 | + led_cdev->brightness = 255; |
| 561 | + led_cdev->max_brightness = 255; |
| 562 | + led_cdev->brightness_set_blocking = brightness_set; |
| 563 | + |
| 564 | + ret = devm_led_classdev_multicolor_register(&hdev->dev, lightbar_mc_dev); |
| 565 | + if (ret < 0) { |
| 566 | + hid_err(hdev, "Cannot register multicolor LED device\n"); |
| 567 | + return ret; |
| 568 | + } |
| 569 | + |
| 570 | + return 0; |
| 571 | +} |
| 572 | + |
528 | 573 | static struct input_dev *ps_sensors_create(struct hid_device *hdev, int accel_range, int accel_res, |
529 | 574 | int gyro_range, int gyro_res) |
530 | 575 | { |
@@ -761,6 +806,22 @@ static int dualsense_get_mac_address(struct dualsense *ds) |
761 | 806 | return ret; |
762 | 807 | } |
763 | 808 |
|
| 809 | +static int dualsense_lightbar_set_brightness(struct led_classdev *cdev, |
| 810 | + enum led_brightness brightness) |
| 811 | +{ |
| 812 | + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); |
| 813 | + struct dualsense *ds = container_of(mc_cdev, struct dualsense, lightbar); |
| 814 | + uint8_t red, green, blue; |
| 815 | + |
| 816 | + led_mc_calc_color_components(mc_cdev, brightness); |
| 817 | + red = mc_cdev->subled_info[0].brightness; |
| 818 | + green = mc_cdev->subled_info[1].brightness; |
| 819 | + blue = mc_cdev->subled_info[2].brightness; |
| 820 | + |
| 821 | + dualsense_set_lightbar(ds, red, green, blue); |
| 822 | + return 0; |
| 823 | +} |
| 824 | + |
764 | 825 | static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp, |
765 | 826 | void *buf) |
766 | 827 | { |
@@ -1106,10 +1167,14 @@ static int dualsense_reset_leds(struct dualsense *ds) |
1106 | 1167 |
|
1107 | 1168 | static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue) |
1108 | 1169 | { |
| 1170 | + unsigned long flags; |
| 1171 | + |
| 1172 | + spin_lock_irqsave(&ds->base.lock, flags); |
1109 | 1173 | ds->update_lightbar = true; |
1110 | 1174 | ds->lightbar_red = red; |
1111 | 1175 | ds->lightbar_green = green; |
1112 | 1176 | ds->lightbar_blue = blue; |
| 1177 | + spin_unlock_irqrestore(&ds->base.lock, flags); |
1113 | 1178 |
|
1114 | 1179 | schedule_work(&ds->output_worker); |
1115 | 1180 | } |
@@ -1196,6 +1261,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) |
1196 | 1261 | ret = PTR_ERR(ds->gamepad); |
1197 | 1262 | goto err; |
1198 | 1263 | } |
| 1264 | + /* Use gamepad input device name as primary device name for e.g. LEDs */ |
| 1265 | + ps_dev->input_dev_name = dev_name(&ds->gamepad->dev); |
1199 | 1266 |
|
1200 | 1267 | ds->sensors = ps_sensors_create(hdev, DS_ACC_RANGE, DS_ACC_RES_PER_G, |
1201 | 1268 | DS_GYRO_RANGE, DS_GYRO_RES_PER_DEG_S); |
@@ -1223,6 +1290,11 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) |
1223 | 1290 | if (ret) |
1224 | 1291 | goto err; |
1225 | 1292 |
|
| 1293 | + ret = ps_lightbar_register(ps_dev, &ds->lightbar, dualsense_lightbar_set_brightness); |
| 1294 | + if (ret) |
| 1295 | + goto err; |
| 1296 | + |
| 1297 | + /* Set default lightbar color. */ |
1226 | 1298 | dualsense_set_lightbar(ds, 0, 0, 128); /* blue */ |
1227 | 1299 |
|
1228 | 1300 | ret = ps_device_set_player_id(ps_dev); |
|
0 commit comments