ixp4xx-kernel: update to new LEDs code in 2.6.15.2
authorJohn Bowler <jbowler@nslu2-linux.org>
Sun, 5 Feb 2006 06:09:57 +0000 (06:09 +0000)
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>
Sun, 5 Feb 2006 06:09:57 +0000 (06:09 +0000)
 - the meanings of various fields in /sys/class/leds/* have changed,
   slugos-init is updated to take account of these in 0.10

packages/linux/ixp4xx-kernel/2.6.15/950-leds-timer.patch [new file with mode: 0644]
packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch
packages/linux/ixp4xx-kernel_2.6.15.2.bb
packages/slugos-init/files/leds
packages/slugos-init/slugos-init_0.10.bb

diff --git a/packages/linux/ixp4xx-kernel/2.6.15/950-leds-timer.patch b/packages/linux/ixp4xx-kernel/2.6.15/950-leds-timer.patch
new file mode 100644 (file)
index 0000000..c44ef62
--- /dev/null
@@ -0,0 +1,151 @@
+Fix for a bug in led-triggers.c plus an update to the
+timer trigger code to allow for fractional frequency
+values and to correct the evaluation of frequency so
+that it is the actual frequency.
+
+Signed-off-by: John Bowler <jbowler@acm.org>
+
+--- linux-2.6.15/drivers/leds/led-triggers.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.15/drivers/leds/led-triggers.c   1970-01-01 00:00:00.000000000 +0000
+@@ -98,7 +98,7 @@ void led_trigger_event(struct led_trigge
+       if (!trigger)
+               return;
+-      read_lock(&trigger->led_devs);
++      read_lock(&trigger->leddev_list_lock);
+       list_for_each(entry, &trigger->led_devs) {
+               struct led_device *led_dev;
+@@ -107,7 +107,7 @@ void led_trigger_event(struct led_trigge
+               led_set_brightness(led_dev, brightness);
+               write_unlock(&led_dev->lock);
+       }
+-      read_unlock(&trigger->led_devs);
++      read_unlock(&trigger->leddev_list_lock);
+ }
+ /* Caller must ensure led_dev->lock held for write */
+--- linux-2.6.15/drivers/leds/ledtrig-timer.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.15/drivers/leds/ledtrig-timer.c  1970-01-01 00:00:00.000000000 +0000
+@@ -12,6 +12,7 @@
+  */
+ #include <linux/config.h>
++#include <linux/ctype.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+@@ -61,21 +62,19 @@ static void led_timer_function(unsigned 
+ static void led_timer_setdata(struct led_device *led_dev, unsigned long duty, unsigned long frequency)
+ {
+       struct timer_trig_data *timer_data = led_dev->trigger_data;
+-      signed long duty1;
+-
+-      if (frequency > 500)
+-              frequency = 500;
+       if (duty > 100)
+               duty = 100;
+-      duty1 = duty - 50;
+-
+       timer_data->duty = duty;
+       timer_data->frequency = frequency;
+       if (frequency != 0) {
+-              timer_data->delay_on = (50 - duty1) * 1000 / 50 / frequency;
+-              timer_data->delay_off = (50 + duty1) * 1000 / 50 / frequency;
++              timer_data->delay_on = duty * 10000U / frequency;
++              if (timer_data->delay_on == 0)
++                      timer_data->delay_on = 1;
++              timer_data->delay_off = (100U-duty) * 10000U / frequency;
++              if (timer_data->delay_off == 0)
++                      timer_data->delay_off = 1;
+       }
+       mod_timer(&timer_data->timer, jiffies);
+@@ -100,8 +99,8 @@ static ssize_t led_duty_store(struct cla
+       struct timer_trig_data *timer_data;
+       int ret = -EINVAL;
+       char *after;
+-
+       unsigned long state = simple_strtoul(buf, &after, 10);
++
+       if (after - buf > 0) {
+               ret = after - buf;
+               write_lock(&led_dev->lock);
+@@ -118,32 +117,63 @@ static ssize_t led_frequency_show(struct
+ {
+       struct led_device *led_dev = dev->class_data;
+       struct timer_trig_data *timer_data;
++      unsigned long freq000;
+       read_lock(&led_dev->lock);
+       timer_data = led_dev->trigger_data;
+-      sprintf(buf, "%lu\n", timer_data->frequency);
++      freq000 = timer_data->frequency;
+       read_unlock(&led_dev->lock);
++      if (freq000 % 1000)
++              sprintf(buf, "%lu.%.3lu\n", freq000 / 1000, freq000 % 1000);
++      else
++              sprintf(buf, "%lu\n", freq000 / 1000);
++
+       return strlen(buf) + 1;
+ }
+ static ssize_t led_frequency_store(struct class_device *dev, const char *buf, size_t size)
+ {
+       struct led_device *led_dev = dev->class_data;
+-      struct timer_trig_data *timer_data;
+-      int ret = -EINVAL;
+-      char *after;
++      size_t rc = 0;
++      unsigned long freq000 = 0;
++      int have_digit = 0;
++
++      while (rc < size && isspace(buf[rc]))
++              ++rc;
++      if (rc >= size)
++              return rc;
++
++      /* number before decimal point */
++      while (rc < size && isdigit(buf[rc]))
++              freq000 *= 10, freq000 += buf[rc++] - '0', have_digit = 1;
++
++      if (rc < size && (buf[rc] == '.' || buf[rc] == ',')) {
++              int dp = 0;
++              ++rc;
++              while (rc < size && isdigit(buf[rc])) {
++                      if (++dp <= 3)
++                              freq000 *= 10, freq000 += buf[rc] - '0';
++                      ++rc;
++                      have_digit = 1;
++              }
++              while (++dp <= 3)
++                      freq000 *= 10;
++      } else
++              freq000 *= 1000;
+-      unsigned long state = simple_strtoul(buf, &after, 10);
+-      if (after - buf > 0) {
+-              ret = after - buf;
+-              write_lock(&led_dev->lock);
+-              timer_data = led_dev->trigger_data;
+-              led_timer_setdata(led_dev, timer_data->duty, state);
+-              write_unlock(&led_dev->lock);
++      if (!have_digit)
++              return -EINVAL;
++
++      write_lock(&led_dev->lock);
++      {
++              struct timer_trig_data *timer_data = led_dev->trigger_data;
++              led_timer_setdata(led_dev, timer_data->duty, freq000);
+       }
++      write_unlock(&led_dev->lock);
+-      return ret;
++
++      return rc;
+ }
+ static CLASS_DEVICE_ATTR(duty, 0644, led_duty_show, led_duty_store);
index adfd659..b6b3897 100644 (file)
@@ -1,16 +1,12 @@
 This patches the new LEDs code to add cpu activity and
 inactivity triggers to the timer triggers.  The new triggers
-flash an LED at a given rate when the CPU is active and
-set it to on (cpu-idle) or off (cpu-activity trigger) when
-the CPU is idle.
-
-The patch also adds a duty_cycle attribute to the LED
-timer class, this allows control of the mark/space ratio
-in the flash.  Using duty cycles of about 50% far higher
-flash rates become possible.
+set the LED with the trigger to different states (on,
+flashing or off) according to whether or not all CPUs
+are idle.
 
 Signed-off-by: John Bowler <jbowler@acm.org>
 
+diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/arch/arm/kernel/process.c linux-2.6.15.2/arch/arm/kernel/process.c
 --- linux-2.6.15/arch/arm/kernel/process.c     1970-01-01 00:00:00.000000000 +0000
 +++ linux-2.6.15/arch/arm/kernel/process.c     1970-01-01 00:00:00.000000000 +0000
 @@ -27,6 +27,7 @@
@@ -21,20 +17,36 @@ Signed-off-by: John Bowler <jbowler@acm.org>
  
  #include <asm/system.h>
  #include <asm/io.h>
-@@ -121,8 +122,10 @@ void cpu_idle(void)
+@@ -81,6 +82,12 @@ void (*pm_power_off)(void);
+ EXPORT_SYMBOL(pm_power_off);
+ /*
++ * CPU activity indicator.
++ */
++void (*leds_idle)(int is_idle);
++EXPORT_SYMBOL(leds_idle);
++
++/*
+  * This is our default idle handler.  We need to disable
+  * interrupts here to ensure we don't miss a wakeup call.
+  */
+@@ -121,8 +128,12 @@ void cpu_idle(void)
                if (!idle)
                        idle = default_idle;
                leds_event(led_idle_start);
-+              leds_idle(1);
++              if (leds_idle)
++                      leds_idle(1);
                while (!need_resched())
                        idle();
-+              leds_idle(0);
++              if (leds_idle)
++                      leds_idle(0);
                leds_event(led_idle_end);
                preempt_enable_no_resched();
                schedule();
+diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/drivers/leds/Kconfig linux-2.6.15.2/drivers/leds/Kconfig
 --- linux-2.6.15/drivers/leds/Kconfig  1970-01-01 00:00:00.000000000 +0000
 +++ linux-2.6.15/drivers/leds/Kconfig  1970-01-01 00:00:00.000000000 +0000
-@@ -59,5 +59,14 @@ config LEDS_TRIGGER_TIMER
+@@ -66,5 +66,14 @@ config LEDS_TRIGGER_TIMER
          This allows LEDs to be controlled by a programmable timer
          via sysfs. If unsure, say Y.
  
@@ -49,309 +61,523 @@ Signed-off-by: John Bowler <jbowler@acm.org>
 +
  endmenu
  
---- linux-2.6.15/drivers/leds/led-triggers.c   1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.15/drivers/leds/led-triggers.c   1970-01-01 00:00:00.000000000 +0000
-@@ -98,7 +98,7 @@ void led_trigger_event(struct led_trigge
-       if (!trigger)
-               return;
+diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/drivers/leds/Makefile linux-2.6.15.2/drivers/leds/Makefile
+--- linux-2.6.15/drivers/leds/Makefile 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.15/drivers/leds/Makefile 1970-01-01 00:00:00.000000000 +0000
+@@ -13,3 +13,4 @@ obj-$(CONFIG_LEDS_TOSA)                      += leds-tosa.o
  
--      read_lock(&trigger->led_devs);
-+      read_lock(&trigger->leddev_list_lock);
-       list_for_each(entry, &trigger->led_devs) {
-               struct led_device *led_dev;
-@@ -107,7 +107,7 @@ void led_trigger_event(struct led_trigge
-               leds_set_brightness(led_dev, brightness);
-               write_unlock(&led_dev->lock);
-       }
--      read_unlock(&trigger->led_devs);
-+      read_unlock(&trigger->leddev_list_lock);
- }
- /* Caller must ensure led_dev->lock held for write */
---- linux-2.6.15/drivers/leds/ledtrig-timer.c  1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.15/drivers/leds/ledtrig-timer.c  1970-01-01 00:00:00.000000000 +0000
-@@ -24,46 +24,95 @@
- #include "leds.h"
- struct timer_trig_data {
--      unsigned long frequency; /* frequency of blinking, in milliseconds */
-+      unsigned long frequency;  /* length of space, in milliseconds */
-+      unsigned long duty_cycle; /* mark/space ratio as a percentage */
-       struct timer_list timer;
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+      unsigned long idle_brightness;
-+      int           is_idle;
-+#endif
+ # LED Triggers
+ obj-$(CONFIG_LEDS_TRIGGER_TIMER)      += ledtrig-timer.o
++obj-$(CONFIG_LEDS_TRIGGER_CPU_ACTIVITY)       += ledtrig-cpu.o
+diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/drivers/leds/ledtrig-cpu.c linux-2.6.15.2/drivers/leds/ledtrig-cpu.c
+--- linux-2.6.15/drivers/leds/ledtrig-cpu.c    1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.15/drivers/leds/ledtrig-cpu.c    1970-01-01 00:00:00.000000000 +0000
+@@ -0,0 +1,501 @@
++/*
++ * LEDs CPU activity trigger
++ *
++ * Author: John Bowler <jbowler@acm.org>
++ *
++ * Copyright (c) 2006 John Bowler
++ *
++ * Permission is hereby granted, free of charge, to any
++ * person obtaining a copy of this software and associated
++ * documentation files (the "Software"), to deal in the
++ * Software without restriction, including without
++ * limitation the rights to use, copy, modify, merge,
++ * publish, distribute, sublicense, and/or sell copies of
++ * the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the
++ * following conditions:
++ *
++ * The above copyright notice and this permission notice
++ * shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
++ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
++ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
++ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
++ * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
++ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/ctype.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/spinlock.h>
++#include <linux/timer.h>
++#include <linux/device.h>
++
++#include <linux/leds.h>
++#include "leds.h"
++
++//#include <linux/list.h>
++//#include <linux/sysdev.h>
++
++
++/*
++ * To simplify this the LED state is given for each case of
++ * CPU state - idle or active.  The LED can be:
++ *
++ * off
++ * flash - slow for idle, fast (flicker) for active
++ * on
++ *
++ * This gives two useless states - off/off and on/on
++ */
++typedef enum cpu_trigger_led_state {
++      cpu_led_off,
++      cpu_led_flash,
++      cpu_led_on,
++      cpu_led_invalid
++} cpu_trigger_led_state;
++
++static const char *const cpu_trigger_names[] = {
++      "off",
++      "flash",
++      "on",
++      "invalid"
 +};
 +
-+enum timer_property {
-+      TimerFrequency,
-+      TimerDutyCycle
- };
- static void leds_timer_function(unsigned long data)
- {
-       struct led_device *led_dev = (struct led_device *) data;
--      struct timer_trig_data *timer_data = led_dev->trigger_data;
--      unsigned long value = 0;
-+      struct timer_trig_data *timer_data;
-+      unsigned long value;
-       write_lock(&led_dev->lock);
-+      timer_data = led_dev->trigger_data;
-+
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+      if (timer_data->is_idle) {
-+              /* LED solid (or or off), no timer. */
-+              value = timer_data->idle_brightness;
-+      } else if (!timer_data->frequency) {
-+              /* Put the LED in the non-idle state. */
-+              value = 100-timer_data->idle_brightness;
-+      }
-+#else
-       if (!timer_data->frequency) {
--              leds_set_brightness(led_dev, 0);
--              write_unlock(&led_dev->lock);
--              return;
-+              value = 0;
-       }
-+#endif
-+      else {
-+              unsigned long timeout = timer_data->frequency;
-+
-+              /* LED flashing - toggle the brightness. */
-+              value = led_dev->brightness ? 0 : 100;
-+
-+              /* If this is the 'mark' adjust by the duty cycle. */
-+              if (value)
-+                      timeout = timeout * timer_data->duty_cycle / 100;
-+
-+              timeout = msecs_to_jiffies(timeout);
-+              if (!timeout)
-+                      timeout = 1;
--      if (!led_dev->brightness)
--              value = 100;
-+              mod_timer(&timer_data->timer, jiffies + timeout);
++/* Forward declaration - this is called back when an LED property
++ * is changed.
++ */
++static void leds_cpu_trigger_state_change(void);
++
++/*
++ * These constants define the actual mark/space of the flashing
++ * in jiffies.  msecs_to_jiffies rounds up and is compile time
++ * evaluable for constant arguments.  Writing the ?: stuff below
++ * this way ensures the compiler doesn't think it needs to
++ * compile in the math of msecs_to_jiffies.
++ *
++ * These values have been determined by experiment to work well
++ * for the ready/status LED on a LinkSys NSLU2 (light piped) and
++ * for the user LED on a Loft (Gateway Avila variant) board where
++ * the LED was directly visible.  Light Output Varies Everywhere.
++ */
++#define LEDS_CPU_ACTIVE_MARK  msecs_to_jiffies(40)
++#define LEDS_CPU_IDLE_MARK    msecs_to_jiffies(800)
++#define LEDS_CPU_ACTIVE_SPACE msecs_to_jiffies(60)
++#define LEDS_CPU_IDLE_SPACE   msecs_to_jiffies(800)
++
++
++/*
++ * Individual LEDs ------------------------------------------------------------
++ */
++struct cpu_trigger_data {
++      cpu_trigger_led_state active; /* Behaviour when the CPU is active. */
++      cpu_trigger_led_state idle;   /* Behaviour when the CPU is idle. */
++};
++
++/*
++ * LED state change - called when the state of a single LED might
++ * have changed.  Returns true if the LED is blinking.  The argument
++ * is the blink state - the brightness of the blinking LED.
++ */
++static int leds_cpu_trigger_led_state_change(struct led_device *led,
++              int is_active, enum led_brightness brightness)
++{
++      int is_blinking = 0;
++
++      write_lock(&led->lock);
++      {
++              struct cpu_trigger_data *data = led->trigger_data;
++
++              /* Find the new brightness for the LED, if the LED is
++               * set to flash then the brightness passed in is the
++               * required value.
++               */
++              if (likely(data != 0))
++                      switch (is_active ? data->active : data->idle) {
++                      case cpu_led_off:   brightness = LED_OFF;  break;
++                      case cpu_led_flash: is_blinking = 1;       break;
++                      case cpu_led_on:    brightness = LED_FULL; break;
++                      }
++              else
++                      brightness = is_active ? LED_FULL : LED_OFF;
++
++              led_set_brightness(led, brightness);
 +      }
-       leds_set_brightness(led_dev, value);
--
--      mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(timer_data->frequency));
-       write_unlock(&led_dev->lock);
- }
--static ssize_t leds_show_frequency(struct class_device *dev, char *buf)
-+static ssize_t leds_show_prop(struct class_device *dev, char *buf, enum timer_property what)
- {
-       struct led_device *led_dev = dev->class_data;
-       struct timer_trig_data *timer_data = led_dev->trigger_data;
-+      unsigned long value = 0;
-       read_lock(&led_dev->lock);
--      sprintf(buf, "%lu\n", timer_data->frequency);
-+      switch (what)
++      write_unlock(&led->lock);
++
++      return is_blinking;
++}
++
++/*
++ * sysfs properties, the property is output at an list of the
++ * values with the current setting enclosed in []
++ */
++static ssize_t leds_cpu_trigger_show_prop(struct class_device *dev, char *buf,
++              size_t where)
++{
++      struct led_device     *led = dev->class_data;
++      cpu_trigger_led_state  item = cpu_led_invalid, i;
++      char                  *next;
++
++      read_lock(&led->lock);
 +      {
-+              case TimerFrequency: value = timer_data->frequency; break;
-+              case TimerDutyCycle: value = timer_data->duty_cycle; break;
++              if (likely(led->trigger_data != 0))
++                      item = *(const cpu_trigger_led_state*)(
++                                      led->trigger_data + where);
 +      }
-       read_unlock(&led_dev->lock);
-+      sprintf(buf, "%lu\n", value);
++      read_unlock(&led->lock);
 +
-       return strlen(buf) + 1;
- }
--static ssize_t leds_store_frequency(struct class_device *dev, const char *buf, size_t size)
-+static ssize_t leds_show_frequency(struct class_device *dev, char *buf)
++      for (i=0, next=buf; i<cpu_led_invalid; ++i) {
++              const char *name = cpu_trigger_names[i];
++              size_t len = strlen(name);
++
++              if (i == item)
++                      *next++ = '[';
++              memcpy(next, name, len);
++              next += len;
++              if (i == item)
++                      *next++ = ']';
++              *next++ = ' ';
++      }
++
++      next[-1] = '\n';
++      *next++ = 0;
++
++      return next - buf;
++}
++
++static ssize_t leds_cpu_trigger_show_active(struct class_device *dev, char *buf)
 +{
-+      return leds_show_prop(dev, buf, TimerFrequency);
++      return leds_cpu_trigger_show_prop(dev, buf,
++                      offsetof(struct cpu_trigger_data, active));
 +}
 +
-+static ssize_t leds_show_duty_cycle(struct class_device *dev, char *buf)
++static ssize_t leds_cpu_trigger_show_idle(struct class_device *dev, char *buf)
 +{
-+      return leds_show_prop(dev, buf, TimerDutyCycle);
++      return leds_cpu_trigger_show_prop(dev, buf,
++                      offsetof(struct cpu_trigger_data, idle));
 +}
 +
-+static ssize_t leds_store_prop(struct class_device *dev, const char *buf,
-+              size_t size, enum timer_property what)
- {
-       struct led_device *led_dev = dev->class_data;
-       struct timer_trig_data *timer_data = led_dev->trigger_data;
-@@ -74,17 +123,33 @@ static ssize_t leds_store_frequency(stru
-       if (after - buf > 0) {
-               ret = after - buf;
-               write_lock(&led_dev->lock);
--              timer_data->frequency = state;
--              mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(timer_data->frequency));
-+              switch (what)
-+              {
-+                      case TimerFrequency: timer_data->frequency = state; break;
-+                      case TimerDutyCycle: timer_data->duty_cycle = state; break;
++/*
++ * Any matching leading substring selects a property - so "onoffonoff"
++ * sets the property to off.
++ */
++static ssize_t leds_cpu_trigger_store_prop(struct class_device *dev,
++              const char *buf, size_t size, size_t where)
++{
++      size_t rc = 0;
++      cpu_trigger_led_state value = 0/*sic*/;
++      struct led_device *led;
++
++      /* ignore space characters before the value. */
++      while (rc < size && isspace(buf[rc]))
++              ++rc;
++      if (rc >= size)
++              return rc;
++
++      /* look for a simple match against the trigger name, case
++       * sensitive.
++       */
++      do {
++              const char *name = cpu_trigger_names[value];
++              size_t len = strlen(name);
++              if (len <= size && memcmp(buf+rc, name, len) == 0) {
++                      rc = len;
++                      break;
 +              }
-+              /* Cause the timer to fire in a jiffy */
-+              mod_timer(&timer_data->timer, jiffies + 1);
-               write_unlock(&led_dev->lock);
-       }
-       return ret;
- }
-+static ssize_t leds_store_frequency(struct class_device *dev, const char *buf, size_t size)
++              if (++value >= cpu_led_invalid)
++                      return -EINVAL;
++      } while (1);
++
++      led = dev->class_data;
++      write_lock(&led->lock);
++      {
++              if (likely(led->trigger_data != 0))
++                      *(cpu_trigger_led_state*)(
++                                      led->trigger_data + where) = value;
++
++      }
++      write_unlock(&led->lock);
++
++      return rc;
++}
++
++static ssize_t leds_cpu_trigger_store_active(struct class_device *dev,
++              const char *buf, size_t size)
 +{
-+      return leds_store_prop(dev, buf, size, TimerFrequency);
++      ssize_t rc = leds_cpu_trigger_store_prop(dev, buf, size,
++                      offsetof(struct cpu_trigger_data, active));
++      /*
++       * At least one CPU must be active (otherwise who is doing this?)
++       * Call down into the global state below to cause an update
++       * to happen now.
++       */
++      leds_cpu_trigger_state_change();
++      return rc;
 +}
 +
-+static ssize_t leds_store_duty_cycle(struct class_device *dev, const char *buf, size_t size)
++static ssize_t leds_cpu_trigger_store_idle(struct class_device *dev,
++              const char *buf, size_t size)
 +{
-+      return leds_store_prop(dev, buf, size, TimerDutyCycle);
++      return leds_cpu_trigger_store_prop(dev, buf, size,
++                      offsetof(struct cpu_trigger_data, idle));
 +}
 +
- static CLASS_DEVICE_ATTR(frequency, 0644, leds_show_frequency, leds_store_frequency);
-+static CLASS_DEVICE_ATTR(duty_cycle, 0644, leds_show_duty_cycle, leds_store_duty_cycle);
--void timer_trig_activate(struct led_device *led_dev)
-+static void do_activate(struct led_device *led_dev, unsigned long idle_brightness)
- {
-       struct timer_trig_data *timer_data;
-@@ -94,6 +159,11 @@ void timer_trig_activate(struct led_devi
-       led_dev->trigger_data = timer_data;
-       timer_data->frequency = 0;
-+      timer_data->duty_cycle = 100;
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+      timer_data->idle_brightness = idle_brightness;
-+      timer_data->is_idle = 0;
-+#endif
-       init_timer(&timer_data->timer);
-       timer_data->timer.function = leds_timer_function;
-@@ -101,12 +171,27 @@ void timer_trig_activate(struct led_devi
-       timer_data->timer.expires = 0;
-       class_device_create_file(led_dev->class_dev, &class_device_attr_frequency);
-+      class_device_create_file(led_dev->class_dev, &class_device_attr_duty_cycle);
- }
--void timer_trig_deactivate(struct led_device *led_dev)
-+static void timer_trig_activate(struct led_device *led_dev)
++static CLASS_DEVICE_ATTR(active, 0644, leds_cpu_trigger_show_active,
++                                      leds_cpu_trigger_store_active);
++
++static CLASS_DEVICE_ATTR(idle, 0644, leds_cpu_trigger_show_idle,
++                                      leds_cpu_trigger_store_idle);
++
++/*
++ * Activate and deactivate are called on individual LEDs when the
++ * LED trigger property is changed.  The LED write lock is held.
++ */
++static void leds_cpu_trigger_activate(struct led_device *led)
 +{
-+      do_activate(led_dev, 100);
++      /*
++       * The initial setting of the trigger is simple CPU activity
++       * with the LED off for idle and on for active.  Consequently
++       * there is no need to mess with the global state initially,
++       * we know the CPU is active at this moment!
++       */
++      struct cpu_trigger_data *data = kmalloc(sizeof *data, GFP_KERNEL);
++      if (unlikely(data == 0))
++              return;
++
++      data->active = cpu_led_on;
++      data->idle = cpu_led_off;
++      led->trigger_data = data;
++
++      class_device_create_file(led->class_dev, &class_device_attr_active);
++      class_device_create_file(led->class_dev, &class_device_attr_idle);
++
++      led_set_brightness(led, LED_FULL);
 +}
 +
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+static void cpu_trig_activate(struct led_device *led_dev)
++static void leds_cpu_trigger_deactivate(struct led_device *led)
 +{
-+      /* As above but the LED is off when the CPU is idle */
-+      do_activate(led_dev, 0);
++      struct cpu_trigger_data *data = led->trigger_data;
++      if (likely(data != 0)) {
++              led_set_brightness(led, LED_OFF);
++
++              class_device_remove_file(led->class_dev, &class_device_attr_idle);
++              class_device_remove_file(led->class_dev, &class_device_attr_active);
++
++              led->trigger_data = 0;
++              kfree(data);
++      }
 +}
-+#endif
-+
-+static void timer_trig_deactivate(struct led_device *led_dev)
- {
-       struct timer_trig_data *timer_data = led_dev->trigger_data;
-       if (timer_data) {
-+              class_device_remove_file(led_dev->class_dev, &class_device_attr_duty_cycle);
-               class_device_remove_file(led_dev->class_dev, &class_device_attr_frequency);
-               del_timer_sync(&timer_data->timer);
-               kfree(timer_data);
-@@ -119,16 +204,90 @@ static struct led_trigger timer_led_trig
-       .deactivate = timer_trig_deactivate,
- };
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+static struct led_trigger cpu_led_trigger = {
-+      .name     = "cpu-activity",
-+      .activate = cpu_trig_activate,
-+      .deactivate = timer_trig_deactivate,
-+};
 +
-+static struct led_trigger idle_led_trigger = {
-+      .name     = "cpu-idle",
-+      .activate = timer_trig_activate,
-+      .deactivate = timer_trig_deactivate,
-+};
 +
-+static int leds_do_idle = 0;
-+#endif
++/*
++ * Global state  --------------------------------------------------------------
++ *
++ * This is global because the CPU state is global and we only need one timer to
++ * do this stuff.
++ */
++typedef struct leds_cpu_trigger_data {
++      struct led_trigger trigger; /* the lock in here protects everything */
++      struct timer_list  timer;
++      unsigned long      last_active_time; /* record of last jiffies */
++      unsigned long      last_idle_time;   /* record of last jiffies */
++      int                count_active;     /* number of active CPUs */
++} leds_cpu_trigger_data;
 +
- static int __init timer_trig_init(void)
- {
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+      int rc = led_trigger_register(&idle_led_trigger);
-+      if (rc)
-+              return rc;
-+      rc = led_trigger_register(&cpu_led_trigger);
-+      if (rc)
-+              return rc;
-+      leds_do_idle = 1;
-+#endif
-       return led_trigger_register(&timer_led_trigger);
- }
- static void __exit timer_trig_exit (void)
- {
-       led_trigger_unregister(&timer_led_trigger);
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+      leds_do_idle = 0;
-+      led_trigger_unregister(&cpu_led_trigger);
-+      led_trigger_unregister(&idle_led_trigger);
-+#endif
- }
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+static void leds_trigger_idle(struct led_trigger *trigger, int is_idle)
++/*
++ * Mark state - uses the current time (jiffies) to work out
++ * whether this is a mark or space.
++ */
++static int leds_cpu_trigger_mark(struct leds_cpu_trigger_data *data,
++              unsigned long now) {
++      if (data->count_active > 0) {
++              unsigned long elapsed = now - data->last_active_time;
++              elapsed %= LEDS_CPU_ACTIVE_SPACE + LEDS_CPU_ACTIVE_MARK;
++              data->last_active_time = now - elapsed;
++              return elapsed > LEDS_CPU_ACTIVE_SPACE;
++      } else {
++              unsigned long elapsed = now - data->last_idle_time;
++              elapsed %= LEDS_CPU_IDLE_SPACE + LEDS_CPU_IDLE_MARK;
++              data->last_idle_time = now - elapsed;
++              return elapsed > LEDS_CPU_IDLE_SPACE;
++      }
++}
++
++
++/*
++ * State change - given information about the nature of the
++ * (possible) state change call up to each LED to adjust its
++ * state.  Returns true if any LED is blinking.  The lock
++ * must be held (a read lock is adequate).
++ */
++static int leds_cpu_trigger_scan_leds(struct leds_cpu_trigger_data *data,
++              unsigned long now)
 +{
++      int blinking = 0;
++      const int active = data->count_active > 0;
++      const enum led_brightness brightness =
++              leds_cpu_trigger_mark(data, now) ? LED_FULL : LED_OFF;
 +      struct list_head *entry;
 +
-+      if (!trigger)
-+              return;
++      list_for_each(entry, &data->trigger.led_devs) {
++              struct led_device *led =
++                      list_entry(entry, struct led_device, trig_list);
 +
-+      read_lock(&trigger->leddev_list_lock);
-+      list_for_each(entry, &trigger->led_devs) {
-+              struct led_device *led_dev;
-+              struct timer_trig_data *timer_data;
++              blinking |= leds_cpu_trigger_led_state_change(led,
++                              active, brightness);
++      }
 +
-+              /* The timer must be deactivated in this thread if the CPU
-+               * is going idle, otherwise this function will simply stop
-+               * the CPU ever becoming idle.
-+               */
-+              led_dev = list_entry(entry, struct led_device, trig_list);
-+              write_lock(&led_dev->lock);
-+              timer_data = led_dev->trigger_data;
-+              if (is_idle && !timer_data->is_idle && timer_data->frequency)
-+                      del_timer(&timer_data->timer);
-+              timer_data->is_idle = is_idle;
-+              write_unlock(&led_dev->lock);
-+
-+              /* Force the LED to the correct state and instantiate
-+               * a timer if necessary.
++      return blinking;
++}
++
++/*
++ * Set the timer correctly according to the current state, the lock
++ * must be held for write.
++ */
++static void leds_cpu_trigger_set_timer(struct leds_cpu_trigger_data *state,
++              unsigned long now)
++{
++      unsigned long next;
++      if (state->count_active > 0) {
++              next = state->last_active_time;
++              if (now - next > LEDS_CPU_ACTIVE_SPACE)
++                      next += LEDS_CPU_ACTIVE_MARK;
++              next += LEDS_CPU_ACTIVE_SPACE;
++      } else {
++              next = state->last_idle_time;
++              if (now - next > LEDS_CPU_IDLE_SPACE)
++                      next += LEDS_CPU_IDLE_MARK;
++              next += LEDS_CPU_IDLE_SPACE;
++      }
++      mod_timer(&state->timer, next);
++}
++
++/*
++ * The timer callback if the LED is currently flashing, the callback
++ * calls the state change function and, if that returns true, meaning
++ * that at least one LED is still blinking, the timer is restarted
++ * with the correct timeout.
++ */
++static void leds_cpu_trigger_timer_callback(unsigned long data)
++{
++      struct leds_cpu_trigger_data *state =
++                              (struct leds_cpu_trigger_data *)data;
++
++      write_lock(&state->trigger.leddev_list_lock);
++      {
++              unsigned long now = jiffies;
++
++              /* If at least one LED is set to flash; set the timer
++               * again (this won't reset the timer set within the
++               * idle loop).
 +               */
-+              leds_timer_function((unsigned long)led_dev);
++              if (leds_cpu_trigger_scan_leds(state, now))
++                      leds_cpu_trigger_set_timer(state, now);
++      }
++      write_unlock(&state->trigger.leddev_list_lock);
++}
++
++
++/*
++ * There is one global control structure, one timer and one set
++ * of state for active CPUs shared across all the LEDs.  Individual
++ * LEDs say how this state to be handled.  It is currently *not*
++ * possible to show per-cpu activity on individual LEDs, the code
++ * maintains a count of active CPUs and the state is only 'idle'
++ * if all CPUs are idle.
++ */
++static struct leds_cpu_trigger_data leds_cpu_trigger = {
++      .trigger = {
++              .name       = "cpu",
++              .activate   = leds_cpu_trigger_activate,
++              .deactivate = leds_cpu_trigger_deactivate,
++      } ,
++      .timer   = TIMER_INITIALIZER(leds_cpu_trigger_timer_callback, 0,
++                      (unsigned long)&leds_cpu_trigger),
++      .last_active_time = 0,
++      .last_idle_time   = 0,
++      .count_active     = 0,
++};
++
++/*
++ * State change - callback from an individual LED on a property change which
++ * might require a redisplay.
++ */
++static void leds_cpu_trigger_state_change() {
++      write_lock(&leds_cpu_trigger.trigger.leddev_list_lock);
++      {
++              unsigned long now = jiffies;
++
++              if (leds_cpu_trigger_scan_leds(&leds_cpu_trigger, now) &&
++                      !timer_pending(&leds_cpu_trigger.timer))
++                      leds_cpu_trigger_set_timer(&leds_cpu_trigger, now);
 +      }
-+      read_unlock(&trigger->leddev_list_lock);
++      write_unlock(&leds_cpu_trigger.trigger.leddev_list_lock);
 +}
 +
-+void leds_idle(int is_idle)
++/*
++ * Called from every CPU at the start and end of the idle loop.
++ * The active count is initially 0, even though CPUs are running,
++ * so the code below must check for the resultant underflow.
++ *
++ * If the idle behaviour is 'flash' then when the timer times out
++ * it will take the CPU out of idle, set the active state (which
++ * may also be flash), drop back into idle and reset the timer to
++ * the idle timeout...
++ */
++static void leds_cpu_trigger_idle(int is_idle)
 +{
-+      if (leds_do_idle) {
-+              leds_trigger_idle(&cpu_led_trigger, is_idle);
-+              leds_trigger_idle(&idle_led_trigger, is_idle);
++      write_lock(&leds_cpu_trigger.trigger.leddev_list_lock);
++      if ((is_idle && leds_cpu_trigger.count_active > 0 &&
++                              --leds_cpu_trigger.count_active == 0) ||
++          (!is_idle && leds_cpu_trigger.count_active < num_online_cpus() &&
++                              ++leds_cpu_trigger.count_active == 1)) {
++              unsigned long now = jiffies;
++
++              /* State change - the system just became idle or active,
++               * call the del_timer first in an attempt to minimise
++               * getting a timer interrupt which will take us unnecessarily
++               * out of idle (this doesn't matter).
++               */
++              del_timer(&leds_cpu_trigger.timer);
++              if (leds_cpu_trigger_scan_leds(&leds_cpu_trigger, now))
++                      leds_cpu_trigger_set_timer(&leds_cpu_trigger, now);
 +      }
++      write_unlock(&leds_cpu_trigger.trigger.leddev_list_lock);
 +}
 +
-+EXPORT_SYMBOL_GPL(leds_idle);
-+#endif
++/*
++ * Module init and exit - register the trigger, then store
++ * the idle callback in the arch-specific global.  For this
++ * module to link (into the kernel) or load (into a running
++ * kernel) the architecture must define the leds_idle global.
++ */
++static int __init leds_cpu_trigger_init(void)
++{
++      int rc = led_trigger_register(&leds_cpu_trigger.trigger);
++      leds_idle = leds_cpu_trigger_idle;
++      return rc;
++}
++module_init(leds_cpu_trigger_init);
 +
- module_init(timer_trig_init);
- module_exit(timer_trig_exit);
++static void __exit leds_cpu_trigger_exit(void)
++{
++      leds_idle = 0;
++      del_timer_sync(&leds_cpu_trigger.timer);
++      led_trigger_unregister(&leds_cpu_trigger.trigger);
++}
++module_exit(leds_cpu_trigger_exit);
++
++MODULE_AUTHOR("John Bowler <jbowler@acm.org>");
++MODULE_DESCRIPTION("CPU activity LED trigger");
++MODULE_LICENSE("MIT");
+diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/include/linux/leds.h linux-2.6.15.2/include/linux/leds.h
 --- linux-2.6.15/include/linux/leds.h  1970-01-01 00:00:00.000000000 +0000
 +++ linux-2.6.15/include/linux/leds.h  1970-01-01 00:00:00.000000000 +0000
-@@ -89,3 +89,21 @@ void led_trigger_event(struct led_trigge
+@@ -95,3 +95,12 @@ void led_trigger_event(struct led_trigge
  #define led_trigger_event(x, y) do {} while(0)
  
  #endif
@@ -359,17 +585,8 @@ Signed-off-by: John Bowler <jbowler@acm.org>
 +/*
 + * CPU activity indication.
 + */
-+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
-+
 +/* Idle callback - call with is_idle==1 at the start of the idle loop
-+ * and with is_idle==0 at the end.
++ * and with is_idle==0 at the end.  This symbol must be defined by
++ * the arch core to be able to use LEDS_TRIGGER_CPU_ACTIVITY
 + */
-+void leds_idle(int is_idle);
-+
-+#else
-+
-+/* No CPU activity support. */
-+#define leds_idle(x) do {} while (0)
-+
-+#endif
-+
++extern void (*leds_idle)(int is_idle);
index 86eec02..90bc655 100644 (file)
@@ -8,7 +8,7 @@ PR_CONFIG = "0"
 # Increment the number below (i.e. the digits after PR) when
 # making changes within this file or for changes to the patches
 # applied to the kernel.
-PR = "r0.${PR_CONFIG}"
+PR = "r1.${PR_CONFIG}"
 
 include ixp4xx-kernel.inc
 
@@ -21,14 +21,18 @@ IXP4XX_PATCHES += "file://00-memory-h-page-shift.patch;patch=1"
 IXP4XX_PATCHES += "file://10-mtdpart-redboot-fis-byteswap.patch;patch=1"
 IXP4XX_PATCHES += "file://11-mtdpart-redboot-config-byteswap.patch;patch=1"
 IXP4XX_PATCHES += "file://15-jffs2-endian-config.patch;patch=1"
-IXP4XX_PATCHES += "${RPSRC}/led_core-r10.patch;patch=1"
-IXP4XX_PATCHES += "${RPSRC}/led_triggers-r8.patch;patch=1"
-IXP4XX_PATCHES += "${RPSRC}/led_trig_timer-r3.patch;patch=1"
-# IXP4XX_PATCHES += "${RPSRC}/led_trig_sharpsl_pm-r2.patch;patch=1"
-IXP4XX_PATCHES += "${RPSRC}/led_zaurus-r7.patch;patch=1"
-IXP4XX_PATCHES += "${RPSRC}/led_locomo-r4.patch;patch=1"
-IXP4XX_PATCHES += "${RPSRC}/led_ide-r1.patch;patch=1"
-IXP4XX_PATCHES += "${RPSRC}/led_nand-r1.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_core-r11.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_triggers-r9.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_trig_timer-r4.patch;patch=1"
+# IXP4XX_PATCHES += "${RPSRC}/led_trig_sharpsl_pm-r4a.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_zaurus-r8.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_locomo-r5.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_ixp4xx-r0.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_tosa-r3.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_ide-r2.patch;patch=1"
+IXP4XX_PATCHES += "${RPSRC}/led_nand-r2.patch;patch=1"
+IXP4XX_PATCHES += "file://950-leds-timer.patch;patch=1"
+IXP4XX_PATCHES += "file://951-ixp4xx-leds-cpu-activity.patch;patch=1"
 IXP4XX_PATCHES += "file://40-rtc-class.patch;patch=1"
 IXP4XX_PATCHES += "file://45-eeprom-notifier.patch;patch=1"
 IXP4XX_PATCHES += "file://48-setup-byteswap-cmdline.patch;patch=1"
@@ -48,8 +52,6 @@ IXP4XX_PATCHES += "file://92-nslu2-maclist.patch;patch=1"
 IXP4XX_PATCHES += "file://93-loft-maclist.patch;patch=1"
 IXP4XX_PATCHES += "file://94-nas100d-setup.patch;patch=1"
 IXP4XX_PATCHES += "file://94-nslu2-setup.patch;patch=1"
-IXP4XX_PATCHES += "file://95-ixp4xx-leds.patch;patch=1"
-IXP4XX_PATCHES += "file://951-ixp4xx-leds-cpu-activity.patch;patch=1"
 IXP4XX_PATCHES += "file://96-loft-leds.patch;patch=1"
 IXP4XX_PATCHES += "file://96-nas100d-leds.patch;patch=1"
 IXP4XX_PATCHES += "file://96-nslu2-leds.patch;patch=1"
index b614dd6..227ed78 100644 (file)
@@ -5,10 +5,11 @@
 #
 # load the utility functions unless this script is being called
 # just to load its own functions.
-test "$1" != leds && {
-       . /etc/default/rcS
-       . /etc/default/functions
-}
+case "$1" in
+leds)  ;;
+*)     . /etc/default/rcS
+       . /etc/default/functions;;
+esac
 
 #
 # led_set led-dir off|on|slow|fast|panic|blink|flash|user|*
@@ -18,7 +19,7 @@ test "$1" != leds && {
 led_user_default(){
        case "$(machine)" in
        nslu2)  echo -n "cpu-idle";;
-       *)      echo -n "cpu-activity";;
+       *)      echo -n "cpu";;
        esac
 }
 #
@@ -41,7 +42,7 @@ led_set(){
                off|on)         echo -n none
 
                                case "$setting" in
-                               on)     echo -n 100;;
+                               on)     echo -n 255;;
                                off)    echo -n 0;;
                                esac >"$1/brightness";;
 
@@ -49,23 +50,23 @@ led_set(){
                                echo -n timer
 
                                case "$setting" in
-                               flash)  echo -n 727;;
-                               blink)  echo -n 72;;
-                               slow)   echo -n 400;;
-                               fast)   echo -n 100;;
-                               panic)  echo -n 50;;
+                               flash)  echo -n 0.6;;
+                               blink)  echo -n 0.6;;
+                               slow)   echo -n 1;;
+                               fast)   echo -n 3;;
+                               panic)  echo -n 6;;
                                esac >"$1/frequency"
 
                                case "$setting" in
                                flash)  echo -n 10;;
-                               blink)  echo -n 1000;;
-                               *)      echo -n 100;;
-                               esac >"$1/duty_cycle";;
+                               blink)  echo -n 90;;
+                               *)      echo -n 50;;
+                               esac >"$1/duty";;
 
-               cpu-idle)       echo -n cpu-idle
+               cpu-idle)       echo -n cpu
                                # these settings work well on NSLU2
-                               echo -n 80 >"$1/frequency"
-                               echo -n 10 >"$1/duty_cycle";;
+                               echo -n on >"$1/idle"
+                               echo -n flash >"$1/active";;
 
                *)              echo -n "$setting";;
                esac >"$1/trigger"
@@ -195,7 +196,7 @@ leds_help(){
        echo "    the new state.  'error' or 'panic' means a (potentially)" >&2
        echo "    recoverable error or an unrecoverable error ('panic') has" >&2
        echo "    occured." >&2
-       echo "  <led> off|on|slow|fast|panic|blink|flash|user|*" >&2
+       echo "  <led> off|on|slow|fast|panic|blink|flash|user|cpu|cpu-idle" >&2
        echo "    set the named led to the given display, an arbitrary led" >&2
        echo "    trigger may be given.  'user' will use the default specified" >&2
        echo "    in USER_LED from /etc/default/rcS, if not specified a cpu" >&2
@@ -212,7 +213,7 @@ boot|system|user|singleuser|shutdown)
 
 beep)  beep "$@";;
 
-"")    leds_help;;
+""|-*) leds_help;;
 help)  leds_help;;
 
 leds)  # just load the functions
index 6a31fca..561f593 100644 (file)
@@ -4,7 +4,7 @@ PRIORITY = "required"
 LICENSE = "GPL"
 DEPENDS = "base-files devio"
 RDEPENDS = "busybox devio"
-PR = "r52"
+PR = "r53"
 
 SRC_URI = "file://boot/flash \
           file://boot/disk \