Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: playstation: expose DualSense lightbar through a multi-color LED.

The DualSense lightbar has so far been supported, but it was not yet
adjustable from user space. This patch exposes it through a multi-color
LED.

Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Roderick Colenbrander and committed by
Jiri Kosina
fc97b4d6 42d43c92

+72
+72
drivers/hid/hid-playstation.c
··· 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> ··· 40 38 uint8_t battery_capacity; 41 39 int battery_status; 42 40 41 + const char *input_dev_name; /* Name of primary input device. */ 43 42 uint8_t mac_address[6]; /* Note: stored in little endian order. */ 44 43 uint32_t hw_version; 45 44 uint32_t fw_version; ··· 150 147 uint8_t motor_right; 151 148 152 149 /* RGB lightbar */ 150 + struct led_classdev_mc lightbar; 153 151 bool update_lightbar; 154 152 uint8_t lightbar_red; 155 153 uint8_t lightbar_green; ··· 291 287 {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, 292 288 {0, 0}, 293 289 }; 290 + 291 + static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue); 294 292 295 293 /* 296 294 * Add a new ps_device to ps_devices if it doesn't exist. ··· 526 520 hid_err(hdev, "CRC check failed for reportID=%d\n", report_id); 527 521 return -EILSEQ; 528 522 } 523 + } 524 + 525 + return 0; 526 + } 527 + 528 + /* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */ 529 + static int ps_lightbar_register(struct ps_device *ps_dev, struct led_classdev_mc *lightbar_mc_dev, 530 + int (*brightness_set)(struct led_classdev *, enum led_brightness)) 531 + { 532 + struct hid_device *hdev = ps_dev->hdev; 533 + struct mc_subled *mc_led_info; 534 + struct led_classdev *led_cdev; 535 + int ret; 536 + 537 + mc_led_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_led_info), 538 + GFP_KERNEL | __GFP_ZERO); 539 + if (!mc_led_info) 540 + return -ENOMEM; 541 + 542 + mc_led_info[0].color_index = LED_COLOR_ID_RED; 543 + mc_led_info[1].color_index = LED_COLOR_ID_GREEN; 544 + mc_led_info[2].color_index = LED_COLOR_ID_BLUE; 545 + 546 + lightbar_mc_dev->subled_info = mc_led_info; 547 + lightbar_mc_dev->num_colors = 3; 548 + 549 + led_cdev = &lightbar_mc_dev->led_cdev; 550 + led_cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s:rgb:indicator", 551 + ps_dev->input_dev_name); 552 + if (!led_cdev->name) 553 + return -ENOMEM; 554 + led_cdev->brightness = 255; 555 + led_cdev->max_brightness = 255; 556 + led_cdev->brightness_set_blocking = brightness_set; 557 + 558 + ret = devm_led_classdev_multicolor_register(&hdev->dev, lightbar_mc_dev); 559 + if (ret < 0) { 560 + hid_err(hdev, "Cannot register multicolor LED device\n"); 561 + return ret; 529 562 } 530 563 531 564 return 0; ··· 804 759 err_free: 805 760 kfree(buf); 806 761 return ret; 762 + } 763 + 764 + static int dualsense_lightbar_set_brightness(struct led_classdev *cdev, 765 + enum led_brightness brightness) 766 + { 767 + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); 768 + struct dualsense *ds = container_of(mc_cdev, struct dualsense, lightbar); 769 + uint8_t red, green, blue; 770 + 771 + led_mc_calc_color_components(mc_cdev, brightness); 772 + red = mc_cdev->subled_info[0].brightness; 773 + green = mc_cdev->subled_info[1].brightness; 774 + blue = mc_cdev->subled_info[2].brightness; 775 + 776 + dualsense_set_lightbar(ds, red, green, blue); 777 + return 0; 807 778 } 808 779 809 780 static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp, ··· 1167 1106 1168 1107 static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue) 1169 1108 { 1109 + unsigned long flags; 1110 + 1111 + spin_lock_irqsave(&ds->base.lock, flags); 1170 1112 ds->update_lightbar = true; 1171 1113 ds->lightbar_red = red; 1172 1114 ds->lightbar_green = green; 1173 1115 ds->lightbar_blue = blue; 1116 + spin_unlock_irqrestore(&ds->base.lock, flags); 1174 1117 1175 1118 schedule_work(&ds->output_worker); 1176 1119 } ··· 1261 1196 ret = PTR_ERR(ds->gamepad); 1262 1197 goto err; 1263 1198 } 1199 + /* Use gamepad input device name as primary device name for e.g. LEDs */ 1200 + ps_dev->input_dev_name = dev_name(&ds->gamepad->dev); 1264 1201 1265 1202 ds->sensors = ps_sensors_create(hdev, DS_ACC_RANGE, DS_ACC_RES_PER_G, 1266 1203 DS_GYRO_RANGE, DS_GYRO_RES_PER_DEG_S); ··· 1290 1223 if (ret) 1291 1224 goto err; 1292 1225 1226 + ret = ps_lightbar_register(ps_dev, &ds->lightbar, dualsense_lightbar_set_brightness); 1227 + if (ret) 1228 + goto err; 1229 + 1230 + /* Set default lightbar color. */ 1293 1231 dualsense_set_lightbar(ds, 0, 0, 128); /* blue */ 1294 1232 1295 1233 ret = ps_device_set_player_id(ps_dev);