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/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 class_device *dev, const char *buf, 32 size_t count) 33 { 34 struct led_classdev *led_cdev = class_get_devdata(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 class_device *dev, char *buf) 71 { 72 struct led_classdev *led_cdev = class_get_devdata(dev); 73 struct led_trigger *trig; 74 int len = 0; 75 76 read_lock(&triggers_list_lock); 77 read_lock(&led_cdev->trigger_lock); 78 79 if (!led_cdev->trigger) 80 len += sprintf(buf+len, "[none] "); 81 else 82 len += sprintf(buf+len, "none "); 83 84 list_for_each_entry(trig, &trigger_list, next_trig) { 85 if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, 86 trig->name)) 87 len += sprintf(buf+len, "[%s] ", trig->name); 88 else 89 len += sprintf(buf+len, "%s ", trig->name); 90 } 91 read_unlock(&led_cdev->trigger_lock); 92 read_unlock(&triggers_list_lock); 93 94 len += sprintf(len+buf, "\n"); 95 return len; 96 } 97 98 void led_trigger_event(struct led_trigger *trigger, 99 enum led_brightness brightness) 100 { 101 struct list_head *entry; 102 103 if (!trigger) 104 return; 105 106 read_lock(&trigger->leddev_list_lock); 107 list_for_each(entry, &trigger->led_cdevs) { 108 struct led_classdev *led_cdev; 109 110 led_cdev = list_entry(entry, struct led_classdev, trig_list); 111 led_set_brightness(led_cdev, brightness); 112 } 113 read_unlock(&trigger->leddev_list_lock); 114 } 115 116 /* Caller must ensure led_cdev->trigger_lock held */ 117 void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) 118 { 119 unsigned long flags; 120 121 /* Remove any existing trigger */ 122 if (led_cdev->trigger) { 123 write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags); 124 list_del(&led_cdev->trig_list); 125 write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags); 126 if (led_cdev->trigger->deactivate) 127 led_cdev->trigger->deactivate(led_cdev); 128 } 129 if (trigger) { 130 write_lock_irqsave(&trigger->leddev_list_lock, flags); 131 list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs); 132 write_unlock_irqrestore(&trigger->leddev_list_lock, flags); 133 if (trigger->activate) 134 trigger->activate(led_cdev); 135 } 136 led_cdev->trigger = trigger; 137 } 138 139 void led_trigger_set_default(struct led_classdev *led_cdev) 140 { 141 struct led_trigger *trig; 142 143 if (!led_cdev->default_trigger) 144 return; 145 146 read_lock(&triggers_list_lock); 147 write_lock(&led_cdev->trigger_lock); 148 list_for_each_entry(trig, &trigger_list, next_trig) { 149 if (!strcmp(led_cdev->default_trigger, trig->name)) 150 led_trigger_set(led_cdev, trig); 151 } 152 write_unlock(&led_cdev->trigger_lock); 153 read_unlock(&triggers_list_lock); 154 } 155 156 int led_trigger_register(struct led_trigger *trigger) 157 { 158 struct led_classdev *led_cdev; 159 160 rwlock_init(&trigger->leddev_list_lock); 161 INIT_LIST_HEAD(&trigger->led_cdevs); 162 163 /* Add to the list of led triggers */ 164 write_lock(&triggers_list_lock); 165 list_add_tail(&trigger->next_trig, &trigger_list); 166 write_unlock(&triggers_list_lock); 167 168 /* Register with any LEDs that have this as a default trigger */ 169 read_lock(&leds_list_lock); 170 list_for_each_entry(led_cdev, &leds_list, node) { 171 write_lock(&led_cdev->trigger_lock); 172 if (!led_cdev->trigger && led_cdev->default_trigger && 173 !strcmp(led_cdev->default_trigger, trigger->name)) 174 led_trigger_set(led_cdev, trigger); 175 write_unlock(&led_cdev->trigger_lock); 176 } 177 read_unlock(&leds_list_lock); 178 179 return 0; 180 } 181 182 void led_trigger_register_simple(const char *name, struct led_trigger **tp) 183 { 184 struct led_trigger *trigger; 185 186 trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 187 188 if (trigger) { 189 trigger->name = name; 190 led_trigger_register(trigger); 191 } 192 *tp = trigger; 193 } 194 195 void led_trigger_unregister(struct led_trigger *trigger) 196 { 197 struct led_classdev *led_cdev; 198 199 /* Remove from the list of led triggers */ 200 write_lock(&triggers_list_lock); 201 list_del(&trigger->next_trig); 202 write_unlock(&triggers_list_lock); 203 204 /* Remove anyone actively using this trigger */ 205 read_lock(&leds_list_lock); 206 list_for_each_entry(led_cdev, &leds_list, node) { 207 write_lock(&led_cdev->trigger_lock); 208 if (led_cdev->trigger == trigger) 209 led_trigger_set(led_cdev, NULL); 210 write_unlock(&led_cdev->trigger_lock); 211 } 212 read_unlock(&leds_list_lock); 213 } 214 215 void led_trigger_unregister_simple(struct led_trigger *trigger) 216 { 217 led_trigger_unregister(trigger); 218 kfree(trigger); 219 } 220 221 /* Used by LED Class */ 222 EXPORT_SYMBOL_GPL(led_trigger_set); 223 EXPORT_SYMBOL_GPL(led_trigger_set_default); 224 EXPORT_SYMBOL_GPL(led_trigger_show); 225 EXPORT_SYMBOL_GPL(led_trigger_store); 226 227 /* LED Trigger Interface */ 228 EXPORT_SYMBOL_GPL(led_trigger_register); 229 EXPORT_SYMBOL_GPL(led_trigger_unregister); 230 231 /* Simple LED Tigger Interface */ 232 EXPORT_SYMBOL_GPL(led_trigger_register_simple); 233 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); 234 EXPORT_SYMBOL_GPL(led_trigger_event); 235 236 MODULE_AUTHOR("Richard Purdie"); 237 MODULE_LICENSE("GPL"); 238 MODULE_DESCRIPTION("LED Triggers Core"); 239