aboutsummaryrefslogtreecommitdiff
path: root/drivers/leds/led-triggers.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/led-triggers.c')
-rw-r--r--drivers/leds/led-triggers.c237
1 files changed, 160 insertions, 77 deletions
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 13c9026d68a..c3734f10fdd 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -13,14 +13,13 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
-#include <linux/sysdev.h>
#include <linux/timer.h>
#include <linux/rwsem.h>
#include <linux/leds.h>
+#include <linux/slab.h>
#include "leds.h"
/*
@@ -29,6 +28,8 @@
static DECLARE_RWSEM(triggers_list_lock);
static LIST_HEAD(trigger_list);
+ /* Used by LED Class */
+
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -45,9 +46,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
trigger_name[len - 1] = '\0';
if (!strcmp(trigger_name, "none")) {
- down_write(&led_cdev->trigger_lock);
- led_trigger_set(led_cdev, NULL);
- up_write(&led_cdev->trigger_lock);
+ led_trigger_remove(led_cdev);
return count;
}
@@ -66,7 +65,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
}
-
+EXPORT_SYMBOL_GPL(led_trigger_store);
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -96,48 +95,57 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
len += sprintf(len+buf, "\n");
return len;
}
-
-void led_trigger_event(struct led_trigger *trigger,
- enum led_brightness brightness)
-{
- struct list_head *entry;
-
- if (!trigger)
- return;
-
- read_lock(&trigger->leddev_list_lock);
- list_for_each(entry, &trigger->led_cdevs) {
- struct led_classdev *led_cdev;
-
- led_cdev = list_entry(entry, struct led_classdev, trig_list);
- led_set_brightness(led_cdev, brightness);
- }
- read_unlock(&trigger->leddev_list_lock);
-}
+EXPORT_SYMBOL_GPL(led_trigger_show);
/* Caller must ensure led_cdev->trigger_lock held */
-void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
+void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
{
unsigned long flags;
+ char *event = NULL;
+ char *envp[2];
+ const char *name;
+
+ name = trig ? trig->name : "none";
+ event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
/* Remove any existing trigger */
if (led_cdev->trigger) {
write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
list_del(&led_cdev->trig_list);
- write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
+ write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
+ flags);
+ cancel_work_sync(&led_cdev->set_brightness_work);
+ led_stop_software_blink(led_cdev);
if (led_cdev->trigger->deactivate)
led_cdev->trigger->deactivate(led_cdev);
+ led_cdev->trigger = NULL;
led_set_brightness(led_cdev, LED_OFF);
}
- if (trigger) {
- write_lock_irqsave(&trigger->leddev_list_lock, flags);
- list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
- write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
- if (trigger->activate)
- trigger->activate(led_cdev);
+ if (trig) {
+ write_lock_irqsave(&trig->leddev_list_lock, flags);
+ list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
+ write_unlock_irqrestore(&trig->leddev_list_lock, flags);
+ led_cdev->trigger = trig;
+ if (trig->activate)
+ trig->activate(led_cdev);
+ }
+
+ if (event) {
+ envp[0] = event;
+ envp[1] = NULL;
+ kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp);
+ kfree(event);
}
- led_cdev->trigger = trigger;
}
+EXPORT_SYMBOL_GPL(led_trigger_set);
+
+void led_trigger_remove(struct led_classdev *led_cdev)
+{
+ down_write(&led_cdev->trigger_lock);
+ led_trigger_set(led_cdev, NULL);
+ up_write(&led_cdev->trigger_lock);
+}
+EXPORT_SYMBOL_GPL(led_trigger_remove);
void led_trigger_set_default(struct led_classdev *led_cdev)
{
@@ -155,17 +163,41 @@ void led_trigger_set_default(struct led_classdev *led_cdev)
up_write(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
}
+EXPORT_SYMBOL_GPL(led_trigger_set_default);
+
+void led_trigger_rename_static(const char *name, struct led_trigger *trig)
+{
+ /* new name must be on a temporary string to prevent races */
+ BUG_ON(name == trig->name);
+
+ down_write(&triggers_list_lock);
+ /* this assumes that trig->name was originaly allocated to
+ * non constant storage */
+ strcpy((char *)trig->name, name);
+ up_write(&triggers_list_lock);
+}
+EXPORT_SYMBOL_GPL(led_trigger_rename_static);
+
+/* LED Trigger Interface */
-int led_trigger_register(struct led_trigger *trigger)
+int led_trigger_register(struct led_trigger *trig)
{
struct led_classdev *led_cdev;
+ struct led_trigger *_trig;
- rwlock_init(&trigger->leddev_list_lock);
- INIT_LIST_HEAD(&trigger->led_cdevs);
+ rwlock_init(&trig->leddev_list_lock);
+ INIT_LIST_HEAD(&trig->led_cdevs);
- /* Add to the list of led triggers */
down_write(&triggers_list_lock);
- list_add_tail(&trigger->next_trig, &trigger_list);
+ /* Make sure the trigger's name isn't already in use */
+ list_for_each_entry(_trig, &trigger_list, next_trig) {
+ if (!strcmp(_trig->name, trig->name)) {
+ up_write(&triggers_list_lock);
+ return -EEXIST;
+ }
+ }
+ /* Add to the list of led triggers */
+ list_add_tail(&trig->next_trig, &trigger_list);
up_write(&triggers_list_lock);
/* Register with any LEDs that have this as a default trigger */
@@ -173,76 +205,127 @@ int led_trigger_register(struct led_trigger *trigger)
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (!led_cdev->trigger && led_cdev->default_trigger &&
- !strcmp(led_cdev->default_trigger, trigger->name))
- led_trigger_set(led_cdev, trigger);
+ !strcmp(led_cdev->default_trigger, trig->name))
+ led_trigger_set(led_cdev, trig);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);
return 0;
}
+EXPORT_SYMBOL_GPL(led_trigger_register);
-void led_trigger_register_simple(const char *name, struct led_trigger **tp)
-{
- struct led_trigger *trigger;
- int err;
-
- trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
-
- if (trigger) {
- trigger->name = name;
- err = led_trigger_register(trigger);
- if (err < 0)
- printk(KERN_WARNING "LED trigger %s failed to register"
- " (%d)\n", name, err);
- } else
- printk(KERN_WARNING "LED trigger %s failed to register"
- " (no memory)\n", name);
-
- *tp = trigger;
-}
-
-void led_trigger_unregister(struct led_trigger *trigger)
+void led_trigger_unregister(struct led_trigger *trig)
{
struct led_classdev *led_cdev;
+ if (list_empty_careful(&trig->next_trig))
+ return;
+
/* Remove from the list of led triggers */
down_write(&triggers_list_lock);
- list_del(&trigger->next_trig);
+ list_del_init(&trig->next_trig);
up_write(&triggers_list_lock);
/* Remove anyone actively using this trigger */
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
- if (led_cdev->trigger == trigger)
+ if (led_cdev->trigger == trig)
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);
}
+EXPORT_SYMBOL_GPL(led_trigger_unregister);
+
+/* Simple LED Tigger Interface */
-void led_trigger_unregister_simple(struct led_trigger *trigger)
+void led_trigger_event(struct led_trigger *trig,
+ enum led_brightness brightness)
{
- if (trigger)
- led_trigger_unregister(trigger);
- kfree(trigger);
+ struct led_classdev *led_cdev;
+
+ if (!trig)
+ return;
+
+ read_lock(&trig->leddev_list_lock);
+ list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
+ led_set_brightness(led_cdev, brightness);
+ read_unlock(&trig->leddev_list_lock);
}
+EXPORT_SYMBOL_GPL(led_trigger_event);
-/* Used by LED Class */
-EXPORT_SYMBOL_GPL(led_trigger_set);
-EXPORT_SYMBOL_GPL(led_trigger_set_default);
-EXPORT_SYMBOL_GPL(led_trigger_show);
-EXPORT_SYMBOL_GPL(led_trigger_store);
+static void led_trigger_blink_setup(struct led_trigger *trig,
+ unsigned long *delay_on,
+ unsigned long *delay_off,
+ int oneshot,
+ int invert)
+{
+ struct led_classdev *led_cdev;
-/* LED Trigger Interface */
-EXPORT_SYMBOL_GPL(led_trigger_register);
-EXPORT_SYMBOL_GPL(led_trigger_unregister);
+ if (!trig)
+ return;
-/* Simple LED Tigger Interface */
+ read_lock(&trig->leddev_list_lock);
+ list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
+ if (oneshot)
+ led_blink_set_oneshot(led_cdev, delay_on, delay_off,
+ invert);
+ else
+ led_blink_set(led_cdev, delay_on, delay_off);
+ }
+ read_unlock(&trig->leddev_list_lock);
+}
+
+void led_trigger_blink(struct led_trigger *trig,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
+}
+EXPORT_SYMBOL_GPL(led_trigger_blink);
+
+void led_trigger_blink_oneshot(struct led_trigger *trig,
+ unsigned long *delay_on,
+ unsigned long *delay_off,
+ int invert)
+{
+ led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
+}
+EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
+
+void led_trigger_register_simple(const char *name, struct led_trigger **tp)
+{
+ struct led_trigger *trig;
+ int err;
+
+ trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+
+ if (trig) {
+ trig->name = name;
+ err = led_trigger_register(trig);
+ if (err < 0) {
+ kfree(trig);
+ trig = NULL;
+ pr_warn("LED trigger %s failed to register (%d)\n",
+ name, err);
+ }
+ } else {
+ pr_warn("LED trigger %s failed to register (no memory)\n",
+ name);
+ }
+ *tp = trig;
+}
EXPORT_SYMBOL_GPL(led_trigger_register_simple);
+
+void led_trigger_unregister_simple(struct led_trigger *trig)
+{
+ if (trig)
+ led_trigger_unregister(trig);
+ kfree(trig);
+}
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
-EXPORT_SYMBOL_GPL(led_trigger_event);
MODULE_AUTHOR("Richard Purdie");
MODULE_LICENSE("GPL");