diff options
Diffstat (limited to 'drivers/leds/trigger/ledtrig-input-events.c')
-rw-r--r-- | drivers/leds/trigger/ledtrig-input-events.c | 136 |
1 files changed, 34 insertions, 102 deletions
diff --git a/drivers/leds/trigger/ledtrig-input-events.c b/drivers/leds/trigger/ledtrig-input-events.c index 1de0176799f9..1c79731562c2 100644 --- a/drivers/leds/trigger/ledtrig-input-events.c +++ b/drivers/leds/trigger/ledtrig-input-events.c @@ -3,31 +3,32 @@ * Input Events LED trigger * * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> - * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c. */ #include <linux/input.h> #include <linux/jiffies.h> #include <linux/leds.h> #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include "../leds.h" -#define DEFAULT_LED_OFF_DELAY_MS 5000 +static unsigned long led_off_delay_ms = 5000; +module_param(led_off_delay_ms, ulong, 0644); +MODULE_PARM_DESC(led_off_delay_ms, + "Specify delay in ms for turning LEDs off after last input event"); -struct input_events_data { - struct input_handler handler; +static struct input_events_data { struct delayed_work work; spinlock_t lock; - struct led_classdev *led_cdev; - int led_cdev_saved_flags; /* To avoid repeatedly setting the brightness while there are events */ bool led_on; unsigned long led_off_time; - unsigned long led_off_delay; -}; +} input_events_data; + +static struct led_trigger *input_events_led_trigger; static void led_input_events_work(struct work_struct *work) { @@ -41,62 +42,24 @@ static void led_input_events_work(struct work_struct *work) * running before a new event pushed led_off_time back. */ if (time_after_eq(jiffies, data->led_off_time)) { - led_set_brightness_nosleep(data->led_cdev, LED_OFF); + led_trigger_event(input_events_led_trigger, LED_OFF); data->led_on = false; } spin_unlock_irq(&data->lock); } -static ssize_t delay_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct input_events_data *input_events_data = led_trigger_get_drvdata(dev); - - return sysfs_emit(buf, "%lu\n", input_events_data->led_off_delay); -} - -static ssize_t delay_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - struct input_events_data *input_events_data = led_trigger_get_drvdata(dev); - unsigned long delay; - int ret; - - ret = kstrtoul(buf, 0, &delay); - if (ret) - return ret; - - /* Clamp between 0.5 and 1000 seconds */ - delay = clamp_val(delay, 500UL, 1000000UL); - input_events_data->led_off_delay = msecs_to_jiffies(delay); - - return size; -} - -static DEVICE_ATTR_RW(delay); - -static struct attribute *input_events_led_attrs[] = { - &dev_attr_delay.attr, - NULL -}; -ATTRIBUTE_GROUPS(input_events_led); - static void input_events_event(struct input_handle *handle, unsigned int type, unsigned int code, int val) { - struct input_events_data *data = - container_of(handle->handler, struct input_events_data, handler); - unsigned long led_off_delay = READ_ONCE(data->led_off_delay); - struct led_classdev *led_cdev = data->led_cdev; + struct input_events_data *data = &input_events_data; + unsigned long led_off_delay = msecs_to_jiffies(led_off_delay_ms); unsigned long flags; - if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags)) - led_cdev->blink_brightness = led_cdev->new_blink_brightness; - spin_lock_irqsave(&data->lock, flags); if (!data->led_on) { - led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness); + led_trigger_event(input_events_led_trigger, LED_FULL); data->led_on = true; } data->led_off_time = jiffies + led_off_delay; @@ -118,7 +81,7 @@ static int input_events_connect(struct input_handler *handler, struct input_dev handle->dev = dev; handle->handler = handler; - handle->name = "input-events"; + handle->name = KBUILD_MODNAME; ret = input_register_handle(handle); if (ret) @@ -160,72 +123,41 @@ static const struct input_device_id input_events_ids[] = { { } }; -static int input_events_activate(struct led_classdev *led_cdev) +static struct input_handler input_events_handler = { + .name = KBUILD_MODNAME, + .event = input_events_event, + .connect = input_events_connect, + .disconnect = input_events_disconnect, + .id_table = input_events_ids, +}; + +static int __init input_events_init(void) { - struct input_events_data *data; int ret; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->handler.name = "input-events"; - data->handler.event = input_events_event; - data->handler.connect = input_events_connect; - data->handler.disconnect = input_events_disconnect; - data->handler.id_table = input_events_ids; - - INIT_DELAYED_WORK(&data->work, led_input_events_work); - spin_lock_init(&data->lock); + INIT_DELAYED_WORK(&input_events_data.work, led_input_events_work); + spin_lock_init(&input_events_data.lock); - data->led_cdev = led_cdev; - data->led_cdev_saved_flags = led_cdev->flags; - data->led_off_delay = msecs_to_jiffies(DEFAULT_LED_OFF_DELAY_MS); + led_trigger_register_simple("input-events", &input_events_led_trigger); - /* - * Use led_cdev->blink_brightness + LED_BLINK_SW flag so that sysfs - * brightness writes will change led_cdev->new_blink_brightness for - * configuring the on state brightness (like ledtrig-heartbeat). - */ - if (!led_cdev->blink_brightness) - led_cdev->blink_brightness = led_cdev->max_brightness; - - /* Start with LED off */ - led_set_brightness_nosleep(data->led_cdev, LED_OFF); - - ret = input_register_handler(&data->handler); + ret = input_register_handler(&input_events_handler); if (ret) { - kfree(data); + led_trigger_unregister_simple(input_events_led_trigger); return ret; } - set_bit(LED_BLINK_SW, &led_cdev->work_flags); - - /* Turn LED off during suspend, original flags are restored on deactivate() */ - led_cdev->flags |= LED_CORE_SUSPENDRESUME; - - led_set_trigger_data(led_cdev, data); return 0; } -static void input_events_deactivate(struct led_classdev *led_cdev) +static void __exit input_events_exit(void) { - struct input_events_data *data = led_get_trigger_data(led_cdev); - - led_cdev->flags = data->led_cdev_saved_flags; - clear_bit(LED_BLINK_SW, &led_cdev->work_flags); - input_unregister_handler(&data->handler); - cancel_delayed_work_sync(&data->work); - kfree(data); + input_unregister_handler(&input_events_handler); + cancel_delayed_work_sync(&input_events_data.work); + led_trigger_unregister_simple(input_events_led_trigger); } -static struct led_trigger input_events_led_trigger = { - .name = "input-events", - .activate = input_events_activate, - .deactivate = input_events_deactivate, - .groups = input_events_led_groups, -}; -module_led_trigger(input_events_led_trigger); +module_init(input_events_init); +module_exit(input_events_exit); MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); MODULE_DESCRIPTION("Input Events LED trigger"); |