GNU Linux-libre 4.19.264-gnu1
[releases.git] / 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 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 }