Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[linux-2.6.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_rx(struct ieee80211_local *local)
16 {
17         if (unlikely(!local->rx_led))
18                 return;
19         if (local->rx_led_counter++ % 2 == 0)
20                 led_trigger_event(local->rx_led, LED_OFF);
21         else
22                 led_trigger_event(local->rx_led, LED_FULL);
23 }
24
25 /* q is 1 if a packet was enqueued, 0 if it has been transmitted */
26 void ieee80211_led_tx(struct ieee80211_local *local, int q)
27 {
28         if (unlikely(!local->tx_led))
29                 return;
30         /* not sure how this is supposed to work ... */
31         local->tx_led_counter += 2*q-1;
32         if (local->tx_led_counter % 2 == 0)
33                 led_trigger_event(local->tx_led, LED_OFF);
34         else
35                 led_trigger_event(local->tx_led, LED_FULL);
36 }
37
38 void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
39 {
40         if (unlikely(!local->assoc_led))
41                 return;
42         if (associated)
43                 led_trigger_event(local->assoc_led, LED_FULL);
44         else
45                 led_trigger_event(local->assoc_led, LED_OFF);
46 }
47
48 void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)
49 {
50         if (unlikely(!local->radio_led))
51                 return;
52         if (enabled)
53                 led_trigger_event(local->radio_led, LED_FULL);
54         else
55                 led_trigger_event(local->radio_led, LED_OFF);
56 }
57
58 void ieee80211_led_names(struct ieee80211_local *local)
59 {
60         snprintf(local->rx_led_name, sizeof(local->rx_led_name),
61                  "%srx", wiphy_name(local->hw.wiphy));
62         snprintf(local->tx_led_name, sizeof(local->tx_led_name),
63                  "%stx", wiphy_name(local->hw.wiphy));
64         snprintf(local->assoc_led_name, sizeof(local->assoc_led_name),
65                  "%sassoc", wiphy_name(local->hw.wiphy));
66         snprintf(local->radio_led_name, sizeof(local->radio_led_name),
67                  "%sradio", wiphy_name(local->hw.wiphy));
68 }
69
70 void ieee80211_led_init(struct ieee80211_local *local)
71 {
72         local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
73         if (local->rx_led) {
74                 local->rx_led->name = local->rx_led_name;
75                 if (led_trigger_register(local->rx_led)) {
76                         kfree(local->rx_led);
77                         local->rx_led = NULL;
78                 }
79         }
80
81         local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
82         if (local->tx_led) {
83                 local->tx_led->name = local->tx_led_name;
84                 if (led_trigger_register(local->tx_led)) {
85                         kfree(local->tx_led);
86                         local->tx_led = NULL;
87                 }
88         }
89
90         local->assoc_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
91         if (local->assoc_led) {
92                 local->assoc_led->name = local->assoc_led_name;
93                 if (led_trigger_register(local->assoc_led)) {
94                         kfree(local->assoc_led);
95                         local->assoc_led = NULL;
96                 }
97         }
98
99         local->radio_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
100         if (local->radio_led) {
101                 local->radio_led->name = local->radio_led_name;
102                 if (led_trigger_register(local->radio_led)) {
103                         kfree(local->radio_led);
104                         local->radio_led = NULL;
105                 }
106         }
107
108         if (local->tpt_led_trigger) {
109                 if (led_trigger_register(&local->tpt_led_trigger->trig)) {
110                         kfree(local->tpt_led_trigger);
111                         local->tpt_led_trigger = NULL;
112                 }
113         }
114 }
115
116 void ieee80211_led_exit(struct ieee80211_local *local)
117 {
118         if (local->radio_led) {
119                 led_trigger_unregister(local->radio_led);
120                 kfree(local->radio_led);
121         }
122         if (local->assoc_led) {
123                 led_trigger_unregister(local->assoc_led);
124                 kfree(local->assoc_led);
125         }
126         if (local->tx_led) {
127                 led_trigger_unregister(local->tx_led);
128                 kfree(local->tx_led);
129         }
130         if (local->rx_led) {
131                 led_trigger_unregister(local->rx_led);
132                 kfree(local->rx_led);
133         }
134
135         if (local->tpt_led_trigger) {
136                 led_trigger_unregister(&local->tpt_led_trigger->trig);
137                 kfree(local->tpt_led_trigger);
138         }
139 }
140
141 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
142 {
143         struct ieee80211_local *local = hw_to_local(hw);
144
145         return local->radio_led_name;
146 }
147 EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
148
149 char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
150 {
151         struct ieee80211_local *local = hw_to_local(hw);
152
153         return local->assoc_led_name;
154 }
155 EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
156
157 char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
158 {
159         struct ieee80211_local *local = hw_to_local(hw);
160
161         return local->tx_led_name;
162 }
163 EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
164
165 char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
166 {
167         struct ieee80211_local *local = hw_to_local(hw);
168
169         return local->rx_led_name;
170 }
171 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
172
173 static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
174                                       struct tpt_led_trigger *tpt_trig)
175 {
176         unsigned long traffic, delta;
177
178         traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
179
180         delta = traffic - tpt_trig->prev_traffic;
181         tpt_trig->prev_traffic = traffic;
182         return DIV_ROUND_UP(delta, 1024 / 8);
183 }
184
185 static void tpt_trig_timer(unsigned long data)
186 {
187         struct ieee80211_local *local = (void *)data;
188         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
189         struct led_classdev *led_cdev;
190         unsigned long on, off, tpt;
191         int i;
192
193         if (!tpt_trig->running)
194                 return;
195
196         mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
197
198         tpt = tpt_trig_traffic(local, tpt_trig);
199
200         /* default to just solid on */
201         on = 1;
202         off = 0;
203
204         for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
205                 if (tpt_trig->blink_table[i].throughput < 0 ||
206                     tpt > tpt_trig->blink_table[i].throughput) {
207                         off = tpt_trig->blink_table[i].blink_time / 2;
208                         on = tpt_trig->blink_table[i].blink_time - off;
209                         break;
210                 }
211         }
212
213         read_lock(&tpt_trig->trig.leddev_list_lock);
214         list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
215                 led_blink_set(led_cdev, &on, &off);
216         read_unlock(&tpt_trig->trig.leddev_list_lock);
217 }
218
219 char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
220                                 unsigned int flags,
221                                 const struct ieee80211_tpt_blink *blink_table,
222                                 unsigned int blink_table_len)
223 {
224         struct ieee80211_local *local = hw_to_local(hw);
225         struct tpt_led_trigger *tpt_trig;
226
227         if (WARN_ON(local->tpt_led_trigger))
228                 return NULL;
229
230         tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
231         if (!tpt_trig)
232                 return NULL;
233
234         snprintf(tpt_trig->name, sizeof(tpt_trig->name),
235                  "%stpt", wiphy_name(local->hw.wiphy));
236
237         tpt_trig->trig.name = tpt_trig->name;
238
239         tpt_trig->blink_table = blink_table;
240         tpt_trig->blink_table_len = blink_table_len;
241         tpt_trig->want = flags;
242
243         setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
244
245         local->tpt_led_trigger = tpt_trig;
246
247         return tpt_trig->name;
248 }
249 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
250
251 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
252 {
253         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
254
255         if (tpt_trig->running)
256                 return;
257
258         /* reset traffic */
259         tpt_trig_traffic(local, tpt_trig);
260         tpt_trig->running = true;
261
262         tpt_trig_timer((unsigned long)local);
263         mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
264 }
265
266 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
267 {
268         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
269         struct led_classdev *led_cdev;
270
271         if (!tpt_trig->running)
272                 return;
273
274         tpt_trig->running = false;
275         del_timer_sync(&tpt_trig->timer);
276
277         read_lock(&tpt_trig->trig.leddev_list_lock);
278         list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
279                 led_brightness_set(led_cdev, LED_OFF);
280         read_unlock(&tpt_trig->trig.leddev_list_lock);
281 }
282
283 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
284                                 unsigned int types_on, unsigned int types_off)
285 {
286         struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
287         bool allowed;
288
289         WARN_ON(types_on & types_off);
290
291         if (!tpt_trig)
292                 return;
293
294         tpt_trig->active &= ~types_off;
295         tpt_trig->active |= types_on;
296
297         /*
298          * Regardless of wanted state, we shouldn't blink when
299          * the radio is disabled -- this can happen due to some
300          * code ordering issues with __ieee80211_recalc_idle()
301          * being called before the radio is started.
302          */
303         allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
304
305         if (!allowed || !(tpt_trig->active & tpt_trig->want))
306                 ieee80211_stop_tpt_led_trig(local);
307         else
308                 ieee80211_start_tpt_led_trig(local);
309 }