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_brightness_set(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
;
97 ssize_t ret
= -EINVAL
;
101 port_mask
= simple_strtoul(buf
, &after
, 16);
104 if (*after
&& isspace(*after
))
110 write_lock(&trig_data
->lock
);
112 changed
= (trig_data
->port_mask
!= port_mask
);
114 trig_data
->port_mask
= port_mask
;
116 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
119 write_unlock(&trig_data
->lock
);
122 swconfig_trig_update_port_mask(led_cdev
->trigger
);
131 swconfig_trig_port_mask_show(struct device
*dev
, struct device_attribute
*attr
,
134 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
135 struct swconfig_trig_data
*trig_data
= led_cdev
->trigger_data
;
137 read_lock(&trig_data
->lock
);
138 sprintf(buf
, "%#x\n", trig_data
->port_mask
);
139 read_unlock(&trig_data
->lock
);
141 return strlen(buf
) + 1;
144 static DEVICE_ATTR(port_mask
, 0644, swconfig_trig_port_mask_show
,
145 swconfig_trig_port_mask_store
);
148 swconfig_trig_activate(struct led_classdev
*led_cdev
)
150 struct switch_led_trigger
*sw_trig
;
151 struct swconfig_trig_data
*trig_data
;
154 if (led_cdev
->trigger
->activate
!= swconfig_trig_activate
)
157 trig_data
= kzalloc(sizeof(struct swconfig_trig_data
), GFP_KERNEL
);
161 sw_trig
= (void *) led_cdev
->trigger
;
163 rwlock_init(&trig_data
->lock
);
164 trig_data
->led_cdev
= led_cdev
;
165 trig_data
->swdev
= sw_trig
->swdev
;
166 led_cdev
->trigger_data
= trig_data
;
168 err
= device_create_file(led_cdev
->dev
, &dev_attr_port_mask
);
175 led_cdev
->trigger_data
= NULL
;
180 swconfig_trig_deactivate(struct led_classdev
*led_cdev
)
182 struct swconfig_trig_data
*trig_data
;
184 swconfig_trig_update_port_mask(led_cdev
->trigger
);
186 trig_data
= (void *) led_cdev
->trigger_data
;
188 device_remove_file(led_cdev
->dev
, &dev_attr_port_mask
);
194 swconfig_trig_led_event(struct switch_led_trigger
*sw_trig
,
195 struct led_classdev
*led_cdev
)
197 struct swconfig_trig_data
*trig_data
;
201 trig_data
= led_cdev
->trigger_data
;
205 read_lock(&trig_data
->lock
);
206 port_mask
= trig_data
->port_mask
;
207 read_unlock(&trig_data
->lock
);
209 link
= !!(sw_trig
->port_link
& port_mask
);
211 if (link
!= trig_data
->prev_link
)
212 led_brightness_set(trig_data
->led_cdev
, LED_OFF
);
214 unsigned long traffic
;
218 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
219 if (port_mask
& (1 << i
))
220 traffic
+= sw_trig
->port_traffic
[i
];
223 if (trig_data
->prev_brightness
!= LED_FULL
)
224 swconfig_trig_set_brightness(trig_data
, LED_FULL
);
225 else if (traffic
!= trig_data
->prev_traffic
)
226 swconfig_trig_set_brightness(trig_data
, LED_OFF
);
228 trig_data
->prev_traffic
= traffic
;
231 trig_data
->prev_link
= link
;
235 swconfig_trig_update_leds(struct switch_led_trigger
*sw_trig
)
237 struct list_head
*entry
;
238 struct led_trigger
*trigger
;
240 trigger
= &sw_trig
->trig
;
241 read_lock(&trigger
->leddev_list_lock
);
242 list_for_each(entry
, &trigger
->led_cdevs
) {
243 struct led_classdev
*led_cdev
;
245 led_cdev
= list_entry(entry
, struct led_classdev
, trig_list
);
246 swconfig_trig_led_event(sw_trig
, led_cdev
);
248 read_unlock(&trigger
->leddev_list_lock
);
252 swconfig_led_work_func(struct work_struct
*work
)
254 struct switch_led_trigger
*sw_trig
;
255 struct switch_dev
*swdev
;
260 sw_trig
= container_of(work
, struct switch_led_trigger
,
263 port_mask
= sw_trig
->port_mask
;
264 swdev
= sw_trig
->swdev
;
267 for (i
= 0; i
< SWCONFIG_LED_NUM_PORTS
; i
++) {
271 if ((port_mask
& port_bit
) == 0)
274 if (swdev
->ops
->get_port_link
) {
275 struct switch_port_link port_link
;
277 memset(&port_link
, '\0', sizeof(port_link
));
278 swdev
->ops
->get_port_link(swdev
, i
, &port_link
);
284 if (swdev
->ops
->get_port_stats
) {
285 struct switch_port_stats port_stats
;
287 memset(&port_stats
, '\0', sizeof(port_stats
));
288 swdev
->ops
->get_port_stats(swdev
, i
, &port_stats
);
289 sw_trig
->port_traffic
[i
] = port_stats
.tx_bytes
+
294 sw_trig
->port_link
= link
;
296 swconfig_trig_update_leds(sw_trig
);
298 schedule_delayed_work(&sw_trig
->sw_led_work
,
299 SWCONFIG_LED_TIMER_INTERVAL
);
303 swconfig_create_led_trigger(struct switch_dev
*swdev
)
305 struct switch_led_trigger
*sw_trig
;
308 if (!swdev
->ops
->get_port_link
)
311 sw_trig
= kzalloc(sizeof(struct switch_led_trigger
), GFP_KERNEL
);
315 sw_trig
->swdev
= swdev
;
316 sw_trig
->trig
.name
= swdev
->devname
;
317 sw_trig
->trig
.activate
= swconfig_trig_activate
;
318 sw_trig
->trig
.deactivate
= swconfig_trig_deactivate
;
320 INIT_DELAYED_WORK(&sw_trig
->sw_led_work
, swconfig_led_work_func
);
322 err
= led_trigger_register(&sw_trig
->trig
);
326 swdev
->led_trigger
= sw_trig
;
336 swconfig_destroy_led_trigger(struct switch_dev
*swdev
)
338 struct switch_led_trigger
*sw_trig
;
340 sw_trig
= swdev
->led_trigger
;
342 cancel_delayed_work_sync(&sw_trig
->sw_led_work
);
343 led_trigger_unregister(&sw_trig
->trig
);
348 #else /* SWCONFIG_LEDS */
350 swconfig_create_led_trigger(struct switch_dev
*swdev
) { return 0; }
353 swconfig_destroy_led_trigger(struct switch_dev
*swdev
) { }
354 #endif /* CONFIG_SWCONFIG_LEDS */