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