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