aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-led-trigger-netdev45
-rw-r--r--drivers/leds/trigger/Kconfig7
-rw-r--r--drivers/leds/trigger/Makefile1
-rw-r--r--drivers/leds/trigger/ledtrig-netdev.c496
4 files changed, 549 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led-trigger-netdev b/Documentation/ABI/testing/sysfs-class-led-trigger-netdev
new file mode 100644
index 000000000000..451af6d6768c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-led-trigger-netdev
@@ -0,0 +1,45 @@
1What: /sys/class/leds/<led>/device_name
2Date: Dec 2017
3KernelVersion: 4.16
4Contact: linux-leds@vger.kernel.org
5Description:
6 Specifies the network device name to monitor.
7
8What: /sys/class/leds/<led>/interval
9Date: Dec 2017
10KernelVersion: 4.16
11Contact: linux-leds@vger.kernel.org
12Description:
13 Specifies the duration of the LED blink in milliseconds.
14 Defaults to 50 ms.
15
16What: /sys/class/leds/<led>/link
17Date: Dec 2017
18KernelVersion: 4.16
19Contact: linux-leds@vger.kernel.org
20Description:
21 Signal the link state of the named network device.
22 If set to 0 (default), the LED's normal state is off.
23 If set to 1, the LED's normal state reflects the link state
24 of the named network device.
25 Setting this value also immediately changes the LED state.
26
27What: /sys/class/leds/<led>/tx
28Date: Dec 2017
29KernelVersion: 4.16
30Contact: linux-leds@vger.kernel.org
31Description:
32 Signal transmission of data on the named network device.
33 If set to 0 (default), the LED will not blink on transmission.
34 If set to 1, the LED will blink for the milliseconds specified
35 in interval to signal transmission.
36
37What: /sys/class/leds/<led>/rx
38Date: Dec 2017
39KernelVersion: 4.16
40Contact: linux-leds@vger.kernel.org
41Description:
42 Signal reception of data on the named network device.
43 If set to 0 (default), the LED will not blink on reception.
44 If set to 1, the LED will blink for the milliseconds specified
45 in interval to signal reception.
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index 6dac48c457d1..a2559b4fdfff 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -135,4 +135,11 @@ config LEDS_TRIGGER_PANIC
135 a different trigger. 135 a different trigger.
136 If unsure, say Y. 136 If unsure, say Y.
137 137
138config LEDS_TRIGGER_NETDEV
139 tristate "LED Netdev Trigger"
140 depends on NET && LEDS_TRIGGERS
141 help
142 This allows LEDs to be controlled by network device activity.
143 If unsure, say Y.
144
138endif # LEDS_TRIGGERS 145endif # LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
index 4a8b6cff7761..f3cfe1950538 100644
--- a/drivers/leds/trigger/Makefile
+++ b/drivers/leds/trigger/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
12obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o 12obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
13obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o 13obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
14obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o 14obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
15obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
new file mode 100644
index 000000000000..6df4781a6308
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -0,0 +1,496 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright 2017 Ben Whitten <ben.whitten@gmail.com>
3// Copyright 2007 Oliver Jowett <oliver@opencloud.com>
4//
5// LED Kernel Netdev Trigger
6//
7// Toggles the LED to reflect the link and traffic state of a named net device
8//
9// Derived from ledtrig-timer.c which is:
10// Copyright 2005-2006 Openedhand Ltd.
11// Author: Richard Purdie <rpurdie@openedhand.com>
12
13#include <linux/atomic.h>
14#include <linux/ctype.h>
15#include <linux/device.h>
16#include <linux/init.h>
17#include <linux/jiffies.h>
18#include <linux/kernel.h>
19#include <linux/leds.h>
20#include <linux/list.h>
21#include <linux/module.h>
22#include <linux/netdevice.h>
23#include <linux/spinlock.h>
24#include <linux/timer.h>
25#include "../leds.h"
26
27/*
28 * Configurable sysfs attributes:
29 *
30 * device_name - network device name to monitor
31 * interval - duration of LED blink, in milliseconds
32 * link - LED's normal state reflects whether the link is up
33 * (has carrier) or not
34 * tx - LED blinks on transmitted data
35 * rx - LED blinks on receive data
36 *
37 */
38
39struct led_netdev_data {
40 spinlock_t lock;
41
42 struct delayed_work work;
43 struct notifier_block notifier;
44
45 struct led_classdev *led_cdev;
46 struct net_device *net_dev;
47
48 char device_name[IFNAMSIZ];
49 atomic_t interval;
50 unsigned int last_activity;
51
52 unsigned long mode;
53#define NETDEV_LED_LINK 0
54#define NETDEV_LED_TX 1
55#define NETDEV_LED_RX 2
56#define NETDEV_LED_MODE_LINKUP 3
57};
58
59enum netdev_led_attr {
60 NETDEV_ATTR_LINK,
61 NETDEV_ATTR_TX,
62 NETDEV_ATTR_RX
63};
64
65static void set_baseline_state(struct led_netdev_data *trigger_data)
66{
67 int current_brightness;
68 struct led_classdev *led_cdev = trigger_data->led_cdev;
69
70 current_brightness = led_cdev->brightness;
71 if (current_brightness)
72 led_cdev->blink_brightness = current_brightness;
73 if (!led_cdev->blink_brightness)
74 led_cdev->blink_brightness = led_cdev->max_brightness;
75
76 if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode))
77 led_set_brightness(led_cdev, LED_OFF);
78 else {
79 if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
80 led_set_brightness(led_cdev,
81 led_cdev->blink_brightness);
82 else
83 led_set_brightness(led_cdev, LED_OFF);
84
85 /* If we are looking for RX/TX start periodically
86 * checking stats
87 */
88 if (test_bit(NETDEV_LED_TX, &trigger_data->mode) ||
89 test_bit(NETDEV_LED_RX, &trigger_data->mode))
90 schedule_delayed_work(&trigger_data->work, 0);
91 }
92}
93
94static ssize_t device_name_show(struct device *dev,
95 struct device_attribute *attr, char *buf)
96{
97 struct led_classdev *led_cdev = dev_get_drvdata(dev);
98 struct led_netdev_data *trigger_data = led_cdev->trigger_data;
99 ssize_t len;
100
101 spin_lock_bh(&trigger_data->lock);
102 len = sprintf(buf, "%s\n", trigger_data->device_name);
103 spin_unlock_bh(&trigger_data->lock);
104
105 return len;
106}
107
108static ssize_t device_name_store(struct device *dev,
109 struct device_attribute *attr, const char *buf,
110 size_t size)
111{
112 struct led_classdev *led_cdev = dev_get_drvdata(dev);
113 struct led_netdev_data *trigger_data = led_cdev->trigger_data;
114
115 if (size >= IFNAMSIZ)
116 return -EINVAL;
117
118 cancel_delayed_work_sync(&trigger_data->work);
119
120 spin_lock_bh(&trigger_data->lock);
121
122 if (trigger_data->net_dev) {
123 dev_put(trigger_data->net_dev);
124 trigger_data->net_dev = NULL;
125 }
126
127 strncpy(trigger_data->device_name, buf, size);
128 if (size > 0 && trigger_data->device_name[size - 1] == '\n')
129 trigger_data->device_name[size - 1] = 0;
130
131 if (trigger_data->device_name[0] != 0)
132 trigger_data->net_dev =
133 dev_get_by_name(&init_net, trigger_data->device_name);
134
135 clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
136 if (trigger_data->net_dev != NULL)
137 if (netif_carrier_ok(trigger_data->net_dev))
138 set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
139
140 trigger_data->last_activity = 0;
141
142 set_baseline_state(trigger_data);
143 spin_unlock_bh(&trigger_data->lock);
144
145 return size;
146}
147
148static DEVICE_ATTR_RW(device_name);
149
150static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
151 enum netdev_led_attr attr)
152{
153 struct led_classdev *led_cdev = dev_get_drvdata(dev);
154 struct led_netdev_data *trigger_data = led_cdev->trigger_data;
155 int bit;
156
157 switch (attr) {
158 case NETDEV_ATTR_LINK:
159 bit = NETDEV_LED_LINK;
160 break;
161 case NETDEV_ATTR_TX:
162 bit = NETDEV_LED_TX;
163 break;
164 case NETDEV_ATTR_RX:
165 bit = NETDEV_LED_RX;
166 break;
167 default:
168 return -EINVAL;
169 }
170
171 return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
172}
173
174static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
175 size_t size, enum netdev_led_attr attr)
176{
177 struct led_classdev *led_cdev = dev_get_drvdata(dev);
178 struct led_netdev_data *trigger_data = led_cdev->trigger_data;
179 unsigned long state;
180 int ret;
181 int bit;
182
183 ret = kstrtoul(buf, 0, &state);
184 if (ret)
185 return ret;
186
187 switch (attr) {
188 case NETDEV_ATTR_LINK:
189 bit = NETDEV_LED_LINK;
190 break;
191 case NETDEV_ATTR_TX:
192 bit = NETDEV_LED_TX;
193 break;
194 case NETDEV_ATTR_RX:
195 bit = NETDEV_LED_RX;
196 break;
197 default:
198 return -EINVAL;
199 }
200
201 cancel_delayed_work_sync(&trigger_data->work);
202
203 if (state)
204 set_bit(bit, &trigger_data->mode);
205 else
206 clear_bit(bit, &trigger_data->mode);
207
208 set_baseline_state(trigger_data);
209
210 return size;
211}
212
213static ssize_t link_show(struct device *dev,
214 struct device_attribute *attr, char *buf)
215{
216 return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
217}
218
219static ssize_t link_store(struct device *dev,
220 struct device_attribute *attr, const char *buf, size_t size)
221{
222 return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
223}
224
225static DEVICE_ATTR_RW(link);
226
227static ssize_t tx_show(struct device *dev,
228 struct device_attribute *attr, char *buf)
229{
230 return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
231}
232
233static ssize_t tx_store(struct device *dev,
234 struct device_attribute *attr, const char *buf, size_t size)
235{
236 return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
237}
238
239static DEVICE_ATTR_RW(tx);
240
241static ssize_t rx_show(struct device *dev,
242 struct device_attribute *attr, char *buf)
243{
244 return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
245}
246
247static ssize_t rx_store(struct device *dev,
248 struct device_attribute *attr, const char *buf, size_t size)
249{
250 return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
251}
252
253static DEVICE_ATTR_RW(rx);
254
255static ssize_t interval_show(struct device *dev,
256 struct device_attribute *attr, char *buf)
257{
258 struct led_classdev *led_cdev = dev_get_drvdata(dev);
259 struct led_netdev_data *trigger_data = led_cdev->trigger_data;
260
261 return sprintf(buf, "%u\n",
262 jiffies_to_msecs(atomic_read(&trigger_data->interval)));
263}
264
265static ssize_t interval_store(struct device *dev,
266 struct device_attribute *attr, const char *buf,
267 size_t size)
268{
269 struct led_classdev *led_cdev = dev_get_drvdata(dev);
270 struct led_netdev_data *trigger_data = led_cdev->trigger_data;
271 unsigned long value;
272 int ret;
273
274 ret = kstrtoul(buf, 0, &value);
275 if (ret)
276 return ret;
277
278 /* impose some basic bounds on the timer interval */
279 if (value >= 5 && value <= 10000) {
280 cancel_delayed_work_sync(&trigger_data->work);
281
282 atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
283 set_baseline_state(trigger_data); /* resets timer */
284 }
285
286 return size;
287}
288
289static DEVICE_ATTR_RW(interval);
290
291static int netdev_trig_notify(struct notifier_block *nb,
292 unsigned long evt, void *dv)
293{
294 struct net_device *dev =
295 netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
296 struct led_netdev_data *trigger_data = container_of(nb,
297 struct
298 led_netdev_data,
299 notifier);
300
301 if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
302 && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
303 && evt != NETDEV_CHANGENAME)
304 return NOTIFY_DONE;
305
306 if (strcmp(dev->name, trigger_data->device_name))
307 return NOTIFY_DONE;
308
309 cancel_delayed_work_sync(&trigger_data->work);
310
311 spin_lock_bh(&trigger_data->lock);
312
313 clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
314 switch (evt) {
315 case NETDEV_REGISTER:
316 if (trigger_data->net_dev)
317 dev_put(trigger_data->net_dev);
318 dev_hold(dev);
319 trigger_data->net_dev = dev;
320 break;
321 case NETDEV_CHANGENAME:
322 case NETDEV_UNREGISTER:
323 if (trigger_data->net_dev) {
324 dev_put(trigger_data->net_dev);
325 trigger_data->net_dev = NULL;
326 }
327 break;
328 case NETDEV_UP:
329 case NETDEV_CHANGE:
330 if (netif_carrier_ok(dev))
331 set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
332 break;
333 }
334
335 set_baseline_state(trigger_data);
336
337 spin_unlock_bh(&trigger_data->lock);
338
339 return NOTIFY_DONE;
340}
341
342/* here's the real work! */
343static void netdev_trig_work(struct work_struct *work)
344{
345 struct led_netdev_data *trigger_data = container_of(work,
346 struct
347 led_netdev_data,
348 work.work);
349 struct rtnl_link_stats64 *dev_stats;
350 unsigned int new_activity;
351 struct rtnl_link_stats64 temp;
352 unsigned long interval;
353 int invert;
354
355 /* If we dont have a device, insure we are off */
356 if (!trigger_data->net_dev) {
357 led_set_brightness(trigger_data->led_cdev, LED_OFF);
358 return;
359 }
360
361 /* If we are not looking for RX/TX then return */
362 if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
363 !test_bit(NETDEV_LED_RX, &trigger_data->mode))
364 return;
365
366 dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
367 new_activity =
368 (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
369 dev_stats->tx_packets : 0) +
370 (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
371 dev_stats->rx_packets : 0);
372
373 if (trigger_data->last_activity != new_activity) {
374 led_stop_software_blink(trigger_data->led_cdev);
375
376 invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
377 interval = jiffies_to_msecs(
378 atomic_read(&trigger_data->interval));
379 /* base state is ON (link present) */
380 led_blink_set_oneshot(trigger_data->led_cdev,
381 &interval,
382 &interval,
383 invert);
384 trigger_data->last_activity = new_activity;
385 }
386
387 schedule_delayed_work(&trigger_data->work,
388 (atomic_read(&trigger_data->interval)*2));
389}
390
391static void netdev_trig_activate(struct led_classdev *led_cdev)
392{
393 struct led_netdev_data *trigger_data;
394 int rc;
395
396 trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
397 if (!trigger_data)
398 return;
399
400 spin_lock_init(&trigger_data->lock);
401
402 trigger_data->notifier.notifier_call = netdev_trig_notify;
403 trigger_data->notifier.priority = 10;
404
405 INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
406
407 trigger_data->led_cdev = led_cdev;
408 trigger_data->net_dev = NULL;
409 trigger_data->device_name[0] = 0;
410
411 trigger_data->mode = 0;
412 atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
413 trigger_data->last_activity = 0;
414
415 led_cdev->trigger_data = trigger_data;
416
417 rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
418 if (rc)
419 goto err_out;
420 rc = device_create_file(led_cdev->dev, &dev_attr_link);
421 if (rc)
422 goto err_out_device_name;
423 rc = device_create_file(led_cdev->dev, &dev_attr_rx);
424 if (rc)
425 goto err_out_link;
426 rc = device_create_file(led_cdev->dev, &dev_attr_tx);
427 if (rc)
428 goto err_out_rx;
429 rc = device_create_file(led_cdev->dev, &dev_attr_interval);
430 if (rc)
431 goto err_out_tx;
432 rc = register_netdevice_notifier(&trigger_data->notifier);
433 if (rc)
434 goto err_out_interval;
435 return;
436
437err_out_interval:
438 device_remove_file(led_cdev->dev, &dev_attr_interval);
439err_out_tx:
440 device_remove_file(led_cdev->dev, &dev_attr_tx);
441err_out_rx:
442 device_remove_file(led_cdev->dev, &dev_attr_rx);
443err_out_link:
444 device_remove_file(led_cdev->dev, &dev_attr_link);
445err_out_device_name:
446 device_remove_file(led_cdev->dev, &dev_attr_device_name);
447err_out:
448 led_cdev->trigger_data = NULL;
449 kfree(trigger_data);
450}
451
452static void netdev_trig_deactivate(struct led_classdev *led_cdev)
453{
454 struct led_netdev_data *trigger_data = led_cdev->trigger_data;
455
456 if (trigger_data) {
457 unregister_netdevice_notifier(&trigger_data->notifier);
458
459 device_remove_file(led_cdev->dev, &dev_attr_device_name);
460 device_remove_file(led_cdev->dev, &dev_attr_link);
461 device_remove_file(led_cdev->dev, &dev_attr_rx);
462 device_remove_file(led_cdev->dev, &dev_attr_tx);
463 device_remove_file(led_cdev->dev, &dev_attr_interval);
464
465 cancel_delayed_work_sync(&trigger_data->work);
466
467 if (trigger_data->net_dev)
468 dev_put(trigger_data->net_dev);
469
470 kfree(trigger_data);
471 }
472}
473
474static struct led_trigger netdev_led_trigger = {
475 .name = "netdev",
476 .activate = netdev_trig_activate,
477 .deactivate = netdev_trig_deactivate,
478};
479
480static int __init netdev_trig_init(void)
481{
482 return led_trigger_register(&netdev_led_trigger);
483}
484
485static void __exit netdev_trig_exit(void)
486{
487 led_trigger_unregister(&netdev_led_trigger);
488}
489
490module_init(netdev_trig_init);
491module_exit(netdev_trig_exit);
492
493MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
494MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
495MODULE_DESCRIPTION("Netdev LED trigger");
496MODULE_LICENSE("GPL v2");