[PATCH RFC 2/2] leds: trigger: add a trigger for UARTs

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

 



This is only a proof of concept driver that makes an LED blink on UART
activity. Feedback about the concept is very welcome.

Without-S-o-b-on-purpose-by: Uwe Kleine-König <u.kleine-koenig@xxxxxxxxxxxxxx>
---
 drivers/leds/trigger/Kconfig        |   7 ++
 drivers/leds/trigger/Makefile       |   1 +
 drivers/leds/trigger/ledtrig-uart.c | 138 ++++++++++++++++++++++++++++
 3 files changed, 146 insertions(+)
 create mode 100644 drivers/leds/trigger/ledtrig-uart.c

diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index 4018af769969..fcea6a87adc7 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -129,4 +129,11 @@ config LEDS_TRIGGER_NETDEV
 	  This allows LEDs to be controlled by network device activity.
 	  If unsure, say Y.
 
+config LEDS_TRIGGER_UART
+	tristate "LED Trigger for UART devices"
+	depends on SERIAL_CORE
+	help
+	  This allows LEDs to be controlled by activity on serial devices like
+	  /dev/ttyS0.
+
 endif # LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
index f3cfe1950538..9e1341b711fd 100644
--- a/drivers/leds/trigger/Makefile
+++ b/drivers/leds/trigger/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
 obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
 obj-$(CONFIG_LEDS_TRIGGER_PANIC)	+= ledtrig-panic.o
 obj-$(CONFIG_LEDS_TRIGGER_NETDEV)	+= ledtrig-netdev.o
+obj-$(CONFIG_LEDS_TRIGGER_UART)		+= ledtrig-uart.o
diff --git a/drivers/leds/trigger/ledtrig-uart.c b/drivers/leds/trigger/ledtrig-uart.c
new file mode 100644
index 000000000000..6f0ece9829de
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-uart.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/serial_core.h>
+
+struct ledtrig_uart_data {
+	struct led_classdev *led_cdev;
+	const char *devname;
+	struct delayed_work dwork;
+	struct uart_icount icount;
+};
+
+static void ledtrig_uart_halt(struct ledtrig_uart_data *trigger_data)
+{
+	cancel_delayed_work_sync(&trigger_data->dwork);
+}
+
+static void ledtrig_uart_restart(struct ledtrig_uart_data *trigger_data)
+{
+	pr_info("%s:%d: devname = %s\n", __func__, __LINE__, trigger_data->devname);
+	if (!trigger_data->devname)
+		return;
+
+	schedule_delayed_work(&trigger_data->dwork, 0);
+}
+
+static ssize_t devname_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct ledtrig_uart_data *trigger_data = led_trigger_get_drvdata(dev);
+	ssize_t len;
+
+	len = sprintf(buf, "%s\n", trigger_data->devname);
+
+	return len;
+}
+
+static ssize_t devname_store(struct device *dev,
+			     struct device_attribute *attr, const char *buf,
+			     size_t size)
+{
+	struct ledtrig_uart_data *trigger_data = led_trigger_get_drvdata(dev);
+	char *devname;
+
+	if (size == 0 || (size == 1 || buf[0] == '\n')) {
+		devname = NULL;
+	} else {
+		devname = kstrndup(buf, size, GFP_KERNEL);
+		if (!devname)
+			return -ENOMEM;
+
+		if (devname[size - 1] == '\n')
+			devname[size - 1] = '\0';
+	}
+
+	ledtrig_uart_halt(trigger_data);
+
+	kfree(trigger_data->devname);
+	trigger_data->devname = devname;
+
+	ledtrig_uart_restart(trigger_data);
+
+	return size;
+}
+static DEVICE_ATTR_RW(devname);
+
+static void ledtrig_uart_work(struct work_struct *work)
+{
+	struct ledtrig_uart_data *trigger_data =
+		container_of(work, struct ledtrig_uart_data, dwork.work);
+	const struct uart_port *port = uart_get_port_by_name(trigger_data->devname);
+
+	pr_info("%s:%d: devname = %s, port = %px\n", __func__, __LINE__, trigger_data->devname, port);
+
+	if (!port) {
+		led_set_brightness(trigger_data->led_cdev, LED_OFF);
+		return;
+	}
+
+	if (port->icount.rx > trigger_data->icount.rx ||
+	    port->icount.tx > trigger_data->icount.tx) {
+		unsigned long delay_on = 0, delay_off = 0;
+
+		led_blink_set_oneshot(trigger_data->led_cdev,
+				      &delay_on, &delay_off, 0);
+
+		trigger_data->icount = port->icount;
+	}
+
+	schedule_delayed_work(&trigger_data->dwork, msecs_to_jiffies(1000));
+}
+
+static struct attribute *ledtrig_uart_attrs[] = {
+	&dev_attr_devname.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(ledtrig_uart);
+
+static int ledtrig_uart_activate(struct led_classdev *led_cdev)
+{
+	struct ledtrig_uart_data *trigger_data;
+
+	trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL);
+	if (!trigger_data)
+		return -ENOMEM;
+
+	led_set_trigger_data(led_cdev, trigger_data);
+
+	INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_uart_work);
+	trigger_data->led_cdev = led_cdev;
+
+	pr_info("%s:%d\n", __func__, __LINE__);
+
+	return 0;
+}
+
+static void ledtrig_uart_deactivate(struct led_classdev *led_cdev)
+{
+	struct ledtrig_uart_data *trigger_data = led_get_trigger_data(led_cdev);
+
+	cancel_delayed_work_sync(&trigger_data->dwork);
+
+	kfree(trigger_data);
+}
+
+struct led_trigger ledtrig_uart = {
+	.name = "uart",
+	.activate = ledtrig_uart_activate,
+	.deactivate = ledtrig_uart_deactivate,
+	.groups = ledtrig_uart_groups,
+};
+module_led_trigger(ledtrig_uart);
+
+MODULE_AUTHOR("Uwe Kleine-König <u.kleine-koenig@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("UART LED trigger");
+MODULE_LICENSE("GPL v2");
-- 
2.19.1




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux