From: nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Date: Thu, 26 Mar 2009 20:56:58 +0000 (+0000)
Subject: implement support for wprobe in madwifi
X-Git-Url: https://git.rohieb.name/openwrt.git/commitdiff_plain/02ababff437b5a656d8565a8eee25b532755dee6

implement support for wprobe in madwifi

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@15051 3c298f89-4303-0410-b956-a3cf2f4a3e73
---

diff --git a/package/madwifi/Makefile b/package/madwifi/Makefile
index 943a13c21..dcff41c2f 100644
--- a/package/madwifi/Makefile
+++ b/package/madwifi/Makefile
@@ -39,6 +39,8 @@ else
   PATCH_DIR=./patches
 endif
 
+PKG_BUILD_DEPENDS:=wprobe
+
 include $(INCLUDE_DIR)/package.mk
 
 ifneq ($(CONFIG_TARGET_atheros),)
@@ -168,6 +170,16 @@ define KernelPackage/madwifi/config
 	source "$(SOURCE)/Config.in"
 endef
 
+MADWIFI_INC = \
+	-I$(PKG_BUILD_DIR) \
+	-I$(PKG_BUILD_DIR)/include \
+	-I$(PKG_BUILD_DIR)/hal \
+	-I$(PKG_BUILD_DIR)/ath \
+	-I$(PKG_BUILD_DIR)/ath_hal \
+	-I$(PKG_BUILD_DIR)/net80211 \
+	-I$(STAGING_DIR)/usr/include \
+	-include $(PKG_BUILD_DIR)/include/compat.h
+
 MAKE_ARGS:= \
 	PATH="$(TARGET_PATH)" \
 	ARCH="$(LINUX_KARCH)" \
@@ -179,6 +191,7 @@ MAKE_ARGS:= \
 	LDOPTS="--no-warn-mismatch " \
 	ATH_RATE="ath_rate/$(RATE_CONTROL)" \
 	DO_MULTI=1 \
+	INCS="$(MADWIFI_INC)" \
 	$(if $(CONFIG_MADWIFI_DEBUG),,DEBUG=) WARNINGS="-Wno-unused"
 
 MAKE_VARS:= \
diff --git a/package/madwifi/extra/wprobe.spatch b/package/madwifi/extra/wprobe.spatch
new file mode 100644
index 000000000..78718aa54
--- /dev/null
+++ b/package/madwifi/extra/wprobe.spatch
@@ -0,0 +1,84 @@
+@@
+@@
+ #include "if_athioctl.h"
++#include "wprobe.h"
+
+@@
+@@
+ struct ath_vap {
+ 	...
++	struct wprobe_iface av_wpif;
+ };
+
+@@
+@@
+ struct ath_node {
+ 	...
++	struct wprobe_link an_wplink;
++	uint8_t an_wplink_active;
++	struct work_struct an_destroy;
+ 	u_int16_t an_decomp_index;
+ 	...
+ };
+
+@@
+@@
+ ath_vap_create(...) {
+ 	<...
++	ath_init_wprobe_dev(avp);
+ 	return vap;
+ 	...>
+ }
+
+@ rule5 @
+expression vap;
+@@
+ ath_vap_delete(...) {
+ 	<...
+ 	ieee80211_vap_detach(vap);
++	ath_remove_wprobe_dev(ATH_VAP(vap));
+ 	...>
+ }
+
+@ rule6 @
+@@
+ static int xchanmode = -1;
++
++#include "ath_wprobe.c"
+
+
+@ rule7 @
+expression sc, ni;
+@@
+ <...
+ 	sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
++	ath_wprobe_node_join(ni->ni_vap, ni);
+ ...>
+
+@ rule8 @
+expression ni;
+expression sc;
+@@
+ <...
+ 	sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
++	ath_wprobe_node_leave(ni->ni_vap, ni);
+ ...>
+
+@ rule9 @
+expression ni;
+expression rs;
+@@
+ <...
+ 	ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
++	ath_node_sample_rx(ni, rs);
+ ...>
+
+@ rule10 @
+expression an;
+expression ts;
+@@
+ <...
+ 	ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi, ts->ts_rssi);
++	ath_node_sample_tx(&an->an_node, ts, bf->bf_skb->len);
+ ...>
+
diff --git a/package/madwifi/patches/416-wprobe.patch b/package/madwifi/patches/416-wprobe.patch
new file mode 100644
index 000000000..5e181e5bc
--- /dev/null
+++ b/package/madwifi/patches/416-wprobe.patch
@@ -0,0 +1,461 @@
+--- /dev/null	2009-03-26 21:01:06.000000000 +0100
++++ ./ath/ath_wprobe.c	2009-03-26 20:58:07.000000000 +0100
+@@ -0,0 +1,364 @@
++#include <net80211/ieee80211_node.h>
++#include <linux/wprobe.h>
++
++atomic_t cleanup_tasks = ATOMIC_INIT(0);
++
++enum wp_node_val {
++	WP_NODE_RSSI,
++	WP_NODE_SIGNAL,
++	WP_NODE_RX_RATE,
++	WP_NODE_TX_RATE,
++	WP_NODE_RETRANSMIT_200,
++	WP_NODE_RETRANSMIT_400,
++	WP_NODE_RETRANSMIT_800,
++	WP_NODE_RETRANSMIT_1600,
++};
++
++enum wp_global_val {
++	WP_GLOBAL_NOISE,
++	WP_GLOBAL_PHY_BUSY,
++	WP_GLOBAL_PHY_RX,
++	WP_GLOBAL_PHY_TX,
++};
++
++static struct wprobe_item ath_wprobe_globals[] = {
++	[WP_GLOBAL_NOISE] = {
++		.name = "noise",
++		.type = WPROBE_VAL_S16,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_GLOBAL_PHY_BUSY] = {
++		.name = "phy_busy",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_GLOBAL_PHY_RX] = {
++		.name = "phy_rx",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_GLOBAL_PHY_TX] = {
++		.name = "phy_tx",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++};
++
++static struct wprobe_item ath_wprobe_link[] = {
++	[WP_NODE_RSSI] = {
++		.name = "rssi",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_NODE_SIGNAL] = {
++		.name = "signal",
++		.type = WPROBE_VAL_S16,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_NODE_RX_RATE] = {
++		.name = "rx_rate",
++		.type = WPROBE_VAL_U16,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_NODE_TX_RATE] = {
++		.name = "tx_rate",
++		.type = WPROBE_VAL_U16,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_NODE_RETRANSMIT_200] = {
++		.name = "retransmit_200",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_NODE_RETRANSMIT_400] = {
++		.name = "retransmit_400",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_NODE_RETRANSMIT_800] = {
++		.name = "retransmit_800",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++	[WP_NODE_RETRANSMIT_1600] = {
++		.name = "retransmit_1600",
++		.type = WPROBE_VAL_U8,
++		.flags = WPROBE_F_KEEPSTAT
++	},
++};
++
++#define AR5K_MIBC       0x0040
++#define AR5K_MIBC_FREEZE	(1 << 1)
++#define AR5K_TXFC       0x80ec
++#define AR5K_RXFC       0x80f0
++#define AR5K_RXCLEAR    0x80f4
++#define AR5K_CYCLES     0x80f8
++
++#define READ_CLR(_ah, _reg) \
++	({ u32 __val = OS_REG_READ(_ah, _reg); OS_REG_WRITE(_ah, _reg, 0); __val; })
++
++static bool
++wprobe_disabled(void)
++{
++	return (!wprobe_add_iface || IS_ERR(wprobe_add_iface));
++}
++
++static int
++ath_wprobe_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure)
++{
++	struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif);
++	struct ieee80211vap *vap = &avp->av_vap;
++	struct ieee80211com *ic = vap->iv_ic;
++	struct ath_softc *sc = ic->ic_dev->priv;
++	struct ath_hal *ah = sc->sc_ah;
++	u32 cc, busy, rx, tx;
++	s16 noise;
++
++	if (l)
++		goto out;
++
++	OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
++	cc = READ_CLR(ah, AR5K_CYCLES);
++	busy = READ_CLR(ah, AR5K_RXCLEAR);
++	rx = READ_CLR(ah, AR5K_RXFC);
++	tx = READ_CLR(ah, AR5K_TXFC);
++	OS_REG_WRITE(ah, AR5K_MIBC, 0);
++	noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
++	ic->ic_channoise = noise;
++
++	WPROBE_FILL_BEGIN(val, ath_wprobe_globals);
++	if (cc & 0xf0000000) {
++		/* scale down if the counters are near max */
++		cc >>= 8;
++		busy >>= 8;
++		rx >>= 8;
++		tx >>= 8;
++	}
++	if (ah->ah_macType < 5212)
++		goto phy_skip;
++	if (!cc)
++		goto phy_skip;
++	if (busy > cc)
++		goto phy_skip;
++	if (rx > cc)
++		goto phy_skip;
++	if (tx > cc)
++		goto phy_skip;
++	busy = (busy * 100) / cc;
++	rx = (rx * 100) / cc;
++	tx = (tx * 100) / cc;
++	WPROBE_SET(WP_GLOBAL_PHY_BUSY, U8, busy);
++	WPROBE_SET(WP_GLOBAL_PHY_RX, U8, rx);
++	WPROBE_SET(WP_GLOBAL_PHY_TX, U8, tx);
++phy_skip:
++	WPROBE_SET(WP_GLOBAL_NOISE, S16, noise);
++	WPROBE_FILL_END();
++
++out:
++	return 0;
++}
++
++#undef AR5K_TXFC
++#undef AR5K_RXFC
++#undef AR5K_RXCLEAR
++#undef AR5K_CYCLES
++#undef AR5K_MIBC
++#undef AR5K_MIBC_FREEZE
++#undef READ_CLR
++
++static const struct wprobe_iface ath_wprobe_dev = {
++	.link_items = ath_wprobe_link,
++	.n_link_items = ARRAY_SIZE(ath_wprobe_link),
++	.global_items = ath_wprobe_globals,
++	.n_global_items = ARRAY_SIZE(ath_wprobe_globals),
++	.sync_data = ath_wprobe_sync,
++};
++
++static int
++ath_lookup_rateval(struct ieee80211_node *ni, int rate)
++{
++	struct ieee80211vap *vap = ni->ni_vap;
++	struct ieee80211com *ic = vap->iv_ic;
++	struct ath_softc *sc = ic->ic_dev->priv;
++	const HAL_RATE_TABLE *rt = sc->sc_currates;
++
++	if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap)))
++		return -1;
++
++	rate = sc->sc_hwmap[rate].ieeerate;
++	rate = sc->sc_rixmap[rate & IEEE80211_RATE_VAL];
++	if ((rate < 0) || (rate >= rt->rateCount))
++		return -1;
++
++	return rt->info[rate].rateKbps / 10;
++}
++
++static void
++ath_node_sample_rx(struct ieee80211_node *ni, struct ath_rx_status *rs)
++{
++	struct ath_node *an = ATH_NODE(ni);
++	struct ieee80211vap *vap = ni->ni_vap;
++	struct ieee80211com *ic = vap->iv_ic;
++	struct wprobe_link *l = &an->an_wplink;
++	struct wprobe_value *v = l->val;
++	unsigned long flags;
++	int rate;
++
++	if (wprobe_disabled() || !an->an_wplink_active || !l->val)
++		return;
++
++	rate = ath_lookup_rateval(ni, rs->rs_rate);
++
++	spin_lock_irqsave(&l->iface->lock, flags);
++	WPROBE_FILL_BEGIN(v, ath_wprobe_link);
++	WPROBE_SET(WP_NODE_RSSI, U8, rs->rs_rssi);
++	WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + rs->rs_rssi);
++	if ((rate > 0) && (rate <= 600000))
++		WPROBE_SET(WP_NODE_RX_RATE, U16, rate);
++	WPROBE_FILL_END();
++	wprobe_update_stats(l->iface, l);
++	spin_unlock_irqrestore(&l->iface->lock, flags);
++}
++
++static void
++ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, int len)
++{
++	struct ath_node *an = ATH_NODE(ni);
++	struct ieee80211vap *vap = ni->ni_vap;
++	struct ieee80211com *ic = vap->iv_ic;
++	struct wprobe_link *l = &an->an_wplink;
++	struct wprobe_value *v = l->val;
++	unsigned long flags;
++	int rate, rexmit_counter;
++
++	if (wprobe_disabled() || !an->an_wplink_active || !l->val)
++		return;
++
++	rate = ath_lookup_rateval(ni, ts->ts_rate);
++
++	spin_lock_irqsave(&l->iface->lock, flags);
++	WPROBE_FILL_BEGIN(v, ath_wprobe_link);
++	WPROBE_SET(WP_NODE_RSSI, U8, ts->ts_rssi);
++	WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + ts->ts_rssi);
++
++	if (len <= 200)
++		rexmit_counter = WP_NODE_RETRANSMIT_200;
++	else if (len <= 400)
++		rexmit_counter = WP_NODE_RETRANSMIT_400;
++	else if (len <= 800)
++		rexmit_counter = WP_NODE_RETRANSMIT_800;
++	else
++		rexmit_counter = WP_NODE_RETRANSMIT_1600;
++	WPROBE_SET(rexmit_counter, U8, ts->ts_longretry);
++
++	if ((rate > 0) && (rate <= 600000))
++		WPROBE_SET(WP_NODE_TX_RATE, U16, rate);
++	WPROBE_FILL_END();
++	wprobe_update_stats(l->iface, l);
++	spin_unlock_irqrestore(&l->iface->lock, flags);
++}
++
++static void
++ath_wprobe_node_join(struct ieee80211vap *vap, struct ieee80211_node *ni)
++{
++	struct wprobe_iface *dev;
++	struct wprobe_link *l;
++	struct ath_vap *avp;
++	struct ath_node *an = ATH_NODE(ni);
++
++	if (wprobe_disabled() || an->an_wplink_active)
++		return;
++
++	avp = ATH_VAP(vap);
++	dev = &avp->av_wpif;
++	l = &an->an_wplink;
++
++	ieee80211_ref_node(ni);
++	wprobe_add_link(dev, l, ni->ni_macaddr);
++	an->an_wplink_active = 1;
++}
++
++static void
++ath_wprobe_do_node_leave(struct work_struct *work)
++{
++	struct ath_node *an = container_of(work, struct ath_node, an_destroy);
++	struct ieee80211_node *ni = &an->an_node;
++	struct ieee80211vap *vap = ni->ni_vap;
++	struct wprobe_iface *dev;
++	struct wprobe_link *l;
++	struct ath_vap *avp;
++
++	avp = ATH_VAP(vap);
++	dev = &avp->av_wpif;
++	l = &an->an_wplink;
++
++	wprobe_remove_link(dev, l);
++	ieee80211_unref_node(&ni);
++	atomic_dec(&cleanup_tasks);
++}
++
++static void
++ath_wprobe_node_leave(struct ieee80211vap *vap, struct ieee80211_node *ni)
++{
++	struct ath_node *an = ATH_NODE(ni);
++
++	if (wprobe_disabled() || !an->an_wplink_active)
++		return;
++
++	atomic_inc(&cleanup_tasks);
++	an->an_wplink_active = 0;
++	IEEE80211_INIT_WORK(&an->an_destroy, ath_wprobe_do_node_leave);
++	schedule_work(&an->an_destroy);
++}
++
++static void
++ath_init_wprobe_dev(struct ath_vap *avp)
++{
++	struct ieee80211vap *vap = &avp->av_vap;
++	struct wprobe_iface *dev = &avp->av_wpif;
++
++	if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
++		return;
++
++	memcpy(dev, &ath_wprobe_dev, sizeof(struct wprobe_iface));
++	dev->addr = vap->iv_myaddr;
++	dev->name = vap->iv_dev->name;
++	wprobe_add_iface(dev);
++}
++
++static void
++ath_remove_wprobe_dev(struct ath_vap *avp)
++{
++	struct ieee80211vap *vap = &avp->av_vap;
++	struct ieee80211com *ic = vap->iv_ic;
++	struct ieee80211_node *ni;
++	struct wprobe_iface *dev = &avp->av_wpif;
++	struct wprobe_link *l;
++	struct ath_node *an;
++	unsigned long flags;
++
++	if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
++		return;
++
++restart:
++	rcu_read_lock();
++	list_for_each_entry_rcu(l, &dev->links, list) {
++		an = container_of(l, struct ath_node, an_wplink);
++
++		if (!an->an_wplink_active)
++			continue;
++
++		ni = &an->an_node;
++		ath_wprobe_node_leave(vap, ni);
++		rcu_read_unlock();
++		goto restart;
++	}
++	rcu_read_unlock();
++
++	/* wait for the cleanup tasks to finish */
++	while (atomic_read(&cleanup_tasks) != 0) {
++		schedule();
++	}
++
++	wprobe_remove_iface(dev);
++}
+--- a/ath/if_ath.c	2009-03-26 19:54:36.000000000 +0100
++++ /var/folders/DB/DBZUyxsHGRKP0B3nCU1mmU+++TI/-Tmp-/cocci-output-73937-18abc0-if_ath.c	2009-03-26 21:08:34.000000000 +0100
+@@ -400,6 +400,7 @@ static int countrycode = -1;
+ static int maxvaps = -1;
+ static int outdoor = -1;
+ static int xchanmode = -1;
++#include "ath_wprobe.c"
+ static int beacon_cal = 1;
+ 
+ static const struct ath_hw_detect generic_hw_info = {
+@@ -1525,6 +1526,7 @@ ath_vap_create(struct ieee80211com *ic, 
+ 		ath_hal_intrset(ah, sc->sc_imask);
+ 	}
+ 
++	ath_init_wprobe_dev(avp);
+ 	return vap;
+ }
+ 
+@@ -1605,6 +1607,7 @@ ath_vap_delete(struct ieee80211vap *vap)
+ 		decrease = 0;
+ 
+ 	ieee80211_vap_detach(vap);
++	ath_remove_wprobe_dev(ATH_VAP(vap));
+ 	/* NB: memory is reclaimed through dev->destructor callback */
+ 	if (decrease)
+ 		sc->sc_nvaps--;
+@@ -5931,6 +5934,7 @@ ath_node_cleanup(struct ieee80211_node *
+ 	/* Clean up node-specific rate things - this currently appears to 
+ 	 * always be a no-op */
+ 	sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
++	ath_wprobe_node_leave(ni->ni_vap, ni);
+ 
+ 	ATH_NODE_UAPSD_LOCK_IRQ(an);
+ #ifdef IEEE80211_DEBUG_REFCNT
+@@ -7001,6 +7005,7 @@ drop_micfail:
+ 				goto lookup_slowpath;
+ 			}
+ 			ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
++			ath_node_sample_rx(ni, rs);
+ 			type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+ 			ieee80211_unref_node(&ni);
+ 		} else {
+@@ -7020,6 +7025,7 @@ lookup_slowpath:
+ 				ieee80211_keyix_t keyix;
+ 
+ 				ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
++				ath_node_sample_rx(ni, rs);
+ 				type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+ 				/*
+ 				 * If the station has a key cache slot assigned
+@@ -8599,6 +8605,7 @@ ath_tx_processq(struct ath_softc *sc, st
+ 				sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
+ 				ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
+ 					ts->ts_rssi);
++					ath_node_sample_tx(&an->an_node, ts, bf->bf_skb->len);
+ 				if (bf->bf_skb->priority == WME_AC_VO ||
+ 				    bf->bf_skb->priority == WME_AC_VI)
+ 					ni->ni_ic->ic_wme.wme_hipri_traffic++;
+@@ -10090,6 +10097,7 @@ ath_newassoc(struct ieee80211_node *ni, 
+ 	struct ath_softc *sc = ic->ic_dev->priv;
+ 
+ 	sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
++	ath_wprobe_node_join(ni->ni_vap, ni);
+ 
+ 	/* are we supporting compression? */
+ 	if (!(vap->iv_ath_cap & ni->ni_ath_flags & IEEE80211_NODE_COMP))
+--- a/ath/if_athvar.h	2009-03-26 19:54:35.000000000 +0100
++++ /var/folders/DB/DBZUyxsHGRKP0B3nCU1mmU+++TI/-Tmp-/cocci-output-73937-80429d-if_athvar.h	2009-03-26 21:08:42.000000000 +0100
+@@ -46,6 +46,7 @@
+ #include "ah_desc.h"
+ #include "ah_os.h"
+ #include "if_athioctl.h"
++#include <linux/wprobe.h>
+ #include "net80211/ieee80211.h"		/* XXX for WME_NUM_AC */
+ #include <asm/io.h>
+ #include <linux/list.h>
+@@ -352,6 +353,9 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhe
+ /* driver-specific node state */
+ struct ath_node {
+ 	struct ieee80211_node an_node;		/* base class */
++	struct wprobe_link an_wplink;
++	uint8_t an_wplink_active;
++	struct work_struct an_destroy;
+ 	u_int16_t an_decomp_index; 		/* decompression mask index */
+ 	u_int32_t an_avgrssi;			/* average rssi over all rx frames */
+ 	u_int8_t  an_prevdatarix;		/* rate ix of last data frame */
+@@ -521,6 +525,7 @@ struct ath_vap {
+ #else
+ 	unsigned int av_beacon_alloc;
+ #endif
++	struct wprobe_iface av_wpif;
+ };
+ #define	ATH_VAP(_v)	((struct ath_vap *)(_v))
+