led-triggers.c (11394B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * LED Triggers Core 4 * 5 * Copyright 2005-2007 Openedhand Ltd. 6 * 7 * Author: Richard Purdie <rpurdie@openedhand.com> 8 */ 9 10#include <linux/export.h> 11#include <linux/kernel.h> 12#include <linux/list.h> 13#include <linux/spinlock.h> 14#include <linux/device.h> 15#include <linux/timer.h> 16#include <linux/rwsem.h> 17#include <linux/leds.h> 18#include <linux/slab.h> 19#include <linux/mm.h> 20#include "leds.h" 21 22/* 23 * Nests outside led_cdev->trigger_lock 24 */ 25static DECLARE_RWSEM(triggers_list_lock); 26LIST_HEAD(trigger_list); 27 28 /* Used by LED Class */ 29 30static inline bool 31trigger_relevant(struct led_classdev *led_cdev, struct led_trigger *trig) 32{ 33 return !trig->trigger_type || trig->trigger_type == led_cdev->trigger_type; 34} 35 36ssize_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); 41 struct led_classdev *led_cdev = dev_get_drvdata(dev); 42 struct led_trigger *trig; 43 int ret = count; 44 45 mutex_lock(&led_cdev->led_access); 46 47 if (led_sysfs_is_disabled(led_cdev)) { 48 ret = -EBUSY; 49 goto unlock; 50 } 51 52 if (sysfs_streq(buf, "none")) { 53 led_trigger_remove(led_cdev); 54 goto unlock; 55 } 56 57 down_read(&triggers_list_lock); 58 list_for_each_entry(trig, &trigger_list, next_trig) { 59 if (sysfs_streq(buf, trig->name) && trigger_relevant(led_cdev, trig)) { 60 down_write(&led_cdev->trigger_lock); 61 led_trigger_set(led_cdev, trig); 62 up_write(&led_cdev->trigger_lock); 63 64 up_read(&triggers_list_lock); 65 goto unlock; 66 } 67 } 68 /* we come here only if buf matches no trigger */ 69 ret = -EINVAL; 70 up_read(&triggers_list_lock); 71 72unlock: 73 mutex_unlock(&led_cdev->led_access); 74 return ret; 75} 76EXPORT_SYMBOL_GPL(led_trigger_write); 77 78__printf(3, 4) 79static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...) 80{ 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 94static int led_trigger_format(char *buf, size_t size, 95 struct led_classdev *led_cdev) 96{ 97 struct led_trigger *trig; 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 */ 125ssize_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; 133 134 down_read(&triggers_list_lock); 135 down_read(&led_cdev->trigger_lock); 136 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; 143 } 144 len = led_trigger_format(data, len + 1, led_cdev); 145 146 up_read(&led_cdev->trigger_lock); 147 up_read(&triggers_list_lock); 148 149 len = memory_read_from_buffer(buf, count, &pos, data, len); 150 151 kvfree(data); 152 153 return len; 154} 155EXPORT_SYMBOL_GPL(led_trigger_read); 156 157/* Caller must ensure led_cdev->trigger_lock held */ 158int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) 159{ 160 char *event = NULL; 161 char *envp[2]; 162 const char *name; 163 int ret; 164 165 if (!led_cdev->trigger && !trig) 166 return 0; 167 168 name = trig ? trig->name : "none"; 169 event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name); 170 171 /* Remove any existing trigger */ 172 if (led_cdev->trigger) { 173 spin_lock(&led_cdev->trigger->leddev_list_lock); 174 list_del_rcu(&led_cdev->trig_list); 175 spin_unlock(&led_cdev->trigger->leddev_list_lock); 176 177 /* ensure it's no longer visible on the led_cdevs list */ 178 synchronize_rcu(); 179 180 cancel_work_sync(&led_cdev->set_brightness_work); 181 led_stop_software_blink(led_cdev); 182 if (led_cdev->trigger->deactivate) 183 led_cdev->trigger->deactivate(led_cdev); 184 device_remove_groups(led_cdev->dev, led_cdev->trigger->groups); 185 led_cdev->trigger = NULL; 186 led_cdev->trigger_data = NULL; 187 led_cdev->activated = false; 188 led_set_brightness(led_cdev, LED_OFF); 189 } 190 if (trig) { 191 spin_lock(&trig->leddev_list_lock); 192 list_add_tail_rcu(&led_cdev->trig_list, &trig->led_cdevs); 193 spin_unlock(&trig->leddev_list_lock); 194 led_cdev->trigger = trig; 195 196 if (trig->activate) 197 ret = trig->activate(led_cdev); 198 else 199 ret = 0; 200 201 if (ret) 202 goto err_activate; 203 204 ret = device_add_groups(led_cdev->dev, trig->groups); 205 if (ret) { 206 dev_err(led_cdev->dev, "Failed to add trigger attributes\n"); 207 goto err_add_groups; 208 } 209 } 210 211 if (event) { 212 envp[0] = event; 213 envp[1] = NULL; 214 if (kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp)) 215 dev_err(led_cdev->dev, 216 "%s: Error sending uevent\n", __func__); 217 kfree(event); 218 } 219 220 return 0; 221 222err_add_groups: 223 224 if (trig->deactivate) 225 trig->deactivate(led_cdev); 226err_activate: 227 228 spin_lock(&led_cdev->trigger->leddev_list_lock); 229 list_del_rcu(&led_cdev->trig_list); 230 spin_unlock(&led_cdev->trigger->leddev_list_lock); 231 synchronize_rcu(); 232 led_cdev->trigger = NULL; 233 led_cdev->trigger_data = NULL; 234 led_set_brightness(led_cdev, LED_OFF); 235 kfree(event); 236 237 return ret; 238} 239EXPORT_SYMBOL_GPL(led_trigger_set); 240 241void led_trigger_remove(struct led_classdev *led_cdev) 242{ 243 down_write(&led_cdev->trigger_lock); 244 led_trigger_set(led_cdev, NULL); 245 up_write(&led_cdev->trigger_lock); 246} 247EXPORT_SYMBOL_GPL(led_trigger_remove); 248 249void led_trigger_set_default(struct led_classdev *led_cdev) 250{ 251 struct led_trigger *trig; 252 253 if (!led_cdev->default_trigger) 254 return; 255 256 down_read(&triggers_list_lock); 257 down_write(&led_cdev->trigger_lock); 258 list_for_each_entry(trig, &trigger_list, next_trig) { 259 if (!strcmp(led_cdev->default_trigger, trig->name) && 260 trigger_relevant(led_cdev, trig)) { 261 led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER; 262 led_trigger_set(led_cdev, trig); 263 break; 264 } 265 } 266 up_write(&led_cdev->trigger_lock); 267 up_read(&triggers_list_lock); 268} 269EXPORT_SYMBOL_GPL(led_trigger_set_default); 270 271void led_trigger_rename_static(const char *name, struct led_trigger *trig) 272{ 273 /* new name must be on a temporary string to prevent races */ 274 BUG_ON(name == trig->name); 275 276 down_write(&triggers_list_lock); 277 /* this assumes that trig->name was originaly allocated to 278 * non constant storage */ 279 strcpy((char *)trig->name, name); 280 up_write(&triggers_list_lock); 281} 282EXPORT_SYMBOL_GPL(led_trigger_rename_static); 283 284/* LED Trigger Interface */ 285 286int led_trigger_register(struct led_trigger *trig) 287{ 288 struct led_classdev *led_cdev; 289 struct led_trigger *_trig; 290 291 spin_lock_init(&trig->leddev_list_lock); 292 INIT_LIST_HEAD(&trig->led_cdevs); 293 294 down_write(&triggers_list_lock); 295 /* Make sure the trigger's name isn't already in use */ 296 list_for_each_entry(_trig, &trigger_list, next_trig) { 297 if (!strcmp(_trig->name, trig->name) && 298 (trig->trigger_type == _trig->trigger_type || 299 !trig->trigger_type || !_trig->trigger_type)) { 300 up_write(&triggers_list_lock); 301 return -EEXIST; 302 } 303 } 304 /* Add to the list of led triggers */ 305 list_add_tail(&trig->next_trig, &trigger_list); 306 up_write(&triggers_list_lock); 307 308 /* Register with any LEDs that have this as a default trigger */ 309 down_read(&leds_list_lock); 310 list_for_each_entry(led_cdev, &leds_list, node) { 311 down_write(&led_cdev->trigger_lock); 312 if (!led_cdev->trigger && led_cdev->default_trigger && 313 !strcmp(led_cdev->default_trigger, trig->name) && 314 trigger_relevant(led_cdev, trig)) { 315 led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER; 316 led_trigger_set(led_cdev, trig); 317 } 318 up_write(&led_cdev->trigger_lock); 319 } 320 up_read(&leds_list_lock); 321 322 return 0; 323} 324EXPORT_SYMBOL_GPL(led_trigger_register); 325 326void led_trigger_unregister(struct led_trigger *trig) 327{ 328 struct led_classdev *led_cdev; 329 330 if (list_empty_careful(&trig->next_trig)) 331 return; 332 333 /* Remove from the list of led triggers */ 334 down_write(&triggers_list_lock); 335 list_del_init(&trig->next_trig); 336 up_write(&triggers_list_lock); 337 338 /* Remove anyone actively using this trigger */ 339 down_read(&leds_list_lock); 340 list_for_each_entry(led_cdev, &leds_list, node) { 341 down_write(&led_cdev->trigger_lock); 342 if (led_cdev->trigger == trig) 343 led_trigger_set(led_cdev, NULL); 344 up_write(&led_cdev->trigger_lock); 345 } 346 up_read(&leds_list_lock); 347} 348EXPORT_SYMBOL_GPL(led_trigger_unregister); 349 350static void devm_led_trigger_release(struct device *dev, void *res) 351{ 352 led_trigger_unregister(*(struct led_trigger **)res); 353} 354 355int devm_led_trigger_register(struct device *dev, 356 struct led_trigger *trig) 357{ 358 struct led_trigger **dr; 359 int rc; 360 361 dr = devres_alloc(devm_led_trigger_release, sizeof(*dr), 362 GFP_KERNEL); 363 if (!dr) 364 return -ENOMEM; 365 366 *dr = trig; 367 368 rc = led_trigger_register(trig); 369 if (rc) 370 devres_free(dr); 371 else 372 devres_add(dev, dr); 373 374 return rc; 375} 376EXPORT_SYMBOL_GPL(devm_led_trigger_register); 377 378/* Simple LED Trigger Interface */ 379 380void led_trigger_event(struct led_trigger *trig, 381 enum led_brightness brightness) 382{ 383 struct led_classdev *led_cdev; 384 385 if (!trig) 386 return; 387 388 rcu_read_lock(); 389 list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) 390 led_set_brightness(led_cdev, brightness); 391 rcu_read_unlock(); 392} 393EXPORT_SYMBOL_GPL(led_trigger_event); 394 395static void led_trigger_blink_setup(struct led_trigger *trig, 396 unsigned long *delay_on, 397 unsigned long *delay_off, 398 int oneshot, 399 int invert) 400{ 401 struct led_classdev *led_cdev; 402 403 if (!trig) 404 return; 405 406 rcu_read_lock(); 407 list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) { 408 if (oneshot) 409 led_blink_set_oneshot(led_cdev, delay_on, delay_off, 410 invert); 411 else 412 led_blink_set(led_cdev, delay_on, delay_off); 413 } 414 rcu_read_unlock(); 415} 416 417void led_trigger_blink(struct led_trigger *trig, 418 unsigned long *delay_on, 419 unsigned long *delay_off) 420{ 421 led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0); 422} 423EXPORT_SYMBOL_GPL(led_trigger_blink); 424 425void led_trigger_blink_oneshot(struct led_trigger *trig, 426 unsigned long *delay_on, 427 unsigned long *delay_off, 428 int invert) 429{ 430 led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert); 431} 432EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot); 433 434void led_trigger_register_simple(const char *name, struct led_trigger **tp) 435{ 436 struct led_trigger *trig; 437 int err; 438 439 trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 440 441 if (trig) { 442 trig->name = name; 443 err = led_trigger_register(trig); 444 if (err < 0) { 445 kfree(trig); 446 trig = NULL; 447 pr_warn("LED trigger %s failed to register (%d)\n", 448 name, err); 449 } 450 } else { 451 pr_warn("LED trigger %s failed to register (no memory)\n", 452 name); 453 } 454 *tp = trig; 455} 456EXPORT_SYMBOL_GPL(led_trigger_register_simple); 457 458void led_trigger_unregister_simple(struct led_trigger *trig) 459{ 460 if (trig) 461 led_trigger_unregister(trig); 462 kfree(trig); 463} 464EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);