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_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
 144err_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
 154err_unregister_handle:
 155        input_unregister_handle(&leds->handle);
 156
 157err_free_mem:
 158        kfree(leds);
 159        return error;
 160}
 161
 162static 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
 180static 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};
 187MODULE_DEVICE_TABLE(input, input_leds_ids);
 188
 189static 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
 197static int __init input_leds_init(void)
 198{
 199        return input_register_handler(&input_leds_handler);
 200}
 201module_init(input_leds_init);
 202
 203static void __exit input_leds_exit(void)
 204{
 205        input_unregister_handler(&input_leds_handler);
 206}
 207module_exit(input_leds_exit);
 208
 209MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
 210MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
 211MODULE_DESCRIPTION("Input -> LEDs Bridge");
 212MODULE_LICENSE("GPL v2");
 213