--- ./drivers/char/led.c.led Fri Jan 25 09:36:27 2002 +++ ./drivers/char/led.c Fri Jan 25 11:03:22 2002 @@ -0,0 +1,316 @@ +/* + * Device driver for controlling LEDs. See linux/led.h for info on + * how to use this. + * + * This currently only supports mono and bicolor LEDs, but support for + * fancier LEDs (scrolling text or numeric LEDs, for instance) could + * easily be added. + * + * Corey Minyard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "1.0" + +/* A lock that is held when manipulating LED info. */ +static spinlock_t led_lock; + +/* A linked list of all the LEDs registered. */ +static struct led_reg_info *leds; + +/* Register a new LED with the driver. */ +int register_led_info(struct led_reg_info *info) +{ + struct led_reg_info *led; + int rv = 0; + + spin_lock(&led_lock); + info->next = NULL; + led = leds; + if (led == NULL) + { + leds = info; + } + else + { + while (led->next != NULL) + { + if (strcmp(led->info->name, info->info->name) == 0) + { + /* Registering a duplicate. */ + rv = -1; + goto out; + } + led = led->next; + } + led->next = info; + } + MOD_INC_USE_COUNT; + +out: + spin_unlock(&led_lock); + + return rv; +} + +/* Unregister an LED from the driver. */ +int unregister_led_info(struct led_reg_info *info) +{ + int rv = -1; + struct led_reg_info *led; + + if (info == NULL) + { + return -1; + } + + spin_lock(&led_lock); + led = leds; + if (led == info) + { + MOD_DEC_USE_COUNT; + leds = leds->next; + } + else + { + while (led->next != NULL) + { + if (led->next == info) + { + MOD_DEC_USE_COUNT; + led->next = info->next; + rv = 0; + goto out; + } + led = led->next; + } + } +out: + spin_unlock(&led_lock); + return rv; +} + +/* Return the number of LEDs registered. */ +static int count_leds(void) +{ + struct led_reg_info *led; + int count = 0; + + spin_lock(&led_lock); + led = leds; + while (led != NULL) + { + count = count + 1; + led = led->next; + } + spin_unlock(&led_lock); + + return count; +} + +/* Find an LED by its number. Must be called with the led spin lock + held. */ +static struct led_reg_info *get_led_by_num(int num) +{ + struct led_reg_info *led; + int count = num; + + led = leds; + while (led != NULL) + { + if (count == 0) + { + led->info->led_num = num; + return led; + } + count = count - 1; + led = led->next; + } + + return NULL; +} + +/* Find an LED by its name. Must be called with the led spin lock + held. */ +static struct led_reg_info *get_led_by_name(char *name) +{ + struct led_reg_info *led; + int count = 0; + + led = leds; + while (led != NULL) + { + if (strcmp(name, led->info->name) == 0) + { + led->info->led_num = count; + return led; + } + count++; + led = led->next; + } + + return NULL; +} + +/* The IOCTL handler for the LED driver. */ +static int led_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int i; + struct led_reg_info *led; + struct led_info info; + struct led_op op; + + switch(cmd) + { + case LEDIOC_GETCOUNT: + return count_leds(); + + case LEDIOC_GETINFO_BY_NUM: + case LEDIOC_GETINFO_BY_NAME: + i = copy_from_user(&info, (void*)arg, sizeof(info)); + if (i) + { + return -EFAULT; + } + + spin_lock(&led_lock); + if (cmd == LEDIOC_GETINFO_BY_NUM) + led = get_led_by_num(info.led_num); + else + led = get_led_by_name(info.name); + if (led == NULL) + { + spin_unlock(&led_lock); + return -EINVAL; + } + + info.type = led->info->type; + info.led_num = led->info->led_num; + strcpy(info.name, led->info->name); + info.info = led->info->info; + i = copy_to_user((void *)arg, &info, sizeof(info)); + + spin_unlock(&led_lock); + + if (i) + { + return -EFAULT; + } + return 0; + + case LEDIOC_OP: + i = copy_from_user(&op, (void*)arg, sizeof(op)); + if (i) + { + return -EFAULT; + } + + spin_lock(&led_lock); + led = get_led_by_name(op.name); + if (led == NULL) + { + spin_unlock(&led_lock); + return -EINVAL; + } + + i = led->handle_led_op(led, &op); + if (!i) + { + i = copy_to_user((void *)arg, &op, sizeof(op)); + if (i) + { + i = -EFAULT; + } + } + spin_unlock(&led_lock); + + return i; + + default: + return -ENOIOCTLCMD; + } +} + +/* Not much to do for opening. */ +static int led_open(struct inode *inode, struct file *file) +{ + switch(MINOR(inode->i_rdev)) + { + case LED_MINOR: + return 0; + + default: + return -ENODEV; + } +} + +/* Closing is really easy. */ +static int led_close(struct inode *inode, struct file *file) +{ + return 0; +} + + +static struct file_operations led_fops = { + owner: THIS_MODULE, + llseek: NULL, + read: NULL, + write: NULL, + ioctl: led_ioctl, + open: led_open, + release: led_close, +}; + +static struct miscdevice led_miscdev= +{ + LED_MINOR, + "led", + &led_fops +}; + + +/* Remove the LED driver. */ +static void __exit led_exit(void) +{ + misc_deregister(&led_miscdev); +} + +/* Set up the LED device driver. */ +static int __init led_init(void) +{ + int ret; + + printk("generic LED driver: v%s Corey Minyard (minyard@mvista.com)\n", + VERSION); + + leds = NULL; + ret = misc_register(&led_miscdev); + if (ret) { + printk(KERN_ERR "led: can't misc_register on minor=%d\n", + LED_MINOR); + return -1; + } + return 0; +} + +module_init(led_init); +module_exit(led_exit); + +EXPORT_SYMBOL(register_led_info); +EXPORT_SYMBOL(unregister_led_info); --- ./drivers/char/Config.in.led Fri Jan 25 10:33:28 2002 +++ ./drivers/char/Config.in Fri Jan 25 11:03:22 2002 @@ -222,6 +222,8 @@ fi endmenu +tristate 'LED device driver support' CONFIG_LINUX_LED + if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then tristate 'NetWinder thermometer support' CONFIG_DS1620 tristate 'NetWinder Button' CONFIG_NWBUTTON --- ./drivers/char/Makefile.led Fri Jan 25 09:36:27 2002 +++ ./drivers/char/Makefile Fri Jan 25 11:03:22 2002 @@ -24,7 +24,7 @@ export-objs := busmouse.o console.o keyboard.o sysrq.o \ misc.o pty.o random.o selection.o serial.o \ sonypi.o tty_io.o tty_ioctl.o generic_serial.o \ - ppc405_gpio.o au1000_gpio.o lcd.o \ + ppc405_gpio.o au1000_gpio.o lcd.o led.o \ h3600_backpaq_fpga.o mod-subdirs := joystick ftape drm pcmcia @@ -250,6 +250,7 @@ obj-$(CONFIG_ITE_GPIO) += ite_gpio.o obj-$(CONFIG_AU1000_GPIO) += au1000_gpio.o obj-$(CONFIG_PPC405_GPIO) += ppc405_gpio.o +obj-$(CONFIG_LINUX_LED) += led.o obj-$(CONFIG_QIC02_TAPE) += tpqic02.o --- ./Documentation/Configure.help.led Fri Jan 25 11:02:12 2002 +++ ./Documentation/Configure.help Fri Jan 25 11:03:22 2002 @@ -11578,6 +11578,12 @@ then also Y to the driver for your FDDI card, below). Most people will say N. +Linux LED Support +CONFIG_LINUX_LED + This is a driver that supports controlling LEDs. It is a generic + driver that provides a control framework, the actual drivers must + register with it to work. + Digital DEFEA and DEFPA adapter support CONFIG_DEFXX This is support for the DIGITAL series of EISA (DEFEA) and PCI --- ./include/linux/led.h.led Fri Jan 25 09:36:27 2002 +++ ./include/linux/led.h Fri Jan 25 11:03:22 2002 @@ -0,0 +1,130 @@ +/* + * Defines for the LED device driver. + * + * This driver uses ioctl() calls to get information about the LEDS + * and to set their values. + * + * This currently only supports mono and bicolor LEDs, but support for + * fancier LEDs (scrolling text or numeric LEDs, for instance) could + * easily be added. + * + * Corey Minyard + * + */ + +#ifndef _LINUX_LED_H +#define _LINUX_LED_H + +#include + +#define LED_IOCTL_BASE 'L' + +/* Types of LEDs. */ +#define LED_TYPE_MONOCOLOR 1 +#define LED_TYPE_BICOLOR 2 + +/* Defines for LED colors. */ +#define LED_COLOR_OFF 0 +#define LED_COLOR_RED 1 +#define LED_COLOR_GREEN 2 +#define LED_COLOR_BLUE 3 +#define LED_COLOR_YELLOW 4 +#define LED_COLOR_AUXMODE -1 /* Set it to the auxiliary mode. */ + +union led_info_u +{ + struct + { + int color; /* Color of the LED. */ + int has_aux_mode; /* If true, the LED has an auxiliary + mode (like a disk drive or + network LED) that it can be set + to work in. */ + } monocolor; + + struct + { + int color1; /* First color of the LED. */ + int color2; /* Second color of the LED. */ + int has_aux_mode; /* If true, the LED has an auxiliary + mode (like a disk drive or + network LED) that it can be set + to work in. */ + } bicolor; +}; + +/* Information about an LED. This is not the dynamic information, but + the information about the capabilities of the LED. */ +struct led_info +{ + int led_num; + int type; + char name[32]; /* The name of the LED, used to address it. */ + union led_info_u info; +}; + +/* Operation types that are valid. */ +#define SET_LED 1 /* Set the value of the LED. */ +#define GET_LED 2 /* Return the LED's current value. */ + +/* Operations defined for each different type of LED. */ +union led_op_u +{ + struct + { + int color; + } monocolor; + + struct + { + int color; + } bicolor; +}; + +/* An operation to perform on the LED. Set the name and the operation + and any needed info and do an LEDIOC_OP ioctl. */ +struct led_op +{ + int op; + char name[32]; /* The name of the LED, used to address it. */ + union led_op_u op_info; +}; + + + +/* Return the number of LEDs that have registered with the driver. */ +#define LEDIOC_GETCOUNT _IOR(LED_IOCTL_BASE, 0, void) + +/* Get the info for a single LED, indexed by number or name. */ +#define LEDIOC_GETINFO_BY_NUM _IOR(LED_IOCTL_BASE, 1, struct led_info) +#define LEDIOC_GETINFO_BY_NAME _IOR(LED_IOCTL_BASE, 2, struct led_info) + +/* Perform an operation on single LED, indexed by name. */ +#define LEDIOC_OP _IOR(LED_IOCTL_BASE, 3, struct led_op) + + + +/* This is the structure that is registered with the LED driver by the + subtending LED drivers. One of these must be registered for each + LED. */ +struct led_reg_info +{ + struct led_info *info; /* Pointer to the info structure for + this LED. */ + void *data; /* Used by the specific LED driver for + whatever it likes. */ + + /* The handler the driver supplies to handle operations. */ + int (*handle_led_op)(struct led_reg_info *info, + struct led_op *op); + + struct led_reg_info *next; +}; + +/* Add an LED to the set of LEDs the driver controls. */ +int register_led_info(struct led_reg_info *info); + +/* Remove an LED from the set of LEDs the driver controls. */ +int unregister_led_info(struct led_reg_info *info); + +#endif /* ifndef _LINUX_LED_H */ --- ./include/linux/miscdevice.h.led Fri Jan 25 11:00:55 2002 +++ ./include/linux/miscdevice.h Fri Jan 25 11:03:22 2002 @@ -15,6 +15,7 @@ #define ADB_MOUSE_MINOR 10 #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ +#define LED_MINOR 132 /* LED device driver */ #define RTC_MINOR 135 #define EFI_RTC_MINOR 136 /* EFI Time services */ #define SUN_OPENPROM_MINOR 139