new class for led devices
Subsystems (2)
PATCH 1/1 new class for led devices
This is an attempt to provide an alternative to the current arm specific led interface that is generic for all arches and uses the "one value, one file" idea of sysfs.
I removed the function attribute that was in the previous patch, and added the ability for userspace to control the timer on each led individually. Userspace can also set the delay in milliseconds for the blink.
John
A new class that drivers can register a leds_properties structure. Each led that is registered is assigned a number, and three attributes are exported to sysfs in /sys/class/leds/1/, /sys/class/ leds/2, etc.
- color: a read only attribute which contains the color of this led. If this led is a multi color led, the possible colors will be seperated by a "/" (ex. "red/green").
- heartbeat: a read/write attribute that controls the heartbeat of this led. If heartbeat=0, then this led is controlled by userspace. Otherwise, heartbeat gives the time in milliseconds to delay between light changes.
- light: a read/write attribute that controls the actual light. If heartbeat <> 0, then writing to this attribute is ignored. If heartbeat = 0, then writing an integer to this attribute will turn the led off or on. 0 means off, 1 means light the first color, 2 means light the second color, etc. (2,3,etc only make sense if this is a multi color led, which you can tell from color="red/green"). Reading from this attribute will display the current status of the led, regardless of heartbeat.
drivers/leds/ledscore.c new file +293 -0
@@ -0,0 +1,293 @@ +/* +* linux/drivers/leds/ledscore.c +* +* Copyright (C) 2004 John Lenz <[email protected]> +* +* 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" ); +
include/linux/leds.h new file +46 -0
46 lines not on this page · raw message
drivers/leds/Kconfig new file +11 -0
@@ -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
drivers/leds/Makefile new file +3 -0
@@ -0,0 +1,3 @@ + +# Core functionality. +obj-$(CONFIG_CLASS_LEDS) += ledscore.o
drivers/Kconfig renamed from drivers/Kconfig~leds_sysfs +2 -0
@@ -48,6 +48,8 @@ source "drivers/media/Kconfig" +source "drivers/leds/Kconfig" + source "drivers/video/Kconfig" source "sound/Kconfig"
drivers/Makefile renamed from drivers/Makefile~leds_sysfs +1 -0
@@ -50,4 +50,5 @@ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ +obj-$(CONFIG_CLASS_LEDS) += leds/ obj-y += firmware/
Quoted 8 lines · John Lenz
This is an attempt to provide an alternative to the current arm specific led interface that is generic for all arches and uses the "one value, one file" idea of sysfs.
I removed the function attribute that was in the previous patch, and added the ability for userspace to control the timer on each led individually. Userspace can also set the delay in milliseconds for the blink.
Well, we already have an interface for setting LEDs through the input layer, it'd be trivial to create an input device driver with just LEDs and no buttons/keys ...
Signature · 2 lines
Vojtech Pavlik SuSE Labs, SuSE CR
Quoted 5 lines · Vojtech Pavlik
Well, we already have an interface for setting LEDs through the input layer, it'd be trivial to create an input device driver with just LEDs and no buttons/keys ...
It's not really a nice fit with what we are trying to do. In the input layer, there is a whole list of led types, none of which make sense... For example, on the Sharp Zaurus, we have two leds, one green, one amber. Which one is LED_NUML? We don't enforce anything on the policy userspace has for the leds, sometimes it might use the amber led to let the user know they have new mail, and sometimes to show the power is plugged in, sometimes for something else (maybe even that caps lock or numlock is on).
Secondly, there is no good way through the input layer to query which leds and what colors are present. I guess you can look at /proc/bus/input/devices and if we agreed on a common name format or something. If you look around in arch/arm/mach-sa1100 and arch/arm/mach-pxa you can see we already have around 15 led devices, each one of them different. Some support a green and a red led, some only a red led, some an amber and green, some control a single led that can be multiple colors, etc. Userspace needs a way to ask which leds are available and which color will be turned on by which codes.
We could solve these problems in the input layer by adding some more information into /sys/class/input/. If we could add an attribute saying, this is a led (or better yet, returning the info found in /proc/bus/input/devices for this device), and moreover an attribute specifying the mapping between led code numbers and a description (be it a color or numlock or whatever), then userspace would not need to use the LED_NUML symbols from input.h and we wouldn't need to add entries into that enumeration for every possible led type in the future.
Userspace would look in /sys/class/input/event2/mapping or whatever and realize there are two entries, 0 = green, 1 = amber or something. Then when writing to /dev/input/event2 userspace fills in 0 in input_event.code to change the green and 1 to change the amber.
It's still not as intuitive as going /sys/class/leds and having two directories, one for each led, a color attribute to see color and a light attribute to toggle the led, but in the interest of using already available interfaces I could scrap it and use the input layer.
John
Hi!
Quoted 6 lines
Quoted 5 lines
Well, we already have an interface for setting LEDs through the input layer, it'd be trivial to create an input device driver with just LEDs
and no buttons/keys ...
Quoted 8 lines
It's not really a nice fit with what we are trying to do. In the input layer, there is a whole list of led types, none of which make sense... For example, on the Sharp Zaurus, we have two leds, one green, one amber. Which one is
LED_NUML? We don't enforce anything on the policy userspace has for the leds, sometimes it might use the amber led to let the user know they have new mail, and sometimes to show the power is plugged in, sometimes for something else (maybe even that caps lock or numlock is on).
Actually on zaurus one led is labeled "CHARGING" and second is labeled "MAIL". There are PC keyboards with "MAIL" led already, I believe... It does not seem to be that bad fit. I do not think you want to label leds by colors, machine may well have three green leds (see normal pc keyboard). And on most machines you do not even know what color the leds are (new notebooks like blue leds :-().
So right solution seems to be adding LED_MAIL and LED_CHARGING and be done with that...
Pavel
Signature · 2 lines
People were complaining that M$ turns users into beta-testers... ...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!
Quoted 16 lines · Pavel Machek
Hi!
Quoted 6 lines
Quoted 4 lines
Well, we already have an interface for setting LEDs through the input layer, it'd be trivial to create an input device driver with just LEDs and no buttons/keys ...
It's not really a nice fit with what we are trying to do. In the input layer, there is a whole list of led types, none of which make sense... For example, on the Sharp Zaurus, we have two leds, one green, one amber. Which one is
LED_NUML? We don't enforce anything on the policy userspace has for the leds, sometimes it might use the amber led to let the user know they have new mail, and sometimes to show the power is plugged in, sometimes for something else (maybe even that caps lock or numlock is on).Actually on zaurus one led is labeled "CHARGING" and second is labeled "MAIL". There are PC keyboards with "MAIL" led already, I believe... It does not seem to be that bad fit. I do not think you want to label leds by colors, machine may well have three green leds (see normal pc keyboard). And on most machines you do not even know what color the leds are (new notebooks like blue leds :-().
Quoted 2 lines
So right solution seems to be adding
LED_MAILandLED_CHARGINGand be done with that...
Yeah, that would work. And if userspace wants to use the led for something else, just uses MAIL and CHARGING as the names of the leds.
include/linux/input.h renamed from include/linux/input.h~input +0 -0
@@ -542,6 +542,8 @@
#define LED_SUSPEND 0x06
#define LED_MUTE 0x07
#define LED_MISC 0x08
+#define LED_MAIL 0x09
+#define LED_CHARGING 0x0a
#define LED_MAX 0x0f
/*
Hi!
Quoted 22 lines
Quoted 22 lines
Quoted 6 lines
Quoted 5 lines
Well, we already have an interface for setting LEDs through the input layer, it'd be trivial to create an input device driver with just LEDs
and no buttons/keys ...Quoted 8 lines
It's not really a nice fit with what we are trying to do. In the input layer, there is a whole list of led types, none of which make sense... For example, on the Sharp Zaurus, we have two leds, one green, one amber. Which one is
LED_NUML? We don't enforce anything on the policy userspace has for the leds, sometimes it might use the amber led to let the user know they have new mail, and sometimes to show the power is plugged in, sometimes for something else (maybe even that caps lock or numlock is on).Actually on zaurus one led is labeled "CHARGING" and second is labeled "MAIL". There are PC keyboards with "MAIL" led already, I believe... It does not seem to be that bad fit. I do not think you want to label leds by colors, machine may well have three green leds (see normal pc keyboard). And on most machines you do not even know what color the leds are (new notebooks like blue leds :-().
Quoted 5 lines
Quoted 2 lines
So right solution seems to be adding
LED_MAILandLED_CHARGINGand be done with that...Yeah, that would work. And if userspace wants to use the led for something else, just uses MAIL and CHARGING as the names of the leds.
Looks good to me.
There's LED_MAX defined to be 0xf. Can we support more than 16 leds?
Pavel
Signature · 2 lines
People were complaining that M$ turns users into beta-testers... ...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!
Quoted 27 lines · Pavel Machek
Hi!
Quoted 25 lines
Quoted 24 lines
Quoted 6 lines
Quoted 5 lines
Well, we already have an interface for setting LEDs through the input layer, it'd be trivial to create an input device driver with just LEDs
and no buttons/keys ...Quoted 8 lines
It's not really a nice fit with what we are trying to do. In the input layer, there is a whole list of led types, none of which make sense... For example, on the Sharp Zaurus, we have two leds, one green, one amber. Which one is
LED_NUML? We don't enforce anything on the policy userspace has for the leds, sometimes it might use the amber led to let the user know they have new mail, and sometimes to show the power is plugged in, sometimes for something else (maybe even that caps lock or numlock is on).Actually on zaurus one led is labeled "CHARGING" and second is labeled "MAIL". There are PC keyboards with "MAIL" led already, I believe... It does not seem to be that bad fit. I do not think you want to label leds by colors, machine may well have three green leds (see normal pc keyboard). And on most machines you do not even know what color the leds are (new notebooks like blue leds :-().
So right solution seems to be adding
LED_MAILandLED_CHARGINGand be done with that...
Quoted 6 lines
Quoted 2 lines
Yeah, that would work. And if userspace wants to use the led for something else, just uses MAIL and CHARGING as the names of the leds.
Looks good to me.
There's
LED_MAXdefined to be0xf. Can we support more than 16 leds?
It's a constant that can be changed without breaking anything. Old apps will keep working (but will only have access to the first 16), recompiled apps will have access to all. So I think we can leave it as it is for now and when need arises we can change it.
Signature · 2 lines
Vojtech Pavlik SuSE Labs, SuSE CR
Quoted 11 lines · John Lenz
Quoted 2 lines
So right solution seems to be adding
LED_MAILandLED_CHARGINGand be done with that...Yeah, that would work. And if userspace wants to use the led for something else, just uses MAIL and CHARGING as the names of the leds.
Signed-off-by John Lenz <[email protected]>
include/linux/input.h line 547
Quoted 6 lines
include/linux/input.h renamed from include/linux/input.h~input +2
-0@@ -542,6 +542,8 @@#define LED_SUSPEND 0x06#define LED_MUTE 0x07#define LED_MISC 0x08 +#define LED_MAIL 0x09 +#define LED_CHARGING 0x0a#define LED_MAX 0x0f
Thanks, applied. Now where is the driver for those Zaurus LEDs?
Signature · 2 lines
Vojtech Pavlik SuSE Labs, SuSE CR
Quoted 7 lines · John Lenz
This is an attempt to provide an alternative to the current arm specific led interface that is generic for all arches and uses the "one value, one file" idea of sysfs.
I removed the function attribute that was in the previous patch, and added the ability for userspace to control the timer on each led individually. Userspace can also set the delay in milliseconds for the blink.
(damned, non-inlined patch)
| - heartbeat: a read/write attribute that controls the heartbeat of this | led. If heartbeat=0, then this led is controlled by userspace. | Otherwise, heartbeat gives the time in milliseconds to delay between | light changes.
That's not the real heartbeat! The real one says thumb-thumb-pause, and goes faster if the load average increases :-)
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds