Blame view

net/mac80211/led.c 9.97 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
f0706e828   Jiri Benc   [MAC80211]: Add m...
2
3
  /*
   * Copyright 2006, Johannes Berg <johannes@sipsolutions.net>
f0706e828   Jiri Benc   [MAC80211]: Add m...
4
5
6
7
   */
  
  /* just for IFNAMSIZ */
  #include <linux/if.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
8
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
9
  #include <linux/export.h>
2c8dccc77   Johannes Berg   mac80211: rename ...
10
  #include "led.h"
f0706e828   Jiri Benc   [MAC80211]: Add m...
11

47f0c5022   Michael Buesch   [MAC80211]: Add a...
12
13
  void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
  {
8d5c25856   Johannes Berg   mac80211: make LE...
14
  	if (!atomic_read(&local->assoc_led_active))
47f0c5022   Michael Buesch   [MAC80211]: Add a...
15
16
  		return;
  	if (associated)
8d5c25856   Johannes Berg   mac80211: make LE...
17
  		led_trigger_event(&local->assoc_led, LED_FULL);
47f0c5022   Michael Buesch   [MAC80211]: Add a...
18
  	else
8d5c25856   Johannes Berg   mac80211: make LE...
19
  		led_trigger_event(&local->assoc_led, LED_OFF);
47f0c5022   Michael Buesch   [MAC80211]: Add a...
20
  }
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
21
22
  void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)
  {
8d5c25856   Johannes Berg   mac80211: make LE...
23
  	if (!atomic_read(&local->radio_led_active))
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
24
25
  		return;
  	if (enabled)
8d5c25856   Johannes Berg   mac80211: make LE...
26
  		led_trigger_event(&local->radio_led, LED_FULL);
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
27
  	else
8d5c25856   Johannes Berg   mac80211: make LE...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  		led_trigger_event(&local->radio_led, LED_OFF);
  }
  
  void ieee80211_alloc_led_names(struct ieee80211_local *local)
  {
  	local->rx_led.name = kasprintf(GFP_KERNEL, "%srx",
  				       wiphy_name(local->hw.wiphy));
  	local->tx_led.name = kasprintf(GFP_KERNEL, "%stx",
  				       wiphy_name(local->hw.wiphy));
  	local->assoc_led.name = kasprintf(GFP_KERNEL, "%sassoc",
  					  wiphy_name(local->hw.wiphy));
  	local->radio_led.name = kasprintf(GFP_KERNEL, "%sradio",
  					  wiphy_name(local->hw.wiphy));
  }
  
  void ieee80211_free_led_names(struct ieee80211_local *local)
  {
  	kfree(local->rx_led.name);
  	kfree(local->tx_led.name);
  	kfree(local->assoc_led.name);
  	kfree(local->radio_led.name);
  }
2282e125a   Uwe Kleine-König   leds: triggers: l...
50
  static int ieee80211_tx_led_activate(struct led_classdev *led_cdev)
8d5c25856   Johannes Berg   mac80211: make LE...
51
52
53
54
55
56
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     tx_led);
  
  	atomic_inc(&local->tx_led_active);
2282e125a   Uwe Kleine-König   leds: triggers: l...
57
58
  
  	return 0;
8d5c25856   Johannes Berg   mac80211: make LE...
59
60
61
62
63
64
65
66
67
68
  }
  
  static void ieee80211_tx_led_deactivate(struct led_classdev *led_cdev)
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     tx_led);
  
  	atomic_dec(&local->tx_led_active);
  }
2282e125a   Uwe Kleine-König   leds: triggers: l...
69
  static int ieee80211_rx_led_activate(struct led_classdev *led_cdev)
8d5c25856   Johannes Berg   mac80211: make LE...
70
71
72
73
74
75
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     rx_led);
  
  	atomic_inc(&local->rx_led_active);
2282e125a   Uwe Kleine-König   leds: triggers: l...
76
77
  
  	return 0;
8d5c25856   Johannes Berg   mac80211: make LE...
78
79
80
81
82
83
84
85
86
87
  }
  
  static void ieee80211_rx_led_deactivate(struct led_classdev *led_cdev)
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     rx_led);
  
  	atomic_dec(&local->rx_led_active);
  }
2282e125a   Uwe Kleine-König   leds: triggers: l...
88
  static int ieee80211_assoc_led_activate(struct led_classdev *led_cdev)
8d5c25856   Johannes Berg   mac80211: make LE...
89
90
91
92
93
94
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     assoc_led);
  
  	atomic_inc(&local->assoc_led_active);
2282e125a   Uwe Kleine-König   leds: triggers: l...
95
96
  
  	return 0;
8d5c25856   Johannes Berg   mac80211: make LE...
97
98
99
100
101
102
103
104
105
106
  }
  
  static void ieee80211_assoc_led_deactivate(struct led_classdev *led_cdev)
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     assoc_led);
  
  	atomic_dec(&local->assoc_led_active);
  }
2282e125a   Uwe Kleine-König   leds: triggers: l...
107
  static int ieee80211_radio_led_activate(struct led_classdev *led_cdev)
8d5c25856   Johannes Berg   mac80211: make LE...
108
109
110
111
112
113
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     radio_led);
  
  	atomic_inc(&local->radio_led_active);
2282e125a   Uwe Kleine-König   leds: triggers: l...
114
115
  
  	return 0;
8d5c25856   Johannes Berg   mac80211: make LE...
116
117
118
119
120
121
122
123
124
125
  }
  
  static void ieee80211_radio_led_deactivate(struct led_classdev *led_cdev)
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     radio_led);
  
  	atomic_dec(&local->radio_led_active);
  }
2282e125a   Uwe Kleine-König   leds: triggers: l...
126
  static int ieee80211_tpt_led_activate(struct led_classdev *led_cdev)
8d5c25856   Johannes Berg   mac80211: make LE...
127
128
129
130
131
132
  {
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     tpt_led);
  
  	atomic_inc(&local->tpt_led_active);
2282e125a   Uwe Kleine-König   leds: triggers: l...
133
134
  
  	return 0;
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
135
  }
8d5c25856   Johannes Berg   mac80211: make LE...
136
  static void ieee80211_tpt_led_deactivate(struct led_classdev *led_cdev)
fe67c913f   Johannes Berg   mac80211: make LE...
137
  {
8d5c25856   Johannes Berg   mac80211: make LE...
138
139
140
141
142
  	struct ieee80211_local *local = container_of(led_cdev->trigger,
  						     struct ieee80211_local,
  						     tpt_led);
  
  	atomic_dec(&local->tpt_led_active);
fe67c913f   Johannes Berg   mac80211: make LE...
143
  }
f0706e828   Jiri Benc   [MAC80211]: Add m...
144
145
  void ieee80211_led_init(struct ieee80211_local *local)
  {
8d5c25856   Johannes Berg   mac80211: make LE...
146
147
148
149
150
151
  	atomic_set(&local->rx_led_active, 0);
  	local->rx_led.activate = ieee80211_rx_led_activate;
  	local->rx_led.deactivate = ieee80211_rx_led_deactivate;
  	if (local->rx_led.name && led_trigger_register(&local->rx_led)) {
  		kfree(local->rx_led.name);
  		local->rx_led.name = NULL;
f0706e828   Jiri Benc   [MAC80211]: Add m...
152
  	}
8d5c25856   Johannes Berg   mac80211: make LE...
153
154
155
156
157
158
  	atomic_set(&local->tx_led_active, 0);
  	local->tx_led.activate = ieee80211_tx_led_activate;
  	local->tx_led.deactivate = ieee80211_tx_led_deactivate;
  	if (local->tx_led.name && led_trigger_register(&local->tx_led)) {
  		kfree(local->tx_led.name);
  		local->tx_led.name = NULL;
47f0c5022   Michael Buesch   [MAC80211]: Add a...
159
  	}
8d5c25856   Johannes Berg   mac80211: make LE...
160
161
162
163
164
165
  	atomic_set(&local->assoc_led_active, 0);
  	local->assoc_led.activate = ieee80211_assoc_led_activate;
  	local->assoc_led.deactivate = ieee80211_assoc_led_deactivate;
  	if (local->assoc_led.name && led_trigger_register(&local->assoc_led)) {
  		kfree(local->assoc_led.name);
  		local->assoc_led.name = NULL;
f0706e828   Jiri Benc   [MAC80211]: Add m...
166
  	}
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
167

8d5c25856   Johannes Berg   mac80211: make LE...
168
169
170
171
172
173
  	atomic_set(&local->radio_led_active, 0);
  	local->radio_led.activate = ieee80211_radio_led_activate;
  	local->radio_led.deactivate = ieee80211_radio_led_deactivate;
  	if (local->radio_led.name && led_trigger_register(&local->radio_led)) {
  		kfree(local->radio_led.name);
  		local->radio_led.name = NULL;
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
174
  	}
e1e540685   Johannes Berg   mac80211: add thr...
175

8d5c25856   Johannes Berg   mac80211: make LE...
176
  	atomic_set(&local->tpt_led_active, 0);
e1e540685   Johannes Berg   mac80211: add thr...
177
  	if (local->tpt_led_trigger) {
8d5c25856   Johannes Berg   mac80211: make LE...
178
179
180
  		local->tpt_led.activate = ieee80211_tpt_led_activate;
  		local->tpt_led.deactivate = ieee80211_tpt_led_deactivate;
  		if (led_trigger_register(&local->tpt_led)) {
e1e540685   Johannes Berg   mac80211: add thr...
181
182
183
184
  			kfree(local->tpt_led_trigger);
  			local->tpt_led_trigger = NULL;
  		}
  	}
f0706e828   Jiri Benc   [MAC80211]: Add m...
185
186
187
188
  }
  
  void ieee80211_led_exit(struct ieee80211_local *local)
  {
8d5c25856   Johannes Berg   mac80211: make LE...
189
190
191
192
193
194
195
196
  	if (local->radio_led.name)
  		led_trigger_unregister(&local->radio_led);
  	if (local->assoc_led.name)
  		led_trigger_unregister(&local->assoc_led);
  	if (local->tx_led.name)
  		led_trigger_unregister(&local->tx_led);
  	if (local->rx_led.name)
  		led_trigger_unregister(&local->rx_led);
e1e540685   Johannes Berg   mac80211: add thr...
197
198
  
  	if (local->tpt_led_trigger) {
8d5c25856   Johannes Berg   mac80211: make LE...
199
  		led_trigger_unregister(&local->tpt_led);
e1e540685   Johannes Berg   mac80211: add thr...
200
201
  		kfree(local->tpt_led_trigger);
  	}
f0706e828   Jiri Benc   [MAC80211]: Add m...
202
  }
f5c4ae079   Johannes Berg   mac80211: make LE...
203
  const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
204
205
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
206
  	return local->radio_led.name;
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
207
208
  }
  EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
f5c4ae079   Johannes Berg   mac80211: make LE...
209
  const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
47f0c5022   Michael Buesch   [MAC80211]: Add a...
210
211
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
212
  	return local->assoc_led.name;
47f0c5022   Michael Buesch   [MAC80211]: Add a...
213
214
  }
  EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
f5c4ae079   Johannes Berg   mac80211: make LE...
215
  const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
f0706e828   Jiri Benc   [MAC80211]: Add m...
216
217
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
218
  	return local->tx_led.name;
f0706e828   Jiri Benc   [MAC80211]: Add m...
219
220
  }
  EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
f5c4ae079   Johannes Berg   mac80211: make LE...
221
  const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
f0706e828   Jiri Benc   [MAC80211]: Add m...
222
223
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
224
  	return local->rx_led.name;
f0706e828   Jiri Benc   [MAC80211]: Add m...
225
226
  }
  EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
e1e540685   Johannes Berg   mac80211: add thr...
227
228
229
230
231
232
233
234
235
236
237
238
  
  static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
  				      struct tpt_led_trigger *tpt_trig)
  {
  	unsigned long traffic, delta;
  
  	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
  
  	delta = traffic - tpt_trig->prev_traffic;
  	tpt_trig->prev_traffic = traffic;
  	return DIV_ROUND_UP(delta, 1024 / 8);
  }
34f11cd32   Kees Cook   mac80211: Convert...
239
  static void tpt_trig_timer(struct timer_list *t)
e1e540685   Johannes Berg   mac80211: add thr...
240
  {
34f11cd32   Kees Cook   mac80211: Convert...
241
242
  	struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer);
  	struct ieee80211_local *local = tpt_trig->local;
e1e540685   Johannes Berg   mac80211: add thr...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  	struct led_classdev *led_cdev;
  	unsigned long on, off, tpt;
  	int i;
  
  	if (!tpt_trig->running)
  		return;
  
  	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
  
  	tpt = tpt_trig_traffic(local, tpt_trig);
  
  	/* default to just solid on */
  	on = 1;
  	off = 0;
  
  	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
  		if (tpt_trig->blink_table[i].throughput < 0 ||
  		    tpt > tpt_trig->blink_table[i].throughput) {
  			off = tpt_trig->blink_table[i].blink_time / 2;
  			on = tpt_trig->blink_table[i].blink_time - off;
  			break;
  		}
  	}
658358cec   Johannes Berg   mac80211: fix thr...
266
267
  	read_lock(&local->tpt_led.leddev_list_lock);
  	list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
e1e540685   Johannes Berg   mac80211: add thr...
268
  		led_blink_set(led_cdev, &on, &off);
658358cec   Johannes Berg   mac80211: fix thr...
269
  	read_unlock(&local->tpt_led.leddev_list_lock);
e1e540685   Johannes Berg   mac80211: add thr...
270
  }
f5c4ae079   Johannes Berg   mac80211: make LE...
271
272
273
274
275
  const char *
  __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
  				   unsigned int flags,
  				   const struct ieee80211_tpt_blink *blink_table,
  				   unsigned int blink_table_len)
e1e540685   Johannes Berg   mac80211: add thr...
276
277
278
279
280
281
282
283
284
285
286
287
288
  {
  	struct ieee80211_local *local = hw_to_local(hw);
  	struct tpt_led_trigger *tpt_trig;
  
  	if (WARN_ON(local->tpt_led_trigger))
  		return NULL;
  
  	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
  	if (!tpt_trig)
  		return NULL;
  
  	snprintf(tpt_trig->name, sizeof(tpt_trig->name),
  		 "%stpt", wiphy_name(local->hw.wiphy));
8d5c25856   Johannes Berg   mac80211: make LE...
289
  	local->tpt_led.name = tpt_trig->name;
e1e540685   Johannes Berg   mac80211: add thr...
290
291
292
  
  	tpt_trig->blink_table = blink_table;
  	tpt_trig->blink_table_len = blink_table_len;
67408c8c7   Johannes Berg   mac80211: selecti...
293
  	tpt_trig->want = flags;
34f11cd32   Kees Cook   mac80211: Convert...
294
  	tpt_trig->local = local;
e1e540685   Johannes Berg   mac80211: add thr...
295

34f11cd32   Kees Cook   mac80211: Convert...
296
  	timer_setup(&tpt_trig->timer, tpt_trig_timer, 0);
e1e540685   Johannes Berg   mac80211: add thr...
297
298
299
300
301
302
  
  	local->tpt_led_trigger = tpt_trig;
  
  	return tpt_trig->name;
  }
  EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
67408c8c7   Johannes Berg   mac80211: selecti...
303
  static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
e1e540685   Johannes Berg   mac80211: add thr...
304
305
  {
  	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
67408c8c7   Johannes Berg   mac80211: selecti...
306
  	if (tpt_trig->running)
e1e540685   Johannes Berg   mac80211: add thr...
307
308
309
310
311
  		return;
  
  	/* reset traffic */
  	tpt_trig_traffic(local, tpt_trig);
  	tpt_trig->running = true;
34f11cd32   Kees Cook   mac80211: Convert...
312
  	tpt_trig_timer(&tpt_trig->timer);
e1e540685   Johannes Berg   mac80211: add thr...
313
314
  	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
  }
67408c8c7   Johannes Berg   mac80211: selecti...
315
  static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
e1e540685   Johannes Berg   mac80211: add thr...
316
317
318
  {
  	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
  	struct led_classdev *led_cdev;
67408c8c7   Johannes Berg   mac80211: selecti...
319
  	if (!tpt_trig->running)
e1e540685   Johannes Berg   mac80211: add thr...
320
321
322
323
  		return;
  
  	tpt_trig->running = false;
  	del_timer_sync(&tpt_trig->timer);
658358cec   Johannes Berg   mac80211: fix thr...
324
325
  	read_lock(&local->tpt_led.leddev_list_lock);
  	list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
19cd67e2d   Shuah Khan   leds: Rename led_...
326
  		led_set_brightness(led_cdev, LED_OFF);
658358cec   Johannes Berg   mac80211: fix thr...
327
  	read_unlock(&local->tpt_led.leddev_list_lock);
e1e540685   Johannes Berg   mac80211: add thr...
328
  }
67408c8c7   Johannes Berg   mac80211: selecti...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  
  void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
  				unsigned int types_on, unsigned int types_off)
  {
  	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
  	bool allowed;
  
  	WARN_ON(types_on & types_off);
  
  	if (!tpt_trig)
  		return;
  
  	tpt_trig->active &= ~types_off;
  	tpt_trig->active |= types_on;
  
  	/*
  	 * Regardless of wanted state, we shouldn't blink when
  	 * the radio is disabled -- this can happen due to some
  	 * code ordering issues with __ieee80211_recalc_idle()
  	 * being called before the radio is started.
  	 */
  	allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
  
  	if (!allowed || !(tpt_trig->active & tpt_trig->want))
  		ieee80211_stop_tpt_led_trig(local);
  	else
  		ieee80211_start_tpt_led_trig(local);
  }