led-class.c 4.5 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
static ssize_t led_brightness_show(struct device *dev, 
		struct device_attribute *attr, char *buf)
R
Richard Purdie 已提交
29
{
30
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
R
Richard Purdie 已提交
31 32 33 34 35 36 37 38 39
	ssize_t ret = 0;

	/* no lock needed for this */
	sprintf(buf, "%u\n", led_cdev->brightness);
	ret = strlen(buf) + 1;

	return ret;
}

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

49 50 51 52 53
	if (*after && isspace(*after))
		count++;

	if (count == size) {
		ret = count;
54 55 56

		if (state == LED_OFF)
			led_trigger_remove(led_cdev);
R
Richard Purdie 已提交
57 58 59 60 61 62
		led_set_brightness(led_cdev, state);
	}

	return ret;
}

63
static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
64
#ifdef CONFIG_LEDS_TRIGGERS
65
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
66
#endif
R
Richard Purdie 已提交
67 68 69 70 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

/**
 * 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)
{
97 98
	int rc;

99 100 101 102
	led_cdev->dev = device_create(leds_class, parent, 0, "%s",
					    led_cdev->name);
	if (unlikely(IS_ERR(led_cdev->dev)))
		return PTR_ERR(led_cdev->dev);
R
Richard Purdie 已提交
103

104
	dev_set_drvdata(led_cdev->dev, led_cdev);
R
Richard Purdie 已提交
105 106

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

	/* add to the list of leds */
112
	down_write(&leds_list_lock);
R
Richard Purdie 已提交
113
	list_add_tail(&led_cdev->node, &leds_list);
114
	up_write(&leds_list_lock);
R
Richard Purdie 已提交
115

116
#ifdef CONFIG_LEDS_TRIGGERS
117
	init_rwsem(&led_cdev->trigger_lock);
118

119
	rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
120 121
	if (rc)
		goto err_out_led_list;
122

123
	led_trigger_set_default(led_cdev);
124 125
#endif

R
Richard Purdie 已提交
126
	printk(KERN_INFO "Registered led device: %s\n",
127
			led_cdev->name);
R
Richard Purdie 已提交
128 129

	return 0;
130 131 132

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
133
	device_remove_file(led_cdev->dev, &dev_attr_brightness);
134 135 136
	list_del(&led_cdev->node);
#endif
err_out:
137
	device_unregister(led_cdev->dev);
138
	return rc;
R
Richard Purdie 已提交
139 140 141 142
}
EXPORT_SYMBOL_GPL(led_classdev_register);

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

159
	device_unregister(led_cdev->dev);
R
Richard Purdie 已提交
160

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

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");