[PATCH RFC] leds: Add status code trigger

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all,

Here is a new trigger. It would allow userspace to make an LED
blink with a pattern to show a status (or error) code to a human.
This is useful for embedded systems without screen or network
access. Obviously, the LED will keep on blinking even if userspace
crashes.

This trigger exports three properties to sysfs when activated:
Nblink, period, and pause. It makes the LED blink "Nblink" times
with a period of "period" milliseconds, then pause for "pause"
milliseconds, and repeat.

It's been tested on an ARM architecture (Xilinx Zynq 7010 SoC,
which CPU is a dual ARM Cortex-A9), with a non-mainline
kernel (xilinx-v2014.4, based of a 3.17.0 kernel).
Unfortunately, I don't have other hardware to test it on.
It compiles fine in a 3.19.0-rc1 source tree.

Additional testing and comments would be appreciated.
Do the properties names seem appropriate to you ? I hesitated
between Nblink, N_blink, nblink, and n_blink.

Best regards,

Signed-off-by: Raphaël Teysseyre <rteysseyre@xxxxxxxxx>
---
 Documentation/leds/ledtrig-statuscode.txt |   41 ++++++
 drivers/leds/trigger/Kconfig              |    9 ++
 drivers/leds/trigger/Makefile             |    1 +
 drivers/leds/trigger/ledtrig-statuscode.c |  194 +++++++++++++++++++++++++++++
 4 files changed, 245 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/leds/ledtrig-statuscode.txt
 create mode 100644 drivers/leds/trigger/ledtrig-statuscode.c

diff --git a/Documentation/leds/ledtrig-statuscode.txt b/Documentation/leds/ledtrig-statuscode.txt
new file mode 100644
index 0000000..f5ea3ac
--- /dev/null
+++ b/Documentation/leds/ledtrig-statuscode.txt
@@ -0,0 +1,41 @@
+LED Status code Trigger
+=======================
+
+This trigger allows userspace to make a LED blink with a pattern
+to show a status (or error) code to a human.
+
+Use case : this can be useful for an embedded system without screen
+or network access. An LED with this trigger can be used to convey
+a basic "status code" to the final user. If user space crashes,
+the LED will keep on blinking, which might help debugging the system.
+
+This trigger exports three properties : period, pause, and Nblink.
+The LED will blink "Nblink" times with a period of "period" milliseconds,
+then pause for "pause" milliseconds, and repeat.
+
+When the trigger is activated, its properties are set to default values.
+
+	Nblink : Number of LED blinks per cycle. Default value : 1.
+
+	period : Period length (in milliseconds). The LED will blink
+		with this period, with a 50% duty cycle.
+		Default value : 1000.
+
+	pause :  Pause duration (in milliseconds) between each cycle.
+		Every Nblink blinks, the LED will stay off for this
+		duration. Default value : 1000.
+
+For example, if Nblink = 3, period = 1000, and pause = 2000,
+the LED will blink with the following pattern :
+
+ ...OFF  |  ON  |  OFF |  ON  |  OFF |  ON  |         OFF               |   ON  |  OFF...
+          <     1s    > <     1s    > <0.5s> <            2s           >
+
+To get this pattern, the user will have to do :
+	echo statuscode > trigger
+	echo 3 > Nblink
+	echo 1000 > period
+	echo 2000 > pause
+
+When ON the LED will be driven at its max_brightness property. When the trigger
+is deactivated, the LED is returned to the OFF state.
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index 49794b4..09f0df5 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -108,4 +108,13 @@ config LEDS_TRIGGER_CAMERA
 	  This enables direct flash/torch on/off by the driver, kernel space.
 	  If unsure, say Y.
 
+config LEDS_TRIGGER_STATUSCODE
+	tristate "LED StatusCode Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This allows LEDs blinking with a pattern. Can be useful on embedded
+	  systems with no screen to give out a status code to a human.
+
+	  If unsure, say N.
+
 endif # LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
index 1abf48d..8c09e77 100644
--- a/drivers/leds/trigger/Makefile
+++ b/drivers/leds/trigger/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
 obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
+obj-$(CONFIG_LEDS_TRIGGER_STATUSCODE)   += ledtrig-statuscode.o
diff --git a/drivers/leds/trigger/ledtrig-statuscode.c b/drivers/leds/trigger/ledtrig-statuscode.c
new file mode 100644
index 0000000..7f0e7ae
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-statuscode.c
@@ -0,0 +1,194 @@
+/*
+ * Status code trigger
+ *
+ * Copyright 2015, Epsiline
+ *
+ * Author : Raphaël Teysseyre <rteysseyre@xxxxxxxxx>
+ *
+ * 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include "../leds.h"
+
+struct statuscode_trig_data {
+	unsigned int period;
+	unsigned int pause;
+	unsigned int Nblink;
+	unsigned int phase;
+	struct timer_list timer;
+};
+
+static const struct statuscode_trig_data STATUSCODE_TRIG_DATA_DEFAULT = {
+	.period = 1000,
+	.pause = 1000,
+	.Nblink = 1,
+	.phase = (unsigned int) -1, /* The first call to
+				       statuscode_trig_update()
+				       will increment this to zero */
+	.timer = TIMER_INITIALIZER(NULL, 0, 0),
+};
+
+/* --- Sysfs handling --- */
+
+#define statuscode_trig_create_attribute(name)				\
+									\
+	static ssize_t statuscode_trig_show_ ## name			\
+	(struct device *dev, struct device_attribute *attr, char *buf)	\
+	{								\
+		struct led_classdev *led_cdev = dev_get_drvdata(dev);	\
+		struct statuscode_trig_data *data = led_cdev->trigger_data; \
+		return scnprintf(buf, PAGE_SIZE, "%u\n", data->name);	\
+	}								\
+									\
+	static ssize_t statuscode_trig_store_ ## name			\
+	(struct device *dev, struct device_attribute *attr,		\
+		const char *buf, size_t count)				\
+	{								\
+		struct led_classdev *led_cdev = dev_get_drvdata(dev);	\
+		struct statuscode_trig_data *data = led_cdev->trigger_data; \
+		unsigned int res;					\
+		int err = kstrtouint(buf, 10, &res);			\
+		if (err)						\
+			return err;					\
+		data->name = res;					\
+		return count;						\
+	}								\
+									\
+	DEVICE_ATTR(name, S_IRUGO | S_IWUSR,				\
+		statuscode_trig_show_ ## name, statuscode_trig_store_ ## name) \
+
+statuscode_trig_create_attribute(period);
+statuscode_trig_create_attribute(pause);
+statuscode_trig_create_attribute(Nblink);
+
+static int statuscode_trig_create_sysfs_files(struct device *dev)
+{
+	int err;
+
+	err = device_create_file(dev, &dev_attr_period);
+	if (err)
+		return err;
+
+	err = device_create_file(dev, &dev_attr_pause);
+	if (err)
+		goto err_pause;
+
+	err = device_create_file(dev, &dev_attr_Nblink);
+	if (err)
+		goto err_Nblink;
+
+	return 0;
+
+err_Nblink:
+	device_remove_file(dev, &dev_attr_pause);
+err_pause:
+	device_remove_file(dev, &dev_attr_period);
+	return err;
+}
+
+static void statuscode_trig_remove_sysfs_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_Nblink);
+	device_remove_file(dev, &dev_attr_pause);
+	device_remove_file(dev, &dev_attr_period);
+}
+
+/* --- Led intensity updating --- */
+
+/*
+ * For Nblink = 3 :
+ * LED intensity :
+ *       +---+   +---+   +---+         +---+   +---+   +---+
+ * ------+   +---+   +---+   +---------+   +---+   +---+   +------
+ * Phase :
+ *    0  | 1 | 2 | 3 | 4 | 5 |    0    | 1 | 2 | 3 | 4 | 5 |   0
+ *
+ */
+
+static void statuscode_trig_update(unsigned long led_cdev_ptr)
+{
+	struct led_classdev *led_cdev = (struct led_classdev *) led_cdev_ptr;
+	struct statuscode_trig_data *data = led_cdev->trigger_data;
+
+	data->phase++;
+	if (data->phase >= 2 * data->Nblink)
+		data->phase = 0;
+
+	if (data->phase % 2)
+		led_set_brightness(led_cdev, led_cdev->max_brightness);
+	else
+		led_set_brightness(led_cdev, LED_OFF);
+
+	if (data->phase == 0)
+		mod_timer(&data->timer, jiffies +
+			msecs_to_jiffies(data->pause));
+	else
+		mod_timer(&data->timer, jiffies +
+			msecs_to_jiffies(data->period / 2));
+}
+
+/* --- Activate / deactivate trigger --- */
+
+static void statuscode_trig_activate(struct led_classdev *led_cdev)
+{
+	struct statuscode_trig_data *data = NULL;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return;
+
+	led_cdev->trigger_data = data;
+
+	*data = STATUSCODE_TRIG_DATA_DEFAULT;
+	setup_timer(&data->timer,
+		statuscode_trig_update,
+		(unsigned long) led_cdev);
+
+	statuscode_trig_create_sysfs_files(led_cdev->dev);
+	statuscode_trig_update(data->timer.data);
+}
+
+static void statuscode_trig_deactivate(struct led_classdev *led_cdev)
+{
+	struct statuscode_trig_data *data = led_cdev->trigger_data;
+
+	if (data) {
+		statuscode_trig_remove_sysfs_files(led_cdev->dev);
+		del_timer_sync(&data->timer);
+		led_set_brightness(led_cdev, LED_OFF);
+		kfree(data);
+		led_cdev->trigger_data = NULL;
+	}
+}
+
+/* --- Module loading/unloading --- */
+
+static struct led_trigger statuscode_led_trigger = {
+	.name = "statuscode",
+	.activate = statuscode_trig_activate,
+	.deactivate = statuscode_trig_deactivate,
+};
+
+static int __init statuscode_trig_init(void)
+{
+	return led_trigger_register(&statuscode_led_trigger);
+}
+
+static void __exit statuscode_trig_exit(void)
+{
+	led_trigger_unregister(&statuscode_led_trigger);
+}
+
+module_init(statuscode_trig_init);
+module_exit(statuscode_trig_exit);
+
+MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@xxxxxxxxx");
+MODULE_DESCRIPTION("Statuscode LED trigger");
+MODULE_LICENSE("GPL");
-- 
1.7.1



--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux