]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/linux/linux-4.9.16-ledtrig_netdev.patch
Merge remote-tracking branch 'origin/next' into kernel-4.14
[ipfire-2.x.git] / src / patches / linux / linux-4.9.16-ledtrig_netdev.patch
CommitLineData
94112e8b
AF
1diff -Naur linux-4.9.16.org/drivers/leds/trigger/Kconfig linux-4.9.16/drivers/leds/trigger/Kconfig
2--- linux-4.9.16.org/drivers/leds/trigger/Kconfig 2017-03-18 12:15:30.000000000 +0100
3+++ linux-4.9.16/drivers/leds/trigger/Kconfig 2017-03-18 16:53:42.290859631 +0100
91648bd1
AF
4@@ -126,4 +126,11 @@
5 a different trigger.
817317ed
AF
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
94112e8b
AF
16diff -Naur linux-4.9.16.org/drivers/leds/trigger/ledtrig-netdev.c linux-4.9.16/drivers/leds/trigger/ledtrig-netdev.c
17--- linux-4.9.16.org/drivers/leds/trigger/ledtrig-netdev.c 1970-01-01 01:00:00.000000000 +0100
18+++ linux-4.9.16/drivers/leds/trigger/ledtrig-netdev.c 2017-03-18 16:53:44.280859607 +0100
19@@ -0,0 +1,444 @@
817317ed
AF
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 {
94112e8b 90+ spinlock_t lock;
817317ed
AF
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);
817317ed
AF
114+}
115+
116+static ssize_t led_device_name_show(struct device *dev,
117+ struct device_attribute *attr, char *buf)
118+{
119+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
120+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
121+
94112e8b 122+ spin_lock_bh(&trigger_data->lock);
817317ed 123+ sprintf(buf, "%s\n", trigger_data->device_name);
94112e8b 124+ spin_unlock_bh(&trigger_data->lock);
817317ed
AF
125+
126+ return strlen(buf) + 1;
127+}
128+
129+static ssize_t led_device_name_store(struct device *dev,
130+ struct device_attribute *attr, const char *buf, size_t size)
131+{
132+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
133+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
134+
135+ if (size < 0 || size >= IFNAMSIZ)
136+ return -EINVAL;
137+
94112e8b
AF
138+ spin_lock_bh(&trigger_data->lock);
139+ del_timer_sync(&trigger_data->timer);
817317ed
AF
140+
141+ strcpy(trigger_data->device_name, buf);
142+ if (size > 0 && trigger_data->device_name[size-1] == '\n')
143+ trigger_data->device_name[size-1] = 0;
94112e8b
AF
144+ trigger_data->link_up = 0;
145+ trigger_data->last_activity = 0;
817317ed
AF
146+
147+ if (trigger_data->device_name[0] != 0) {
148+ /* check for existing device to update from */
149+ trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
150+ if (trigger_data->net_dev != NULL)
151+ trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
817317ed
AF
152+ }
153+
94112e8b
AF
154+ set_baseline_state(trigger_data);
155+ spin_unlock_bh(&trigger_data->lock);
156+
817317ed
AF
157+ return size;
158+}
159+
160+static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
161+
162+static ssize_t led_mode_show(struct device *dev,
163+ struct device_attribute *attr, char *buf)
164+{
165+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
166+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
167+
94112e8b 168+ spin_lock_bh(&trigger_data->lock);
817317ed
AF
169+
170+ if (trigger_data->mode == 0) {
171+ strcpy(buf, "none\n");
172+ } else {
173+ if (trigger_data->mode & MODE_LINK)
174+ strcat(buf, "link ");
175+ if (trigger_data->mode & MODE_TX)
176+ strcat(buf, "tx ");
177+ if (trigger_data->mode & MODE_RX)
178+ strcat(buf, "rx ");
179+ strcat(buf, "\n");
180+ }
181+
94112e8b 182+ spin_unlock_bh(&trigger_data->lock);
817317ed
AF
183+
184+ return strlen(buf)+1;
185+}
186+
187+static ssize_t led_mode_store(struct device *dev,
188+ struct device_attribute *attr, const char *buf, size_t size)
189+{
190+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
191+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
192+ char copybuf[128];
193+ int new_mode = -1;
194+ char *p, *token;
195+
196+ /* take a copy since we don't want to trash the inbound buffer when using strsep */
197+ strncpy(copybuf, buf, sizeof(copybuf));
198+ copybuf[sizeof(copybuf) - 1] = 0;
199+ p = copybuf;
200+
201+ while ((token = strsep(&p, " \t\n")) != NULL) {
202+ if (!*token)
203+ continue;
204+
205+ if (new_mode == -1)
206+ new_mode = 0;
207+
208+ if (!strcmp(token, "none"))
209+ new_mode = 0;
210+ else if (!strcmp(token, "tx"))
211+ new_mode |= MODE_TX;
212+ else if (!strcmp(token, "rx"))
213+ new_mode |= MODE_RX;
214+ else if (!strcmp(token, "link"))
215+ new_mode |= MODE_LINK;
216+ else
217+ return -EINVAL;
218+ }
219+
220+ if (new_mode == -1)
221+ return -EINVAL;
222+
94112e8b
AF
223+ spin_lock_bh(&trigger_data->lock);
224+ del_timer_sync(&trigger_data->timer);
225+
817317ed 226+ trigger_data->mode = new_mode;
94112e8b 227+
817317ed 228+ set_baseline_state(trigger_data);
94112e8b 229+ spin_unlock_bh(&trigger_data->lock);
817317ed
AF
230+
231+ return size;
232+}
233+
234+static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
235+
236+static ssize_t led_interval_show(struct device *dev,
237+ struct device_attribute *attr, char *buf)
238+{
239+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
240+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
241+
94112e8b 242+ spin_lock_bh(&trigger_data->lock);
817317ed 243+ sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
94112e8b 244+ spin_unlock_bh(&trigger_data->lock);
817317ed
AF
245+
246+ return strlen(buf) + 1;
247+}
248+
249+static ssize_t led_interval_store(struct device *dev,
250+ struct device_attribute *attr, const char *buf, size_t size)
251+{
252+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
253+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
254+ int ret = -EINVAL;
255+ char *after;
256+ unsigned long value = simple_strtoul(buf, &after, 10);
257+ size_t count = after - buf;
258+
259+ if (isspace(*after))
260+ count++;
261+
262+ /* impose some basic bounds on the timer interval */
263+ if (count == size && value >= 5 && value <= 10000) {
94112e8b
AF
264+ spin_lock_bh(&trigger_data->lock);
265+ del_timer_sync(&trigger_data->timer);
266+
817317ed 267+ trigger_data->interval = msecs_to_jiffies(value);
94112e8b 268+
817317ed 269+ set_baseline_state(trigger_data); /* resets timer */
94112e8b
AF
270+ spin_unlock_bh(&trigger_data->lock);
271+
817317ed
AF
272+ ret = count;
273+ }
274+
275+ return ret;
276+}
277+
278+static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
279+
280+static int netdev_trig_notify(struct notifier_block *nb,
281+ unsigned long evt,
282+ void *dv)
283+{
284+ struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv);
285+ struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
286+
94112e8b 287+ if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER && evt != NETDEV_CHANGENAME)
817317ed
AF
288+ return NOTIFY_DONE;
289+
94112e8b 290+ spin_lock_bh(&trigger_data->lock);
817317ed
AF
291+
292+ if (strcmp(dev->name, trigger_data->device_name))
293+ goto done;
294+
94112e8b
AF
295+ del_timer_sync(&trigger_data->timer);
296+
297+ if (evt == NETDEV_REGISTER || evt == NETDEV_CHANGENAME) {
817317ed
AF
298+ if (trigger_data->net_dev != NULL)
299+ dev_put(trigger_data->net_dev);
94112e8b 300+
817317ed
AF
301+ dev_hold(dev);
302+ trigger_data->net_dev = dev;
303+ trigger_data->link_up = 0;
304+ goto done;
305+ }
306+
307+ if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) {
308+ dev_put(trigger_data->net_dev);
309+ trigger_data->net_dev = NULL;
310+ goto done;
311+ }
312+
313+ /* UP / DOWN / CHANGE */
314+
315+ trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
316+ set_baseline_state(trigger_data);
317+
318+done:
94112e8b 319+ spin_unlock_bh(&trigger_data->lock);
817317ed
AF
320+ return NOTIFY_DONE;
321+}
322+
323+/* here's the real work! */
324+static void netdev_trig_timer(unsigned long arg)
325+{
326+ struct led_netdev_data *trigger_data = (struct led_netdev_data *)arg;
327+ struct rtnl_link_stats64 *dev_stats;
328+ unsigned new_activity;
329+ struct rtnl_link_stats64 temp;
330+
817317ed
AF
331+ if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
332+ /* we don't need to do timer work, just reflect link state. */
333+ led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
94112e8b 334+ return;
817317ed
AF
335+ }
336+
337+ dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
338+ new_activity =
339+ ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
340+ ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
341+
342+ if (trigger_data->mode & MODE_LINK) {
343+ /* base state is ON (link present) */
344+ /* if there's no link, we don't get this far and the LED is off */
345+
346+ /* OFF -> ON always */
347+ /* ON -> OFF on activity */
348+ if (trigger_data->led_cdev->brightness == LED_OFF) {
349+ led_set_brightness(trigger_data->led_cdev, LED_FULL);
350+ } else if (trigger_data->last_activity != new_activity) {
351+ led_set_brightness(trigger_data->led_cdev, LED_OFF);
352+ }
353+ } else {
354+ /* base state is OFF */
355+ /* ON -> OFF always */
356+ /* OFF -> ON on activity */
357+ if (trigger_data->led_cdev->brightness == LED_FULL) {
358+ led_set_brightness(trigger_data->led_cdev, LED_OFF);
359+ } else if (trigger_data->last_activity != new_activity) {
360+ led_set_brightness(trigger_data->led_cdev, LED_FULL);
361+ }
362+ }
363+
364+ trigger_data->last_activity = new_activity;
365+ mod_timer(&trigger_data->timer, jiffies + trigger_data->interval);
817317ed
AF
366+}
367+
368+static void netdev_trig_activate(struct led_classdev *led_cdev)
369+{
370+ struct led_netdev_data *trigger_data;
371+ int rc;
372+
373+ trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
374+ if (!trigger_data)
375+ return;
376+
94112e8b 377+ spin_lock_init(&trigger_data->lock);
817317ed
AF
378+
379+ trigger_data->notifier.notifier_call = netdev_trig_notify;
380+ trigger_data->notifier.priority = 10;
381+
382+ setup_timer(&trigger_data->timer, netdev_trig_timer, (unsigned long) trigger_data);
383+
384+ trigger_data->led_cdev = led_cdev;
385+ trigger_data->net_dev = NULL;
386+ trigger_data->device_name[0] = 0;
387+
388+ trigger_data->mode = 0;
389+ trigger_data->interval = msecs_to_jiffies(50);
390+ trigger_data->link_up = 0;
391+ trigger_data->last_activity = 0;
392+
393+ led_cdev->trigger_data = trigger_data;
394+
395+ rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
396+ if (rc)
397+ goto err_out;
398+ rc = device_create_file(led_cdev->dev, &dev_attr_mode);
399+ if (rc)
400+ goto err_out_device_name;
401+ rc = device_create_file(led_cdev->dev, &dev_attr_interval);
402+ if (rc)
403+ goto err_out_mode;
404+
405+ register_netdevice_notifier(&trigger_data->notifier);
406+ return;
407+
408+err_out_mode:
409+ device_remove_file(led_cdev->dev, &dev_attr_mode);
410+err_out_device_name:
411+ device_remove_file(led_cdev->dev, &dev_attr_device_name);
412+err_out:
413+ led_cdev->trigger_data = NULL;
414+ kfree(trigger_data);
415+}
416+
417+static void netdev_trig_deactivate(struct led_classdev *led_cdev)
418+{
419+ struct led_netdev_data *trigger_data = led_cdev->trigger_data;
420+
421+ if (trigger_data) {
422+ unregister_netdevice_notifier(&trigger_data->notifier);
423+
424+ device_remove_file(led_cdev->dev, &dev_attr_device_name);
425+ device_remove_file(led_cdev->dev, &dev_attr_mode);
426+ device_remove_file(led_cdev->dev, &dev_attr_interval);
427+
94112e8b
AF
428+ spin_lock_bh(&trigger_data->lock);
429+ del_timer_sync(&trigger_data->timer);
817317ed
AF
430+
431+ if (trigger_data->net_dev) {
432+ dev_put(trigger_data->net_dev);
433+ trigger_data->net_dev = NULL;
434+ }
435+
94112e8b 436+ spin_unlock_bh(&trigger_data->lock);
817317ed
AF
437+
438+ kfree(trigger_data);
439+ }
440+}
441+
442+static struct led_trigger netdev_led_trigger = {
443+ .name = "netdev",
444+ .activate = netdev_trig_activate,
445+ .deactivate = netdev_trig_deactivate,
446+};
447+
448+static int __init netdev_trig_init(void)
449+{
450+ return led_trigger_register(&netdev_led_trigger);
451+}
452+
453+static void __exit netdev_trig_exit(void)
454+{
455+ led_trigger_unregister(&netdev_led_trigger);
456+}
457+
458+module_init(netdev_trig_init);
459+module_exit(netdev_trig_exit);
460+
461+MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
462+MODULE_DESCRIPTION("Netdev LED trigger");
463+MODULE_LICENSE("GPL");
94112e8b
AF
464diff -Naur linux-4.9.16.org/drivers/leds/trigger/Makefile linux-4.9.16/drivers/leds/trigger/Makefile
465--- linux-4.9.16.org/drivers/leds/trigger/Makefile 2017-03-18 12:15:30.000000000 +0100
466+++ linux-4.9.16/drivers/leds/trigger/Makefile 2017-03-18 16:53:44.280859607 +0100
91648bd1 467@@ -10,3 +10,4 @@
817317ed
AF
468 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
469 obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
91648bd1 470 obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
817317ed 471+obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o