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/ethtool.h> |
17 | #include <linux/init.h> |
18 | #include <linux/jiffies.h> |
19 | #include <linux/kernel.h> |
20 | #include <linux/leds.h> |
21 | #include <linux/linkmode.h> |
22 | #include <linux/list.h> |
23 | #include <linux/module.h> |
24 | #include <linux/netdevice.h> |
25 | #include <linux/mutex.h> |
26 | #include <linux/phy.h> |
27 | #include <linux/rtnetlink.h> |
28 | #include <linux/timer.h> |
29 | #include "../leds.h" |
30 | |
31 | #define NETDEV_LED_DEFAULT_INTERVAL 50 |
32 | |
33 | /* |
34 | * Configurable sysfs attributes: |
35 | * |
36 | * device_name - network device name to monitor |
37 | * interval - duration of LED blink, in milliseconds |
38 | * link - LED's normal state reflects whether the link is up |
39 | * (has carrier) or not |
40 | * tx - LED blinks on transmitted data |
41 | * rx - LED blinks on receive data |
42 | * |
43 | * Note: If the user selects a mode that is not supported by hw, default |
44 | * behavior is to fall back to software control of the LED. However not every |
45 | * hw supports software control. LED callbacks brightness_set() and |
46 | * brightness_set_blocking() are NULL in this case. hw_control_is_supported() |
47 | * should use available means supported by hw to inform the user that selected |
48 | * mode isn't supported by hw. This could be switching off the LED or any |
49 | * hw blink mode. If software control fallback isn't possible, we return |
50 | * -EOPNOTSUPP to the user, but still store the selected mode. This is needed |
51 | * in case an intermediate unsupported mode is necessary to switch from one |
52 | * supported mode to another. |
53 | */ |
54 | |
55 | struct led_netdev_data { |
56 | struct mutex lock; |
57 | |
58 | struct delayed_work work; |
59 | struct notifier_block notifier; |
60 | |
61 | struct led_classdev *led_cdev; |
62 | struct net_device *net_dev; |
63 | |
64 | char device_name[IFNAMSIZ]; |
65 | atomic_t interval; |
66 | unsigned int last_activity; |
67 | |
68 | unsigned long mode; |
69 | int link_speed; |
70 | __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes); |
71 | u8 duplex; |
72 | |
73 | bool carrier_link_up; |
74 | bool hw_control; |
75 | }; |
76 | |
77 | static const struct attribute_group netdev_trig_link_speed_attrs_group; |
78 | |
79 | static void set_baseline_state(struct led_netdev_data *trigger_data) |
80 | { |
81 | int current_brightness; |
82 | struct led_classdev *led_cdev = trigger_data->led_cdev; |
83 | |
84 | /* Already validated, hw control is possible with the requested mode */ |
85 | if (trigger_data->hw_control) { |
86 | led_cdev->hw_control_set(led_cdev, trigger_data->mode); |
87 | |
88 | return; |
89 | } |
90 | |
91 | current_brightness = led_cdev->brightness; |
92 | if (current_brightness) |
93 | led_cdev->blink_brightness = current_brightness; |
94 | if (!led_cdev->blink_brightness) |
95 | led_cdev->blink_brightness = led_cdev->max_brightness; |
96 | |
97 | if (!trigger_data->carrier_link_up) { |
98 | led_set_brightness(led_cdev, brightness: LED_OFF); |
99 | } else { |
100 | bool blink_on = false; |
101 | |
102 | if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode)) |
103 | blink_on = true; |
104 | |
105 | if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) && |
106 | trigger_data->link_speed == SPEED_10) |
107 | blink_on = true; |
108 | |
109 | if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) && |
110 | trigger_data->link_speed == SPEED_100) |
111 | blink_on = true; |
112 | |
113 | if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) && |
114 | trigger_data->link_speed == SPEED_1000) |
115 | blink_on = true; |
116 | |
117 | if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) && |
118 | trigger_data->link_speed == SPEED_2500) |
119 | blink_on = true; |
120 | |
121 | if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) && |
122 | trigger_data->link_speed == SPEED_5000) |
123 | blink_on = true; |
124 | |
125 | if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) && |
126 | trigger_data->link_speed == SPEED_10000) |
127 | blink_on = true; |
128 | |
129 | if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && |
130 | trigger_data->duplex == DUPLEX_HALF) |
131 | blink_on = true; |
132 | |
133 | if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) && |
134 | trigger_data->duplex == DUPLEX_FULL) |
135 | blink_on = true; |
136 | |
137 | if (blink_on) |
138 | led_set_brightness(led_cdev, |
139 | brightness: led_cdev->blink_brightness); |
140 | else |
141 | led_set_brightness(led_cdev, brightness: LED_OFF); |
142 | |
143 | /* If we are looking for RX/TX start periodically |
144 | * checking stats |
145 | */ |
146 | if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) || |
147 | test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) |
148 | schedule_delayed_work(dwork: &trigger_data->work, delay: 0); |
149 | } |
150 | } |
151 | |
152 | static bool supports_hw_control(struct led_classdev *led_cdev) |
153 | { |
154 | if (!led_cdev->hw_control_get || !led_cdev->hw_control_set || |
155 | !led_cdev->hw_control_is_supported) |
156 | return false; |
157 | |
158 | return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name); |
159 | } |
160 | |
161 | /* |
162 | * Validate the configured netdev is the same as the one associated with |
163 | * the LED driver in hw control. |
164 | */ |
165 | static bool validate_net_dev(struct led_classdev *led_cdev, |
166 | struct net_device *net_dev) |
167 | { |
168 | struct device *dev = led_cdev->hw_control_get_device(led_cdev); |
169 | struct net_device *ndev; |
170 | |
171 | if (!dev) |
172 | return false; |
173 | |
174 | ndev = to_net_dev(dev); |
175 | |
176 | return ndev == net_dev; |
177 | } |
178 | |
179 | static bool can_hw_control(struct led_netdev_data *trigger_data) |
180 | { |
181 | unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL); |
182 | unsigned int interval = atomic_read(v: &trigger_data->interval); |
183 | struct led_classdev *led_cdev = trigger_data->led_cdev; |
184 | int ret; |
185 | |
186 | if (!supports_hw_control(led_cdev)) |
187 | return false; |
188 | |
189 | /* |
190 | * Interval must be set to the default |
191 | * value. Any different value is rejected if in hw |
192 | * control. |
193 | */ |
194 | if (interval != default_interval) |
195 | return false; |
196 | |
197 | /* |
198 | * net_dev must be set with hw control, otherwise no |
199 | * blinking can be happening and there is nothing to |
200 | * offloaded. Additionally, for hw control to be |
201 | * valid, the configured netdev must be the same as |
202 | * netdev associated to the LED. |
203 | */ |
204 | if (!validate_net_dev(led_cdev, net_dev: trigger_data->net_dev)) |
205 | return false; |
206 | |
207 | /* Check if the requested mode is supported */ |
208 | ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode); |
209 | /* Fall back to software blinking if not supported */ |
210 | if (ret == -EOPNOTSUPP) |
211 | return false; |
212 | if (ret) { |
213 | dev_warn(led_cdev->dev, |
214 | "Current mode check failed with error %d\n" , ret); |
215 | return false; |
216 | } |
217 | |
218 | return true; |
219 | } |
220 | |
221 | static void get_device_state(struct led_netdev_data *trigger_data) |
222 | { |
223 | struct ethtool_link_ksettings cmd; |
224 | |
225 | trigger_data->carrier_link_up = netif_carrier_ok(dev: trigger_data->net_dev); |
226 | |
227 | if (__ethtool_get_link_ksettings(dev: trigger_data->net_dev, link_ksettings: &cmd)) |
228 | return; |
229 | |
230 | if (trigger_data->carrier_link_up) { |
231 | trigger_data->link_speed = cmd.base.speed; |
232 | trigger_data->duplex = cmd.base.duplex; |
233 | } |
234 | |
235 | /* |
236 | * Have a local copy of the link speed supported to avoid rtnl lock every time |
237 | * modes are refreshed on any change event |
238 | */ |
239 | linkmode_copy(dst: trigger_data->supported_link_modes, src: cmd.link_modes.supported); |
240 | } |
241 | |
242 | static ssize_t device_name_show(struct device *dev, |
243 | struct device_attribute *attr, char *buf) |
244 | { |
245 | struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); |
246 | ssize_t len; |
247 | |
248 | mutex_lock(&trigger_data->lock); |
249 | len = sprintf(buf, fmt: "%s\n" , trigger_data->device_name); |
250 | mutex_unlock(lock: &trigger_data->lock); |
251 | |
252 | return len; |
253 | } |
254 | |
255 | static int set_device_name(struct led_netdev_data *trigger_data, |
256 | const char *name, size_t size) |
257 | { |
258 | if (size >= IFNAMSIZ) |
259 | return -EINVAL; |
260 | |
261 | cancel_delayed_work_sync(dwork: &trigger_data->work); |
262 | |
263 | /* |
264 | * Take RTNL lock before trigger_data lock to prevent potential |
265 | * deadlock with netdev notifier registration. |
266 | */ |
267 | rtnl_lock(); |
268 | mutex_lock(&trigger_data->lock); |
269 | |
270 | if (trigger_data->net_dev) { |
271 | dev_put(dev: trigger_data->net_dev); |
272 | trigger_data->net_dev = NULL; |
273 | } |
274 | |
275 | memcpy(trigger_data->device_name, name, size); |
276 | trigger_data->device_name[size] = 0; |
277 | if (size > 0 && trigger_data->device_name[size - 1] == '\n') |
278 | trigger_data->device_name[size - 1] = 0; |
279 | |
280 | if (trigger_data->device_name[0] != 0) |
281 | trigger_data->net_dev = |
282 | dev_get_by_name(net: &init_net, name: trigger_data->device_name); |
283 | |
284 | trigger_data->carrier_link_up = false; |
285 | trigger_data->link_speed = SPEED_UNKNOWN; |
286 | trigger_data->duplex = DUPLEX_UNKNOWN; |
287 | if (trigger_data->net_dev) |
288 | get_device_state(trigger_data); |
289 | |
290 | trigger_data->last_activity = 0; |
291 | |
292 | /* Skip if we're called from netdev_trig_activate() and hw_control is true */ |
293 | if (!trigger_data->hw_control || led_get_trigger_data(led_cdev: trigger_data->led_cdev)) |
294 | set_baseline_state(trigger_data); |
295 | |
296 | mutex_unlock(lock: &trigger_data->lock); |
297 | rtnl_unlock(); |
298 | |
299 | return 0; |
300 | } |
301 | |
302 | static ssize_t device_name_store(struct device *dev, |
303 | struct device_attribute *attr, const char *buf, |
304 | size_t size) |
305 | { |
306 | struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); |
307 | int ret; |
308 | |
309 | ret = set_device_name(trigger_data, name: buf, size); |
310 | |
311 | if (ret < 0) |
312 | return ret; |
313 | |
314 | /* Refresh link_speed visibility */ |
315 | sysfs_update_group(kobj: &dev->kobj, grp: &netdev_trig_link_speed_attrs_group); |
316 | |
317 | return size; |
318 | } |
319 | |
320 | static DEVICE_ATTR_RW(device_name); |
321 | |
322 | static ssize_t netdev_led_attr_show(struct device *dev, char *buf, |
323 | enum led_trigger_netdev_modes attr) |
324 | { |
325 | struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); |
326 | int bit; |
327 | |
328 | switch (attr) { |
329 | case TRIGGER_NETDEV_LINK: |
330 | case TRIGGER_NETDEV_LINK_10: |
331 | case TRIGGER_NETDEV_LINK_100: |
332 | case TRIGGER_NETDEV_LINK_1000: |
333 | case TRIGGER_NETDEV_LINK_2500: |
334 | case TRIGGER_NETDEV_LINK_5000: |
335 | case TRIGGER_NETDEV_LINK_10000: |
336 | case TRIGGER_NETDEV_HALF_DUPLEX: |
337 | case TRIGGER_NETDEV_FULL_DUPLEX: |
338 | case TRIGGER_NETDEV_TX: |
339 | case TRIGGER_NETDEV_RX: |
340 | bit = attr; |
341 | break; |
342 | default: |
343 | return -EINVAL; |
344 | } |
345 | |
346 | return sprintf(buf, fmt: "%u\n" , test_bit(bit, &trigger_data->mode)); |
347 | } |
348 | |
349 | static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, |
350 | size_t size, enum led_trigger_netdev_modes attr) |
351 | { |
352 | struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); |
353 | struct led_classdev *led_cdev = trigger_data->led_cdev; |
354 | unsigned long state, mode = trigger_data->mode; |
355 | int ret; |
356 | int bit; |
357 | |
358 | ret = kstrtoul(s: buf, base: 0, res: &state); |
359 | if (ret) |
360 | return ret; |
361 | |
362 | switch (attr) { |
363 | case TRIGGER_NETDEV_LINK: |
364 | case TRIGGER_NETDEV_LINK_10: |
365 | case TRIGGER_NETDEV_LINK_100: |
366 | case TRIGGER_NETDEV_LINK_1000: |
367 | case TRIGGER_NETDEV_LINK_2500: |
368 | case TRIGGER_NETDEV_LINK_5000: |
369 | case TRIGGER_NETDEV_LINK_10000: |
370 | case TRIGGER_NETDEV_HALF_DUPLEX: |
371 | case TRIGGER_NETDEV_FULL_DUPLEX: |
372 | case TRIGGER_NETDEV_TX: |
373 | case TRIGGER_NETDEV_RX: |
374 | bit = attr; |
375 | break; |
376 | default: |
377 | return -EINVAL; |
378 | } |
379 | |
380 | if (state) |
381 | set_bit(nr: bit, addr: &mode); |
382 | else |
383 | clear_bit(nr: bit, addr: &mode); |
384 | |
385 | if (test_bit(TRIGGER_NETDEV_LINK, &mode) && |
386 | (test_bit(TRIGGER_NETDEV_LINK_10, &mode) || |
387 | test_bit(TRIGGER_NETDEV_LINK_100, &mode) || |
388 | test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || |
389 | test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || |
390 | test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || |
391 | test_bit(TRIGGER_NETDEV_LINK_10000, &mode))) |
392 | return -EINVAL; |
393 | |
394 | cancel_delayed_work_sync(dwork: &trigger_data->work); |
395 | |
396 | trigger_data->mode = mode; |
397 | trigger_data->hw_control = can_hw_control(trigger_data); |
398 | |
399 | if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking && |
400 | !trigger_data->hw_control) |
401 | return -EOPNOTSUPP; |
402 | |
403 | set_baseline_state(trigger_data); |
404 | |
405 | return size; |
406 | } |
407 | |
408 | #define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \ |
409 | static ssize_t trigger_name##_show(struct device *dev, \ |
410 | struct device_attribute *attr, char *buf) \ |
411 | { \ |
412 | return netdev_led_attr_show(dev, buf, trigger); \ |
413 | } \ |
414 | static ssize_t trigger_name##_store(struct device *dev, \ |
415 | struct device_attribute *attr, const char *buf, size_t size) \ |
416 | { \ |
417 | return netdev_led_attr_store(dev, buf, size, trigger); \ |
418 | } \ |
419 | static DEVICE_ATTR_RW(trigger_name) |
420 | |
421 | DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK); |
422 | DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10); |
423 | DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100); |
424 | DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000); |
425 | DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500); |
426 | DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000); |
427 | DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000); |
428 | DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); |
429 | DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); |
430 | DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); |
431 | DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX); |
432 | |
433 | static ssize_t interval_show(struct device *dev, |
434 | struct device_attribute *attr, char *buf) |
435 | { |
436 | struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); |
437 | |
438 | return sprintf(buf, fmt: "%u\n" , |
439 | jiffies_to_msecs(j: atomic_read(v: &trigger_data->interval))); |
440 | } |
441 | |
442 | static ssize_t interval_store(struct device *dev, |
443 | struct device_attribute *attr, const char *buf, |
444 | size_t size) |
445 | { |
446 | struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); |
447 | unsigned long value; |
448 | int ret; |
449 | |
450 | if (trigger_data->hw_control) |
451 | return -EINVAL; |
452 | |
453 | ret = kstrtoul(s: buf, base: 0, res: &value); |
454 | if (ret) |
455 | return ret; |
456 | |
457 | /* impose some basic bounds on the timer interval */ |
458 | if (value >= 5 && value <= 10000) { |
459 | cancel_delayed_work_sync(dwork: &trigger_data->work); |
460 | |
461 | atomic_set(v: &trigger_data->interval, i: msecs_to_jiffies(m: value)); |
462 | set_baseline_state(trigger_data); /* resets timer */ |
463 | } |
464 | |
465 | return size; |
466 | } |
467 | |
468 | static DEVICE_ATTR_RW(interval); |
469 | |
470 | static ssize_t offloaded_show(struct device *dev, |
471 | struct device_attribute *attr, char *buf) |
472 | { |
473 | struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); |
474 | |
475 | return sprintf(buf, fmt: "%d\n" , trigger_data->hw_control); |
476 | } |
477 | |
478 | static DEVICE_ATTR_RO(offloaded); |
479 | |
480 | #define CHECK_LINK_MODE_ATTR(link_speed) \ |
481 | do { \ |
482 | if (attr == &dev_attr_link_##link_speed.attr && \ |
483 | link_ksettings.base.speed == SPEED_##link_speed) \ |
484 | return attr->mode; \ |
485 | } while (0) |
486 | |
487 | static umode_t netdev_trig_link_speed_visible(struct kobject *kobj, |
488 | struct attribute *attr, int n) |
489 | { |
490 | struct device *dev = kobj_to_dev(kobj); |
491 | struct led_netdev_data *trigger_data; |
492 | unsigned long *supported_link_modes; |
493 | u32 mode; |
494 | |
495 | trigger_data = led_trigger_get_drvdata(dev); |
496 | supported_link_modes = trigger_data->supported_link_modes; |
497 | |
498 | /* |
499 | * Search in the supported link mode mask a matching supported mode. |
500 | * Stop at the first matching entry as we care only to check if a particular |
501 | * speed is supported and not the kind. |
502 | */ |
503 | for_each_set_bit(mode, supported_link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS) { |
504 | struct ethtool_link_ksettings link_ksettings; |
505 | |
506 | ethtool_params_from_link_mode(link_ksettings: &link_ksettings, link_mode: mode); |
507 | |
508 | CHECK_LINK_MODE_ATTR(10); |
509 | CHECK_LINK_MODE_ATTR(100); |
510 | CHECK_LINK_MODE_ATTR(1000); |
511 | CHECK_LINK_MODE_ATTR(2500); |
512 | CHECK_LINK_MODE_ATTR(5000); |
513 | CHECK_LINK_MODE_ATTR(10000); |
514 | } |
515 | |
516 | return 0; |
517 | } |
518 | |
519 | static struct attribute *netdev_trig_link_speed_attrs[] = { |
520 | &dev_attr_link_10.attr, |
521 | &dev_attr_link_100.attr, |
522 | &dev_attr_link_1000.attr, |
523 | &dev_attr_link_2500.attr, |
524 | &dev_attr_link_5000.attr, |
525 | &dev_attr_link_10000.attr, |
526 | NULL |
527 | }; |
528 | |
529 | static const struct attribute_group netdev_trig_link_speed_attrs_group = { |
530 | .attrs = netdev_trig_link_speed_attrs, |
531 | .is_visible = netdev_trig_link_speed_visible, |
532 | }; |
533 | |
534 | static struct attribute *netdev_trig_attrs[] = { |
535 | &dev_attr_device_name.attr, |
536 | &dev_attr_link.attr, |
537 | &dev_attr_full_duplex.attr, |
538 | &dev_attr_half_duplex.attr, |
539 | &dev_attr_rx.attr, |
540 | &dev_attr_tx.attr, |
541 | &dev_attr_interval.attr, |
542 | &dev_attr_offloaded.attr, |
543 | NULL |
544 | }; |
545 | |
546 | static const struct attribute_group netdev_trig_attrs_group = { |
547 | .attrs = netdev_trig_attrs, |
548 | }; |
549 | |
550 | static const struct attribute_group *netdev_trig_groups[] = { |
551 | &netdev_trig_attrs_group, |
552 | &netdev_trig_link_speed_attrs_group, |
553 | NULL, |
554 | }; |
555 | |
556 | static int netdev_trig_notify(struct notifier_block *nb, |
557 | unsigned long evt, void *dv) |
558 | { |
559 | struct net_device *dev = |
560 | netdev_notifier_info_to_dev(info: (struct netdev_notifier_info *)dv); |
561 | struct led_netdev_data *trigger_data = |
562 | container_of(nb, struct led_netdev_data, notifier); |
563 | struct led_classdev *led_cdev = trigger_data->led_cdev; |
564 | |
565 | if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE |
566 | && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER |
567 | && evt != NETDEV_CHANGENAME) |
568 | return NOTIFY_DONE; |
569 | |
570 | if (!(dev == trigger_data->net_dev || |
571 | (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) || |
572 | (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name)))) |
573 | return NOTIFY_DONE; |
574 | |
575 | cancel_delayed_work_sync(dwork: &trigger_data->work); |
576 | |
577 | mutex_lock(&trigger_data->lock); |
578 | |
579 | trigger_data->carrier_link_up = false; |
580 | trigger_data->link_speed = SPEED_UNKNOWN; |
581 | trigger_data->duplex = DUPLEX_UNKNOWN; |
582 | switch (evt) { |
583 | case NETDEV_CHANGENAME: |
584 | case NETDEV_REGISTER: |
585 | dev_put(dev: trigger_data->net_dev); |
586 | dev_hold(dev); |
587 | trigger_data->net_dev = dev; |
588 | if (evt == NETDEV_CHANGENAME) |
589 | get_device_state(trigger_data); |
590 | break; |
591 | case NETDEV_UNREGISTER: |
592 | dev_put(dev: trigger_data->net_dev); |
593 | trigger_data->net_dev = NULL; |
594 | break; |
595 | case NETDEV_UP: |
596 | case NETDEV_CHANGE: |
597 | get_device_state(trigger_data); |
598 | /* Refresh link_speed visibility */ |
599 | if (evt == NETDEV_CHANGE) |
600 | sysfs_update_group(kobj: &led_cdev->dev->kobj, |
601 | grp: &netdev_trig_link_speed_attrs_group); |
602 | break; |
603 | } |
604 | |
605 | set_baseline_state(trigger_data); |
606 | |
607 | mutex_unlock(lock: &trigger_data->lock); |
608 | |
609 | return NOTIFY_DONE; |
610 | } |
611 | |
612 | /* here's the real work! */ |
613 | static void netdev_trig_work(struct work_struct *work) |
614 | { |
615 | struct led_netdev_data *trigger_data = |
616 | container_of(work, struct led_netdev_data, work.work); |
617 | struct rtnl_link_stats64 *dev_stats; |
618 | unsigned int new_activity; |
619 | struct rtnl_link_stats64 temp; |
620 | unsigned long interval; |
621 | int invert; |
622 | |
623 | /* If we dont have a device, insure we are off */ |
624 | if (!trigger_data->net_dev) { |
625 | led_set_brightness(led_cdev: trigger_data->led_cdev, brightness: LED_OFF); |
626 | return; |
627 | } |
628 | |
629 | /* If we are not looking for RX/TX then return */ |
630 | if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) && |
631 | !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) |
632 | return; |
633 | |
634 | dev_stats = dev_get_stats(dev: trigger_data->net_dev, storage: &temp); |
635 | new_activity = |
636 | (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ? |
637 | dev_stats->tx_packets : 0) + |
638 | (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ? |
639 | dev_stats->rx_packets : 0); |
640 | |
641 | if (trigger_data->last_activity != new_activity) { |
642 | led_stop_software_blink(led_cdev: trigger_data->led_cdev); |
643 | |
644 | invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) || |
645 | test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) || |
646 | test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) || |
647 | test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) || |
648 | test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) || |
649 | test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) || |
650 | test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) || |
651 | test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) || |
652 | test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode); |
653 | interval = jiffies_to_msecs( |
654 | j: atomic_read(v: &trigger_data->interval)); |
655 | /* base state is ON (link present) */ |
656 | led_blink_set_oneshot(led_cdev: trigger_data->led_cdev, |
657 | delay_on: &interval, |
658 | delay_off: &interval, |
659 | invert); |
660 | trigger_data->last_activity = new_activity; |
661 | } |
662 | |
663 | schedule_delayed_work(dwork: &trigger_data->work, |
664 | delay: (atomic_read(v: &trigger_data->interval)*2)); |
665 | } |
666 | |
667 | static int netdev_trig_activate(struct led_classdev *led_cdev) |
668 | { |
669 | struct led_netdev_data *trigger_data; |
670 | unsigned long mode = 0; |
671 | struct device *dev; |
672 | int rc; |
673 | |
674 | trigger_data = kzalloc(size: sizeof(struct led_netdev_data), GFP_KERNEL); |
675 | if (!trigger_data) |
676 | return -ENOMEM; |
677 | |
678 | mutex_init(&trigger_data->lock); |
679 | |
680 | trigger_data->notifier.notifier_call = netdev_trig_notify; |
681 | trigger_data->notifier.priority = 10; |
682 | |
683 | INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work); |
684 | |
685 | trigger_data->led_cdev = led_cdev; |
686 | trigger_data->net_dev = NULL; |
687 | trigger_data->device_name[0] = 0; |
688 | |
689 | trigger_data->mode = 0; |
690 | atomic_set(v: &trigger_data->interval, i: msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL)); |
691 | trigger_data->last_activity = 0; |
692 | |
693 | /* Check if hw control is active by default on the LED. |
694 | * Init already enabled mode in hw control. |
695 | */ |
696 | if (supports_hw_control(led_cdev)) { |
697 | dev = led_cdev->hw_control_get_device(led_cdev); |
698 | if (dev) { |
699 | const char *name = dev_name(dev); |
700 | |
701 | trigger_data->hw_control = true; |
702 | set_device_name(trigger_data, name, strlen(name)); |
703 | |
704 | rc = led_cdev->hw_control_get(led_cdev, &mode); |
705 | if (!rc) |
706 | trigger_data->mode = mode; |
707 | } |
708 | } |
709 | |
710 | led_set_trigger_data(led_cdev, trigger_data); |
711 | |
712 | rc = register_netdevice_notifier(nb: &trigger_data->notifier); |
713 | if (rc) |
714 | kfree(objp: trigger_data); |
715 | |
716 | return rc; |
717 | } |
718 | |
719 | static void netdev_trig_deactivate(struct led_classdev *led_cdev) |
720 | { |
721 | struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev); |
722 | |
723 | unregister_netdevice_notifier(nb: &trigger_data->notifier); |
724 | |
725 | cancel_delayed_work_sync(dwork: &trigger_data->work); |
726 | |
727 | led_set_brightness(led_cdev, brightness: LED_OFF); |
728 | |
729 | dev_put(dev: trigger_data->net_dev); |
730 | |
731 | kfree(objp: trigger_data); |
732 | } |
733 | |
734 | static struct led_trigger netdev_led_trigger = { |
735 | .name = "netdev" , |
736 | .activate = netdev_trig_activate, |
737 | .deactivate = netdev_trig_deactivate, |
738 | .groups = netdev_trig_groups, |
739 | }; |
740 | |
741 | module_led_trigger(netdev_led_trigger); |
742 | |
743 | MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>" ); |
744 | MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>" ); |
745 | MODULE_DESCRIPTION("Netdev LED trigger" ); |
746 | MODULE_LICENSE("GPL v2" ); |
747 | MODULE_ALIAS("ledtrig:netdev" ); |
748 | |