]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/linux-3.14.22-ledtrig_netdev.patch
Merge remote-tracking branch 'mfischer/daq' into next
[ipfire-2.x.git] / src / patches / linux-3.14.22-ledtrig_netdev.patch
CommitLineData
817317ed
AF
1diff -Naur linux-3.14.22.org/drivers/leds/trigger/Kconfig linux-3.14.22/drivers/leds/trigger/Kconfig
2--- linux-3.14.22.org/drivers/leds/trigger/Kconfig 2014-10-15 08:42:04.000000000 +0200
3+++ linux-3.14.22/drivers/leds/trigger/Kconfig 2014-11-06 17:52:02.303748375 +0100
4@@ -108,4 +108,11 @@
5 This enables direct flash/torch on/off by the driver, kernel space.
6 If unsure, say Y.
7
8+config LEDS_TRIGGER_NETDEV
9+ tristate "LED Netdev Trigger"
10+ depends on NET && LEDS_TRIGGERS
11+ help
12+ This allows LEDs to be controlled by network device activity.
13+ If unsure, say Y.
14+
15 endif # LEDS_TRIGGERS
16diff -Naur linux-3.14.22.org/drivers/leds/trigger/ledtrig-netdev.c linux-3.14.22/drivers/leds/trigger/ledtrig-netdev.c
17--- linux-3.14.22.org/drivers/leds/trigger/ledtrig-netdev.c 1970-01-01 01:00:00.000000000 +0100
18+++ linux-3.14.22/drivers/leds/trigger/ledtrig-netdev.c 2014-11-06 17:47:09.913748236 +0100
19@@ -0,0 +1,438 @@
20+/*
21+ * LED Kernel Netdev Trigger
22+ *
23+ * Toggles the LED to reflect the link and traffic state of a named net device
24+ *
25+ * Copyright 2007 Oliver Jowett <oliver@opencloud.com>
26+ *
27+ * Derived from ledtrig-timer.c which is:
28+ * Copyright 2005-2006 Openedhand Ltd.
29+ * Author: Richard Purdie <rpurdie@openedhand.com>
30+ *
31+ * This program is free software; you can redistribute it and/or modify
32+ * it under the terms of the GNU General Public License version 2 as
33+ * published by the Free Software Foundation.
34+ *
35+ */
36+
37+#include <linux/module.h>
38+#include <linux/jiffies.h>
39+#include <linux/kernel.h>
40+#include <linux/init.h>
41+#include <linux/list.h>
42+#include <linux/spinlock.h>
43+#include <linux/device.h>
44+#include <linux/netdevice.h>
45+#include <linux/timer.h>
46+#include <linux/ctype.h>
47+#include <linux/leds.h>
48+
49+#include "../leds.h"
50+
51+/*
52+ * Configurable sysfs attributes:
53+ *
54+ * device_name - network device name to monitor
55+ *
56+ * interval - duration of LED blink, in milliseconds
57+ *
58+ * mode - either "none" (LED is off) or a space separated list of one or more of:
59+ * link: LED's normal state reflects whether the link is up (has carrier) or not
60+ * tx: LED blinks on transmitted data
61+ * rx: LED blinks on receive data
62+ *
63+ * Some suggestions:
64+ *
65+ * Simple link status LED:
66+ * $ echo netdev >someled/trigger
67+ * $ echo eth0 >someled/device_name
68+ * $ echo link >someled/mode
69+ *
70+ * Ethernet-style link/activity LED:
71+ * $ echo netdev >someled/trigger
72+ * $ echo eth0 >someled/device_name
73+ * $ echo "link tx rx" >someled/mode
74+ *
75+ * Modem-style tx/rx LEDs:
76+ * $ echo netdev >led1/trigger
77+ * $ echo ppp0 >led1/device_name
78+ * $ echo tx >led1/mode
79+ * $ echo netdev >led2/trigger
80+ * $ echo ppp0 >led2/device_name
81+ * $ echo rx >led2/mode
82+ *
83+ */
84+
85+#define MODE_LINK 1
86+#define MODE_TX 2
87+#define MODE_RX 4
88+
89+struct led_netdev_data {
90+ rwlock_t lock;
91+
92+ struct timer_list timer;
93+ struct notifier_block notifier;
94+
95+ struct led_classdev *led_cdev;
96+ struct net_device *net_dev;
97+
98+ char device_name[IFNAMSIZ];
99+ unsigned interval;
100+ unsigned mode;
101+ unsigned link_up;
102+ unsigned last_activity;
103+};
104+
105+static void set_baseline_state(struct led_netdev_data *trigger_data)
106+{
107+ if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up)
108+ led_set_brightness(trigger_data->led_cdev, LED_FULL);
109+ else
110+ led_set_brightness(trigger_data->led_cdev, LED_OFF);
111+
112+ if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up)
113+ mod_timer(&trigger_data->timer, jiffies + trigger_data->interval);
114+ else
115+ del_timer(&trigger_data->timer);
116+}
117+
118+static ssize_t led_device_name_show(struct device *dev,
119+ struct device_attribute *attr, char *buf)
120+{
121+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
122+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
123+
124+ read_lock(&trigger_data->lock);
125+ sprintf(buf, "%s\n", trigger_data->device_name);
126+ read_unlock(&trigger_data->lock);
127+
128+ return strlen(buf) + 1;
129+}
130+
131+static ssize_t led_device_name_store(struct device *dev,
132+ struct device_attribute *attr, const char *buf, size_t size)
133+{
134+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
135+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
136+
137+ if (size < 0 || size >= IFNAMSIZ)
138+ return -EINVAL;
139+
140+ write_lock(&trigger_data->lock);
141+
142+ strcpy(trigger_data->device_name, buf);
143+ if (size > 0 && trigger_data->device_name[size-1] == '\n')
144+ trigger_data->device_name[size-1] = 0;
145+
146+ if (trigger_data->device_name[0] != 0) {
147+ /* check for existing device to update from */
148+ trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
149+ if (trigger_data->net_dev != NULL)
150+ trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
151+ set_baseline_state(trigger_data); /* updates LEDs, may start timers */
152+ }
153+
154+ write_unlock(&trigger_data->lock);
155+ return size;
156+}
157+
158+static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
159+
160+static ssize_t led_mode_show(struct device *dev,
161+ struct device_attribute *attr, char *buf)
162+{
163+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
164+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
165+
166+ read_lock(&trigger_data->lock);
167+
168+ if (trigger_data->mode == 0) {
169+ strcpy(buf, "none\n");
170+ } else {
171+ if (trigger_data->mode & MODE_LINK)
172+ strcat(buf, "link ");
173+ if (trigger_data->mode & MODE_TX)
174+ strcat(buf, "tx ");
175+ if (trigger_data->mode & MODE_RX)
176+ strcat(buf, "rx ");
177+ strcat(buf, "\n");
178+ }
179+
180+ read_unlock(&trigger_data->lock);
181+
182+ return strlen(buf)+1;
183+}
184+
185+static ssize_t led_mode_store(struct device *dev,
186+ struct device_attribute *attr, const char *buf, size_t size)
187+{
188+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
189+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
190+ char copybuf[128];
191+ int new_mode = -1;
192+ char *p, *token;
193+
194+ /* take a copy since we don't want to trash the inbound buffer when using strsep */
195+ strncpy(copybuf, buf, sizeof(copybuf));
196+ copybuf[sizeof(copybuf) - 1] = 0;
197+ p = copybuf;
198+
199+ while ((token = strsep(&p, " \t\n")) != NULL) {
200+ if (!*token)
201+ continue;
202+
203+ if (new_mode == -1)
204+ new_mode = 0;
205+
206+ if (!strcmp(token, "none"))
207+ new_mode = 0;
208+ else if (!strcmp(token, "tx"))
209+ new_mode |= MODE_TX;
210+ else if (!strcmp(token, "rx"))
211+ new_mode |= MODE_RX;
212+ else if (!strcmp(token, "link"))
213+ new_mode |= MODE_LINK;
214+ else
215+ return -EINVAL;
216+ }
217+
218+ if (new_mode == -1)
219+ return -EINVAL;
220+
221+ write_lock(&trigger_data->lock);
222+ trigger_data->mode = new_mode;
223+ set_baseline_state(trigger_data);
224+ write_unlock(&trigger_data->lock);
225+
226+ return size;
227+}
228+
229+static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
230+
231+static ssize_t led_interval_show(struct device *dev,
232+ struct device_attribute *attr, char *buf)
233+{
234+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
235+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
236+
237+ read_lock(&trigger_data->lock);
238+ sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
239+ read_unlock(&trigger_data->lock);
240+
241+ return strlen(buf) + 1;
242+}
243+
244+static ssize_t led_interval_store(struct device *dev,
245+ struct device_attribute *attr, const char *buf, size_t size)
246+{
247+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
248+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
249+ int ret = -EINVAL;
250+ char *after;
251+ unsigned long value = simple_strtoul(buf, &after, 10);
252+ size_t count = after - buf;
253+
254+ if (isspace(*after))
255+ count++;
256+
257+ /* impose some basic bounds on the timer interval */
258+ if (count == size && value >= 5 && value <= 10000) {
259+ write_lock(&trigger_data->lock);
260+ trigger_data->interval = msecs_to_jiffies(value);
261+ set_baseline_state(trigger_data); /* resets timer */
262+ write_unlock(&trigger_data->lock);
263+ ret = count;
264+ }
265+
266+ return ret;
267+}
268+
269+static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
270+
271+static int netdev_trig_notify(struct notifier_block *nb,
272+ unsigned long evt,
273+ void *dv)
274+{
275+ struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv);
276+ struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
277+
278+ if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER)
279+ return NOTIFY_DONE;
280+
281+ write_lock(&trigger_data->lock);
282+
283+ if (strcmp(dev->name, trigger_data->device_name))
284+ goto done;
285+
286+ if (evt == NETDEV_REGISTER) {
287+ if (trigger_data->net_dev != NULL)
288+ dev_put(trigger_data->net_dev);
289+ dev_hold(dev);
290+ trigger_data->net_dev = dev;
291+ trigger_data->link_up = 0;
292+ goto done;
293+ }
294+
295+ if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) {
296+ dev_put(trigger_data->net_dev);
297+ trigger_data->net_dev = NULL;
298+ goto done;
299+ }
300+
301+ /* UP / DOWN / CHANGE */
302+
303+ trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
304+ set_baseline_state(trigger_data);
305+
306+done:
307+ write_unlock(&trigger_data->lock);
308+ return NOTIFY_DONE;
309+}
310+
311+/* here's the real work! */
312+static void netdev_trig_timer(unsigned long arg)
313+{
314+ struct led_netdev_data *trigger_data = (struct led_netdev_data *)arg;
315+ struct rtnl_link_stats64 *dev_stats;
316+ unsigned new_activity;
317+ struct rtnl_link_stats64 temp;
318+
319+ write_lock(&trigger_data->lock);
320+
321+ if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
322+ /* we don't need to do timer work, just reflect link state. */
323+ led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
324+ goto no_restart;
325+ }
326+
327+ dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
328+ new_activity =
329+ ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
330+ ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
331+
332+ if (trigger_data->mode & MODE_LINK) {
333+ /* base state is ON (link present) */
334+ /* if there's no link, we don't get this far and the LED is off */
335+
336+ /* OFF -> ON always */
337+ /* ON -> OFF on activity */
338+ if (trigger_data->led_cdev->brightness == LED_OFF) {
339+ led_set_brightness(trigger_data->led_cdev, LED_FULL);
340+ } else if (trigger_data->last_activity != new_activity) {
341+ led_set_brightness(trigger_data->led_cdev, LED_OFF);
342+ }
343+ } else {
344+ /* base state is OFF */
345+ /* ON -> OFF always */
346+ /* OFF -> ON on activity */
347+ if (trigger_data->led_cdev->brightness == LED_FULL) {
348+ led_set_brightness(trigger_data->led_cdev, LED_OFF);
349+ } else if (trigger_data->last_activity != new_activity) {
350+ led_set_brightness(trigger_data->led_cdev, LED_FULL);
351+ }
352+ }
353+
354+ trigger_data->last_activity = new_activity;
355+ mod_timer(&trigger_data->timer, jiffies + trigger_data->interval);
356+
357+no_restart:
358+ write_unlock(&trigger_data->lock);
359+}
360+
361+static void netdev_trig_activate(struct led_classdev *led_cdev)
362+{
363+ struct led_netdev_data *trigger_data;
364+ int rc;
365+
366+ trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
367+ if (!trigger_data)
368+ return;
369+
370+ rwlock_init(&trigger_data->lock);
371+
372+ trigger_data->notifier.notifier_call = netdev_trig_notify;
373+ trigger_data->notifier.priority = 10;
374+
375+ setup_timer(&trigger_data->timer, netdev_trig_timer, (unsigned long) trigger_data);
376+
377+ trigger_data->led_cdev = led_cdev;
378+ trigger_data->net_dev = NULL;
379+ trigger_data->device_name[0] = 0;
380+
381+ trigger_data->mode = 0;
382+ trigger_data->interval = msecs_to_jiffies(50);
383+ trigger_data->link_up = 0;
384+ trigger_data->last_activity = 0;
385+
386+ led_cdev->trigger_data = trigger_data;
387+
388+ rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
389+ if (rc)
390+ goto err_out;
391+ rc = device_create_file(led_cdev->dev, &dev_attr_mode);
392+ if (rc)
393+ goto err_out_device_name;
394+ rc = device_create_file(led_cdev->dev, &dev_attr_interval);
395+ if (rc)
396+ goto err_out_mode;
397+
398+ register_netdevice_notifier(&trigger_data->notifier);
399+ return;
400+
401+err_out_mode:
402+ device_remove_file(led_cdev->dev, &dev_attr_mode);
403+err_out_device_name:
404+ device_remove_file(led_cdev->dev, &dev_attr_device_name);
405+err_out:
406+ led_cdev->trigger_data = NULL;
407+ kfree(trigger_data);
408+}
409+
410+static void netdev_trig_deactivate(struct led_classdev *led_cdev)
411+{
412+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
413+
414+ if (trigger_data) {
415+ unregister_netdevice_notifier(&trigger_data->notifier);
416+
417+ device_remove_file(led_cdev->dev, &dev_attr_device_name);
418+ device_remove_file(led_cdev->dev, &dev_attr_mode);
419+ device_remove_file(led_cdev->dev, &dev_attr_interval);
420+
421+ write_lock(&trigger_data->lock);
422+
423+ if (trigger_data->net_dev) {
424+ dev_put(trigger_data->net_dev);
425+ trigger_data->net_dev = NULL;
426+ }
427+
428+ write_unlock(&trigger_data->lock);
429+
430+ del_timer_sync(&trigger_data->timer);
431+
432+ kfree(trigger_data);
433+ }
434+}
435+
436+static struct led_trigger netdev_led_trigger = {
437+ .name = "netdev",
438+ .activate = netdev_trig_activate,
439+ .deactivate = netdev_trig_deactivate,
440+};
441+
442+static int __init netdev_trig_init(void)
443+{
444+ return led_trigger_register(&netdev_led_trigger);
445+}
446+
447+static void __exit netdev_trig_exit(void)
448+{
449+ led_trigger_unregister(&netdev_led_trigger);
450+}
451+
452+module_init(netdev_trig_init);
453+module_exit(netdev_trig_exit);
454+
455+MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
456+MODULE_DESCRIPTION("Netdev LED trigger");
457+MODULE_LICENSE("GPL");
458diff -Naur linux-3.14.22.org/drivers/leds/trigger/Makefile linux-3.14.22/drivers/leds/trigger/Makefile
459--- linux-3.14.22.org/drivers/leds/trigger/Makefile 2014-10-15 08:42:04.000000000 +0200
460+++ linux-3.14.22/drivers/leds/trigger/Makefile 2014-11-06 17:51:29.818748360 +0100
461@@ -8,3 +8,4 @@
462 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
463 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
464 obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
465+obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o