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