.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * LED Triggers Core |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright 2005-2007 Openedhand Ltd. |
---|
5 | 6 | * |
---|
6 | 7 | * Author: Richard Purdie <rpurdie@openedhand.com> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or modify |
---|
9 | | - * it under the terms of the GNU General Public License version 2 as |
---|
10 | | - * published by the Free Software Foundation. |
---|
11 | | - * |
---|
12 | 8 | */ |
---|
13 | 9 | |
---|
14 | 10 | #include <linux/export.h> |
---|
.. | .. |
---|
20 | 16 | #include <linux/rwsem.h> |
---|
21 | 17 | #include <linux/leds.h> |
---|
22 | 18 | #include <linux/slab.h> |
---|
| 19 | +#include <linux/mm.h> |
---|
23 | 20 | #include "leds.h" |
---|
24 | 21 | |
---|
25 | 22 | /* |
---|
.. | .. |
---|
30 | 27 | |
---|
31 | 28 | /* Used by LED Class */ |
---|
32 | 29 | |
---|
33 | | -ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, |
---|
34 | | - const char *buf, size_t count) |
---|
| 30 | +static inline bool |
---|
| 31 | +trigger_relevant(struct led_classdev *led_cdev, struct led_trigger *trig) |
---|
35 | 32 | { |
---|
| 33 | + return !trig->trigger_type || trig->trigger_type == led_cdev->trigger_type; |
---|
| 34 | +} |
---|
| 35 | + |
---|
| 36 | +ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, |
---|
| 37 | + struct bin_attribute *bin_attr, char *buf, |
---|
| 38 | + loff_t pos, size_t count) |
---|
| 39 | +{ |
---|
| 40 | + struct device *dev = kobj_to_dev(kobj); |
---|
36 | 41 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
---|
37 | 42 | struct led_trigger *trig; |
---|
38 | 43 | int ret = count; |
---|
.. | .. |
---|
51 | 56 | |
---|
52 | 57 | down_read(&triggers_list_lock); |
---|
53 | 58 | list_for_each_entry(trig, &trigger_list, next_trig) { |
---|
54 | | - if (sysfs_streq(buf, trig->name)) { |
---|
| 59 | + if (sysfs_streq(buf, trig->name) && trigger_relevant(led_cdev, trig)) { |
---|
55 | 60 | down_write(&led_cdev->trigger_lock); |
---|
56 | 61 | led_trigger_set(led_cdev, trig); |
---|
57 | 62 | up_write(&led_cdev->trigger_lock); |
---|
.. | .. |
---|
68 | 73 | mutex_unlock(&led_cdev->led_access); |
---|
69 | 74 | return ret; |
---|
70 | 75 | } |
---|
71 | | -EXPORT_SYMBOL_GPL(led_trigger_store); |
---|
| 76 | +EXPORT_SYMBOL_GPL(led_trigger_write); |
---|
72 | 77 | |
---|
73 | | -ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, |
---|
74 | | - char *buf) |
---|
| 78 | +__printf(3, 4) |
---|
| 79 | +static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...) |
---|
75 | 80 | { |
---|
76 | | - struct led_classdev *led_cdev = dev_get_drvdata(dev); |
---|
| 81 | + va_list args; |
---|
| 82 | + int i; |
---|
| 83 | + |
---|
| 84 | + va_start(args, fmt); |
---|
| 85 | + if (size <= 0) |
---|
| 86 | + i = vsnprintf(NULL, 0, fmt, args); |
---|
| 87 | + else |
---|
| 88 | + i = vscnprintf(buf, size, fmt, args); |
---|
| 89 | + va_end(args); |
---|
| 90 | + |
---|
| 91 | + return i; |
---|
| 92 | +} |
---|
| 93 | + |
---|
| 94 | +static int led_trigger_format(char *buf, size_t size, |
---|
| 95 | + struct led_classdev *led_cdev) |
---|
| 96 | +{ |
---|
77 | 97 | struct led_trigger *trig; |
---|
78 | | - int len = 0; |
---|
| 98 | + int len = led_trigger_snprintf(buf, size, "%s", |
---|
| 99 | + led_cdev->trigger ? "none" : "[none]"); |
---|
| 100 | + |
---|
| 101 | + list_for_each_entry(trig, &trigger_list, next_trig) { |
---|
| 102 | + bool hit; |
---|
| 103 | + |
---|
| 104 | + if (!trigger_relevant(led_cdev, trig)) |
---|
| 105 | + continue; |
---|
| 106 | + |
---|
| 107 | + hit = led_cdev->trigger && !strcmp(led_cdev->trigger->name, trig->name); |
---|
| 108 | + |
---|
| 109 | + len += led_trigger_snprintf(buf + len, size - len, |
---|
| 110 | + " %s%s%s", hit ? "[" : "", |
---|
| 111 | + trig->name, hit ? "]" : ""); |
---|
| 112 | + } |
---|
| 113 | + |
---|
| 114 | + len += led_trigger_snprintf(buf + len, size - len, "\n"); |
---|
| 115 | + |
---|
| 116 | + return len; |
---|
| 117 | +} |
---|
| 118 | + |
---|
| 119 | +/* |
---|
| 120 | + * It was stupid to create 10000 cpu triggers, but we are stuck with it now. |
---|
| 121 | + * Don't make that mistake again. We work around it here by creating binary |
---|
| 122 | + * attribute, which is not limited by length. This is _not_ good design, do not |
---|
| 123 | + * copy it. |
---|
| 124 | + */ |
---|
| 125 | +ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, |
---|
| 126 | + struct bin_attribute *attr, char *buf, |
---|
| 127 | + loff_t pos, size_t count) |
---|
| 128 | +{ |
---|
| 129 | + struct device *dev = kobj_to_dev(kobj); |
---|
| 130 | + struct led_classdev *led_cdev = dev_get_drvdata(dev); |
---|
| 131 | + void *data; |
---|
| 132 | + int len; |
---|
79 | 133 | |
---|
80 | 134 | down_read(&triggers_list_lock); |
---|
81 | 135 | down_read(&led_cdev->trigger_lock); |
---|
82 | 136 | |
---|
83 | | - if (!led_cdev->trigger) |
---|
84 | | - len += scnprintf(buf+len, PAGE_SIZE - len, "[none] "); |
---|
85 | | - else |
---|
86 | | - len += scnprintf(buf+len, PAGE_SIZE - len, "none "); |
---|
87 | | - |
---|
88 | | - list_for_each_entry(trig, &trigger_list, next_trig) { |
---|
89 | | - if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, |
---|
90 | | - trig->name)) |
---|
91 | | - len += scnprintf(buf+len, PAGE_SIZE - len, "[%s] ", |
---|
92 | | - trig->name); |
---|
93 | | - else |
---|
94 | | - len += scnprintf(buf+len, PAGE_SIZE - len, "%s ", |
---|
95 | | - trig->name); |
---|
| 137 | + len = led_trigger_format(NULL, 0, led_cdev); |
---|
| 138 | + data = kvmalloc(len + 1, GFP_KERNEL); |
---|
| 139 | + if (!data) { |
---|
| 140 | + up_read(&led_cdev->trigger_lock); |
---|
| 141 | + up_read(&triggers_list_lock); |
---|
| 142 | + return -ENOMEM; |
---|
96 | 143 | } |
---|
| 144 | + len = led_trigger_format(data, len + 1, led_cdev); |
---|
| 145 | + |
---|
97 | 146 | up_read(&led_cdev->trigger_lock); |
---|
98 | 147 | up_read(&triggers_list_lock); |
---|
99 | 148 | |
---|
100 | | - len += scnprintf(len+buf, PAGE_SIZE - len, "\n"); |
---|
| 149 | + len = memory_read_from_buffer(buf, count, &pos, data, len); |
---|
| 150 | + |
---|
| 151 | + kvfree(data); |
---|
| 152 | + |
---|
101 | 153 | return len; |
---|
102 | 154 | } |
---|
103 | | -EXPORT_SYMBOL_GPL(led_trigger_show); |
---|
| 155 | +EXPORT_SYMBOL_GPL(led_trigger_read); |
---|
104 | 156 | |
---|
105 | 157 | /* Caller must ensure led_cdev->trigger_lock held */ |
---|
106 | 158 | int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) |
---|
.. | .. |
---|
201 | 253 | down_read(&triggers_list_lock); |
---|
202 | 254 | down_write(&led_cdev->trigger_lock); |
---|
203 | 255 | list_for_each_entry(trig, &trigger_list, next_trig) { |
---|
204 | | - if (!strcmp(led_cdev->default_trigger, trig->name)) |
---|
| 256 | + if (!strcmp(led_cdev->default_trigger, trig->name) && |
---|
| 257 | + trigger_relevant(led_cdev, trig)) { |
---|
| 258 | + led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER; |
---|
205 | 259 | led_trigger_set(led_cdev, trig); |
---|
| 260 | + break; |
---|
| 261 | + } |
---|
206 | 262 | } |
---|
207 | 263 | up_write(&led_cdev->trigger_lock); |
---|
208 | 264 | up_read(&triggers_list_lock); |
---|
.. | .. |
---|
235 | 291 | down_write(&triggers_list_lock); |
---|
236 | 292 | /* Make sure the trigger's name isn't already in use */ |
---|
237 | 293 | list_for_each_entry(_trig, &trigger_list, next_trig) { |
---|
238 | | - if (!strcmp(_trig->name, trig->name)) { |
---|
| 294 | + if (!strcmp(_trig->name, trig->name) && |
---|
| 295 | + (trig->trigger_type == _trig->trigger_type || |
---|
| 296 | + !trig->trigger_type || !_trig->trigger_type)) { |
---|
239 | 297 | up_write(&triggers_list_lock); |
---|
240 | 298 | return -EEXIST; |
---|
241 | 299 | } |
---|
.. | .. |
---|
249 | 307 | list_for_each_entry(led_cdev, &leds_list, node) { |
---|
250 | 308 | down_write(&led_cdev->trigger_lock); |
---|
251 | 309 | if (!led_cdev->trigger && led_cdev->default_trigger && |
---|
252 | | - !strcmp(led_cdev->default_trigger, trig->name)) |
---|
| 310 | + !strcmp(led_cdev->default_trigger, trig->name) && |
---|
| 311 | + trigger_relevant(led_cdev, trig)) { |
---|
| 312 | + led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER; |
---|
253 | 313 | led_trigger_set(led_cdev, trig); |
---|
| 314 | + } |
---|
254 | 315 | up_write(&led_cdev->trigger_lock); |
---|
255 | 316 | } |
---|
256 | 317 | up_read(&leds_list_lock); |
---|
.. | .. |
---|
311 | 372 | } |
---|
312 | 373 | EXPORT_SYMBOL_GPL(devm_led_trigger_register); |
---|
313 | 374 | |
---|
314 | | -/* Simple LED Tigger Interface */ |
---|
| 375 | +/* Simple LED Trigger Interface */ |
---|
315 | 376 | |
---|
316 | 377 | void led_trigger_event(struct led_trigger *trig, |
---|
317 | 378 | enum led_brightness brightness) |
---|