led-class.c 4.6 KB
Newer Older
R
Richard Purdie 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * LED Class Core
 *
 * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
 * Copyright (C) 2005-2006 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>
#include <linux/err.h>
21
#include <linux/ctype.h>
R
Richard Purdie 已提交
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#include <linux/leds.h>
#include "leds.h"

static struct class *leds_class;

static ssize_t led_brightness_show(struct class_device *dev, char *buf)
{
	struct led_classdev *led_cdev = class_get_devdata(dev);
	ssize_t ret = 0;

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

	return ret;
}

static ssize_t led_brightness_store(struct class_device *dev,
				const char *buf, size_t size)
{
	struct led_classdev *led_cdev = class_get_devdata(dev);
	ssize_t ret = -EINVAL;
	char *after;
	unsigned long state = simple_strtoul(buf, &after, 10);
46
	size_t count = after - buf;
R
Richard Purdie 已提交
47

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

	if (count == size) {
		ret = count;
R
Richard Purdie 已提交
53 54 55 56 57 58 59 60
		led_set_brightness(led_cdev, state);
	}

	return ret;
}

static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show,
			led_brightness_store);
61 62 63
#ifdef CONFIG_LEDS_TRIGGERS
static CLASS_DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
#endif
R
Richard Purdie 已提交
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;

R
Richard Purdie 已提交
96 97 98 99 100 101 102 103
	led_cdev->class_dev = class_device_create(leds_class, NULL, 0,
						parent, "%s", led_cdev->name);
	if (unlikely(IS_ERR(led_cdev->class_dev)))
		return PTR_ERR(led_cdev->class_dev);

	class_set_devdata(led_cdev->class_dev, led_cdev);

	/* register the attributes */
104 105 106 107
	rc = class_device_create_file(led_cdev->class_dev,
				      &class_device_attr_brightness);
	if (rc)
		goto err_out;
R
Richard Purdie 已提交
108 109 110 111 112 113

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

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

117 118 119 120
	rc = class_device_create_file(led_cdev->class_dev,
				      &class_device_attr_trigger);
	if (rc)
		goto err_out_led_list;
121

122
	led_trigger_set_default(led_cdev);
123 124
#endif

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

	return 0;
129 130 131 132 133 134 135 136 137 138

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
	class_device_remove_file(led_cdev->class_dev,
				&class_device_attr_brightness);
	list_del(&led_cdev->node);
#endif
err_out:
	class_device_unregister(led_cdev->class_dev);
	return rc;
R
Richard Purdie 已提交
139 140 141 142 143
}
EXPORT_SYMBOL_GPL(led_classdev_register);

/**
 * 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 148 149 150 151
 *
 * Unregisters a previously registered via led_classdev_register object.
 */
void led_classdev_unregister(struct led_classdev *led_cdev)
{
	class_device_remove_file(led_cdev->class_dev,
				&class_device_attr_brightness);
152 153 154 155 156 157 158 159
#ifdef CONFIG_LEDS_TRIGGERS
	class_device_remove_file(led_cdev->class_dev,
				&class_device_attr_trigger);
	write_lock(&led_cdev->trigger_lock);
	if (led_cdev->trigger)
		led_trigger_set(led_cdev, NULL);
	write_unlock(&led_cdev->trigger_lock);
#endif
R
Richard Purdie 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

	class_device_unregister(led_cdev->class_dev);

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