led-class.c 4.42 KB
Newer Older
Richard Purdie's avatar
Richard Purdie committed
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>
Richard Purdie's avatar
Richard Purdie committed
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>
Richard Purdie's avatar
Richard Purdie committed
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)
Richard Purdie's avatar
Richard Purdie committed
29
{
30
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
Richard Purdie's avatar
Richard Purdie committed
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)
Richard Purdie's avatar
Richard Purdie committed
42
{
43
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
Richard Purdie's avatar
Richard Purdie committed
44 45 46
	ssize_t ret = -EINVAL;
	char *after;
	unsigned long state = simple_strtoul(buf, &after, 10);
47
	size_t count = after - buf;
Richard Purdie's avatar
Richard Purdie committed
48

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

	if (count == size) {
		ret = count;
Richard Purdie's avatar
Richard Purdie committed
54 55 56 57 58 59
		led_set_brightness(led_cdev, state);
	}

	return ret;
}

60
static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
61
#ifdef CONFIG_LEDS_TRIGGERS
62
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
63
#endif
Richard Purdie's avatar
Richard Purdie committed
64 65 66 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

/**
 * 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)
{
94 95
	int rc;

96 97 98 99
	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);
Richard Purdie's avatar
Richard Purdie committed
100

101
	dev_set_drvdata(led_cdev->dev, led_cdev);
Richard Purdie's avatar
Richard Purdie committed
102 103

	/* register the attributes */
104
	rc = device_create_file(led_cdev->dev, &dev_attr_brightness);
105 106
	if (rc)
		goto err_out;
Richard Purdie's avatar
Richard Purdie committed
107 108 109 110 111 112

	/* add to the list of leds */
	write_lock(&leds_list_lock);
	list_add_tail(&led_cdev->node, &leds_list);
	write_unlock(&leds_list_lock);

113 114 115
#ifdef CONFIG_LEDS_TRIGGERS
	rwlock_init(&led_cdev->trigger_lock);

116
	rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
117 118
	if (rc)
		goto err_out_led_list;
119

120
	led_trigger_set_default(led_cdev);
121 122
#endif

Richard Purdie's avatar
Richard Purdie committed
123
	printk(KERN_INFO "Registered led device: %s\n",
124
			led_cdev->name);
Richard Purdie's avatar
Richard Purdie committed
125 126

	return 0;
127 128 129

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
130
	device_remove_file(led_cdev->dev, &dev_attr_brightness);
131 132 133
	list_del(&led_cdev->node);
#endif
err_out:
134
	device_unregister(led_cdev->dev);
135
	return rc;
Richard Purdie's avatar
Richard Purdie committed
136 137 138 139 140
}
EXPORT_SYMBOL_GPL(led_classdev_register);

/**
 * led_classdev_unregister - unregisters a object of led_properties class.
141
 * @led_cdev: the led device to unregister
Richard Purdie's avatar
Richard Purdie committed
142 143 144 145 146
 *
 * Unregisters a previously registered via led_classdev_register object.
 */
void led_classdev_unregister(struct led_classdev *led_cdev)
{
147
	device_remove_file(led_cdev->dev, &dev_attr_brightness);
148
#ifdef CONFIG_LEDS_TRIGGERS
149
	device_remove_file(led_cdev->dev, &dev_attr_trigger);
150 151 152 153 154
	write_lock(&led_cdev->trigger_lock);
	if (led_cdev->trigger)
		led_trigger_set(led_cdev, NULL);
	write_unlock(&led_cdev->trigger_lock);
#endif
Richard Purdie's avatar
Richard Purdie committed
155

156
	device_unregister(led_cdev->dev);
Richard Purdie's avatar
Richard Purdie committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

	write_lock(&leds_list_lock);
	list_del(&led_cdev->node);
	write_unlock(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);

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