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