xref: /openbmc/linux/net/mac80211/led.c (revision df2634f43f5106947f3735a0b61a6527a4b278cd)
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 "led.h"
13 
14 void ieee80211_led_rx(struct ieee80211_local *local)
15 {
16 	if (unlikely(!local->rx_led))
17 		return;
18 	if (local->rx_led_counter++ % 2 == 0)
19 		led_trigger_event(local->rx_led, LED_OFF);
20 	else
21 		led_trigger_event(local->rx_led, LED_FULL);
22 }
23 
24 /* q is 1 if a packet was enqueued, 0 if it has been transmitted */
25 void ieee80211_led_tx(struct ieee80211_local *local, int q)
26 {
27 	if (unlikely(!local->tx_led))
28 		return;
29 	/* not sure how this is supposed to work ... */
30 	local->tx_led_counter += 2*q-1;
31 	if (local->tx_led_counter % 2 == 0)
32 		led_trigger_event(local->tx_led, LED_OFF);
33 	else
34 		led_trigger_event(local->tx_led, LED_FULL);
35 }
36 
37 void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
38 {
39 	if (unlikely(!local->assoc_led))
40 		return;
41 	if (associated)
42 		led_trigger_event(local->assoc_led, LED_FULL);
43 	else
44 		led_trigger_event(local->assoc_led, LED_OFF);
45 }
46 
47 void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)
48 {
49 	if (unlikely(!local->radio_led))
50 		return;
51 	if (enabled)
52 		led_trigger_event(local->radio_led, LED_FULL);
53 	else
54 		led_trigger_event(local->radio_led, LED_OFF);
55 }
56 
57 void ieee80211_led_names(struct ieee80211_local *local)
58 {
59 	snprintf(local->rx_led_name, sizeof(local->rx_led_name),
60 		 "%srx", wiphy_name(local->hw.wiphy));
61 	snprintf(local->tx_led_name, sizeof(local->tx_led_name),
62 		 "%stx", wiphy_name(local->hw.wiphy));
63 	snprintf(local->assoc_led_name, sizeof(local->assoc_led_name),
64 		 "%sassoc", wiphy_name(local->hw.wiphy));
65 	snprintf(local->radio_led_name, sizeof(local->radio_led_name),
66 		 "%sradio", wiphy_name(local->hw.wiphy));
67 }
68 
69 void ieee80211_led_init(struct ieee80211_local *local)
70 {
71 	local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
72 	if (local->rx_led) {
73 		local->rx_led->name = local->rx_led_name;
74 		if (led_trigger_register(local->rx_led)) {
75 			kfree(local->rx_led);
76 			local->rx_led = NULL;
77 		}
78 	}
79 
80 	local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
81 	if (local->tx_led) {
82 		local->tx_led->name = local->tx_led_name;
83 		if (led_trigger_register(local->tx_led)) {
84 			kfree(local->tx_led);
85 			local->tx_led = NULL;
86 		}
87 	}
88 
89 	local->assoc_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
90 	if (local->assoc_led) {
91 		local->assoc_led->name = local->assoc_led_name;
92 		if (led_trigger_register(local->assoc_led)) {
93 			kfree(local->assoc_led);
94 			local->assoc_led = NULL;
95 		}
96 	}
97 
98 	local->radio_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
99 	if (local->radio_led) {
100 		local->radio_led->name = local->radio_led_name;
101 		if (led_trigger_register(local->radio_led)) {
102 			kfree(local->radio_led);
103 			local->radio_led = NULL;
104 		}
105 	}
106 
107 	if (local->tpt_led_trigger) {
108 		if (led_trigger_register(&local->tpt_led_trigger->trig)) {
109 			kfree(local->tpt_led_trigger);
110 			local->tpt_led_trigger = NULL;
111 		}
112 	}
113 }
114 
115 void ieee80211_led_exit(struct ieee80211_local *local)
116 {
117 	if (local->radio_led) {
118 		led_trigger_unregister(local->radio_led);
119 		kfree(local->radio_led);
120 	}
121 	if (local->assoc_led) {
122 		led_trigger_unregister(local->assoc_led);
123 		kfree(local->assoc_led);
124 	}
125 	if (local->tx_led) {
126 		led_trigger_unregister(local->tx_led);
127 		kfree(local->tx_led);
128 	}
129 	if (local->rx_led) {
130 		led_trigger_unregister(local->rx_led);
131 		kfree(local->rx_led);
132 	}
133 
134 	if (local->tpt_led_trigger) {
135 		led_trigger_unregister(&local->tpt_led_trigger->trig);
136 		kfree(local->tpt_led_trigger);
137 	}
138 }
139 
140 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
141 {
142 	struct ieee80211_local *local = hw_to_local(hw);
143 
144 	return local->radio_led_name;
145 }
146 EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
147 
148 char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
149 {
150 	struct ieee80211_local *local = hw_to_local(hw);
151 
152 	return local->assoc_led_name;
153 }
154 EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
155 
156 char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
157 {
158 	struct ieee80211_local *local = hw_to_local(hw);
159 
160 	return local->tx_led_name;
161 }
162 EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
163 
164 char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
165 {
166 	struct ieee80211_local *local = hw_to_local(hw);
167 
168 	return local->rx_led_name;
169 }
170 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
171 
172 static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
173 				      struct tpt_led_trigger *tpt_trig)
174 {
175 	unsigned long traffic, delta;
176 
177 	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
178 
179 	delta = traffic - tpt_trig->prev_traffic;
180 	tpt_trig->prev_traffic = traffic;
181 	return DIV_ROUND_UP(delta, 1024 / 8);
182 }
183 
184 static void tpt_trig_timer(unsigned long data)
185 {
186 	struct ieee80211_local *local = (void *)data;
187 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
188 	struct led_classdev *led_cdev;
189 	unsigned long on, off, tpt;
190 	int i;
191 
192 	if (!tpt_trig->running)
193 		return;
194 
195 	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
196 
197 	tpt = tpt_trig_traffic(local, tpt_trig);
198 
199 	/* default to just solid on */
200 	on = 1;
201 	off = 0;
202 
203 	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
204 		if (tpt_trig->blink_table[i].throughput < 0 ||
205 		    tpt > tpt_trig->blink_table[i].throughput) {
206 			off = tpt_trig->blink_table[i].blink_time / 2;
207 			on = tpt_trig->blink_table[i].blink_time - off;
208 			break;
209 		}
210 	}
211 
212 	read_lock(&tpt_trig->trig.leddev_list_lock);
213 	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
214 		led_blink_set(led_cdev, &on, &off);
215 	read_unlock(&tpt_trig->trig.leddev_list_lock);
216 }
217 
218 char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
219 				unsigned int flags,
220 				const struct ieee80211_tpt_blink *blink_table,
221 				unsigned int blink_table_len)
222 {
223 	struct ieee80211_local *local = hw_to_local(hw);
224 	struct tpt_led_trigger *tpt_trig;
225 
226 	if (WARN_ON(local->tpt_led_trigger))
227 		return NULL;
228 
229 	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
230 	if (!tpt_trig)
231 		return NULL;
232 
233 	snprintf(tpt_trig->name, sizeof(tpt_trig->name),
234 		 "%stpt", wiphy_name(local->hw.wiphy));
235 
236 	tpt_trig->trig.name = tpt_trig->name;
237 
238 	tpt_trig->blink_table = blink_table;
239 	tpt_trig->blink_table_len = blink_table_len;
240 	tpt_trig->want = flags;
241 
242 	setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
243 
244 	local->tpt_led_trigger = tpt_trig;
245 
246 	return tpt_trig->name;
247 }
248 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
249 
250 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
251 {
252 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
253 
254 	if (tpt_trig->running)
255 		return;
256 
257 	/* reset traffic */
258 	tpt_trig_traffic(local, tpt_trig);
259 	tpt_trig->running = true;
260 
261 	tpt_trig_timer((unsigned long)local);
262 	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
263 }
264 
265 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
266 {
267 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
268 	struct led_classdev *led_cdev;
269 
270 	if (!tpt_trig->running)
271 		return;
272 
273 	tpt_trig->running = false;
274 	del_timer_sync(&tpt_trig->timer);
275 
276 	read_lock(&tpt_trig->trig.leddev_list_lock);
277 	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
278 		led_brightness_set(led_cdev, LED_OFF);
279 	read_unlock(&tpt_trig->trig.leddev_list_lock);
280 }
281 
282 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
283 				unsigned int types_on, unsigned int types_off)
284 {
285 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
286 	bool allowed;
287 
288 	WARN_ON(types_on & types_off);
289 
290 	if (!tpt_trig)
291 		return;
292 
293 	tpt_trig->active &= ~types_off;
294 	tpt_trig->active |= types_on;
295 
296 	/*
297 	 * Regardless of wanted state, we shouldn't blink when
298 	 * the radio is disabled -- this can happen due to some
299 	 * code ordering issues with __ieee80211_recalc_idle()
300 	 * being called before the radio is started.
301 	 */
302 	allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
303 
304 	if (!allowed || !(tpt_trig->active & tpt_trig->want))
305 		ieee80211_stop_tpt_led_trig(local);
306 	else
307 		ieee80211_start_tpt_led_trig(local);
308 }
309