-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PATCH] LED: add LED trigger tupport
Add support for LED triggers to the LED subsystem. "Triggers" are events which change the state of an LED. Two kinds of trigger are available, simple ones which can be added to exising code with minimum disruption and complex ones for implementing new or more complex functionality. Signed-off-by: Richard Purdie <rpurdie@rpsys.net> Cc: Russell King <rmk@arm.linux.org.uk> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
- Loading branch information
Richard Purdie
authored and
Linus Torvalds
committed
Mar 31, 2006
1 parent
c72a1d6
commit c3bc995
Showing
6 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
/* | ||
* LED Triggers Core | ||
* | ||
* Copyright 2005-2006 Openedhand Ltd. | ||
* | ||
* Author: Richard Purdie <rpurdie@openedhand.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
*/ | ||
|
||
#include <linux/config.h> | ||
#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/leds.h> | ||
#include "leds.h" | ||
|
||
/* | ||
* Nests outside led_cdev->trigger_lock | ||
*/ | ||
static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED; | ||
static LIST_HEAD(trigger_list); | ||
|
||
ssize_t led_trigger_store(struct class_device *dev, const char *buf, | ||
size_t count) | ||
{ | ||
struct led_classdev *led_cdev = class_get_devdata(dev); | ||
char trigger_name[TRIG_NAME_MAX]; | ||
struct led_trigger *trig; | ||
size_t len; | ||
|
||
trigger_name[sizeof(trigger_name) - 1] = '\0'; | ||
strncpy(trigger_name, buf, sizeof(trigger_name) - 1); | ||
len = strlen(trigger_name); | ||
|
||
if (len && trigger_name[len - 1] == '\n') | ||
trigger_name[len - 1] = '\0'; | ||
|
||
if (!strcmp(trigger_name, "none")) { | ||
write_lock(&led_cdev->trigger_lock); | ||
led_trigger_set(led_cdev, NULL); | ||
write_unlock(&led_cdev->trigger_lock); | ||
return count; | ||
} | ||
|
||
read_lock(&triggers_list_lock); | ||
list_for_each_entry(trig, &trigger_list, next_trig) { | ||
if (!strcmp(trigger_name, trig->name)) { | ||
write_lock(&led_cdev->trigger_lock); | ||
led_trigger_set(led_cdev, trig); | ||
write_unlock(&led_cdev->trigger_lock); | ||
|
||
read_unlock(&triggers_list_lock); | ||
return count; | ||
} | ||
} | ||
read_unlock(&triggers_list_lock); | ||
|
||
return -EINVAL; | ||
} | ||
|
||
|
||
ssize_t led_trigger_show(struct class_device *dev, char *buf) | ||
{ | ||
struct led_classdev *led_cdev = class_get_devdata(dev); | ||
struct led_trigger *trig; | ||
int len = 0; | ||
|
||
read_lock(&triggers_list_lock); | ||
read_lock(&led_cdev->trigger_lock); | ||
|
||
if (!led_cdev->trigger) | ||
len += sprintf(buf+len, "[none] "); | ||
else | ||
len += sprintf(buf+len, "none "); | ||
|
||
list_for_each_entry(trig, &trigger_list, next_trig) { | ||
if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, | ||
trig->name)) | ||
len += sprintf(buf+len, "[%s] ", trig->name); | ||
else | ||
len += sprintf(buf+len, "%s ", trig->name); | ||
} | ||
read_unlock(&led_cdev->trigger_lock); | ||
read_unlock(&triggers_list_lock); | ||
|
||
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); | ||
} | ||
|
||
/* Caller must ensure led_cdev->trigger_lock held */ | ||
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) | ||
{ | ||
unsigned long flags; | ||
|
||
/* 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); | ||
if (led_cdev->trigger->deactivate) | ||
led_cdev->trigger->deactivate(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); | ||
if (trigger->activate) | ||
trigger->activate(led_cdev); | ||
} | ||
led_cdev->trigger = trigger; | ||
} | ||
|
||
void led_trigger_set_default(struct led_classdev *led_cdev) | ||
{ | ||
struct led_trigger *trig; | ||
|
||
if (!led_cdev->default_trigger) | ||
return; | ||
|
||
read_lock(&triggers_list_lock); | ||
write_lock(&led_cdev->trigger_lock); | ||
list_for_each_entry(trig, &trigger_list, next_trig) { | ||
if (!strcmp(led_cdev->default_trigger, trig->name)) | ||
led_trigger_set(led_cdev, trig); | ||
} | ||
write_unlock(&led_cdev->trigger_lock); | ||
read_unlock(&triggers_list_lock); | ||
} | ||
|
||
int led_trigger_register(struct led_trigger *trigger) | ||
{ | ||
struct led_classdev *led_cdev; | ||
|
||
rwlock_init(&trigger->leddev_list_lock); | ||
INIT_LIST_HEAD(&trigger->led_cdevs); | ||
|
||
/* Add to the list of led triggers */ | ||
write_lock(&triggers_list_lock); | ||
list_add_tail(&trigger->next_trig, &trigger_list); | ||
write_unlock(&triggers_list_lock); | ||
|
||
/* Register with any LEDs that have this as a default trigger */ | ||
read_lock(&leds_list_lock); | ||
list_for_each_entry(led_cdev, &leds_list, node) { | ||
write_lock(&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); | ||
write_unlock(&led_cdev->trigger_lock); | ||
} | ||
read_unlock(&leds_list_lock); | ||
|
||
return 0; | ||
} | ||
|
||
void led_trigger_register_simple(const char *name, struct led_trigger **tp) | ||
{ | ||
struct led_trigger *trigger; | ||
|
||
trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); | ||
|
||
if (trigger) { | ||
trigger->name = name; | ||
led_trigger_register(trigger); | ||
} | ||
*tp = trigger; | ||
} | ||
|
||
void led_trigger_unregister(struct led_trigger *trigger) | ||
{ | ||
struct led_classdev *led_cdev; | ||
|
||
/* Remove from the list of led triggers */ | ||
write_lock(&triggers_list_lock); | ||
list_del(&trigger->next_trig); | ||
write_unlock(&triggers_list_lock); | ||
|
||
/* Remove anyone actively using this trigger */ | ||
read_lock(&leds_list_lock); | ||
list_for_each_entry(led_cdev, &leds_list, node) { | ||
write_lock(&led_cdev->trigger_lock); | ||
if (led_cdev->trigger == trigger) | ||
led_trigger_set(led_cdev, NULL); | ||
write_unlock(&led_cdev->trigger_lock); | ||
} | ||
read_unlock(&leds_list_lock); | ||
} | ||
|
||
void led_trigger_unregister_simple(struct led_trigger *trigger) | ||
{ | ||
led_trigger_unregister(trigger); | ||
kfree(trigger); | ||
} | ||
|
||
/* 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); | ||
|
||
/* LED Trigger Interface */ | ||
EXPORT_SYMBOL_GPL(led_trigger_register); | ||
EXPORT_SYMBOL_GPL(led_trigger_unregister); | ||
|
||
/* Simple LED Tigger Interface */ | ||
EXPORT_SYMBOL_GPL(led_trigger_register_simple); | ||
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); | ||
EXPORT_SYMBOL_GPL(led_trigger_event); | ||
|
||
MODULE_AUTHOR("Richard Purdie"); | ||
MODULE_LICENSE("GPL"); | ||
MODULE_DESCRIPTION("LED Triggers Core"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters