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 struct switch_led_trigger
{
24 struct led_trigger trig
;
25 struct switch_dev
*swdev
;
27 struct delayed_work sw_led_work
;
30 unsigned long port_traffic
[SWCONFIG_LED_NUM_PORTS
];
33 struct swconfig_trig_data
{
34 struct led_classdev
*led_cdev
;
35 struct switch_dev
*swdev
;
41 unsigned long prev_traffic
;
42 enum led_brightness prev_brightness
;
46 swconfig_trig_set_brightness(struct swconfig_trig_data
*trig_data
,
47 enum led_brightness brightness
)
49 led_set_brightness(trig_data
->led_cdev
, brightness
);
50 trig_data
->prev_brightness
= brightness
;
54 swconfig_trig_update_port_mask(struct led_trigger
*trigger
)
56 struct list_head
*entry
;
57 struct switch_led_trigger
*sw_trig
;
63 sw_trig
= (void *) trigger
;
66 read_lock(&trigger
->leddev_list_lock
);
67 list_for_each(entry
, &trigger
->led_cdevs
) {
68 struct led_classdev
*led_cdev
;
69 struct swconfig_trig_data
*trig_data
;
71 led_cdev
= list_entry(entry
, struct led_classdev
, trig_list
);
72 trig_data
= led_cdev
->trigger_data
;
74 read_lock(&trig_data
->lock
);
75 port_mask
|= trig_data
->port_mask
;
76 read_unlock(&trig_data
->lock
);
79 read_unlock(&trigger
->leddev_list_lock
);
81 sw_trig
->port_mask
= port_mask
;
84 schedule_delayed_work(&sw_trig
->sw_led_work
,
85 SWCONFIG_LED_TIMER_INTERVAL
);
87 cancel_delayed_work_sync(&sw_trig
->sw_led_work
);
91 swconfig_trig_port_mask_store(struct device
*dev
, struct device_attribute
*attr
,
92 const char *buf
, size_t size
)
94 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
95 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
96 unsigned long port_mask
;
100 ret
= kstrtoul(buf
, 0, &port_mask
);
104 write_lock(&trig_data
->lock
);
106 changed
= (trig_data
->port_mask
!= port_mask
);
108 trig_data
->port_mask
= port_mask
;
110 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
113 write_unlock(&trig_data
->lock
);
116 swconfig_trig_update_port_mask(led_cdev
->trigger
);
122 swconfig_trig_port_mask_show(struct device
*dev
, struct device_attribute
*attr
,
125 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
126 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
128 read_lock(&trig_data
->lock
);
129 sprintf(buf
, "%#x\n", trig_data
->port_mask
);
130 read_unlock(&trig_data
->lock
);
132 return strlen(buf
) + 1;
135 static DEVICE_ATTR(port_mask
, 0644, swconfig_trig_port_mask_show
,
136 swconfig_trig_port_mask_store
);
139 swconfig_trig_activate(struct led_classdev
*led_cdev
)
141 struct switch_led_trigger
*sw_trig
;
142 struct swconfig_trig_data
*trig_data
;
145 if (led_cdev
->trigger
->activate
!= swconfig_trig_activate
)
148 trig_data
= kzalloc(sizeof(struct swconfig_trig_data
), GFP_KERNEL
);
152 sw_trig
= (void *) led_cdev
->trigger
;
154 rwlock_init(&trig_data
->lock
);
155 trig_data
->led_cdev
= led_cdev
;
156 trig_data
->swdev
= sw_trig
->swdev
;
157 led_cdev
->trigger_data
= trig_data
;
159 err
= device_create_file(led_cdev
->dev
, &dev_attr_port_mask
);
166 led_cdev
->trigger_data
= NULL
;
171 swconfig_trig_deactivate(struct led_classdev
*led_cdev
)
173 struct swconfig_trig_data
*trig_data
;
175 swconfig_trig_update_port_mask(led_cdev
->trigger
);
177 trig_data
= (void *) led_cdev
->trigger_data
;
179 device_remove_file(led_cdev
->dev
, &dev_attr_port_mask
);
185 swconfig_trig_led_event(struct switch_led_trigger
*sw_trig
,
186 struct led_classdev
*led_cdev
)
188 struct swconfig_trig_data
*trig_data
;
192 trig_data
= led_cdev
->trigger_data
;
196 read_lock(&trig_data
->lock
);
197 port_mask
= trig_data
->port_mask
;
198 read_unlock(&trig_data
->lock
);
200 link
= !!(sw_trig
->port_link
& port_mask
);
202 if (link
!= trig_data
->prev_link
)
203 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
205 unsigned long traffic
;
209 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
210 if (port_mask
& (1 << i
))
211 traffic
+= sw_trig
->port_traffic
[i
];
214 if (trig_data
->prev_brightness
!= LED_FULL
)
215 swconfig_trig_set_brightness(trig_data
, LED_FULL
);
216 else if (traffic
!= trig_data
->prev_traffic
)
217 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
219 trig_data
->prev_traffic
= traffic
;
222 trig_data
->prev_link
= link
;
226 swconfig_trig_update_leds(struct switch_led_trigger
*sw_trig
)
228 struct list_head
*entry
;
229 struct led_trigger
*trigger
;
231 trigger
= &sw_trig
->trig
;
232 read_lock(&trigger
->leddev_list_lock
);
233 list_for_each(entry
, &trigger
->led_cdevs
) {
234 struct led_classdev
*led_cdev
;
236 led_cdev
= list_entry(entry
, struct led_classdev
, trig_list
);
237 swconfig_trig_led_event(sw_trig
, led_cdev
);
239 read_unlock(&trigger
->leddev_list_lock
);
243 swconfig_led_work_func(struct work_struct
*work
)
245 struct switch_led_trigger
*sw_trig
;
246 struct switch_dev
*swdev
;
251 sw_trig
= container_of(work
, struct switch_led_trigger
,
254 port_mask
= sw_trig
->port_mask
;
255 swdev
= sw_trig
->swdev
;
258 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
262 if ((port_mask
& port_bit
) == 0)
265 if (swdev
->ops
->get_port_link
) {
266 struct switch_port_link port_link
;
268 memset(&port_link
, '\0', sizeof(port_link
));
269 swdev
->ops
->get_port_link(swdev
, i
, &port_link
);
275 if (swdev
->ops
->get_port_stats
) {
276 struct switch_port_stats port_stats
;
278 memset(&port_stats
, '\0', sizeof(port_stats
));
279 swdev
->ops
->get_port_stats(swdev
, i
, &port_stats
);
280 sw_trig
->port_traffic
[i
] = port_stats
.tx_bytes
+
285 sw_trig
->port_link
= link
;
287 swconfig_trig_update_leds(sw_trig
);
289 schedule_delayed_work(&sw_trig
->sw_led_work
,
290 SWCONFIG_LED_TIMER_INTERVAL
);
294 swconfig_create_led_trigger(struct switch_dev
*swdev
)
296 struct switch_led_trigger
*sw_trig
;
299 if (!swdev
->ops
->get_port_link
)
302 sw_trig
= kzalloc(sizeof(struct switch_led_trigger
), GFP_KERNEL
);
306 sw_trig
->swdev
= swdev
;
307 sw_trig
->trig
.name
= swdev
->devname
;
308 sw_trig
->trig
.activate
= swconfig_trig_activate
;
309 sw_trig
->trig
.deactivate
= swconfig_trig_deactivate
;
311 INIT_DELAYED_WORK(&sw_trig
->sw_led_work
, swconfig_led_work_func
);
313 err
= led_trigger_register(&sw_trig
->trig
);
317 swdev
->led_trigger
= sw_trig
;
327 swconfig_destroy_led_trigger(struct switch_dev
*swdev
)
329 struct switch_led_trigger
*sw_trig
;
331 sw_trig
= swdev
->led_trigger
;
333 cancel_delayed_work_sync(&sw_trig
->sw_led_work
);
334 led_trigger_unregister(&sw_trig
->trig
);
339 #else /* SWCONFIG_LEDS */
341 swconfig_create_led_trigger(struct switch_dev
*swdev
) { return 0; }
344 swconfig_destroy_led_trigger(struct switch_dev
*swdev
) { }
345 #endif /* CONFIG_SWCONFIG_LEDS */