leds: add oneshot blink functions
authorFabio Baltieri <fabio.baltieri@gmail.com>
Sat, 26 May 2012 23:19:22 +0000 (07:19 +0800)
committerBryan Wu <bryan.wu@canonical.com>
Mon, 23 Jul 2012 23:52:34 +0000 (07:52 +0800)
Add two new functions, led_blink_set_oneshot and
led_trigger_blink_oneshot, to be used by triggers for one-shot blink of
led devices.

This is implemented extending the existing software-blink code, and uses
the same timer and handler function.

The behavior of the code is to do a blink-on, blink-off sequence when
the function is called, ignoring other calls until the sequence is
completed so that the leds keep blinking at constant rate if the
functions are called repeatedly.

This is meant to be used by drivers which needs to trigger on sporadic
event, but doesn't have clear busy/idle trigger points.

After the blink sequence the led remains off. This behavior can be
inverted setting the "invert" argument, which blink the led off, than on
and leave the led on after the sequence.

(bryan.wu@canonical.com: rebase to commit 'leds: don't disable blinking
when writing the same value to delay_on or delay_off')

Signed-off-by: Fabio Baltieri <fabio.baltieri@gmail.com>
Acked-by: Shuah Khan <shuahkhan@gmail.com>
Signed-off-by: Bryan Wu <bryan.wu@canonical.com>
drivers/leds/led-class.c
drivers/leds/led-core.c
drivers/leds/led-triggers.c
include/linux/leds.h

index e663e6f413e989d835067811a992bb62dc316f36..81eb0916b44ad2588a0c6a6790b6d6cf192ccf20 100644 (file)
@@ -86,6 +86,11 @@ static void led_timer_function(unsigned long data)
                return;
        }
 
+       if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
+               led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
+               return;
+       }
+
        brightness = led_get_brightness(led_cdev);
        if (!brightness) {
                /* Time to switch the LED on. */
@@ -102,6 +107,20 @@ static void led_timer_function(unsigned long data)
 
        led_set_brightness(led_cdev, brightness);
 
+       /* Return in next iteration if led is in one-shot mode and we are in
+        * the final blink state so that the led is toggled each delay_on +
+        * delay_off milliseconds in worst case.
+        */
+       if (led_cdev->flags & LED_BLINK_ONESHOT) {
+               if (led_cdev->flags & LED_BLINK_INVERT) {
+                       if (brightness)
+                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
+               } else {
+                       if (!brightness)
+                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
+               }
+       }
+
        mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
 }
 
index d65353d8d3fcb4ae012460c20bc33f20c2afa84f..a6f4d910ca08de5407da089d78cabfb6530a3bec 100644 (file)
@@ -27,7 +27,6 @@ EXPORT_SYMBOL_GPL(leds_list);
 static void led_stop_software_blink(struct led_classdev *led_cdev)
 {
        /* deactivate previous settings */
-       del_timer_sync(&led_cdev->blink_timer);
        led_cdev->blink_delay_on = 0;
        led_cdev->blink_delay_off = 0;
 }
@@ -61,13 +60,12 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
 }
 
 
-void led_blink_set(struct led_classdev *led_cdev,
-                  unsigned long *delay_on,
-                  unsigned long *delay_off)
+void led_blink_setup(struct led_classdev *led_cdev,
+                    unsigned long *delay_on,
+                    unsigned long *delay_off)
 {
-       del_timer_sync(&led_cdev->blink_timer);
-
-       if (led_cdev->blink_set &&
+       if (!(led_cdev->flags & LED_BLINK_ONESHOT) &&
+           led_cdev->blink_set &&
            !led_cdev->blink_set(led_cdev, delay_on, delay_off))
                return;
 
@@ -77,8 +75,41 @@ void led_blink_set(struct led_classdev *led_cdev,
 
        led_set_software_blink(led_cdev, *delay_on, *delay_off);
 }
+
+void led_blink_set(struct led_classdev *led_cdev,
+                  unsigned long *delay_on,
+                  unsigned long *delay_off)
+{
+       del_timer_sync(&led_cdev->blink_timer);
+
+       led_cdev->flags &= ~LED_BLINK_ONESHOT;
+       led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
+
+       led_blink_setup(led_cdev, delay_on, delay_off);
+}
 EXPORT_SYMBOL(led_blink_set);
 
+void led_blink_set_oneshot(struct led_classdev *led_cdev,
+                          unsigned long *delay_on,
+                          unsigned long *delay_off,
+                          int invert)
+{
+       if ((led_cdev->flags & LED_BLINK_ONESHOT) &&
+            timer_pending(&led_cdev->blink_timer))
+               return;
+
+       led_cdev->flags |= LED_BLINK_ONESHOT;
+       led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
+
+       if (invert)
+               led_cdev->flags |= LED_BLINK_INVERT;
+       else
+               led_cdev->flags &= ~LED_BLINK_INVERT;
+
+       led_blink_setup(led_cdev, delay_on, delay_off);
+}
+EXPORT_SYMBOL(led_blink_set_oneshot);
+
 void led_brightness_set(struct led_classdev *led_cdev,
                        enum led_brightness brightness)
 {
index b449ed8d87124311f65a92156efec80612fd8a17..fa0b9be019eaa6587eedc461d26331a081d61225 100644 (file)
@@ -230,9 +230,11 @@ void led_trigger_event(struct led_trigger *trig,
 }
 EXPORT_SYMBOL_GPL(led_trigger_event);
 
-void led_trigger_blink(struct led_trigger *trig,
-                      unsigned long *delay_on,
-                      unsigned long *delay_off)
+void led_trigger_blink_setup(struct led_trigger *trig,
+                            unsigned long *delay_on,
+                            unsigned long *delay_off,
+                            int oneshot,
+                            int invert)
 {
        struct list_head *entry;
 
@@ -244,12 +246,32 @@ void led_trigger_blink(struct led_trigger *trig,
                struct led_classdev *led_cdev;
 
                led_cdev = list_entry(entry, struct led_classdev, trig_list);
-               led_blink_set(led_cdev, delay_on, delay_off);
+               if (oneshot)
+                       led_blink_set_oneshot(led_cdev, delay_on, delay_off,
+                                             invert);
+               else
+                       led_blink_set(led_cdev, delay_on, delay_off);
        }
        read_unlock(&trig->leddev_list_lock);
 }
+
+void led_trigger_blink(struct led_trigger *trig,
+                      unsigned long *delay_on,
+                      unsigned long *delay_off)
+{
+       led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
+}
 EXPORT_SYMBOL_GPL(led_trigger_blink);
 
+void led_trigger_blink_oneshot(struct led_trigger *trig,
+                              unsigned long *delay_on,
+                              unsigned long *delay_off,
+                              int invert)
+{
+       led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
+}
+EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
+
 void led_trigger_register_simple(const char *name, struct led_trigger **tp)
 {
        struct led_trigger *trig;
index 39eee41d8c6f4deee39d6904087a567657a2349e..dd93a22044bb1b80f33073c2f610dbc0db393f50 100644 (file)
@@ -38,6 +38,9 @@ struct led_classdev {
 #define LED_SUSPENDED          (1 << 0)
        /* Upper 16 bits reflect control information */
 #define LED_CORE_SUSPENDRESUME (1 << 16)
+#define LED_BLINK_ONESHOT      (1 << 17)
+#define LED_BLINK_ONESHOT_STOP (1 << 18)
+#define LED_BLINK_INVERT       (1 << 19)
 
        /* Set LED brightness level */
        /* Must not sleep, use a workqueue if needed */
@@ -102,6 +105,24 @@ extern void led_classdev_resume(struct led_classdev *led_cdev);
 extern void led_blink_set(struct led_classdev *led_cdev,
                          unsigned long *delay_on,
                          unsigned long *delay_off);
+/**
+ * led_blink_set_oneshot - do a oneshot software blink
+ * @led_cdev: the LED to start blinking
+ * @delay_on: the time it should be on (in ms)
+ * @delay_off: the time it should ble off (in ms)
+ * @invert: blink off, then on, leaving the led on
+ *
+ * This function makes the LED blink one time for delay_on +
+ * delay_off time, ignoring the request if another one-shot
+ * blink is already in progress.
+ *
+ * If invert is set, led blinks for delay_off first, then for
+ * delay_on and leave the led on after the on-off cycle.
+ */
+extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
+                                 unsigned long *delay_on,
+                                 unsigned long *delay_off,
+                                 int invert);
 /**
  * led_brightness_set - set LED brightness
  * @led_cdev: the LED to set
@@ -150,6 +171,10 @@ extern void led_trigger_event(struct led_trigger *trigger,
 extern void led_trigger_blink(struct led_trigger *trigger,
                              unsigned long *delay_on,
                              unsigned long *delay_off);
+extern void led_trigger_blink_oneshot(struct led_trigger *trigger,
+                                     unsigned long *delay_on,
+                                     unsigned long *delay_off,
+                                     int invert);
 
 #else