From: Richard Purdie Add the foundations of a new LEDs subsystem. This patch adds a class which presents LED devices within sysfs and allows their brightness to be controlled. Signed-off-by: Richard Purdie Cc: Russell King Cc: Greg KH Signed-off-by: Andrew Morton --- arch/arm/Kconfig | 2 drivers/Kconfig | 2 drivers/Makefile | 1 drivers/leds/Kconfig | 18 ++++ drivers/leds/Makefile | 4 drivers/leds/led-class.c | 154 +++++++++++++++++++++++++++++++++++++ drivers/leds/led-core.c | 25 ++++++ drivers/leds/leds.h | 32 +++++++ include/linux/leds.h | 54 ++++++++++++ 9 files changed, 292 insertions(+) diff -puN arch/arm/Kconfig~led-add-led-class arch/arm/Kconfig --- devel/arch/arm/Kconfig~led-add-led-class 2006-02-23 19:41:31.000000000 -0800 +++ devel-akpm/arch/arm/Kconfig 2006-02-23 19:41:31.000000000 -0800 @@ -807,6 +807,8 @@ source "drivers/misc/Kconfig" source "drivers/mfd/Kconfig" +source "drivers/leds/Kconfig" + source "drivers/media/Kconfig" source "drivers/video/Kconfig" diff -puN drivers/Kconfig~led-add-led-class drivers/Kconfig --- devel/drivers/Kconfig~led-add-led-class 2006-02-23 19:41:31.000000000 -0800 +++ devel-akpm/drivers/Kconfig 2006-02-23 19:41:31.000000000 -0800 @@ -64,6 +64,8 @@ source "drivers/usb/Kconfig" source "drivers/mmc/Kconfig" +source "drivers/leds/Kconfig" + source "drivers/infiniband/Kconfig" source "drivers/sn/Kconfig" diff -puN /dev/null drivers/leds/Kconfig --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/leds/Kconfig 2006-02-23 19:41:31.000000000 -0800 @@ -0,0 +1,18 @@ + +menu "LED devices" + +config NEW_LEDS + bool "LED Support" + help + Say Y to enable Linux LED support. This is not related to standard + keyboard LEDs which are controlled via the input system. + +config LEDS_CLASS + tristate "LED Class Support" + depends NEW_LEDS + help + This option enables the led sysfs class in /sys/class/leds. You'll + need this to do anything useful with LEDs. If unsure, say N. + +endmenu + diff -puN /dev/null drivers/leds/led-class.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/leds/led-class.c 2006-02-23 19:41:31.000000000 -0800 @@ -0,0 +1,154 @@ +/* + * LED Class Core + * + * Copyright (C) 2005 John Lenz + * Copyright (C) 2005-2006 Richard Purdie + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); + + if (after - buf > 0) { + ret = after - buf; + write_lock(&led_cdev->lock); + led_set_brightness(led_cdev, state); + write_unlock(&led_cdev->lock); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show, + led_brightness_store); + +/** + * led_classdev_suspend - suspend an led_classdev. + * @led_cdev: the led_classdev to suspend. + */ +void led_classdev_suspend(struct led_classdev *led_cdev) +{ + write_lock(&led_cdev->lock); + led_cdev->flags |= LED_SUSPENDED; + led_cdev->brightness_set(led_cdev, 0); + write_unlock(&led_cdev->lock); +} +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) +{ + write_lock(&led_cdev->lock); + led_cdev->flags &= ~LED_SUSPENDED; + led_cdev->brightness_set(led_cdev, led_cdev->brightness); + write_unlock(&led_cdev->lock); +} +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) +{ + 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); + + rwlock_init(&led_cdev->lock); + class_set_devdata(led_cdev->class_dev, led_cdev); + + /* register the attributes */ + class_device_create_file(led_cdev->class_dev, + &class_device_attr_brightness); + + /* add to the list of leds */ + write_lock(&leds_list_lock); + list_add_tail(&led_cdev->node, &leds_list); + write_unlock(&leds_list_lock); + + printk(KERN_INFO "Registered led device: %s\n", + led_cdev->class_dev->class_id); + + return 0; +} +EXPORT_SYMBOL_GPL(led_classdev_register); + +/** + * led_classdev_unregister - unregisters a object of led_properties class. + * @led_cdev: the led device to unreigister + * + * 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); + + 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"); diff -puN /dev/null drivers/leds/led-core.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/leds/led-core.c 2006-02-23 19:41:31.000000000 -0800 @@ -0,0 +1,25 @@ +/* + * LED Class Core + * + * Copyright 2005-2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * 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 +#include +#include +#include +#include +#include "leds.h" + +rwlock_t leds_list_lock = RW_LOCK_UNLOCKED; +LIST_HEAD(leds_list); + +EXPORT_SYMBOL_GPL(leds_list); +EXPORT_SYMBOL_GPL(leds_list_lock); diff -puN /dev/null drivers/leds/leds.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/leds/leds.h 2006-02-23 19:41:31.000000000 -0800 @@ -0,0 +1,32 @@ +/* + * LED Core + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * 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. + * + */ +#ifndef __LEDS_H_INCLUDED +#define __LEDS_H_INCLUDED + +#include + +/* led_cdev->lock must be held as write */ +static inline void led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value > LED_FULL) + value = LED_FULL; + led_cdev->brightness = value; + if (!(led_cdev->flags & LED_SUSPENDED)) + led_cdev->brightness_set(led_cdev, value); +} + +extern rwlock_t leds_list_lock; +extern struct list_head leds_list; + +#endif /* __LEDS_H_INCLUDED */ diff -puN /dev/null drivers/leds/Makefile --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/leds/Makefile 2006-02-23 19:41:31.000000000 -0800 @@ -0,0 +1,4 @@ + +# LED Core +obj-$(CONFIG_NEW_LEDS) += led-core.o +obj-$(CONFIG_LEDS_CLASS) += led-class.o diff -puN drivers/Makefile~led-add-led-class drivers/Makefile --- devel/drivers/Makefile~led-add-led-class 2006-02-23 19:41:31.000000000 -0800 +++ devel-akpm/drivers/Makefile 2006-02-23 19:41:31.000000000 -0800 @@ -66,6 +66,7 @@ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_MMC) += mmc/ +obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ diff -puN /dev/null include/linux/leds.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/include/linux/leds.h 2006-02-23 19:41:31.000000000 -0800 @@ -0,0 +1,54 @@ +/* + * Driver model for leds and led triggers + * + * Copyright (C) 2005 John Lenz + * Copyright (C) 2005 Richard Purdie + * + * 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. + * + */ +#ifndef __LINUX_LEDS_H_INCLUDED +#define __LINUX_LEDS_H_INCLUDED + +struct device; +struct class_device; +/* + * LED Core + */ + +enum led_brightness { + LED_OFF = 0, + LED_HALF = 127, + LED_FULL = 255, +}; + +struct led_classdev { + const char *name; + int brightness; + int flags; +#define LED_SUSPENDED (1 << 0) + + /* A function to set the brightness of the led */ + void (*brightness_set)(struct led_classdev *led_cdev, + enum led_brightness brightness); + + struct class_device *class_dev; + /* LED Device linked list */ + struct list_head node; + + /* Trigger data */ + char *default_trigger; + + /* This protects the data in this structure */ + rwlock_t lock; +}; + +extern int led_classdev_register(struct device *parent, + struct led_classdev *led_cdev); +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); + +#endif /* __LINUX_LEDS_H_INCLUDED */ _