led-triggers.c 7.3 KB
Newer Older
1 2 3
/*
 * LED Triggers Core
 *
4
 * Copyright 2005-2007 Openedhand Ltd.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * Author: Richard Purdie <rpurdie@openedhand.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/timer.h>
22
#include <linux/rwsem.h>
23
#include <linux/leds.h>
24
#include <linux/slab.h>
25 26 27 28 29
#include "leds.h"

/*
 * Nests outside led_cdev->trigger_lock
 */
30
static DECLARE_RWSEM(triggers_list_lock);
31 32
static LIST_HEAD(trigger_list);

33 34
 /* Used by LED Class */

35 36
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
		const char *buf, size_t count)
37
{
38
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
39 40 41 42 43 44 45 46 47 48 49 50
	char trigger_name[TRIG_NAME_MAX];
	struct led_trigger *trig;
	size_t len;

	trigger_name[sizeof(trigger_name) - 1] = '\0';
	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
	len = strlen(trigger_name);

	if (len && trigger_name[len - 1] == '\n')
		trigger_name[len - 1] = '\0';

	if (!strcmp(trigger_name, "none")) {
51
		led_trigger_remove(led_cdev);
52 53 54
		return count;
	}

55
	down_read(&triggers_list_lock);
56 57
	list_for_each_entry(trig, &trigger_list, next_trig) {
		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 64 65
			return count;
		}
	}
66
	up_read(&triggers_list_lock);
67 68 69

	return -EINVAL;
}
70
EXPORT_SYMBOL_GPL(led_trigger_store);
71

72 73
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
		char *buf)
74
{
75
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
76 77 78
	struct led_trigger *trig;
	int len = 0;

79 80
	down_read(&triggers_list_lock);
	down_read(&led_cdev->trigger_lock);
81 82 83 84 85 86 87 88 89 90 91 92 93

	if (!led_cdev->trigger)
		len += sprintf(buf+len, "[none] ");
	else
		len += sprintf(buf+len, "none ");

	list_for_each_entry(trig, &trigger_list, next_trig) {
		if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
							trig->name))
			len += sprintf(buf+len, "[%s] ", trig->name);
		else
			len += sprintf(buf+len, "%s ", trig->name);
	}
94 95
	up_read(&led_cdev->trigger_lock);
	up_read(&triggers_list_lock);
96 97 98 99

	len += sprintf(len+buf, "\n");
	return len;
}
100
EXPORT_SYMBOL_GPL(led_trigger_show);
101 102 103 104 105 106 107 108 109 110

/* Caller must ensure led_cdev->trigger_lock held */
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
{
	unsigned long flags;

	/* Remove any existing trigger */
	if (led_cdev->trigger) {
		write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
		list_del(&led_cdev->trig_list);
111 112
		write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
			flags);
113 114
		if (led_cdev->trigger->deactivate)
			led_cdev->trigger->deactivate(led_cdev);
115
		led_cdev->trigger = NULL;
116
		led_brightness_set(led_cdev, LED_OFF);
117 118 119 120 121
	}
	if (trigger) {
		write_lock_irqsave(&trigger->leddev_list_lock, flags);
		list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
		write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
122
		led_cdev->trigger = trigger;
123 124 125 126
		if (trigger->activate)
			trigger->activate(led_cdev);
	}
}
127
EXPORT_SYMBOL_GPL(led_trigger_set);
128

129 130 131 132 133 134
void led_trigger_remove(struct led_classdev *led_cdev)
{
	down_write(&led_cdev->trigger_lock);
	led_trigger_set(led_cdev, NULL);
	up_write(&led_cdev->trigger_lock);
}
135
EXPORT_SYMBOL_GPL(led_trigger_remove);
136

137 138 139 140 141 142 143
void led_trigger_set_default(struct led_classdev *led_cdev)
{
	struct led_trigger *trig;

	if (!led_cdev->default_trigger)
		return;

144 145
	down_read(&triggers_list_lock);
	down_write(&led_cdev->trigger_lock);
146 147 148 149
	list_for_each_entry(trig, &trigger_list, next_trig) {
		if (!strcmp(led_cdev->default_trigger, trig->name))
			led_trigger_set(led_cdev, trig);
	}
150 151
	up_write(&led_cdev->trigger_lock);
	up_read(&triggers_list_lock);
152
}
153 154 155
EXPORT_SYMBOL_GPL(led_trigger_set_default);

/* LED Trigger Interface */
156 157 158 159

int led_trigger_register(struct led_trigger *trigger)
{
	struct led_classdev *led_cdev;
160
	struct led_trigger *trig;
161 162 163 164

	rwlock_init(&trigger->leddev_list_lock);
	INIT_LIST_HEAD(&trigger->led_cdevs);

165
	down_write(&triggers_list_lock);
166 167 168 169 170 171 172 173
	/* Make sure the trigger's name isn't already in use */
	list_for_each_entry(trig, &trigger_list, next_trig) {
		if (!strcmp(trig->name, trigger->name)) {
			up_write(&triggers_list_lock);
			return -EEXIST;
		}
	}
	/* 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 182 183
		if (!led_cdev->trigger && led_cdev->default_trigger &&
			    !strcmp(led_cdev->default_trigger, trigger->name))
			led_trigger_set(led_cdev, trigger);
184
		up_write(&led_cdev->trigger_lock);
185
	}
186
	up_read(&leds_list_lock);
187 188 189

	return 0;
}
190
EXPORT_SYMBOL_GPL(led_trigger_register);
191 192 193 194 195 196

void led_trigger_unregister(struct led_trigger *trigger)
{
	struct led_classdev *led_cdev;

	/* 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 206
		if (led_cdev->trigger == trigger)
			led_trigger_set(led_cdev, NULL);
207
		up_write(&led_cdev->trigger_lock);
208
	}
209
	up_read(&leds_list_lock);
210
}
211
EXPORT_SYMBOL_GPL(led_trigger_unregister);
212

213 214 215 216
/* Simple LED Tigger Interface */

void led_trigger_event(struct led_trigger *trigger,
			enum led_brightness brightness)
217
{
218 219 220 221 222 223 224 225 226 227 228 229 230
	struct list_head *entry;

	if (!trigger)
		return;

	read_lock(&trigger->leddev_list_lock);
	list_for_each(entry, &trigger->led_cdevs) {
		struct led_classdev *led_cdev;

		led_cdev = list_entry(entry, struct led_classdev, trig_list);
		led_set_brightness(led_cdev, brightness);
	}
	read_unlock(&trigger->leddev_list_lock);
231
}
232
EXPORT_SYMBOL_GPL(led_trigger_event);
233

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
void led_trigger_blink(struct led_trigger *trigger,
		       unsigned long *delay_on,
		       unsigned long *delay_off)
{
	struct list_head *entry;

	if (!trigger)
		return;

	read_lock(&trigger->leddev_list_lock);
	list_for_each(entry, &trigger->led_cdevs) {
		struct led_classdev *led_cdev;

		led_cdev = list_entry(entry, struct led_classdev, trig_list);
		led_blink_set(led_cdev, delay_on, delay_off);
	}
	read_unlock(&trigger->leddev_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_blink);

254 255 256 257
void led_trigger_register_simple(const char *name, struct led_trigger **tp)
{
	struct led_trigger *trigger;
	int err;
258

259
	trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
260

261 262 263 264 265 266 267 268 269 270 271 272
	if (trigger) {
		trigger->name = name;
		err = led_trigger_register(trigger);
		if (err < 0)
			printk(KERN_WARNING "LED trigger %s failed to register"
				" (%d)\n", name, err);
	} else
		printk(KERN_WARNING "LED trigger %s failed to register"
			" (no memory)\n", name);

	*tp = trigger;
}
273
EXPORT_SYMBOL_GPL(led_trigger_register_simple);
274 275 276 277 278 279 280

void led_trigger_unregister_simple(struct led_trigger *trigger)
{
	if (trigger)
		led_trigger_unregister(trigger);
	kfree(trigger);
}
281 282 283 284 285
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);

MODULE_AUTHOR("Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Triggers Core");