Hi all,

 I've written a kernel module that allows you to control the fron LED on
Sparcstations via /proc. It can also be compiled into the kernel, supporting
display of the system load (a la NetBSD and OpenBSD) then.

Please find the patch against a vanilla 2.4.30 tree included. The sources and
the patch can also be found at http://www.metalhead.ws/led.tar.gz.

diff -uprN linux-2.4.30-vanilla/Documentation/Configure.help 
linux-2.4.30-mod/Documentation/Configure.help
--- linux-2.4.30-vanilla/Documentation/Configure.help   2005-04-04 
01:42:19.000000000 +0000
+++ linux-2.4.30-mod/Documentation/Configure.help       2005-08-25 
20:32:22.000000000 +0000
@@ -23069,6 +23083,17 @@ CONFIG_SUN_OPENPROMFS
   <file:Documentation/modules.txt>.
   The module will be called openpromfs.o.  If unsure, say M.
 
+Control front LED on sun4m machines via /proc/led (EXPERIMENTAL)
+CONFIG_SUN_LED
+  This allows you to control the front LED on sun4m machines via /proc/led. The
+  state of the LED can also be queried.
+
+  On, off and toggle do what their names suggest, an integer number makes the
+  LED blink at this interval seconds, load makes it blink according to the
+  system load (not available when compiled as a module).
+
+  If unsure, say N.
+
 Kernel support for Linux/Sparc 32bit binary compatibility
 CONFIG_SPARC32_COMPAT
   This allows you to run 32-bit binaries on your Ultra.
diff -uprN linux-2.4.30-vanilla/arch/sparc/config.in 
linux-2.4.30-mod/arch/sparc/config.in
--- linux-2.4.30-vanilla/arch/sparc/config.in   2004-11-17 11:54:21.000000000 
+0000
+++ linux-2.4.30-mod/arch/sparc/config.in       2005-08-25 20:32:38.000000000 
+0000
@@ -64,6 +64,10 @@ else
    define_bool CONFIG_PCI n
 fi
 
+if [ "$CONFIG_PROC_FS" = "y" ]; then
+   tristate 'Control front LED on sun4m machines via /proc/led (EXPERIMENTAL)' 
CONFIG_SUN_LED
+fi
+
 tristate 'Openprom tree appears in /proc/openprom' CONFIG_SUN_OPENPROMFS
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
diff -uprN linux-2.4.30-vanilla/arch/sparc/kernel/Makefile 
linux-2.4.30-mod/arch/sparc/kernel/Makefile
--- linux-2.4.30-vanilla/arch/sparc/kernel/Makefile     2003-11-28 
18:26:19.000000000 +0000
+++ linux-2.4.30-mod/arch/sparc/kernel/Makefile 2005-08-25 20:32:59.000000000 
+0000
@@ -33,6 +33,7 @@ obj-$(CONFIG_SMP) += trampoline.o smp.o 
 obj-$(CONFIG_SUN_AUXIO) += auxio.o
 obj-$(CONFIG_PCI) += ebus.o
 obj-$(CONFIG_SUN_PM) += apc.o pmc.o
+obj-$(CONFIG_SUN_LED) += led.o
 
 ifdef CONFIG_SUNOS_EMUL
 obj-y += sys_sunos.o sunos_ioctl.o
diff -uprN linux-2.4.30-vanilla/arch/sparc/kernel/led.c 
linux-2.4.30-mod/arch/sparc/kernel/led.c
--- linux-2.4.30-vanilla/arch/sparc/kernel/led.c        1970-01-01 
00:00:00.000000000 +0000
+++ linux-2.4.30-mod/arch/sparc/kernel/led.c    2005-08-25 20:32:03.000000000 
+0000
@@ -0,0 +1,170 @@
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <asm/auxio.h>
+#include <asm/uaccess.h>
+
+#define MODULE_VERSION "0.1"
+#define MODULE_NAME "led"
+
+MODULE_AUTHOR("Lars Kotthoff <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("Provides control of the front LED on SPARC systems.");
+MODULE_LICENSE("GPL");
+
+EXPORT_NO_SYMBOLS;
+
+#define LED_MAX_LENGTH 8 /* maximum chars written to proc file */
+
+struct proc_dir_entry *led;
+struct timer_list led_blink_timer;
+
+inline void print_debug(const char *msg)
+{
+       printk(KERN_DEBUG "[%s] %s\n", MODULE_NAME, msg);
+}
+
+inline void led_toggle(void)
+{
+       /* set_auxio takes 2 arguments, first the bits to enable, second the
+        * bits to disable (huh?) */
+       set_auxio(get_auxio() ^ AUXIO_LED, get_auxio() & AUXIO_LED);
+}
+
+void led_blink(unsigned long timeout)
+{
+       led_toggle();
+
+       /* reschedule */
+#ifndef CONFIG_SUN_LED_MODULE
+       if(!timeout) { /* blink according to load */
+               led_blink_timer.expires = jiffies +
+                       ((1 + (avenrun[0] >> FSHIFT)) * HZ);
+               /* avenrun isn't exported by the kernel, hence the ifndefs */
+               led_blink_timer.data = 0;
+       } else { /* blink at user specified interval */
+#endif
+               led_blink_timer.expires = jiffies + (timeout * HZ);
+               led_blink_timer.data = timeout;
+#ifndef CONFIG_SUN_LED_MODULE
+       }
+#endif
+       add_timer(&led_blink_timer);
+}
+
+int led_read_proc(char *buf, char **start, off_t offset, int count,
+               int *eof, void *data)
+{
+       int len = 0;
+
+       MOD_INC_USE_COUNT;
+
+       if(get_auxio() & AUXIO_LED) {
+               len = sprintf(buf, "on\n");
+       } else {
+               len = sprintf(buf, "off\n");
+       }
+
+       MOD_DEC_USE_COUNT;
+
+       return len;
+}
+
+int led_write_proc(struct file *file, const char *buffer,
+               unsigned long count, void *data)
+{
+       char *buf = NULL;
+       unsigned long l = 0;
+
+       if(count > LED_MAX_LENGTH) {
+               count = LED_MAX_LENGTH;
+       }
+
+       buf = kmalloc(sizeof(char) * (count + 1), GFP_KERNEL);
+       if(!buf) {
+               return -ENOMEM;
+       }
+
+       MOD_INC_USE_COUNT;
+
+       if(copy_from_user(buf, buffer, count)) {
+               MOD_DEC_USE_COUNT;
+               kfree(buf);
+               return -EFAULT;
+       }
+       buf[count] = '\0';
+
+       /* work around \n when echo'ing into proc */
+       if(buf[count - 1] == '\n') {
+               buf[count - 1] = '\0';
+       }
+
+       /* before we change anything we want to stop any running timers,
+        * otherwise calls such as on will have no persistent effect */
+       if(timer_pending(&led_blink_timer)) {
+               del_timer_sync(&led_blink_timer);
+       }
+
+       if(!strcmp(buf, "on")) {
+               print_debug("Turning LED on...");
+               auxio_set_led(AUXIO_LED_ON);
+       } else if(!strcmp(buf, "toggle")) {
+               print_debug("Toggling LED...");
+               led_toggle();
+       } else if((*buf > '0') && (*buf <= '9')) { /* 0 would cause division by
+                                                     zero */
+               for(l = 0; *buf != '\0'; buf++) { /* convert to number */
+                       l = 10 * l + (*buf - '0');
+               }
+               print_debug("Starting to blink...");
+               led_blink(l);
+#ifndef CONFIG_SUN_LED_MODULE
+       } else if(!strcmp(buf, "load")) {
+               print_debug("Starting to display load...");
+               led_blink(0);
+#endif
+       } else {
+               print_debug("Turning LED off...");
+               auxio_set_led(AUXIO_LED_OFF);
+       }
+
+       MOD_DEC_USE_COUNT;
+       kfree(buf);
+
+       return count;
+}
+
+static int __init init_led(void)
+{
+       led = create_proc_entry(MODULE_NAME,
+                       0, /* default mode */
+                       NULL /* parent dir */);
+       if(!led) {
+               return -ENOMEM;
+       }
+
+       led->read_proc = led_read_proc; /* reader function */
+       led->write_proc = led_write_proc; /* writer function */
+       led->owner = THIS_MODULE;
+
+       /* initialize timer for blinking */
+       init_timer(&led_blink_timer);
+       led_blink_timer.function = led_blink; /* blinking function */
+
+       printk(KERN_INFO
+               "%s version %s, Lars Kotthoff <[EMAIL PROTECTED]>\n",
+               MODULE_NAME, MODULE_VERSION);
+
+       return 0;
+}
+
+static void __exit cleanup_led(void)
+{
+       remove_proc_entry(MODULE_NAME, NULL);
+       if(timer_pending(&led_blink_timer)) { /* kill timer if necessary */
+               del_timer_sync(&led_blink_timer);
+       }
+}
+
+module_init(init_led);
+module_exit(cleanup_led);

-- 
They say that bandaging one's wounds helps to keep up one's appearance.

Attachment: pgpnJAdwpdTvc.pgp
Description: PGP signature

Reply via email to