linux/drivers/input/input-leds.c
<<
>>
Prefs
   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
  24static 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
  41struct input_led {
  42        struct led_classdev cdev;
  43        struct input_handle *handle;
  44        unsigned int code; /* One of LED_* constants */
  45};
  46
  47struct input_leds {
  48        struct input_handle handle;
  49        unsigned int num_leds;
  50        struct input_led leds[];
  51};
  52
  53static 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
  61static 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
  69static void input_leds_event(struct input_handle *handle, unsigned int type,
  70                             unsigned int code, int value)
  71{
  72}
  73
  74static int input_leds_get_count(struct input_dev *dev)
  75{
  76        unsigned int led_code;
  77        int count = 0;
  78
  79        for_each_set_bit(led_code, dev->ledbit, LED_CNT)
  80                if (input_led_info[led_code].name)
  81                        count++;
  82
  83        return count;
  84}
  85
  86static int input_leds_connect(struct input_handler *handler,
  87                              struct input_dev *dev,
  88                              const struct input_device_id *id)
  89{
  90        struct input_leds *leds;
  91        unsigned int num_leds;
  92        unsigned int led_code;
  93        int led_no;
  94        int error;
  95
  96        num_leds = input_leds_get_count(dev);
  97        if (!num_leds)
  98                return -ENXIO;
  99
 100        leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds),
 101                       GFP_KERNEL);
 102        if (!leds)
 103                return -ENOMEM;
 104
 105        leds->num_leds = num_leds;
 106
 107        leds->handle.dev = dev;
 108        leds->handle.handler = handler;
 109        leds->handle.name = "leds";
 110        leds->handle.private = leds;
 111
 112        error = input_register_handle(&leds->handle);
 113        if (error)
 114                goto err_free_mem;
 115
 116        error = input_open_device(&leds->handle);
 117        if (error)
 118                goto err_unregister_handle;
 119
 120        led_no = 0;
 121        for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
 122                struct input_led *led = &leds->leds[led_no];
 123
 124                led->handle = &leds->handle;
 125                led->code = led_code;
 126
 127                if (!input_led_info[led_code].name)
 128                        continue;
 129
 130                led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
 131                                           dev_name(&dev->dev),
 132                                           input_led_info[led_code].name);
 133                if (!led->cdev.name) {
 134                        error = -ENOMEM;
 135                        goto err_unregister_leds;
 136                }
 137
 138                led->cdev.max_brightness = 1;
 139                led->cdev.brightness_get = input_leds_brightness_get;
 140                led->cdev.brightness_set = input_leds_brightness_set;
 141                led->cdev.default_trigger = input_led_info[led_code].trigger;
 142
 143                error = led_classdev_register(&dev->dev, &led->cdev);
 144                if (error) {
 145                        dev_err(&dev->dev, "failed to register LED %s: %d\n",
 146                                led->cdev.name, error);
 147                        kfree(led->cdev.name);
 148                        goto err_unregister_leds;
 149                }
 150
 151                led_no++;
 152        }
 153
 154        return 0;
 155
 156err_unregister_leds:
 157        while (--led_no >= 0) {
 158                struct input_led *led = &leds->leds[led_no];
 159
 160                led_classdev_unregister(&led->cdev);
 161                kfree(led->cdev.name);
 162        }
 163
 164        input_close_device(&leds->handle);
 165
 166err_unregister_handle:
 167        input_unregister_handle(&leds->handle);
 168
 169err_free_mem:
 170        kfree(leds);
 171        return error;
 172}
 173
 174static void input_leds_disconnect(struct input_handle *handle)
 175{
 176        struct input_leds *leds = handle->private;
 177        int i;
 178
 179        for (i = 0; i < leds->num_leds; i++) {
 180                struct input_led *led = &leds->leds[i];
 181
 182                led_classdev_unregister(&led->cdev);
 183                kfree(led->cdev.name);
 184        }
 185
 186        input_close_device(handle);
 187        input_unregister_handle(handle);
 188
 189        kfree(leds);
 190}
 191
 192static const struct input_device_id input_leds_ids[] = {
 193        {
 194                .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
 195                .evbit = { BIT_MASK(EV_LED) },
 196        },
 197        { },
 198};
 199MODULE_DEVICE_TABLE(input, input_leds_ids);
 200
 201static struct input_handler input_leds_handler = {
 202        .event =        input_leds_event,
 203        .connect =      input_leds_connect,
 204        .disconnect =   input_leds_disconnect,
 205        .name =         "leds",
 206        .id_table =     input_leds_ids,
 207};
 208
 209static int __init input_leds_init(void)
 210{
 211        return input_register_handler(&input_leds_handler);
 212}
 213module_init(input_leds_init);
 214
 215static void __exit input_leds_exit(void)
 216{
 217        input_unregister_handler(&input_leds_handler);
 218}
 219module_exit(input_leds_exit);
 220
 221MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
 222MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
 223MODULE_DESCRIPTION("Input -> LEDs Bridge");
 224MODULE_LICENSE("GPL v2");
 225