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