166 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Input Events LED trigger4 *5 * Copyright (C) 2024 Hans de Goede <hansg@kernel.org>6 */7 8#include <linux/input.h>9#include <linux/jiffies.h>10#include <linux/leds.h>11#include <linux/module.h>12#include <linux/moduleparam.h>13#include <linux/slab.h>14#include <linux/spinlock.h>15#include <linux/workqueue.h>16#include "../leds.h"17 18static unsigned long led_off_delay_ms = 5000;19module_param(led_off_delay_ms, ulong, 0644);20MODULE_PARM_DESC(led_off_delay_ms,21 "Specify delay in ms for turning LEDs off after last input event");22 23static struct input_events_data {24 struct delayed_work work;25 spinlock_t lock;26 /* To avoid repeatedly setting the brightness while there are events */27 bool led_on;28 unsigned long led_off_time;29} input_events_data;30 31static struct led_trigger *input_events_led_trigger;32 33static void led_input_events_work(struct work_struct *work)34{35 struct input_events_data *data =36 container_of(work, struct input_events_data, work.work);37 38 spin_lock_irq(&data->lock);39 40 /*41 * This time_after_eq() check avoids a race where this work starts42 * running before a new event pushed led_off_time back.43 */44 if (time_after_eq(jiffies, data->led_off_time)) {45 led_trigger_event(input_events_led_trigger, LED_OFF);46 data->led_on = false;47 }48 49 spin_unlock_irq(&data->lock);50}51 52static void input_events_event(struct input_handle *handle, unsigned int type,53 unsigned int code, int val)54{55 struct input_events_data *data = &input_events_data;56 unsigned long led_off_delay = msecs_to_jiffies(led_off_delay_ms);57 unsigned long flags;58 59 spin_lock_irqsave(&data->lock, flags);60 61 if (!data->led_on) {62 led_trigger_event(input_events_led_trigger, LED_FULL);63 data->led_on = true;64 }65 data->led_off_time = jiffies + led_off_delay;66 67 spin_unlock_irqrestore(&data->lock, flags);68 69 mod_delayed_work(system_wq, &data->work, led_off_delay);70}71 72static int input_events_connect(struct input_handler *handler, struct input_dev *dev,73 const struct input_device_id *id)74{75 struct input_handle *handle;76 int ret;77 78 handle = kzalloc(sizeof(*handle), GFP_KERNEL);79 if (!handle)80 return -ENOMEM;81 82 handle->dev = dev;83 handle->handler = handler;84 handle->name = KBUILD_MODNAME;85 86 ret = input_register_handle(handle);87 if (ret)88 goto err_free_handle;89 90 ret = input_open_device(handle);91 if (ret)92 goto err_unregister_handle;93 94 return 0;95 96err_unregister_handle:97 input_unregister_handle(handle);98err_free_handle:99 kfree(handle);100 return ret;101}102 103static void input_events_disconnect(struct input_handle *handle)104{105 input_close_device(handle);106 input_unregister_handle(handle);107 kfree(handle);108}109 110static const struct input_device_id input_events_ids[] = {111 {112 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,113 .evbit = { BIT_MASK(EV_KEY) },114 },115 {116 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,117 .evbit = { BIT_MASK(EV_REL) },118 },119 {120 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,121 .evbit = { BIT_MASK(EV_ABS) },122 },123 { }124};125 126static struct input_handler input_events_handler = {127 .name = KBUILD_MODNAME,128 .event = input_events_event,129 .connect = input_events_connect,130 .disconnect = input_events_disconnect,131 .id_table = input_events_ids,132};133 134static int __init input_events_init(void)135{136 int ret;137 138 INIT_DELAYED_WORK(&input_events_data.work, led_input_events_work);139 spin_lock_init(&input_events_data.lock);140 141 led_trigger_register_simple("input-events", &input_events_led_trigger);142 143 ret = input_register_handler(&input_events_handler);144 if (ret) {145 led_trigger_unregister_simple(input_events_led_trigger);146 return ret;147 }148 149 return 0;150}151 152static void __exit input_events_exit(void)153{154 input_unregister_handler(&input_events_handler);155 cancel_delayed_work_sync(&input_events_data.work);156 led_trigger_unregister_simple(input_events_led_trigger);157}158 159module_init(input_events_init);160module_exit(input_events_exit);161 162MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");163MODULE_DESCRIPTION("Input Events LED trigger");164MODULE_LICENSE("GPL");165MODULE_ALIAS("ledtrig:input-events");166