input-leds.c (5151B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * LED support for the input layer 4 * 5 * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org> 6 */ 7 8#include <linux/kernel.h> 9#include <linux/slab.h> 10#include <linux/module.h> 11#include <linux/init.h> 12#include <linux/leds.h> 13#include <linux/input.h> 14 15#if IS_ENABLED(CONFIG_VT) 16#define VT_TRIGGER(_name) .trigger = _name 17#else 18#define VT_TRIGGER(_name) .trigger = NULL 19#endif 20 21static const struct { 22 const char *name; 23 const char *trigger; 24} input_led_info[LED_CNT] = { 25 [LED_NUML] = { "numlock", VT_TRIGGER("kbd-numlock") }, 26 [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") }, 27 [LED_SCROLLL] = { "scrolllock", VT_TRIGGER("kbd-scrolllock") }, 28 [LED_COMPOSE] = { "compose" }, 29 [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") }, 30 [LED_SLEEP] = { "sleep" } , 31 [LED_SUSPEND] = { "suspend" }, 32 [LED_MUTE] = { "mute" }, 33 [LED_MISC] = { "misc" }, 34 [LED_MAIL] = { "mail" }, 35 [LED_CHARGING] = { "charging" }, 36}; 37 38struct input_led { 39 struct led_classdev cdev; 40 struct input_handle *handle; 41 unsigned int code; /* One of LED_* constants */ 42}; 43 44struct input_leds { 45 struct input_handle handle; 46 unsigned int num_leds; 47 struct input_led leds[]; 48}; 49 50static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev) 51{ 52 struct input_led *led = container_of(cdev, struct input_led, cdev); 53 struct input_dev *input = led->handle->dev; 54 55 return test_bit(led->code, input->led) ? cdev->max_brightness : 0; 56} 57 58static void input_leds_brightness_set(struct led_classdev *cdev, 59 enum led_brightness brightness) 60{ 61 struct input_led *led = container_of(cdev, struct input_led, cdev); 62 63 input_inject_event(led->handle, EV_LED, led->code, !!brightness); 64} 65 66static void input_leds_event(struct input_handle *handle, unsigned int type, 67 unsigned int code, int value) 68{ 69} 70 71static int input_leds_get_count(struct input_dev *dev) 72{ 73 unsigned int led_code; 74 int count = 0; 75 76 for_each_set_bit(led_code, dev->ledbit, LED_CNT) 77 if (input_led_info[led_code].name) 78 count++; 79 80 return count; 81} 82 83static int input_leds_connect(struct input_handler *handler, 84 struct input_dev *dev, 85 const struct input_device_id *id) 86{ 87 struct input_leds *leds; 88 struct input_led *led; 89 unsigned int num_leds; 90 unsigned int led_code; 91 int led_no; 92 int error; 93 94 num_leds = input_leds_get_count(dev); 95 if (!num_leds) 96 return -ENXIO; 97 98 leds = kzalloc(struct_size(leds, leds, num_leds), GFP_KERNEL); 99 if (!leds) 100 return -ENOMEM; 101 102 leds->num_leds = num_leds; 103 104 leds->handle.dev = dev; 105 leds->handle.handler = handler; 106 leds->handle.name = "leds"; 107 leds->handle.private = leds; 108 109 error = input_register_handle(&leds->handle); 110 if (error) 111 goto err_free_mem; 112 113 error = input_open_device(&leds->handle); 114 if (error) 115 goto err_unregister_handle; 116 117 led_no = 0; 118 for_each_set_bit(led_code, dev->ledbit, LED_CNT) { 119 if (!input_led_info[led_code].name) 120 continue; 121 122 led = &leds->leds[led_no]; 123 led->handle = &leds->handle; 124 led->code = led_code; 125 126 led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s", 127 dev_name(&dev->dev), 128 input_led_info[led_code].name); 129 if (!led->cdev.name) { 130 error = -ENOMEM; 131 goto err_unregister_leds; 132 } 133 134 led->cdev.max_brightness = 1; 135 led->cdev.brightness_get = input_leds_brightness_get; 136 led->cdev.brightness_set = input_leds_brightness_set; 137 led->cdev.default_trigger = input_led_info[led_code].trigger; 138 139 error = led_classdev_register(&dev->dev, &led->cdev); 140 if (error) { 141 dev_err(&dev->dev, "failed to register LED %s: %d\n", 142 led->cdev.name, error); 143 kfree(led->cdev.name); 144 goto err_unregister_leds; 145 } 146 147 led_no++; 148 } 149 150 return 0; 151 152err_unregister_leds: 153 while (--led_no >= 0) { 154 struct input_led *led = &leds->leds[led_no]; 155 156 led_classdev_unregister(&led->cdev); 157 kfree(led->cdev.name); 158 } 159 160 input_close_device(&leds->handle); 161 162err_unregister_handle: 163 input_unregister_handle(&leds->handle); 164 165err_free_mem: 166 kfree(leds); 167 return error; 168} 169 170static void input_leds_disconnect(struct input_handle *handle) 171{ 172 struct input_leds *leds = handle->private; 173 int i; 174 175 for (i = 0; i < leds->num_leds; i++) { 176 struct input_led *led = &leds->leds[i]; 177 178 led_classdev_unregister(&led->cdev); 179 kfree(led->cdev.name); 180 } 181 182 input_close_device(handle); 183 input_unregister_handle(handle); 184 185 kfree(leds); 186} 187 188static const struct input_device_id input_leds_ids[] = { 189 { 190 .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 191 .evbit = { BIT_MASK(EV_LED) }, 192 }, 193 { }, 194}; 195MODULE_DEVICE_TABLE(input, input_leds_ids); 196 197static struct input_handler input_leds_handler = { 198 .event = input_leds_event, 199 .connect = input_leds_connect, 200 .disconnect = input_leds_disconnect, 201 .name = "leds", 202 .id_table = input_leds_ids, 203}; 204 205static int __init input_leds_init(void) 206{ 207 return input_register_handler(&input_leds_handler); 208} 209module_init(input_leds_init); 210 211static void __exit input_leds_exit(void) 212{ 213 input_unregister_handler(&input_leds_handler); 214} 215module_exit(input_leds_exit); 216 217MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>"); 218MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); 219MODULE_DESCRIPTION("Input -> LEDs Bridge"); 220MODULE_LICENSE("GPL v2");