Linux Audio

Check our new training course

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");