All of lore.kernel.org
 help / color / mirror / Atom feed
From: Fabio Baltieri <fabio.baltieri@gmail.com>
To: linux-can@vger.kernel.org
Cc: Fabio Baltieri <fabio.baltieri@gmail.com>
Subject: [RFC PATCH] can: add tx/rx led trigger support
Date: Tue, 10 Apr 2012 23:39:25 +0200	[thread overview]
Message-ID: <1334093965-2692-1-git-send-email-fabio.baltieri@gmail.com> (raw)

This patch adds two led triggers, named <ifname>-tx and <ifname>-rx to
each registered canbus interface.

Triggers are called from can_send() and can_rcv() functions in af_can.h,
and can be disabled with a Kconfig option.

The implementation lights up the LED when a packet is transmitted or
received and turn it off after a configurable time using a timer.

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

Signed-off-by: Fabio Baltieri <fabio.baltieri@gmail.com>
---
Hi all,

this is a try to add generic tx/rx LED triggers for canbus interfaces, somthing
I think is very useful in embedded systems where canbus devices often have
status leds associated with them, maybe on the bus connector itself, like
ethernet interfaces. I saw a couple of hardware implementation to drive status
leds from tx/rx lines but these were not as effective as software ones.

The implementation is similar to the MAC80211_LEDS one, and takes quite a lot
of inspiration and some code from it.

In this case, however, tx and rx events are trapped from the af_can source file
as there are no generic tx/rx functions in can/dev.c. This also required an
additional counter to discard rx events for looped frames.

Also, as all the support data are in the can_priv structure, this only supports
can-dev based drivers. All the others are ignored by checking the
netdev->rtnl_link_ops->kind string. This actually excludes only vcan and slcan
drivers.

The implementation should be quite unintrusive on existing code and can be disabled
altogether with a config option.

This has been tested tested on x86 and a powerpc with a custom USB-CAN
interface (which I hope to publish as open-hardware soon BTW) and i2c-based
leds.

Any thoughts? Do you think this can be merged?

Thanks,
Fabio


 include/linux/can/dev.h |    9 +++
 net/can/Kconfig         |   10 +++
 net/can/Makefile        |    2 +
 net/can/af_can.c        |   14 ++++-
 net/can/led.c           |  154 +++++++++++++++++++++++++++++++++++++++++++++++
 net/can/led.h           |   29 +++++++++
 6 files changed, 217 insertions(+), 1 deletions(-)
 create mode 100644 net/can/led.c
 create mode 100644 net/can/led.h

diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 5d2efe7..76eb70c 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -16,6 +16,8 @@
 #include <linux/can.h>
 #include <linux/can/netlink.h>
 #include <linux/can/error.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
 
 /*
  * CAN mode
@@ -52,6 +54,13 @@ struct can_priv {
 
 	unsigned int echo_skb_max;
 	struct sk_buff **echo_skb;
+
+#ifdef CONFIG_CAN_LEDS
+	struct timer_list tx_off_timer, rx_off_timer;
+	struct led_trigger *tx_led, *rx_led;
+	char tx_led_name[32], rx_led_name[32];
+	atomic_t led_discard_count;
+#endif
 };
 
 /*
diff --git a/net/can/Kconfig b/net/can/Kconfig
index 0320069..55894b7 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -52,4 +52,14 @@ config CAN_GW
 	  They can be modified with AND/OR/XOR/SET operations as configured
 	  by the netlink configuration interface known e.g. from iptables.
 
+config CAN_LEDS
+	bool "Enable LED triggers for Netlink based drivers"
+	depends on CAN
+	depends on CAN_DEV
+	depends on LEDS_CLASS
+	select LEDS_TRIGGERS
+	---help---
+	  This option enables two LED triggers for packet receive and transmit
+	  events on each CAN device based on the can-dev framework.
+
 source "drivers/net/can/Kconfig"
diff --git a/net/can/Makefile b/net/can/Makefile
index cef49eb..fc6b286 100644
--- a/net/can/Makefile
+++ b/net/can/Makefile
@@ -5,6 +5,8 @@
 obj-$(CONFIG_CAN)	+= can.o
 can-y			:= af_can.o proc.o
 
+can-$(CONFIG_CAN_LEDS)	+= led.o
+
 obj-$(CONFIG_CAN_RAW)	+= can-raw.o
 can-raw-y		:= raw.o
 
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 0ce2ad0..e0c17cf 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -61,6 +61,7 @@
 #include <net/sock.h>
 
 #include "af_can.h"
+#include "led.h"
 
 static __initdata const char banner[] = KERN_INFO
 	"can: controller area network core (" CAN_VERSION_STRING ")\n";
@@ -282,6 +283,8 @@ int can_send(struct sk_buff *skb, int loop)
 		skb->pkt_type = PACKET_HOST;
 	}
 
+	can_led_tx(skb->dev, loop);
+
 	/* send to netdevice */
 	err = dev_queue_xmit(skb);
 	if (err > 0)
@@ -674,6 +677,8 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
 		can_stats.matches_delta++;
 	}
 
+	can_led_rx(dev);
+
 	return NET_RX_SUCCESS;
 
 drop:
@@ -776,6 +781,8 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
 		BUG_ON(dev->ml_priv);
 		dev->ml_priv = d;
 
+		can_led_init(dev);
+
 		break;
 
 	case NETDEV_UNREGISTER:
@@ -795,6 +802,8 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
 
 		spin_unlock(&can_rcvlists_lock);
 
+		can_led_free(dev);
+
 		break;
 	}
 
@@ -867,7 +876,7 @@ static __exit void can_exit(void)
 	/* remove created dev_rcv_lists from still registered CAN devices */
 	rcu_read_lock();
 	for_each_netdev_rcu(&init_net, dev) {
-		if (dev->type == ARPHRD_CAN && dev->ml_priv){
+		if (dev->type == ARPHRD_CAN && dev->ml_priv) {
 
 			struct dev_rcv_lists *d = dev->ml_priv;
 
@@ -875,6 +884,9 @@ static __exit void can_exit(void)
 			kfree(d);
 			dev->ml_priv = NULL;
 		}
+
+		if (dev->type == ARPHRD_CAN)
+			can_led_free(dev);
 	}
 	rcu_read_unlock();
 
diff --git a/net/can/led.c b/net/can/led.c
new file mode 100644
index 0000000..33e34e8
--- /dev/null
+++ b/net/can/led.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com>
+ *
+ * Implementation inspired by mac80211_leds driver.
+ *
+ * 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/slab.h>
+#include <linux/netdevice.h>
+#include <linux/can/dev.h>
+#include <linux/timer.h>
+
+#include <net/rtnetlink.h>
+
+#include "led.h"
+
+static int led_off_delay = 20;
+module_param(led_off_delay, int, 0644);
+MODULE_PARM_DESC(led_off_delay,
+		"turn-off delay time for activity leds (msecs, default: 20).");
+
+static void tx_off_function(unsigned long data)
+{
+	struct can_priv *priv = (struct can_priv *)data;
+
+	led_trigger_event(priv->tx_led, LED_OFF);
+}
+
+static void rx_off_function(unsigned long data)
+{
+	struct can_priv *priv = (struct can_priv *)data;
+
+	led_trigger_event(priv->rx_led, LED_OFF);
+}
+
+/* This is used to ignore devices not based on the can-dev framework */
+static int rtnl_link_kind_is(struct net_device *netdev, const char *kind)
+{
+	if (netdev->rtnl_link_ops &&
+		strncmp(kind, netdev->rtnl_link_ops->kind, strlen(kind)) == 0)
+		return 1;
+	else
+		return 0;
+}
+
+void can_led_tx(struct net_device *netdev, int loop)
+{
+	struct can_priv *priv = netdev_priv(netdev);
+
+	if (!rtnl_link_kind_is(netdev, "can"))
+		return;
+
+	if (unlikely(!priv->tx_led))
+		return;
+
+	if (!timer_pending(&priv->tx_off_timer)) {
+		led_trigger_event(priv->tx_led, LED_FULL);
+
+		mod_timer(&priv->tx_off_timer,
+			jiffies + msecs_to_jiffies(led_off_delay));
+	}
+
+	if (loop)
+		atomic_dec(&priv->led_discard_count);
+}
+
+void can_led_rx(struct net_device *netdev)
+{
+	struct can_priv *priv = netdev_priv(netdev);
+
+	if (!rtnl_link_kind_is(netdev, "can"))
+		return;
+
+	if (unlikely(!priv->rx_led))
+		return;
+
+	/* discard echoed packets */
+	if (atomic_inc_not_zero(&priv->led_discard_count))
+		return;
+
+	if (!timer_pending(&priv->rx_off_timer)) {
+		led_trigger_event(priv->rx_led, LED_FULL);
+
+		mod_timer(&priv->rx_off_timer,
+			jiffies + msecs_to_jiffies(led_off_delay));
+	}
+}
+
+void can_led_init(struct net_device *netdev)
+{
+	struct can_priv *priv = netdev_priv(netdev);
+
+	if (!rtnl_link_kind_is(netdev, "can"))
+		return;
+
+	snprintf(priv->tx_led_name, sizeof(priv->tx_led_name),
+		"%s-tx", netdev->name);
+	snprintf(priv->rx_led_name, sizeof(priv->rx_led_name),
+		"%s-rx", netdev->name);
+
+	priv->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+	if (priv->tx_led) {
+		priv->tx_led->name = priv->tx_led_name;
+		if (led_trigger_register(priv->tx_led)) {
+			kfree(priv->tx_led);
+			priv->tx_led = NULL;
+		}
+	}
+
+	priv->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+	if (priv->rx_led) {
+		priv->rx_led->name = priv->rx_led_name;
+		if (led_trigger_register(priv->rx_led)) {
+			kfree(priv->rx_led);
+			priv->rx_led = NULL;
+		}
+	}
+
+	if (priv->tx_led)
+		setup_timer(&priv->tx_off_timer, tx_off_function,
+			(unsigned long)priv);
+
+	if (priv->rx_led)
+		setup_timer(&priv->rx_off_timer, rx_off_function,
+			(unsigned long)priv);
+
+	atomic_set(&priv->led_discard_count, 0);
+}
+
+void can_led_free(struct net_device *netdev)
+{
+	struct can_priv *priv = netdev_priv(netdev);
+
+	if (!rtnl_link_kind_is(netdev, "can"))
+		return;
+
+	if (priv->tx_led) {
+		del_timer_sync(&priv->tx_off_timer);
+
+		led_trigger_unregister(priv->tx_led);
+		kfree(priv->tx_led);
+	}
+
+	if (priv->rx_led) {
+		del_timer_sync(&priv->rx_off_timer);
+
+		led_trigger_unregister(priv->rx_led);
+		kfree(priv->rx_led);
+	}
+}
diff --git a/net/can/led.h b/net/can/led.h
new file mode 100644
index 0000000..d55151c
--- /dev/null
+++ b/net/can/led.h
@@ -0,0 +1,29 @@
+/*
+ * 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/leds.h>
+
+#ifdef CONFIG_CAN_LEDS
+void can_led_tx(struct net_device *netdev, int loop);
+void can_led_rx(struct net_device *netdev);
+void can_led_init(struct net_device *netdev);
+void can_led_free(struct net_device *netdev);
+#else
+static inline void can_led_tx(struct net_device *netdev, int loop)
+{
+}
+static inline void can_led_rx(struct net_device *netdev)
+{
+}
+static inline void can_led_init(struct net_device *netdev)
+{
+}
+static inline void can_led_free(struct net_device *netdev)
+{
+}
+#endif
-- 
1.7.5.1


             reply	other threads:[~2012-04-10 21:38 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-10 21:39 Fabio Baltieri [this message]
2012-04-11  6:14 ` [RFC PATCH] can: add tx/rx led trigger support Oliver Hartkopp
2012-04-11 17:58   ` Fabio Baltieri
2012-04-11 18:29     ` Oliver Hartkopp
2012-04-11 18:58       ` Wolfgang Grandegger
2012-04-12  6:16         ` Oliver Hartkopp
2012-04-11 19:02     ` Oliver Hartkopp
2012-04-11 19:36       ` Fabio Baltieri
2012-04-12  6:05         ` Oliver Hartkopp
2012-04-12  6:32         ` Alexander Stein
2012-04-12 15:52           ` Oliver Hartkopp
2012-04-12 18:30             ` Fabio Baltieri
2012-04-13 19:00               ` Oliver Hartkopp
2012-04-12  7:37         ` Wolfgang Grandegger
2012-04-12 11:07           ` Martin Gysel
2012-04-12 16:02             ` Oliver Hartkopp
2012-04-12 16:13               ` Wolfgang Grandegger
2012-04-12 17:28                 ` Fabio Baltieri
2012-04-12 18:47                   ` Wolfgang Grandegger
2012-04-12 17:46           ` Fabio Baltieri
2012-04-12 18:53             ` Wolfgang Grandegger
2012-04-11  6:29 ` Alexander Stein
2012-04-11 18:03   ` Fabio Baltieri
2012-04-11 14:45 ` Wolfgang Grandegger
2012-04-11 16:24   ` Oliver Hartkopp
2012-04-11 19:11   ` Fabio Baltieri

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1334093965-2692-1-git-send-email-fabio.baltieri@gmail.com \
    --to=fabio.baltieri@gmail.com \
    --cc=linux-can@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.