Blame view

net/mac80211/led.c 10 KB
f0706e828   Jiri Benc   [MAC80211]: Add m...
1
2
3
4
5
6
7
8
9
10
  /*
   * Copyright 2006, Johannes Berg <johannes@sipsolutions.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  /* just for IFNAMSIZ */
  #include <linux/if.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
11
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
12
  #include <linux/export.h>
2c8dccc77   Johannes Berg   mac80211: rename ...
13
  #include "led.h"
f0706e828   Jiri Benc   [MAC80211]: Add m...
14

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

8d5c25856   Johannes Berg   mac80211: make LE...
166
167
168
169
170
171
  	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...
172
  	}
e1e540685   Johannes Berg   mac80211: add thr...
173

8d5c25856   Johannes Berg   mac80211: make LE...
174
  	atomic_set(&local->tpt_led_active, 0);
e1e540685   Johannes Berg   mac80211: add thr...
175
  	if (local->tpt_led_trigger) {
8d5c25856   Johannes Berg   mac80211: make LE...
176
177
178
  		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...
179
180
181
182
  			kfree(local->tpt_led_trigger);
  			local->tpt_led_trigger = NULL;
  		}
  	}
f0706e828   Jiri Benc   [MAC80211]: Add m...
183
184
185
186
  }
  
  void ieee80211_led_exit(struct ieee80211_local *local)
  {
8d5c25856   Johannes Berg   mac80211: make LE...
187
188
189
190
191
192
193
194
  	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...
195
196
  
  	if (local->tpt_led_trigger) {
8d5c25856   Johannes Berg   mac80211: make LE...
197
  		led_trigger_unregister(&local->tpt_led);
e1e540685   Johannes Berg   mac80211: add thr...
198
199
  		kfree(local->tpt_led_trigger);
  	}
f0706e828   Jiri Benc   [MAC80211]: Add m...
200
  }
f5c4ae079   Johannes Berg   mac80211: make LE...
201
  const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
202
203
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
204
  	return local->radio_led.name;
cdcb006fb   Ivo van Doorn   mac80211: Add rad...
205
206
  }
  EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
f5c4ae079   Johannes Berg   mac80211: make LE...
207
  const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
47f0c5022   Michael Buesch   [MAC80211]: Add a...
208
209
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
210
  	return local->assoc_led.name;
47f0c5022   Michael Buesch   [MAC80211]: Add a...
211
212
  }
  EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
f5c4ae079   Johannes Berg   mac80211: make LE...
213
  const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
f0706e828   Jiri Benc   [MAC80211]: Add m...
214
215
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
216
  	return local->tx_led.name;
f0706e828   Jiri Benc   [MAC80211]: Add m...
217
218
  }
  EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
f5c4ae079   Johannes Berg   mac80211: make LE...
219
  const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
f0706e828   Jiri Benc   [MAC80211]: Add m...
220
221
  {
  	struct ieee80211_local *local = hw_to_local(hw);
8d5c25856   Johannes Berg   mac80211: make LE...
222
  	return local->rx_led.name;
f0706e828   Jiri Benc   [MAC80211]: Add m...
223
224
  }
  EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
e1e540685   Johannes Berg   mac80211: add thr...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  
  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);
  }
  
  static void tpt_trig_timer(unsigned long data)
  {
  	struct ieee80211_local *local = (void *)data;
  	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
  	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...
265
266
  	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...
267
  		led_blink_set(led_cdev, &on, &off);
658358cec   Johannes Berg   mac80211: fix thr...
268
  	read_unlock(&local->tpt_led.leddev_list_lock);
e1e540685   Johannes Berg   mac80211: add thr...
269
  }
f5c4ae079   Johannes Berg   mac80211: make LE...
270
271
272
273
274
  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...
275
276
277
278
279
280
281
282
283
284
285
286
287
  {
  	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...
288
  	local->tpt_led.name = tpt_trig->name;
e1e540685   Johannes Berg   mac80211: add thr...
289
290
291
  
  	tpt_trig->blink_table = blink_table;
  	tpt_trig->blink_table_len = blink_table_len;
67408c8c7   Johannes Berg   mac80211: selecti...
292
  	tpt_trig->want = flags;
e1e540685   Johannes Berg   mac80211: add thr...
293
294
295
296
297
298
299
300
  
  	setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
  
  	local->tpt_led_trigger = tpt_trig;
  
  	return tpt_trig->name;
  }
  EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
67408c8c7   Johannes Berg   mac80211: selecti...
301
  static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
e1e540685   Johannes Berg   mac80211: add thr...
302
303
  {
  	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
67408c8c7   Johannes Berg   mac80211: selecti...
304
  	if (tpt_trig->running)
e1e540685   Johannes Berg   mac80211: add thr...
305
306
307
308
309
310
311
312
313
  		return;
  
  	/* reset traffic */
  	tpt_trig_traffic(local, tpt_trig);
  	tpt_trig->running = true;
  
  	tpt_trig_timer((unsigned long)local);
  	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
  }
67408c8c7   Johannes Berg   mac80211: selecti...
314
  static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
e1e540685   Johannes Berg   mac80211: add thr...
315
316
317
  {
  	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
  	struct led_classdev *led_cdev;
67408c8c7   Johannes Berg   mac80211: selecti...
318
  	if (!tpt_trig->running)
e1e540685   Johannes Berg   mac80211: add thr...
319
320
321
322
  		return;
  
  	tpt_trig->running = false;
  	del_timer_sync(&tpt_trig->timer);
658358cec   Johannes Berg   mac80211: fix thr...
323
324
  	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_...
325
  		led_set_brightness(led_cdev, LED_OFF);
658358cec   Johannes Berg   mac80211: fix thr...
326
  	read_unlock(&local->tpt_led.leddev_list_lock);
e1e540685   Johannes Berg   mac80211: add thr...
327
  }
67408c8c7   Johannes Berg   mac80211: selecti...
328
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
  
  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);
  }