leds: trigger: add support for LED-private device triggers
Some LED controllers may come with an internal HW triggering mechanism for the LED and the ability to switch between SW control and the internal HW control. This includes most PHYs, various ethernet switches, the Turris Omnia LED controller or AXP20X PMIC. This adds support for registering such triggers. This code is based on work by Pavel Machek <pavel@ucw.cz> and Ondřej Jirman <megous@megous.com>. Signed-off-by: Marek Behún <marek.behun@nic.cz> Acked-by: Jacek Anaszewski <jacek.anaszewski@gmail.com> Signed-off-by: Pavel Machek <pavel@ucw.cz>
This commit is contained in:
parent
00253ec2c9
commit
93690cdf30
@ -27,6 +27,12 @@ LIST_HEAD(trigger_list);
|
|||||||
|
|
||||||
/* Used by LED Class */
|
/* Used by LED Class */
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
trigger_relevant(struct led_classdev *led_cdev, struct led_trigger *trig)
|
||||||
|
{
|
||||||
|
return !trig->trigger_type || trig->trigger_type == led_cdev->trigger_type;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
|
ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
|
||||||
struct bin_attribute *bin_attr, char *buf,
|
struct bin_attribute *bin_attr, char *buf,
|
||||||
loff_t pos, size_t count)
|
loff_t pos, size_t count)
|
||||||
@ -50,7 +56,7 @@ ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
|
|||||||
|
|
||||||
down_read(&triggers_list_lock);
|
down_read(&triggers_list_lock);
|
||||||
list_for_each_entry(trig, &trigger_list, next_trig) {
|
list_for_each_entry(trig, &trigger_list, next_trig) {
|
||||||
if (sysfs_streq(buf, trig->name)) {
|
if (sysfs_streq(buf, trig->name) && trigger_relevant(led_cdev, trig)) {
|
||||||
down_write(&led_cdev->trigger_lock);
|
down_write(&led_cdev->trigger_lock);
|
||||||
led_trigger_set(led_cdev, trig);
|
led_trigger_set(led_cdev, trig);
|
||||||
up_write(&led_cdev->trigger_lock);
|
up_write(&led_cdev->trigger_lock);
|
||||||
@ -93,8 +99,12 @@ static int led_trigger_format(char *buf, size_t size,
|
|||||||
led_cdev->trigger ? "none" : "[none]");
|
led_cdev->trigger ? "none" : "[none]");
|
||||||
|
|
||||||
list_for_each_entry(trig, &trigger_list, next_trig) {
|
list_for_each_entry(trig, &trigger_list, next_trig) {
|
||||||
bool hit = led_cdev->trigger &&
|
bool hit;
|
||||||
!strcmp(led_cdev->trigger->name, trig->name);
|
|
||||||
|
if (!trigger_relevant(led_cdev, trig))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hit = led_cdev->trigger && !strcmp(led_cdev->trigger->name, trig->name);
|
||||||
|
|
||||||
len += led_trigger_snprintf(buf + len, size - len,
|
len += led_trigger_snprintf(buf + len, size - len,
|
||||||
" %s%s%s", hit ? "[" : "",
|
" %s%s%s", hit ? "[" : "",
|
||||||
@ -243,7 +253,8 @@ void led_trigger_set_default(struct led_classdev *led_cdev)
|
|||||||
down_read(&triggers_list_lock);
|
down_read(&triggers_list_lock);
|
||||||
down_write(&led_cdev->trigger_lock);
|
down_write(&led_cdev->trigger_lock);
|
||||||
list_for_each_entry(trig, &trigger_list, next_trig) {
|
list_for_each_entry(trig, &trigger_list, next_trig) {
|
||||||
if (!strcmp(led_cdev->default_trigger, trig->name)) {
|
if (!strcmp(led_cdev->default_trigger, trig->name) &&
|
||||||
|
trigger_relevant(led_cdev, trig)) {
|
||||||
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
|
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
|
||||||
led_trigger_set(led_cdev, trig);
|
led_trigger_set(led_cdev, trig);
|
||||||
break;
|
break;
|
||||||
@ -280,7 +291,9 @@ int led_trigger_register(struct led_trigger *trig)
|
|||||||
down_write(&triggers_list_lock);
|
down_write(&triggers_list_lock);
|
||||||
/* Make sure the trigger's name isn't already in use */
|
/* Make sure the trigger's name isn't already in use */
|
||||||
list_for_each_entry(_trig, &trigger_list, next_trig) {
|
list_for_each_entry(_trig, &trigger_list, next_trig) {
|
||||||
if (!strcmp(_trig->name, trig->name)) {
|
if (!strcmp(_trig->name, trig->name) &&
|
||||||
|
(trig->trigger_type == _trig->trigger_type ||
|
||||||
|
!trig->trigger_type || !_trig->trigger_type)) {
|
||||||
up_write(&triggers_list_lock);
|
up_write(&triggers_list_lock);
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
@ -294,7 +307,8 @@ int led_trigger_register(struct led_trigger *trig)
|
|||||||
list_for_each_entry(led_cdev, &leds_list, node) {
|
list_for_each_entry(led_cdev, &leds_list, node) {
|
||||||
down_write(&led_cdev->trigger_lock);
|
down_write(&led_cdev->trigger_lock);
|
||||||
if (!led_cdev->trigger && led_cdev->default_trigger &&
|
if (!led_cdev->trigger && led_cdev->default_trigger &&
|
||||||
!strcmp(led_cdev->default_trigger, trig->name)) {
|
!strcmp(led_cdev->default_trigger, trig->name) &&
|
||||||
|
trigger_relevant(led_cdev, trig)) {
|
||||||
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
|
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
|
||||||
led_trigger_set(led_cdev, trig);
|
led_trigger_set(led_cdev, trig);
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,10 @@ struct led_init_data {
|
|||||||
bool devname_mandatory;
|
bool devname_mandatory;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct led_hw_trigger_type {
|
||||||
|
int dummy;
|
||||||
|
};
|
||||||
|
|
||||||
struct led_classdev {
|
struct led_classdev {
|
||||||
const char *name;
|
const char *name;
|
||||||
enum led_brightness brightness;
|
enum led_brightness brightness;
|
||||||
@ -141,6 +145,9 @@ struct led_classdev {
|
|||||||
void *trigger_data;
|
void *trigger_data;
|
||||||
/* true if activated - deactivate routine uses it to do cleanup */
|
/* true if activated - deactivate routine uses it to do cleanup */
|
||||||
bool activated;
|
bool activated;
|
||||||
|
|
||||||
|
/* LEDs that have private triggers have this set */
|
||||||
|
struct led_hw_trigger_type *trigger_type;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
|
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
|
||||||
@ -345,6 +352,9 @@ struct led_trigger {
|
|||||||
int (*activate)(struct led_classdev *led_cdev);
|
int (*activate)(struct led_classdev *led_cdev);
|
||||||
void (*deactivate)(struct led_classdev *led_cdev);
|
void (*deactivate)(struct led_classdev *led_cdev);
|
||||||
|
|
||||||
|
/* LED-private triggers have this set */
|
||||||
|
struct led_hw_trigger_type *trigger_type;
|
||||||
|
|
||||||
/* LEDs under control by this trigger (for simple triggers) */
|
/* LEDs under control by this trigger (for simple triggers) */
|
||||||
rwlock_t leddev_list_lock;
|
rwlock_t leddev_list_lock;
|
||||||
struct list_head led_cdevs;
|
struct list_head led_cdevs;
|
||||||
|
Loading…
Reference in New Issue
Block a user