diff options
Diffstat (limited to 'drivers/leds/trigger')
-rw-r--r-- | drivers/leds/trigger/Kconfig | 23 | ||||
-rw-r--r-- | drivers/leds/trigger/Makefile | 2 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-activity.c | 2 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-audio.c | 67 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-input-events.c | 165 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-netdev.c | 44 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-pattern.c | 126 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-timer.c | 5 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-transient.c | 2 |
9 files changed, 323 insertions, 113 deletions
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index d11d80176fc0..c11282a74b5a 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -136,13 +136,6 @@ config LEDS_TRIGGER_PATTERN which is a series of tuples, of brightness and duration (ms). If unsure, say N -config LEDS_TRIGGER_AUDIO - tristate "Audio Mute LED Trigger" - help - This allows LEDs to be controlled by audio drivers for following - the audio mute and mic-mute changes. - If unsure, say N - config LEDS_TRIGGER_TTY tristate "LED Trigger for TTY devices" depends on TTY @@ -152,4 +145,20 @@ config LEDS_TRIGGER_TTY When build as a module this driver will be called ledtrig-tty. +config LEDS_TRIGGER_INPUT_EVENTS + tristate "LED Input events trigger" + depends on INPUT + help + Turn LEDs on when there is input (/dev/input/event*) activity and turn + them back off again after there has been no activity for 5 seconds. + + This is primarily intended to control LEDs which are a backlight for + capacitive touch-buttons, such as e.g. the menu / home / back buttons + found on the bottom bezel of many older smartphones and tablets. + + This can also be used to turn on the keyboard backlight LED on + input events and turn the keyboard backlight off again when idle. + + When build as a module this driver will be called ledtrig-input-events. + endif # LEDS_TRIGGERS diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 25c4db97cdd4..3b3628889f68 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -14,5 +14,5 @@ obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o -obj-$(CONFIG_LEDS_TRIGGER_AUDIO) += ledtrig-audio.o obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o +obj-$(CONFIG_LEDS_TRIGGER_INPUT_EVENTS) += ledtrig-input-events.o diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c index 33cbf8413658..b3ee33aed36e 100644 --- a/drivers/leds/trigger/ledtrig-activity.c +++ b/drivers/leds/trigger/ledtrig-activity.c @@ -156,7 +156,7 @@ static ssize_t led_invert_show(struct device *dev, { struct activity_data *activity_data = led_trigger_get_drvdata(dev); - return sprintf(buf, "%u\n", activity_data->invert); + return sprintf(buf, "%d\n", activity_data->invert); } static ssize_t led_invert_store(struct device *dev, diff --git a/drivers/leds/trigger/ledtrig-audio.c b/drivers/leds/trigger/ledtrig-audio.c deleted file mode 100644 index 2ecd4b760fc3..000000000000 --- a/drivers/leds/trigger/ledtrig-audio.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Audio Mute LED trigger -// - -#include <linux/kernel.h> -#include <linux/leds.h> -#include <linux/module.h> -#include "../leds.h" - -static enum led_brightness audio_state[NUM_AUDIO_LEDS]; - -static int ledtrig_audio_mute_activate(struct led_classdev *led_cdev) -{ - led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MUTE]); - return 0; -} - -static int ledtrig_audio_micmute_activate(struct led_classdev *led_cdev) -{ - led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MICMUTE]); - return 0; -} - -static struct led_trigger ledtrig_audio[NUM_AUDIO_LEDS] = { - [LED_AUDIO_MUTE] = { - .name = "audio-mute", - .activate = ledtrig_audio_mute_activate, - }, - [LED_AUDIO_MICMUTE] = { - .name = "audio-micmute", - .activate = ledtrig_audio_micmute_activate, - }, -}; - -enum led_brightness ledtrig_audio_get(enum led_audio type) -{ - return audio_state[type]; -} -EXPORT_SYMBOL_GPL(ledtrig_audio_get); - -void ledtrig_audio_set(enum led_audio type, enum led_brightness state) -{ - audio_state[type] = state; - led_trigger_event(&ledtrig_audio[type], state); -} -EXPORT_SYMBOL_GPL(ledtrig_audio_set); - -static int __init ledtrig_audio_init(void) -{ - led_trigger_register(&ledtrig_audio[LED_AUDIO_MUTE]); - led_trigger_register(&ledtrig_audio[LED_AUDIO_MICMUTE]); - return 0; -} -module_init(ledtrig_audio_init); - -static void __exit ledtrig_audio_exit(void) -{ - led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MUTE]); - led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MICMUTE]); -} -module_exit(ledtrig_audio_exit); - -MODULE_DESCRIPTION("LED trigger for audio mute control"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("ledtrig:audio-mute"); -MODULE_ALIAS("ledtrig:audio-micmute"); diff --git a/drivers/leds/trigger/ledtrig-input-events.c b/drivers/leds/trigger/ledtrig-input-events.c new file mode 100644 index 000000000000..1c79731562c2 --- /dev/null +++ b/drivers/leds/trigger/ledtrig-input-events.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Input Events LED trigger + * + * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> + */ + +#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" + +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"); + +static struct input_events_data { + struct delayed_work work; + spinlock_t lock; + /* To avoid repeatedly setting the brightness while there are events */ + bool led_on; + unsigned long led_off_time; +} input_events_data; + +static struct led_trigger *input_events_led_trigger; + +static void led_input_events_work(struct work_struct *work) +{ + struct input_events_data *data = + container_of(work, struct input_events_data, work.work); + + spin_lock_irq(&data->lock); + + /* + * This time_after_eq() check avoids a race where this work starts + * running before a new event pushed led_off_time back. + */ + if (time_after_eq(jiffies, data->led_off_time)) { + led_trigger_event(input_events_led_trigger, LED_OFF); + data->led_on = false; + } + + spin_unlock_irq(&data->lock); +} + +static void input_events_event(struct input_handle *handle, unsigned int type, + unsigned int code, int val) +{ + struct input_events_data *data = &input_events_data; + unsigned long led_off_delay = msecs_to_jiffies(led_off_delay_ms); + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + + if (!data->led_on) { + led_trigger_event(input_events_led_trigger, LED_FULL); + data->led_on = true; + } + data->led_off_time = jiffies + led_off_delay; + + spin_unlock_irqrestore(&data->lock, flags); + + mod_delayed_work(system_wq, &data->work, led_off_delay); +} + +static int input_events_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int ret; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = KBUILD_MODNAME; + + ret = input_register_handle(handle); + if (ret) + goto err_free_handle; + + ret = input_open_device(handle); + if (ret) + goto err_unregister_handle; + + return 0; + +err_unregister_handle: + input_unregister_handle(handle); +err_free_handle: + kfree(handle); + return ret; +} + +static void input_events_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id input_events_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_REL) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_ABS) }, + }, + { } +}; + +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) +{ + int ret; + + INIT_DELAYED_WORK(&input_events_data.work, led_input_events_work); + spin_lock_init(&input_events_data.lock); + + led_trigger_register_simple("input-events", &input_events_led_trigger); + + ret = input_register_handler(&input_events_handler); + if (ret) { + led_trigger_unregister_simple(input_events_led_trigger); + return ret; + } + + return 0; +} + +static void __exit input_events_exit(void) +{ + input_unregister_handler(&input_events_handler); + cancel_delayed_work_sync(&input_events_data.work); + led_trigger_unregister_simple(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"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ledtrig:input-events"); diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index ea00f6c70882..4e048e08c4fd 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -39,6 +39,8 @@ * (has carrier) or not * tx - LED blinks on transmitted data * rx - LED blinks on receive data + * tx_err - LED blinks on transmit error + * rx_err - LED blinks on receive error * * Note: If the user selects a mode that is not supported by hw, default * behavior is to fall back to software control of the LED. However not every @@ -66,6 +68,7 @@ struct led_netdev_data { unsigned int last_activity; unsigned long mode; + unsigned long blink_delay; int link_speed; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes); u8 duplex; @@ -84,6 +87,10 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) /* Already validated, hw control is possible with the requested mode */ if (trigger_data->hw_control) { led_cdev->hw_control_set(led_cdev, trigger_data->mode); + if (led_cdev->blink_set) { + led_cdev->blink_set(led_cdev, &trigger_data->blink_delay, + &trigger_data->blink_delay); + } return; } @@ -144,7 +151,9 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) * checking stats */ if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) || - test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) + test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode)) schedule_delayed_work(&trigger_data->work, 0); } } @@ -337,6 +346,8 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf, case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_RX: + case TRIGGER_NETDEV_TX_ERR: + case TRIGGER_NETDEV_RX_ERR: bit = attr; break; default: @@ -371,6 +382,8 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_RX: + case TRIGGER_NETDEV_TX_ERR: + case TRIGGER_NETDEV_RX_ERR: bit = attr; break; default: @@ -429,6 +442,8 @@ DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX); +DEFINE_NETDEV_TRIGGER(tx_err, TRIGGER_NETDEV_TX_ERR); +DEFINE_NETDEV_TRIGGER(rx_err, TRIGGER_NETDEV_RX_ERR); static ssize_t interval_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -444,10 +459,11 @@ static ssize_t interval_store(struct device *dev, size_t size) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); + struct led_classdev *led_cdev = trigger_data->led_cdev; unsigned long value; int ret; - if (trigger_data->hw_control) + if (trigger_data->hw_control && !led_cdev->blink_set) return -EINVAL; ret = kstrtoul(buf, 0, &value); @@ -456,9 +472,13 @@ static ssize_t interval_store(struct device *dev, /* impose some basic bounds on the timer interval */ if (value >= 5 && value <= 10000) { - cancel_delayed_work_sync(&trigger_data->work); + if (trigger_data->hw_control) { + trigger_data->blink_delay = value; + } else { + cancel_delayed_work_sync(&trigger_data->work); - atomic_set(&trigger_data->interval, msecs_to_jiffies(value)); + atomic_set(&trigger_data->interval, msecs_to_jiffies(value)); + } set_baseline_state(trigger_data); /* resets timer */ } @@ -538,6 +558,8 @@ static struct attribute *netdev_trig_attrs[] = { &dev_attr_half_duplex.attr, &dev_attr_rx.attr, &dev_attr_tx.attr, + &dev_attr_rx_err.attr, + &dev_attr_tx_err.attr, &dev_attr_interval.attr, &dev_attr_offloaded.attr, NULL @@ -593,6 +615,8 @@ static int netdev_trig_notify(struct notifier_block *nb, trigger_data->net_dev = NULL; break; case NETDEV_UP: + trigger_data->hw_control = can_hw_control(trigger_data); + fallthrough; case NETDEV_CHANGE: get_device_state(trigger_data); /* Refresh link_speed visibility */ @@ -628,7 +652,9 @@ static void netdev_trig_work(struct work_struct *work) /* If we are not looking for RX/TX then return */ if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) && - !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) + !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) && + !test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) && + !test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode)) return; dev_stats = dev_get_stats(trigger_data->net_dev, &temp); @@ -636,7 +662,11 @@ static void netdev_trig_work(struct work_struct *work) (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ? dev_stats->tx_packets : 0) + (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ? - dev_stats->rx_packets : 0); + dev_stats->rx_packets : 0) + + (test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ? + dev_stats->tx_errors : 0) + + (test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode) ? + dev_stats->rx_errors : 0); if (trigger_data->last_activity != new_activity) { led_stop_software_blink(trigger_data->led_cdev); @@ -724,8 +754,6 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev) cancel_delayed_work_sync(&trigger_data->work); - led_set_brightness(led_cdev, LED_OFF); - dev_put(trigger_data->net_dev); kfree(trigger_data); diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c index fadd87dbe993..06d052957d37 100644 --- a/drivers/leds/trigger/ledtrig-pattern.c +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -13,6 +13,7 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/timer.h> +#include <linux/hrtimer.h> #define MAX_PATTERNS 1024 /* @@ -21,6 +22,12 @@ */ #define UPDATE_INTERVAL 50 +enum pattern_type { + PATTERN_TYPE_SW, /* Use standard timer for software pattern */ + PATTERN_TYPE_HR, /* Use hrtimer for software pattern */ + PATTERN_TYPE_HW, /* Hardware pattern */ +}; + struct pattern_trig_data { struct led_classdev *led_cdev; struct led_pattern patterns[MAX_PATTERNS]; @@ -32,8 +39,9 @@ struct pattern_trig_data { int last_repeat; int delta_t; bool is_indefinite; - bool is_hw_pattern; + enum pattern_type type; struct timer_list timer; + struct hrtimer hrtimer; }; static void pattern_trig_update_patterns(struct pattern_trig_data *data) @@ -71,10 +79,35 @@ static int pattern_trig_compute_brightness(struct pattern_trig_data *data) return data->curr->brightness - step_brightness; } -static void pattern_trig_timer_function(struct timer_list *t) +static void pattern_trig_timer_start(struct pattern_trig_data *data) { - struct pattern_trig_data *data = from_timer(data, t, timer); + if (data->type == PATTERN_TYPE_HR) { + hrtimer_start(&data->hrtimer, ns_to_ktime(0), HRTIMER_MODE_REL); + } else { + data->timer.expires = jiffies; + add_timer(&data->timer); + } +} +static void pattern_trig_timer_cancel(struct pattern_trig_data *data) +{ + if (data->type == PATTERN_TYPE_HR) + hrtimer_cancel(&data->hrtimer); + else + timer_delete_sync(&data->timer); +} + +static void pattern_trig_timer_restart(struct pattern_trig_data *data, + unsigned long interval) +{ + if (data->type == PATTERN_TYPE_HR) + hrtimer_forward_now(&data->hrtimer, ms_to_ktime(interval)); + else + mod_timer(&data->timer, jiffies + msecs_to_jiffies(interval)); +} + +static void pattern_trig_timer_common_function(struct pattern_trig_data *data) +{ for (;;) { if (!data->is_indefinite && !data->repeat) break; @@ -83,8 +116,7 @@ static void pattern_trig_timer_function(struct timer_list *t) /* Step change of brightness */ led_set_brightness(data->led_cdev, data->curr->brightness); - mod_timer(&data->timer, - jiffies + msecs_to_jiffies(data->curr->delta_t)); + pattern_trig_timer_restart(data, data->curr->delta_t); if (!data->next->delta_t) { /* Skip the tuple with zero duration */ pattern_trig_update_patterns(data); @@ -106,8 +138,7 @@ static void pattern_trig_timer_function(struct timer_list *t) led_set_brightness(data->led_cdev, pattern_trig_compute_brightness(data)); - mod_timer(&data->timer, - jiffies + msecs_to_jiffies(UPDATE_INTERVAL)); + pattern_trig_timer_restart(data, UPDATE_INTERVAL); /* Accumulate the gradual dimming time */ data->delta_t += UPDATE_INTERVAL; @@ -117,6 +148,25 @@ static void pattern_trig_timer_function(struct timer_list *t) } } +static void pattern_trig_timer_function(struct timer_list *t) +{ + struct pattern_trig_data *data = from_timer(data, t, timer); + + return pattern_trig_timer_common_function(data); +} + +static enum hrtimer_restart pattern_trig_hrtimer_function(struct hrtimer *t) +{ + struct pattern_trig_data *data = + container_of(t, struct pattern_trig_data, hrtimer); + + pattern_trig_timer_common_function(data); + if (!data->is_indefinite && !data->repeat) + return HRTIMER_NORESTART; + + return HRTIMER_RESTART; +} + static int pattern_trig_start_pattern(struct led_classdev *led_cdev) { struct pattern_trig_data *data = led_cdev->trigger_data; @@ -124,7 +174,7 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev) if (!data->npatterns) return 0; - if (data->is_hw_pattern) { + if (data->type == PATTERN_TYPE_HW) { return led_cdev->pattern_set(led_cdev, data->patterns, data->npatterns, data->repeat); } @@ -136,8 +186,7 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev) data->delta_t = 0; data->curr = data->patterns; data->next = data->patterns + 1; - data->timer.expires = jiffies; - add_timer(&data->timer); + pattern_trig_timer_start(data); return 0; } @@ -175,9 +224,9 @@ static ssize_t repeat_store(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); - del_timer_sync(&data->timer); + pattern_trig_timer_cancel(data); - if (data->is_hw_pattern) + if (data->type == PATTERN_TYPE_HW) led_cdev->pattern_clear(led_cdev); data->last_repeat = data->repeat = res; @@ -196,14 +245,14 @@ static ssize_t repeat_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(repeat); static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data, - char *buf, bool hw_pattern) + char *buf, enum pattern_type type) { ssize_t count = 0; int i; mutex_lock(&data->lock); - if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern)) + if (!data->npatterns || data->type != type) goto out; for (i = 0; i < data->npatterns; i++) { @@ -260,19 +309,19 @@ static int pattern_trig_store_patterns_int(struct pattern_trig_data *data, static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev, const char *buf, const u32 *buf_int, - size_t count, bool hw_pattern) + size_t count, enum pattern_type type) { struct pattern_trig_data *data = led_cdev->trigger_data; int err = 0; mutex_lock(&data->lock); - del_timer_sync(&data->timer); + pattern_trig_timer_cancel(data); - if (data->is_hw_pattern) + if (data->type == PATTERN_TYPE_HW) led_cdev->pattern_clear(led_cdev); - data->is_hw_pattern = hw_pattern; + data->type = type; data->npatterns = 0; if (buf) @@ -297,7 +346,7 @@ static ssize_t pattern_show(struct device *dev, struct device_attribute *attr, struct led_classdev *led_cdev = dev_get_drvdata(dev); struct pattern_trig_data *data = led_cdev->trigger_data; - return pattern_trig_show_patterns(data, buf, false); + return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_SW); } static ssize_t pattern_store(struct device *dev, struct device_attribute *attr, @@ -305,7 +354,8 @@ static ssize_t pattern_store(struct device *dev, struct device_attribute *attr, { struct led_classdev *led_cdev = dev_get_drvdata(dev); - return pattern_trig_store_patterns(led_cdev, buf, NULL, count, false); + return pattern_trig_store_patterns(led_cdev, buf, NULL, count, + PATTERN_TYPE_SW); } static DEVICE_ATTR_RW(pattern); @@ -316,7 +366,7 @@ static ssize_t hw_pattern_show(struct device *dev, struct led_classdev *led_cdev = dev_get_drvdata(dev); struct pattern_trig_data *data = led_cdev->trigger_data; - return pattern_trig_show_patterns(data, buf, true); + return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HW); } static ssize_t hw_pattern_store(struct device *dev, @@ -325,11 +375,33 @@ static ssize_t hw_pattern_store(struct device *dev, { struct led_classdev *led_cdev = dev_get_drvdata(dev); - return pattern_trig_store_patterns(led_cdev, buf, NULL, count, true); + return pattern_trig_store_patterns(led_cdev, buf, NULL, count, + PATTERN_TYPE_HW); } static DEVICE_ATTR_RW(hw_pattern); +static ssize_t hr_pattern_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct pattern_trig_data *data = led_cdev->trigger_data; + + return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HR); +} + +static ssize_t hr_pattern_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return pattern_trig_store_patterns(led_cdev, buf, NULL, count, + PATTERN_TYPE_HR); +} + +static DEVICE_ATTR_RW(hr_pattern); + static umode_t pattern_trig_attrs_mode(struct kobject *kobj, struct attribute *attr, int index) { @@ -338,6 +410,8 @@ static umode_t pattern_trig_attrs_mode(struct kobject *kobj, if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr) return attr->mode; + else if (attr == &dev_attr_hr_pattern.attr) + return attr->mode; else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set) return attr->mode; @@ -347,6 +421,7 @@ static umode_t pattern_trig_attrs_mode(struct kobject *kobj, static struct attribute *pattern_trig_attrs[] = { &dev_attr_pattern.attr, &dev_attr_hw_pattern.attr, + &dev_attr_hr_pattern.attr, &dev_attr_repeat.attr, NULL }; @@ -376,7 +451,8 @@ static void pattern_init(struct led_classdev *led_cdev) goto out; } - err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size, false); + err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size, + PATTERN_TYPE_SW); if (err < 0) dev_warn(led_cdev->dev, "Pattern initialization failed with error %d\n", err); @@ -400,12 +476,15 @@ static int pattern_trig_activate(struct led_classdev *led_cdev) led_cdev->pattern_clear = NULL; } + data->type = PATTERN_TYPE_SW; data->is_indefinite = true; data->last_repeat = -1; mutex_init(&data->lock); data->led_cdev = led_cdev; led_set_trigger_data(led_cdev, data); timer_setup(&data->timer, pattern_trig_timer_function, 0); + hrtimer_setup(&data->hrtimer, pattern_trig_hrtimer_function, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); led_cdev->activated = true; if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) { @@ -431,6 +510,7 @@ static void pattern_trig_deactivate(struct led_classdev *led_cdev) led_cdev->pattern_clear(led_cdev); timer_shutdown_sync(&data->timer); + hrtimer_cancel(&data->hrtimer); led_set_brightness(led_cdev, LED_OFF); kfree(data); diff --git a/drivers/leds/trigger/ledtrig-timer.c b/drivers/leds/trigger/ledtrig-timer.c index b4688d1d9d2b..1d213c999d40 100644 --- a/drivers/leds/trigger/ledtrig-timer.c +++ b/drivers/leds/trigger/ledtrig-timer.c @@ -110,11 +110,6 @@ static int timer_trig_activate(struct led_classdev *led_cdev) led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER; } - /* - * If "set brightness to 0" is pending in workqueue, we don't - * want that to be reordered after blink_set() - */ - flush_work(&led_cdev->set_brightness_work); led_blink_set(led_cdev, &led_cdev->blink_delay_on, &led_cdev->blink_delay_off); diff --git a/drivers/leds/trigger/ledtrig-transient.c b/drivers/leds/trigger/ledtrig-transient.c index f111fa7635e5..e103c7ed830b 100644 --- a/drivers/leds/trigger/ledtrig-transient.c +++ b/drivers/leds/trigger/ledtrig-transient.c @@ -66,7 +66,7 @@ static ssize_t transient_activate_store(struct device *dev, /* cancel the running timer */ if (state == 0 && transient_data->activate == 1) { - del_timer(&transient_data->timer); + timer_delete(&transient_data->timer); transient_data->activate = state; led_set_brightness_nosleep(led_cdev, transient_data->restore_state); |