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