Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752086Ab0K3Xkp (ORCPT ); Tue, 30 Nov 2010 18:40:45 -0500 Received: from mailhost.informatik.uni-hamburg.de ([134.100.9.70]:34799 "EHLO mailhost.informatik.uni-hamburg.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751686Ab0K3Xkn (ORCPT ); Tue, 30 Nov 2010 18:40:43 -0500 Message-ID: <4CF58B6F.5070602@metafoo.de> Date: Wed, 01 Dec 2010 00:40:31 +0100 From: Lars-Peter Clausen User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.15) Gecko/20101030 Icedove/3.0.10 MIME-Version: 1.0 To: Eric Cooper CC: linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Richard Purdie , Randy Dunlap Subject: Re: [PATCH v2] add netdev led trigger References: <1290998056-8310-1-git-send-email-ecc@cmu.edu> In-Reply-To: <1290998056-8310-1-git-send-email-ecc@cmu.edu> X-Enigmail-Version: 1.0.1 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 19602 Lines: 618 On 11/29/2010 03:34 AM, Eric Cooper wrote: > Add a netdev LED trigger for all Blinkenlights lovers... > Originally taken from https://dev.openwrt.org/ticket/2776 > Slightly updated for 2.6.24 by Mickey Lauer > and for 2.6.36 by Eric Cooper > > Signed-off-by: Eric Cooper > --- > Changes in v2: > - Use individual entries for link, transmit, and receive; > each can be "enabled" or "disabled" > - Use strict_strtoul instead of simple_strtoul > - Add descriptions to Documentation/ABI/testing/sysfs-class-led Hi Looks like most of the comments I had on v1 are still valid: https://lkml.org/lkml/2010/11/15/506 - Lars > > Documentation/ABI/testing/sysfs-class-led | 64 ++++ > drivers/leds/Kconfig | 7 + > drivers/leds/Makefile | 1 + > drivers/leds/ledtrig-netdev.c | 475 +++++++++++++++++++++++++++++ > 4 files changed, 547 insertions(+), 0 deletions(-) > create mode 100644 drivers/leds/ledtrig-netdev.c > > diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led > index 9e4541d..0b03863 100644 > --- a/Documentation/ABI/testing/sysfs-class-led > +++ b/Documentation/ABI/testing/sysfs-class-led > @@ -26,3 +26,67 @@ Description: > scheduler is chosen. Trigger specific parameters can appear in > /sys/class/leds/ once a given trigger is selected. > > +What: /sys/class/leds//device_name > +Date: November 2010 > +KernelVersion: 2.6.37 > +Contact: Eric Cooper > +Description: > + Network device name to monitor when trigger is set to "netdev". > + > +What: /sys/class/leds//interval > +Date: November 2010 > +KernelVersion: 2.6.37 > +Contact: Eric Cooper > +Description: > + Duration of LED blink in milliseconds when trigger is set to > + "netdev". > + > +What: /sys/class/leds//link > +Date: November 2010 > +KernelVersion: 2.6.37 > +Contact: Eric Cooper > +Description: > + When trigger is set to "netdev", this value is either "enabled" > + or "disabled". If enabled, this LED's normal state reflects > + whether or not the link for the corresponding network device > + is up (has carrier). > + > +What: /sys/class/leds//transmit > +Date: November 2010 > +KernelVersion: 2.6.37 > +Contact: Eric Cooper > +Description: > + When trigger is set to "netdev", this value is either "enabled" > + or "disabled". If enabled, this LED blinks when data is > + transmitted on the corresponding network device. > + > +What: /sys/class/leds//receive > +Date: November 2010 > +KernelVersion: 2.6.37 > +Contact: Eric Cooper > +Description: > + When trigger is set to "netdev", this value is either "enabled" > + or "disabled". If enabled, this LED blinks when data is > + received on the corresponding network device. > + > + Example configurations: > + > + Simple link status LED: > + # echo netdev >someled/trigger > + # echo eth0 >someled/device_name > + # echo enabled >someled/link > + > + Ethernet-style link/activity LED: > + # echo netdev >someled/trigger > + # echo eth0 >someled/device_name > + # echo enabled >someled/link > + # echo enabled >someled/transmit > + # echo enabled >someled/receive > + > + Modem-style tx/rx LEDs: > + # echo netdev >led1/trigger > + # echo ppp0 >led1/device_name > + # echo enabled >led1/transmit > + # echo netdev >led2/trigger > + # echo ppp0 >led2/device_name > + # echo enabled >led2/receive > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig > index 77b8fd2..8d81cd0 100644 > --- a/drivers/leds/Kconfig > +++ b/drivers/leds/Kconfig > @@ -374,6 +374,13 @@ config LEDS_TRIGGER_HEARTBEAT > load average. > If unsure, say Y. > > +config LEDS_TRIGGER_NETDEV > + tristate "LED Network Device Trigger" > + depends on NET > + help > + This allows LEDs to be controlled by network device activity. > + If unsure, say Y. > + > config LEDS_TRIGGER_BACKLIGHT > tristate "LED backlight Trigger" > help > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile > index aae6989..a3bb67d 100644 > --- a/drivers/leds/Makefile > +++ b/drivers/leds/Makefile > @@ -49,6 +49,7 @@ obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o > obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o > obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o > obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o > +obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o > obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o > obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o > obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o > diff --git a/drivers/leds/ledtrig-netdev.c b/drivers/leds/ledtrig-netdev.c > new file mode 100644 > index 0000000..eeb5ac8 > --- /dev/null > +++ b/drivers/leds/ledtrig-netdev.c > @@ -0,0 +1,475 @@ > +/* > + * LED Kernel Netdev Trigger > + * > + * Toggles the LED to reflect the link and traffic state of a named net device > + * > + * Copyright 2007 Oliver Jowett > + * > + * Derived from ledtrig-timer.c which is: > + * Copyright 2005-2006 Openedhand Ltd. > + * Author: Richard Purdie > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "leds.h" > + > +/* > + * Configurable sysfs attributes: > + * > + * device_name - network device name to monitor > + * > + * interval - duration of LED blink, in milliseconds > + * > + * Each of the following attributes may be "enabled" or "disabled": > + * > + * link - if enabled, the LED's normal state reflects > + * whether the link is up (has carrier) > + * transmit - if enabled, the LED blinks on transmitted data > + * receive - if enabled, the LED blinks on received data > + * > + * Some suggestions: > + * > + * Simple link status LED: > + * $ echo netdev >someled/trigger > + * $ echo eth0 >someled/device_name > + * $ echo enabled >someled/link > + * > + * Ethernet-style link/activity LED: > + * $ echo netdev >someled/trigger > + * $ echo eth0 >someled/device_name > + * $ echo enabled >someled/link > + * $ echo enabled >someled/transmit > + * $ echo enabled >someled/receive > + * > + * Modem-style tx/rx LEDs: > + * $ echo netdev >led1/trigger > + * $ echo ppp0 >led1/device_name > + * $ echo enabled >led1/transmit > + * $ echo netdev >led2/trigger > + * $ echo ppp0 >led2/device_name > + * $ echo enabled >led2/receive > + */ > + > +#define MODE_LINK 1 > +#define MODE_TX 2 > +#define MODE_RX 4 > + > +struct led_netdev_data { > + rwlock_t lock; > + > + struct timer_list timer; > + struct notifier_block notifier; > + > + struct led_classdev *led_cdev; > + struct net_device *net_dev; > + > + char device_name[IFNAMSIZ]; > + unsigned interval; > + unsigned mode; > + unsigned link_up; > + unsigned last_activity; > +}; > + > +static void set_baseline_state(struct led_netdev_data *trigger_data) > +{ > + if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) > + led_set_brightness(trigger_data->led_cdev, LED_FULL); > + else > + led_set_brightness(trigger_data->led_cdev, LED_OFF); > + > + if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && > + trigger_data->link_up) > + mod_timer(&trigger_data->timer, > + jiffies + trigger_data->interval); > + else > + del_timer(&trigger_data->timer); > +} > + > +static ssize_t led_device_name_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct led_classdev *led_cdev = dev_get_drvdata(dev); > + struct led_netdev_data *trigger_data = led_cdev->trigger_data; > + > + read_lock(&trigger_data->lock); > + sprintf(buf, "%s\n", trigger_data->device_name); > + read_unlock(&trigger_data->lock); > + > + return strlen(buf) + 1; > +} > + > +static ssize_t led_device_name_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t size) > +{ > + struct led_classdev *led_cdev = dev_get_drvdata(dev); > + struct led_netdev_data *trigger_data = led_cdev->trigger_data; > + > + if (size < 0 || size >= IFNAMSIZ) > + return -EINVAL; > + > + write_lock(&trigger_data->lock); > + > + strcpy(trigger_data->device_name, buf); > + if (size > 0 && trigger_data->device_name[size-1] == '\n') > + trigger_data->device_name[size-1] = 0; > + > + if (trigger_data->device_name[0] != 0) { > + /* check for existing device to update from */ > + struct net_device *dev = > + dev_get_by_name(&init_net, trigger_data->device_name); > + if (dev != NULL) { > + unsigned int flags = dev_get_flags(dev); > + trigger_data->net_dev = dev; > + trigger_data->link_up = (flags & IFF_LOWER_UP) != 0; > + } > + /* updates LEDs, may start timers */ > + set_baseline_state(trigger_data); > + } > + > + write_unlock(&trigger_data->lock); > + return size; > +} > + > +static DEVICE_ATTR(device_name, 0644, > + led_device_name_show, led_device_name_store); > + > +static const char enabled[] = "enabled"; > +static const char disabled[] = "disabled"; > + > +static ssize_t led_show_mode(struct device *dev, > + struct device_attribute *attr, char *buf, > + unsigned mode_bit) > +{ > + struct led_classdev *led_cdev = dev_get_drvdata(dev); > + struct led_netdev_data *trigger_data = led_cdev->trigger_data; > + > + return sprintf(buf, "%s\n", (trigger_data->mode & mode_bit) ? > + enabled : disabled); > +} > + > +static void led_mode_set(struct led_netdev_data *data, unsigned bit) > +{ > + write_lock(&data->lock); > + data->mode |= bit; > + set_baseline_state(data); > + write_unlock(&data->lock); > +} > + > +static void led_mode_clear(struct led_netdev_data *data, unsigned bit) > +{ > + write_lock(&data->lock); > + data->mode &= ~bit; > + set_baseline_state(data); > + write_unlock(&data->lock); > +} > + > +static ssize_t led_store_mode(struct device *dev, > + struct device_attribute *attr, const char *buf, > + size_t size, unsigned mode_bit) > +{ > + struct led_classdev *led_cdev = dev_get_drvdata(dev); > + struct led_netdev_data *trigger_data = led_cdev->trigger_data; > + char *cp; > + int len = size; > + > + cp = memchr(buf, '\n', size); > + if (cp) > + len = cp - buf; > + if (len == sizeof(enabled) - 1 && strncmp(buf, enabled, len) == 0) > + led_mode_set(trigger_data, mode_bit); > + else if (len == sizeof(disabled) - 1 && > + strncmp(buf, disabled, len) == 0) > + led_mode_clear(trigger_data, mode_bit); > + else > + return -EINVAL; > + return size; > +} > + > +#define LED_NETDEV_ATTR(name, mode_bit) \ > +static ssize_t led_show_##name(struct device *dev, \ > + struct device_attribute *attr, \ > + char *buf) \ > +{ \ > + return led_show_mode(dev, attr, buf, mode_bit); \ > +} \ > + \ > +static ssize_t led_store_##name(struct device *dev, \ > + struct device_attribute *attr, \ > + const char *buf, size_t size) \ > +{ \ > + return led_store_mode(dev, attr, buf, size, mode_bit); \ > +} \ > + \ > +static DEVICE_ATTR(name, 0644, led_show_##name, led_store_##name) > + > +LED_NETDEV_ATTR(link, MODE_LINK); > +LED_NETDEV_ATTR(transmit, MODE_TX); > +LED_NETDEV_ATTR(receive, MODE_RX); > + > +static ssize_t led_interval_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct led_classdev *led_cdev = dev_get_drvdata(dev); > + struct led_netdev_data *trigger_data = led_cdev->trigger_data; > + > + read_lock(&trigger_data->lock); > + sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval)); > + read_unlock(&trigger_data->lock); > + > + return strlen(buf) + 1; > +} > + > +static ssize_t led_interval_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t size) > +{ > + struct led_classdev *led_cdev = dev_get_drvdata(dev); > + struct led_netdev_data *trigger_data = led_cdev->trigger_data; > + unsigned long value; > + int ret; > + > + ret = strict_strtoul(buf, 10, &value); > + if (ret < 0) > + return ret; > + > + /* impose some basic bounds on the timer interval */ > + if (value >= 5 && value <= 10000) { > + write_lock(&trigger_data->lock); > + trigger_data->interval = msecs_to_jiffies(value); > + set_baseline_state(trigger_data); /* resets timer */ > + write_unlock(&trigger_data->lock); > + return size; > + } else { > + return -EINVAL; > + } > +} > + > +static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store); > + > +static int netdev_trig_notify(struct notifier_block *nb, > + unsigned long evt, > + void *dv) > +{ > + struct net_device *dev = dv; > + struct led_netdev_data *trigger_data = > + container_of(nb, struct led_netdev_data, notifier); > + > + if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && > + evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER) > + return NOTIFY_DONE; > + > + write_lock(&trigger_data->lock); > + > + if (strcmp(dev->name, trigger_data->device_name)) > + goto done; > + > + if (evt == NETDEV_REGISTER) { > + if (trigger_data->net_dev != NULL) > + dev_put(trigger_data->net_dev); > + dev_hold(dev); > + trigger_data->net_dev = dev; > + trigger_data->link_up = 0; > + goto done; > + } > + > + if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) { > + dev_put(trigger_data->net_dev); > + trigger_data->net_dev = NULL; > + goto done; > + } > + > + /* UP / DOWN / CHANGE */ > + > + trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev)); > + set_baseline_state(trigger_data); > + > +done: > + write_unlock(&trigger_data->lock); > + return NOTIFY_DONE; > +} > + > +/* here's the real work! */ > +static void netdev_trig_timer(unsigned long arg) > +{ > + struct led_netdev_data *trigger_data = (struct led_netdev_data *)arg; > + struct rtnl_link_stats64 temp; > + const struct rtnl_link_stats64 *dev_stats; > + unsigned new_activity; > + > + write_lock(&trigger_data->lock); > + > + if (!trigger_data->link_up || !trigger_data->net_dev || > + (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) { > + /* we don't need to do timer work, just reflect link state. */ > + int on = (trigger_data->mode & MODE_LINK) != 0 && > + trigger_data->link_up; > + led_set_brightness(trigger_data->led_cdev, > + on ? LED_FULL : LED_OFF); > + goto no_restart; > + } > + > + dev_stats = dev_get_stats(trigger_data->net_dev, &temp); > + > + new_activity = > + ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) + > + ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0); > + > + if (trigger_data->mode & MODE_LINK) { > + /* base state is ON (link present) */ > + /* if there's no link, we don't get this far > + and the LED is off */ > + > + /* OFF -> ON always */ > + /* ON -> OFF on activity */ > + if (trigger_data->led_cdev->brightness == LED_OFF) > + led_set_brightness(trigger_data->led_cdev, LED_FULL); > + else if (trigger_data->last_activity != new_activity) > + led_set_brightness(trigger_data->led_cdev, LED_OFF); > + } else { > + /* base state is OFF */ > + /* ON -> OFF always */ > + /* OFF -> ON on activity */ > + if (trigger_data->led_cdev->brightness == LED_FULL) > + led_set_brightness(trigger_data->led_cdev, LED_OFF); > + else if (trigger_data->last_activity != new_activity) > + led_set_brightness(trigger_data->led_cdev, LED_FULL); > + } > + > + trigger_data->last_activity = new_activity; > + mod_timer(&trigger_data->timer, jiffies + trigger_data->interval); > + > +no_restart: > + write_unlock(&trigger_data->lock); > +} > + > +static void netdev_trig_activate(struct led_classdev *led_cdev) > +{ > + struct led_netdev_data *trigger_data; > + int rc; > + > + trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); > + if (!trigger_data) > + return; > + > + rwlock_init(&trigger_data->lock); > + > + trigger_data->notifier.notifier_call = netdev_trig_notify; > + trigger_data->notifier.priority = 10; > + > + setup_timer(&trigger_data->timer, netdev_trig_timer, > + (unsigned long) trigger_data); > + > + trigger_data->led_cdev = led_cdev; > + trigger_data->net_dev = NULL; > + trigger_data->device_name[0] = 0; > + > + trigger_data->mode = 0; > + trigger_data->interval = msecs_to_jiffies(50); > + trigger_data->link_up = 0; > + trigger_data->last_activity = 0; > + > + led_cdev->trigger_data = trigger_data; > + > + rc = device_create_file(led_cdev->dev, &dev_attr_device_name); > + if (rc) > + goto err_out; > + rc = device_create_file(led_cdev->dev, &dev_attr_link); > + if (rc) > + goto err_out_device_name; > + rc = device_create_file(led_cdev->dev, &dev_attr_transmit); > + if (rc) > + goto err_out_link; > + rc = device_create_file(led_cdev->dev, &dev_attr_receive); > + if (rc) > + goto err_out_transmit; > + rc = device_create_file(led_cdev->dev, &dev_attr_interval); > + if (rc) > + goto err_out_receive; > + > + register_netdevice_notifier(&trigger_data->notifier); > + return; > + > +err_out_receive: > + device_remove_file(led_cdev->dev, &dev_attr_receive); > +err_out_transmit: > + device_remove_file(led_cdev->dev, &dev_attr_transmit); > +err_out_link: > + device_remove_file(led_cdev->dev, &dev_attr_link); > +err_out_device_name: > + device_remove_file(led_cdev->dev, &dev_attr_device_name); > +err_out: > + led_cdev->trigger_data = NULL; > + kfree(trigger_data); > +} > + > +static void netdev_trig_deactivate(struct led_classdev *led_cdev) > +{ > + struct led_netdev_data *trigger_data = led_cdev->trigger_data; > + > + if (trigger_data) { > + unregister_netdevice_notifier(&trigger_data->notifier); > + > + device_remove_file(led_cdev->dev, &dev_attr_device_name); > + device_remove_file(led_cdev->dev, &dev_attr_link); > + device_remove_file(led_cdev->dev, &dev_attr_transmit); > + device_remove_file(led_cdev->dev, &dev_attr_receive); > + device_remove_file(led_cdev->dev, &dev_attr_interval); > + > + write_lock(&trigger_data->lock); > + > + if (trigger_data->net_dev) { > + dev_put(trigger_data->net_dev); > + trigger_data->net_dev = NULL; > + } > + > + write_unlock(&trigger_data->lock); > + > + del_timer_sync(&trigger_data->timer); > + > + kfree(trigger_data); > + } > +} > + > +static struct led_trigger netdev_led_trigger = { > + .name = "netdev", > + .activate = netdev_trig_activate, > + .deactivate = netdev_trig_deactivate, > +}; > + > +static int __init netdev_trig_init(void) > +{ > + return led_trigger_register(&netdev_led_trigger); > +} > + > +static void __exit netdev_trig_exit(void) > +{ > + led_trigger_unregister(&netdev_led_trigger); > +} > + > +module_init(netdev_trig_init); > +module_exit(netdev_trig_exit); > + > +MODULE_AUTHOR("Oliver Jowett "); > +MODULE_DESCRIPTION("Netdev LED trigger"); > +MODULE_LICENSE("GPL"); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/