1/* 2 * LED Triggers Core 3 * 4 * Copyright 2005-2007 Openedhand Ltd. 5 * 6 * 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 */ 13 14#include <linux/module.h> 15#include <linux/kernel.h> 16#include <linux/list.h> 17#include <linux/spinlock.h> 18#include <linux/device.h> 19#include <linux/timer.h> 20#include <linux/rwsem.h> 21#include <linux/leds.h> 22#include <linux/slab.h> 23#include "leds.h" 24 25/* 26 * Nests outside led_cdev->trigger_lock 27 */ 28static DECLARE_RWSEM(triggers_list_lock); 29static LIST_HEAD(trigger_list); 30 31 /* Used by LED Class */ 32 33ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, 34 const char *buf, size_t count) 35{ 36 struct led_classdev *led_cdev = dev_get_drvdata(dev); 37 char trigger_name[TRIG_NAME_MAX]; 38 struct led_trigger *trig; 39 size_t len; 40 int ret = count; 41 42 mutex_lock(&led_cdev->led_access); 43 44 if (led_sysfs_is_disabled(led_cdev)) { 45 ret = -EBUSY; 46 goto unlock; 47 } 48 49 trigger_name[sizeof(trigger_name) - 1] = '\0'; 50 strncpy(trigger_name, buf, sizeof(trigger_name) - 1); 51 len = strlen(trigger_name); 52 53 if (len && trigger_name[len - 1] == '\n') 54 trigger_name[len - 1] = '\0'; 55 56 if (!strcmp(trigger_name, "none")) { 57 led_trigger_remove(led_cdev); 58 goto unlock; 59 } 60 61 down_read(&triggers_list_lock); 62 list_for_each_entry(trig, &trigger_list, next_trig) { 63 if (!strcmp(trigger_name, trig->name)) { 64 down_write(&led_cdev->trigger_lock); 65 led_trigger_set(led_cdev, trig); 66 up_write(&led_cdev->trigger_lock); 67 68 up_read(&triggers_list_lock); 69 goto unlock; 70 } 71 } 72 up_read(&triggers_list_lock); 73 74unlock: 75 mutex_unlock(&led_cdev->led_access); 76 return ret; 77} 78EXPORT_SYMBOL_GPL(led_trigger_store); 79 80ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, 81 char *buf) 82{ 83 struct led_classdev *led_cdev = dev_get_drvdata(dev); 84 struct led_trigger *trig; 85 int len = 0; 86 87 down_read(&triggers_list_lock); 88 down_read(&led_cdev->trigger_lock); 89 90 if (!led_cdev->trigger) 91 len += sprintf(buf+len, "[none] "); 92 else 93 len += sprintf(buf+len, "none "); 94 95 list_for_each_entry(trig, &trigger_list, next_trig) { 96 if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, 97 trig->name)) 98 len += sprintf(buf+len, "[%s] ", trig->name); 99 else 100 len += sprintf(buf+len, "%s ", trig->name); 101 } 102 up_read(&led_cdev->trigger_lock); 103 up_read(&triggers_list_lock); 104 105 len += sprintf(len+buf, "\n"); 106 return len; 107} 108EXPORT_SYMBOL_GPL(led_trigger_show); 109 110/* Caller must ensure led_cdev->trigger_lock held */ 111void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) 112{ 113 unsigned long flags; 114 char *event = NULL; 115 char *envp[2]; 116 const char *name; 117 118 name = trig ? trig->name : "none"; 119 event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name); 120 121 /* Remove any existing trigger */ 122 if (led_cdev->trigger) { 123 write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags); 124 list_del(&led_cdev->trig_list); 125 write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, 126 flags); 127 cancel_work_sync(&led_cdev->set_brightness_work); 128 led_stop_software_blink(led_cdev); 129 if (led_cdev->trigger->deactivate) 130 led_cdev->trigger->deactivate(led_cdev); 131 led_cdev->trigger = NULL; 132 led_set_brightness(led_cdev, LED_OFF); 133 } 134 if (trig) { 135 write_lock_irqsave(&trig->leddev_list_lock, flags); 136 list_add_tail(&led_cdev->trig_list, &trig->led_cdevs); 137 write_unlock_irqrestore(&trig->leddev_list_lock, flags); 138 led_cdev->trigger = trig; 139 if (trig->activate) 140 trig->activate(led_cdev); 141 } 142 143 if (event) { 144 envp[0] = event; 145 envp[1] = NULL; 146 kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp); 147 kfree(event); 148 } 149} 150EXPORT_SYMBOL_GPL(led_trigger_set); 151 152void led_trigger_remove(struct led_classdev *led_cdev) 153{ 154 down_write(&led_cdev->trigger_lock); 155 led_trigger_set(led_cdev, NULL); 156 up_write(&led_cdev->trigger_lock); 157} 158EXPORT_SYMBOL_GPL(led_trigger_remove); 159 160void led_trigger_set_default(struct led_classdev *led_cdev) 161{ 162 struct led_trigger *trig; 163 164 if (!led_cdev->default_trigger) 165 return; 166 167 down_read(&triggers_list_lock); 168 down_write(&led_cdev->trigger_lock); 169 list_for_each_entry(trig, &trigger_list, next_trig) { 170 if (!strcmp(led_cdev->default_trigger, trig->name)) 171 led_trigger_set(led_cdev, trig); 172 } 173 up_write(&led_cdev->trigger_lock); 174 up_read(&triggers_list_lock); 175} 176EXPORT_SYMBOL_GPL(led_trigger_set_default); 177 178void led_trigger_rename_static(const char *name, struct led_trigger *trig) 179{ 180 /* new name must be on a temporary string to prevent races */ 181 BUG_ON(name == trig->name); 182 183 down_write(&triggers_list_lock); 184 /* this assumes that trig->name was originaly allocated to 185 * non constant storage */ 186 strcpy((char *)trig->name, name); 187 up_write(&triggers_list_lock); 188} 189EXPORT_SYMBOL_GPL(led_trigger_rename_static); 190 191/* LED Trigger Interface */ 192 193int led_trigger_register(struct led_trigger *trig) 194{ 195 struct led_classdev *led_cdev; 196 struct led_trigger *_trig; 197 198 rwlock_init(&trig->leddev_list_lock); 199 INIT_LIST_HEAD(&trig->led_cdevs); 200 201 down_write(&triggers_list_lock); 202 /* Make sure the trigger's name isn't already in use */ 203 list_for_each_entry(_trig, &trigger_list, next_trig) { 204 if (!strcmp(_trig->name, trig->name)) { 205 up_write(&triggers_list_lock); 206 return -EEXIST; 207 } 208 } 209 /* Add to the list of led triggers */ 210 list_add_tail(&trig->next_trig, &trigger_list); 211 up_write(&triggers_list_lock); 212 213 /* Register with any LEDs that have this as a default trigger */ 214 down_read(&leds_list_lock); 215 list_for_each_entry(led_cdev, &leds_list, node) { 216 down_write(&led_cdev->trigger_lock); 217 if (!led_cdev->trigger && led_cdev->default_trigger && 218 !strcmp(led_cdev->default_trigger, trig->name)) 219 led_trigger_set(led_cdev, trig); 220 up_write(&led_cdev->trigger_lock); 221 } 222 up_read(&leds_list_lock); 223 224 return 0; 225} 226EXPORT_SYMBOL_GPL(led_trigger_register); 227 228void led_trigger_unregister(struct led_trigger *trig) 229{ 230 struct led_classdev *led_cdev; 231 232 if (list_empty_careful(&trig->next_trig)) 233 return; 234 235 /* Remove from the list of led triggers */ 236 down_write(&triggers_list_lock); 237 list_del_init(&trig->next_trig); 238 up_write(&triggers_list_lock); 239 240 /* Remove anyone actively using this trigger */ 241 down_read(&leds_list_lock); 242 list_for_each_entry(led_cdev, &leds_list, node) { 243 down_write(&led_cdev->trigger_lock); 244 if (led_cdev->trigger == trig) 245 led_trigger_set(led_cdev, NULL); 246 up_write(&led_cdev->trigger_lock); 247 } 248 up_read(&leds_list_lock); 249} 250EXPORT_SYMBOL_GPL(led_trigger_unregister); 251 252/* Simple LED Tigger Interface */ 253 254void led_trigger_event(struct led_trigger *trig, 255 enum led_brightness brightness) 256{ 257 struct led_classdev *led_cdev; 258 259 if (!trig) 260 return; 261 262 read_lock(&trig->leddev_list_lock); 263 list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) 264 led_set_brightness(led_cdev, brightness); 265 read_unlock(&trig->leddev_list_lock); 266} 267EXPORT_SYMBOL_GPL(led_trigger_event); 268 269static void led_trigger_blink_setup(struct led_trigger *trig, 270 unsigned long *delay_on, 271 unsigned long *delay_off, 272 int oneshot, 273 int invert) 274{ 275 struct led_classdev *led_cdev; 276 277 if (!trig) 278 return; 279 280 read_lock(&trig->leddev_list_lock); 281 list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) { 282 if (oneshot) 283 led_blink_set_oneshot(led_cdev, delay_on, delay_off, 284 invert); 285 else 286 led_blink_set(led_cdev, delay_on, delay_off); 287 } 288 read_unlock(&trig->leddev_list_lock); 289} 290 291void led_trigger_blink(struct led_trigger *trig, 292 unsigned long *delay_on, 293 unsigned long *delay_off) 294{ 295 led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0); 296} 297EXPORT_SYMBOL_GPL(led_trigger_blink); 298 299void led_trigger_blink_oneshot(struct led_trigger *trig, 300 unsigned long *delay_on, 301 unsigned long *delay_off, 302 int invert) 303{ 304 led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert); 305} 306EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot); 307 308void led_trigger_register_simple(const char *name, struct led_trigger **tp) 309{ 310 struct led_trigger *trig; 311 int err; 312 313 trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 314 315 if (trig) { 316 trig->name = name; 317 err = led_trigger_register(trig); 318 if (err < 0) { 319 kfree(trig); 320 trig = NULL; 321 pr_warn("LED trigger %s failed to register (%d)\n", 322 name, err); 323 } 324 } else { 325 pr_warn("LED trigger %s failed to register (no memory)\n", 326 name); 327 } 328 *tp = trig; 329} 330EXPORT_SYMBOL_GPL(led_trigger_register_simple); 331 332void led_trigger_unregister_simple(struct led_trigger *trig) 333{ 334 if (trig) 335 led_trigger_unregister(trig); 336 kfree(trig); 337} 338EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); 339 340MODULE_AUTHOR("Richard Purdie"); 341MODULE_LICENSE("GPL"); 342MODULE_DESCRIPTION("LED Triggers Core"); 343