Branch data Line data Source code
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 : : 21 : : static 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 : : 38 : : struct input_led { 39 : : struct led_classdev cdev; 40 : : struct input_handle *handle; 41 : : unsigned int code; /* One of LED_* constants */ 42 : : }; 43 : : 44 : : struct input_leds { 45 : : struct input_handle handle; 46 : : unsigned int num_leds; 47 : : struct input_led leds[]; 48 : : }; 49 : : 50 : 3 : static 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 : 3 : struct input_dev *input = led->handle->dev; 54 : : 55 : 3 : return test_bit(led->code, input->led) ? cdev->max_brightness : 0; 56 : : } 57 : : 58 : 3 : static 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 : 3 : input_inject_event(led->handle, EV_LED, led->code, !!brightness); 64 : 3 : } 65 : : 66 : 3 : static void input_leds_event(struct input_handle *handle, unsigned int type, 67 : : unsigned int code, int value) 68 : : { 69 : 3 : } 70 : : 71 : 3 : static int input_leds_get_count(struct input_dev *dev) 72 : : { 73 : : unsigned int led_code; 74 : : int count = 0; 75 : : 76 : 3 : for_each_set_bit(led_code, dev->ledbit, LED_CNT) 77 : 3 : if (input_led_info[led_code].name) 78 : 3 : count++; 79 : : 80 : 3 : return count; 81 : : } 82 : : 83 : 3 : static 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 : 3 : num_leds = input_leds_get_count(dev); 95 : 3 : if (!num_leds) 96 : : return -ENXIO; 97 : : 98 : 3 : leds = kzalloc(struct_size(leds, leds, num_leds), GFP_KERNEL); 99 : 3 : if (!leds) 100 : : return -ENOMEM; 101 : : 102 : 3 : leds->num_leds = num_leds; 103 : : 104 : 3 : leds->handle.dev = dev; 105 : 3 : leds->handle.handler = handler; 106 : 3 : leds->handle.name = "leds"; 107 : 3 : leds->handle.private = leds; 108 : : 109 : 3 : error = input_register_handle(&leds->handle); 110 : 3 : if (error) 111 : : goto err_free_mem; 112 : : 113 : 3 : error = input_open_device(&leds->handle); 114 : 3 : if (error) 115 : : goto err_unregister_handle; 116 : : 117 : : led_no = 0; 118 : 3 : for_each_set_bit(led_code, dev->ledbit, LED_CNT) { 119 : 3 : if (!input_led_info[led_code].name) 120 : 0 : continue; 121 : : 122 : : led = &leds->leds[led_no]; 123 : 3 : led->handle = &leds->handle; 124 : 3 : led->code = led_code; 125 : : 126 : 3 : led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s", 127 : : dev_name(&dev->dev), 128 : : input_led_info[led_code].name); 129 : 3 : if (!led->cdev.name) { 130 : : error = -ENOMEM; 131 : : goto err_unregister_leds; 132 : : } 133 : : 134 : 3 : led->cdev.max_brightness = 1; 135 : 3 : led->cdev.brightness_get = input_leds_brightness_get; 136 : 3 : led->cdev.brightness_set = input_leds_brightness_set; 137 : 3 : led->cdev.default_trigger = input_led_info[led_code].trigger; 138 : : 139 : 3 : error = led_classdev_register(&dev->dev, &led->cdev); 140 : 3 : if (error) { 141 : 0 : dev_err(&dev->dev, "failed to register LED %s: %d\n", 142 : : led->cdev.name, error); 143 : 0 : kfree(led->cdev.name); 144 : 0 : goto err_unregister_leds; 145 : : } 146 : : 147 : 3 : led_no++; 148 : : } 149 : : 150 : : return 0; 151 : : 152 : : err_unregister_leds: 153 : 0 : while (--led_no >= 0) { 154 : : struct input_led *led = &leds->leds[led_no]; 155 : : 156 : 0 : led_classdev_unregister(&led->cdev); 157 : 0 : kfree(led->cdev.name); 158 : : } 159 : : 160 : 0 : input_close_device(&leds->handle); 161 : : 162 : : err_unregister_handle: 163 : 0 : input_unregister_handle(&leds->handle); 164 : : 165 : : err_free_mem: 166 : 0 : kfree(leds); 167 : 0 : return error; 168 : : } 169 : : 170 : 0 : static void input_leds_disconnect(struct input_handle *handle) 171 : : { 172 : 0 : struct input_leds *leds = handle->private; 173 : : int i; 174 : : 175 : 0 : for (i = 0; i < leds->num_leds; i++) { 176 : : struct input_led *led = &leds->leds[i]; 177 : : 178 : 0 : led_classdev_unregister(&led->cdev); 179 : 0 : kfree(led->cdev.name); 180 : : } 181 : : 182 : 0 : input_close_device(handle); 183 : 0 : input_unregister_handle(handle); 184 : : 185 : 0 : kfree(leds); 186 : 0 : } 187 : : 188 : : static 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 : : }; 195 : : MODULE_DEVICE_TABLE(input, input_leds_ids); 196 : : 197 : : static 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 : : 205 : 3 : static int __init input_leds_init(void) 206 : : { 207 : 3 : return input_register_handler(&input_leds_handler); 208 : : } 209 : : module_init(input_leds_init); 210 : : 211 : 0 : static void __exit input_leds_exit(void) 212 : : { 213 : 0 : input_unregister_handler(&input_leds_handler); 214 : 0 : } 215 : : module_exit(input_leds_exit); 216 : : 217 : : MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>"); 218 : : MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); 219 : : MODULE_DESCRIPTION("Input -> LEDs Bridge"); 220 : : MODULE_LICENSE("GPL v2");