1 /* 2 * LED Triggers Core 3 * 4 * Copyright 2005-2006 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/config.h> 15 #include <linux/module.h> 16 #include <linux/kernel.h> 17 #include <linux/init.h> 18 #include <linux/list.h> 19 #include <linux/spinlock.h> 20 #include <linux/device.h> 21 #include <linux/sysdev.h> 22 #include <linux/timer.h> 23 #include <linux/leds.h> 24 #include "leds.h" 25 26 /* 27 * Nests outside led_cdev->trigger_lock 28 */ 29 static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED; 30 static LIST_HEAD(trigger_list); 31 32 ssize_t led_trigger_store(struct class_device *dev, const char *buf, 33 size_t count) 34 { 35 struct led_classdev *led_cdev = class_get_devdata(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 write_lock(&led_cdev->trigger_lock); 49 led_trigger_set(led_cdev, NULL); 50 write_unlock(&led_cdev->trigger_lock); 51 return count; 52 } 53 54 read_lock(&triggers_list_lock); 55 list_for_each_entry(trig, &trigger_list, next_trig) { 56 if (!strcmp(trigger_name, trig->name)) { 57 write_lock(&led_cdev->trigger_lock); 58 led_trigger_set(led_cdev, trig); 59 write_unlock(&led_cdev->trigger_lock); 60 61 read_unlock(&triggers_list_lock); 62 return count; 63 } 64 } 65 read_unlock(&triggers_list_lock); 66 67 return -EINVAL; 68 } 69 70 71 ssize_t led_trigger_show(struct class_device *dev, char *buf) 72 { 73 struct led_classdev *led_cdev = class_get_devdata(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 } 130 if (trigger) { 131 write_lock_irqsave(&trigger->leddev_list_lock, flags); 132 list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs); 133 write_unlock_irqrestore(&trigger->leddev_list_lock, flags); 134 if (trigger->activate) 135 trigger->activate(led_cdev); 136 } 137 led_cdev->trigger = trigger; 138 } 139 140 void led_trigger_set_default(struct led_classdev *led_cdev) 141 { 142 struct led_trigger *trig; 143 144 if (!led_cdev->default_trigger) 145 return; 146 147 read_lock(&triggers_list_lock); 148 write_lock(&led_cdev->trigger_lock); 149 list_for_each_entry(trig, &trigger_list, next_trig) { 150 if (!strcmp(led_cdev->default_trigger, trig->name)) 151 led_trigger_set(led_cdev, trig); 152 } 153 write_unlock(&led_cdev->trigger_lock); 154 read_unlock(&triggers_list_lock); 155 } 156 157 int led_trigger_register(struct led_trigger *trigger) 158 { 159 struct led_classdev *led_cdev; 160 161 rwlock_init(&trigger->leddev_list_lock); 162 INIT_LIST_HEAD(&trigger->led_cdevs); 163 164 /* Add to the list of led triggers */ 165 write_lock(&triggers_list_lock); 166 list_add_tail(&trigger->next_trig, &trigger_list); 167 write_unlock(&triggers_list_lock); 168 169 /* Register with any LEDs that have this as a default trigger */ 170 read_lock(&leds_list_lock); 171 list_for_each_entry(led_cdev, &leds_list, node) { 172 write_lock(&led_cdev->trigger_lock); 173 if (!led_cdev->trigger && led_cdev->default_trigger && 174 !strcmp(led_cdev->default_trigger, trigger->name)) 175 led_trigger_set(led_cdev, trigger); 176 write_unlock(&led_cdev->trigger_lock); 177 } 178 read_unlock(&leds_list_lock); 179 180 return 0; 181 } 182 183 void led_trigger_register_simple(const char *name, struct led_trigger **tp) 184 { 185 struct led_trigger *trigger; 186 187 trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 188 189 if (trigger) { 190 trigger->name = name; 191 led_trigger_register(trigger); 192 } 193 *tp = trigger; 194 } 195 196 void led_trigger_unregister(struct led_trigger *trigger) 197 { 198 struct led_classdev *led_cdev; 199 200 /* Remove from the list of led triggers */ 201 write_lock(&triggers_list_lock); 202 list_del(&trigger->next_trig); 203 write_unlock(&triggers_list_lock); 204 205 /* Remove anyone actively using this trigger */ 206 read_lock(&leds_list_lock); 207 list_for_each_entry(led_cdev, &leds_list, node) { 208 write_lock(&led_cdev->trigger_lock); 209 if (led_cdev->trigger == trigger) 210 led_trigger_set(led_cdev, NULL); 211 write_unlock(&led_cdev->trigger_lock); 212 } 213 read_unlock(&leds_list_lock); 214 } 215 216 void led_trigger_unregister_simple(struct led_trigger *trigger) 217 { 218 led_trigger_unregister(trigger); 219 kfree(trigger); 220 } 221 222 /* Used by LED Class */ 223 EXPORT_SYMBOL_GPL(led_trigger_set); 224 EXPORT_SYMBOL_GPL(led_trigger_set_default); 225 EXPORT_SYMBOL_GPL(led_trigger_show); 226 EXPORT_SYMBOL_GPL(led_trigger_store); 227 228 /* LED Trigger Interface */ 229 EXPORT_SYMBOL_GPL(led_trigger_register); 230 EXPORT_SYMBOL_GPL(led_trigger_unregister); 231 232 /* Simple LED Tigger Interface */ 233 EXPORT_SYMBOL_GPL(led_trigger_register_simple); 234 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); 235 EXPORT_SYMBOL_GPL(led_trigger_event); 236 237 MODULE_AUTHOR("Richard Purdie"); 238 MODULE_LICENSE("GPL"); 239 MODULE_DESCRIPTION("LED Triggers Core"); 240