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