Index: linux-2.6.12/drivers/leds/Kconfig =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.12/drivers/leds/Kconfig 2005-07-17 14:56:15.213108615 -0500 @@ -0,0 +1,19 @@ + +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. + +config LEDS_LOCOMO + tristate "LED Support for Locomo device" + depends CLASS_LEDS && SHARP_LOCOMO + help + This option enables support for the LEDs on Sharp Locomo. + Zaurus models SL-5500 and SL-5600. + +endmenu + Index: linux-2.6.12/drivers/leds/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.12/drivers/leds/Makefile 2005-07-17 14:56:15.263100023 -0500 @@ -0,0 +1,4 @@ + +# Core functionality. +obj-$(CONFIG_CLASS_LEDS) += ledscore.o +obj-$(CONFIG_LEDS_LOCOMO) += locomo.o Index: linux-2.6.12/drivers/leds/ledscore.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.12/drivers/leds/ledscore.c 2005-07-17 15:51:27.157190247 -0500 @@ -0,0 +1,558 @@ +/* + * linux/drivers/leds/ledscore.c + * + * Copyright (C) 2005 John Lenz + * + * 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 + +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; + + unsigned long ticks_on; /* number of frequency ticks the led is on */ + unsigned long ticks_off; /* number of frequency ticks the led is off */ + int in_use; + + spinlock_t timer_lock; /* protects the timer, frequency, and tick_count */ + struct timer_list *ktimer; + unsigned long frequency; /* frequency of blinking, in milliseconds */ + unsigned long tick_count; + + struct class_device class_dev; + struct list_head node; +}; + +#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 rwlock_t leds_interface_list_lock = RW_LOCK_UNLOCKED; +static LIST_HEAD(leds_interface_list); + +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->node); + 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; + int new_state = -1; + + spin_lock(&led_dev->lock); + spin_lock(&led_dev->timer_lock); + if (likely(led_dev->props->state_get)) { + if (led_dev->frequency) { + delay = led_dev->frequency; + if (led_dev->props->state_get(led_dev->class_dev.dev, led_dev->props)) { + /* led is on */ + if (led_dev->ticks_on > 0) { + led_dev->tick_count++; + if (led_dev->tick_count >= led_dev->ticks_on) + new_state = 0; + } else { + new_state = 0; + } + } else { + /* led is off */ + if (led_dev->ticks_off > 0) { + led_dev->tick_count++; + if (led_dev->tick_count >= led_dev->ticks_off) + new_state = 1; + } else { + new_state = 1; + } + } + if (likely(led_dev->props->state_set) && new_state >= 0) { + led_dev->props->state_set(led_dev>class_dev.dev, led_dev->props, new_state); + } + } + } + spin_unlock(&led_dev->timer_lock); + spin_unlock(&led_dev->lock); + + if (delay) + mod_timer(led_dev->ktimer, jiffies + msecs_to_jiffies(delay)); +} + +static int leds_default_blink_frequency_get(struct device *dev, struct led_properties *props) +{ + int ret; + spin_lock(&props->led_dev->timer_lock); + ret = props->led_dev->frequency; + spin_unlock(&props->led_dev->timer_lock); + return ret; +} + +static void leds_default_blink_frequency_set(struct device *dev, struct led_properties *props, int value) +{ + spin_lock(&props->led_dev->timer_lock); + + props->led_dev->frequency = value; + + if (value && props->led_dev->ktimer) { + /* timer already created, just enable it */ + mod_timer(props->led_dev->ktimer, jiffies + msecs_to_jiffies(value)); + } else if (value && props->led_dev->ktimer == NULL) { + /* create a new timer */ + props->led_dev->ktimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL); + if (props->led_dev->ktimer) { + init_timer(props->led_dev->ktimer); + props->led_dev->ktimer->function = leds_timer_function; + props->led_dev->ktimer->data = (unsigned long) props->led_dev; + props->led_dev->ktimer->expires = jiffies + msecs_to_jiffies(value); + add_timer(props->led_dev->ktimer); + } else { + props->led_dev->frequency = 0; + } + } + + spin_unlock(&props->led_dev->timer_lock); +} + +static ssize_t leds_show_in_use(struct class_device *dev, char *buf) +{ + struct led_device *led_dev = to_led_device(dev); + ssize_t ret = 0; + + spin_lock(&led_dev->lock); + sprintf(buf, "%i\n", led_dev->in_use); + ret = strlen(buf) + 1; + spin_unlock(&led_dev->lock); + + return ret; +} + +static CLASS_DEVICE_ATTR(in_use, 0444, leds_show_in_use, NULL); + +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_current_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)) { + if (led_dev->props->color_get) { + sprintf(buf, "%u\n", led_dev->props->color_get(led_dev->class_dev.dev, led_dev->props)); + ret = strlen(buf) + 1; + } + } + spin_unlock(&led_dev->lock); + + return ret; +} + +static ssize_t leds_store_current_color(struct class_device *dev, const char *buf, size_t size) +{ + struct led_device *led_dev = to_led_device(dev); + ssize_t 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 (led_dev->props && !led_dev->in_use) { + if (led_dev->props->color_set) + led_dev->props->color_set(led_dev->class_dev.dev, led_dev->props, state); + } + spin_unlock(&led_dev->lock); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(current_color, 0444, leds_show_current_color, leds_store_current_color); + +static ssize_t leds_show_brightness(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)) { + if (likely(led_dev->props->brightness_get)) { + sprintf(buf, "%u\n", + led_dev->props->brightness_get(led_dev->class_dev.dev, led_dev->props)); + ret = strlen(buf) + 1; + } + } + spin_unlock(&led_dev->lock); + + return ret; +} + +static ssize_t leds_store_brightness(struct class_device *dev, const char *buf, size_t size) +{ + struct led_device *led_dev = to_led_device(dev); + ssize_t 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 (led_dev->props && !led_dev->in_use) { + if (state > 100) state = 100; + if (led_dev->props->brightness_set) + led_dev->props->brightness_set(led_dev->class_dev.dev, led_dev->props, state); + } + spin_unlock(&led_dev->lock); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(brightness, 0644, leds_show_brightness, leds_store_brightness); + +static ssize_t leds_show_frequency(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)) { + if (led_dev->props->blink_frequency_get) + sprintf(buf, "%lu\n", + led_dev->props->blink_frequency_get(led_dev->class_dev.dev, led_dev->props)); + ret = strlen(buf) + 1; + } + spin_unlock(&led_dev->lock); + + return ret; +} + +static ssize_t leds_store_frequency(struct class_device *dev, const char *buf, size_t size) +{ + struct led_device *led_dev = to_led_device(dev); + int ret = -EINVAL, ret2; + char *after; + + unsigned long state = simple_strtoul(buf, &after, 10); + if (after - buf > 0) { + ret = after - buf; + spin_lock(&led_dev->lock); + if (led_dev->props && !led_dev->in_use) { + if (led_dev->props->blink_frequency_set) { + led_dev->props->blink_frequency_set( + led_dev->class_dev.dev, led_dev->props, state); + } + } + spin_unlock(&led_dev->lock); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(frequency, 0644, leds_show_frequency, leds_store_frequency); + +static ssize_t leds_show_ticks_on(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, "%u\n", led_dev->props->ticks_on); + ret = strlen(buf) + 1; + } + spin_unlock(&led_dev->lock); + + return ret; +} + +static ssize_t leds_store_ticks_on(struct class_device *dev, const char *buf, size_t size) +{ + struct led_device *led_dev = to_led_device(dev); + ssize_t 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 (led_dev->props && !led_dev->in_use) { + led_dev->props->ticks_on = state; + } + spin_unlock(&led_dev->lock); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(ticks_on, 0644, leds_show_ticks_on, leds_store_ticks_on); + +static ssize_t leds_show_ticks_off(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, "%u\n", led_dev->props->ticks_off); + ret = strlen(buf) + 1; + } + spin_unlock(&led_dev->lock); + + return ret; +} + +static ssize_t leds_store_ticks_off(struct class_device *dev, const char *buf, size_t size) +{ + struct led_device *led_dev = to_led_device(dev); + ssize_t 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 (led_dev->props && !led_dev->in_use) { + led_dev->props->ticks_off = state; + } + spin_unlock(&led_dev->lock); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(ticks_off, 0644, leds_show_ticks_off, leds_store_ticks_off); + + +/** + * 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; + struct led_device *new_led; + struct led_interface *interface; + + 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); + spin_lock_init(&new_led->timer_lock); + new_led->props = props; + props->led_dev = new_led; + + new_led->class_dev.class = &leds_class; + new_led->class_dev.dev = dev; + + new_led->frequency = 0; + new_led->in_use = 0; + + if (!props->blink_frequency_get || !props->blink_frequency_set) { + props->blink_frequency_get = leds_default_blink_frequency_get; + props->blink_frequency_set = leds_default_blink_frequency_set; + } + + /* assign this led its name */ + strncpy(new_led->class_dev.class_id, props->name, sizeof(new_led->class_dev.class_id)); + + 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_in_use); + class_device_create_file(&new_led->class_dev, &class_device_attr_color); + class_device_create_file(&new_led->class_dev, &class_device_attr_current_color); + class_device_create_file(&new_led->class_dev, &class_device_attr_brightness); + class_device_create_file(&new_led->class_dev, &class_device_attr_frequency); + class_device_create_file(&new_led->class_dev, &class_device_attr_ticks_on); + class_device_create_file(&new_led->class_dev, &class_device_attr_ticks_off); + + /* add to the list of leds */ + write_lock(&leds_list_lock); + list_add_tail(&new_led->node, &leds_list); + write_unlock(&leds_list_lock); + + /* notify any interfaces */ + read_lock(&leds_interface_list_lock); + list_for_each_entry(interface, &leds_interface_list, node) { + if (interface->add) + interface->add(dev, props); + } + read_unlock(&leds_interface_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; + struct led_interface *interface; + + if (!props || !props->led_dev) + return; + + led_dev = props->led_dev; + + /* notify interfaces device is going away */ + read_lock(&leds_interface_list_lock); + list_for_each_entry(interface, &leds_interface_list, node) { + if (interface->remove) + interface->remove(led_dev->class_dev.dev, props); + } + read_unlock(&leds_interface_list_lock); + + class_device_remove_file (&led_dev->class_dev, &class_device_attr_ticks_off); + class_device_remove_file (&led_dev->class_dev, &class_device_attr_ticks_on); + class_device_remove_file (&led_dev->class_dev, &class_device_attr_frequency); + class_device_remove_file (&led_dev->class_dev, &class_device_attr_brightness); + class_device_remove_file (&led_dev->class_dev, &class_device_attr_current_color); + class_device_remove_file (&led_dev->class_dev, &class_device_attr_color); + class_device_remove_file (&led_dev->class_dev, &class_device_attr_in_use); + + spin_lock(&led_dev->lock); + led_dev->props = NULL; + props->led_dev = NULL; + 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); + +struct device *leds_acquire(struct led_properties *led) +{ + struct device *ret = ERR_PTR(-EBUSY); + + spin_lock(&led->led_dev->lock); + if (!led->led_dev->in_use) { + led->led_dev->in_use = 1; + /* Disable the userspace blinking, if any */ + led->led_dev->frequency = 0; + ret = led->led_dev->class_dev.dev; + } + spin_unlock(&led->led_dev->lock); + + return ret; +} +EXPORT_SYMBOL(leds_acquire); + +void leds_release(struct led_properties *led) +{ + spin_lock(&led->led_dev->lock); + led->led_dev->in_use = 0; + /* Disable the kernel blinking, if any */ + led->led_dev->frequency = 0; + spin_unlock(&led->led_dev->lock); +} +EXPORT_SYMBOL(leds_release); + +int leds_interface_register(struct led_interface *interface) +{ + struct led_device *led_dev; + + write_lock(&leds_interface_list_lock); + list_add_tail(&interface->node, &leds_interface_list); + + read_lock(&leds_list); + list_for_each_entry(led_dev, &leds_list, node) { + spin_lock(&led_dev->lock); + if (led_dev->props) { + interface->add(led_dev->class_dev.dev, led_dev->props); + } + spin_unlock(&led_dev->lock); + } + read_unlock(&leds_list); + + write_unlock(&leds_interface_list_lock); + + return 0; +} +EXPORT_SYMBOL(leds_interface_register); + +void leds_interface_unregister(struct led_interface *interface) +{ + write_lock(&leds_interface_list_lock); + list_del(&interface->node); + write_unlock(&leds_interface_list_lock); +} +EXPORT_SYMBOL(leds_interface_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"); + Index: linux-2.6.12/drivers/leds/locomo.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.12/drivers/leds/locomo.c 2005-07-17 15:26:23.927887762 -0500 @@ -0,0 +1,129 @@ +/* + * linux/drivers/leds/locomo.c + * + * Copyright (C) 2005 John Lenz + * + * 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 + +struct locomoled_data { + unsigned long offset; + int registered; + int state; + struct led_properties props; +}; +#define to_locomoled_data(d) container_of(d, struct locomoled_data, props) + +int locomoled_state_get(struct device *dev, struct led_properties *props) +{ + struct locomoled_data *data = to_locomoled_data(props); + + return data->state; +} + +void locomoled_state_set(struct device *dev, struct led_properties *props, int value) +{ + struct locomo_dev *locomo_dev = LOCOMO_DEV(dev); + struct locomoled_data *data = to_locomoled_data(props); + + unsigned long flags; + + data->state = value; + local_irq_save(flags); + if (data->state) + locomo_writel(LOCOMO_LPT_TOFH, locomo_dev->mapbase + data->offset); + else + locomo_writel(LOCOMO_LPT_TOFL, locomo_dev->mapbase + data->offset); + local_irq_restore(flags); +} + +static struct locomoled_data leds[] = { + { + .offset = LOCOMO_LPT0, + .props = { + .owner = THIS_MODULE, + .name = "power", + .color = "amber", + .state_get = locomoled_brightness_get, + .state_set = locomoled_brightness_set, + .color_get = NULL, + .color_set = NULL, + .blink_frequency_get = NULL, + .blink_frequency_set = NULL, + .brightness_get = NULL, + .brightness_set = NULL + } + }, + { + .offset = LOCOMO_LPT1, + .props = { + .owner = THIS_MODULE, + .name = "mail", + .color = "green", + .state_get = locomoled_brightness_get, + .state_set = locomoled_brightness_set, + .color_get = NULL, + .color_set = NULL, + .blink_frequency_get = NULL, + .blink_frequency_set = NULL, + .brightness_get = NULL, + .brightness_set = NULL + } + }, +}; + +static int locomoled_probe(struct locomo_dev *dev) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(leds); i++) { + ret = leds_device_register(&dev->dev, &leds[i].props); + leds[i].registered = 1; + if (unlikely(ret)) { + printk(KERN_WARNING "Unable to register locomo led %s\n", leds[i].props.color); + leds[i].registered = 0; + } + } + + return ret; +} + +static int locomoled_remove(struct locomo_dev *dev) { + int i; + + for (i = 0; i < ARRAY_SIZE(leds); i++) { + if (leds[i].registered) { + leds_device_unregister(&leds[i].props); + } + } + return 0; +} + +static struct locomo_driver locomoled_driver = { + .drv = { + .name = "locomoled" + }, + .devid = LOCOMO_DEVID_LED, + .probe = locomoled_probe, + .remove = locomoled_remove, +}; + +static int __init locomoled_init(void) { + return locomo_driver_register(&locomoled_driver); +} +module_init(locomoled_init); + +MODULE_AUTHOR("John Lenz "); +MODULE_DESCRIPTION("Locomo LED driver"); +MODULE_LICENSE("GPL"); Index: linux-2.6.12/include/linux/leds.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.12/include/linux/leds.h 2005-07-17 15:51:23.019903584 -0500 @@ -0,0 +1,78 @@ +/* + * linux/include/leds.h + * + * Copyright (C) 2005 John Lenz + * + * 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 + +struct led_device; + +struct led_properties { + struct module *owner; + + /* Read-only name for this led */ + char *name; + + /* Color of the led. For multiple color leds, the color names should + * be seperated by a "/". For example, "amber/green". + * This is read-only. + */ + char *color; + + /* For multi-colored leds, these function are called to manipulate the + * current color. The integer value should be the position in the above + * list of colors. For a single color led, set equal to NULL. + */ + int (*color_get)(struct device *, struct led_properties *props); + void (*color_set)(struct device *, struct led_properties *props, int value); + + /* These functions manipulate the brightness of the led. For LEDs with only + * one brightness, set equal to NULL. + * Values are between 0-100 */ + int (*brightness_get)(struct device *, struct led_properties *props); + void (*brightness_set)(struct device *, struct led_properties *props, int value); + + /* These functions turn the led on and off */ + int (*state_get)(struct device *, struct led_properties *props); + void (*state_set)(struct device *, struct led_properties *props, int on); + + /* These functions manipulate the hardware blink frequency for this led. Values + * are in milliseconds. If these functions are set to NULL, the ledscore + * will blink this led using a kernel timer. Setting a value of zero disables + * the blinking. */ + unsigned long (*blink_frequency_get)(struct device *, struct led_properties *props); + void (*blink_frequency_set)(struct device *, struct led_properties *props, unsigned long value); + + /* 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); + +/* Acquires kernel access to this led device. + * Userspace access through sysfs will be denied. + * Returns the device for this led, or ERR_PTR(-EBUSY) */ +struct device *leds_acquire(struct led_properties *led); +/* Release kernel access to this led device, and re-allow userspace access */ +void leds_release(struct led_properties *led); + +struct led_interface { + int (*add)(struct device *dev, struct led_properties *led); + void (*remove)(struct device *dev, struct led_properties *led); + + struct list_head node; +}; +int leds_interface_register(struct led_interface *interface); +void leds_interface_unregister(struct led_interface *interface); + +#endif