Loading...
Note: File does not exist in v6.2.
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Input Events LED trigger
4 *
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 starts
42 * 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");