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 * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c. 7 */ 8 9 #include <linux/input.h> 10 #include <linux/jiffies.h> 11 #include <linux/leds.h> 12 #include <linux/module.h> 13 #include <linux/slab.h> 14 #include <linux/spinlock.h> 15 #include <linux/workqueue.h> 16 #include "../leds.h" 17 18 #define DEFAULT_LED_OFF_DELAY_MS 5000 19 20 struct input_events_data { 21 struct input_handler handler; 22 struct delayed_work work; 23 spinlock_t lock; 24 struct led_classdev *led_cdev; 25 int led_cdev_saved_flags; 26 /* To avoid repeatedly setting the brightness while there are events */ 27 bool led_on; 28 unsigned long led_off_time; 29 unsigned long led_off_delay; 30 }; 31 32 static void led_input_events_work(struct work_struct *work) 33 { 34 struct input_events_data *data = 35 container_of(work, struct input_events_data, work.work); 36 37 spin_lock_irq(&data->lock); 38 39 /* 40 * This time_after_eq() check avoids a race where this work starts 41 * running before a new event pushed led_off_time back. 42 */ 43 if (time_after_eq(jiffies, data->led_off_time)) { 44 led_set_brightness_nosleep(data->led_cdev, LED_OFF); 45 data->led_on = false; 46 } 47 48 spin_unlock_irq(&data->lock); 49 } 50 51 static ssize_t delay_show(struct device *dev, struct device_attribute *attr, char *buf) 52 { 53 struct input_events_data *input_events_data = led_trigger_get_drvdata(dev); 54 55 return sysfs_emit(buf, "%lu\n", input_events_data->led_off_delay); 56 } 57 58 static ssize_t delay_store(struct device *dev, struct device_attribute *attr, 59 const char *buf, size_t size) 60 { 61 struct input_events_data *input_events_data = led_trigger_get_drvdata(dev); 62 unsigned long delay; 63 int ret; 64 65 ret = kstrtoul(buf, 0, &delay); 66 if (ret) 67 return ret; 68 69 /* Clamp between 0.5 and 1000 seconds */ 70 delay = clamp_val(delay, 500UL, 1000000UL); 71 input_events_data->led_off_delay = msecs_to_jiffies(delay); 72 73 return size; 74 } 75 76 static DEVICE_ATTR_RW(delay); 77 78 static struct attribute *input_events_led_attrs[] = { 79 &dev_attr_delay.attr, 80 NULL 81 }; 82 ATTRIBUTE_GROUPS(input_events_led); 83 84 static void input_events_event(struct input_handle *handle, unsigned int type, 85 unsigned int code, int val) 86 { 87 struct input_events_data *data = 88 container_of(handle->handler, struct input_events_data, handler); 89 unsigned long led_off_delay = READ_ONCE(data->led_off_delay); 90 struct led_classdev *led_cdev = data->led_cdev; 91 unsigned long flags; 92 93 if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags)) 94 led_cdev->blink_brightness = led_cdev->new_blink_brightness; 95 96 spin_lock_irqsave(&data->lock, flags); 97 98 if (!data->led_on) { 99 led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness); 100 data->led_on = true; 101 } 102 data->led_off_time = jiffies + led_off_delay; 103 104 spin_unlock_irqrestore(&data->lock, flags); 105 106 mod_delayed_work(system_wq, &data->work, led_off_delay); 107 } 108 109 static int input_events_connect(struct input_handler *handler, struct input_dev *dev, 110 const struct input_device_id *id) 111 { 112 struct input_handle *handle; 113 int ret; 114 115 handle = kzalloc(sizeof(*handle), GFP_KERNEL); 116 if (!handle) 117 return -ENOMEM; 118 119 handle->dev = dev; 120 handle->handler = handler; 121 handle->name = "input-events"; 122 123 ret = input_register_handle(handle); 124 if (ret) 125 goto err_free_handle; 126 127 ret = input_open_device(handle); 128 if (ret) 129 goto err_unregister_handle; 130 131 return 0; 132 133 err_unregister_handle: 134 input_unregister_handle(handle); 135 err_free_handle: 136 kfree(handle); 137 return ret; 138 } 139 140 static void input_events_disconnect(struct input_handle *handle) 141 { 142 input_close_device(handle); 143 input_unregister_handle(handle); 144 kfree(handle); 145 } 146 147 static const struct input_device_id input_events_ids[] = { 148 { 149 .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 150 .evbit = { BIT_MASK(EV_KEY) }, 151 }, 152 { 153 .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 154 .evbit = { BIT_MASK(EV_REL) }, 155 }, 156 { 157 .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 158 .evbit = { BIT_MASK(EV_ABS) }, 159 }, 160 { } 161 }; 162 163 static int input_events_activate(struct led_classdev *led_cdev) 164 { 165 struct input_events_data *data; 166 int ret; 167 168 data = kzalloc(sizeof(*data), GFP_KERNEL); 169 if (!data) 170 return -ENOMEM; 171 172 data->handler.name = "input-events"; 173 data->handler.event = input_events_event; 174 data->handler.connect = input_events_connect; 175 data->handler.disconnect = input_events_disconnect; 176 data->handler.id_table = input_events_ids; 177 178 INIT_DELAYED_WORK(&data->work, led_input_events_work); 179 spin_lock_init(&data->lock); 180 181 data->led_cdev = led_cdev; 182 data->led_cdev_saved_flags = led_cdev->flags; 183 data->led_off_delay = msecs_to_jiffies(DEFAULT_LED_OFF_DELAY_MS); 184 185 /* 186 * Use led_cdev->blink_brightness + LED_BLINK_SW flag so that sysfs 187 * brightness writes will change led_cdev->new_blink_brightness for 188 * configuring the on state brightness (like ledtrig-heartbeat). 189 */ 190 if (!led_cdev->blink_brightness) 191 led_cdev->blink_brightness = led_cdev->max_brightness; 192 193 /* Start with LED off */ 194 led_set_brightness_nosleep(data->led_cdev, LED_OFF); 195 196 ret = input_register_handler(&data->handler); 197 if (ret) { 198 kfree(data); 199 return ret; 200 } 201 202 set_bit(LED_BLINK_SW, &led_cdev->work_flags); 203 204 /* Turn LED off during suspend, original flags are restored on deactivate() */ 205 led_cdev->flags |= LED_CORE_SUSPENDRESUME; 206 207 led_set_trigger_data(led_cdev, data); 208 return 0; 209 } 210 211 static void input_events_deactivate(struct led_classdev *led_cdev) 212 { 213 struct input_events_data *data = led_get_trigger_data(led_cdev); 214 215 led_cdev->flags = data->led_cdev_saved_flags; 216 clear_bit(LED_BLINK_SW, &led_cdev->work_flags); 217 input_unregister_handler(&data->handler); 218 cancel_delayed_work_sync(&data->work); 219 kfree(data); 220 } 221 222 static struct led_trigger input_events_led_trigger = { 223 .name = "input-events", 224 .activate = input_events_activate, 225 .deactivate = input_events_deactivate, 226 .groups = input_events_led_groups, 227 }; 228 module_led_trigger(input_events_led_trigger); 229 230 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); 231 MODULE_DESCRIPTION("Input Events LED trigger"); 232 MODULE_LICENSE("GPL"); 233 MODULE_ALIAS("ledtrig:input-events"); 234