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 */ 28 static DECLARE_RWSEM(triggers_list_lock); 29 static LIST_HEAD(trigger_list); 30 31 /* Used by LED Class */ 32 33 ssize_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 74 unlock: 75 mutex_unlock(&led_cdev->led_access); 76 return ret; 77 } 78 EXPORT_SYMBOL_GPL(led_trigger_store); 79 80 ssize_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 } 108 EXPORT_SYMBOL_GPL(led_trigger_show); 109 110 /* Caller must ensure led_cdev->trigger_lock held */ 111 void 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 } 150 EXPORT_SYMBOL_GPL(led_trigger_set); 151 152 void 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 } 158 EXPORT_SYMBOL_GPL(led_trigger_remove); 159 160 void 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 } 176 EXPORT_SYMBOL_GPL(led_trigger_set_default); 177 178 void 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 } 189 EXPORT_SYMBOL_GPL(led_trigger_rename_static); 190 191 /* LED Trigger Interface */ 192 193 int 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 } 226 EXPORT_SYMBOL_GPL(led_trigger_register); 227 228 void 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 } 250 EXPORT_SYMBOL_GPL(led_trigger_unregister); 251 252 static void devm_led_trigger_release(struct device *dev, void *res) 253 { 254 led_trigger_unregister(*(struct led_trigger **)res); 255 } 256 257 int devm_led_trigger_register(struct device *dev, 258 struct led_trigger *trig) 259 { 260 struct led_trigger **dr; 261 int rc; 262 263 dr = devres_alloc(devm_led_trigger_release, sizeof(*dr), 264 GFP_KERNEL); 265 if (!dr) 266 return -ENOMEM; 267 268 *dr = trig; 269 270 rc = led_trigger_register(trig); 271 if (rc) 272 devres_free(dr); 273 else 274 devres_add(dev, dr); 275 276 return rc; 277 } 278 EXPORT_SYMBOL_GPL(devm_led_trigger_register); 279 280 /* Simple LED Tigger Interface */ 281 282 void led_trigger_event(struct led_trigger *trig, 283 enum led_brightness brightness) 284 { 285 struct led_classdev *led_cdev; 286 287 if (!trig) 288 return; 289 290 read_lock(&trig->leddev_list_lock); 291 list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) 292 led_set_brightness(led_cdev, brightness); 293 read_unlock(&trig->leddev_list_lock); 294 } 295 EXPORT_SYMBOL_GPL(led_trigger_event); 296 297 static void led_trigger_blink_setup(struct led_trigger *trig, 298 unsigned long *delay_on, 299 unsigned long *delay_off, 300 int oneshot, 301 int invert) 302 { 303 struct led_classdev *led_cdev; 304 305 if (!trig) 306 return; 307 308 read_lock(&trig->leddev_list_lock); 309 list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) { 310 if (oneshot) 311 led_blink_set_oneshot(led_cdev, delay_on, delay_off, 312 invert); 313 else 314 led_blink_set(led_cdev, delay_on, delay_off); 315 } 316 read_unlock(&trig->leddev_list_lock); 317 } 318 319 void led_trigger_blink(struct led_trigger *trig, 320 unsigned long *delay_on, 321 unsigned long *delay_off) 322 { 323 led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0); 324 } 325 EXPORT_SYMBOL_GPL(led_trigger_blink); 326 327 void led_trigger_blink_oneshot(struct led_trigger *trig, 328 unsigned long *delay_on, 329 unsigned long *delay_off, 330 int invert) 331 { 332 led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert); 333 } 334 EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot); 335 336 void led_trigger_register_simple(const char *name, struct led_trigger **tp) 337 { 338 struct led_trigger *trig; 339 int err; 340 341 trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 342 343 if (trig) { 344 trig->name = name; 345 err = led_trigger_register(trig); 346 if (err < 0) { 347 kfree(trig); 348 trig = NULL; 349 pr_warn("LED trigger %s failed to register (%d)\n", 350 name, err); 351 } 352 } else { 353 pr_warn("LED trigger %s failed to register (no memory)\n", 354 name); 355 } 356 *tp = trig; 357 } 358 EXPORT_SYMBOL_GPL(led_trigger_register_simple); 359 360 void led_trigger_unregister_simple(struct led_trigger *trig) 361 { 362 if (trig) 363 led_trigger_unregister(trig); 364 kfree(trig); 365 } 366 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); 367 368 MODULE_AUTHOR("Richard Purdie"); 369 MODULE_LICENSE("GPL"); 370 MODULE_DESCRIPTION("LED Triggers Core"); 371