2 * swconfig_led.c: LED trigger support for the switch configuration API
4 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
13 #ifdef CONFIG_SWCONFIG_LEDS
15 #include <linux/leds.h>
16 #include <linux/ctype.h>
17 #include <linux/device.h>
18 #include <linux/workqueue.h>
20 #define SWCONFIG_LED_TIMER_INTERVAL (HZ / 10)
21 #define SWCONFIG_LED_NUM_PORTS 32
23 #define SWCONFIG_LED_PORT_SPEED_NA 0x01 /* unknown speed */
24 #define SWCONFIG_LED_PORT_SPEED_10 0x02 /* 10 Mbps */
25 #define SWCONFIG_LED_PORT_SPEED_100 0x04 /* 100 Mbps */
26 #define SWCONFIG_LED_PORT_SPEED_1000 0x08 /* 1000 Mbps */
27 #define SWCONFIG_LED_PORT_SPEED_ALL (SWCONFIG_LED_PORT_SPEED_NA | \
28 SWCONFIG_LED_PORT_SPEED_10 | \
29 SWCONFIG_LED_PORT_SPEED_100 | \
30 SWCONFIG_LED_PORT_SPEED_1000)
32 #define SWCONFIG_LED_MODE_LINK 0x01
33 #define SWCONFIG_LED_MODE_TX 0x02
34 #define SWCONFIG_LED_MODE_RX 0x04
35 #define SWCONFIG_LED_MODE_TXRX (SWCONFIG_LED_MODE_TX | \
37 #define SWCONFIG_LED_MODE_ALL (SWCONFIG_LED_MODE_LINK | \
38 SWCONFIG_LED_MODE_TX | \
41 struct switch_led_trigger
{
42 struct led_trigger trig
;
43 struct switch_dev
*swdev
;
45 struct delayed_work sw_led_work
;
48 unsigned long long port_tx_traffic
[SWCONFIG_LED_NUM_PORTS
];
49 unsigned long long port_rx_traffic
[SWCONFIG_LED_NUM_PORTS
];
50 u8 link_speed
[SWCONFIG_LED_NUM_PORTS
];
53 struct swconfig_trig_data
{
54 struct led_classdev
*led_cdev
;
55 struct switch_dev
*swdev
;
61 unsigned long prev_traffic
;
62 enum led_brightness prev_brightness
;
68 swconfig_trig_set_brightness(struct swconfig_trig_data
*trig_data
,
69 enum led_brightness brightness
)
71 led_set_brightness(trig_data
->led_cdev
, brightness
);
72 trig_data
->prev_brightness
= brightness
;
76 swconfig_trig_update_port_mask(struct led_trigger
*trigger
)
78 struct list_head
*entry
;
79 struct switch_led_trigger
*sw_trig
;
85 sw_trig
= (void *) trigger
;
88 read_lock(&trigger
->leddev_list_lock
);
89 list_for_each(entry
, &trigger
->led_cdevs
) {
90 struct led_classdev
*led_cdev
;
91 struct swconfig_trig_data
*trig_data
;
93 led_cdev
= list_entry(entry
, struct led_classdev
, trig_list
);
94 trig_data
= led_cdev
->trigger_data
;
96 read_lock(&trig_data
->lock
);
97 port_mask
|= trig_data
->port_mask
;
98 read_unlock(&trig_data
->lock
);
101 read_unlock(&trigger
->leddev_list_lock
);
103 sw_trig
->port_mask
= port_mask
;
106 schedule_delayed_work(&sw_trig
->sw_led_work
,
107 SWCONFIG_LED_TIMER_INTERVAL
);
109 cancel_delayed_work_sync(&sw_trig
->sw_led_work
);
113 swconfig_trig_port_mask_store(struct device
*dev
, struct device_attribute
*attr
,
114 const char *buf
, size_t size
)
116 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
117 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
118 unsigned long port_mask
;
122 ret
= kstrtoul(buf
, 0, &port_mask
);
126 write_lock(&trig_data
->lock
);
127 changed
= (trig_data
->port_mask
!= port_mask
);
128 trig_data
->port_mask
= port_mask
;
129 write_unlock(&trig_data
->lock
);
133 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
135 swconfig_trig_update_port_mask(led_cdev
->trigger
);
142 swconfig_trig_port_mask_show(struct device
*dev
, struct device_attribute
*attr
,
145 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
146 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
149 read_lock(&trig_data
->lock
);
150 port_mask
= trig_data
->port_mask
;
151 read_unlock(&trig_data
->lock
);
153 sprintf(buf
, "%#x\n", port_mask
);
155 return strlen(buf
) + 1;
158 static DEVICE_ATTR(port_mask
, 0644, swconfig_trig_port_mask_show
,
159 swconfig_trig_port_mask_store
);
161 /* speed_mask file handler - display value */
162 static ssize_t
swconfig_trig_speed_mask_show(struct device
*dev
,
163 struct device_attribute
*attr
,
166 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
167 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
170 read_lock(&trig_data
->lock
);
171 speed_mask
= trig_data
->speed_mask
;
172 read_unlock(&trig_data
->lock
);
174 sprintf(buf
, "%#x\n", speed_mask
);
176 return strlen(buf
) + 1;
179 /* speed_mask file handler - store value */
180 static ssize_t
swconfig_trig_speed_mask_store(struct device
*dev
,
181 struct device_attribute
*attr
,
182 const char *buf
, size_t size
)
184 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
185 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
189 ret
= kstrtou8(buf
, 0, &speed_mask
);
193 write_lock(&trig_data
->lock
);
194 trig_data
->speed_mask
= speed_mask
& SWCONFIG_LED_PORT_SPEED_ALL
;
195 write_unlock(&trig_data
->lock
);
200 /* speed_mask special file */
201 static DEVICE_ATTR(speed_mask
, 0644, swconfig_trig_speed_mask_show
,
202 swconfig_trig_speed_mask_store
);
204 static ssize_t
swconfig_trig_mode_show(struct device
*dev
,
205 struct device_attribute
*attr
, char *buf
)
207 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
208 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
211 read_lock(&trig_data
->lock
);
212 mode
= trig_data
->mode
;
213 read_unlock(&trig_data
->lock
);
216 strcpy(buf
, "none\n");
218 if (mode
& SWCONFIG_LED_MODE_LINK
)
219 strcat(buf
, "link ");
220 if (mode
& SWCONFIG_LED_MODE_TX
)
222 if (mode
& SWCONFIG_LED_MODE_RX
)
227 return strlen(buf
)+1;
230 static ssize_t
swconfig_trig_mode_store(struct device
*dev
,
231 struct device_attribute
*attr
, const char *buf
, size_t size
)
233 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
234 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
239 /* take a copy since we don't want to trash the inbound buffer when using strsep */
240 strncpy(copybuf
, buf
, sizeof(copybuf
));
241 copybuf
[sizeof(copybuf
) - 1] = 0;
244 while ((token
= strsep(&p
, " \t\n")) != NULL
) {
251 if (!strcmp(token
, "none"))
253 else if (!strcmp(token
, "tx"))
254 new_mode
|= SWCONFIG_LED_MODE_TX
;
255 else if (!strcmp(token
, "rx"))
256 new_mode
|= SWCONFIG_LED_MODE_RX
;
257 else if (!strcmp(token
, "link"))
258 new_mode
|= SWCONFIG_LED_MODE_LINK
;
266 write_lock(&trig_data
->lock
);
267 trig_data
->mode
= (u8
)new_mode
;
268 write_unlock(&trig_data
->lock
);
273 /* mode special file */
274 static DEVICE_ATTR(mode
, 0644, swconfig_trig_mode_show
,
275 swconfig_trig_mode_store
);
278 swconfig_trig_activate(struct led_classdev
*led_cdev
)
280 struct switch_led_trigger
*sw_trig
;
281 struct swconfig_trig_data
*trig_data
;
284 trig_data
= kzalloc(sizeof(struct swconfig_trig_data
), GFP_KERNEL
);
288 sw_trig
= (void *) led_cdev
->trigger
;
290 rwlock_init(&trig_data
->lock
);
291 trig_data
->led_cdev
= led_cdev
;
292 trig_data
->swdev
= sw_trig
->swdev
;
293 trig_data
->speed_mask
= SWCONFIG_LED_PORT_SPEED_ALL
;
294 trig_data
->mode
= SWCONFIG_LED_MODE_ALL
;
295 led_cdev
->trigger_data
= trig_data
;
297 err
= device_create_file(led_cdev
->dev
, &dev_attr_port_mask
);
301 err
= device_create_file(led_cdev
->dev
, &dev_attr_speed_mask
);
305 err
= device_create_file(led_cdev
->dev
, &dev_attr_mode
);
312 device_remove_file(led_cdev
->dev
, &dev_attr_speed_mask
);
315 device_remove_file(led_cdev
->dev
, &dev_attr_port_mask
);
318 led_cdev
->trigger_data
= NULL
;
324 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
326 swconfig_trig_activate_void(struct led_classdev
*led_cdev
)
328 swconfig_trig_activate(led_cdev
);
333 swconfig_trig_deactivate(struct led_classdev
*led_cdev
)
335 struct swconfig_trig_data
*trig_data
;
337 swconfig_trig_update_port_mask(led_cdev
->trigger
);
339 trig_data
= (void *) led_cdev
->trigger_data
;
341 device_remove_file(led_cdev
->dev
, &dev_attr_port_mask
);
342 device_remove_file(led_cdev
->dev
, &dev_attr_speed_mask
);
343 device_remove_file(led_cdev
->dev
, &dev_attr_mode
);
349 * link off -> led off (can't be any other reason to turn it on)
351 * mode link: led on by default only if speed matches, else off
352 * mode txrx: blink only if speed matches, else off
355 swconfig_trig_led_event(struct switch_led_trigger
*sw_trig
,
356 struct led_classdev
*led_cdev
)
358 struct swconfig_trig_data
*trig_data
;
362 enum led_brightness led_base
, led_blink
;
364 trig_data
= led_cdev
->trigger_data
;
368 read_lock(&trig_data
->lock
);
369 port_mask
= trig_data
->port_mask
;
370 speed_mask
= trig_data
->speed_mask
;
371 mode
= trig_data
->mode
;
372 read_unlock(&trig_data
->lock
);
374 link
= !!(sw_trig
->port_link
& port_mask
);
376 if (trig_data
->prev_brightness
!= LED_OFF
)
377 swconfig_trig_set_brightness(trig_data
, LED_OFF
); /* and stop */
380 unsigned long traffic
;
381 int speedok
; /* link speed flag */
388 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
389 if (port_mask
& (1 << i
)) {
390 if (sw_trig
->link_speed
[i
] & speed_mask
) {
391 traffic
+= ((mode
& SWCONFIG_LED_MODE_TX
) ?
392 sw_trig
->port_tx_traffic
[i
] : 0) +
393 ((mode
& SWCONFIG_LED_MODE_RX
) ?
394 sw_trig
->port_rx_traffic
[i
] : 0);
401 /* At least one port speed matches speed_mask */
402 if (!(mode
& SWCONFIG_LED_MODE_LINK
)) {
404 led_blink
= LED_FULL
;
407 if (trig_data
->prev_brightness
!= led_base
)
408 swconfig_trig_set_brightness(trig_data
,
410 else if (traffic
!= trig_data
->prev_traffic
)
411 swconfig_trig_set_brightness(trig_data
,
413 } else if (trig_data
->prev_brightness
!= LED_OFF
)
414 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
416 trig_data
->prev_traffic
= traffic
;
419 trig_data
->prev_link
= link
;
423 swconfig_trig_update_leds(struct switch_led_trigger
*sw_trig
)
425 struct list_head
*entry
;
426 struct led_trigger
*trigger
;
428 trigger
= &sw_trig
->trig
;
429 read_lock(&trigger
->leddev_list_lock
);
430 list_for_each(entry
, &trigger
->led_cdevs
) {
431 struct led_classdev
*led_cdev
;
433 led_cdev
= list_entry(entry
, struct led_classdev
, trig_list
);
434 swconfig_trig_led_event(sw_trig
, led_cdev
);
436 read_unlock(&trigger
->leddev_list_lock
);
440 swconfig_led_work_func(struct work_struct
*work
)
442 struct switch_led_trigger
*sw_trig
;
443 struct switch_dev
*swdev
;
448 sw_trig
= container_of(work
, struct switch_led_trigger
,
451 port_mask
= sw_trig
->port_mask
;
452 swdev
= sw_trig
->swdev
;
455 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
458 sw_trig
->link_speed
[i
] = 0;
461 if ((port_mask
& port_bit
) == 0)
464 if (swdev
->ops
->get_port_link
) {
465 struct switch_port_link port_link
;
467 memset(&port_link
, '\0', sizeof(port_link
));
468 swdev
->ops
->get_port_link(swdev
, i
, &port_link
);
470 if (port_link
.link
) {
472 switch (port_link
.speed
) {
473 case SWITCH_PORT_SPEED_UNKNOWN
:
474 sw_trig
->link_speed
[i
] =
475 SWCONFIG_LED_PORT_SPEED_NA
;
477 case SWITCH_PORT_SPEED_10
:
478 sw_trig
->link_speed
[i
] =
479 SWCONFIG_LED_PORT_SPEED_10
;
481 case SWITCH_PORT_SPEED_100
:
482 sw_trig
->link_speed
[i
] =
483 SWCONFIG_LED_PORT_SPEED_100
;
485 case SWITCH_PORT_SPEED_1000
:
486 sw_trig
->link_speed
[i
] =
487 SWCONFIG_LED_PORT_SPEED_1000
;
493 if (swdev
->ops
->get_port_stats
) {
494 struct switch_port_stats port_stats
;
496 memset(&port_stats
, '\0', sizeof(port_stats
));
497 swdev
->ops
->get_port_stats(swdev
, i
, &port_stats
);
498 sw_trig
->port_tx_traffic
[i
] = port_stats
.tx_bytes
;
499 sw_trig
->port_rx_traffic
[i
] = port_stats
.rx_bytes
;
503 sw_trig
->port_link
= link
;
505 swconfig_trig_update_leds(sw_trig
);
507 schedule_delayed_work(&sw_trig
->sw_led_work
,
508 SWCONFIG_LED_TIMER_INTERVAL
);
512 swconfig_create_led_trigger(struct switch_dev
*swdev
)
514 struct switch_led_trigger
*sw_trig
;
517 if (!swdev
->ops
->get_port_link
)
520 sw_trig
= kzalloc(sizeof(struct switch_led_trigger
), GFP_KERNEL
);
524 sw_trig
->swdev
= swdev
;
525 sw_trig
->trig
.name
= swdev
->devname
;
526 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
527 sw_trig
->trig
.activate
= swconfig_trig_activate_void
;
529 sw_trig
->trig
.activate
= swconfig_trig_activate
;
531 sw_trig
->trig
.deactivate
= swconfig_trig_deactivate
;
533 INIT_DELAYED_WORK(&sw_trig
->sw_led_work
, swconfig_led_work_func
);
535 err
= led_trigger_register(&sw_trig
->trig
);
539 swdev
->led_trigger
= sw_trig
;
549 swconfig_destroy_led_trigger(struct switch_dev
*swdev
)
551 struct switch_led_trigger
*sw_trig
;
553 sw_trig
= swdev
->led_trigger
;
555 cancel_delayed_work_sync(&sw_trig
->sw_led_work
);
556 led_trigger_unregister(&sw_trig
->trig
);
561 #else /* SWCONFIG_LEDS */
563 swconfig_create_led_trigger(struct switch_dev
*swdev
) { return 0; }
566 swconfig_destroy_led_trigger(struct switch_dev
*swdev
) { }
567 #endif /* CONFIG_SWCONFIG_LEDS */