These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / mac80211 / led.c
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 void 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
64 static void ieee80211_tx_led_deactivate(struct led_classdev *led_cdev)
65 {
66         struct ieee80211_local *local = container_of(led_cdev->trigger,
67                                                      struct ieee80211_local,
68                                                      tx_led);
69
70         atomic_dec(&local->tx_led_active);
71 }
72
73 static void ieee80211_rx_led_activate(struct led_classdev *led_cdev)
74 {
75         struct ieee80211_local *local = container_of(led_cdev->trigger,
76                                                      struct ieee80211_local,
77                                                      rx_led);
78
79         atomic_inc(&local->rx_led_active);
80 }
81
82 static void ieee80211_rx_led_deactivate(struct led_classdev *led_cdev)
83 {
84         struct ieee80211_local *local = container_of(led_cdev->trigger,
85                                                      struct ieee80211_local,
86                                                      rx_led);
87
88         atomic_dec(&local->rx_led_active);
89 }
90
91 static void ieee80211_assoc_led_activate(struct led_classdev *led_cdev)
92 {
93         struct ieee80211_local *local = container_of(led_cdev->trigger,
94                                                      struct ieee80211_local,
95                                                      assoc_led);
96
97         atomic_inc(&local->assoc_led_active);
98 }
99
100 static void ieee80211_assoc_led_deactivate(struct led_classdev *led_cdev)
101 {
102         struct ieee80211_local *local = container_of(led_cdev->trigger,
103                                                      struct ieee80211_local,
104                                                      assoc_led);
105
106         atomic_dec(&local->assoc_led_active);
107 }
108
109 static void ieee80211_radio_led_activate(struct led_classdev *led_cdev)
110 {
111         struct ieee80211_local *local = container_of(led_cdev->trigger,
112                                                      struct ieee80211_local,
113                                                      radio_led);
114
115         atomic_inc(&local->radio_led_active);
116 }
117
118 static void ieee80211_radio_led_deactivate(struct led_classdev *led_cdev)
119 {
120         struct ieee80211_local *local = container_of(led_cdev->trigger,
121                                                      struct ieee80211_local,
122                                                      radio_led);
123
124         atomic_dec(&local->radio_led_active);
125 }
126
127 static void ieee80211_tpt_led_activate(struct led_classdev *led_cdev)
128 {
129         struct ieee80211_local *local = container_of(led_cdev->trigger,
130                                                      struct ieee80211_local,
131                                                      tpt_led);
132
133         atomic_inc(&local->tpt_led_active);
134 }
135
136 static void ieee80211_tpt_led_deactivate(struct led_classdev *led_cdev)
137 {
138         struct ieee80211_local *local = container_of(led_cdev->trigger,
139                                                      struct ieee80211_local,
140                                                      tpt_led);
141
142         atomic_dec(&local->tpt_led_active);
143 }
144
145 void ieee80211_led_init(struct ieee80211_local *local)
146 {
147         atomic_set(&local->rx_led_active, 0);
148         local->rx_led.activate = ieee80211_rx_led_activate;
149         local->rx_led.deactivate = ieee80211_rx_led_deactivate;
150         if (local->rx_led.name && led_trigger_register(&local->rx_led)) {
151                 kfree(local->rx_led.name);
152                 local->rx_led.name = NULL;
153         }
154
155         atomic_set(&local->tx_led_active, 0);
156         local->tx_led.activate = ieee80211_tx_led_activate;
157         local->tx_led.deactivate = ieee80211_tx_led_deactivate;
158         if (local->tx_led.name && led_trigger_register(&local->tx_led)) {
159                 kfree(local->tx_led.name);
160                 local->tx_led.name = NULL;
161         }
162
163         atomic_set(&local->assoc_led_active, 0);
164         local->assoc_led.activate = ieee80211_assoc_led_activate;
165         local->assoc_led.deactivate = ieee80211_assoc_led_deactivate;
166         if (local->assoc_led.name && led_trigger_register(&local->assoc_led)) {
167                 kfree(local->assoc_led.name);
168                 local->assoc_led.name = NULL;
169         }
170
171         atomic_set(&local->radio_led_active, 0);
172         local->radio_led.activate = ieee80211_radio_led_activate;
173         local->radio_led.deactivate = ieee80211_radio_led_deactivate;
174         if (local->radio_led.name && led_trigger_register(&local->radio_led)) {
175                 kfree(local->radio_led.name);
176                 local->radio_led.name = NULL;
177         }
178
179         atomic_set(&local->tpt_led_active, 0);
180         if (local->tpt_led_trigger) {
181                 local->tpt_led.activate = ieee80211_tpt_led_activate;
182                 local->tpt_led.deactivate = ieee80211_tpt_led_deactivate;
183                 if (led_trigger_register(&local->tpt_led)) {
184                         kfree(local->tpt_led_trigger);
185                         local->tpt_led_trigger = NULL;
186                 }
187         }
188 }
189
190 void ieee80211_led_exit(struct ieee80211_local *local)
191 {
192         if (local->radio_led.name)
193                 led_trigger_unregister(&local->radio_led);
194         if (local->assoc_led.name)
195                 led_trigger_unregister(&local->assoc_led);
196         if (local->tx_led.name)
197                 led_trigger_unregister(&local->tx_led);
198         if (local->rx_led.name)
199                 led_trigger_unregister(&local->rx_led);
200
201         if (local->tpt_led_trigger) {
202                 led_trigger_unregister(&local->tpt_led);
203                 kfree(local->tpt_led_trigger);
204         }
205 }
206
207 const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
208 {
209         struct ieee80211_local *local = hw_to_local(hw);
210
211         return local->radio_led.name;
212 }
213 EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
214
215 const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
216 {
217         struct ieee80211_local *local = hw_to_local(hw);
218
219         return local->assoc_led.name;
220 }
221 EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
222
223 const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
224 {
225         struct ieee80211_local *local = hw_to_local(hw);
226
227         return local->tx_led.name;
228 }
229 EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
230
231 const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
232 {
233         struct ieee80211_local *local = hw_to_local(hw);
234
235         return local->rx_led.name;
236 }
237 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
238
239 static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
240                                       struct tpt_led_trigger *tpt_trig)
241 {
242         unsigned long traffic, delta;
243
244         traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
245
246         delta = traffic - tpt_trig->prev_traffic;
247         tpt_trig->prev_traffic = traffic;
248         return DIV_ROUND_UP(delta, 1024 / 8);
249 }
250
251 static void tpt_trig_timer(unsigned long data)
252 {
253         struct ieee80211_local *local = (void *)data;
254         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
255         struct led_classdev *led_cdev;
256         unsigned long on, off, tpt;
257         int i;
258
259         if (!tpt_trig->running)
260                 return;
261
262         mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
263
264         tpt = tpt_trig_traffic(local, tpt_trig);
265
266         /* default to just solid on */
267         on = 1;
268         off = 0;
269
270         for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
271                 if (tpt_trig->blink_table[i].throughput < 0 ||
272                     tpt > tpt_trig->blink_table[i].throughput) {
273                         off = tpt_trig->blink_table[i].blink_time / 2;
274                         on = tpt_trig->blink_table[i].blink_time - off;
275                         break;
276                 }
277         }
278
279         read_lock(&local->tpt_led.leddev_list_lock);
280         list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
281                 led_blink_set(led_cdev, &on, &off);
282         read_unlock(&local->tpt_led.leddev_list_lock);
283 }
284
285 const char *
286 __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
287                                    unsigned int flags,
288                                    const struct ieee80211_tpt_blink *blink_table,
289                                    unsigned int blink_table_len)
290 {
291         struct ieee80211_local *local = hw_to_local(hw);
292         struct tpt_led_trigger *tpt_trig;
293
294         if (WARN_ON(local->tpt_led_trigger))
295                 return NULL;
296
297         tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
298         if (!tpt_trig)
299                 return NULL;
300
301         snprintf(tpt_trig->name, sizeof(tpt_trig->name),
302                  "%stpt", wiphy_name(local->hw.wiphy));
303
304         local->tpt_led.name = tpt_trig->name;
305
306         tpt_trig->blink_table = blink_table;
307         tpt_trig->blink_table_len = blink_table_len;
308         tpt_trig->want = flags;
309
310         setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
311
312         local->tpt_led_trigger = tpt_trig;
313
314         return tpt_trig->name;
315 }
316 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
317
318 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
319 {
320         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
321
322         if (tpt_trig->running)
323                 return;
324
325         /* reset traffic */
326         tpt_trig_traffic(local, tpt_trig);
327         tpt_trig->running = true;
328
329         tpt_trig_timer((unsigned long)local);
330         mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
331 }
332
333 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
334 {
335         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
336         struct led_classdev *led_cdev;
337
338         if (!tpt_trig->running)
339                 return;
340
341         tpt_trig->running = false;
342         del_timer_sync(&tpt_trig->timer);
343
344         read_lock(&local->tpt_led.leddev_list_lock);
345         list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
346                 led_set_brightness(led_cdev, LED_OFF);
347         read_unlock(&local->tpt_led.leddev_list_lock);
348 }
349
350 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
351                                 unsigned int types_on, unsigned int types_off)
352 {
353         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
354         bool allowed;
355
356         WARN_ON(types_on & types_off);
357
358         if (!tpt_trig)
359                 return;
360
361         tpt_trig->active &= ~types_off;
362         tpt_trig->active |= types_on;
363
364         /*
365          * Regardless of wanted state, we shouldn't blink when
366          * the radio is disabled -- this can happen due to some
367          * code ordering issues with __ieee80211_recalc_idle()
368          * being called before the radio is started.
369          */
370         allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
371
372         if (!allowed || !(tpt_trig->active & tpt_trig->want))
373                 ieee80211_stop_tpt_led_trig(local);
374         else
375                 ieee80211_start_tpt_led_trig(local);
376 }