Commit e1e5406854378dfada3f33c7192b012083a5b8e0

Authored by Johannes Berg
Committed by John W. Linville
1 parent fe67c913f1

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>

Showing 8 changed files with 216 additions and 9 deletions Side-by-side Diff

include/net/mac80211.h
... ... @@ -1852,11 +1852,26 @@
1852 1852 */
1853 1853 int 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 + */
  1861 +struct ieee80211_tpt_blink {
  1862 + int throughput;
  1863 + int blink_time;
  1864 +};
  1865 +
1855 1866 #ifdef CONFIG_MAC80211_LEDS
1856 1867 extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
1857 1868 extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
1858 1869 extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
1859 1870 extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
  1871 +extern 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 1875 #endif
1861 1876 /**
1862 1877 * ieee80211_get_tx_led_name - get name of TX LED
... ... @@ -1929,6 +1944,29 @@
1929 1944 {
1930 1945 #ifdef CONFIG_MAC80211_LEDS
1931 1946 return __ieee80211_get_radio_led_name(hw);
  1947 +#else
  1948 + return NULL;
  1949 +#endif
  1950 +}
  1951 +
  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 + */
  1962 +static inline char *
  1963 +ieee80211_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);
1932 1970 #else
1933 1971 return NULL;
1934 1972 #endif
net/mac80211/ieee80211_i.h
... ... @@ -23,6 +23,7 @@
23 23 #include <linux/types.h>
24 24 #include <linux/spinlock.h>
25 25 #include <linux/etherdevice.h>
  26 +#include <linux/leds.h>
26 27 #include <net/ieee80211_radiotap.h>
27 28 #include <net/cfg80211.h>
28 29 #include <net/mac80211.h>
... ... @@ -630,6 +631,17 @@
630 631 IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
631 632 };
632 633  
  634 +struct 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 646 * mac80211 scan flags - currently active scan mode
635 647 *
... ... @@ -838,6 +850,7 @@
838 850 #ifdef CONFIG_MAC80211_LEDS
839 851 int tx_led_counter, rx_led_counter;
840 852 struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
  853 + struct tpt_led_trigger *tpt_led_trigger;
841 854 char tx_led_name[32], rx_led_name[32],
842 855 assoc_led_name[32], radio_led_name[32];
843 856 #endif
net/mac80211/iface.c
... ... @@ -220,6 +220,7 @@
220 220 /* we're brought up, everything changes */
221 221 hw_reconf_flags = ~0;
222 222 ieee80211_led_radio(local, true);
  223 + ieee80211_start_tpt_led_trig(local);
223 224 }
224 225  
225 226 /*
... ... @@ -103,6 +103,13 @@
103 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  
108 115 void ieee80211_led_exit(struct ieee80211_local *local)
... ... @@ -123,6 +130,11 @@
123 130 led_trigger_unregister(local->rx_led);
124 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  
128 140 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
... ... @@ -156,4 +168,113 @@
156 168 return local->rx_led_name;
157 169 }
158 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 +extern 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 +}
  247 +EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
  248 +
  249 +void 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 +
  264 +void 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 +}
... ... @@ -12,15 +12,17 @@
12 12 #include "ieee80211_i.h"
13 13  
14 14 #ifdef CONFIG_MAC80211_LEDS
15   -extern void ieee80211_led_rx(struct ieee80211_local *local);
16   -extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
17   -extern void ieee80211_led_assoc(struct ieee80211_local *local,
18   - bool associated);
19   -extern void ieee80211_led_radio(struct ieee80211_local *local,
20   - bool enabled);
21   -extern void ieee80211_led_names(struct ieee80211_local *local);
22   -extern void ieee80211_led_init(struct ieee80211_local *local);
23   -extern void ieee80211_led_exit(struct ieee80211_local *local);
  15 +void ieee80211_led_rx(struct ieee80211_local *local);
  16 +void ieee80211_led_tx(struct ieee80211_local *local, int q);
  17 +void ieee80211_led_assoc(struct ieee80211_local *local,
  18 + bool associated);
  19 +void ieee80211_led_radio(struct ieee80211_local *local,
  20 + bool enabled);
  21 +void ieee80211_led_names(struct ieee80211_local *local);
  22 +void ieee80211_led_init(struct ieee80211_local *local);
  23 +void ieee80211_led_exit(struct ieee80211_local *local);
  24 +void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
  25 +void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
24 26 #else
25 27 static inline void ieee80211_led_rx(struct ieee80211_local *local)
26 28 {
27 29  
... ... @@ -45,5 +47,29 @@
45 47 static inline void ieee80211_led_exit(struct ieee80211_local *local)
46 48 {
47 49 }
  50 +static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
  51 +{
  52 +}
  53 +static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
  54 +{
  55 +}
48 56 #endif
  57 +
  58 +static inline void
  59 +ieee80211_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;
  64 +#endif
  65 +}
  66 +
  67 +static inline void
  68 +ieee80211_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 +}
... ... @@ -2928,6 +2928,9 @@
2928 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 2934 __ieee80211_rx_handle_packet(hw, skb);
2932 2935  
2933 2936 rcu_read_unlock();
... ... @@ -1297,6 +1297,7 @@
1297 1297  
1298 1298 while (skb) {
1299 1299 int q = skb_get_queue_mapping(skb);
  1300 + __le16 fc;
1300 1301  
1301 1302 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
1302 1303 ret = IEEE80211_TX_OK;
... ... @@ -1339,6 +1340,7 @@
1339 1340 else
1340 1341 info->control.sta = NULL;
1341 1342  
  1343 + fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
1342 1344 ret = drv_tx(local, skb);
1343 1345 if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
1344 1346 dev_kfree_skb(skb);
... ... @@ -1349,6 +1351,7 @@
1349 1351 return IEEE80211_TX_AGAIN;
1350 1352 }
1351 1353  
  1354 + ieee80211_tpt_led_trig_tx(local, fc, len);
1352 1355 *skbp = skb = next;
1353 1356 ieee80211_led_tx(local, 1);
1354 1357 fragm = true;
... ... @@ -1116,6 +1116,7 @@
1116 1116 void ieee80211_stop_device(struct ieee80211_local *local)
1117 1117 {
1118 1118 ieee80211_led_radio(local, false);
  1119 + ieee80211_stop_tpt_led_trig(local);
1119 1120  
1120 1121 cancel_work_sync(&local->reconfig_filter);
1121 1122  
... ... @@ -1150,6 +1151,7 @@
1150 1151 }
1151 1152  
1152 1153 ieee80211_led_radio(local, true);
  1154 + ieee80211_start_tpt_led_trig(local);
1153 1155 }
1154 1156  
1155 1157 /* add interfaces */