Skip to content

Commit

Permalink
leds: trigger: add support for LED-private device triggers
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Marek Behún authored and Pavel Machek committed Jul 22, 2020
1 parent 00253ec commit 93690cd
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 6 deletions.
26 changes: 20 additions & 6 deletions drivers/leds/led-triggers.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ LIST_HEAD(trigger_list);

/* 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,
struct bin_attribute *bin_attr, char *buf,
loff_t pos, size_t count)
Expand All @@ -50,7 +56,7 @@ ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,

down_read(&triggers_list_lock);
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);
led_trigger_set(led_cdev, trig);
up_write(&led_cdev->trigger_lock);
Expand Down Expand Up @@ -93,8 +99,12 @@ static int led_trigger_format(char *buf, size_t size,
led_cdev->trigger ? "none" : "[none]");

list_for_each_entry(trig, &trigger_list, next_trig) {
bool hit = led_cdev->trigger &&
!strcmp(led_cdev->trigger->name, trig->name);
bool hit;

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,
" %s%s%s", hit ? "[" : "",
Expand Down Expand Up @@ -243,7 +253,8 @@ void led_trigger_set_default(struct led_classdev *led_cdev)
down_read(&triggers_list_lock);
down_write(&led_cdev->trigger_lock);
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_trigger_set(led_cdev, trig);
break;
Expand Down Expand Up @@ -280,7 +291,9 @@ int led_trigger_register(struct led_trigger *trig)
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, 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);
return -EEXIST;
}
Expand All @@ -294,7 +307,8 @@ int led_trigger_register(struct led_trigger *trig)
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, trig->name)) {
!strcmp(led_cdev->default_trigger, trig->name) &&
trigger_relevant(led_cdev, trig)) {
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
led_trigger_set(led_cdev, trig);
}
Expand Down
10 changes: 10 additions & 0 deletions include/linux/leds.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ struct led_init_data {
bool devname_mandatory;
};

struct led_hw_trigger_type {
int dummy;
};

struct led_classdev {
const char *name;
enum led_brightness brightness;
Expand Down Expand Up @@ -141,6 +145,9 @@ struct led_classdev {
void *trigger_data;
/* true if activated - deactivate routine uses it to do cleanup */
bool activated;

/* LEDs that have private triggers have this set */
struct led_hw_trigger_type *trigger_type;
#endif

#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
Expand Down Expand Up @@ -345,6 +352,9 @@ struct led_trigger {
int (*activate)(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) */
rwlock_t leddev_list_lock;
struct list_head led_cdevs;
Expand Down

0 comments on commit 93690cd

Please sign in to comment.