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