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 "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 *trigger) 103 { 104 unsigned long flags; 105 106 /* Remove any existing trigger */ 107 if (led_cdev->trigger) { 108 write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags); 109 list_del(&led_cdev->trig_list); 110 write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, 111 flags); 112 if (led_cdev->trigger->deactivate) 113 led_cdev->trigger->deactivate(led_cdev); 114 led_cdev->trigger = NULL; 115 led_set_brightness(led_cdev, LED_OFF); 116 } 117 if (trigger) { 118 write_lock_irqsave(&trigger->leddev_list_lock, flags); 119 list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs); 120 write_unlock_irqrestore(&trigger->leddev_list_lock, flags); 121 led_cdev->trigger = trigger; 122 if (trigger->activate) 123 trigger->activate(led_cdev); 124 } 125 } 126 EXPORT_SYMBOL_GPL(led_trigger_set); 127 128 void led_trigger_remove(struct led_classdev *led_cdev) 129 { 130 down_write(&led_cdev->trigger_lock); 131 led_trigger_set(led_cdev, NULL); 132 up_write(&led_cdev->trigger_lock); 133 } 134 EXPORT_SYMBOL_GPL(led_trigger_remove); 135 136 void led_trigger_set_default(struct led_classdev *led_cdev) 137 { 138 struct led_trigger *trig; 139 140 if (!led_cdev->default_trigger) 141 return; 142 143 down_read(&triggers_list_lock); 144 down_write(&led_cdev->trigger_lock); 145 list_for_each_entry(trig, &trigger_list, next_trig) { 146 if (!strcmp(led_cdev->default_trigger, trig->name)) 147 led_trigger_set(led_cdev, trig); 148 } 149 up_write(&led_cdev->trigger_lock); 150 up_read(&triggers_list_lock); 151 } 152 EXPORT_SYMBOL_GPL(led_trigger_set_default); 153 154 /* LED Trigger Interface */ 155 156 int led_trigger_register(struct led_trigger *trigger) 157 { 158 struct led_classdev *led_cdev; 159 struct led_trigger *trig; 160 161 rwlock_init(&trigger->leddev_list_lock); 162 INIT_LIST_HEAD(&trigger->led_cdevs); 163 164 down_write(&triggers_list_lock); 165 /* Make sure the trigger's name isn't already in use */ 166 list_for_each_entry(trig, &trigger_list, next_trig) { 167 if (!strcmp(trig->name, trigger->name)) { 168 up_write(&triggers_list_lock); 169 return -EEXIST; 170 } 171 } 172 /* Add to the list of led triggers */ 173 list_add_tail(&trigger->next_trig, &trigger_list); 174 up_write(&triggers_list_lock); 175 176 /* Register with any LEDs that have this as a default trigger */ 177 down_read(&leds_list_lock); 178 list_for_each_entry(led_cdev, &leds_list, node) { 179 down_write(&led_cdev->trigger_lock); 180 if (!led_cdev->trigger && led_cdev->default_trigger && 181 !strcmp(led_cdev->default_trigger, trigger->name)) 182 led_trigger_set(led_cdev, trigger); 183 up_write(&led_cdev->trigger_lock); 184 } 185 up_read(&leds_list_lock); 186 187 return 0; 188 } 189 EXPORT_SYMBOL_GPL(led_trigger_register); 190 191 void led_trigger_unregister(struct led_trigger *trigger) 192 { 193 struct led_classdev *led_cdev; 194 195 /* Remove from the list of led triggers */ 196 down_write(&triggers_list_lock); 197 list_del(&trigger->next_trig); 198 up_write(&triggers_list_lock); 199 200 /* Remove anyone actively using this trigger */ 201 down_read(&leds_list_lock); 202 list_for_each_entry(led_cdev, &leds_list, node) { 203 down_write(&led_cdev->trigger_lock); 204 if (led_cdev->trigger == trigger) 205 led_trigger_set(led_cdev, NULL); 206 up_write(&led_cdev->trigger_lock); 207 } 208 up_read(&leds_list_lock); 209 } 210 EXPORT_SYMBOL_GPL(led_trigger_unregister); 211 212 /* Simple LED Tigger Interface */ 213 214 void led_trigger_event(struct led_trigger *trigger, 215 enum led_brightness brightness) 216 { 217 struct list_head *entry; 218 219 if (!trigger) 220 return; 221 222 read_lock(&trigger->leddev_list_lock); 223 list_for_each(entry, &trigger->led_cdevs) { 224 struct led_classdev *led_cdev; 225 226 led_cdev = list_entry(entry, struct led_classdev, trig_list); 227 led_set_brightness(led_cdev, brightness); 228 } 229 read_unlock(&trigger->leddev_list_lock); 230 } 231 EXPORT_SYMBOL_GPL(led_trigger_event); 232 233 void led_trigger_register_simple(const char *name, struct led_trigger **tp) 234 { 235 struct led_trigger *trigger; 236 int err; 237 238 trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 239 240 if (trigger) { 241 trigger->name = name; 242 err = led_trigger_register(trigger); 243 if (err < 0) 244 printk(KERN_WARNING "LED trigger %s failed to register" 245 " (%d)\n", name, err); 246 } else 247 printk(KERN_WARNING "LED trigger %s failed to register" 248 " (no memory)\n", name); 249 250 *tp = trigger; 251 } 252 EXPORT_SYMBOL_GPL(led_trigger_register_simple); 253 254 void led_trigger_unregister_simple(struct led_trigger *trigger) 255 { 256 if (trigger) 257 led_trigger_unregister(trigger); 258 kfree(trigger); 259 } 260 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); 261 262 MODULE_AUTHOR("Richard Purdie"); 263 MODULE_LICENSE("GPL"); 264 MODULE_DESCRIPTION("LED Triggers Core"); 265