can: add tx/rx LED trigger support
authorFabio Baltieri <fabio.baltieri@gmail.com>
Tue, 18 Dec 2012 17:50:55 +0000 (18:50 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Sat, 26 Jan 2013 15:58:59 +0000 (16:58 +0100)
This patch implements the functions to add two LED triggers, named
<ifname>-tx and <ifname>-rx, to a canbus device driver.

Triggers are called from specific handlers by each CAN device driver and
can be disabled altogether with a Kconfig option.

The implementation keeps the LED on when the interface is UP and blinks
the LED on network activity at a configurable rate.

This only supports can-dev based drivers, as it uses some support field
in the can_priv structure.

Supported drivers should call devm_can_led_init() and can_led_event() as
needed.

Cleanup is handled automatically by devres, so no *_exit function is
needed.

Supported events are:
- CAN_LED_EVENT_OPEN: turn on tx/rx LEDs
- CAN_LED_EVENT_STOP: turn off tx/rx LEDs
- CAN_LED_EVENT_TX: trigger tx LED blink
- CAN_LED_EVENT_RX: trigger tx LED blink

Cc: Wolfgang Grandegger <wg@grandegger.com>
Cc: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Fabio Baltieri <fabio.baltieri@gmail.com>
Acked-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/led.c [new file with mode: 0644]
include/linux/can/dev.h
include/linux/can/led.h [new file with mode: 0644]

index 0c5a65682d01c78313244b5579859c9a71c722f1..1cca19f1c490b5807f5862d6d75d90f7752b6c9e 100644 (file)
@@ -51,6 +51,17 @@ config CAN_CALC_BITTIMING
          arguments "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw".
          If unsure, say Y.
 
+config CAN_LEDS
+       bool "Enable LED triggers for Netlink based drivers"
+       depends on LEDS_CLASS
+       select LEDS_TRIGGERS
+       ---help---
+         This option adds two LED triggers for packet receive and transmit
+         events on each supported CAN device.
+
+         Say Y here if you are working on a system with led-class supported
+         LEDs and you want to use them as canbus activity indicators.
+
 config CAN_AT91
        tristate "Atmel AT91 onchip CAN controller"
        depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9X5
index 7de59862bbe940aeddc9ac93a3c1bd16d8eac30a..c7440392adbbaaabd5ca5b8ba872ba18c23f41d2 100644 (file)
@@ -8,6 +8,8 @@ obj-$(CONFIG_CAN_SLCAN)         += slcan.o
 obj-$(CONFIG_CAN_DEV)          += can-dev.o
 can-dev-y                      := dev.o
 
+can-dev-$(CONFIG_CAN_LEDS)     += led.o
+
 obj-y                          += usb/
 obj-y                          += softing/
 
diff --git a/drivers/net/can/led.c b/drivers/net/can/led.c
new file mode 100644 (file)
index 0000000..c50a0d7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com>
+ *
+ * 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/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/can/dev.h>
+
+#include <linux/can/led.h>
+
+static unsigned long led_delay = 50;
+module_param(led_delay, ulong, 0644);
+MODULE_PARM_DESC(led_delay,
+               "blink delay time for activity leds (msecs, default: 50).");
+
+/* Trigger a LED event in response to a CAN device event */
+void can_led_event(struct net_device *netdev, enum can_led_event event)
+{
+       struct can_priv *priv = netdev_priv(netdev);
+
+       switch (event) {
+       case CAN_LED_EVENT_OPEN:
+               led_trigger_event(priv->tx_led_trig, LED_FULL);
+               led_trigger_event(priv->rx_led_trig, LED_FULL);
+               break;
+       case CAN_LED_EVENT_STOP:
+               led_trigger_event(priv->tx_led_trig, LED_OFF);
+               led_trigger_event(priv->rx_led_trig, LED_OFF);
+               break;
+       case CAN_LED_EVENT_TX:
+               if (led_delay)
+                       led_trigger_blink_oneshot(priv->tx_led_trig,
+                                                 &led_delay, &led_delay, 1);
+               break;
+       case CAN_LED_EVENT_RX:
+               if (led_delay)
+                       led_trigger_blink_oneshot(priv->rx_led_trig,
+                                                 &led_delay, &led_delay, 1);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(can_led_event);
+
+static void can_led_release(struct device *gendev, void *res)
+{
+       struct can_priv *priv = netdev_priv(to_net_dev(gendev));
+
+       led_trigger_unregister_simple(priv->tx_led_trig);
+       led_trigger_unregister_simple(priv->rx_led_trig);
+}
+
+/* Register CAN LED triggers for a CAN device
+ *
+ * This is normally called from a driver's probe function
+ */
+void devm_can_led_init(struct net_device *netdev)
+{
+       struct can_priv *priv = netdev_priv(netdev);
+       void *res;
+
+       res = devres_alloc(can_led_release, 0, GFP_KERNEL);
+       if (!res) {
+               netdev_err(netdev, "cannot register LED triggers\n");
+               return;
+       }
+
+       snprintf(priv->tx_led_trig_name, sizeof(priv->tx_led_trig_name),
+                "%s-tx", netdev->name);
+       snprintf(priv->rx_led_trig_name, sizeof(priv->rx_led_trig_name),
+                "%s-rx", netdev->name);
+
+       led_trigger_register_simple(priv->tx_led_trig_name,
+                                   &priv->tx_led_trig);
+       led_trigger_register_simple(priv->rx_led_trig_name,
+                                   &priv->rx_led_trig);
+
+       devres_add(&netdev->dev, res);
+}
+EXPORT_SYMBOL_GPL(devm_can_led_init);
index 2b2fc345afca9103e6f9adf13610524b4ddf5844..7747d9bcdc84164408f5e06c355397ee180ff818 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/can.h>
 #include <linux/can/netlink.h>
 #include <linux/can/error.h>
+#include <linux/can/led.h>
 
 /*
  * CAN mode
@@ -52,6 +53,13 @@ struct can_priv {
 
        unsigned int echo_skb_max;
        struct sk_buff **echo_skb;
+
+#ifdef CONFIG_CAN_LEDS
+       struct led_trigger *tx_led_trig;
+       char tx_led_trig_name[CAN_LED_NAME_SZ];
+       struct led_trigger *rx_led_trig;
+       char rx_led_trig_name[CAN_LED_NAME_SZ];
+#endif
 };
 
 /*
diff --git a/include/linux/can/led.h b/include/linux/can/led.h
new file mode 100644 (file)
index 0000000..12d5549
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef CAN_LED_H
+#define CAN_LED_H
+
+#include <linux/if.h>
+#include <linux/leds.h>
+
+enum can_led_event {
+       CAN_LED_EVENT_OPEN,
+       CAN_LED_EVENT_STOP,
+       CAN_LED_EVENT_TX,
+       CAN_LED_EVENT_RX,
+};
+
+#ifdef CONFIG_CAN_LEDS
+
+/* keep space for interface name + "-tx"/"-rx" suffix and null terminator */
+#define CAN_LED_NAME_SZ (IFNAMSIZ + 4)
+
+void can_led_event(struct net_device *netdev, enum can_led_event event);
+void devm_can_led_init(struct net_device *netdev);
+
+#else
+
+static inline void can_led_event(struct net_device *netdev,
+                                enum can_led_event event)
+{
+}
+static inline void devm_can_led_init(struct net_device *netdev)
+{
+}
+
+#endif
+
+#endif