diff options
Diffstat (limited to 'drivers/leds/led-triggers.c')
| -rw-r--r-- | drivers/leds/led-triggers.c | 167 | 
1 files changed, 117 insertions, 50 deletions
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index c41eb6180c9..c3734f10fdd 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -13,11 +13,9 @@  #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> @@ -100,9 +98,15 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,  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) { @@ -110,18 +114,27 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)  		list_del(&led_cdev->trig_list);  		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_brightness_set(led_cdev, LED_OFF); +		led_set_brightness(led_cdev, LED_OFF); +	} +	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 (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); -		led_cdev->trigger = trigger; -		if (trigger->activate) -			trigger->activate(led_cdev); + +	if (event) { +		envp[0] = event; +		envp[1] = NULL; +		kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp); +		kfree(event);  	}  }  EXPORT_SYMBOL_GPL(led_trigger_set); @@ -152,26 +165,39 @@ void led_trigger_set_default(struct led_classdev *led_cdev)  }  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; +	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);  	down_write(&triggers_list_lock);  	/* Make sure the trigger's name isn't already in use */ -	list_for_each_entry(trig, &trigger_list, next_trig) { -		if (!strcmp(trig->name, trigger->name)) { +	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(&trigger->next_trig, &trigger_list); +	list_add_tail(&trig->next_trig, &trigger_list);  	up_write(&triggers_list_lock);  	/* Register with any LEDs that have this as a default trigger */ @@ -179,8 +205,8 @@ 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); @@ -189,20 +215,23 @@ int led_trigger_register(struct led_trigger *trigger)  }  EXPORT_SYMBOL_GPL(led_trigger_register); -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);  	} @@ -212,51 +241,89 @@ EXPORT_SYMBOL_GPL(led_trigger_unregister);  /* Simple LED Tigger Interface */ -void led_trigger_event(struct led_trigger *trigger, +void led_trigger_event(struct led_trigger *trig,  			enum led_brightness brightness)  { -	struct list_head *entry; +	struct led_classdev *led_cdev; -	if (!trigger) +	if (!trig)  		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); +	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(&trigger->leddev_list_lock); +	read_unlock(&trig->leddev_list_lock);  }  EXPORT_SYMBOL_GPL(led_trigger_event); +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; + +	if (!trig) +		return; + +	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 *trigger; +	struct led_trigger *trig;  	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); +	trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); -	*tp = trigger; +	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 *trigger) +void led_trigger_unregister_simple(struct led_trigger *trig)  { -	if (trigger) -		led_trigger_unregister(trigger); -	kfree(trigger); +	if (trig) +		led_trigger_unregister(trig); +	kfree(trig);  }  EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);  | 
