implement support for wprobe in madwifi
[openwrt.git] / package / madwifi / patches / 416-wprobe.patch
1 --- /dev/null   2009-03-26 21:01:06.000000000 +0100
2 +++ ./ath/ath_wprobe.c  2009-03-26 20:58:07.000000000 +0100
3 @@ -0,0 +1,364 @@
4 +#include <net80211/ieee80211_node.h>
5 +#include <linux/wprobe.h>
6 +
7 +atomic_t cleanup_tasks = ATOMIC_INIT(0);
8 +
9 +enum wp_node_val {
10 +       WP_NODE_RSSI,
11 +       WP_NODE_SIGNAL,
12 +       WP_NODE_RX_RATE,
13 +       WP_NODE_TX_RATE,
14 +       WP_NODE_RETRANSMIT_200,
15 +       WP_NODE_RETRANSMIT_400,
16 +       WP_NODE_RETRANSMIT_800,
17 +       WP_NODE_RETRANSMIT_1600,
18 +};
19 +
20 +enum wp_global_val {
21 +       WP_GLOBAL_NOISE,
22 +       WP_GLOBAL_PHY_BUSY,
23 +       WP_GLOBAL_PHY_RX,
24 +       WP_GLOBAL_PHY_TX,
25 +};
26 +
27 +static struct wprobe_item ath_wprobe_globals[] = {
28 +       [WP_GLOBAL_NOISE] = {
29 +               .name = "noise",
30 +               .type = WPROBE_VAL_S16,
31 +               .flags = WPROBE_F_KEEPSTAT
32 +       },
33 +       [WP_GLOBAL_PHY_BUSY] = {
34 +               .name = "phy_busy",
35 +               .type = WPROBE_VAL_U8,
36 +               .flags = WPROBE_F_KEEPSTAT
37 +       },
38 +       [WP_GLOBAL_PHY_RX] = {
39 +               .name = "phy_rx",
40 +               .type = WPROBE_VAL_U8,
41 +               .flags = WPROBE_F_KEEPSTAT
42 +       },
43 +       [WP_GLOBAL_PHY_TX] = {
44 +               .name = "phy_tx",
45 +               .type = WPROBE_VAL_U8,
46 +               .flags = WPROBE_F_KEEPSTAT
47 +       },
48 +};
49 +
50 +static struct wprobe_item ath_wprobe_link[] = {
51 +       [WP_NODE_RSSI] = {
52 +               .name = "rssi",
53 +               .type = WPROBE_VAL_U8,
54 +               .flags = WPROBE_F_KEEPSTAT
55 +       },
56 +       [WP_NODE_SIGNAL] = {
57 +               .name = "signal",
58 +               .type = WPROBE_VAL_S16,
59 +               .flags = WPROBE_F_KEEPSTAT
60 +       },
61 +       [WP_NODE_RX_RATE] = {
62 +               .name = "rx_rate",
63 +               .type = WPROBE_VAL_U16,
64 +               .flags = WPROBE_F_KEEPSTAT
65 +       },
66 +       [WP_NODE_TX_RATE] = {
67 +               .name = "tx_rate",
68 +               .type = WPROBE_VAL_U16,
69 +               .flags = WPROBE_F_KEEPSTAT
70 +       },
71 +       [WP_NODE_RETRANSMIT_200] = {
72 +               .name = "retransmit_200",
73 +               .type = WPROBE_VAL_U8,
74 +               .flags = WPROBE_F_KEEPSTAT
75 +       },
76 +       [WP_NODE_RETRANSMIT_400] = {
77 +               .name = "retransmit_400",
78 +               .type = WPROBE_VAL_U8,
79 +               .flags = WPROBE_F_KEEPSTAT
80 +       },
81 +       [WP_NODE_RETRANSMIT_800] = {
82 +               .name = "retransmit_800",
83 +               .type = WPROBE_VAL_U8,
84 +               .flags = WPROBE_F_KEEPSTAT
85 +       },
86 +       [WP_NODE_RETRANSMIT_1600] = {
87 +               .name = "retransmit_1600",
88 +               .type = WPROBE_VAL_U8,
89 +               .flags = WPROBE_F_KEEPSTAT
90 +       },
91 +};
92 +
93 +#define AR5K_MIBC       0x0040
94 +#define AR5K_MIBC_FREEZE       (1 << 1)
95 +#define AR5K_TXFC       0x80ec
96 +#define AR5K_RXFC       0x80f0
97 +#define AR5K_RXCLEAR    0x80f4
98 +#define AR5K_CYCLES     0x80f8
99 +
100 +#define READ_CLR(_ah, _reg) \
101 +       ({ u32 __val = OS_REG_READ(_ah, _reg); OS_REG_WRITE(_ah, _reg, 0); __val; })
102 +
103 +static bool
104 +wprobe_disabled(void)
105 +{
106 +       return (!wprobe_add_iface || IS_ERR(wprobe_add_iface));
107 +}
108 +
109 +static int
110 +ath_wprobe_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure)
111 +{
112 +       struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif);
113 +       struct ieee80211vap *vap = &avp->av_vap;
114 +       struct ieee80211com *ic = vap->iv_ic;
115 +       struct ath_softc *sc = ic->ic_dev->priv;
116 +       struct ath_hal *ah = sc->sc_ah;
117 +       u32 cc, busy, rx, tx;
118 +       s16 noise;
119 +
120 +       if (l)
121 +               goto out;
122 +
123 +       OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
124 +       cc = READ_CLR(ah, AR5K_CYCLES);
125 +       busy = READ_CLR(ah, AR5K_RXCLEAR);
126 +       rx = READ_CLR(ah, AR5K_RXFC);
127 +       tx = READ_CLR(ah, AR5K_TXFC);
128 +       OS_REG_WRITE(ah, AR5K_MIBC, 0);
129 +       noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
130 +       ic->ic_channoise = noise;
131 +
132 +       WPROBE_FILL_BEGIN(val, ath_wprobe_globals);
133 +       if (cc & 0xf0000000) {
134 +               /* scale down if the counters are near max */
135 +               cc >>= 8;
136 +               busy >>= 8;
137 +               rx >>= 8;
138 +               tx >>= 8;
139 +       }
140 +       if (ah->ah_macType < 5212)
141 +               goto phy_skip;
142 +       if (!cc)
143 +               goto phy_skip;
144 +       if (busy > cc)
145 +               goto phy_skip;
146 +       if (rx > cc)
147 +               goto phy_skip;
148 +       if (tx > cc)
149 +               goto phy_skip;
150 +       busy = (busy * 100) / cc;
151 +       rx = (rx * 100) / cc;
152 +       tx = (tx * 100) / cc;
153 +       WPROBE_SET(WP_GLOBAL_PHY_BUSY, U8, busy);
154 +       WPROBE_SET(WP_GLOBAL_PHY_RX, U8, rx);
155 +       WPROBE_SET(WP_GLOBAL_PHY_TX, U8, tx);
156 +phy_skip:
157 +       WPROBE_SET(WP_GLOBAL_NOISE, S16, noise);
158 +       WPROBE_FILL_END();
159 +
160 +out:
161 +       return 0;
162 +}
163 +
164 +#undef AR5K_TXFC
165 +#undef AR5K_RXFC
166 +#undef AR5K_RXCLEAR
167 +#undef AR5K_CYCLES
168 +#undef AR5K_MIBC
169 +#undef AR5K_MIBC_FREEZE
170 +#undef READ_CLR
171 +
172 +static const struct wprobe_iface ath_wprobe_dev = {
173 +       .link_items = ath_wprobe_link,
174 +       .n_link_items = ARRAY_SIZE(ath_wprobe_link),
175 +       .global_items = ath_wprobe_globals,
176 +       .n_global_items = ARRAY_SIZE(ath_wprobe_globals),
177 +       .sync_data = ath_wprobe_sync,
178 +};
179 +
180 +static int
181 +ath_lookup_rateval(struct ieee80211_node *ni, int rate)
182 +{
183 +       struct ieee80211vap *vap = ni->ni_vap;
184 +       struct ieee80211com *ic = vap->iv_ic;
185 +       struct ath_softc *sc = ic->ic_dev->priv;
186 +       const HAL_RATE_TABLE *rt = sc->sc_currates;
187 +
188 +       if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap)))
189 +               return -1;
190 +
191 +       rate = sc->sc_hwmap[rate].ieeerate;
192 +       rate = sc->sc_rixmap[rate & IEEE80211_RATE_VAL];
193 +       if ((rate < 0) || (rate >= rt->rateCount))
194 +               return -1;
195 +
196 +       return rt->info[rate].rateKbps / 10;
197 +}
198 +
199 +static void
200 +ath_node_sample_rx(struct ieee80211_node *ni, struct ath_rx_status *rs)
201 +{
202 +       struct ath_node *an = ATH_NODE(ni);
203 +       struct ieee80211vap *vap = ni->ni_vap;
204 +       struct ieee80211com *ic = vap->iv_ic;
205 +       struct wprobe_link *l = &an->an_wplink;
206 +       struct wprobe_value *v = l->val;
207 +       unsigned long flags;
208 +       int rate;
209 +
210 +       if (wprobe_disabled() || !an->an_wplink_active || !l->val)
211 +               return;
212 +
213 +       rate = ath_lookup_rateval(ni, rs->rs_rate);
214 +
215 +       spin_lock_irqsave(&l->iface->lock, flags);
216 +       WPROBE_FILL_BEGIN(v, ath_wprobe_link);
217 +       WPROBE_SET(WP_NODE_RSSI, U8, rs->rs_rssi);
218 +       WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + rs->rs_rssi);
219 +       if ((rate > 0) && (rate <= 600000))
220 +               WPROBE_SET(WP_NODE_RX_RATE, U16, rate);
221 +       WPROBE_FILL_END();
222 +       wprobe_update_stats(l->iface, l);
223 +       spin_unlock_irqrestore(&l->iface->lock, flags);
224 +}
225 +
226 +static void
227 +ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, int len)
228 +{
229 +       struct ath_node *an = ATH_NODE(ni);
230 +       struct ieee80211vap *vap = ni->ni_vap;
231 +       struct ieee80211com *ic = vap->iv_ic;
232 +       struct wprobe_link *l = &an->an_wplink;
233 +       struct wprobe_value *v = l->val;
234 +       unsigned long flags;
235 +       int rate, rexmit_counter;
236 +
237 +       if (wprobe_disabled() || !an->an_wplink_active || !l->val)
238 +               return;
239 +
240 +       rate = ath_lookup_rateval(ni, ts->ts_rate);
241 +
242 +       spin_lock_irqsave(&l->iface->lock, flags);
243 +       WPROBE_FILL_BEGIN(v, ath_wprobe_link);
244 +       WPROBE_SET(WP_NODE_RSSI, U8, ts->ts_rssi);
245 +       WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + ts->ts_rssi);
246 +
247 +       if (len <= 200)
248 +               rexmit_counter = WP_NODE_RETRANSMIT_200;
249 +       else if (len <= 400)
250 +               rexmit_counter = WP_NODE_RETRANSMIT_400;
251 +       else if (len <= 800)
252 +               rexmit_counter = WP_NODE_RETRANSMIT_800;
253 +       else
254 +               rexmit_counter = WP_NODE_RETRANSMIT_1600;
255 +       WPROBE_SET(rexmit_counter, U8, ts->ts_longretry);
256 +
257 +       if ((rate > 0) && (rate <= 600000))
258 +               WPROBE_SET(WP_NODE_TX_RATE, U16, rate);
259 +       WPROBE_FILL_END();
260 +       wprobe_update_stats(l->iface, l);
261 +       spin_unlock_irqrestore(&l->iface->lock, flags);
262 +}
263 +
264 +static void
265 +ath_wprobe_node_join(struct ieee80211vap *vap, struct ieee80211_node *ni)
266 +{
267 +       struct wprobe_iface *dev;
268 +       struct wprobe_link *l;
269 +       struct ath_vap *avp;
270 +       struct ath_node *an = ATH_NODE(ni);
271 +
272 +       if (wprobe_disabled() || an->an_wplink_active)
273 +               return;
274 +
275 +       avp = ATH_VAP(vap);
276 +       dev = &avp->av_wpif;
277 +       l = &an->an_wplink;
278 +
279 +       ieee80211_ref_node(ni);
280 +       wprobe_add_link(dev, l, ni->ni_macaddr);
281 +       an->an_wplink_active = 1;
282 +}
283 +
284 +static void
285 +ath_wprobe_do_node_leave(struct work_struct *work)
286 +{
287 +       struct ath_node *an = container_of(work, struct ath_node, an_destroy);
288 +       struct ieee80211_node *ni = &an->an_node;
289 +       struct ieee80211vap *vap = ni->ni_vap;
290 +       struct wprobe_iface *dev;
291 +       struct wprobe_link *l;
292 +       struct ath_vap *avp;
293 +
294 +       avp = ATH_VAP(vap);
295 +       dev = &avp->av_wpif;
296 +       l = &an->an_wplink;
297 +
298 +       wprobe_remove_link(dev, l);
299 +       ieee80211_unref_node(&ni);
300 +       atomic_dec(&cleanup_tasks);
301 +}
302 +
303 +static void
304 +ath_wprobe_node_leave(struct ieee80211vap *vap, struct ieee80211_node *ni)
305 +{
306 +       struct ath_node *an = ATH_NODE(ni);
307 +
308 +       if (wprobe_disabled() || !an->an_wplink_active)
309 +               return;
310 +
311 +       atomic_inc(&cleanup_tasks);
312 +       an->an_wplink_active = 0;
313 +       IEEE80211_INIT_WORK(&an->an_destroy, ath_wprobe_do_node_leave);
314 +       schedule_work(&an->an_destroy);
315 +}
316 +
317 +static void
318 +ath_init_wprobe_dev(struct ath_vap *avp)
319 +{
320 +       struct ieee80211vap *vap = &avp->av_vap;
321 +       struct wprobe_iface *dev = &avp->av_wpif;
322 +
323 +       if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
324 +               return;
325 +
326 +       memcpy(dev, &ath_wprobe_dev, sizeof(struct wprobe_iface));
327 +       dev->addr = vap->iv_myaddr;
328 +       dev->name = vap->iv_dev->name;
329 +       wprobe_add_iface(dev);
330 +}
331 +
332 +static void
333 +ath_remove_wprobe_dev(struct ath_vap *avp)
334 +{
335 +       struct ieee80211vap *vap = &avp->av_vap;
336 +       struct ieee80211com *ic = vap->iv_ic;
337 +       struct ieee80211_node *ni;
338 +       struct wprobe_iface *dev = &avp->av_wpif;
339 +       struct wprobe_link *l;
340 +       struct ath_node *an;
341 +       unsigned long flags;
342 +
343 +       if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
344 +               return;
345 +
346 +restart:
347 +       rcu_read_lock();
348 +       list_for_each_entry_rcu(l, &dev->links, list) {
349 +               an = container_of(l, struct ath_node, an_wplink);
350 +
351 +               if (!an->an_wplink_active)
352 +                       continue;
353 +
354 +               ni = &an->an_node;
355 +               ath_wprobe_node_leave(vap, ni);
356 +               rcu_read_unlock();
357 +               goto restart;
358 +       }
359 +       rcu_read_unlock();
360 +
361 +       /* wait for the cleanup tasks to finish */
362 +       while (atomic_read(&cleanup_tasks) != 0) {
363 +               schedule();
364 +       }
365 +
366 +       wprobe_remove_iface(dev);
367 +}
368 --- a/ath/if_ath.c      2009-03-26 19:54:36.000000000 +0100
369 +++ /var/folders/DB/DBZUyxsHGRKP0B3nCU1mmU+++TI/-Tmp-/cocci-output-73937-18abc0-if_ath.c        2009-03-26 21:08:34.000000000 +0100
370 @@ -400,6 +400,7 @@ static int countrycode = -1;
371  static int maxvaps = -1;
372  static int outdoor = -1;
373  static int xchanmode = -1;
374 +#include "ath_wprobe.c"
375  static int beacon_cal = 1;
376  
377  static const struct ath_hw_detect generic_hw_info = {
378 @@ -1525,6 +1526,7 @@ ath_vap_create(struct ieee80211com *ic, 
379                 ath_hal_intrset(ah, sc->sc_imask);
380         }
381  
382 +       ath_init_wprobe_dev(avp);
383         return vap;
384  }
385  
386 @@ -1605,6 +1607,7 @@ ath_vap_delete(struct ieee80211vap *vap)
387                 decrease = 0;
388  
389         ieee80211_vap_detach(vap);
390 +       ath_remove_wprobe_dev(ATH_VAP(vap));
391         /* NB: memory is reclaimed through dev->destructor callback */
392         if (decrease)
393                 sc->sc_nvaps--;
394 @@ -5931,6 +5934,7 @@ ath_node_cleanup(struct ieee80211_node *
395         /* Clean up node-specific rate things - this currently appears to 
396          * always be a no-op */
397         sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
398 +       ath_wprobe_node_leave(ni->ni_vap, ni);
399  
400         ATH_NODE_UAPSD_LOCK_IRQ(an);
401  #ifdef IEEE80211_DEBUG_REFCNT
402 @@ -7001,6 +7005,7 @@ drop_micfail:
403                                 goto lookup_slowpath;
404                         }
405                         ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
406 +                       ath_node_sample_rx(ni, rs);
407                         type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
408                         ieee80211_unref_node(&ni);
409                 } else {
410 @@ -7020,6 +7025,7 @@ lookup_slowpath:
411                                 ieee80211_keyix_t keyix;
412  
413                                 ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
414 +                               ath_node_sample_rx(ni, rs);
415                                 type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
416                                 /*
417                                  * If the station has a key cache slot assigned
418 @@ -8599,6 +8605,7 @@ ath_tx_processq(struct ath_softc *sc, st
419                                 sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
420                                 ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
421                                         ts->ts_rssi);
422 +                                       ath_node_sample_tx(&an->an_node, ts, bf->bf_skb->len);
423                                 if (bf->bf_skb->priority == WME_AC_VO ||
424                                     bf->bf_skb->priority == WME_AC_VI)
425                                         ni->ni_ic->ic_wme.wme_hipri_traffic++;
426 @@ -10090,6 +10097,7 @@ ath_newassoc(struct ieee80211_node *ni, 
427         struct ath_softc *sc = ic->ic_dev->priv;
428  
429         sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
430 +       ath_wprobe_node_join(ni->ni_vap, ni);
431  
432         /* are we supporting compression? */
433         if (!(vap->iv_ath_cap & ni->ni_ath_flags & IEEE80211_NODE_COMP))
434 --- a/ath/if_athvar.h   2009-03-26 19:54:35.000000000 +0100
435 +++ /var/folders/DB/DBZUyxsHGRKP0B3nCU1mmU+++TI/-Tmp-/cocci-output-73937-80429d-if_athvar.h     2009-03-26 21:08:42.000000000 +0100
436 @@ -46,6 +46,7 @@
437  #include "ah_desc.h"
438  #include "ah_os.h"
439  #include "if_athioctl.h"
440 +#include <linux/wprobe.h>
441  #include "net80211/ieee80211.h"                /* XXX for WME_NUM_AC */
442  #include <asm/io.h>
443  #include <linux/list.h>
444 @@ -352,6 +353,9 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhe
445  /* driver-specific node state */
446  struct ath_node {
447         struct ieee80211_node an_node;          /* base class */
448 +       struct wprobe_link an_wplink;
449 +       uint8_t an_wplink_active;
450 +       struct work_struct an_destroy;
451         u_int16_t an_decomp_index;              /* decompression mask index */
452         u_int32_t an_avgrssi;                   /* average rssi over all rx frames */
453         u_int8_t  an_prevdatarix;               /* rate ix of last data frame */
454 @@ -521,6 +525,7 @@ struct ath_vap {
455  #else
456         unsigned int av_beacon_alloc;
457  #endif
458 +       struct wprobe_iface av_wpif;
459  };
460  #define        ATH_VAP(_v)     ((struct ath_vap *)(_v))
461