linux/drivers/input/input-leds.c
<<
>>
Prefs
   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");
 221