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