
#
# Patch managed by http://www.holgerschurig.de/patcher.html
#

--- /dev/null
+++ linux/drivers/leds/ledscore.c
@@ -0,0 +1,293 @@
+/*
+ * linux/drivers/leds/ledscore.c
+ *
+ *   Copyright (C) 2004 John Lenz <lenz@cs.wisc.edu>
+ *
+ * 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/config.h>
+#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/leds.h>
+
+struct led_device {
+	/* This protects the props field.*/
+	spinlock_t lock;
+	/* If props is NULL, the driver that registered this device has been unloaded */
+	struct led_properties *props;
+	struct class_device class_dev;
+	struct timer_list *ktimer;
+	struct list_head list;
+};
+
+#define to_led_device(d) container_of(d, struct led_device, class_dev)
+
+static rwlock_t leds_list_lock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(leds_list);
+static atomic_t leds_count = ATOMIC_INIT(0);
+
+static void leds_class_release(struct class_device *dev)
+{
+	struct led_device *d = to_led_device(dev);
+
+	write_lock(&leds_list_lock);
+		list_del(&d->list);
+	write_unlock(&leds_list_lock);
+	
+	kfree(d);
+}
+
+static struct class leds_class = {
+	.name		= "leds",
+	.release	= leds_class_release,
+};
+
+static void leds_timer_function(unsigned long data)
+{
+	struct led_device *led_dev = (struct led_device *) data;
+	unsigned long delay = 0;
+	
+	spin_lock(&led_dev->lock);
+	if (led_dev->props->heartbeat) {
+		delay = led_dev->props->heartbeat;
+		led_dev->props->light_state = !led_dev->props->light_state;
+		if (led_dev->props->light)
+			led_dev->props->light(led_dev->class_dev.dev, led_dev->props);
+	}
+	spin_unlock(&led_dev->lock);
+
+	if (delay)
+		mod_timer(led_dev->ktimer, jiffies + msecs_to_jiffies(delay));
+}
+
+static ssize_t leds_show_color(struct class_device *dev, char *buf)
+{
+	struct led_device *led_dev = to_led_device(dev);
+	ssize_t ret = 0;
+	
+	spin_lock(&led_dev->lock);
+	if (likely(led_dev->props)) {
+		sprintf(buf, "%s\n", led_dev->props->color);
+		ret = strlen(buf) + 1;
+	}
+	spin_unlock(&led_dev->lock);
+
+	return ret;
+}
+
+static CLASS_DEVICE_ATTR(color, 0444, leds_show_color, NULL);
+
+static ssize_t leds_show_light(struct class_device *dev, char *buf)
+{
+	struct led_device *led_dev = to_led_device(dev);
+	ssize_t ret = 0;
+
+	spin_lock(&led_dev->lock);
+	if (likely(led_dev->props)) {
+		sprintf(buf, "%lu\n", led_dev->props->light_state);
+		ret = strlen(buf) + 1;
+	}
+	spin_unlock(&led_dev->lock);
+
+	return ret;
+}
+
+static ssize_t leds_store_light(struct class_device *dev, const char *buf, size_t size)
+{
+	struct led_device *led_dev = to_led_device(dev);
+	int ret = -EINVAL;
+	char *after;
+
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	if (after - buf > 0) {
+		ret = after - buf;
+		spin_lock(&led_dev->lock);
+		if (likely(led_dev->props)) {
+			if (!led_dev->props->heartbeat) {
+				led_dev->props->light_state = state;
+				if (led_dev->props->light) 
+					led_dev->props->light(led_dev->class_dev.dev, led_dev->props);
+			}
+		}
+		spin_unlock(&led_dev->lock);
+	}
+
+	return ret;
+}
+
+static CLASS_DEVICE_ATTR(light, 0644, leds_show_light, leds_store_light);
+
+static ssize_t leds_show_heartbeat(struct class_device *dev, char *buf)
+{
+	struct led_device *led_dev = to_led_device(dev);
+	ssize_t ret = 0;
+
+	spin_lock(&led_dev->lock);
+	if (likely(led_dev->props)) {
+		sprintf(buf, "%lu\n", led_dev->props->heartbeat);
+		ret = strlen(buf) + 1;
+	}
+	spin_unlock(&led_dev->lock);
+
+	return ret;
+}
+
+static ssize_t leds_store_heartbeat(struct class_device *dev, const char *buf, size_t size)
+{
+	struct led_device *led_dev = to_led_device(dev);
+	int ret = -EINVAL;
+	char *after;
+
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	if (after - buf > 0) {
+		ret = after - buf;
+		spin_lock(&led_dev->lock);
+		if (likely(led_dev->props)) {
+			led_dev->props->heartbeat = state;
+			if (led_dev->props->heartbeat && led_dev->ktimer) {
+				/* timer already created, just enable it */
+				mod_timer(led_dev->ktimer, jiffies + msecs_to_jiffies(led_dev->props->heartbeat));
+			} else if (led_dev->props->heartbeat && led_dev->ktimer == NULL) {
+				/* create a new timer */
+				led_dev->ktimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
+				if (led_dev->ktimer) {
+					init_timer(led_dev->ktimer);
+					led_dev->ktimer->function = leds_timer_function;
+					led_dev->ktimer->data = (unsigned long) led_dev;
+					led_dev->ktimer->expires = jiffies + msecs_to_jiffies(led_dev->props->heartbeat);
+					add_timer(led_dev->ktimer);
+				} else {
+					led_dev->props->heartbeat = 0;
+					ret = -ENOMEM;
+				}
+			}
+		}
+		spin_unlock(&led_dev->lock);
+	}
+
+	return ret;
+}
+
+static CLASS_DEVICE_ATTR(heartbeat, 0644, leds_show_heartbeat, leds_store_heartbeat);
+
+/**
+ * leds_device_register - register a new object of led_device class.
+ * @dev: The device to register.
+ * @prop: the led properties structure for this device.
+ */
+int leds_device_register(struct device *dev, struct led_properties *props)
+{
+	int rc, num;
+	struct led_device *new_led;
+
+	new_led = kmalloc (sizeof (struct led_device), GFP_KERNEL);
+	if (unlikely (!new_led))
+		return -ENOMEM;
+
+	memset(new_led, 0, sizeof(struct led_device));
+
+	spin_lock_init(&new_led->lock);
+	new_led->props = props;
+	props->led_dev = new_led;
+
+	new_led->class_dev.class = &leds_class;
+	new_led->class_dev.dev = dev;
+
+	/* assign this led a number */
+	num = atomic_add_return(1, &leds_count);
+	sprintf(new_led->class_dev.class_id, "%i", num);
+
+	if (props->heartbeat) {
+		/* create a new timer */
+		new_led->ktimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
+		if (new_led->ktimer) {
+			init_timer(new_led->ktimer);
+			new_led->ktimer->function = leds_timer_function;
+			new_led->ktimer->data = (unsigned long) new_led;
+			new_led->ktimer->expires = jiffies + msecs_to_jiffies(props->heartbeat);
+			add_timer(new_led->ktimer);
+		}
+	}
+
+	rc = class_device_register (&new_led->class_dev);
+	if (unlikely (rc)) {
+		kfree (new_led);
+		return rc;
+	}
+
+	/* register the attributes */
+	class_device_create_file(&new_led->class_dev, &class_device_attr_color);
+	class_device_create_file(&new_led->class_dev, &class_device_attr_light);
+	class_device_create_file(&new_led->class_dev, &class_device_attr_heartbeat);
+
+	/* add to the list of leds */
+	write_lock(&leds_list_lock);
+		list_add_tail(&new_led->list, &leds_list);
+	write_unlock(&leds_list_lock);
+
+	printk(KERN_INFO "Registered led device: number=%s, color=%s\n", new_led->class_dev.class_id, props->color);
+
+	return 0;
+}
+EXPORT_SYMBOL(leds_device_register);
+
+/**
+ * leds_device_unregister - unregisters a object of led_properties class.
+ * @props: the property to unreigister
+ *
+ * Unregisters a previously registered via leds_device_register object.
+ */
+void leds_device_unregister(struct led_properties *props)
+{
+	struct led_device *led_dev;
+	if (!props || !props->led_dev)
+		return;
+
+	led_dev = props->led_dev;
+
+	class_device_remove_file (&led_dev->class_dev, &class_device_attr_heartbeat);
+	class_device_remove_file (&led_dev->class_dev, &class_device_attr_light);
+	class_device_remove_file (&led_dev->class_dev, &class_device_attr_color);
+
+	spin_lock(&led_dev->lock);
+	led_dev->props = NULL;
+	props->led_dev = NULL;
+	props->heartbeat = 0;
+	spin_unlock(&led_dev->lock);
+
+	if (led_dev->ktimer) {
+		del_timer_sync(led_dev->ktimer);
+		kfree(led_dev->ktimer);
+		led_dev->ktimer = NULL;
+	}
+
+	class_device_unregister(&led_dev->class_dev);
+}
+EXPORT_SYMBOL(leds_device_unregister);
+
+static int __init leds_init(void)
+{
+	/* initialize the class device */
+	return class_register(&leds_class);
+}
+subsys_initcall(leds_init);
+
+static void __exit leds_exit(void)
+{
+	class_unregister(&leds_class);
+}
+module_exit(leds_exit);
+
+MODULE_AUTHOR("John Lenz");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED core class interface");
+
--- /dev/null
+++ linux/include/linux/leds.h
@@ -0,0 +1,46 @@
+/*
+ *  linux/include/leds.h
+ *
+ *  Copyright (C) 2004 John Lenz <lenz@cs.wisc.edu>
+ *
+ * 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.
+ *
+ * Driver model for leds
+ */
+#ifndef ASM_ARM_LEDS_H
+#define ASM_ARM_LEDS_H
+
+#include <linux/device.h>
+
+struct led_device;
+
+struct led_properties {
+	struct module *owner;
+	
+	/* Color of the led.  For multiple color leds, the color names should
+	 * be seperated by a "/".  For example, "amber/green".
+	 */
+	char *color;
+	
+	/* current state of this led.
+	 * 0 = off, 1,2,3,... = on, where the number is the posision in the list
+	 * of colors given above.
+	 */
+	unsigned long light_state;
+
+	/* heartbeat time in milliseconds of this led */
+	unsigned long heartbeat;
+
+	/* This function is called after the light_state property is changed. */
+	void (*light)(struct device *, struct led_properties *props);
+	
+	/* private structure */
+	struct led_device *led_dev;
+};
+
+int leds_device_register(struct device *dev, struct led_properties *props);
+void leds_device_unregister(struct led_properties *props);
+
+#endif
--- /dev/null
+++ linux/drivers/leds/Kconfig
@@ -0,0 +1,11 @@
+
+menu "LED devices"
+
+config CLASS_LEDS
+	tristate "LED support"
+	help
+	  This option provides the generic support for the leds class.
+	  LEDs can be accessed from /sys/class/leds.  It will also allow you
+	  to select individual drivers for LED devices.  If unsure, say N.
+
+endmenu
--- /dev/null
+++ linux/drivers/leds/Makefile
@@ -0,0 +1,3 @@
+
+# Core functionality.
+obj-$(CONFIG_CLASS_LEDS)		+= ledscore.o
--- linux/drivers/Kconfig~leds_sysfs
+++ linux/drivers/Kconfig
@@ -48,6 +48,8 @@
 
 source "drivers/media/Kconfig"
 
+source "drivers/leds/Kconfig"
+
 source "drivers/video/Kconfig"
 
 source "sound/Kconfig"

