[trunk/include/autotools.mk] disable libtool fixups in trunk globally
[openwrt/svn-archive/archive.git] / package / mac80211 / patches / 562-throughput_trigger.patch
1 Subject: mac80211: add throughput based LED blink trigger
2 From: Johannes Berg <johannes.berg@intel.com>
3
4 iwlwifi and other drivers like to blink their LED
5 based on throughput. Implement this generically in
6 mac80211, based on a throughput table the driver
7 specifies. That way, drivers can set the blink
8 frequencies depending on their desired behaviour
9 and max throughput.
10
11 All the drivers need to do is provide an LED class
12 device, best with blink hardware offload.
13
14 Signed-off-by: Johannes Berg <johannes.berg@intel.com>
15 ---
16 v2: turn off LED when turning off radio
17 v3: - use only data frames
18 - fix update frequency to 1 second
19 - use DIV_ROUND_UP
20
21 include/net/mac80211.h | 38 ++++++++++++++
22 net/mac80211/ieee80211_i.h | 13 ++++
23 net/mac80211/iface.c | 1
24 net/mac80211/led.c | 119 +++++++++++++++++++++++++++++++++++++++++++++
25 net/mac80211/led.h | 44 +++++++++++++---
26 net/mac80211/rx.c | 3 +
27 net/mac80211/tx.c | 3 +
28 net/mac80211/util.c | 2
29 8 files changed, 214 insertions(+), 9 deletions(-)
30
31 --- a/include/net/mac80211.h
32 +++ b/include/net/mac80211.h
33 @@ -1849,11 +1849,26 @@ struct ieee80211_hw *ieee80211_alloc_hw(
34 */
35 int ieee80211_register_hw(struct ieee80211_hw *hw);
36
37 +/**
38 + * struct ieee80211_tpt_blink - throughput blink description
39 + * @throughput: throughput in Kbit/sec
40 + * @blink_time: blink time in milliseconds
41 + * (full cycle, ie. one off + one on period)
42 + */
43 +struct ieee80211_tpt_blink {
44 + int throughput;
45 + int blink_time;
46 +};
47 +
48 #ifdef CONFIG_MAC80211_LEDS
49 extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
50 extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
51 extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
52 extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
53 +extern char *__ieee80211_create_tpt_led_trigger(
54 + struct ieee80211_hw *hw,
55 + const struct ieee80211_tpt_blink *blink_table,
56 + unsigned int blink_table_len);
57 #endif
58 /**
59 * ieee80211_get_tx_led_name - get name of TX LED
60 @@ -1932,6 +1947,29 @@ static inline char *ieee80211_get_radio_
61 }
62
63 /**
64 + * ieee80211_create_tpt_led_trigger - create throughput LED trigger
65 + * @hw: the hardware to create the trigger for
66 + * @blink_table: the blink table -- needs to be ordered by throughput
67 + * @blink_table_len: size of the blink table
68 + *
69 + * This function returns %NULL (in case of error, or if no LED
70 + * triggers are configured) or the name of the new trigger.
71 + * This function must be called before ieee80211_register_hw().
72 + */
73 +static inline char *
74 +ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
75 + const struct ieee80211_tpt_blink *blink_table,
76 + unsigned int blink_table_len)
77 +{
78 +#ifdef CONFIG_MAC80211_LEDS
79 + return __ieee80211_create_tpt_led_trigger(hw, blink_table,
80 + blink_table_len);
81 +#else
82 + return NULL;
83 +#endif
84 +}
85 +
86 +/**
87 * ieee80211_unregister_hw - Unregister a hardware device
88 *
89 * This function instructs mac80211 to free allocated resources
90 --- a/net/mac80211/ieee80211_i.h
91 +++ b/net/mac80211/ieee80211_i.h
92 @@ -23,6 +23,7 @@
93 #include <linux/types.h>
94 #include <linux/spinlock.h>
95 #include <linux/etherdevice.h>
96 +#include <linux/leds.h>
97 #include <net/ieee80211_radiotap.h>
98 #include <net/cfg80211.h>
99 #include <net/mac80211.h>
100 @@ -636,6 +637,17 @@ enum queue_stop_reason {
101 IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
102 };
103
104 +struct tpt_led_trigger {
105 + struct led_trigger trig;
106 + char name[32];
107 + const struct ieee80211_tpt_blink *blink_table;
108 + unsigned int blink_table_len;
109 + struct timer_list timer;
110 + bool running;
111 + unsigned long prev_traffic;
112 + unsigned long tx_bytes, rx_bytes;
113 +};
114 +
115 /**
116 * mac80211 scan flags - currently active scan mode
117 *
118 @@ -849,6 +861,7 @@ struct ieee80211_local {
119 #ifdef CONFIG_MAC80211_LEDS
120 int tx_led_counter, rx_led_counter;
121 struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
122 + struct tpt_led_trigger *tpt_led_trigger;
123 char tx_led_name[32], rx_led_name[32],
124 assoc_led_name[32], radio_led_name[32];
125 #endif
126 --- a/net/mac80211/led.c
127 +++ b/net/mac80211/led.c
128 @@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211
129 local->radio_led = NULL;
130 }
131 }
132 +
133 + if (local->tpt_led_trigger) {
134 + if (led_trigger_register(&local->tpt_led_trigger->trig)) {
135 + kfree(local->tpt_led_trigger);
136 + local->tpt_led_trigger = NULL;
137 + }
138 + }
139 }
140
141 void ieee80211_led_exit(struct ieee80211_local *local)
142 @@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211
143 led_trigger_unregister(local->rx_led);
144 kfree(local->rx_led);
145 }
146 +
147 + if (local->tpt_led_trigger) {
148 + led_trigger_unregister(&local->tpt_led_trigger->trig);
149 + kfree(local->tpt_led_trigger);
150 + }
151 }
152
153 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
154 @@ -156,3 +168,110 @@ char *__ieee80211_get_rx_led_name(struct
155 return local->rx_led_name;
156 }
157 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
158 +
159 +static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
160 + struct tpt_led_trigger *tpt_trig)
161 +{
162 + unsigned long traffic, delta;
163 +
164 + traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
165 +
166 + delta = traffic - tpt_trig->prev_traffic;
167 + tpt_trig->prev_traffic = traffic;
168 + return DIV_ROUND_UP(delta, 1024 / 8);
169 +}
170 +
171 +static void tpt_trig_timer(unsigned long data)
172 +{
173 + struct ieee80211_local *local = (void *)data;
174 + struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
175 + struct led_classdev *led_cdev;
176 + unsigned long on, off, tpt;
177 + int i;
178 +
179 + if (!tpt_trig->running)
180 + return;
181 +
182 + mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
183 +
184 + tpt = tpt_trig_traffic(local, tpt_trig);
185 +
186 + /* default to just solid on */
187 + on = 1;
188 + off = 0;
189 +
190 + for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
191 + if (tpt > tpt_trig->blink_table[i].throughput) {
192 + off = tpt_trig->blink_table[i].blink_time / 2;
193 + on = tpt_trig->blink_table[i].blink_time - off;
194 + break;
195 + }
196 + }
197 +
198 + read_lock(&tpt_trig->trig.leddev_list_lock);
199 + list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
200 + led_blink_set(led_cdev, &on, &off);
201 + read_unlock(&tpt_trig->trig.leddev_list_lock);
202 +}
203 +
204 +extern char *__ieee80211_create_tpt_led_trigger(
205 + struct ieee80211_hw *hw,
206 + const struct ieee80211_tpt_blink *blink_table,
207 + unsigned int blink_table_len)
208 +{
209 + struct ieee80211_local *local = hw_to_local(hw);
210 + struct tpt_led_trigger *tpt_trig;
211 +
212 + if (WARN_ON(local->tpt_led_trigger))
213 + return NULL;
214 +
215 + tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
216 + if (!tpt_trig)
217 + return NULL;
218 +
219 + snprintf(tpt_trig->name, sizeof(tpt_trig->name),
220 + "%stpt", wiphy_name(local->hw.wiphy));
221 +
222 + tpt_trig->trig.name = tpt_trig->name;
223 +
224 + tpt_trig->blink_table = blink_table;
225 + tpt_trig->blink_table_len = blink_table_len;
226 +
227 + setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
228 +
229 + local->tpt_led_trigger = tpt_trig;
230 +
231 + return tpt_trig->name;
232 +}
233 +EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
234 +
235 +void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
236 +{
237 + struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
238 +
239 + if (!tpt_trig)
240 + return;
241 +
242 + /* reset traffic */
243 + tpt_trig_traffic(local, tpt_trig);
244 + tpt_trig->running = true;
245 +
246 + mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
247 +}
248 +
249 +void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
250 +{
251 + struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
252 + struct led_classdev *led_cdev;
253 +
254 + if (!tpt_trig)
255 + return;
256 +
257 + tpt_trig->running = false;
258 + del_timer_sync(&tpt_trig->timer);
259 +
260 + read_lock(&tpt_trig->trig.leddev_list_lock);
261 + list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
262 + led_brightness_set(led_cdev, LED_OFF);
263 + read_unlock(&tpt_trig->trig.leddev_list_lock);
264 +}
265 --- a/net/mac80211/led.h
266 +++ b/net/mac80211/led.h
267 @@ -12,15 +12,17 @@
268 #include "ieee80211_i.h"
269
270 #ifdef CONFIG_MAC80211_LEDS
271 -extern void ieee80211_led_rx(struct ieee80211_local *local);
272 -extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
273 -extern void ieee80211_led_assoc(struct ieee80211_local *local,
274 - bool associated);
275 -extern void ieee80211_led_radio(struct ieee80211_local *local,
276 - bool enabled);
277 -extern void ieee80211_led_names(struct ieee80211_local *local);
278 -extern void ieee80211_led_init(struct ieee80211_local *local);
279 -extern void ieee80211_led_exit(struct ieee80211_local *local);
280 +void ieee80211_led_rx(struct ieee80211_local *local);
281 +void ieee80211_led_tx(struct ieee80211_local *local, int q);
282 +void ieee80211_led_assoc(struct ieee80211_local *local,
283 + bool associated);
284 +void ieee80211_led_radio(struct ieee80211_local *local,
285 + bool enabled);
286 +void ieee80211_led_names(struct ieee80211_local *local);
287 +void ieee80211_led_init(struct ieee80211_local *local);
288 +void ieee80211_led_exit(struct ieee80211_local *local);
289 +void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
290 +void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
291 #else
292 static inline void ieee80211_led_rx(struct ieee80211_local *local)
293 {
294 @@ -45,4 +47,28 @@ static inline void ieee80211_led_init(st
295 static inline void ieee80211_led_exit(struct ieee80211_local *local)
296 {
297 }
298 +static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
299 +{
300 +}
301 +static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
302 +{
303 +}
304 +#endif
305 +
306 +static inline void
307 +ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes)
308 +{
309 +#ifdef CONFIG_MAC80211_LEDS
310 + if (local->tpt_led_trigger && ieee80211_is_data(fc))
311 + local->tpt_led_trigger->tx_bytes += bytes;
312 #endif
313 +}
314 +
315 +static inline void
316 +ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes)
317 +{
318 +#ifdef CONFIG_MAC80211_LEDS
319 + if (local->tpt_led_trigger && ieee80211_is_data(fc))
320 + local->tpt_led_trigger->rx_bytes += bytes;
321 +#endif
322 +}
323 --- a/net/mac80211/iface.c
324 +++ b/net/mac80211/iface.c
325 @@ -225,6 +225,7 @@ static int ieee80211_do_open(struct net_
326 /* we're brought up, everything changes */
327 hw_reconf_flags = ~0;
328 ieee80211_led_radio(local, true);
329 + ieee80211_start_tpt_led_trig(local);
330 }
331
332 /*
333 --- a/net/mac80211/util.c
334 +++ b/net/mac80211/util.c
335 @@ -1141,6 +1141,7 @@ u32 ieee80211_sta_get_rates(struct ieee8
336 void ieee80211_stop_device(struct ieee80211_local *local)
337 {
338 ieee80211_led_radio(local, false);
339 + ieee80211_stop_tpt_led_trig(local);
340
341 cancel_work_sync(&local->reconfig_filter);
342
343 @@ -1175,6 +1176,7 @@ int ieee80211_reconfig(struct ieee80211_
344 }
345
346 ieee80211_led_radio(local, true);
347 + ieee80211_start_tpt_led_trig(local);
348 }
349
350 /* add interfaces */
351 --- a/net/mac80211/rx.c
352 +++ b/net/mac80211/rx.c
353 @@ -2873,6 +2873,9 @@ void ieee80211_rx(struct ieee80211_hw *h
354 return;
355 }
356
357 + ieee80211_tpt_led_trig_rx(local,
358 + ((struct ieee80211_hdr *)skb->data)->frame_control,
359 + skb->len);
360 __ieee80211_rx_handle_packet(hw, skb);
361
362 rcu_read_unlock();
363 --- a/net/mac80211/tx.c
364 +++ b/net/mac80211/tx.c
365 @@ -1292,6 +1292,7 @@ static int __ieee80211_tx(struct ieee802
366
367 while (skb) {
368 int q = skb_get_queue_mapping(skb);
369 + __le16 fc;
370
371 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
372 ret = IEEE80211_TX_OK;
373 @@ -1334,6 +1335,7 @@ static int __ieee80211_tx(struct ieee802
374 else
375 info->control.sta = NULL;
376
377 + fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
378 ret = drv_tx(local, skb);
379 if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
380 dev_kfree_skb(skb);
381 @@ -1344,6 +1346,7 @@ static int __ieee80211_tx(struct ieee802
382 return IEEE80211_TX_AGAIN;
383 }
384
385 + ieee80211_tpt_led_trig_tx(local, fc, len);
386 *skbp = skb = next;
387 ieee80211_led_tx(local, 1);
388 fragm = true;