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
);
128 changed
= (trig_data
->port_mask
!= port_mask
);
130 trig_data
->port_mask
= port_mask
;
132 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
135 write_unlock(&trig_data
->lock
);
138 swconfig_trig_update_port_mask(led_cdev
->trigger
);
144 swconfig_trig_port_mask_show(struct device
*dev
, struct device_attribute
*attr
,
147 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
148 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
150 read_lock(&trig_data
->lock
);
151 sprintf(buf
, "%#x\n", trig_data
->port_mask
);
152 read_unlock(&trig_data
->lock
);
154 return strlen(buf
) + 1;
157 static DEVICE_ATTR(port_mask
, 0644, swconfig_trig_port_mask_show
,
158 swconfig_trig_port_mask_store
);
160 /* speed_mask file handler - display value */
161 static ssize_t
swconfig_trig_speed_mask_show(struct device
*dev
,
162 struct device_attribute
*attr
,
165 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
166 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
168 read_lock(&trig_data
->lock
);
169 sprintf(buf
, "%#x\n", trig_data
->speed_mask
);
170 read_unlock(&trig_data
->lock
);
172 return strlen(buf
) + 1;
175 /* speed_mask file handler - store value */
176 static ssize_t
swconfig_trig_speed_mask_store(struct device
*dev
,
177 struct device_attribute
*attr
,
178 const char *buf
, size_t size
)
180 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
181 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
185 ret
= kstrtou8(buf
, 0, &speed_mask
);
189 write_lock(&trig_data
->lock
);
190 trig_data
->speed_mask
= speed_mask
& SWCONFIG_LED_PORT_SPEED_ALL
;
191 write_unlock(&trig_data
->lock
);
196 /* speed_mask special file */
197 static DEVICE_ATTR(speed_mask
, 0644, swconfig_trig_speed_mask_show
,
198 swconfig_trig_speed_mask_store
);
200 static ssize_t
swconfig_trig_mode_show(struct device
*dev
,
201 struct device_attribute
*attr
, char *buf
)
203 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
204 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
207 read_lock(&trig_data
->lock
);
208 mode
= trig_data
->mode
;
209 read_unlock(&trig_data
->lock
);
212 strcpy(buf
, "none\n");
214 if (mode
& SWCONFIG_LED_MODE_LINK
)
215 strcat(buf
, "link ");
216 if (mode
& SWCONFIG_LED_MODE_TX
)
218 if (mode
& SWCONFIG_LED_MODE_RX
)
223 return strlen(buf
)+1;
226 static ssize_t
swconfig_trig_mode_store(struct device
*dev
,
227 struct device_attribute
*attr
, const char *buf
, size_t size
)
229 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
230 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
235 /* take a copy since we don't want to trash the inbound buffer when using strsep */
236 strncpy(copybuf
, buf
, sizeof(copybuf
));
237 copybuf
[sizeof(copybuf
) - 1] = 0;
240 while ((token
= strsep(&p
, " \t\n")) != NULL
) {
247 if (!strcmp(token
, "none"))
249 else if (!strcmp(token
, "tx"))
250 new_mode
|= SWCONFIG_LED_MODE_TX
;
251 else if (!strcmp(token
, "rx"))
252 new_mode
|= SWCONFIG_LED_MODE_RX
;
253 else if (!strcmp(token
, "link"))
254 new_mode
|= SWCONFIG_LED_MODE_LINK
;
262 write_lock(&trig_data
->lock
);
263 trig_data
->mode
= (u8
)new_mode
;
264 write_unlock(&trig_data
->lock
);
269 /* mode special file */
270 static DEVICE_ATTR(mode
, 0644, swconfig_trig_mode_show
,
271 swconfig_trig_mode_store
);
274 swconfig_trig_activate(struct led_classdev
*led_cdev
)
276 struct switch_led_trigger
*sw_trig
;
277 struct swconfig_trig_data
*trig_data
;
280 if (led_cdev
->trigger
->activate
!= swconfig_trig_activate
)
283 trig_data
= kzalloc(sizeof(struct swconfig_trig_data
), GFP_KERNEL
);
287 sw_trig
= (void *) led_cdev
->trigger
;
289 rwlock_init(&trig_data
->lock
);
290 trig_data
->led_cdev
= led_cdev
;
291 trig_data
->swdev
= sw_trig
->swdev
;
292 trig_data
->speed_mask
= SWCONFIG_LED_PORT_SPEED_ALL
;
293 trig_data
->mode
= SWCONFIG_LED_MODE_ALL
;
294 led_cdev
->trigger_data
= trig_data
;
296 err
= device_create_file(led_cdev
->dev
, &dev_attr_port_mask
);
300 err
= device_create_file(led_cdev
->dev
, &dev_attr_speed_mask
);
304 err
= device_create_file(led_cdev
->dev
, &dev_attr_mode
);
311 device_remove_file(led_cdev
->dev
, &dev_attr_speed_mask
);
314 device_remove_file(led_cdev
->dev
, &dev_attr_port_mask
);
317 led_cdev
->trigger_data
= NULL
;
322 swconfig_trig_deactivate(struct led_classdev
*led_cdev
)
324 struct swconfig_trig_data
*trig_data
;
326 swconfig_trig_update_port_mask(led_cdev
->trigger
);
328 trig_data
= (void *) led_cdev
->trigger_data
;
330 device_remove_file(led_cdev
->dev
, &dev_attr_port_mask
);
331 device_remove_file(led_cdev
->dev
, &dev_attr_speed_mask
);
332 device_remove_file(led_cdev
->dev
, &dev_attr_mode
);
338 * link off -> led off (can't be any other reason to turn it on)
340 * mode link: led on by default only if speed matches, else off
341 * mode txrx: blink only if speed matches, else off
344 swconfig_trig_led_event(struct switch_led_trigger
*sw_trig
,
345 struct led_classdev
*led_cdev
)
347 struct swconfig_trig_data
*trig_data
;
351 enum led_brightness led_base
, led_blink
;
353 trig_data
= led_cdev
->trigger_data
;
357 read_lock(&trig_data
->lock
);
358 port_mask
= trig_data
->port_mask
;
359 speed_mask
= trig_data
->speed_mask
;
360 mode
= trig_data
->mode
;
361 read_unlock(&trig_data
->lock
);
363 link
= !!(sw_trig
->port_link
& port_mask
);
365 if (trig_data
->prev_brightness
!= LED_OFF
)
366 swconfig_trig_set_brightness(trig_data
, LED_OFF
); /* and stop */
369 unsigned long traffic
;
370 int speedok
; /* link speed flag */
377 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
378 if (port_mask
& (1 << i
)) {
379 if (sw_trig
->link_speed
[i
] & speed_mask
) {
380 traffic
+= ((mode
& SWCONFIG_LED_MODE_TX
) ?
381 sw_trig
->port_tx_traffic
[i
] : 0) +
382 ((mode
& SWCONFIG_LED_MODE_RX
) ?
383 sw_trig
->port_rx_traffic
[i
] : 0);
390 /* At least one port speed matches speed_mask */
391 if (!(mode
& SWCONFIG_LED_MODE_LINK
)) {
393 led_blink
= LED_FULL
;
396 if (trig_data
->prev_brightness
!= led_base
)
397 swconfig_trig_set_brightness(trig_data
,
399 else if (traffic
!= trig_data
->prev_traffic
)
400 swconfig_trig_set_brightness(trig_data
,
402 } else if (trig_data
->prev_brightness
!= LED_OFF
)
403 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
405 trig_data
->prev_traffic
= traffic
;
408 trig_data
->prev_link
= link
;
412 swconfig_trig_update_leds(struct switch_led_trigger
*sw_trig
)
414 struct list_head
*entry
;
415 struct led_trigger
*trigger
;
417 trigger
= &sw_trig
->trig
;
418 read_lock(&trigger
->leddev_list_lock
);
419 list_for_each(entry
, &trigger
->led_cdevs
) {
420 struct led_classdev
*led_cdev
;
422 led_cdev
= list_entry(entry
, struct led_classdev
, trig_list
);
423 swconfig_trig_led_event(sw_trig
, led_cdev
);
425 read_unlock(&trigger
->leddev_list_lock
);
429 swconfig_led_work_func(struct work_struct
*work
)
431 struct switch_led_trigger
*sw_trig
;
432 struct switch_dev
*swdev
;
437 sw_trig
= container_of(work
, struct switch_led_trigger
,
440 port_mask
= sw_trig
->port_mask
;
441 swdev
= sw_trig
->swdev
;
444 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
447 sw_trig
->link_speed
[i
] = 0;
450 if ((port_mask
& port_bit
) == 0)
453 if (swdev
->ops
->get_port_link
) {
454 struct switch_port_link port_link
;
456 memset(&port_link
, '\0', sizeof(port_link
));
457 swdev
->ops
->get_port_link(swdev
, i
, &port_link
);
459 if (port_link
.link
) {
461 switch (port_link
.speed
) {
462 case SWITCH_PORT_SPEED_UNKNOWN
:
463 sw_trig
->link_speed
[i
] =
464 SWCONFIG_LED_PORT_SPEED_NA
;
466 case SWITCH_PORT_SPEED_10
:
467 sw_trig
->link_speed
[i
] =
468 SWCONFIG_LED_PORT_SPEED_10
;
470 case SWITCH_PORT_SPEED_100
:
471 sw_trig
->link_speed
[i
] =
472 SWCONFIG_LED_PORT_SPEED_100
;
474 case SWITCH_PORT_SPEED_1000
:
475 sw_trig
->link_speed
[i
] =
476 SWCONFIG_LED_PORT_SPEED_1000
;
482 if (swdev
->ops
->get_port_stats
) {
483 struct switch_port_stats port_stats
;
485 memset(&port_stats
, '\0', sizeof(port_stats
));
486 swdev
->ops
->get_port_stats(swdev
, i
, &port_stats
);
487 sw_trig
->port_tx_traffic
[i
] = port_stats
.tx_bytes
;
488 sw_trig
->port_rx_traffic
[i
] = port_stats
.rx_bytes
;
492 sw_trig
->port_link
= link
;
494 swconfig_trig_update_leds(sw_trig
);
496 schedule_delayed_work(&sw_trig
->sw_led_work
,
497 SWCONFIG_LED_TIMER_INTERVAL
);
501 swconfig_create_led_trigger(struct switch_dev
*swdev
)
503 struct switch_led_trigger
*sw_trig
;
506 if (!swdev
->ops
->get_port_link
)
509 sw_trig
= kzalloc(sizeof(struct switch_led_trigger
), GFP_KERNEL
);
513 sw_trig
->swdev
= swdev
;
514 sw_trig
->trig
.name
= swdev
->devname
;
515 sw_trig
->trig
.activate
= swconfig_trig_activate
;
516 sw_trig
->trig
.deactivate
= swconfig_trig_deactivate
;
518 INIT_DELAYED_WORK(&sw_trig
->sw_led_work
, swconfig_led_work_func
);
520 err
= led_trigger_register(&sw_trig
->trig
);
524 swdev
->led_trigger
= sw_trig
;
534 swconfig_destroy_led_trigger(struct switch_dev
*swdev
)
536 struct switch_led_trigger
*sw_trig
;
538 sw_trig
= swdev
->led_trigger
;
540 cancel_delayed_work_sync(&sw_trig
->sw_led_work
);
541 led_trigger_unregister(&sw_trig
->trig
);
546 #else /* SWCONFIG_LEDS */
548 swconfig_create_led_trigger(struct switch_dev
*swdev
) { return 0; }
551 swconfig_destroy_led_trigger(struct switch_dev
*swdev
) { }
552 #endif /* CONFIG_SWCONFIG_LEDS */