led-class.c 4.6 KB
Newer Older
R
Richard Purdie 已提交
1 2 3 4
/*
 * LED Class Core
 *
 * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
5
 * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
R
Richard Purdie 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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>
#include <linux/err.h>
21
#include <linux/ctype.h>
R
Richard Purdie 已提交
22 23 24 25 26
#include <linux/leds.h>
#include "leds.h"

static struct class *leds_class;

27 28 29 30 31 32
static void led_update_brightness(struct led_classdev *led_cdev)
{
	if (led_cdev->brightness_get)
		led_cdev->brightness = led_cdev->brightness_get(led_cdev);
}

33 34
static ssize_t led_brightness_show(struct device *dev, 
		struct device_attribute *attr, char *buf)
R
Richard Purdie 已提交
35
{
36
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
R
Richard Purdie 已提交
37 38

	/* no lock needed for this */
39
	led_update_brightness(led_cdev);
R
Richard Purdie 已提交
40

S
Sven Wegener 已提交
41
	return sprintf(buf, "%u\n", led_cdev->brightness);
R
Richard Purdie 已提交
42 43
}

44 45
static ssize_t led_brightness_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
R
Richard Purdie 已提交
46
{
47
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
R
Richard Purdie 已提交
48 49 50
	ssize_t ret = -EINVAL;
	char *after;
	unsigned long state = simple_strtoul(buf, &after, 10);
51
	size_t count = after - buf;
R
Richard Purdie 已提交
52

53 54 55 56 57
	if (*after && isspace(*after))
		count++;

	if (count == size) {
		ret = count;
58 59 60

		if (state == LED_OFF)
			led_trigger_remove(led_cdev);
R
Richard Purdie 已提交
61 62 63 64 65 66
		led_set_brightness(led_cdev, state);
	}

	return ret;
}

67
static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
68
#ifdef CONFIG_LEDS_TRIGGERS
69
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
70
#endif
R
Richard Purdie 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

/**
 * led_classdev_suspend - suspend an led_classdev.
 * @led_cdev: the led_classdev to suspend.
 */
void led_classdev_suspend(struct led_classdev *led_cdev)
{
	led_cdev->flags |= LED_SUSPENDED;
	led_cdev->brightness_set(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);

/**
 * led_classdev_resume - resume an led_classdev.
 * @led_cdev: the led_classdev to resume.
 */
void led_classdev_resume(struct led_classdev *led_cdev)
{
	led_cdev->brightness_set(led_cdev, led_cdev->brightness);
	led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);

/**
 * led_classdev_register - register a new object of led_classdev class.
 * @dev: The device to register.
 * @led_cdev: the led_classdev structure for this device.
 */
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
101 102
	int rc;

103 104
	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
				      "%s", led_cdev->name);
105
	if (IS_ERR(led_cdev->dev))
106
		return PTR_ERR(led_cdev->dev);
R
Richard Purdie 已提交
107 108

	/* register the attributes */
109
	rc = device_create_file(led_cdev->dev, &dev_attr_brightness);
110 111
	if (rc)
		goto err_out;
R
Richard Purdie 已提交
112

113 114 115
#ifdef CONFIG_LEDS_TRIGGERS
	init_rwsem(&led_cdev->trigger_lock);
#endif
R
Richard Purdie 已提交
116
	/* add to the list of leds */
117
	down_write(&leds_list_lock);
R
Richard Purdie 已提交
118
	list_add_tail(&led_cdev->node, &leds_list);
119
	up_write(&leds_list_lock);
R
Richard Purdie 已提交
120

121 122
	led_update_brightness(led_cdev);

123
#ifdef CONFIG_LEDS_TRIGGERS
124
	rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
125 126
	if (rc)
		goto err_out_led_list;
127

128
	led_trigger_set_default(led_cdev);
129 130
#endif

R
Richard Purdie 已提交
131
	printk(KERN_INFO "Registered led device: %s\n",
132
			led_cdev->name);
R
Richard Purdie 已提交
133 134

	return 0;
135 136 137

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
138
	device_remove_file(led_cdev->dev, &dev_attr_brightness);
139 140 141
	list_del(&led_cdev->node);
#endif
err_out:
142
	device_unregister(led_cdev->dev);
143
	return rc;
R
Richard Purdie 已提交
144 145 146 147
}
EXPORT_SYMBOL_GPL(led_classdev_register);

/**
Q
Qinghuang Feng 已提交
148
 * led_classdev_unregister - unregisters a object of led_properties class.
H
Henrik Kretzschmar 已提交
149
 * @led_cdev: the led device to unregister
R
Richard Purdie 已提交
150 151 152
 *
 * Unregisters a previously registered via led_classdev_register object.
 */
153
void led_classdev_unregister(struct led_classdev *led_cdev)
R
Richard Purdie 已提交
154
{
155
	device_remove_file(led_cdev->dev, &dev_attr_brightness);
156
#ifdef CONFIG_LEDS_TRIGGERS
157
	device_remove_file(led_cdev->dev, &dev_attr_trigger);
158
	down_write(&led_cdev->trigger_lock);
159 160
	if (led_cdev->trigger)
		led_trigger_set(led_cdev, NULL);
161
	up_write(&led_cdev->trigger_lock);
162
#endif
R
Richard Purdie 已提交
163

164
	device_unregister(led_cdev->dev);
R
Richard Purdie 已提交
165

166
	down_write(&leds_list_lock);
R
Richard Purdie 已提交
167
	list_del(&led_cdev->node);
168
	up_write(&leds_list_lock);
R
Richard Purdie 已提交
169
}
170
EXPORT_SYMBOL_GPL(led_classdev_unregister);
R
Richard Purdie 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190

static int __init leds_init(void)
{
	leds_class = class_create(THIS_MODULE, "leds");
	if (IS_ERR(leds_class))
		return PTR_ERR(leds_class);
	return 0;
}

static void __exit leds_exit(void)
{
	class_destroy(leds_class);
}

subsys_initcall(leds_init);
module_exit(leds_exit);

MODULE_AUTHOR("John Lenz, Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Class Interface");