1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */ |
---|
4 | |
---|
5 | /*- |
---|
6 | * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> |
---|
7 | * Copyright (c) 2006 |
---|
8 | * Damien Bergamini <damien.bergamini@free.fr> |
---|
9 | * |
---|
10 | * Permission to use, copy, modify, and distribute this software for any |
---|
11 | * purpose with or without fee is hereby granted, provided that the above |
---|
12 | * copyright notice and this permission notice appear in all copies. |
---|
13 | * |
---|
14 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
---|
15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
---|
17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
---|
19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
---|
20 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
21 | */ |
---|
22 | |
---|
23 | #include <sys/cdefs.h> |
---|
24 | __FBSDID("$FreeBSD$"); |
---|
25 | |
---|
26 | /*- |
---|
27 | * Naive implementation of the Adaptive Multi Rate Retry algorithm: |
---|
28 | * |
---|
29 | * "IEEE 802.11 Rate Adaptation: A Practical Approach" |
---|
30 | * Mathieu Lacage, Hossein Manshaei, Thierry Turletti |
---|
31 | * INRIA Sophia - Projet Planete |
---|
32 | * http://www-sop.inria.fr/rapports/sophia/RR-5208.html |
---|
33 | */ |
---|
34 | #include <rtems/bsd/local/opt_wlan.h> |
---|
35 | |
---|
36 | #include <rtems/bsd/sys/param.h> |
---|
37 | #include <sys/kernel.h> |
---|
38 | #include <sys/malloc.h> |
---|
39 | #include <sys/module.h> |
---|
40 | #include <sys/sbuf.h> |
---|
41 | #include <sys/socket.h> |
---|
42 | #include <sys/sysctl.h> |
---|
43 | |
---|
44 | #include <net/if.h> |
---|
45 | #include <net/if_var.h> |
---|
46 | #include <net/if_media.h> |
---|
47 | #include <net/ethernet.h> |
---|
48 | |
---|
49 | #ifdef INET |
---|
50 | #include <netinet/in.h> |
---|
51 | #include <netinet/if_ether.h> |
---|
52 | #endif |
---|
53 | |
---|
54 | #include <net80211/ieee80211_var.h> |
---|
55 | #include <net80211/ieee80211_ht.h> |
---|
56 | #include <net80211/ieee80211_amrr.h> |
---|
57 | #include <net80211/ieee80211_ratectl.h> |
---|
58 | |
---|
59 | #define is_success(amn) \ |
---|
60 | ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10) |
---|
61 | #define is_failure(amn) \ |
---|
62 | ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) |
---|
63 | #define is_enough(amn) \ |
---|
64 | ((amn)->amn_txcnt > 10) |
---|
65 | |
---|
66 | static void amrr_setinterval(const struct ieee80211vap *, int); |
---|
67 | static void amrr_init(struct ieee80211vap *); |
---|
68 | static void amrr_deinit(struct ieee80211vap *); |
---|
69 | static void amrr_node_init(struct ieee80211_node *); |
---|
70 | static void amrr_node_deinit(struct ieee80211_node *); |
---|
71 | static int amrr_update(struct ieee80211_amrr *, |
---|
72 | struct ieee80211_amrr_node *, struct ieee80211_node *); |
---|
73 | static int amrr_rate(struct ieee80211_node *, void *, uint32_t); |
---|
74 | static void amrr_tx_complete(const struct ieee80211_node *, |
---|
75 | const struct ieee80211_ratectl_tx_status *); |
---|
76 | static void amrr_tx_update_cb(void *, struct ieee80211_node *); |
---|
77 | static void amrr_tx_update(struct ieee80211vap *vap, |
---|
78 | struct ieee80211_ratectl_tx_stats *); |
---|
79 | static void amrr_sysctlattach(struct ieee80211vap *, |
---|
80 | struct sysctl_ctx_list *, struct sysctl_oid *); |
---|
81 | static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s); |
---|
82 | |
---|
83 | /* number of references from net80211 layer */ |
---|
84 | static int nrefs = 0; |
---|
85 | |
---|
86 | static const struct ieee80211_ratectl amrr = { |
---|
87 | .ir_name = "amrr", |
---|
88 | .ir_attach = NULL, |
---|
89 | .ir_detach = NULL, |
---|
90 | .ir_init = amrr_init, |
---|
91 | .ir_deinit = amrr_deinit, |
---|
92 | .ir_node_init = amrr_node_init, |
---|
93 | .ir_node_deinit = amrr_node_deinit, |
---|
94 | .ir_rate = amrr_rate, |
---|
95 | .ir_tx_complete = amrr_tx_complete, |
---|
96 | .ir_tx_update = amrr_tx_update, |
---|
97 | .ir_setinterval = amrr_setinterval, |
---|
98 | .ir_node_stats = amrr_node_stats, |
---|
99 | }; |
---|
100 | IEEE80211_RATECTL_MODULE(amrr, 1); |
---|
101 | IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); |
---|
102 | |
---|
103 | static void |
---|
104 | amrr_setinterval(const struct ieee80211vap *vap, int msecs) |
---|
105 | { |
---|
106 | struct ieee80211_amrr *amrr = vap->iv_rs; |
---|
107 | int t; |
---|
108 | |
---|
109 | if (msecs < 100) |
---|
110 | msecs = 100; |
---|
111 | t = msecs_to_ticks(msecs); |
---|
112 | amrr->amrr_interval = (t < 1) ? 1 : t; |
---|
113 | } |
---|
114 | |
---|
115 | static void |
---|
116 | amrr_init(struct ieee80211vap *vap) |
---|
117 | { |
---|
118 | struct ieee80211_amrr *amrr; |
---|
119 | |
---|
120 | KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); |
---|
121 | |
---|
122 | amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr), |
---|
123 | M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); |
---|
124 | if (amrr == NULL) { |
---|
125 | if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); |
---|
126 | return; |
---|
127 | } |
---|
128 | amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD; |
---|
129 | amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD; |
---|
130 | amrr_setinterval(vap, 500 /* ms */); |
---|
131 | amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); |
---|
132 | } |
---|
133 | |
---|
134 | static void |
---|
135 | amrr_deinit(struct ieee80211vap *vap) |
---|
136 | { |
---|
137 | IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); |
---|
138 | } |
---|
139 | |
---|
140 | /* |
---|
141 | * Return whether 11n rates are possible. |
---|
142 | * |
---|
143 | * Some 11n devices may return HT information but no HT rates. |
---|
144 | * Thus, we shouldn't treat them as an 11n node. |
---|
145 | */ |
---|
146 | static int |
---|
147 | amrr_node_is_11n(struct ieee80211_node *ni) |
---|
148 | { |
---|
149 | |
---|
150 | if (ni->ni_chan == NULL) |
---|
151 | return (0); |
---|
152 | if (ni->ni_chan == IEEE80211_CHAN_ANYC) |
---|
153 | return (0); |
---|
154 | if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0) |
---|
155 | return (0); |
---|
156 | return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); |
---|
157 | } |
---|
158 | |
---|
159 | static void |
---|
160 | amrr_node_init(struct ieee80211_node *ni) |
---|
161 | { |
---|
162 | const struct ieee80211_rateset *rs = NULL; |
---|
163 | struct ieee80211vap *vap = ni->ni_vap; |
---|
164 | struct ieee80211_amrr *amrr = vap->iv_rs; |
---|
165 | struct ieee80211_amrr_node *amn; |
---|
166 | uint8_t rate; |
---|
167 | |
---|
168 | if (ni->ni_rctls == NULL) { |
---|
169 | ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), |
---|
170 | M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); |
---|
171 | if (amn == NULL) { |
---|
172 | if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " |
---|
173 | "structure\n"); |
---|
174 | return; |
---|
175 | } |
---|
176 | } else |
---|
177 | amn = ni->ni_rctls; |
---|
178 | amn->amn_amrr = amrr; |
---|
179 | amn->amn_success = 0; |
---|
180 | amn->amn_recovery = 0; |
---|
181 | amn->amn_txcnt = amn->amn_retrycnt = 0; |
---|
182 | amn->amn_success_threshold = amrr->amrr_min_success_threshold; |
---|
183 | |
---|
184 | /* 11n or not? Pick the right rateset */ |
---|
185 | if (amrr_node_is_11n(ni)) { |
---|
186 | /* XXX ew */ |
---|
187 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, |
---|
188 | "%s: 11n node", __func__); |
---|
189 | rs = (struct ieee80211_rateset *) &ni->ni_htrates; |
---|
190 | } else { |
---|
191 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, |
---|
192 | "%s: non-11n node", __func__); |
---|
193 | rs = &ni->ni_rates; |
---|
194 | } |
---|
195 | |
---|
196 | /* Initial rate - lowest */ |
---|
197 | rate = rs->rs_rates[0]; |
---|
198 | |
---|
199 | /* XXX clear the basic rate flag if it's not 11n */ |
---|
200 | if (! amrr_node_is_11n(ni)) |
---|
201 | rate &= IEEE80211_RATE_VAL; |
---|
202 | |
---|
203 | /* pick initial rate from the rateset - HT or otherwise */ |
---|
204 | /* Pick something low that's likely to succeed */ |
---|
205 | for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; |
---|
206 | amn->amn_rix--) { |
---|
207 | /* legacy - anything < 36mbit, stop searching */ |
---|
208 | /* 11n - stop at MCS4 */ |
---|
209 | if (amrr_node_is_11n(ni)) { |
---|
210 | if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) |
---|
211 | break; |
---|
212 | } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) |
---|
213 | break; |
---|
214 | } |
---|
215 | rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; |
---|
216 | |
---|
217 | /* if the rate is an 11n rate, ensure the MCS bit is set */ |
---|
218 | if (amrr_node_is_11n(ni)) |
---|
219 | rate |= IEEE80211_RATE_MCS; |
---|
220 | |
---|
221 | /* Assign initial rate from the rateset */ |
---|
222 | ni->ni_txrate = rate; |
---|
223 | amn->amn_ticks = ticks; |
---|
224 | |
---|
225 | /* XXX TODO: we really need a rate-to-string method */ |
---|
226 | /* XXX TODO: non-11n rate should be divided by two.. */ |
---|
227 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, |
---|
228 | "AMRR: nrates=%d, initial rate %s%d", |
---|
229 | rs->rs_nrates, |
---|
230 | amrr_node_is_11n(ni) ? "MCS " : "", |
---|
231 | rate & IEEE80211_RATE_VAL); |
---|
232 | } |
---|
233 | |
---|
234 | static void |
---|
235 | amrr_node_deinit(struct ieee80211_node *ni) |
---|
236 | { |
---|
237 | IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); |
---|
238 | } |
---|
239 | |
---|
240 | static int |
---|
241 | amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, |
---|
242 | struct ieee80211_node *ni) |
---|
243 | { |
---|
244 | int rix = amn->amn_rix; |
---|
245 | const struct ieee80211_rateset *rs = NULL; |
---|
246 | |
---|
247 | KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); |
---|
248 | |
---|
249 | /* 11n or not? Pick the right rateset */ |
---|
250 | if (amrr_node_is_11n(ni)) { |
---|
251 | /* XXX ew */ |
---|
252 | rs = (struct ieee80211_rateset *) &ni->ni_htrates; |
---|
253 | } else { |
---|
254 | rs = &ni->ni_rates; |
---|
255 | } |
---|
256 | |
---|
257 | /* XXX TODO: we really need a rate-to-string method */ |
---|
258 | /* XXX TODO: non-11n rate should be divided by two.. */ |
---|
259 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, |
---|
260 | "AMRR: current rate %d, txcnt=%d, retrycnt=%d", |
---|
261 | rs->rs_rates[rix] & IEEE80211_RATE_VAL, |
---|
262 | amn->amn_txcnt, |
---|
263 | amn->amn_retrycnt); |
---|
264 | |
---|
265 | /* |
---|
266 | * XXX This is totally bogus for 11n, as although high MCS |
---|
267 | * rates for each stream may be failing, the next stream |
---|
268 | * should be checked. |
---|
269 | * |
---|
270 | * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to |
---|
271 | * MCS23, we should skip 6/7 and try 8 onwards. |
---|
272 | */ |
---|
273 | if (is_success(amn)) { |
---|
274 | amn->amn_success++; |
---|
275 | if (amn->amn_success >= amn->amn_success_threshold && |
---|
276 | rix + 1 < rs->rs_nrates) { |
---|
277 | amn->amn_recovery = 1; |
---|
278 | amn->amn_success = 0; |
---|
279 | rix++; |
---|
280 | /* XXX TODO: we really need a rate-to-string method */ |
---|
281 | /* XXX TODO: non-11n rate should be divided by two.. */ |
---|
282 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, |
---|
283 | "AMRR increasing rate %d (txcnt=%d retrycnt=%d)", |
---|
284 | rs->rs_rates[rix] & IEEE80211_RATE_VAL, |
---|
285 | amn->amn_txcnt, amn->amn_retrycnt); |
---|
286 | } else { |
---|
287 | amn->amn_recovery = 0; |
---|
288 | } |
---|
289 | } else if (is_failure(amn)) { |
---|
290 | amn->amn_success = 0; |
---|
291 | if (rix > 0) { |
---|
292 | if (amn->amn_recovery) { |
---|
293 | amn->amn_success_threshold *= 2; |
---|
294 | if (amn->amn_success_threshold > |
---|
295 | amrr->amrr_max_success_threshold) |
---|
296 | amn->amn_success_threshold = |
---|
297 | amrr->amrr_max_success_threshold; |
---|
298 | } else { |
---|
299 | amn->amn_success_threshold = |
---|
300 | amrr->amrr_min_success_threshold; |
---|
301 | } |
---|
302 | rix--; |
---|
303 | /* XXX TODO: we really need a rate-to-string method */ |
---|
304 | /* XXX TODO: non-11n rate should be divided by two.. */ |
---|
305 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, |
---|
306 | "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", |
---|
307 | rs->rs_rates[rix] & IEEE80211_RATE_VAL, |
---|
308 | amn->amn_txcnt, amn->amn_retrycnt); |
---|
309 | } |
---|
310 | amn->amn_recovery = 0; |
---|
311 | } |
---|
312 | |
---|
313 | /* reset counters */ |
---|
314 | amn->amn_txcnt = 0; |
---|
315 | amn->amn_retrycnt = 0; |
---|
316 | |
---|
317 | return rix; |
---|
318 | } |
---|
319 | |
---|
320 | /* |
---|
321 | * Return the rate index to use in sending a data frame. |
---|
322 | * Update our internal state if it's been long enough. |
---|
323 | * If the rate changes we also update ni_txrate to match. |
---|
324 | */ |
---|
325 | static int |
---|
326 | amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) |
---|
327 | { |
---|
328 | struct ieee80211_amrr_node *amn = ni->ni_rctls; |
---|
329 | struct ieee80211_amrr *amrr = amn->amn_amrr; |
---|
330 | const struct ieee80211_rateset *rs = NULL; |
---|
331 | int rix; |
---|
332 | |
---|
333 | /* 11n or not? Pick the right rateset */ |
---|
334 | if (amrr_node_is_11n(ni)) { |
---|
335 | /* XXX ew */ |
---|
336 | rs = (struct ieee80211_rateset *) &ni->ni_htrates; |
---|
337 | } else { |
---|
338 | rs = &ni->ni_rates; |
---|
339 | } |
---|
340 | |
---|
341 | if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { |
---|
342 | rix = amrr_update(amrr, amn, ni); |
---|
343 | if (rix != amn->amn_rix) { |
---|
344 | /* update public rate */ |
---|
345 | ni->ni_txrate = rs->rs_rates[rix]; |
---|
346 | /* XXX strip basic rate flag from txrate, if non-11n */ |
---|
347 | if (amrr_node_is_11n(ni)) |
---|
348 | ni->ni_txrate |= IEEE80211_RATE_MCS; |
---|
349 | else |
---|
350 | ni->ni_txrate &= IEEE80211_RATE_VAL; |
---|
351 | amn->amn_rix = rix; |
---|
352 | } |
---|
353 | amn->amn_ticks = ticks; |
---|
354 | } else |
---|
355 | rix = amn->amn_rix; |
---|
356 | return rix; |
---|
357 | } |
---|
358 | |
---|
359 | /* |
---|
360 | * Update statistics with tx complete status. Ok is non-zero |
---|
361 | * if the packet is known to be ACK'd. Retries has the number |
---|
362 | * retransmissions (i.e. xmit attempts - 1). |
---|
363 | */ |
---|
364 | static void |
---|
365 | amrr_tx_complete(const struct ieee80211_node *ni, |
---|
366 | const struct ieee80211_ratectl_tx_status *status) |
---|
367 | { |
---|
368 | struct ieee80211_amrr_node *amn = ni->ni_rctls; |
---|
369 | int retries; |
---|
370 | |
---|
371 | retries = 0; |
---|
372 | if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY) |
---|
373 | retries = status->long_retries; |
---|
374 | |
---|
375 | amn->amn_txcnt++; |
---|
376 | if (status->status == IEEE80211_RATECTL_TX_SUCCESS) |
---|
377 | amn->amn_success++; |
---|
378 | amn->amn_retrycnt += retries; |
---|
379 | } |
---|
380 | |
---|
381 | static void |
---|
382 | amrr_tx_update_cb(void *arg, struct ieee80211_node *ni) |
---|
383 | { |
---|
384 | struct ieee80211_ratectl_tx_stats *stats = arg; |
---|
385 | struct ieee80211_amrr_node *amn = ni->ni_rctls; |
---|
386 | int txcnt, success, retrycnt; |
---|
387 | |
---|
388 | txcnt = stats->nframes; |
---|
389 | success = stats->nsuccess; |
---|
390 | retrycnt = 0; |
---|
391 | if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES) |
---|
392 | retrycnt = stats->nretries; |
---|
393 | |
---|
394 | amn->amn_txcnt += txcnt; |
---|
395 | amn->amn_success += success; |
---|
396 | amn->amn_retrycnt += retrycnt; |
---|
397 | } |
---|
398 | |
---|
399 | /* |
---|
400 | * Set tx count/retry statistics explicitly. Intended for |
---|
401 | * drivers that poll the device for statistics maintained |
---|
402 | * in the device. |
---|
403 | */ |
---|
404 | static void |
---|
405 | amrr_tx_update(struct ieee80211vap *vap, |
---|
406 | struct ieee80211_ratectl_tx_stats *stats) |
---|
407 | { |
---|
408 | |
---|
409 | if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE) |
---|
410 | amrr_tx_update_cb(stats, stats->ni); |
---|
411 | else { |
---|
412 | ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap, |
---|
413 | amrr_tx_update_cb, stats); |
---|
414 | } |
---|
415 | } |
---|
416 | |
---|
417 | static int |
---|
418 | amrr_sysctl_interval(SYSCTL_HANDLER_ARGS) |
---|
419 | { |
---|
420 | struct ieee80211vap *vap = arg1; |
---|
421 | struct ieee80211_amrr *amrr = vap->iv_rs; |
---|
422 | int msecs = ticks_to_msecs(amrr->amrr_interval); |
---|
423 | int error; |
---|
424 | |
---|
425 | error = sysctl_handle_int(oidp, &msecs, 0, req); |
---|
426 | if (error || !req->newptr) |
---|
427 | return error; |
---|
428 | amrr_setinterval(vap, msecs); |
---|
429 | return 0; |
---|
430 | } |
---|
431 | |
---|
432 | static void |
---|
433 | amrr_sysctlattach(struct ieee80211vap *vap, |
---|
434 | struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) |
---|
435 | { |
---|
436 | struct ieee80211_amrr *amrr = vap->iv_rs; |
---|
437 | |
---|
438 | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, |
---|
439 | "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap, |
---|
440 | 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); |
---|
441 | /* XXX bounds check values */ |
---|
442 | SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, |
---|
443 | "amrr_max_sucess_threshold", CTLFLAG_RW, |
---|
444 | &amrr->amrr_max_success_threshold, 0, ""); |
---|
445 | SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, |
---|
446 | "amrr_min_sucess_threshold", CTLFLAG_RW, |
---|
447 | &amrr->amrr_min_success_threshold, 0, ""); |
---|
448 | } |
---|
449 | |
---|
450 | static void |
---|
451 | amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) |
---|
452 | { |
---|
453 | int rate; |
---|
454 | struct ieee80211_amrr_node *amn = ni->ni_rctls; |
---|
455 | struct ieee80211_rateset *rs; |
---|
456 | |
---|
457 | /* XXX TODO: check locking? */ |
---|
458 | |
---|
459 | /* XXX TODO: this should be a method */ |
---|
460 | if (amrr_node_is_11n(ni)) { |
---|
461 | rs = (struct ieee80211_rateset *) &ni->ni_htrates; |
---|
462 | rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; |
---|
463 | sbuf_printf(s, "rate: MCS %d\n", rate); |
---|
464 | } else { |
---|
465 | rs = &ni->ni_rates; |
---|
466 | rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; |
---|
467 | sbuf_printf(s, "rate: %d Mbit\n", rate / 2); |
---|
468 | } |
---|
469 | |
---|
470 | sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); |
---|
471 | sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); |
---|
472 | sbuf_printf(s, "success: %u\n", amn->amn_success); |
---|
473 | sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); |
---|
474 | sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); |
---|
475 | sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); |
---|
476 | } |
---|