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