4 +#include <net80211/ieee80211_node.h>
5 +#include <linux/wprobe.h>
7 +atomic_t cleanup_tasks = ATOMIC_INIT(0);
14 + WP_NODE_RETRANSMIT_200,
15 + WP_NODE_RETRANSMIT_400,
16 + WP_NODE_RETRANSMIT_800,
17 + WP_NODE_RETRANSMIT_1600,
29 +static struct wprobe_item ath_wprobe_globals[] = {
30 + [WP_GLOBAL_NOISE] = {
32 + .type = WPROBE_VAL_S16,
33 + .flags = WPROBE_F_KEEPSTAT
35 + [WP_GLOBAL_PHY_BUSY] = {
37 + .type = WPROBE_VAL_U8,
38 + .flags = WPROBE_F_KEEPSTAT
40 + [WP_GLOBAL_PHY_RX] = {
42 + .type = WPROBE_VAL_U8,
43 + .flags = WPROBE_F_KEEPSTAT
45 + [WP_GLOBAL_PHY_TX] = {
47 + .type = WPROBE_VAL_U8,
48 + .flags = WPROBE_F_KEEPSTAT
50 + [WP_GLOBAL_FRAMES] = {
52 + .type = WPROBE_VAL_U32,
54 + [WP_GLOBAL_PROBEREQ] = {
56 + .type = WPROBE_VAL_U32,
60 +static struct wprobe_item ath_wprobe_link[] = {
63 + .type = WPROBE_VAL_U8,
64 + .flags = WPROBE_F_KEEPSTAT
66 + [WP_NODE_SIGNAL] = {
68 + .type = WPROBE_VAL_S16,
69 + .flags = WPROBE_F_KEEPSTAT
71 + [WP_NODE_RX_RATE] = {
73 + .type = WPROBE_VAL_U16,
74 + .flags = WPROBE_F_KEEPSTAT
76 + [WP_NODE_TX_RATE] = {
78 + .type = WPROBE_VAL_U16,
79 + .flags = WPROBE_F_KEEPSTAT
81 + [WP_NODE_RETRANSMIT_200] = {
82 + .name = "retransmit_200",
83 + .type = WPROBE_VAL_U8,
84 + .flags = WPROBE_F_KEEPSTAT
86 + [WP_NODE_RETRANSMIT_400] = {
87 + .name = "retransmit_400",
88 + .type = WPROBE_VAL_U8,
89 + .flags = WPROBE_F_KEEPSTAT
91 + [WP_NODE_RETRANSMIT_800] = {
92 + .name = "retransmit_800",
93 + .type = WPROBE_VAL_U8,
94 + .flags = WPROBE_F_KEEPSTAT
96 + [WP_NODE_RETRANSMIT_1600] = {
97 + .name = "retransmit_1600",
98 + .type = WPROBE_VAL_U8,
99 + .flags = WPROBE_F_KEEPSTAT
103 +#define AR5K_MIBC 0x0040
104 +#define AR5K_MIBC_FREEZE (1 << 1)
105 +#define AR5K_TXFC 0x80ec
106 +#define AR5K_RXFC 0x80f0
107 +#define AR5K_RXCLEAR 0x80f4
108 +#define AR5K_CYCLES 0x80f8
110 +#define READ_CLR(_ah, _reg) \
111 + ({ u32 __val = OS_REG_READ(_ah, _reg); OS_REG_WRITE(_ah, _reg, 0); __val; })
114 +wprobe_disabled(void)
116 + return (!wprobe_add_iface || IS_ERR(wprobe_add_iface));
120 +ath_wprobe_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure)
122 + struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif);
123 + struct ieee80211vap *vap = &avp->av_vap;
124 + struct ieee80211com *ic = vap->iv_ic;
125 + struct ath_softc *sc = ic->ic_dev->priv;
126 + struct ath_hal *ah = sc->sc_ah;
127 + u32 cc, busy, rx, tx;
133 + OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
134 + cc = READ_CLR(ah, AR5K_CYCLES);
135 + busy = READ_CLR(ah, AR5K_RXCLEAR);
136 + rx = READ_CLR(ah, AR5K_RXFC);
137 + tx = READ_CLR(ah, AR5K_TXFC);
138 + OS_REG_WRITE(ah, AR5K_MIBC, 0);
139 + noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
140 + ic->ic_channoise = noise;
142 + WPROBE_FILL_BEGIN(val, ath_wprobe_globals);
143 + if (cc & 0xf0000000) {
144 + /* scale down if the counters are near max */
150 + if (ah->ah_macType < 5212)
160 + busy = (busy * 100) / cc;
161 + rx = (rx * 100) / cc;
162 + tx = (tx * 100) / cc;
163 + WPROBE_SET(WP_GLOBAL_PHY_BUSY, U8, busy);
164 + WPROBE_SET(WP_GLOBAL_PHY_RX, U8, rx);
165 + WPROBE_SET(WP_GLOBAL_PHY_TX, U8, tx);
166 + WPROBE_SET(WP_GLOBAL_FRAMES, U32, avp->av_rxframes);
167 + WPROBE_SET(WP_GLOBAL_PROBEREQ, U32, avp->av_rxprobereq);
170 + WPROBE_SET(WP_GLOBAL_NOISE, S16, noise);
182 +#undef AR5K_MIBC_FREEZE
185 +static const struct wprobe_iface ath_wprobe_dev = {
186 + .link_items = ath_wprobe_link,
187 + .n_link_items = ARRAY_SIZE(ath_wprobe_link),
188 + .global_items = ath_wprobe_globals,
189 + .n_global_items = ARRAY_SIZE(ath_wprobe_globals),
190 + .sync_data = ath_wprobe_sync,
194 +ath_lookup_rateval(struct ieee80211_node *ni, int rate)
196 + struct ieee80211vap *vap = ni->ni_vap;
197 + struct ieee80211com *ic = vap->iv_ic;
198 + struct ath_softc *sc = ic->ic_dev->priv;
199 + const HAL_RATE_TABLE *rt = sc->sc_currates;
201 + if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap)))
204 + rate = sc->sc_hwmap[rate].ieeerate;
205 + rate = sc->sc_rixmap[rate & IEEE80211_RATE_VAL];
206 + if ((rate < 0) || (rate >= rt->rateCount))
209 + return rt->info[rate].rateKbps;
213 +ath_wprobe_report_rx(struct ieee80211vap *vap, struct ath_rx_status *rs, struct sk_buff *skb)
215 + const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
216 + struct wprobe_wlan_hdr hdr;
217 + struct ath_vap *avp;
220 + if (wprobe_disabled())
223 + avp = ATH_VAP(vap);
224 + avp->av_rxframes++;
225 + if (wh->i_fc[0] == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ))
226 + avp->av_rxprobereq++;
228 + memset(&hdr, 0, sizeof(hdr));
229 + hdr.len = skb->len;
230 + hdr.snr = rs->rs_rssi;
231 + hdr.type = WPROBE_PKT_RX;
232 + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
233 + hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
235 + hdrsize = ieee80211_hdrsize(skb->data);
236 + wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
241 +ath_node_sample_rx(struct ieee80211_node *ni, struct ath_rx_status *rs)
243 + struct ath_node *an = ATH_NODE(ni);
244 + struct ieee80211vap *vap = ni->ni_vap;
245 + struct ieee80211com *ic = vap->iv_ic;
246 + struct wprobe_link *l = &an->an_wplink;
247 + struct wprobe_value *v = l->val;
248 + unsigned long flags;
251 + if (wprobe_disabled() || !an->an_wplink_active || !l->val)
254 + rate = ath_lookup_rateval(ni, rs->rs_rate);
256 + spin_lock_irqsave(&l->iface->lock, flags);
257 + WPROBE_FILL_BEGIN(v, ath_wprobe_link);
258 + WPROBE_SET(WP_NODE_RSSI, U8, rs->rs_rssi);
259 + WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + rs->rs_rssi);
260 + if ((rate > 0) && (rate <= 600000))
261 + WPROBE_SET(WP_NODE_RX_RATE, U16, rate);
263 + wprobe_update_stats(l->iface, l);
264 + spin_unlock_irqrestore(&l->iface->lock, flags);
268 +ath_wprobe_report_tx(struct ieee80211vap *vap, struct ath_tx_status *ts, struct sk_buff *skb)
270 + const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
271 + struct wprobe_wlan_hdr hdr;
272 + struct ath_vap *avp;
275 + if (wprobe_disabled())
278 + avp = ATH_VAP(vap);
280 + memset(&hdr, 0, sizeof(hdr));
281 + hdr.len = skb->len;
282 + hdr.snr = ts->ts_rssi;
283 + hdr.type = WPROBE_PKT_TX;
284 + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
285 + hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
287 + hdrsize = ieee80211_hdrsize(skb->data);
288 + wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
294 +ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, struct sk_buff *skb)
296 + struct ath_node *an = ATH_NODE(ni);
297 + struct ieee80211vap *vap = ni->ni_vap;
298 + struct ieee80211com *ic = vap->iv_ic;
299 + struct wprobe_link *l = &an->an_wplink;
300 + struct wprobe_value *v = l->val;
301 + unsigned long flags;
302 + int rate, rexmit_counter;
303 + int len = skb->len;
305 + if (wprobe_disabled() || !an->an_wplink_active || !l->val)
308 + ath_wprobe_report_tx(vap, ts, skb);
309 + rate = ath_lookup_rateval(ni, ts->ts_rate);
311 + spin_lock_irqsave(&l->iface->lock, flags);
312 + WPROBE_FILL_BEGIN(v, ath_wprobe_link);
313 + WPROBE_SET(WP_NODE_RSSI, U8, ts->ts_rssi);
314 + WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + ts->ts_rssi);
317 + rexmit_counter = WP_NODE_RETRANSMIT_200;
318 + else if (len <= 400)
319 + rexmit_counter = WP_NODE_RETRANSMIT_400;
320 + else if (len <= 800)
321 + rexmit_counter = WP_NODE_RETRANSMIT_800;
323 + rexmit_counter = WP_NODE_RETRANSMIT_1600;
324 + WPROBE_SET(rexmit_counter, U8, ts->ts_longretry);
326 + if ((rate > 0) && (rate <= 600000))
327 + WPROBE_SET(WP_NODE_TX_RATE, U16, rate);
329 + wprobe_update_stats(l->iface, l);
330 + spin_unlock_irqrestore(&l->iface->lock, flags);
334 +ath_wprobe_node_join(struct ieee80211vap *vap, struct ieee80211_node *ni)
336 + struct wprobe_iface *dev;
337 + struct wprobe_link *l;
338 + struct ath_vap *avp;
339 + struct ath_node *an = ATH_NODE(ni);
341 + if (wprobe_disabled() || an->an_wplink_active)
344 + avp = ATH_VAP(vap);
345 + dev = &avp->av_wpif;
346 + l = &an->an_wplink;
348 + ieee80211_ref_node(ni);
349 + wprobe_add_link(dev, l, ni->ni_macaddr);
350 + an->an_wplink_active = 1;
354 +ath_wprobe_do_node_leave(struct work_struct *work)
356 + struct ath_node *an = container_of(work, struct ath_node, an_destroy);
357 + struct ieee80211_node *ni = &an->an_node;
358 + struct ieee80211vap *vap = ni->ni_vap;
359 + struct wprobe_iface *dev;
360 + struct wprobe_link *l;
361 + struct ath_vap *avp;
363 + avp = ATH_VAP(vap);
364 + dev = &avp->av_wpif;
365 + l = &an->an_wplink;
367 + wprobe_remove_link(dev, l);
368 + ieee80211_unref_node(&ni);
369 + atomic_dec(&cleanup_tasks);
373 +ath_wprobe_node_leave(struct ieee80211vap *vap, struct ieee80211_node *ni)
375 + struct ath_node *an = ATH_NODE(ni);
377 + if (wprobe_disabled() || !an->an_wplink_active)
380 + atomic_inc(&cleanup_tasks);
381 + an->an_wplink_active = 0;
382 + IEEE80211_INIT_WORK(&an->an_destroy, ath_wprobe_do_node_leave);
383 + schedule_work(&an->an_destroy);
387 +ath_init_wprobe_dev(struct ath_vap *avp)
389 + struct ieee80211vap *vap = &avp->av_vap;
390 + struct wprobe_iface *dev = &avp->av_wpif;
392 + if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
395 + memcpy(dev, &ath_wprobe_dev, sizeof(struct wprobe_iface));
396 + dev->addr = vap->iv_myaddr;
397 + dev->name = vap->iv_dev->name;
398 + wprobe_add_iface(dev);
402 +ath_remove_wprobe_dev(struct ath_vap *avp)
404 + struct ieee80211vap *vap = &avp->av_vap;
405 + struct ieee80211com *ic = vap->iv_ic;
406 + struct ieee80211_node *ni;
407 + struct wprobe_iface *dev = &avp->av_wpif;
408 + struct wprobe_link *l;
409 + struct ath_node *an;
410 + unsigned long flags;
412 + if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
417 + list_for_each_entry_rcu(l, &dev->links, list) {
418 + an = container_of(l, struct ath_node, an_wplink);
420 + if (!an->an_wplink_active)
424 + ath_wprobe_node_leave(vap, ni);
430 + /* wait for the cleanup tasks to finish */
431 + while (atomic_read(&cleanup_tasks) != 0) {
435 + wprobe_remove_iface(dev);
439 @@ -400,6 +400,7 @@ static int countrycode = -1;
440 static int maxvaps = -1;
441 static int outdoor = -1;
442 static int xchanmode = -1;
443 +#include "ath_wprobe.c"
444 static int beacon_cal = 1;
446 static const struct ath_hw_detect generic_hw_info = {
447 @@ -1525,6 +1526,7 @@ ath_vap_create(struct ieee80211com *ic,
448 ath_hal_intrset(ah, sc->sc_imask);
451 + ath_init_wprobe_dev(avp);
455 @@ -1606,6 +1608,7 @@ ath_vap_delete(struct ieee80211vap *vap)
458 ieee80211_vap_detach(vap);
459 + ath_remove_wprobe_dev(ATH_VAP(vap));
460 /* NB: memory is reclaimed through dev->destructor callback */
463 @@ -5940,6 +5943,7 @@ ath_node_cleanup(struct ieee80211_node *
464 /* Clean up node-specific rate things - this currently appears to
465 * always be a no-op */
466 sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
467 + ath_wprobe_node_leave(ni->ni_vap, ni);
469 ATH_NODE_UAPSD_LOCK_IRQ(an);
470 #ifdef IEEE80211_DEBUG_REFCNT
471 @@ -7010,6 +7014,8 @@ drop_micfail:
472 goto lookup_slowpath;
474 ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
475 + ath_node_sample_rx(ni, rs);
476 + ath_wprobe_report_rx(ni->ni_vap, rs, skb);
477 type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
478 ieee80211_unref_node(&ni);
480 @@ -7020,15 +7026,22 @@ drop_micfail:
483 vap = ieee80211_find_rxvap(ic, wh->i_addr1);
486 + ath_wprobe_report_rx(vap, rs, skb);
487 ni = ieee80211_find_rxnode(ic, vap, wh);
490 + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
491 + ath_wprobe_report_rx(vap, rs, skb);
498 ieee80211_keyix_t keyix;
500 ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
501 + ath_node_sample_rx(ni, rs);
502 type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
504 * If the station has a key cache slot assigned
505 @@ -8608,6 +8621,7 @@ ath_tx_processq(struct ath_softc *sc, st
506 sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
507 ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
509 + ath_node_sample_tx(&an->an_node, ts, bf->bf_skb);
510 if (bf->bf_skb->priority == WME_AC_VO ||
511 bf->bf_skb->priority == WME_AC_VI)
512 ni->ni_ic->ic_wme.wme_hipri_traffic++;
513 @@ -10107,6 +10121,7 @@ ath_newassoc(struct ieee80211_node *ni,
514 struct ath_softc *sc = ic->ic_dev->priv;
516 sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
517 + ath_wprobe_node_join(ni->ni_vap, ni);
519 /* are we supporting compression? */
520 if (!(vap->iv_ath_cap & ni->ni_ath_flags & IEEE80211_NODE_COMP))
521 --- a/ath/if_athvar.h
522 +++ b/ath/if_athvar.h
526 #include "if_athioctl.h"
527 +#include <linux/wprobe.h>
528 #include "net80211/ieee80211.h" /* XXX for WME_NUM_AC */
530 #include <linux/list.h>
531 @@ -352,6 +353,9 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhe
532 /* driver-specific node state */
534 struct ieee80211_node an_node; /* base class */
535 + struct wprobe_link an_wplink;
536 + uint8_t an_wplink_active;
537 + struct work_struct an_destroy;
538 u_int16_t an_decomp_index; /* decompression mask index */
539 u_int32_t an_avgrssi; /* average rssi over all rx frames */
540 u_int8_t an_prevdatarix; /* rate ix of last data frame */
541 @@ -521,6 +525,9 @@ struct ath_vap {
543 unsigned int av_beacon_alloc;
545 + struct wprobe_iface av_wpif;
549 #define ATH_VAP(_v) ((struct ath_vap *)(_v))