diff options
Diffstat (limited to 'drivers/leds/led-triggers.c')
-rw-r--r-- | drivers/leds/led-triggers.c | 90 |
1 files changed, 68 insertions, 22 deletions
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 23963e5cb5d6..79e30d2cb7a5 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -16,6 +16,7 @@ #include <linux/rwsem.h> #include <linux/leds.h> #include <linux/slab.h> +#include <linux/mm.h> #include "leds.h" /* @@ -26,9 +27,11 @@ 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) +ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count) { + struct device *dev = kobj_to_dev(kobj); struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_trigger *trig; int ret = count; @@ -64,39 +67,82 @@ unlock: mutex_unlock(&led_cdev->led_access); return ret; } -EXPORT_SYMBOL_GPL(led_trigger_store); +EXPORT_SYMBOL_GPL(led_trigger_write); -ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, - char *buf) +__printf(3, 4) +static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + if (size <= 0) + i = vsnprintf(NULL, 0, fmt, args); + else + i = vscnprintf(buf, size, fmt, args); + va_end(args); + + return i; +} + +static int led_trigger_format(char *buf, size_t size, + struct led_classdev *led_cdev) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_trigger *trig; - int len = 0; + int len = led_trigger_snprintf(buf, size, "%s", + 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); + + len += led_trigger_snprintf(buf + len, size - len, + " %s%s%s", hit ? "[" : "", + trig->name, hit ? "]" : ""); + } + + len += led_trigger_snprintf(buf + len, size - len, "\n"); + + return len; +} + +/* + * It was stupid to create 10000 cpu triggers, but we are stuck with it now. + * Don't make that mistake again. We work around it here by creating binary + * attribute, which is not limited by length. This is _not_ good design, do not + * copy it. + */ +ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t pos, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct led_classdev *led_cdev = dev_get_drvdata(dev); + void *data; + int len; down_read(&triggers_list_lock); down_read(&led_cdev->trigger_lock); - if (!led_cdev->trigger) - len += scnprintf(buf+len, PAGE_SIZE - len, "[none] "); - else - len += scnprintf(buf+len, PAGE_SIZE - len, "none "); - - list_for_each_entry(trig, &trigger_list, next_trig) { - if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, - trig->name)) - len += scnprintf(buf+len, PAGE_SIZE - len, "[%s] ", - trig->name); - else - len += scnprintf(buf+len, PAGE_SIZE - len, "%s ", - trig->name); + len = led_trigger_format(NULL, 0, led_cdev); + data = kvmalloc(len + 1, GFP_KERNEL); + if (!data) { + up_read(&led_cdev->trigger_lock); + up_read(&triggers_list_lock); + return -ENOMEM; } + len = led_trigger_format(data, len + 1, led_cdev); + up_read(&led_cdev->trigger_lock); up_read(&triggers_list_lock); - len += scnprintf(len+buf, PAGE_SIZE - len, "\n"); + len = memory_read_from_buffer(buf, count, &pos, data, len); + + kvfree(data); + return len; } -EXPORT_SYMBOL_GPL(led_trigger_show); +EXPORT_SYMBOL_GPL(led_trigger_read); /* Caller must ensure led_cdev->trigger_lock held */ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) |