blob: d6c66fc19716bb164bd90ce6b93d3e8d6d114f2c [file] [log] [blame]
Jiri Bencf0706e82007-05-05 11:45:53 -07001/*
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090011#include <linux/slab.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040012#include <linux/export.h>
Johannes Berg2c8dccc2008-04-08 15:14:40 -040013#include "led.h"
Jiri Bencf0706e82007-05-05 11:45:53 -070014
Michael Buesch47f0c502007-09-27 15:10:44 +020015void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
16{
Johannes Berg8d5c2582015-04-23 12:19:22 +020017 if (!atomic_read(&local->assoc_led_active))
Michael Buesch47f0c502007-09-27 15:10:44 +020018 return;
19 if (associated)
Johannes Berg8d5c2582015-04-23 12:19:22 +020020 led_trigger_event(&local->assoc_led, LED_FULL);
Michael Buesch47f0c502007-09-27 15:10:44 +020021 else
Johannes Berg8d5c2582015-04-23 12:19:22 +020022 led_trigger_event(&local->assoc_led, LED_OFF);
Michael Buesch47f0c502007-09-27 15:10:44 +020023}
24
Ivo van Doorncdcb006f2008-01-07 19:45:24 +010025void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)
26{
Johannes Berg8d5c2582015-04-23 12:19:22 +020027 if (!atomic_read(&local->radio_led_active))
Ivo van Doorncdcb006f2008-01-07 19:45:24 +010028 return;
29 if (enabled)
Johannes Berg8d5c2582015-04-23 12:19:22 +020030 led_trigger_event(&local->radio_led, LED_FULL);
Ivo van Doorncdcb006f2008-01-07 19:45:24 +010031 else
Johannes Berg8d5c2582015-04-23 12:19:22 +020032 led_trigger_event(&local->radio_led, LED_OFF);
Ivo van Doorncdcb006f2008-01-07 19:45:24 +010033}
34
Johannes Berg8d5c2582015-04-23 12:19:22 +020035void ieee80211_alloc_led_names(struct ieee80211_local *local)
Johannes Bergfe67c912010-11-27 20:02:59 +010036{
Johannes Berg8d5c2582015-04-23 12:19:22 +020037 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
47void 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
Uwe Kleine-König2282e1252018-07-02 22:05:21 +020055static int ieee80211_tx_led_activate(struct led_classdev *led_cdev)
Johannes Berg8d5c2582015-04-23 12:19:22 +020056{
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);
Uwe Kleine-König2282e1252018-07-02 22:05:21 +020062
63 return 0;
Johannes Berg8d5c2582015-04-23 12:19:22 +020064}
65
66static 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
Uwe Kleine-König2282e1252018-07-02 22:05:21 +020075static int ieee80211_rx_led_activate(struct led_classdev *led_cdev)
Johannes Berg8d5c2582015-04-23 12:19:22 +020076{
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);
Uwe Kleine-König2282e1252018-07-02 22:05:21 +020082
83 return 0;
Johannes Berg8d5c2582015-04-23 12:19:22 +020084}
85
86static 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
Uwe Kleine-König2282e1252018-07-02 22:05:21 +020095static int ieee80211_assoc_led_activate(struct led_classdev *led_cdev)
Johannes Berg8d5c2582015-04-23 12:19:22 +020096{
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);
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200102
103 return 0;
Johannes Berg8d5c2582015-04-23 12:19:22 +0200104}
105
106static 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
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200115static int ieee80211_radio_led_activate(struct led_classdev *led_cdev)
Johannes Berg8d5c2582015-04-23 12:19:22 +0200116{
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);
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200122
123 return 0;
Johannes Berg8d5c2582015-04-23 12:19:22 +0200124}
125
126static 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
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200135static int ieee80211_tpt_led_activate(struct led_classdev *led_cdev)
Johannes Berg8d5c2582015-04-23 12:19:22 +0200136{
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);
Uwe Kleine-König2282e1252018-07-02 22:05:21 +0200142
143 return 0;
Johannes Berg8d5c2582015-04-23 12:19:22 +0200144}
145
146static 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);
Johannes Bergfe67c912010-11-27 20:02:59 +0100153}
154
Jiri Bencf0706e82007-05-05 11:45:53 -0700155void ieee80211_led_init(struct ieee80211_local *local)
156{
Johannes Berg8d5c2582015-04-23 12:19:22 +0200157 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;
Jiri Bencf0706e82007-05-05 11:45:53 -0700163 }
164
Johannes Berg8d5c2582015-04-23 12:19:22 +0200165 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;
Michael Buesch47f0c502007-09-27 15:10:44 +0200171 }
172
Johannes Berg8d5c2582015-04-23 12:19:22 +0200173 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;
Jiri Bencf0706e82007-05-05 11:45:53 -0700179 }
Ivo van Doorncdcb006f2008-01-07 19:45:24 +0100180
Johannes Berg8d5c2582015-04-23 12:19:22 +0200181 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;
Ivo van Doorncdcb006f2008-01-07 19:45:24 +0100187 }
Johannes Berge1e54062010-11-30 08:58:45 +0100188
Johannes Berg8d5c2582015-04-23 12:19:22 +0200189 atomic_set(&local->tpt_led_active, 0);
Johannes Berge1e54062010-11-30 08:58:45 +0100190 if (local->tpt_led_trigger) {
Johannes Berg8d5c2582015-04-23 12:19:22 +0200191 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)) {
Johannes Berge1e54062010-11-30 08:58:45 +0100194 kfree(local->tpt_led_trigger);
195 local->tpt_led_trigger = NULL;
196 }
197 }
Jiri Bencf0706e82007-05-05 11:45:53 -0700198}
199
200void ieee80211_led_exit(struct ieee80211_local *local)
201{
Johannes Berg8d5c2582015-04-23 12:19:22 +0200202 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);
Johannes Berge1e54062010-11-30 08:58:45 +0100210
211 if (local->tpt_led_trigger) {
Johannes Berg8d5c2582015-04-23 12:19:22 +0200212 led_trigger_unregister(&local->tpt_led);
Johannes Berge1e54062010-11-30 08:58:45 +0100213 kfree(local->tpt_led_trigger);
214 }
Jiri Bencf0706e82007-05-05 11:45:53 -0700215}
216
Johannes Bergf5c4ae02015-04-23 12:09:01 +0200217const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
Ivo van Doorncdcb006f2008-01-07 19:45:24 +0100218{
219 struct ieee80211_local *local = hw_to_local(hw);
220
Johannes Berg8d5c2582015-04-23 12:19:22 +0200221 return local->radio_led.name;
Ivo van Doorncdcb006f2008-01-07 19:45:24 +0100222}
223EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
224
Johannes Bergf5c4ae02015-04-23 12:09:01 +0200225const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
Michael Buesch47f0c502007-09-27 15:10:44 +0200226{
227 struct ieee80211_local *local = hw_to_local(hw);
228
Johannes Berg8d5c2582015-04-23 12:19:22 +0200229 return local->assoc_led.name;
Michael Buesch47f0c502007-09-27 15:10:44 +0200230}
231EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
232
Johannes Bergf5c4ae02015-04-23 12:09:01 +0200233const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
Jiri Bencf0706e82007-05-05 11:45:53 -0700234{
235 struct ieee80211_local *local = hw_to_local(hw);
236
Johannes Berg8d5c2582015-04-23 12:19:22 +0200237 return local->tx_led.name;
Jiri Bencf0706e82007-05-05 11:45:53 -0700238}
239EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
240
Johannes Bergf5c4ae02015-04-23 12:09:01 +0200241const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
Jiri Bencf0706e82007-05-05 11:45:53 -0700242{
243 struct ieee80211_local *local = hw_to_local(hw);
244
Johannes Berg8d5c2582015-04-23 12:19:22 +0200245 return local->rx_led.name;
Jiri Bencf0706e82007-05-05 11:45:53 -0700246}
247EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
Johannes Berge1e54062010-11-30 08:58:45 +0100248
249static 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
Kees Cook34f11cd2017-10-16 16:35:49 -0700261static void tpt_trig_timer(struct timer_list *t)
Johannes Berge1e54062010-11-30 08:58:45 +0100262{
Kees Cook34f11cd2017-10-16 16:35:49 -0700263 struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer);
264 struct ieee80211_local *local = tpt_trig->local;
Johannes Berge1e54062010-11-30 08:58:45 +0100265 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
Johannes Berg658358c2015-05-11 19:12:09 +0200289 read_lock(&local->tpt_led.leddev_list_lock);
290 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
Johannes Berge1e54062010-11-30 08:58:45 +0100291 led_blink_set(led_cdev, &on, &off);
Johannes Berg658358c2015-05-11 19:12:09 +0200292 read_unlock(&local->tpt_led.leddev_list_lock);
Johannes Berge1e54062010-11-30 08:58:45 +0100293}
294
Johannes Bergf5c4ae02015-04-23 12:09:01 +0200295const 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)
Johannes Berge1e54062010-11-30 08:58:45 +0100300{
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
Johannes Berg8d5c2582015-04-23 12:19:22 +0200314 local->tpt_led.name = tpt_trig->name;
Johannes Berge1e54062010-11-30 08:58:45 +0100315
316 tpt_trig->blink_table = blink_table;
317 tpt_trig->blink_table_len = blink_table_len;
Johannes Berg67408c82010-11-30 08:59:23 +0100318 tpt_trig->want = flags;
Kees Cook34f11cd2017-10-16 16:35:49 -0700319 tpt_trig->local = local;
Johannes Berge1e54062010-11-30 08:58:45 +0100320
Kees Cook34f11cd2017-10-16 16:35:49 -0700321 timer_setup(&tpt_trig->timer, tpt_trig_timer, 0);
Johannes Berge1e54062010-11-30 08:58:45 +0100322
323 local->tpt_led_trigger = tpt_trig;
324
325 return tpt_trig->name;
326}
327EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
328
Johannes Berg67408c82010-11-30 08:59:23 +0100329static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
Johannes Berge1e54062010-11-30 08:58:45 +0100330{
331 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
332
Johannes Berg67408c82010-11-30 08:59:23 +0100333 if (tpt_trig->running)
Johannes Berge1e54062010-11-30 08:58:45 +0100334 return;
335
336 /* reset traffic */
337 tpt_trig_traffic(local, tpt_trig);
338 tpt_trig->running = true;
339
Kees Cook34f11cd2017-10-16 16:35:49 -0700340 tpt_trig_timer(&tpt_trig->timer);
Johannes Berge1e54062010-11-30 08:58:45 +0100341 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
342}
343
Johannes Berg67408c82010-11-30 08:59:23 +0100344static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
Johannes Berge1e54062010-11-30 08:58:45 +0100345{
346 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
347 struct led_classdev *led_cdev;
348
Johannes Berg67408c82010-11-30 08:59:23 +0100349 if (!tpt_trig->running)
Johannes Berge1e54062010-11-30 08:58:45 +0100350 return;
351
352 tpt_trig->running = false;
353 del_timer_sync(&tpt_trig->timer);
354
Johannes Berg658358c2015-05-11 19:12:09 +0200355 read_lock(&local->tpt_led.leddev_list_lock);
356 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
Shuah Khan19cd67e2012-06-14 04:34:30 +0800357 led_set_brightness(led_cdev, LED_OFF);
Johannes Berg658358c2015-05-11 19:12:09 +0200358 read_unlock(&local->tpt_led.leddev_list_lock);
Johannes Berge1e54062010-11-30 08:58:45 +0100359}
Johannes Berg67408c82010-11-30 08:59:23 +0100360
361void 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}