xref: /openbmc/linux/net/mac80211/led.c (revision f220d3eb)
1 /*
2  * Copyright 2006, Johannes Berg <johannes@sipsolutions.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 
9 /* just for IFNAMSIZ */
10 #include <linux/if.h>
11 #include <linux/slab.h>
12 #include <linux/export.h>
13 #include "led.h"
14 
15 void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
16 {
17 	if (!atomic_read(&local->assoc_led_active))
18 		return;
19 	if (associated)
20 		led_trigger_event(&local->assoc_led, LED_FULL);
21 	else
22 		led_trigger_event(&local->assoc_led, LED_OFF);
23 }
24 
25 void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)
26 {
27 	if (!atomic_read(&local->radio_led_active))
28 		return;
29 	if (enabled)
30 		led_trigger_event(&local->radio_led, LED_FULL);
31 	else
32 		led_trigger_event(&local->radio_led, LED_OFF);
33 }
34 
35 void ieee80211_alloc_led_names(struct ieee80211_local *local)
36 {
37 	local->rx_led.name = kasprintf(GFP_KERNEL, "%srx",
38 				       wiphy_name(local->hw.wiphy));
39 	local->tx_led.name = kasprintf(GFP_KERNEL, "%stx",
40 				       wiphy_name(local->hw.wiphy));
41 	local->assoc_led.name = kasprintf(GFP_KERNEL, "%sassoc",
42 					  wiphy_name(local->hw.wiphy));
43 	local->radio_led.name = kasprintf(GFP_KERNEL, "%sradio",
44 					  wiphy_name(local->hw.wiphy));
45 }
46 
47 void ieee80211_free_led_names(struct ieee80211_local *local)
48 {
49 	kfree(local->rx_led.name);
50 	kfree(local->tx_led.name);
51 	kfree(local->assoc_led.name);
52 	kfree(local->radio_led.name);
53 }
54 
55 static int ieee80211_tx_led_activate(struct led_classdev *led_cdev)
56 {
57 	struct ieee80211_local *local = container_of(led_cdev->trigger,
58 						     struct ieee80211_local,
59 						     tx_led);
60 
61 	atomic_inc(&local->tx_led_active);
62 
63 	return 0;
64 }
65 
66 static void ieee80211_tx_led_deactivate(struct led_classdev *led_cdev)
67 {
68 	struct ieee80211_local *local = container_of(led_cdev->trigger,
69 						     struct ieee80211_local,
70 						     tx_led);
71 
72 	atomic_dec(&local->tx_led_active);
73 }
74 
75 static int ieee80211_rx_led_activate(struct led_classdev *led_cdev)
76 {
77 	struct ieee80211_local *local = container_of(led_cdev->trigger,
78 						     struct ieee80211_local,
79 						     rx_led);
80 
81 	atomic_inc(&local->rx_led_active);
82 
83 	return 0;
84 }
85 
86 static void ieee80211_rx_led_deactivate(struct led_classdev *led_cdev)
87 {
88 	struct ieee80211_local *local = container_of(led_cdev->trigger,
89 						     struct ieee80211_local,
90 						     rx_led);
91 
92 	atomic_dec(&local->rx_led_active);
93 }
94 
95 static int ieee80211_assoc_led_activate(struct led_classdev *led_cdev)
96 {
97 	struct ieee80211_local *local = container_of(led_cdev->trigger,
98 						     struct ieee80211_local,
99 						     assoc_led);
100 
101 	atomic_inc(&local->assoc_led_active);
102 
103 	return 0;
104 }
105 
106 static void ieee80211_assoc_led_deactivate(struct led_classdev *led_cdev)
107 {
108 	struct ieee80211_local *local = container_of(led_cdev->trigger,
109 						     struct ieee80211_local,
110 						     assoc_led);
111 
112 	atomic_dec(&local->assoc_led_active);
113 }
114 
115 static int ieee80211_radio_led_activate(struct led_classdev *led_cdev)
116 {
117 	struct ieee80211_local *local = container_of(led_cdev->trigger,
118 						     struct ieee80211_local,
119 						     radio_led);
120 
121 	atomic_inc(&local->radio_led_active);
122 
123 	return 0;
124 }
125 
126 static void ieee80211_radio_led_deactivate(struct led_classdev *led_cdev)
127 {
128 	struct ieee80211_local *local = container_of(led_cdev->trigger,
129 						     struct ieee80211_local,
130 						     radio_led);
131 
132 	atomic_dec(&local->radio_led_active);
133 }
134 
135 static int ieee80211_tpt_led_activate(struct led_classdev *led_cdev)
136 {
137 	struct ieee80211_local *local = container_of(led_cdev->trigger,
138 						     struct ieee80211_local,
139 						     tpt_led);
140 
141 	atomic_inc(&local->tpt_led_active);
142 
143 	return 0;
144 }
145 
146 static void ieee80211_tpt_led_deactivate(struct led_classdev *led_cdev)
147 {
148 	struct ieee80211_local *local = container_of(led_cdev->trigger,
149 						     struct ieee80211_local,
150 						     tpt_led);
151 
152 	atomic_dec(&local->tpt_led_active);
153 }
154 
155 void ieee80211_led_init(struct ieee80211_local *local)
156 {
157 	atomic_set(&local->rx_led_active, 0);
158 	local->rx_led.activate = ieee80211_rx_led_activate;
159 	local->rx_led.deactivate = ieee80211_rx_led_deactivate;
160 	if (local->rx_led.name && led_trigger_register(&local->rx_led)) {
161 		kfree(local->rx_led.name);
162 		local->rx_led.name = NULL;
163 	}
164 
165 	atomic_set(&local->tx_led_active, 0);
166 	local->tx_led.activate = ieee80211_tx_led_activate;
167 	local->tx_led.deactivate = ieee80211_tx_led_deactivate;
168 	if (local->tx_led.name && led_trigger_register(&local->tx_led)) {
169 		kfree(local->tx_led.name);
170 		local->tx_led.name = NULL;
171 	}
172 
173 	atomic_set(&local->assoc_led_active, 0);
174 	local->assoc_led.activate = ieee80211_assoc_led_activate;
175 	local->assoc_led.deactivate = ieee80211_assoc_led_deactivate;
176 	if (local->assoc_led.name && led_trigger_register(&local->assoc_led)) {
177 		kfree(local->assoc_led.name);
178 		local->assoc_led.name = NULL;
179 	}
180 
181 	atomic_set(&local->radio_led_active, 0);
182 	local->radio_led.activate = ieee80211_radio_led_activate;
183 	local->radio_led.deactivate = ieee80211_radio_led_deactivate;
184 	if (local->radio_led.name && led_trigger_register(&local->radio_led)) {
185 		kfree(local->radio_led.name);
186 		local->radio_led.name = NULL;
187 	}
188 
189 	atomic_set(&local->tpt_led_active, 0);
190 	if (local->tpt_led_trigger) {
191 		local->tpt_led.activate = ieee80211_tpt_led_activate;
192 		local->tpt_led.deactivate = ieee80211_tpt_led_deactivate;
193 		if (led_trigger_register(&local->tpt_led)) {
194 			kfree(local->tpt_led_trigger);
195 			local->tpt_led_trigger = NULL;
196 		}
197 	}
198 }
199 
200 void ieee80211_led_exit(struct ieee80211_local *local)
201 {
202 	if (local->radio_led.name)
203 		led_trigger_unregister(&local->radio_led);
204 	if (local->assoc_led.name)
205 		led_trigger_unregister(&local->assoc_led);
206 	if (local->tx_led.name)
207 		led_trigger_unregister(&local->tx_led);
208 	if (local->rx_led.name)
209 		led_trigger_unregister(&local->rx_led);
210 
211 	if (local->tpt_led_trigger) {
212 		led_trigger_unregister(&local->tpt_led);
213 		kfree(local->tpt_led_trigger);
214 	}
215 }
216 
217 const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
218 {
219 	struct ieee80211_local *local = hw_to_local(hw);
220 
221 	return local->radio_led.name;
222 }
223 EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
224 
225 const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
226 {
227 	struct ieee80211_local *local = hw_to_local(hw);
228 
229 	return local->assoc_led.name;
230 }
231 EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
232 
233 const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
234 {
235 	struct ieee80211_local *local = hw_to_local(hw);
236 
237 	return local->tx_led.name;
238 }
239 EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
240 
241 const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
242 {
243 	struct ieee80211_local *local = hw_to_local(hw);
244 
245 	return local->rx_led.name;
246 }
247 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
248 
249 static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
250 				      struct tpt_led_trigger *tpt_trig)
251 {
252 	unsigned long traffic, delta;
253 
254 	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
255 
256 	delta = traffic - tpt_trig->prev_traffic;
257 	tpt_trig->prev_traffic = traffic;
258 	return DIV_ROUND_UP(delta, 1024 / 8);
259 }
260 
261 static void tpt_trig_timer(struct timer_list *t)
262 {
263 	struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer);
264 	struct ieee80211_local *local = tpt_trig->local;
265 	struct led_classdev *led_cdev;
266 	unsigned long on, off, tpt;
267 	int i;
268 
269 	if (!tpt_trig->running)
270 		return;
271 
272 	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
273 
274 	tpt = tpt_trig_traffic(local, tpt_trig);
275 
276 	/* default to just solid on */
277 	on = 1;
278 	off = 0;
279 
280 	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
281 		if (tpt_trig->blink_table[i].throughput < 0 ||
282 		    tpt > tpt_trig->blink_table[i].throughput) {
283 			off = tpt_trig->blink_table[i].blink_time / 2;
284 			on = tpt_trig->blink_table[i].blink_time - off;
285 			break;
286 		}
287 	}
288 
289 	read_lock(&local->tpt_led.leddev_list_lock);
290 	list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
291 		led_blink_set(led_cdev, &on, &off);
292 	read_unlock(&local->tpt_led.leddev_list_lock);
293 }
294 
295 const char *
296 __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
297 				   unsigned int flags,
298 				   const struct ieee80211_tpt_blink *blink_table,
299 				   unsigned int blink_table_len)
300 {
301 	struct ieee80211_local *local = hw_to_local(hw);
302 	struct tpt_led_trigger *tpt_trig;
303 
304 	if (WARN_ON(local->tpt_led_trigger))
305 		return NULL;
306 
307 	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
308 	if (!tpt_trig)
309 		return NULL;
310 
311 	snprintf(tpt_trig->name, sizeof(tpt_trig->name),
312 		 "%stpt", wiphy_name(local->hw.wiphy));
313 
314 	local->tpt_led.name = tpt_trig->name;
315 
316 	tpt_trig->blink_table = blink_table;
317 	tpt_trig->blink_table_len = blink_table_len;
318 	tpt_trig->want = flags;
319 	tpt_trig->local = local;
320 
321 	timer_setup(&tpt_trig->timer, tpt_trig_timer, 0);
322 
323 	local->tpt_led_trigger = tpt_trig;
324 
325 	return tpt_trig->name;
326 }
327 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
328 
329 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
330 {
331 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
332 
333 	if (tpt_trig->running)
334 		return;
335 
336 	/* reset traffic */
337 	tpt_trig_traffic(local, tpt_trig);
338 	tpt_trig->running = true;
339 
340 	tpt_trig_timer(&tpt_trig->timer);
341 	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
342 }
343 
344 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
345 {
346 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
347 	struct led_classdev *led_cdev;
348 
349 	if (!tpt_trig->running)
350 		return;
351 
352 	tpt_trig->running = false;
353 	del_timer_sync(&tpt_trig->timer);
354 
355 	read_lock(&local->tpt_led.leddev_list_lock);
356 	list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
357 		led_set_brightness(led_cdev, LED_OFF);
358 	read_unlock(&local->tpt_led.leddev_list_lock);
359 }
360 
361 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
362 				unsigned int types_on, unsigned int types_off)
363 {
364 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
365 	bool allowed;
366 
367 	WARN_ON(types_on & types_off);
368 
369 	if (!tpt_trig)
370 		return;
371 
372 	tpt_trig->active &= ~types_off;
373 	tpt_trig->active |= types_on;
374 
375 	/*
376 	 * Regardless of wanted state, we shouldn't blink when
377 	 * the radio is disabled -- this can happen due to some
378 	 * code ordering issues with __ieee80211_recalc_idle()
379 	 * being called before the radio is started.
380 	 */
381 	allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
382 
383 	if (!allowed || !(tpt_trig->active & tpt_trig->want))
384 		ieee80211_stop_tpt_led_trig(local);
385 	else
386 		ieee80211_start_tpt_led_trig(local);
387 }
388