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 18 static unsigned long led_off_delay_ms = 5000; 19 module_param(led_off_delay_ms, ulong, 0644); 20 MODULE_PARM_DESC(led_off_delay_ms, 21 "Specify delay in ms for turning LEDs off after last input event"); 22 23 static 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 31 static struct led_trigger *input_events_led_trigger; 32 33 static 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 52 static 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 72 static 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 96 err_unregister_handle: 97 input_unregister_handle(handle); 98 err_free_handle: 99 kfree(handle); 100 return ret; 101 } 102 103 static void input_events_disconnect(struct input_handle *handle) 104 { 105 input_close_device(handle); 106 input_unregister_handle(handle); 107 kfree(handle); 108 } 109 110 static 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 126 static 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 134 static 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 152 static 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 159 module_init(input_events_init); 160 module_exit(input_events_exit); 161 162 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); 163 MODULE_DESCRIPTION("Input Events LED trigger"); 164 MODULE_LICENSE("GPL"); 165 MODULE_ALIAS("ledtrig:input-events"); 166