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