diff options
author | Ben Whitten <ben.whitten@gmail.com> | 2017-12-10 16:17:55 -0500 |
---|---|---|
committer | Jacek Anaszewski <jacek.anaszewski@gmail.com> | 2018-01-08 15:28:11 -0500 |
commit | 06f502f57d0d7728f9fa0f157ec5e4111ddb98f6 (patch) | |
tree | 8b393a5c1d6ba40be9c8ba45ffb24909bbdb052f /drivers/leds/trigger | |
parent | e0d422987936958b41999f62ea64f1dd0102606f (diff) |
leds: trigger: Introduce a NETDEV trigger
This commit introduces a NETDEV trigger for named device
activity. Available triggers are link, rx, and tx.
Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
Diffstat (limited to 'drivers/leds/trigger')
-rw-r--r-- | drivers/leds/trigger/Kconfig | 7 | ||||
-rw-r--r-- | drivers/leds/trigger/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-netdev.c | 496 |
3 files changed, 504 insertions, 0 deletions
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 | ||
138 | config 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 | |||
138 | endif # LEDS_TRIGGERS | 145 | endif # 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 | |||
12 | obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o | 12 | obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o |
13 | obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o | 13 | obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o |
14 | obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o | 14 | obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o |
15 | obj-$(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 | |||
39 | struct 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 | |||
59 | enum netdev_led_attr { | ||
60 | NETDEV_ATTR_LINK, | ||
61 | NETDEV_ATTR_TX, | ||
62 | NETDEV_ATTR_RX | ||
63 | }; | ||
64 | |||
65 | static 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 | |||
94 | static 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 | |||
108 | static 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 | |||
148 | static DEVICE_ATTR_RW(device_name); | ||
149 | |||
150 | static 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 | |||
174 | static 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 | |||
213 | static 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 | |||
219 | static 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 | |||
225 | static DEVICE_ATTR_RW(link); | ||
226 | |||
227 | static 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 | |||
233 | static 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 | |||
239 | static DEVICE_ATTR_RW(tx); | ||
240 | |||
241 | static 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 | |||
247 | static 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 | |||
253 | static DEVICE_ATTR_RW(rx); | ||
254 | |||
255 | static 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 | |||
265 | static 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 | |||
289 | static DEVICE_ATTR_RW(interval); | ||
290 | |||
291 | static 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! */ | ||
343 | static 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 | |||
391 | static 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 | |||
437 | err_out_interval: | ||
438 | device_remove_file(led_cdev->dev, &dev_attr_interval); | ||
439 | err_out_tx: | ||
440 | device_remove_file(led_cdev->dev, &dev_attr_tx); | ||
441 | err_out_rx: | ||
442 | device_remove_file(led_cdev->dev, &dev_attr_rx); | ||
443 | err_out_link: | ||
444 | device_remove_file(led_cdev->dev, &dev_attr_link); | ||
445 | err_out_device_name: | ||
446 | device_remove_file(led_cdev->dev, &dev_attr_device_name); | ||
447 | err_out: | ||
448 | led_cdev->trigger_data = NULL; | ||
449 | kfree(trigger_data); | ||
450 | } | ||
451 | |||
452 | static 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 | |||
474 | static struct led_trigger netdev_led_trigger = { | ||
475 | .name = "netdev", | ||
476 | .activate = netdev_trig_activate, | ||
477 | .deactivate = netdev_trig_deactivate, | ||
478 | }; | ||
479 | |||
480 | static int __init netdev_trig_init(void) | ||
481 | { | ||
482 | return led_trigger_register(&netdev_led_trigger); | ||
483 | } | ||
484 | |||
485 | static void __exit netdev_trig_exit(void) | ||
486 | { | ||
487 | led_trigger_unregister(&netdev_led_trigger); | ||
488 | } | ||
489 | |||
490 | module_init(netdev_trig_init); | ||
491 | module_exit(netdev_trig_exit); | ||
492 | |||
493 | MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>"); | ||
494 | MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>"); | ||
495 | MODULE_DESCRIPTION("Netdev LED trigger"); | ||
496 | MODULE_LICENSE("GPL v2"); | ||