linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2/2] mac80211: add throughput based LED blink trigger
@ 2010-11-27 19:03 Johannes Berg
  2010-11-27 19:20 ` [PATCH 2/2 v2] " Johannes Berg
  0 siblings, 1 reply; 9+ messages in thread
From: Johannes Berg @ 2010-11-27 19:03 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless@vger.kernel.org

From: Johannes Berg <johannes.berg@intel.com>

iwlwifi and other drivers like to blink their LED
based on throughput. Implement this generically in
mac80211, based on a throughput table the driver
determines. 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>
---
 include/net/mac80211.h     |   42 ++++++++++++++++
 net/mac80211/ieee80211_i.h |   14 +++++
 net/mac80211/iface.c       |    1 
 net/mac80211/led.c         |  114 +++++++++++++++++++++++++++++++++++++++++++++
 net/mac80211/led.h         |   44 +++++++++++++----
 net/mac80211/rx.c          |    1 
 net/mac80211/tx.c          |    1 
 net/mac80211/util.c        |    2 
 8 files changed, 210 insertions(+), 9 deletions(-)

--- wireless-testing.orig/include/net/mac80211.h	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/include/net/mac80211.h	2010-11-27 19:34:54.000000000 +0100
@@ -1852,11 +1852,27 @@ struct ieee80211_hw *ieee80211_alloc_hw(
  */
 int ieee80211_register_hw(struct ieee80211_hw *hw);
 
+/**
+ * struct ieee80211_tpt_blink - throughput blink description
+ * @throughput: throughput in Kbit/sec
+ * @blink_time: blink time in milliseconds
+ *	(full cycle, ie. one off + one on period)
+ */
+struct ieee80211_tpt_blink {
+	int throughput;
+	int blink_time;
+};
+
 #ifdef CONFIG_MAC80211_LEDS
 extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				unsigned long update_timeout,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len);
 #endif
 /**
  * ieee80211_get_tx_led_name - get name of TX LED
@@ -1932,6 +1948,32 @@ static inline char *ieee80211_get_radio_
 #else
 	return NULL;
 #endif
+}
+
+/**
+ * ieee80211_create_tpt_led_trigger - create throughput LED trigger
+ * @hw: the hardware to create the trigger for
+ * @update_timeout: the update timeout (in jiffies)
+ * @blink_table: the blink table -- needs to be ordered by throughput
+ * @blink_table_len: size of the blink table
+ *
+ * This function returns %NULL (in case of error, or if no LED
+ * triggers are configured) or the name of the new trigger.
+ * This function must be called before ieee80211_register_hw().
+ */
+static inline char *
+ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+				 unsigned long update_timeout,
+				 const struct ieee80211_tpt_blink *blink_table,
+				 unsigned int blink_table_len)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	return __ieee80211_create_tpt_led_trigger(hw, update_timeout,
+						  blink_table,
+						  blink_table_len);
+#else
+	return NULL;
+#endif
 }
 
 /**
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2010-11-27 19:34:54.000000000 +0100
@@ -23,6 +23,7 @@
 #include <linux/types.h>
 #include <linux/spinlock.h>
 #include <linux/etherdevice.h>
+#include <linux/leds.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/cfg80211.h>
 #include <net/mac80211.h>
@@ -636,6 +637,18 @@ enum queue_stop_reason {
 	IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
 };
 
+struct tpt_led_trigger {
+	struct led_trigger trig;
+	char name[32];
+	const struct ieee80211_tpt_blink *blink_table;
+	unsigned int blink_table_len;
+	unsigned long update_timeout;
+	struct timer_list timer;
+	bool running;
+	unsigned long prev_traffic;
+	unsigned long tx_bytes, rx_bytes;
+};
+
 /**
  * mac80211 scan flags - currently active scan mode
  *
@@ -844,6 +857,7 @@ struct ieee80211_local {
 #ifdef CONFIG_MAC80211_LEDS
 	int tx_led_counter, rx_led_counter;
 	struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
+	struct tpt_led_trigger *tpt_led_trigger;
 	char tx_led_name[32], rx_led_name[32],
 	     assoc_led_name[32], radio_led_name[32];
 #endif
--- wireless-testing.orig/net/mac80211/led.c	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/net/mac80211/led.c	2010-11-27 19:52:29.000000000 +0100
@@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211
 			local->radio_led = NULL;
 		}
 	}
+
+	if (local->tpt_led_trigger) {
+		if (led_trigger_register(&local->tpt_led_trigger->trig)) {
+			kfree(local->tpt_led_trigger);
+			local->tpt_led_trigger = NULL;
+		}
+	}
 }
 
 void ieee80211_led_exit(struct ieee80211_local *local)
@@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211
 		led_trigger_unregister(local->rx_led);
 		kfree(local->rx_led);
 	}
+
+	if (local->tpt_led_trigger) {
+		led_trigger_unregister(&local->tpt_led_trigger->trig);
+		kfree(local->tpt_led_trigger);
+	}
 }
 
 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
@@ -156,3 +168,105 @@ char *__ieee80211_get_rx_led_name(struct
 	return local->rx_led_name;
 }
 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
+
+static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
+				      struct tpt_led_trigger *tpt_trig)
+{
+	unsigned long traffic, delta;
+
+	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
+
+	delta = traffic - tpt_trig->prev_traffic;
+	tpt_trig->prev_traffic = traffic;
+	return delta / (1024 / 8);
+}
+
+static void tpt_trig_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *)data;
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+	struct led_classdev *led_cdev;
+	unsigned long on, off, tpt;
+	int i;
+
+	if (!tpt_trig->running)
+		return;
+
+	mod_timer(&tpt_trig->timer, jiffies + tpt_trig->update_timeout);
+
+	tpt = tpt_trig_traffic(local, tpt_trig);
+
+	/* default to just solid on */
+	on = 1;
+	off = 0;
+
+	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
+		if (tpt >= tpt_trig->blink_table[i].throughput) {
+			off = tpt_trig->blink_table[i].blink_time / 2;
+			on = tpt_trig->blink_table[i].blink_time - off;
+			break;
+		}
+	}
+
+	read_lock(&tpt_trig->trig.leddev_list_lock);
+	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+		led_blink_set(led_cdev, &on, &off);
+	read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
+
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				unsigned long update_timeout,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct tpt_led_trigger *tpt_trig;
+
+	if (WARN_ON(local->tpt_led_trigger))
+		return NULL;
+
+	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
+	if (!tpt_trig)
+		return NULL;
+
+	snprintf(tpt_trig->name, sizeof(tpt_trig->name),
+		 "%stpt", wiphy_name(local->hw.wiphy));
+
+	tpt_trig->trig.name = tpt_trig->name;
+
+	tpt_trig->update_timeout = update_timeout;
+	tpt_trig->blink_table = blink_table;
+	tpt_trig->blink_table_len = blink_table_len;
+
+	setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
+
+	local->tpt_led_trigger = tpt_trig;
+
+	return tpt_trig->name;
+}
+EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
+
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+
+	if (!tpt_trig)
+		return;
+
+	/* reset traffic */
+	tpt_trig_traffic(local, tpt_trig);
+	tpt_trig->running = true;
+	mod_timer(&tpt_trig->timer, jiffies + tpt_trig->update_timeout);
+}
+
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+
+	if (!tpt_trig)
+		return;
+
+	tpt_trig->running = false;
+	del_timer_sync(&tpt_trig->timer);
+}
--- wireless-testing.orig/net/mac80211/led.h	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/net/mac80211/led.h	2010-11-27 19:34:54.000000000 +0100
@@ -12,15 +12,17 @@
 #include "ieee80211_i.h"
 
 #ifdef CONFIG_MAC80211_LEDS
-extern void ieee80211_led_rx(struct ieee80211_local *local);
-extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
-extern void ieee80211_led_assoc(struct ieee80211_local *local,
-				bool associated);
-extern void ieee80211_led_radio(struct ieee80211_local *local,
-				bool enabled);
-extern void ieee80211_led_names(struct ieee80211_local *local);
-extern void ieee80211_led_init(struct ieee80211_local *local);
-extern void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_led_rx(struct ieee80211_local *local);
+void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_assoc(struct ieee80211_local *local,
+			 bool associated);
+void ieee80211_led_radio(struct ieee80211_local *local,
+			 bool enabled);
+void ieee80211_led_names(struct ieee80211_local *local);
+void ieee80211_led_init(struct ieee80211_local *local);
+void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
 #else
 static inline void ieee80211_led_rx(struct ieee80211_local *local)
 {
@@ -45,4 +47,28 @@ static inline void ieee80211_led_init(st
 static inline void ieee80211_led_exit(struct ieee80211_local *local)
 {
 }
+static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+#endif
+
+static inline void
+ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger)
+		local->tpt_led_trigger->tx_bytes += bytes;
 #endif
+}
+
+static inline void
+ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger)
+		local->tpt_led_trigger->rx_bytes += bytes;
+#endif
+}
--- wireless-testing.orig/net/mac80211/iface.c	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/net/mac80211/iface.c	2010-11-27 19:34:54.000000000 +0100
@@ -225,6 +225,7 @@ static int ieee80211_do_open(struct net_
 		/* we're brought up, everything changes */
 		hw_reconf_flags = ~0;
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/*
--- wireless-testing.orig/net/mac80211/util.c	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/net/mac80211/util.c	2010-11-27 19:34:54.000000000 +0100
@@ -1116,6 +1116,7 @@ u32 ieee80211_sta_get_rates(struct ieee8
 void ieee80211_stop_device(struct ieee80211_local *local)
 {
 	ieee80211_led_radio(local, false);
+	ieee80211_stop_tpt_led_trig(local);
 
 	cancel_work_sync(&local->reconfig_filter);
 
@@ -1150,6 +1151,7 @@ int ieee80211_reconfig(struct ieee80211_
 		}
 
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/* add interfaces */
--- wireless-testing.orig/net/mac80211/rx.c	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/net/mac80211/rx.c	2010-11-27 19:34:54.000000000 +0100
@@ -2873,6 +2873,7 @@ void ieee80211_rx(struct ieee80211_hw *h
 		return;
 	}
 
+	ieee80211_tpt_led_trig_rx(local, skb->len);
 	__ieee80211_rx_handle_packet(hw, skb);
 
 	rcu_read_unlock();
--- wireless-testing.orig/net/mac80211/tx.c	2010-11-27 19:32:27.000000000 +0100
+++ wireless-testing/net/mac80211/tx.c	2010-11-27 19:34:54.000000000 +0100
@@ -1344,6 +1344,7 @@ static int __ieee80211_tx(struct ieee802
 			return IEEE80211_TX_AGAIN;
 		}
 
+		ieee80211_tpt_led_trig_tx(local, len);
 		*skbp = skb = next;
 		ieee80211_led_tx(local, 1);
 		fragm = true;



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/2 v2] mac80211: add throughput based LED blink trigger
  2010-11-27 19:03 [PATCH 2/2] mac80211: add throughput based LED blink trigger Johannes Berg
@ 2010-11-27 19:20 ` Johannes Berg
  2010-11-28 12:26   ` [PATCH 2/2 v3] " Johannes Berg
  0 siblings, 1 reply; 9+ messages in thread
From: Johannes Berg @ 2010-11-27 19:20 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless@vger.kernel.org

From: Johannes Berg <johannes.berg@intel.com>

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>
---
v2: turn off LED when turning off radio

 include/net/mac80211.h     |   42 +++++++++++++++
 net/mac80211/ieee80211_i.h |   14 +++++
 net/mac80211/iface.c       |    1 
 net/mac80211/led.c         |  120 +++++++++++++++++++++++++++++++++++++++++++++
 net/mac80211/led.h         |   44 +++++++++++++---
 net/mac80211/rx.c          |    1 
 net/mac80211/tx.c          |    1 
 net/mac80211/util.c        |    2 
 8 files changed, 216 insertions(+), 9 deletions(-)

--- wireless-testing.orig/include/net/mac80211.h	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/include/net/mac80211.h	2010-11-27 20:15:34.000000000 +0100
@@ -1852,11 +1852,27 @@ struct ieee80211_hw *ieee80211_alloc_hw(
  */
 int ieee80211_register_hw(struct ieee80211_hw *hw);
 
+/**
+ * struct ieee80211_tpt_blink - throughput blink description
+ * @throughput: throughput in Kbit/sec
+ * @blink_time: blink time in milliseconds
+ *	(full cycle, ie. one off + one on period)
+ */
+struct ieee80211_tpt_blink {
+	int throughput;
+	int blink_time;
+};
+
 #ifdef CONFIG_MAC80211_LEDS
 extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				unsigned long update_timeout,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len);
 #endif
 /**
  * ieee80211_get_tx_led_name - get name of TX LED
@@ -1932,6 +1948,32 @@ static inline char *ieee80211_get_radio_
 #else
 	return NULL;
 #endif
+}
+
+/**
+ * ieee80211_create_tpt_led_trigger - create throughput LED trigger
+ * @hw: the hardware to create the trigger for
+ * @update_timeout: the update timeout (in jiffies)
+ * @blink_table: the blink table -- needs to be ordered by throughput
+ * @blink_table_len: size of the blink table
+ *
+ * This function returns %NULL (in case of error, or if no LED
+ * triggers are configured) or the name of the new trigger.
+ * This function must be called before ieee80211_register_hw().
+ */
+static inline char *
+ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+				 unsigned long update_timeout,
+				 const struct ieee80211_tpt_blink *blink_table,
+				 unsigned int blink_table_len)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	return __ieee80211_create_tpt_led_trigger(hw, update_timeout,
+						  blink_table,
+						  blink_table_len);
+#else
+	return NULL;
+#endif
 }
 
 /**
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2010-11-27 20:15:34.000000000 +0100
@@ -23,6 +23,7 @@
 #include <linux/types.h>
 #include <linux/spinlock.h>
 #include <linux/etherdevice.h>
+#include <linux/leds.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/cfg80211.h>
 #include <net/mac80211.h>
@@ -636,6 +637,18 @@ enum queue_stop_reason {
 	IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
 };
 
+struct tpt_led_trigger {
+	struct led_trigger trig;
+	char name[32];
+	const struct ieee80211_tpt_blink *blink_table;
+	unsigned int blink_table_len;
+	unsigned long update_timeout;
+	struct timer_list timer;
+	bool running;
+	unsigned long prev_traffic;
+	unsigned long tx_bytes, rx_bytes;
+};
+
 /**
  * mac80211 scan flags - currently active scan mode
  *
@@ -844,6 +857,7 @@ struct ieee80211_local {
 #ifdef CONFIG_MAC80211_LEDS
 	int tx_led_counter, rx_led_counter;
 	struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
+	struct tpt_led_trigger *tpt_led_trigger;
 	char tx_led_name[32], rx_led_name[32],
 	     assoc_led_name[32], radio_led_name[32];
 #endif
--- wireless-testing.orig/net/mac80211/led.c	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/net/mac80211/led.c	2010-11-27 20:19:22.000000000 +0100
@@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211
 			local->radio_led = NULL;
 		}
 	}
+
+	if (local->tpt_led_trigger) {
+		if (led_trigger_register(&local->tpt_led_trigger->trig)) {
+			kfree(local->tpt_led_trigger);
+			local->tpt_led_trigger = NULL;
+		}
+	}
 }
 
 void ieee80211_led_exit(struct ieee80211_local *local)
@@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211
 		led_trigger_unregister(local->rx_led);
 		kfree(local->rx_led);
 	}
+
+	if (local->tpt_led_trigger) {
+		led_trigger_unregister(&local->tpt_led_trigger->trig);
+		kfree(local->tpt_led_trigger);
+	}
 }
 
 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
@@ -156,3 +168,111 @@ char *__ieee80211_get_rx_led_name(struct
 	return local->rx_led_name;
 }
 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
+
+static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
+				      struct tpt_led_trigger *tpt_trig)
+{
+	unsigned long traffic, delta;
+
+	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
+
+	delta = traffic - tpt_trig->prev_traffic;
+	tpt_trig->prev_traffic = traffic;
+	return delta / (1024 / 8);
+}
+
+static void tpt_trig_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *)data;
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+	struct led_classdev *led_cdev;
+	unsigned long on, off, tpt;
+	int i;
+
+	if (!tpt_trig->running)
+		return;
+
+	mod_timer(&tpt_trig->timer, jiffies + tpt_trig->update_timeout);
+
+	tpt = tpt_trig_traffic(local, tpt_trig);
+
+	/* default to just solid on */
+	on = 1;
+	off = 0;
+
+	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
+		if (tpt >= tpt_trig->blink_table[i].throughput) {
+			off = tpt_trig->blink_table[i].blink_time / 2;
+			on = tpt_trig->blink_table[i].blink_time - off;
+			break;
+		}
+	}
+
+	read_lock(&tpt_trig->trig.leddev_list_lock);
+	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+		led_blink_set(led_cdev, &on, &off);
+	read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
+
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				unsigned long update_timeout,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct tpt_led_trigger *tpt_trig;
+
+	if (WARN_ON(local->tpt_led_trigger))
+		return NULL;
+
+	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
+	if (!tpt_trig)
+		return NULL;
+
+	snprintf(tpt_trig->name, sizeof(tpt_trig->name),
+		 "%stpt", wiphy_name(local->hw.wiphy));
+
+	tpt_trig->trig.name = tpt_trig->name;
+
+	tpt_trig->update_timeout = update_timeout;
+	tpt_trig->blink_table = blink_table;
+	tpt_trig->blink_table_len = blink_table_len;
+
+	setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
+
+	local->tpt_led_trigger = tpt_trig;
+
+	return tpt_trig->name;
+}
+EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
+
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+
+	if (!tpt_trig)
+		return;
+
+	/* reset traffic */
+	tpt_trig_traffic(local, tpt_trig);
+	tpt_trig->running = true;
+	mod_timer(&tpt_trig->timer, jiffies + tpt_trig->update_timeout);
+}
+
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+	struct led_classdev *led_cdev;
+
+	if (!tpt_trig)
+		return;
+
+	tpt_trig->running = false;
+	del_timer_sync(&tpt_trig->timer);
+
+	read_lock(&tpt_trig->trig.leddev_list_lock);
+	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+		led_brightness_set(led_cdev, LED_OFF);
+	read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
--- wireless-testing.orig/net/mac80211/led.h	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/net/mac80211/led.h	2010-11-27 20:15:34.000000000 +0100
@@ -12,15 +12,17 @@
 #include "ieee80211_i.h"
 
 #ifdef CONFIG_MAC80211_LEDS
-extern void ieee80211_led_rx(struct ieee80211_local *local);
-extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
-extern void ieee80211_led_assoc(struct ieee80211_local *local,
-				bool associated);
-extern void ieee80211_led_radio(struct ieee80211_local *local,
-				bool enabled);
-extern void ieee80211_led_names(struct ieee80211_local *local);
-extern void ieee80211_led_init(struct ieee80211_local *local);
-extern void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_led_rx(struct ieee80211_local *local);
+void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_assoc(struct ieee80211_local *local,
+			 bool associated);
+void ieee80211_led_radio(struct ieee80211_local *local,
+			 bool enabled);
+void ieee80211_led_names(struct ieee80211_local *local);
+void ieee80211_led_init(struct ieee80211_local *local);
+void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
 #else
 static inline void ieee80211_led_rx(struct ieee80211_local *local)
 {
@@ -45,4 +47,28 @@ static inline void ieee80211_led_init(st
 static inline void ieee80211_led_exit(struct ieee80211_local *local)
 {
 }
+static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+#endif
+
+static inline void
+ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger)
+		local->tpt_led_trigger->tx_bytes += bytes;
 #endif
+}
+
+static inline void
+ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger)
+		local->tpt_led_trigger->rx_bytes += bytes;
+#endif
+}
--- wireless-testing.orig/net/mac80211/iface.c	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/net/mac80211/iface.c	2010-11-27 20:15:34.000000000 +0100
@@ -225,6 +225,7 @@ static int ieee80211_do_open(struct net_
 		/* we're brought up, everything changes */
 		hw_reconf_flags = ~0;
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/*
--- wireless-testing.orig/net/mac80211/util.c	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/net/mac80211/util.c	2010-11-27 20:15:34.000000000 +0100
@@ -1116,6 +1116,7 @@ u32 ieee80211_sta_get_rates(struct ieee8
 void ieee80211_stop_device(struct ieee80211_local *local)
 {
 	ieee80211_led_radio(local, false);
+	ieee80211_stop_tpt_led_trig(local);
 
 	cancel_work_sync(&local->reconfig_filter);
 
@@ -1150,6 +1151,7 @@ int ieee80211_reconfig(struct ieee80211_
 		}
 
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/* add interfaces */
--- wireless-testing.orig/net/mac80211/rx.c	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/net/mac80211/rx.c	2010-11-27 20:15:34.000000000 +0100
@@ -2873,6 +2873,7 @@ void ieee80211_rx(struct ieee80211_hw *h
 		return;
 	}
 
+	ieee80211_tpt_led_trig_rx(local, skb->len);
 	__ieee80211_rx_handle_packet(hw, skb);
 
 	rcu_read_unlock();
--- wireless-testing.orig/net/mac80211/tx.c	2010-11-27 20:15:32.000000000 +0100
+++ wireless-testing/net/mac80211/tx.c	2010-11-27 20:15:34.000000000 +0100
@@ -1344,6 +1344,7 @@ static int __ieee80211_tx(struct ieee802
 			return IEEE80211_TX_AGAIN;
 		}
 
+		ieee80211_tpt_led_trig_tx(local, len);
 		*skbp = skb = next;
 		ieee80211_led_tx(local, 1);
 		fragm = true;



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/2 v3] mac80211: add throughput based LED blink trigger
  2010-11-27 19:20 ` [PATCH 2/2 v2] " Johannes Berg
@ 2010-11-28 12:26   ` Johannes Berg
  2010-11-29 16:13     ` Guy, Wey-Yi
  2010-11-30  7:58     ` [PATCH 2/2 v4] " Johannes Berg
  0 siblings, 2 replies; 9+ messages in thread
From: Johannes Berg @ 2010-11-28 12:26 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless@vger.kernel.org

From: Johannes Berg <johannes.berg@intel.com>

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>
---
v2: turn off LED when turning off radio
v3: - use only data frames
    - fix update frequency to 1 second
    - use DIV_ROUND_UP

 include/net/mac80211.h     |   38 ++++++++++++++
 net/mac80211/ieee80211_i.h |   13 ++++
 net/mac80211/iface.c       |    1 
 net/mac80211/led.c         |  119 +++++++++++++++++++++++++++++++++++++++++++++
 net/mac80211/led.h         |   44 +++++++++++++---
 net/mac80211/rx.c          |    3 +
 net/mac80211/tx.c          |    3 +
 net/mac80211/util.c        |    2 
 8 files changed, 214 insertions(+), 9 deletions(-)

--- wireless-testing.orig/include/net/mac80211.h	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/include/net/mac80211.h	2010-11-28 12:44:30.000000000 +0100
@@ -1852,11 +1852,26 @@ struct ieee80211_hw *ieee80211_alloc_hw(
  */
 int ieee80211_register_hw(struct ieee80211_hw *hw);
 
+/**
+ * struct ieee80211_tpt_blink - throughput blink description
+ * @throughput: throughput in Kbit/sec
+ * @blink_time: blink time in milliseconds
+ *	(full cycle, ie. one off + one on period)
+ */
+struct ieee80211_tpt_blink {
+	int throughput;
+	int blink_time;
+};
+
 #ifdef CONFIG_MAC80211_LEDS
 extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len);
 #endif
 /**
  * ieee80211_get_tx_led_name - get name of TX LED
@@ -1932,6 +1947,29 @@ static inline char *ieee80211_get_radio_
 #else
 	return NULL;
 #endif
+}
+
+/**
+ * ieee80211_create_tpt_led_trigger - create throughput LED trigger
+ * @hw: the hardware to create the trigger for
+ * @blink_table: the blink table -- needs to be ordered by throughput
+ * @blink_table_len: size of the blink table
+ *
+ * This function returns %NULL (in case of error, or if no LED
+ * triggers are configured) or the name of the new trigger.
+ * This function must be called before ieee80211_register_hw().
+ */
+static inline char *
+ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+				 const struct ieee80211_tpt_blink *blink_table,
+				 unsigned int blink_table_len)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	return __ieee80211_create_tpt_led_trigger(hw, blink_table,
+						  blink_table_len);
+#else
+	return NULL;
+#endif
 }
 
 /**
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2010-11-28 12:44:30.000000000 +0100
@@ -23,6 +23,7 @@
 #include <linux/types.h>
 #include <linux/spinlock.h>
 #include <linux/etherdevice.h>
+#include <linux/leds.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/cfg80211.h>
 #include <net/mac80211.h>
@@ -636,6 +637,17 @@ enum queue_stop_reason {
 	IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
 };
 
+struct tpt_led_trigger {
+	struct led_trigger trig;
+	char name[32];
+	const struct ieee80211_tpt_blink *blink_table;
+	unsigned int blink_table_len;
+	struct timer_list timer;
+	bool running;
+	unsigned long prev_traffic;
+	unsigned long tx_bytes, rx_bytes;
+};
+
 /**
  * mac80211 scan flags - currently active scan mode
  *
@@ -844,6 +856,7 @@ struct ieee80211_local {
 #ifdef CONFIG_MAC80211_LEDS
 	int tx_led_counter, rx_led_counter;
 	struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
+	struct tpt_led_trigger *tpt_led_trigger;
 	char tx_led_name[32], rx_led_name[32],
 	     assoc_led_name[32], radio_led_name[32];
 #endif
--- wireless-testing.orig/net/mac80211/led.c	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/net/mac80211/led.c	2010-11-28 12:44:30.000000000 +0100
@@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211
 			local->radio_led = NULL;
 		}
 	}
+
+	if (local->tpt_led_trigger) {
+		if (led_trigger_register(&local->tpt_led_trigger->trig)) {
+			kfree(local->tpt_led_trigger);
+			local->tpt_led_trigger = NULL;
+		}
+	}
 }
 
 void ieee80211_led_exit(struct ieee80211_local *local)
@@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211
 		led_trigger_unregister(local->rx_led);
 		kfree(local->rx_led);
 	}
+
+	if (local->tpt_led_trigger) {
+		led_trigger_unregister(&local->tpt_led_trigger->trig);
+		kfree(local->tpt_led_trigger);
+	}
 }
 
 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
@@ -156,3 +168,110 @@ char *__ieee80211_get_rx_led_name(struct
 	return local->rx_led_name;
 }
 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
+
+static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
+				      struct tpt_led_trigger *tpt_trig)
+{
+	unsigned long traffic, delta;
+
+	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
+
+	delta = traffic - tpt_trig->prev_traffic;
+	tpt_trig->prev_traffic = traffic;
+	return DIV_ROUND_UP(delta, 1024 / 8);
+}
+
+static void tpt_trig_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *)data;
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+	struct led_classdev *led_cdev;
+	unsigned long on, off, tpt;
+	int i;
+
+	if (!tpt_trig->running)
+		return;
+
+	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
+
+	tpt = tpt_trig_traffic(local, tpt_trig);
+
+	/* default to just solid on */
+	on = 1;
+	off = 0;
+
+	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
+		if (tpt > tpt_trig->blink_table[i].throughput) {
+			off = tpt_trig->blink_table[i].blink_time / 2;
+			on = tpt_trig->blink_table[i].blink_time - off;
+			break;
+		}
+	}
+
+	read_lock(&tpt_trig->trig.leddev_list_lock);
+	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+		led_blink_set(led_cdev, &on, &off);
+	read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
+
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct tpt_led_trigger *tpt_trig;
+
+	if (WARN_ON(local->tpt_led_trigger))
+		return NULL;
+
+	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
+	if (!tpt_trig)
+		return NULL;
+
+	snprintf(tpt_trig->name, sizeof(tpt_trig->name),
+		 "%stpt", wiphy_name(local->hw.wiphy));
+
+	tpt_trig->trig.name = tpt_trig->name;
+
+	tpt_trig->blink_table = blink_table;
+	tpt_trig->blink_table_len = blink_table_len;
+
+	setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
+
+	local->tpt_led_trigger = tpt_trig;
+
+	return tpt_trig->name;
+}
+EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
+
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+
+	if (!tpt_trig)
+		return;
+
+	/* reset traffic */
+	tpt_trig_traffic(local, tpt_trig);
+	tpt_trig->running = true;
+
+	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
+}
+
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+	struct led_classdev *led_cdev;
+
+	if (!tpt_trig)
+		return;
+
+	tpt_trig->running = false;
+	del_timer_sync(&tpt_trig->timer);
+
+	read_lock(&tpt_trig->trig.leddev_list_lock);
+	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+		led_brightness_set(led_cdev, LED_OFF);
+	read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
--- wireless-testing.orig/net/mac80211/led.h	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/net/mac80211/led.h	2010-11-28 12:44:30.000000000 +0100
@@ -12,15 +12,17 @@
 #include "ieee80211_i.h"
 
 #ifdef CONFIG_MAC80211_LEDS
-extern void ieee80211_led_rx(struct ieee80211_local *local);
-extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
-extern void ieee80211_led_assoc(struct ieee80211_local *local,
-				bool associated);
-extern void ieee80211_led_radio(struct ieee80211_local *local,
-				bool enabled);
-extern void ieee80211_led_names(struct ieee80211_local *local);
-extern void ieee80211_led_init(struct ieee80211_local *local);
-extern void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_led_rx(struct ieee80211_local *local);
+void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_assoc(struct ieee80211_local *local,
+			 bool associated);
+void ieee80211_led_radio(struct ieee80211_local *local,
+			 bool enabled);
+void ieee80211_led_names(struct ieee80211_local *local);
+void ieee80211_led_init(struct ieee80211_local *local);
+void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
 #else
 static inline void ieee80211_led_rx(struct ieee80211_local *local)
 {
@@ -45,4 +47,28 @@ static inline void ieee80211_led_init(st
 static inline void ieee80211_led_exit(struct ieee80211_local *local)
 {
 }
+static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+#endif
+
+static inline void
+ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger && ieee80211_is_data(fc))
+		local->tpt_led_trigger->tx_bytes += bytes;
 #endif
+}
+
+static inline void
+ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger && ieee80211_is_data(fc))
+		local->tpt_led_trigger->rx_bytes += bytes;
+#endif
+}
--- wireless-testing.orig/net/mac80211/iface.c	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/net/mac80211/iface.c	2010-11-28 12:44:30.000000000 +0100
@@ -225,6 +225,7 @@ static int ieee80211_do_open(struct net_
 		/* we're brought up, everything changes */
 		hw_reconf_flags = ~0;
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/*
--- wireless-testing.orig/net/mac80211/util.c	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/net/mac80211/util.c	2010-11-28 12:44:30.000000000 +0100
@@ -1116,6 +1116,7 @@ u32 ieee80211_sta_get_rates(struct ieee8
 void ieee80211_stop_device(struct ieee80211_local *local)
 {
 	ieee80211_led_radio(local, false);
+	ieee80211_stop_tpt_led_trig(local);
 
 	cancel_work_sync(&local->reconfig_filter);
 
@@ -1150,6 +1151,7 @@ int ieee80211_reconfig(struct ieee80211_
 		}
 
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/* add interfaces */
--- wireless-testing.orig/net/mac80211/rx.c	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/net/mac80211/rx.c	2010-11-28 12:44:30.000000000 +0100
@@ -2873,6 +2873,9 @@ void ieee80211_rx(struct ieee80211_hw *h
 		return;
 	}
 
+	ieee80211_tpt_led_trig_rx(local,
+			((struct ieee80211_hdr *)skb->data)->frame_control,
+			skb->len);
 	__ieee80211_rx_handle_packet(hw, skb);
 
 	rcu_read_unlock();
--- wireless-testing.orig/net/mac80211/tx.c	2010-11-28 12:44:29.000000000 +0100
+++ wireless-testing/net/mac80211/tx.c	2010-11-28 12:44:30.000000000 +0100
@@ -1292,6 +1292,7 @@ static int __ieee80211_tx(struct ieee802
 
 	while (skb) {
 		int q = skb_get_queue_mapping(skb);
+		__le16 fc;
 
 		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 		ret = IEEE80211_TX_OK;
@@ -1334,6 +1335,7 @@ static int __ieee80211_tx(struct ieee802
 		else
 			info->control.sta = NULL;
 
+		fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
 		ret = drv_tx(local, skb);
 		if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
 			dev_kfree_skb(skb);
@@ -1344,6 +1346,7 @@ static int __ieee80211_tx(struct ieee802
 			return IEEE80211_TX_AGAIN;
 		}
 
+		ieee80211_tpt_led_trig_tx(local, fc, len);
 		*skbp = skb = next;
 		ieee80211_led_tx(local, 1);
 		fragm = true;



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2 v3] mac80211: add throughput based LED blink trigger
  2010-11-28 12:26   ` [PATCH 2/2 v3] " Johannes Berg
@ 2010-11-29 16:13     ` Guy, Wey-Yi
  2010-11-29 16:50       ` Johannes Berg
  2010-11-30  7:58     ` [PATCH 2/2 v4] " Johannes Berg
  1 sibling, 1 reply; 9+ messages in thread
From: Guy, Wey-Yi @ 2010-11-29 16:13 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John Linville, linux-wireless@vger.kernel.org

Hi Johannes,

On Sun, 2010-11-28 at 04:26 -0800, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
> 
> 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>
> ---
> v2: turn off LED when turning off radio
> v3: - use only data frames
>     - fix update frequency to 1 second
>     - use DIV_ROUND_UP
> 
>  include/net/mac80211.h     |   38 ++++++++++++++
>  net/mac80211/ieee80211_i.h |   13 ++++
>  net/mac80211/iface.c       |    1
>  net/mac80211/led.c         |  119 +++++++++++++++++++++++++++++++++++++++++++++
>  net/mac80211/led.h         |   44 +++++++++++++---
>  net/mac80211/rx.c          |    3 +
>  net/mac80211/tx.c          |    3 +
>  net/mac80211/util.c        |    2
>  8 files changed, 214 insertions(+), 9 deletions(-)
> 
> --- wireless-testing.orig/include/net/mac80211.h        2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/include/net/mac80211.h     2010-11-28 12:44:30.000000000 +0100
> @@ -1852,11 +1852,26 @@ struct ieee80211_hw *ieee80211_alloc_hw(
>   */
>  int ieee80211_register_hw(struct ieee80211_hw *hw);
> 
> +/**
> + * struct ieee80211_tpt_blink - throughput blink description
> + * @throughput: throughput in Kbit/sec
> + * @blink_time: blink time in milliseconds
> + *     (full cycle, ie. one off + one on period)
> + */
> +struct ieee80211_tpt_blink {
> +       int throughput;
> +       int blink_time;
> +};
> +
>  #ifdef CONFIG_MAC80211_LEDS
>  extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
>  extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
>  extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
>  extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
> +extern char *__ieee80211_create_tpt_led_trigger(
> +                               struct ieee80211_hw *hw,
> +                               const struct ieee80211_tpt_blink *blink_table,
> +                               unsigned int blink_table_len);
>  #endif
>  /**
>   * ieee80211_get_tx_led_name - get name of TX LED
> @@ -1932,6 +1947,29 @@ static inline char *ieee80211_get_radio_
>  #else
>         return NULL;
>  #endif
> +}
> +
> +/**
> + * ieee80211_create_tpt_led_trigger - create throughput LED trigger
> + * @hw: the hardware to create the trigger for
> + * @blink_table: the blink table -- needs to be ordered by throughput
> + * @blink_table_len: size of the blink table
> + *
> + * This function returns %NULL (in case of error, or if no LED
> + * triggers are configured) or the name of the new trigger.
> + * This function must be called before ieee80211_register_hw().
> + */
> +static inline char *
> +ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
> +                                const struct ieee80211_tpt_blink *blink_table,
> +                                unsigned int blink_table_len)
> +{
> +#ifdef CONFIG_MAC80211_LEDS
> +       return __ieee80211_create_tpt_led_trigger(hw, blink_table,
> +                                                 blink_table_len);
> +#else
> +       return NULL;
> +#endif
>  }
> 
>  /**
> --- wireless-testing.orig/net/mac80211/ieee80211_i.h    2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/net/mac80211/ieee80211_i.h 2010-11-28 12:44:30.000000000 +0100
> @@ -23,6 +23,7 @@
>  #include <linux/types.h>
>  #include <linux/spinlock.h>
>  #include <linux/etherdevice.h>
> +#include <linux/leds.h>
>  #include <net/ieee80211_radiotap.h>
>  #include <net/cfg80211.h>
>  #include <net/mac80211.h>
> @@ -636,6 +637,17 @@ enum queue_stop_reason {
>         IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
>  };
> 
> +struct tpt_led_trigger {
> +       struct led_trigger trig;
> +       char name[32];
> +       const struct ieee80211_tpt_blink *blink_table;
> +       unsigned int blink_table_len;
> +       struct timer_list timer;
> +       bool running;
> +       unsigned long prev_traffic;
> +       unsigned long tx_bytes, rx_bytes;
> +};
> +
>  /**
>   * mac80211 scan flags - currently active scan mode
>   *
> @@ -844,6 +856,7 @@ struct ieee80211_local {
>  #ifdef CONFIG_MAC80211_LEDS
>         int tx_led_counter, rx_led_counter;
>         struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
> +       struct tpt_led_trigger *tpt_led_trigger;
>         char tx_led_name[32], rx_led_name[32],
>              assoc_led_name[32], radio_led_name[32];
>  #endif
> --- wireless-testing.orig/net/mac80211/led.c    2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/net/mac80211/led.c 2010-11-28 12:44:30.000000000 +0100
> @@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211
>                         local->radio_led = NULL;
>                 }
>         }
> +
> +       if (local->tpt_led_trigger) {
> +               if (led_trigger_register(&local->tpt_led_trigger->trig)) {
> +                       kfree(local->tpt_led_trigger);
> +                       local->tpt_led_trigger = NULL;
> +               }
> +       }
>  }
> 
>  void ieee80211_led_exit(struct ieee80211_local *local)
> @@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211
>                 led_trigger_unregister(local->rx_led);
>                 kfree(local->rx_led);
>         }
> +
> +       if (local->tpt_led_trigger) {
> +               led_trigger_unregister(&local->tpt_led_trigger->trig);
> +               kfree(local->tpt_led_trigger);
> +       }
>  }
> 
>  char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
> @@ -156,3 +168,110 @@ char *__ieee80211_get_rx_led_name(struct
>         return local->rx_led_name;
>  }
>  EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
> +
> +static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
> +                                     struct tpt_led_trigger *tpt_trig)
> +{
> +       unsigned long traffic, delta;
> +
> +       traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
> +
> +       delta = traffic - tpt_trig->prev_traffic;
> +       tpt_trig->prev_traffic = traffic;
> +       return DIV_ROUND_UP(delta, 1024 / 8);
> +}
> +
> +static void tpt_trig_timer(unsigned long data)
> +{
> +       struct ieee80211_local *local = (void *)data;
> +       struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
> +       struct led_classdev *led_cdev;
> +       unsigned long on, off, tpt;
> +       int i;
> +
> +       if (!tpt_trig->running)
> +               return;
> +
> +       mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
> +
> +       tpt = tpt_trig_traffic(local, tpt_trig);
> +
> +       /* default to just solid on */
> +       on = 1;
> +       off = 0;
> +
> +       for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
> +               if (tpt > tpt_trig->blink_table[i].throughput) {
> +                       off = tpt_trig->blink_table[i].blink_time / 2;
> +                       on = tpt_trig->blink_table[i].blink_time - off;
> +                       break;
> +               }
> +       }
> +
> +       read_lock(&tpt_trig->trig.leddev_list_lock);
> +       list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
> +               led_blink_set(led_cdev, &on, &off);
> +       read_unlock(&tpt_trig->trig.leddev_list_lock);
> +}
> +
> +extern char *__ieee80211_create_tpt_led_trigger(
> +                               struct ieee80211_hw *hw,
> +                               const struct ieee80211_tpt_blink *blink_table,
> +                               unsigned int blink_table_len)
> +{
> +       struct ieee80211_local *local = hw_to_local(hw);
> +       struct tpt_led_trigger *tpt_trig;
> +
> +       if (WARN_ON(local->tpt_led_trigger))
> +               return NULL;
> +
> +       tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
> +       if (!tpt_trig)
> +               return NULL;
> +
> +       snprintf(tpt_trig->name, sizeof(tpt_trig->name),
> +                "%stpt", wiphy_name(local->hw.wiphy));
> +
> +       tpt_trig->trig.name = tpt_trig->name;
> +
> +       tpt_trig->blink_table = blink_table;
> +       tpt_trig->blink_table_len = blink_table_len;
> +
> +       setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
> +
> +       local->tpt_led_trigger = tpt_trig;
> +
> +       return tpt_trig->name;
> +}
> +EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
> +
> +void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
> +{
> +       struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
> +
> +       if (!tpt_trig)
> +               return;
> +
> +       /* reset traffic */
> +       tpt_trig_traffic(local, tpt_trig);
> +       tpt_trig->running = true;
> +
> +       mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
> +}
> +
> +void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
> +{
> +       struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
> +       struct led_classdev *led_cdev;
> +
> +       if (!tpt_trig)
> +               return;
> +
> +       tpt_trig->running = false;
> +       del_timer_sync(&tpt_trig->timer);
> +
> +       read_lock(&tpt_trig->trig.leddev_list_lock);
> +       list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
> +               led_brightness_set(led_cdev, LED_OFF);
> +       read_unlock(&tpt_trig->trig.leddev_list_lock);
> +}
> --- wireless-testing.orig/net/mac80211/led.h    2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/net/mac80211/led.h 2010-11-28 12:44:30.000000000 +0100
> @@ -12,15 +12,17 @@
>  #include "ieee80211_i.h"
> 
>  #ifdef CONFIG_MAC80211_LEDS
> -extern void ieee80211_led_rx(struct ieee80211_local *local);
> -extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
> -extern void ieee80211_led_assoc(struct ieee80211_local *local,
> -                               bool associated);
> -extern void ieee80211_led_radio(struct ieee80211_local *local,
> -                               bool enabled);
> -extern void ieee80211_led_names(struct ieee80211_local *local);
> -extern void ieee80211_led_init(struct ieee80211_local *local);
> -extern void ieee80211_led_exit(struct ieee80211_local *local);
> +void ieee80211_led_rx(struct ieee80211_local *local);
> +void ieee80211_led_tx(struct ieee80211_local *local, int q);
> +void ieee80211_led_assoc(struct ieee80211_local *local,
> +                        bool associated);
> +void ieee80211_led_radio(struct ieee80211_local *local,
> +                        bool enabled);
> +void ieee80211_led_names(struct ieee80211_local *local);
> +void ieee80211_led_init(struct ieee80211_local *local);
> +void ieee80211_led_exit(struct ieee80211_local *local);
> +void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
> +void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
>  #else
>  static inline void ieee80211_led_rx(struct ieee80211_local *local)
>  {
> @@ -45,4 +47,28 @@ static inline void ieee80211_led_init(st
>  static inline void ieee80211_led_exit(struct ieee80211_local *local)
>  {
>  }
> +static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
> +{
> +}
> +static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
> +{
> +}
> +#endif
> +
> +static inline void
> +ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes)
> +{
> +#ifdef CONFIG_MAC80211_LEDS
> +       if (local->tpt_led_trigger && ieee80211_is_data(fc))
> +               local->tpt_led_trigger->tx_bytes += bytes;
>  #endif
> +}
> +
> +static inline void
> +ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes)
> +{
> +#ifdef CONFIG_MAC80211_LEDS
> +       if (local->tpt_led_trigger && ieee80211_is_data(fc))
> +               local->tpt_led_trigger->rx_bytes += bytes;
> +#endif
> +}
> --- wireless-testing.orig/net/mac80211/iface.c  2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/net/mac80211/iface.c       2010-11-28 12:44:30.000000000 +0100
> @@ -225,6 +225,7 @@ static int ieee80211_do_open(struct net_
>                 /* we're brought up, everything changes */
>                 hw_reconf_flags = ~0;
>                 ieee80211_led_radio(local, true);
> +               ieee80211_start_tpt_led_trig(local);
>         }
> 
>         /*
> --- wireless-testing.orig/net/mac80211/util.c   2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/net/mac80211/util.c        2010-11-28 12:44:30.000000000 +0100
> @@ -1116,6 +1116,7 @@ u32 ieee80211_sta_get_rates(struct ieee8
>  void ieee80211_stop_device(struct ieee80211_local *local)
>  {
>         ieee80211_led_radio(local, false);
> +       ieee80211_stop_tpt_led_trig(local);
> 
>         cancel_work_sync(&local->reconfig_filter);
> 
> @@ -1150,6 +1151,7 @@ int ieee80211_reconfig(struct ieee80211_
>                 }
> 
>                 ieee80211_led_radio(local, true);
> +               ieee80211_start_tpt_led_trig(local);
>         }
> 
>         /* add interfaces */
> --- wireless-testing.orig/net/mac80211/rx.c     2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/net/mac80211/rx.c  2010-11-28 12:44:30.000000000 +0100
> @@ -2873,6 +2873,9 @@ void ieee80211_rx(struct ieee80211_hw *h
>                 return;
>         }
> 
> +       ieee80211_tpt_led_trig_rx(local,
> +                       ((struct ieee80211_hdr *)skb->data)->frame_control,
> +                       skb->len);
>         __ieee80211_rx_handle_packet(hw, skb);
> 
>         rcu_read_unlock();
> --- wireless-testing.orig/net/mac80211/tx.c     2010-11-28 12:44:29.000000000 +0100
> +++ wireless-testing/net/mac80211/tx.c  2010-11-28 12:44:30.000000000 +0100
> @@ -1292,6 +1292,7 @@ static int __ieee80211_tx(struct ieee802
> 
>         while (skb) {
>                 int q = skb_get_queue_mapping(skb);
> +               __le16 fc;
> 
>                 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
>                 ret = IEEE80211_TX_OK;
> @@ -1334,6 +1335,7 @@ static int __ieee80211_tx(struct ieee802
>                 else
>                         info->control.sta = NULL;
> 
> +               fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
>                 ret = drv_tx(local, skb);
>                 if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
>                         dev_kfree_skb(skb);
> @@ -1344,6 +1346,7 @@ static int __ieee80211_tx(struct ieee802
>                         return IEEE80211_TX_AGAIN;
>                 }
> 
> +               ieee80211_tpt_led_trig_tx(local, fc, len);
>                 *skbp = skb = next;
>                 ieee80211_led_tx(local, 1);
>                 fragm = true;
> 
> 
> --

Do not see how it work for different hardware has different requirement,
for example, in iwlwifi, different device has different blink time
compensation.

Wey



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2 v3] mac80211: add throughput based LED blink trigger
  2010-11-29 16:13     ` Guy, Wey-Yi
@ 2010-11-29 16:50       ` Johannes Berg
  2010-11-29 17:08         ` Guy, Wey-Yi
  0 siblings, 1 reply; 9+ messages in thread
From: Johannes Berg @ 2010-11-29 16:50 UTC (permalink / raw)
  To: Guy, Wey-Yi; +Cc: John Linville, linux-wireless@vger.kernel.org

On Mon, 2010-11-29 at 08:13 -0800, Guy, Wey-Yi wrote:

> Do not see how it work for different hardware has different requirement,
> for example, in iwlwifi, different device has different blink time
> compensation.

Well you haven't seen the iwlwifi patch for it yet :-)

Blink compensation is applied to the values. So if the trigger says
"blink for 167ms on and 167ms off", blink compensation will be applied
to the value 167.

johannes


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2 v3] mac80211: add throughput based LED blink trigger
  2010-11-29 16:50       ` Johannes Berg
@ 2010-11-29 17:08         ` Guy, Wey-Yi
  0 siblings, 0 replies; 9+ messages in thread
From: Guy, Wey-Yi @ 2010-11-29 17:08 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John Linville, linux-wireless@vger.kernel.org

HiJohannes,

On Mon, 2010-11-29 at 08:50 -0800, Johannes Berg wrote:
> On Mon, 2010-11-29 at 08:13 -0800, Guy, Wey-Yi wrote:
> 
> > Do not see how it work for different hardware has different requirement,
> > for example, in iwlwifi, different device has different blink time
> > compensation.
> 
> Well you haven't seen the iwlwifi patch for it yet :-)
> 
> Blink compensation is applied to the values. So if the trigger says
> "blink for 167ms on and 167ms off", blink compensation will be applied
> to the value 167.
> 
fair enough, I will wait :-)

Wey
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/2 v4] mac80211: add throughput based LED blink trigger
  2010-11-28 12:26   ` [PATCH 2/2 v3] " Johannes Berg
  2010-11-29 16:13     ` Guy, Wey-Yi
@ 2010-11-30  7:58     ` Johannes Berg
  2010-11-30 15:30       ` Guy, Wey-Yi
  1 sibling, 1 reply; 9+ messages in thread
From: Johannes Berg @ 2010-11-30  7:58 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless@vger.kernel.org

From: Johannes Berg <johannes.berg@intel.com>

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>
---
v2: turn off LED when turning off radio
v3: - use only data frames
    - fix update frequency to 1 second
    - use DIV_ROUND_UP
v4: - allow using -1 for always blinking
    - run timer function once when enabling

 include/net/mac80211.h     |   38 ++++++++++++++
 net/mac80211/ieee80211_i.h |   13 ++++
 net/mac80211/iface.c       |    1 
 net/mac80211/led.c         |  121 +++++++++++++++++++++++++++++++++++++++++++++
 net/mac80211/led.h         |   44 +++++++++++++---
 net/mac80211/rx.c          |    3 +
 net/mac80211/tx.c          |    3 +
 net/mac80211/util.c        |    2 
 8 files changed, 216 insertions(+), 9 deletions(-)

--- wireless-testing.orig/include/net/mac80211.h	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/include/net/mac80211.h	2010-11-30 08:57:13.000000000 +0100
@@ -1852,11 +1852,26 @@ struct ieee80211_hw *ieee80211_alloc_hw(
  */
 int ieee80211_register_hw(struct ieee80211_hw *hw);
 
+/**
+ * struct ieee80211_tpt_blink - throughput blink description
+ * @throughput: throughput in Kbit/sec
+ * @blink_time: blink time in milliseconds
+ *	(full cycle, ie. one off + one on period)
+ */
+struct ieee80211_tpt_blink {
+	int throughput;
+	int blink_time;
+};
+
 #ifdef CONFIG_MAC80211_LEDS
 extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
 extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len);
 #endif
 /**
  * ieee80211_get_tx_led_name - get name of TX LED
@@ -1932,6 +1947,29 @@ static inline char *ieee80211_get_radio_
 #else
 	return NULL;
 #endif
+}
+
+/**
+ * ieee80211_create_tpt_led_trigger - create throughput LED trigger
+ * @hw: the hardware to create the trigger for
+ * @blink_table: the blink table -- needs to be ordered by throughput
+ * @blink_table_len: size of the blink table
+ *
+ * This function returns %NULL (in case of error, or if no LED
+ * triggers are configured) or the name of the new trigger.
+ * This function must be called before ieee80211_register_hw().
+ */
+static inline char *
+ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+				 const struct ieee80211_tpt_blink *blink_table,
+				 unsigned int blink_table_len)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	return __ieee80211_create_tpt_led_trigger(hw, blink_table,
+						  blink_table_len);
+#else
+	return NULL;
+#endif
 }
 
 /**
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2010-11-30 08:57:13.000000000 +0100
@@ -23,6 +23,7 @@
 #include <linux/types.h>
 #include <linux/spinlock.h>
 #include <linux/etherdevice.h>
+#include <linux/leds.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/cfg80211.h>
 #include <net/mac80211.h>
@@ -641,6 +642,17 @@ enum queue_stop_reason {
 	IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
 };
 
+struct tpt_led_trigger {
+	struct led_trigger trig;
+	char name[32];
+	const struct ieee80211_tpt_blink *blink_table;
+	unsigned int blink_table_len;
+	struct timer_list timer;
+	bool running;
+	unsigned long prev_traffic;
+	unsigned long tx_bytes, rx_bytes;
+};
+
 /**
  * mac80211 scan flags - currently active scan mode
  *
@@ -849,6 +861,7 @@ struct ieee80211_local {
 #ifdef CONFIG_MAC80211_LEDS
 	int tx_led_counter, rx_led_counter;
 	struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
+	struct tpt_led_trigger *tpt_led_trigger;
 	char tx_led_name[32], rx_led_name[32],
 	     assoc_led_name[32], radio_led_name[32];
 #endif
--- wireless-testing.orig/net/mac80211/led.c	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/net/mac80211/led.c	2010-11-30 08:57:13.000000000 +0100
@@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211
 			local->radio_led = NULL;
 		}
 	}
+
+	if (local->tpt_led_trigger) {
+		if (led_trigger_register(&local->tpt_led_trigger->trig)) {
+			kfree(local->tpt_led_trigger);
+			local->tpt_led_trigger = NULL;
+		}
+	}
 }
 
 void ieee80211_led_exit(struct ieee80211_local *local)
@@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211
 		led_trigger_unregister(local->rx_led);
 		kfree(local->rx_led);
 	}
+
+	if (local->tpt_led_trigger) {
+		led_trigger_unregister(&local->tpt_led_trigger->trig);
+		kfree(local->tpt_led_trigger);
+	}
 }
 
 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
@@ -156,3 +168,112 @@ char *__ieee80211_get_rx_led_name(struct
 	return local->rx_led_name;
 }
 EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
+
+static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
+				      struct tpt_led_trigger *tpt_trig)
+{
+	unsigned long traffic, delta;
+
+	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
+
+	delta = traffic - tpt_trig->prev_traffic;
+	tpt_trig->prev_traffic = traffic;
+	return DIV_ROUND_UP(delta, 1024 / 8);
+}
+
+static void tpt_trig_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *)data;
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+	struct led_classdev *led_cdev;
+	unsigned long on, off, tpt;
+	int i;
+
+	if (!tpt_trig->running)
+		return;
+
+	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
+
+	tpt = tpt_trig_traffic(local, tpt_trig);
+
+	/* default to just solid on */
+	on = 1;
+	off = 0;
+
+	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
+		if (tpt_trig->blink_table[i].throughput < 0 ||
+		    tpt > tpt_trig->blink_table[i].throughput) {
+			off = tpt_trig->blink_table[i].blink_time / 2;
+			on = tpt_trig->blink_table[i].blink_time - off;
+			break;
+		}
+	}
+
+	read_lock(&tpt_trig->trig.leddev_list_lock);
+	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+		led_blink_set(led_cdev, &on, &off);
+	read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
+
+extern char *__ieee80211_create_tpt_led_trigger(
+				struct ieee80211_hw *hw,
+				const struct ieee80211_tpt_blink *blink_table,
+				unsigned int blink_table_len)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct tpt_led_trigger *tpt_trig;
+
+	if (WARN_ON(local->tpt_led_trigger))
+		return NULL;
+
+	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
+	if (!tpt_trig)
+		return NULL;
+
+	snprintf(tpt_trig->name, sizeof(tpt_trig->name),
+		 "%stpt", wiphy_name(local->hw.wiphy));
+
+	tpt_trig->trig.name = tpt_trig->name;
+
+	tpt_trig->blink_table = blink_table;
+	tpt_trig->blink_table_len = blink_table_len;
+
+	setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
+
+	local->tpt_led_trigger = tpt_trig;
+
+	return tpt_trig->name;
+}
+EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
+
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+
+	if (!tpt_trig)
+		return;
+
+	/* reset traffic */
+	tpt_trig_traffic(local, tpt_trig);
+	tpt_trig->running = true;
+
+	tpt_trig_timer((unsigned long)local);
+	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
+}
+
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+	struct led_classdev *led_cdev;
+
+	if (!tpt_trig)
+		return;
+
+	tpt_trig->running = false;
+	del_timer_sync(&tpt_trig->timer);
+
+	read_lock(&tpt_trig->trig.leddev_list_lock);
+	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+		led_brightness_set(led_cdev, LED_OFF);
+	read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
--- wireless-testing.orig/net/mac80211/led.h	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/net/mac80211/led.h	2010-11-30 08:57:13.000000000 +0100
@@ -12,15 +12,17 @@
 #include "ieee80211_i.h"
 
 #ifdef CONFIG_MAC80211_LEDS
-extern void ieee80211_led_rx(struct ieee80211_local *local);
-extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
-extern void ieee80211_led_assoc(struct ieee80211_local *local,
-				bool associated);
-extern void ieee80211_led_radio(struct ieee80211_local *local,
-				bool enabled);
-extern void ieee80211_led_names(struct ieee80211_local *local);
-extern void ieee80211_led_init(struct ieee80211_local *local);
-extern void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_led_rx(struct ieee80211_local *local);
+void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_assoc(struct ieee80211_local *local,
+			 bool associated);
+void ieee80211_led_radio(struct ieee80211_local *local,
+			 bool enabled);
+void ieee80211_led_names(struct ieee80211_local *local);
+void ieee80211_led_init(struct ieee80211_local *local);
+void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_start_tpt_led_trig(struct ieee80211_local *local);
+void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local);
 #else
 static inline void ieee80211_led_rx(struct ieee80211_local *local)
 {
@@ -45,4 +47,28 @@ static inline void ieee80211_led_init(st
 static inline void ieee80211_led_exit(struct ieee80211_local *local)
 {
 }
+static inline void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+static inline void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+}
+#endif
+
+static inline void
+ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger && ieee80211_is_data(fc))
+		local->tpt_led_trigger->tx_bytes += bytes;
 #endif
+}
+
+static inline void
+ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+	if (local->tpt_led_trigger && ieee80211_is_data(fc))
+		local->tpt_led_trigger->rx_bytes += bytes;
+#endif
+}
--- wireless-testing.orig/net/mac80211/iface.c	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/net/mac80211/iface.c	2010-11-30 08:57:13.000000000 +0100
@@ -225,6 +225,7 @@ static int ieee80211_do_open(struct net_
 		/* we're brought up, everything changes */
 		hw_reconf_flags = ~0;
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/*
--- wireless-testing.orig/net/mac80211/util.c	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/net/mac80211/util.c	2010-11-30 08:57:13.000000000 +0100
@@ -1116,6 +1116,7 @@ u32 ieee80211_sta_get_rates(struct ieee8
 void ieee80211_stop_device(struct ieee80211_local *local)
 {
 	ieee80211_led_radio(local, false);
+	ieee80211_stop_tpt_led_trig(local);
 
 	cancel_work_sync(&local->reconfig_filter);
 
@@ -1150,6 +1151,7 @@ int ieee80211_reconfig(struct ieee80211_
 		}
 
 		ieee80211_led_radio(local, true);
+		ieee80211_start_tpt_led_trig(local);
 	}
 
 	/* add interfaces */
--- wireless-testing.orig/net/mac80211/rx.c	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/net/mac80211/rx.c	2010-11-30 07:17:54.000000000 +0100
@@ -2873,6 +2873,9 @@ void ieee80211_rx(struct ieee80211_hw *h
 		return;
 	}
 
+	ieee80211_tpt_led_trig_rx(local,
+			((struct ieee80211_hdr *)skb->data)->frame_control,
+			skb->len);
 	__ieee80211_rx_handle_packet(hw, skb);
 
 	rcu_read_unlock();
--- wireless-testing.orig/net/mac80211/tx.c	2010-11-29 11:05:32.000000000 +0100
+++ wireless-testing/net/mac80211/tx.c	2010-11-29 11:05:32.000000000 +0100
@@ -1292,6 +1292,7 @@ static int __ieee80211_tx(struct ieee802
 
 	while (skb) {
 		int q = skb_get_queue_mapping(skb);
+		__le16 fc;
 
 		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 		ret = IEEE80211_TX_OK;
@@ -1334,6 +1335,7 @@ static int __ieee80211_tx(struct ieee802
 		else
 			info->control.sta = NULL;
 
+		fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
 		ret = drv_tx(local, skb);
 		if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
 			dev_kfree_skb(skb);
@@ -1344,6 +1346,7 @@ static int __ieee80211_tx(struct ieee802
 			return IEEE80211_TX_AGAIN;
 		}
 
+		ieee80211_tpt_led_trig_tx(local, fc, len);
 		*skbp = skb = next;
 		ieee80211_led_tx(local, 1);
 		fragm = true;



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2 v4] mac80211: add throughput based LED blink trigger
  2010-11-30  7:58     ` [PATCH 2/2 v4] " Johannes Berg
@ 2010-11-30 15:30       ` Guy, Wey-Yi
  2010-11-30 15:35         ` Johannes Berg
  0 siblings, 1 reply; 9+ messages in thread
From: Guy, Wey-Yi @ 2010-11-30 15:30 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John Linville, linux-wireless@vger.kernel.org

Hi Johannes,

On Mon, 2010-11-29 at 23:58 -0800, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
> 
> 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>
> ---
> v2: turn off LED when turning off radio
> v3: - use only data frames
>     - fix update frequency to 1 second
>     - use DIV_ROUND_UP
> v4: - allow using -1 for always blinking
>     - run timer function once when enabling
> 
>  include/net/mac80211.h     |   38 ++++++++++++++
>  net/mac80211/ieee80211_i.h |   13 ++++
>  net/mac80211/iface.c       |    1
>  net/mac80211/led.c         |  121 +++++++++++++++++++++++++++++++++++++++++++++
>  net/mac80211/led.h         |   44 +++++++++++++---
>  net/mac80211/rx.c          |    3 +
>  net/mac80211/tx.c          |    3 +
>  net/mac80211/util.c        |    2
>  8 files changed, 216 insertions(+), 9 deletions(-)
> 
> --- wireless-testing.orig/include/net/mac80211.h        2010-11-29 11:05:32.000000000 +0100
> +++ wireless-testing/include/net/mac80211.h     2010-11-30 08:57:13.000000000 +0100
> @@ -1852,11 +1852,26 @@ struct ieee80211_hw *ieee80211_alloc_hw(
>   */
>  int ieee80211_register_hw(struct ieee80211_hw *hw);
> 
> +/**
> + * struct ieee80211_tpt_blink - throughput blink description
> + * @throughput: throughput in Kbit/sec
> + * @blink_time: blink time in milliseconds
> + *     (full cycle, ie. one off + one on period)
> + */
> +struct ieee80211_tpt_blink {
> +       int throughput;
> +       int blink_time;
> +};
> +
>  #ifdef CONFIG_MAC80211_LEDS
>  extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
>  extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
>  extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
>  extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
> +extern char *__ieee80211_create_tpt_led_trigger(
> +                               struct ieee80211_hw *hw,
> +                               const struct ieee80211_tpt_blink *blink_table,
> +                               unsigned int blink_table_len);
>  #endif
>  /**
>   * ieee80211_get_tx_led_name - get name of TX LED
> @@ -1932,6 +1947,29 @@ static inline char *ieee80211_get_radio_
>  #else
>         return NULL;
>  #endif
> +}
> +
> +/**
> + * ieee80211_create_tpt_led_trigger - create throughput LED trigger
> + * @hw: the hardware to create the trigger for
> + * @blink_table: the blink table -- needs to be ordered by throughput
> + * @blink_table_len: size of the blink table
> + *
> + * This function returns %NULL (in case of error, or if no LED
> + * triggers are configured) or the name of the new trigger.
> + * This function must be called before ieee80211_register_hw().
> + */
> +static inline char *
> +ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
> +                                const struct ieee80211_tpt_blink *blink_table,
> +                                unsigned int blink_table_len)
> +{
> +#ifdef CONFIG_MAC80211_LEDS
> +       return __ieee80211_create_tpt_led_trigger(hw, blink_table,
> +                                                 blink_table_len);
> +#else
> +       return NULL;
> +#endif
>  }
> 
>  /**
> --- wireless-testing.orig/net/mac80211/ieee80211_i.h    2010-11-29 11:05:32.000000000 +0100
> +++ wireless-testing/net/mac80211/ieee80211_i.h 2010-11-30 08:57:13.000000000 +0100
> @@ -23,6 +23,7 @@
>  #include <linux/types.h>
>  #include <linux/spinlock.h>
>  #include <linux/etherdevice.h>
> +#include <linux/leds.h>
>  #include <net/ieee80211_radiotap.h>
>  #include <net/cfg80211.h>
>  #include <net/mac80211.h>
> @@ -641,6 +642,17 @@ enum queue_stop_reason {
>         IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
>  };
> 
> +struct tpt_led_trigger {
> +       struct led_trigger trig;
> +       char name[32];
> +       const struct ieee80211_tpt_blink *blink_table;
> +       unsigned int blink_table_len;
> +       struct timer_list timer;
> +       bool running;
> +       unsigned long prev_traffic;
> +       unsigned long tx_bytes, rx_bytes;
> +};
> +
>  /**
>   * mac80211 scan flags - currently active scan mode
>   *
> @@ -849,6 +861,7 @@ struct ieee80211_local {
>  #ifdef CONFIG_MAC80211_LEDS
>         int tx_led_counter, rx_led_counter;
>         struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
> +       struct tpt_led_trigger *tpt_led_trigger;
>         char tx_led_name[32], rx_led_name[32],
>              assoc_led_name[32], radio_led_name[32];
>  #endif
> --- wireless-testing.orig/net/mac80211/led.c    2010-11-29 11:05:32.000000000 +0100
> +++ wireless-testing/net/mac80211/led.c 2010-11-30 08:57:13.000000000 +0100
> @@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211
>                         local->radio_led = NULL;
>                 }
>         }
> +
> +       if (local->tpt_led_trigger) {
> +               if (led_trigger_register(&local->tpt_led_trigger->trig)) {
> +                       kfree(local->tpt_led_trigger);
> +                       local->tpt_led_trigger = NULL;
> +               }
> +       }
>  }
> 
>  void ieee80211_led_exit(struct ieee80211_local *local)
> @@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211
>                 led_trigger_unregister(local->rx_led);
>                 kfree(local->rx_led);
>         }
> +
> +       if (local->tpt_led_trigger) {
> +               led_trigger_unregister(&local->tpt_led_trigger->trig);
> +               kfree(local->tpt_led_trigger);
> +       }
>  }
> 
>  char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
> @@ -156,3 +168,112 @@ char *__ieee80211_get_rx_led_name(struct
>         return local->rx_led_name;
>  }
>  EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
> +
> +static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
> +                                     struct tpt_led_trigger *tpt_trig)
> +{
> +       unsigned long traffic, delta;
> +
> +       traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
> +
> +       delta = traffic - tpt_trig->prev_traffic;
> +       tpt_trig->prev_traffic = traffic;
> +       return DIV_ROUND_UP(delta, 1024 / 8);
> +}
> +
> +static void tpt_trig_timer(unsigned long data)
> +{
> +       struct ieee80211_local *local = (void *)data;
> +       struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
> +       struct led_classdev *led_cdev;
> +       unsigned long on, off, tpt;
> +       int i;
> +
> +       if (!tpt_trig->running)
> +               return;
> +
> +       mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
> +
> +       tpt = tpt_trig_traffic(local, tpt_trig);
> +
> +       /* default to just solid on */
> +       on = 1;
> +       off = 0;
> +
> +       for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
> +               if (tpt_trig->blink_table[i].throughput < 0 ||
> +                   tpt > tpt_trig->blink_table[i].throughput) {
> +                       off = tpt_trig->blink_table[i].blink_time / 2;
> +                       on = tpt_trig->blink_table[i].blink_time - off;
> +                       break;
> +               }
> +       }

I know it is the case for iwlwifi today, but is true "on time" == "off
time"?

Wey




^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2 v4] mac80211: add throughput based LED blink trigger
  2010-11-30 15:30       ` Guy, Wey-Yi
@ 2010-11-30 15:35         ` Johannes Berg
  0 siblings, 0 replies; 9+ messages in thread
From: Johannes Berg @ 2010-11-30 15:35 UTC (permalink / raw)
  To: Guy, Wey-Yi; +Cc: John Linville, linux-wireless@vger.kernel.org

On Tue, 2010-11-30 at 07:30 -0800, Guy, Wey-Yi wrote:

> > +       for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
> > +               if (tpt_trig->blink_table[i].throughput < 0 ||
> > +                   tpt > tpt_trig->blink_table[i].throughput) {
> > +                       off = tpt_trig->blink_table[i].blink_time / 2;
> > +                       on = tpt_trig->blink_table[i].blink_time - off;
> > +                       break;
> > +               }
> > +       }
> 
> I know it is the case for iwlwifi today, but is true "on time" == "off
> time"?

Yeah, I discussed that with Felix and we decided that if somebody wants
it different at some point they can change the code themselves :-)

johannes


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2010-11-30 15:35 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-27 19:03 [PATCH 2/2] mac80211: add throughput based LED blink trigger Johannes Berg
2010-11-27 19:20 ` [PATCH 2/2 v2] " Johannes Berg
2010-11-28 12:26   ` [PATCH 2/2 v3] " Johannes Berg
2010-11-29 16:13     ` Guy, Wey-Yi
2010-11-29 16:50       ` Johannes Berg
2010-11-29 17:08         ` Guy, Wey-Yi
2010-11-30  7:58     ` [PATCH 2/2 v4] " Johannes Berg
2010-11-30 15:30       ` Guy, Wey-Yi
2010-11-30 15:35         ` Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).