551 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			551 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| --- /dev/null
 | |
| +++ b/ath/ath_wprobe.c
 | |
| @@ -0,0 +1,433 @@
 | |
| +#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,
 | |
| +	WP_GLOBAL_FRAMES,
 | |
| +	WP_GLOBAL_PROBEREQ,
 | |
| +};
 | |
| +
 | |
| +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
 | |
| +	},
 | |
| +	[WP_GLOBAL_FRAMES] = {
 | |
| +		.name = "frames",
 | |
| +		.type = WPROBE_VAL_U32,
 | |
| +	},
 | |
| +	[WP_GLOBAL_PROBEREQ] = {
 | |
| +		.name = "probereq",
 | |
| +		.type = WPROBE_VAL_U32,
 | |
| +	},
 | |
| +};
 | |
| +
 | |
| +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);
 | |
| +	WPROBE_SET(WP_GLOBAL_FRAMES, U32, avp->av_rxframes);
 | |
| +	WPROBE_SET(WP_GLOBAL_PROBEREQ, U32, avp->av_rxprobereq);
 | |
| +
 | |
| +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;
 | |
| +}
 | |
| +
 | |
| +static void
 | |
| +ath_wprobe_report_rx(struct ieee80211vap *vap, struct ath_rx_status *rs, struct sk_buff *skb)
 | |
| +{
 | |
| +	const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
 | |
| +	struct wprobe_wlan_hdr hdr;
 | |
| +	struct ath_vap *avp;
 | |
| +	int hdrsize;
 | |
| +
 | |
| +	if (wprobe_disabled())
 | |
| +		return;
 | |
| +
 | |
| +	avp = ATH_VAP(vap);
 | |
| +	avp->av_rxframes++;
 | |
| +	if (wh->i_fc[0] == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ))
 | |
| +		avp->av_rxprobereq++;
 | |
| +
 | |
| +	memset(&hdr, 0, sizeof(hdr));
 | |
| +	hdr.len = skb->len;
 | |
| +	hdr.snr = rs->rs_rssi;
 | |
| +	hdr.type = WPROBE_PKT_RX;
 | |
| +	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
 | |
| +		hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
 | |
| +	else
 | |
| +		hdrsize = ieee80211_hdrsize(skb->data);
 | |
| +	wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
 | |
| +}
 | |
| +
 | |
| +
 | |
| +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_wprobe_report_tx(struct ieee80211vap *vap, struct ath_tx_status *ts, struct sk_buff *skb)
 | |
| +{
 | |
| +	const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
 | |
| +	struct wprobe_wlan_hdr hdr;
 | |
| +	struct ath_vap *avp;
 | |
| +	int hdrsize;
 | |
| +
 | |
| +	if (wprobe_disabled())
 | |
| +		return;
 | |
| +
 | |
| +	avp = ATH_VAP(vap);
 | |
| +
 | |
| +	memset(&hdr, 0, sizeof(hdr));
 | |
| +	hdr.len = skb->len;
 | |
| +	hdr.snr = ts->ts_rssi;
 | |
| +	hdr.type = WPROBE_PKT_TX;
 | |
| +	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
 | |
| +		hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
 | |
| +	else
 | |
| +		hdrsize = ieee80211_hdrsize(skb->data);
 | |
| +	wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
 | |
| +}
 | |
| +
 | |
| +
 | |
| +
 | |
| +static void
 | |
| +ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, struct sk_buff *skb)
 | |
| +{
 | |
| +	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;
 | |
| +	int len = skb->len;
 | |
| +
 | |
| +	if (wprobe_disabled() || !an->an_wplink_active || !l->val)
 | |
| +		return;
 | |
| +
 | |
| +	ath_wprobe_report_tx(vap, ts, skb);
 | |
| +	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
 | |
| +++ b/ath/if_ath.c
 | |
| @@ -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;
 | |
|  }
 | |
|  
 | |
| @@ -1606,6 +1608,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--;
 | |
| @@ -5940,6 +5943,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
 | |
| @@ -7010,6 +7014,8 @@ drop_micfail:
 | |
|  				goto lookup_slowpath;
 | |
|  			}
 | |
|  			ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
 | |
| +			ath_node_sample_rx(ni, rs);
 | |
| +			ath_wprobe_report_rx(ni->ni_vap, rs, skb);
 | |
|  			type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
 | |
|  			ieee80211_unref_node(&ni);
 | |
|  		} else {
 | |
| @@ -7020,15 +7026,22 @@ drop_micfail:
 | |
|  
 | |
|  lookup_slowpath:
 | |
|  			vap = ieee80211_find_rxvap(ic, wh->i_addr1);
 | |
| -			if (vap)
 | |
| +			if (vap) {
 | |
| +				ath_wprobe_report_rx(vap, rs, skb);
 | |
|  				ni = ieee80211_find_rxnode(ic, vap, wh);
 | |
| -			else
 | |
| +			} else {
 | |
| +				TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
 | |
| +					ath_wprobe_report_rx(vap, rs, skb);
 | |
| +				}
 | |
| +				vap = NULL;
 | |
|  				ni = NULL;
 | |
| +			}
 | |
|  
 | |
|  			if (ni != NULL) {
 | |
|  				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
 | |
| @@ -8608,6 +8621,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);
 | |
|  				if (bf->bf_skb->priority == WME_AC_VO ||
 | |
|  				    bf->bf_skb->priority == WME_AC_VI)
 | |
|  					ni->ni_ic->ic_wme.wme_hipri_traffic++;
 | |
| @@ -10107,6 +10121,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
 | |
| +++ b/ath/if_athvar.h
 | |
| @@ -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,9 @@ struct ath_vap {
 | |
|  #else
 | |
|  	unsigned int av_beacon_alloc;
 | |
|  #endif
 | |
| +	struct wprobe_iface av_wpif;
 | |
| +	u32 av_rxframes;
 | |
| +	u32 av_rxprobereq;
 | |
|  };
 | |
|  #define	ATH_VAP(_v)	((struct ath_vap *)(_v))
 | |
|  
 | 
