aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-11-30 02:58:45 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-12-22 14:33:37 -0500
commite1e5406854378dfada3f33c7192b012083a5b8e0 (patch)
treee878058f28b8f6db50ef5d73d09aed66dd9ad9f2
parentfe67c913f1ec2a01aaa9176c80ef36eaf87d705d (diff)
mac80211: add throughput based LED blink trigger
iwlwifi and other drivers like to blink their LED based on throughput. Implement this generically in mac80211, based on a throughput table the driver specifies. That way, drivers can set the blink frequencies depending on their desired behaviour and max throughput. All the drivers need to do is provide an LED class device, best with blink hardware offload. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--include/net/mac80211.h38
-rw-r--r--net/mac80211/ieee80211_i.h13
-rw-r--r--net/mac80211/iface.c1
-rw-r--r--net/mac80211/led.c121
-rw-r--r--net/mac80211/led.h44
-rw-r--r--net/mac80211/rx.c3
-rw-r--r--net/mac80211/tx.c3
-rw-r--r--net/mac80211/util.c2
8 files changed, 216 insertions, 9 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 69ded1ee49ce..40a93d582c79 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1852,11 +1852,26 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
1852 */ 1852 */
1853int ieee80211_register_hw(struct ieee80211_hw *hw); 1853int ieee80211_register_hw(struct ieee80211_hw *hw);
1854 1854
1855/**
1856 * struct ieee80211_tpt_blink - throughput blink description
1857 * @throughput: throughput in Kbit/sec
1858 * @blink_time: blink time in milliseconds
1859 * (full cycle, ie. one off + one on period)
1860 */
1861struct ieee80211_tpt_blink {
1862 int throughput;
1863 int blink_time;
1864};
1865
1855#ifdef CONFIG_MAC80211_LEDS 1866#ifdef CONFIG_MAC80211_LEDS
1856extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); 1867extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
1857extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); 1868extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
1858extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw); 1869extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
1859extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw); 1870extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
1871extern char *__ieee80211_create_tpt_led_trigger(
1872 struct ieee80211_hw *hw,
1873 const struct ieee80211_tpt_blink *blink_table,
1874 unsigned int blink_table_len);
1860#endif 1875#endif
1861/** 1876/**
1862 * ieee80211_get_tx_led_name - get name of TX LED 1877 * ieee80211_get_tx_led_name - get name of TX LED
@@ -1935,6 +1950,29 @@ static inline char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
1935} 1950}
1936 1951
1937/** 1952/**
1953 * ieee80211_create_tpt_led_trigger - create throughput LED trigger
1954 * @hw: the hardware to create the trigger for
1955 * @blink_table: the blink table -- needs to be ordered by throughput
1956 * @blink_table_len: size of the blink table
1957 *
1958 * This function returns %NULL (in case of error, or if no LED
1959 * triggers are configured) or the name of the new trigger.
1960 * This function must be called before ieee80211_register_hw().
1961 */
1962static inline char *
1963ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
1964 const struct ieee80211_tpt_blink *blink_table,
1965 unsigned int blink_table_len)
1966{
1967#ifdef CONFIG_MAC80211_LEDS
1968 return __ieee80211_create_tpt_led_trigger(hw, blink_table,
1969 blink_table_len);
1970#else
1971 return NULL;
1972#endif
1973}
1974
1975/**
1938 * ieee80211_unregister_hw - Unregister a hardware device 1976 * ieee80211_unregister_hw - Unregister a hardware device
1939 * 1977 *
1940 * This function instructs mac80211 to free allocated resources 1978 * This function instructs mac80211 to free allocated resources
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index eadaa243a3da..523b90be8dc5 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -23,6 +23,7 @@
23#include <linux/types.h> 23#include <linux/types.h>
24#include <linux/spinlock.h> 24#include <linux/spinlock.h>
25#include <linux/etherdevice.h> 25#include <linux/etherdevice.h>
26#include <linux/leds.h>
26#include <net/ieee80211_radiotap.h> 27#include <net/ieee80211_radiotap.h>
27#include <net/cfg80211.h> 28#include <net/cfg80211.h>
28#include <net/mac80211.h> 29#include <net/mac80211.h>
@@ -630,6 +631,17 @@ enum queue_stop_reason {
630 IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 631 IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
631}; 632};
632 633
634struct tpt_led_trigger {
635 struct led_trigger trig;
636 char name[32];
637 const struct ieee80211_tpt_blink *blink_table;
638 unsigned int blink_table_len;
639 struct timer_list timer;
640 bool running;
641 unsigned long prev_traffic;
642 unsigned long tx_bytes, rx_bytes;
643};
644
633/** 645/**
634 * mac80211 scan flags - currently active scan mode 646 * mac80211 scan flags - currently active scan mode
635 * 647 *
@@ -838,6 +850,7 @@ struct ieee80211_local {
838#ifdef CONFIG_MAC80211_LEDS 850#ifdef CONFIG_MAC80211_LEDS
839 int tx_led_counter, rx_led_counter; 851 int tx_led_counter, rx_led_counter;
840 struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led; 852 struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
853 struct tpt_led_trigger *tpt_led_trigger;
841 char tx_led_name[32], rx_led_name[32], 854 char tx_led_name[32], rx_led_name[32],
842 assoc_led_name[32], radio_led_name[32]; 855 assoc_led_name[32], radio_led_name[32];
843#endif 856#endif
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index f0f11bb794af..989df7065c21 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -220,6 +220,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
220 /* we're brought up, everything changes */ 220 /* we're brought up, everything changes */
221 hw_reconf_flags = ~0; 221 hw_reconf_flags = ~0;
222 ieee80211_led_radio(local, true); 222 ieee80211_led_radio(local, true);
223 ieee80211_start_tpt_led_trig(local);
223 } 224 }
224 225
225 /* 226 /*
diff --git a/net/mac80211/led.c b/net/mac80211/led.c
index 740a1d4e0a9c..79b13090aed7 100644
--- a/net/mac80211/led.c
+++ b/net/mac80211/led.c
@@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211_local *local)
103 local->radio_led = NULL; 103 local->radio_led = NULL;
104 } 104 }
105 } 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 }
106} 113}
107 114
108void ieee80211_led_exit(struct ieee80211_local *local) 115void ieee80211_led_exit(struct ieee80211_local *local)
@@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211_local *local)
123 led_trigger_unregister(local->rx_led); 130 led_trigger_unregister(local->rx_led);
124 kfree(local->rx_led); 131 kfree(local->rx_led);
125 } 132 }
133
134 if (local->tpt_led_trigger) {
135 led_trigger_unregister(&local->tpt_led_trigger->trig);
136 kfree(local->tpt_led_trigger);
137 }
126} 138}
127 139
128char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw) 140char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
@@ -156,3 +168,112 @@ char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
156 return local->rx_led_name; 168 return local->rx_led_name;
157} 169}
158EXPORT_SYMBOL(__ieee80211_get_rx_led_name); 170EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
171
172static 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
184static 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
218extern char *__ieee80211_create_tpt_led_trigger(
219 struct ieee80211_hw *hw,
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
241 setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
242
243 local->tpt_led_trigger = tpt_trig;
244
245 return tpt_trig->name;
246}
247EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
248
249void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
250{
251 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
252
253 if (!tpt_trig)
254 return;
255
256 /* reset traffic */
257 tpt_trig_traffic(local, tpt_trig);
258 tpt_trig->running = true;
259
260 tpt_trig_timer((unsigned long)local);
261 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
262}
263
264void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
265{
266 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
267 struct led_classdev *led_cdev;
268
269 if (!tpt_trig)
270 return;
271
272 tpt_trig->running = false;
273 del_timer_sync(&tpt_trig->timer);
274
275 read_lock(&tpt_trig->trig.leddev_list_lock);
276 list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
277 led_brightness_set(led_cdev, LED_OFF);
278 read_unlock(&tpt_trig->trig.leddev_list_lock);
279}
diff --git a/net/mac80211/led.h b/net/mac80211/led.h
index 8320cbac61c6..6c215dc0fc96 100644
--- a/net/mac80211/led.h
+++ b/net/mac80211/led.h
@@ -12,15 +12,17 @@
12#include "ieee80211_i.h" 12#include "ieee80211_i.h"
13 13
14#ifdef CONFIG_MAC80211_LEDS 14#ifdef CONFIG_MAC80211_LEDS
15extern void ieee80211_led_rx(struct ieee80211_local *local); 15void ieee80211_led_rx(struct ieee80211_local *local);
16extern void ieee80211_led_tx(struct ieee80211_local *local, int q); 16void ieee80211_led_tx(struct ieee80211_local *local, int q);
17extern void ieee80211_led_assoc(struct ieee80211_local *local, 17void ieee80211_led_assoc(struct ieee80211_local *local,
18 bool associated); 18 bool associated);
19extern void ieee80211_led_radio(struct ieee80211_local *local, 19void ieee80211_led_radio(struct ieee80211_local *local,
20 bool enabled); 20 bool enabled);
21extern void ieee80211_led_names(struct ieee80211_local *local); 21void ieee80211_led_names(struct ieee80211_local *local);
22extern void ieee80211_led_init(struct ieee80211_local *local); 22void ieee80211_led_init(struct ieee80211_local *local);
23extern void ieee80211_led_exit(struct ieee80211_local *local); 23void ieee80211_led_exit(struct ieee80211_local *local);
24void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
25void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
24#else 26#else
25static inline void ieee80211_led_rx(struct ieee80211_local *local) 27static inline void ieee80211_led_rx(struct ieee80211_local *local)
26{ 28{
@@ -45,4 +47,28 @@ static inline void ieee80211_led_init(struct ieee80211_local *local)
45static inline void ieee80211_led_exit(struct ieee80211_local *local) 47static inline void ieee80211_led_exit(struct ieee80211_local *local)
46{ 48{
47} 49}
50static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
51{
52}
53static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
54{
55}
56#endif
57
58static inline void
59ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes)
60{
61#ifdef CONFIG_MAC80211_LEDS
62 if (local->tpt_led_trigger && ieee80211_is_data(fc))
63 local->tpt_led_trigger->tx_bytes += bytes;
48#endif 64#endif
65}
66
67static inline void
68ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes)
69{
70#ifdef CONFIG_MAC80211_LEDS
71 if (local->tpt_led_trigger && ieee80211_is_data(fc))
72 local->tpt_led_trigger->rx_bytes += bytes;
73#endif
74}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 7c5d1b2ec453..01a3f2630eaf 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2928,6 +2928,9 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
2928 return; 2928 return;
2929 } 2929 }
2930 2930
2931 ieee80211_tpt_led_trig_rx(local,
2932 ((struct ieee80211_hdr *)skb->data)->frame_control,
2933 skb->len);
2931 __ieee80211_rx_handle_packet(hw, skb); 2934 __ieee80211_rx_handle_packet(hw, skb);
2932 2935
2933 rcu_read_unlock(); 2936 rcu_read_unlock();
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index d2b4b67a7b53..68c2fbd16ebb 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1297,6 +1297,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,
1297 1297
1298 while (skb) { 1298 while (skb) {
1299 int q = skb_get_queue_mapping(skb); 1299 int q = skb_get_queue_mapping(skb);
1300 __le16 fc;
1300 1301
1301 spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 1302 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
1302 ret = IEEE80211_TX_OK; 1303 ret = IEEE80211_TX_OK;
@@ -1339,6 +1340,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,
1339 else 1340 else
1340 info->control.sta = NULL; 1341 info->control.sta = NULL;
1341 1342
1343 fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
1342 ret = drv_tx(local, skb); 1344 ret = drv_tx(local, skb);
1343 if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { 1345 if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
1344 dev_kfree_skb(skb); 1346 dev_kfree_skb(skb);
@@ -1349,6 +1351,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,
1349 return IEEE80211_TX_AGAIN; 1351 return IEEE80211_TX_AGAIN;
1350 } 1352 }
1351 1353
1354 ieee80211_tpt_led_trig_tx(local, fc, len);
1352 *skbp = skb = next; 1355 *skbp = skb = next;
1353 ieee80211_led_tx(local, 1); 1356 ieee80211_led_tx(local, 1);
1354 fragm = true; 1357 fragm = true;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e497476174ce..48306415a1cb 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1116,6 +1116,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
1116void ieee80211_stop_device(struct ieee80211_local *local) 1116void ieee80211_stop_device(struct ieee80211_local *local)
1117{ 1117{
1118 ieee80211_led_radio(local, false); 1118 ieee80211_led_radio(local, false);
1119 ieee80211_stop_tpt_led_trig(local);
1119 1120
1120 cancel_work_sync(&local->reconfig_filter); 1121 cancel_work_sync(&local->reconfig_filter);
1121 1122
@@ -1150,6 +1151,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1150 } 1151 }
1151 1152
1152 ieee80211_led_radio(local, true); 1153 ieee80211_led_radio(local, true);
1154 ieee80211_start_tpt_led_trig(local);
1153 } 1155 }
1154 1156
1155 /* add interfaces */ 1157 /* add interfaces */