Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * LED support for the input layer
0004  *
0005  * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org>
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/slab.h>
0010 #include <linux/module.h>
0011 #include <linux/init.h>
0012 #include <linux/leds.h>
0013 #include <linux/input.h>
0014 
0015 #if IS_ENABLED(CONFIG_VT)
0016 #define VT_TRIGGER(_name)   .trigger = _name
0017 #else
0018 #define VT_TRIGGER(_name)   .trigger = NULL
0019 #endif
0020 
0021 static const struct {
0022     const char *name;
0023     const char *trigger;
0024 } input_led_info[LED_CNT] = {
0025     [LED_NUML]  = { "numlock", VT_TRIGGER("kbd-numlock") },
0026     [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") },
0027     [LED_SCROLLL]   = { "scrolllock", VT_TRIGGER("kbd-scrolllock") },
0028     [LED_COMPOSE]   = { "compose" },
0029     [LED_KANA]  = { "kana", VT_TRIGGER("kbd-kanalock") },
0030     [LED_SLEEP] = { "sleep" } ,
0031     [LED_SUSPEND]   = { "suspend" },
0032     [LED_MUTE]  = { "mute" },
0033     [LED_MISC]  = { "misc" },
0034     [LED_MAIL]  = { "mail" },
0035     [LED_CHARGING]  = { "charging" },
0036 };
0037 
0038 struct input_led {
0039     struct led_classdev cdev;
0040     struct input_handle *handle;
0041     unsigned int code; /* One of LED_* constants */
0042 };
0043 
0044 struct input_leds {
0045     struct input_handle handle;
0046     unsigned int num_leds;
0047     struct input_led leds[];
0048 };
0049 
0050 static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev)
0051 {
0052     struct input_led *led = container_of(cdev, struct input_led, cdev);
0053     struct input_dev *input = led->handle->dev;
0054 
0055     return test_bit(led->code, input->led) ? cdev->max_brightness : 0;
0056 }
0057 
0058 static void input_leds_brightness_set(struct led_classdev *cdev,
0059                       enum led_brightness brightness)
0060 {
0061     struct input_led *led = container_of(cdev, struct input_led, cdev);
0062 
0063     input_inject_event(led->handle, EV_LED, led->code, !!brightness);
0064 }
0065 
0066 static void input_leds_event(struct input_handle *handle, unsigned int type,
0067                  unsigned int code, int value)
0068 {
0069 }
0070 
0071 static int input_leds_get_count(struct input_dev *dev)
0072 {
0073     unsigned int led_code;
0074     int count = 0;
0075 
0076     for_each_set_bit(led_code, dev->ledbit, LED_CNT)
0077         if (input_led_info[led_code].name)
0078             count++;
0079 
0080     return count;
0081 }
0082 
0083 static int input_leds_connect(struct input_handler *handler,
0084                   struct input_dev *dev,
0085                   const struct input_device_id *id)
0086 {
0087     struct input_leds *leds;
0088     struct input_led *led;
0089     unsigned int num_leds;
0090     unsigned int led_code;
0091     int led_no;
0092     int error;
0093 
0094     num_leds = input_leds_get_count(dev);
0095     if (!num_leds)
0096         return -ENXIO;
0097 
0098     leds = kzalloc(struct_size(leds, leds, num_leds), GFP_KERNEL);
0099     if (!leds)
0100         return -ENOMEM;
0101 
0102     leds->num_leds = num_leds;
0103 
0104     leds->handle.dev = dev;
0105     leds->handle.handler = handler;
0106     leds->handle.name = "leds";
0107     leds->handle.private = leds;
0108 
0109     error = input_register_handle(&leds->handle);
0110     if (error)
0111         goto err_free_mem;
0112 
0113     error = input_open_device(&leds->handle);
0114     if (error)
0115         goto err_unregister_handle;
0116 
0117     led_no = 0;
0118     for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
0119         if (!input_led_info[led_code].name)
0120             continue;
0121 
0122         led = &leds->leds[led_no];
0123         led->handle = &leds->handle;
0124         led->code = led_code;
0125 
0126         led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
0127                        dev_name(&dev->dev),
0128                        input_led_info[led_code].name);
0129         if (!led->cdev.name) {
0130             error = -ENOMEM;
0131             goto err_unregister_leds;
0132         }
0133 
0134         led->cdev.max_brightness = 1;
0135         led->cdev.brightness_get = input_leds_brightness_get;
0136         led->cdev.brightness_set = input_leds_brightness_set;
0137         led->cdev.default_trigger = input_led_info[led_code].trigger;
0138 
0139         error = led_classdev_register(&dev->dev, &led->cdev);
0140         if (error) {
0141             dev_err(&dev->dev, "failed to register LED %s: %d\n",
0142                 led->cdev.name, error);
0143             kfree(led->cdev.name);
0144             goto err_unregister_leds;
0145         }
0146 
0147         led_no++;
0148     }
0149 
0150     return 0;
0151 
0152 err_unregister_leds:
0153     while (--led_no >= 0) {
0154         struct input_led *led = &leds->leds[led_no];
0155 
0156         led_classdev_unregister(&led->cdev);
0157         kfree(led->cdev.name);
0158     }
0159 
0160     input_close_device(&leds->handle);
0161 
0162 err_unregister_handle:
0163     input_unregister_handle(&leds->handle);
0164 
0165 err_free_mem:
0166     kfree(leds);
0167     return error;
0168 }
0169 
0170 static void input_leds_disconnect(struct input_handle *handle)
0171 {
0172     struct input_leds *leds = handle->private;
0173     int i;
0174 
0175     for (i = 0; i < leds->num_leds; i++) {
0176         struct input_led *led = &leds->leds[i];
0177 
0178         led_classdev_unregister(&led->cdev);
0179         kfree(led->cdev.name);
0180     }
0181 
0182     input_close_device(handle);
0183     input_unregister_handle(handle);
0184 
0185     kfree(leds);
0186 }
0187 
0188 static const struct input_device_id input_leds_ids[] = {
0189     {
0190         .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
0191         .evbit = { BIT_MASK(EV_LED) },
0192     },
0193     { },
0194 };
0195 MODULE_DEVICE_TABLE(input, input_leds_ids);
0196 
0197 static struct input_handler input_leds_handler = {
0198     .event =    input_leds_event,
0199     .connect =  input_leds_connect,
0200     .disconnect =   input_leds_disconnect,
0201     .name =     "leds",
0202     .id_table = input_leds_ids,
0203 };
0204 
0205 static int __init input_leds_init(void)
0206 {
0207     return input_register_handler(&input_leds_handler);
0208 }
0209 module_init(input_leds_init);
0210 
0211 static void __exit input_leds_exit(void)
0212 {
0213     input_unregister_handler(&input_leds_handler);
0214 }
0215 module_exit(input_leds_exit);
0216 
0217 MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
0218 MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
0219 MODULE_DESCRIPTION("Input -> LEDs Bridge");
0220 MODULE_LICENSE("GPL v2");