Loading Documentation/leds/well-known-leds.txt +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,20 @@ but then try the legacy ones, too. Notice there's a list of functions in include/dt-bindings/leds/common.h . * Gamepads and joysticks Game controllers may feature LEDs to indicate a player number. This is commonly used on game consoles in which multiple controllers can be connected to a system. The "player LEDs" are then programmed with a pattern to indicate a particular player. For example, a game controller with 4 LEDs, may be programmed with "x---" to indicate player 1, "-x--" to indicate player 2 etcetera where "x" means on. Input drivers can utilize the LED class to expose the individual player LEDs of a game controller using the function "player". Note: tracking and management of Player IDs is the responsibility of user space, though drivers may pick a default value. Good: "input*:*:player-{1,2,3,4,5} * Keyboards Good: "input*:*:capslock" Loading drivers/hid/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -868,6 +868,7 @@ config HID_PLANTRONICS config HID_PLAYSTATION tristate "PlayStation HID Driver" depends on HID depends on LEDS_CLASS_MULTICOLOR select CRC32 select POWER_SUPPLY help Loading drivers/hid/hid-playstation.c +158 −1 Original line number Diff line number Diff line Loading @@ -11,6 +11,8 @@ #include <linux/hid.h> #include <linux/idr.h> #include <linux/input/mt.h> #include <linux/leds.h> #include <linux/led-class-multicolor.h> #include <linux/module.h> #include <asm/unaligned.h> Loading Loading @@ -38,6 +40,7 @@ struct ps_device { uint8_t battery_capacity; int battery_status; const char *input_dev_name; /* Name of primary input device. */ uint8_t mac_address[6]; /* Note: stored in little endian order. */ uint32_t hw_version; uint32_t fw_version; Loading @@ -53,6 +56,13 @@ struct ps_calibration_data { int sens_denom; }; struct ps_led_info { const char *name; const char *color; enum led_brightness (*brightness_get)(struct led_classdev *cdev); int (*brightness_set)(struct led_classdev *cdev, enum led_brightness); }; /* Seed values for DualShock4 / DualSense CRC32 for different report types. */ #define PS_INPUT_CRC32_SEED 0xA1 #define PS_OUTPUT_CRC32_SEED 0xA2 Loading Loading @@ -147,6 +157,7 @@ struct dualsense { uint8_t motor_right; /* RGB lightbar */ struct led_classdev_mc lightbar; bool update_lightbar; uint8_t lightbar_red; uint8_t lightbar_green; Loading Loading @@ -288,6 +299,8 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = { {0, 0}, }; static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue); /* * Add a new ps_device to ps_devices if it doesn't exist. * Return error on duplicate device, which can happen if the same Loading Loading @@ -525,6 +538,71 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu return 0; } static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led, const struct ps_led_info *led_info) { int ret; led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL, "%s:%s:%s", ps_dev->input_dev_name, led_info->color, led_info->name); if (!led->name) return -ENOMEM; led->brightness = 0; led->max_brightness = 1; led->flags = LED_CORE_SUSPENDRESUME; led->brightness_get = led_info->brightness_get; led->brightness_set_blocking = led_info->brightness_set; ret = devm_led_classdev_register(&ps_dev->hdev->dev, led); if (ret) { hid_err(ps_dev->hdev, "Failed to register LED %s: %d\n", led_info->name, ret); return ret; } return 0; } /* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */ static int ps_lightbar_register(struct ps_device *ps_dev, struct led_classdev_mc *lightbar_mc_dev, int (*brightness_set)(struct led_classdev *, enum led_brightness)) { struct hid_device *hdev = ps_dev->hdev; struct mc_subled *mc_led_info; struct led_classdev *led_cdev; int ret; mc_led_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_led_info), GFP_KERNEL | __GFP_ZERO); if (!mc_led_info) return -ENOMEM; mc_led_info[0].color_index = LED_COLOR_ID_RED; mc_led_info[1].color_index = LED_COLOR_ID_GREEN; mc_led_info[2].color_index = LED_COLOR_ID_BLUE; lightbar_mc_dev->subled_info = mc_led_info; lightbar_mc_dev->num_colors = 3; led_cdev = &lightbar_mc_dev->led_cdev; led_cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s:rgb:indicator", ps_dev->input_dev_name); if (!led_cdev->name) return -ENOMEM; led_cdev->brightness = 255; led_cdev->max_brightness = 255; led_cdev->brightness_set_blocking = brightness_set; ret = devm_led_classdev_multicolor_register(&hdev->dev, lightbar_mc_dev); if (ret < 0) { hid_err(hdev, "Cannot register multicolor LED device\n"); return ret; } return 0; } static struct input_dev *ps_sensors_create(struct hid_device *hdev, int accel_range, int accel_res, int gyro_range, int gyro_res) { Loading Loading @@ -761,6 +839,53 @@ static int dualsense_get_mac_address(struct dualsense *ds) return ret; } static int dualsense_lightbar_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) { struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); struct dualsense *ds = container_of(mc_cdev, struct dualsense, lightbar); uint8_t red, green, blue; led_mc_calc_color_components(mc_cdev, brightness); red = mc_cdev->subled_info[0].brightness; green = mc_cdev->subled_info[1].brightness; blue = mc_cdev->subled_info[2].brightness; dualsense_set_lightbar(ds, red, green, blue); return 0; } static enum led_brightness dualsense_player_led_get_brightness(struct led_classdev *led) { struct hid_device *hdev = to_hid_device(led->dev->parent); struct dualsense *ds = hid_get_drvdata(hdev); return !!(ds->player_leds_state & BIT(led - ds->player_leds)); } static int dualsense_player_led_set_brightness(struct led_classdev *led, enum led_brightness value) { struct hid_device *hdev = to_hid_device(led->dev->parent); struct dualsense *ds = hid_get_drvdata(hdev); unsigned long flags; unsigned int led_index; spin_lock_irqsave(&ds->base.lock, flags); led_index = led - ds->player_leds; if (value == LED_OFF) ds->player_leds_state &= ~BIT(led_index); else ds->player_leds_state |= BIT(led_index); ds->update_player_leds = true; spin_unlock_irqrestore(&ds->base.lock, flags); schedule_work(&ds->output_worker); return 0; } static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp, void *buf) { Loading Loading @@ -1106,10 +1231,14 @@ static int dualsense_reset_leds(struct dualsense *ds) static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue) { unsigned long flags; spin_lock_irqsave(&ds->base.lock, flags); ds->update_lightbar = true; ds->lightbar_red = red; ds->lightbar_green = green; ds->lightbar_blue = blue; spin_unlock_irqrestore(&ds->base.lock, flags); schedule_work(&ds->output_worker); } Loading Loading @@ -1142,7 +1271,20 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) struct dualsense *ds; struct ps_device *ps_dev; uint8_t max_output_report_size; int ret; int i, ret; static const struct ps_led_info player_leds_info[] = { { LED_FUNCTION_PLAYER1, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER2, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER3, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER4, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER5, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness } }; ds = devm_kzalloc(&hdev->dev, sizeof(*ds), GFP_KERNEL); if (!ds) Loading Loading @@ -1196,6 +1338,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) ret = PTR_ERR(ds->gamepad); goto err; } /* Use gamepad input device name as primary device name for e.g. LEDs */ ps_dev->input_dev_name = dev_name(&ds->gamepad->dev); ds->sensors = ps_sensors_create(hdev, DS_ACC_RANGE, DS_ACC_RES_PER_G, DS_GYRO_RANGE, DS_GYRO_RES_PER_DEG_S); Loading Loading @@ -1223,8 +1367,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) if (ret) goto err; ret = ps_lightbar_register(ps_dev, &ds->lightbar, dualsense_lightbar_set_brightness); if (ret) goto err; /* Set default lightbar color. */ dualsense_set_lightbar(ds, 0, 0, 128); /* blue */ for (i = 0; i < ARRAY_SIZE(player_leds_info); i++) { const struct ps_led_info *led_info = &player_leds_info[i]; ret = ps_led_register(ps_dev, &ds->player_leds[i], led_info); if (ret < 0) goto err; } ret = ps_device_set_player_id(ps_dev); if (ret) { hid_err(hdev, "Failed to assign player id for DualSense: %d\n", ret); Loading include/dt-bindings/leds/common.h +7 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,13 @@ #define LED_FUNCTION_MICMUTE "micmute" #define LED_FUNCTION_MUTE "mute" /* Used for player LEDs as found on game controllers from e.g. Nintendo, Sony. */ #define LED_FUNCTION_PLAYER1 "player-1" #define LED_FUNCTION_PLAYER2 "player-2" #define LED_FUNCTION_PLAYER3 "player-3" #define LED_FUNCTION_PLAYER4 "player-4" #define LED_FUNCTION_PLAYER5 "player-5" /* Miscelleaus functions. Use functions above if you can. */ #define LED_FUNCTION_ACTIVITY "activity" #define LED_FUNCTION_ALARM "alarm" Loading Loading
Documentation/leds/well-known-leds.txt +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,20 @@ but then try the legacy ones, too. Notice there's a list of functions in include/dt-bindings/leds/common.h . * Gamepads and joysticks Game controllers may feature LEDs to indicate a player number. This is commonly used on game consoles in which multiple controllers can be connected to a system. The "player LEDs" are then programmed with a pattern to indicate a particular player. For example, a game controller with 4 LEDs, may be programmed with "x---" to indicate player 1, "-x--" to indicate player 2 etcetera where "x" means on. Input drivers can utilize the LED class to expose the individual player LEDs of a game controller using the function "player". Note: tracking and management of Player IDs is the responsibility of user space, though drivers may pick a default value. Good: "input*:*:player-{1,2,3,4,5} * Keyboards Good: "input*:*:capslock" Loading
drivers/hid/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -868,6 +868,7 @@ config HID_PLANTRONICS config HID_PLAYSTATION tristate "PlayStation HID Driver" depends on HID depends on LEDS_CLASS_MULTICOLOR select CRC32 select POWER_SUPPLY help Loading
drivers/hid/hid-playstation.c +158 −1 Original line number Diff line number Diff line Loading @@ -11,6 +11,8 @@ #include <linux/hid.h> #include <linux/idr.h> #include <linux/input/mt.h> #include <linux/leds.h> #include <linux/led-class-multicolor.h> #include <linux/module.h> #include <asm/unaligned.h> Loading Loading @@ -38,6 +40,7 @@ struct ps_device { uint8_t battery_capacity; int battery_status; const char *input_dev_name; /* Name of primary input device. */ uint8_t mac_address[6]; /* Note: stored in little endian order. */ uint32_t hw_version; uint32_t fw_version; Loading @@ -53,6 +56,13 @@ struct ps_calibration_data { int sens_denom; }; struct ps_led_info { const char *name; const char *color; enum led_brightness (*brightness_get)(struct led_classdev *cdev); int (*brightness_set)(struct led_classdev *cdev, enum led_brightness); }; /* Seed values for DualShock4 / DualSense CRC32 for different report types. */ #define PS_INPUT_CRC32_SEED 0xA1 #define PS_OUTPUT_CRC32_SEED 0xA2 Loading Loading @@ -147,6 +157,7 @@ struct dualsense { uint8_t motor_right; /* RGB lightbar */ struct led_classdev_mc lightbar; bool update_lightbar; uint8_t lightbar_red; uint8_t lightbar_green; Loading Loading @@ -288,6 +299,8 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = { {0, 0}, }; static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue); /* * Add a new ps_device to ps_devices if it doesn't exist. * Return error on duplicate device, which can happen if the same Loading Loading @@ -525,6 +538,71 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu return 0; } static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led, const struct ps_led_info *led_info) { int ret; led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL, "%s:%s:%s", ps_dev->input_dev_name, led_info->color, led_info->name); if (!led->name) return -ENOMEM; led->brightness = 0; led->max_brightness = 1; led->flags = LED_CORE_SUSPENDRESUME; led->brightness_get = led_info->brightness_get; led->brightness_set_blocking = led_info->brightness_set; ret = devm_led_classdev_register(&ps_dev->hdev->dev, led); if (ret) { hid_err(ps_dev->hdev, "Failed to register LED %s: %d\n", led_info->name, ret); return ret; } return 0; } /* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */ static int ps_lightbar_register(struct ps_device *ps_dev, struct led_classdev_mc *lightbar_mc_dev, int (*brightness_set)(struct led_classdev *, enum led_brightness)) { struct hid_device *hdev = ps_dev->hdev; struct mc_subled *mc_led_info; struct led_classdev *led_cdev; int ret; mc_led_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_led_info), GFP_KERNEL | __GFP_ZERO); if (!mc_led_info) return -ENOMEM; mc_led_info[0].color_index = LED_COLOR_ID_RED; mc_led_info[1].color_index = LED_COLOR_ID_GREEN; mc_led_info[2].color_index = LED_COLOR_ID_BLUE; lightbar_mc_dev->subled_info = mc_led_info; lightbar_mc_dev->num_colors = 3; led_cdev = &lightbar_mc_dev->led_cdev; led_cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s:rgb:indicator", ps_dev->input_dev_name); if (!led_cdev->name) return -ENOMEM; led_cdev->brightness = 255; led_cdev->max_brightness = 255; led_cdev->brightness_set_blocking = brightness_set; ret = devm_led_classdev_multicolor_register(&hdev->dev, lightbar_mc_dev); if (ret < 0) { hid_err(hdev, "Cannot register multicolor LED device\n"); return ret; } return 0; } static struct input_dev *ps_sensors_create(struct hid_device *hdev, int accel_range, int accel_res, int gyro_range, int gyro_res) { Loading Loading @@ -761,6 +839,53 @@ static int dualsense_get_mac_address(struct dualsense *ds) return ret; } static int dualsense_lightbar_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) { struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); struct dualsense *ds = container_of(mc_cdev, struct dualsense, lightbar); uint8_t red, green, blue; led_mc_calc_color_components(mc_cdev, brightness); red = mc_cdev->subled_info[0].brightness; green = mc_cdev->subled_info[1].brightness; blue = mc_cdev->subled_info[2].brightness; dualsense_set_lightbar(ds, red, green, blue); return 0; } static enum led_brightness dualsense_player_led_get_brightness(struct led_classdev *led) { struct hid_device *hdev = to_hid_device(led->dev->parent); struct dualsense *ds = hid_get_drvdata(hdev); return !!(ds->player_leds_state & BIT(led - ds->player_leds)); } static int dualsense_player_led_set_brightness(struct led_classdev *led, enum led_brightness value) { struct hid_device *hdev = to_hid_device(led->dev->parent); struct dualsense *ds = hid_get_drvdata(hdev); unsigned long flags; unsigned int led_index; spin_lock_irqsave(&ds->base.lock, flags); led_index = led - ds->player_leds; if (value == LED_OFF) ds->player_leds_state &= ~BIT(led_index); else ds->player_leds_state |= BIT(led_index); ds->update_player_leds = true; spin_unlock_irqrestore(&ds->base.lock, flags); schedule_work(&ds->output_worker); return 0; } static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp, void *buf) { Loading Loading @@ -1106,10 +1231,14 @@ static int dualsense_reset_leds(struct dualsense *ds) static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue) { unsigned long flags; spin_lock_irqsave(&ds->base.lock, flags); ds->update_lightbar = true; ds->lightbar_red = red; ds->lightbar_green = green; ds->lightbar_blue = blue; spin_unlock_irqrestore(&ds->base.lock, flags); schedule_work(&ds->output_worker); } Loading Loading @@ -1142,7 +1271,20 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) struct dualsense *ds; struct ps_device *ps_dev; uint8_t max_output_report_size; int ret; int i, ret; static const struct ps_led_info player_leds_info[] = { { LED_FUNCTION_PLAYER1, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER2, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER3, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER4, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, { LED_FUNCTION_PLAYER5, "white", dualsense_player_led_get_brightness, dualsense_player_led_set_brightness } }; ds = devm_kzalloc(&hdev->dev, sizeof(*ds), GFP_KERNEL); if (!ds) Loading Loading @@ -1196,6 +1338,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) ret = PTR_ERR(ds->gamepad); goto err; } /* Use gamepad input device name as primary device name for e.g. LEDs */ ps_dev->input_dev_name = dev_name(&ds->gamepad->dev); ds->sensors = ps_sensors_create(hdev, DS_ACC_RANGE, DS_ACC_RES_PER_G, DS_GYRO_RANGE, DS_GYRO_RES_PER_DEG_S); Loading Loading @@ -1223,8 +1367,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) if (ret) goto err; ret = ps_lightbar_register(ps_dev, &ds->lightbar, dualsense_lightbar_set_brightness); if (ret) goto err; /* Set default lightbar color. */ dualsense_set_lightbar(ds, 0, 0, 128); /* blue */ for (i = 0; i < ARRAY_SIZE(player_leds_info); i++) { const struct ps_led_info *led_info = &player_leds_info[i]; ret = ps_led_register(ps_dev, &ds->player_leds[i], led_info); if (ret < 0) goto err; } ret = ps_device_set_player_id(ps_dev); if (ret) { hid_err(hdev, "Failed to assign player id for DualSense: %d\n", ret); Loading
include/dt-bindings/leds/common.h +7 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,13 @@ #define LED_FUNCTION_MICMUTE "micmute" #define LED_FUNCTION_MUTE "mute" /* Used for player LEDs as found on game controllers from e.g. Nintendo, Sony. */ #define LED_FUNCTION_PLAYER1 "player-1" #define LED_FUNCTION_PLAYER2 "player-2" #define LED_FUNCTION_PLAYER3 "player-3" #define LED_FUNCTION_PLAYER4 "player-4" #define LED_FUNCTION_PLAYER5 "player-5" /* Miscelleaus functions. Use functions above if you can. */ #define LED_FUNCTION_ACTIVITY "activity" #define LED_FUNCTION_ALARM "alarm" Loading