ieee80211_amrr.c revision 302307
1164633Ssam/* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */ 2164633Ssam 3164633Ssam/*- 4206358Srpaulo * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> 5164633Ssam * Copyright (c) 2006 6164633Ssam * Damien Bergamini <damien.bergamini@free.fr> 7164633Ssam * 8164633Ssam * Permission to use, copy, modify, and distribute this software for any 9164633Ssam * purpose with or without fee is hereby granted, provided that the above 10164633Ssam * copyright notice and this permission notice appear in all copies. 11164633Ssam * 12164633Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13164633Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14164633Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15164633Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16164633Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17164633Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18164633Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19164633Ssam */ 20164633Ssam 21164633Ssam#include <sys/cdefs.h> 22164633Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_amrr.c 302307 2016-07-01 19:58:13Z adrian $"); 23164633Ssam 24164633Ssam/*- 25164633Ssam * Naive implementation of the Adaptive Multi Rate Retry algorithm: 26164633Ssam * 27164633Ssam * "IEEE 802.11 Rate Adaptation: A Practical Approach" 28164633Ssam * Mathieu Lacage, Hossein Manshaei, Thierry Turletti 29164633Ssam * INRIA Sophia - Projet Planete 30164633Ssam * http://www-sop.inria.fr/rapports/sophia/RR-5208.html 31164633Ssam */ 32178354Ssam#include "opt_wlan.h" 33178354Ssam 34164633Ssam#include <sys/param.h> 35164633Ssam#include <sys/kernel.h> 36295126Sglebius#include <sys/malloc.h> 37164633Ssam#include <sys/module.h> 38296925Sadrian#include <sys/sbuf.h> 39164633Ssam#include <sys/socket.h> 40164633Ssam#include <sys/sysctl.h> 41164633Ssam 42164633Ssam#include <net/if.h> 43257176Sglebius#include <net/if_var.h> 44164633Ssam#include <net/if_media.h> 45257241Sglebius#include <net/ethernet.h> 46164633Ssam 47164633Ssam#ifdef INET 48164633Ssam#include <netinet/in.h> 49164633Ssam#include <netinet/if_ether.h> 50164633Ssam#endif 51164633Ssam 52164633Ssam#include <net80211/ieee80211_var.h> 53252727Sadrian#include <net80211/ieee80211_ht.h> 54164633Ssam#include <net80211/ieee80211_amrr.h> 55206358Srpaulo#include <net80211/ieee80211_ratectl.h> 56164633Ssam 57164633Ssam#define is_success(amn) \ 58164633Ssam ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10) 59164633Ssam#define is_failure(amn) \ 60164633Ssam ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) 61164633Ssam#define is_enough(amn) \ 62164633Ssam ((amn)->amn_txcnt > 10) 63164633Ssam 64206358Srpaulostatic void amrr_setinterval(const struct ieee80211vap *, int); 65206358Srpaulostatic void amrr_init(struct ieee80211vap *); 66206358Srpaulostatic void amrr_deinit(struct ieee80211vap *); 67206358Srpaulostatic void amrr_node_init(struct ieee80211_node *); 68206358Srpaulostatic void amrr_node_deinit(struct ieee80211_node *); 69206358Srpaulostatic int amrr_update(struct ieee80211_amrr *, 70206358Srpaulo struct ieee80211_amrr_node *, struct ieee80211_node *); 71206358Srpaulostatic int amrr_rate(struct ieee80211_node *, void *, uint32_t); 72206358Srpaulostatic void amrr_tx_complete(const struct ieee80211vap *, 73206358Srpaulo const struct ieee80211_node *, int, 74206358Srpaulo void *, void *); 75206358Srpaulostatic void amrr_tx_update(const struct ieee80211vap *vap, 76206358Srpaulo const struct ieee80211_node *, void *, void *, void *); 77206358Srpaulostatic void amrr_sysctlattach(struct ieee80211vap *, 78206358Srpaulo struct sysctl_ctx_list *, struct sysctl_oid *); 79296925Sadrianstatic void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s); 80178354Ssam 81178354Ssam/* number of references from net80211 layer */ 82178354Ssamstatic int nrefs = 0; 83178354Ssam 84206358Srpaulostatic const struct ieee80211_ratectl amrr = { 85206358Srpaulo .ir_name = "amrr", 86206358Srpaulo .ir_attach = NULL, 87206358Srpaulo .ir_detach = NULL, 88206358Srpaulo .ir_init = amrr_init, 89206358Srpaulo .ir_deinit = amrr_deinit, 90206358Srpaulo .ir_node_init = amrr_node_init, 91206358Srpaulo .ir_node_deinit = amrr_node_deinit, 92206358Srpaulo .ir_rate = amrr_rate, 93206358Srpaulo .ir_tx_complete = amrr_tx_complete, 94206358Srpaulo .ir_tx_update = amrr_tx_update, 95206358Srpaulo .ir_setinterval = amrr_setinterval, 96296925Sadrian .ir_node_stats = amrr_node_stats, 97206358Srpaulo}; 98206358SrpauloIEEE80211_RATECTL_MODULE(amrr, 1); 99206358SrpauloIEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); 100206358Srpaulo 101206358Srpaulostatic void 102206358Srpauloamrr_setinterval(const struct ieee80211vap *vap, int msecs) 103178354Ssam{ 104206358Srpaulo struct ieee80211_amrr *amrr = vap->iv_rs; 105178354Ssam int t; 106178354Ssam 107178354Ssam if (msecs < 100) 108178354Ssam msecs = 100; 109178354Ssam t = msecs_to_ticks(msecs); 110178354Ssam amrr->amrr_interval = (t < 1) ? 1 : t; 111178354Ssam} 112178354Ssam 113206358Srpaulostatic void 114206358Srpauloamrr_init(struct ieee80211vap *vap) 115164633Ssam{ 116206358Srpaulo struct ieee80211_amrr *amrr; 117178354Ssam 118206358Srpaulo KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); 119206358Srpaulo 120283538Sadrian amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr), 121283538Sadrian M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 122206419Srpaulo if (amrr == NULL) { 123206419Srpaulo if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); 124206419Srpaulo return; 125206419Srpaulo } 126206358Srpaulo amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD; 127206358Srpaulo amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD; 128206358Srpaulo amrr_setinterval(vap, 500 /* ms */); 129206358Srpaulo amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); 130164633Ssam} 131164633Ssam 132206358Srpaulostatic void 133206358Srpauloamrr_deinit(struct ieee80211vap *vap) 134178354Ssam{ 135283538Sadrian IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); 136178354Ssam} 137178354Ssam 138257412Sadrian/* 139257412Sadrian * Return whether 11n rates are possible. 140257412Sadrian * 141257412Sadrian * Some 11n devices may return HT information but no HT rates. 142257412Sadrian * Thus, we shouldn't treat them as an 11n node. 143257412Sadrian */ 144252727Sadrianstatic int 145252727Sadrianamrr_node_is_11n(struct ieee80211_node *ni) 146252727Sadrian{ 147252727Sadrian 148252727Sadrian if (ni->ni_chan == NULL) 149252727Sadrian return (0); 150252727Sadrian if (ni->ni_chan == IEEE80211_CHAN_ANYC) 151252727Sadrian return (0); 152257412Sadrian if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0) 153257412Sadrian return (0); 154252727Sadrian return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); 155252727Sadrian} 156252727Sadrian 157206358Srpaulostatic void 158206358Srpauloamrr_node_init(struct ieee80211_node *ni) 159164633Ssam{ 160252727Sadrian const struct ieee80211_rateset *rs = NULL; 161206358Srpaulo struct ieee80211vap *vap = ni->ni_vap; 162206358Srpaulo struct ieee80211_amrr *amrr = vap->iv_rs; 163206358Srpaulo struct ieee80211_amrr_node *amn; 164252727Sadrian uint8_t rate; 165178354Ssam 166207323Srpaulo if (ni->ni_rctls == NULL) { 167283538Sadrian ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), 168283538Sadrian M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 169207323Srpaulo if (amn == NULL) { 170207323Srpaulo if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " 171207323Srpaulo "structure\n"); 172207323Srpaulo return; 173207323Srpaulo } 174207323Srpaulo } else 175207323Srpaulo amn = ni->ni_rctls; 176178354Ssam amn->amn_amrr = amrr; 177164633Ssam amn->amn_success = 0; 178164633Ssam amn->amn_recovery = 0; 179164633Ssam amn->amn_txcnt = amn->amn_retrycnt = 0; 180164633Ssam amn->amn_success_threshold = amrr->amrr_min_success_threshold; 181178354Ssam 182252727Sadrian /* 11n or not? Pick the right rateset */ 183252727Sadrian if (amrr_node_is_11n(ni)) { 184252727Sadrian /* XXX ew */ 185252727Sadrian IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 186252727Sadrian "%s: 11n node", __func__); 187252727Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 188252727Sadrian } else { 189252727Sadrian IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 190252727Sadrian "%s: non-11n node", __func__); 191252727Sadrian rs = &ni->ni_rates; 192252727Sadrian } 193252727Sadrian 194252727Sadrian /* Initial rate - lowest */ 195252727Sadrian rate = rs->rs_rates[0]; 196252727Sadrian 197252727Sadrian /* XXX clear the basic rate flag if it's not 11n */ 198252727Sadrian if (! amrr_node_is_11n(ni)) 199252727Sadrian rate &= IEEE80211_RATE_VAL; 200252727Sadrian 201252727Sadrian /* pick initial rate from the rateset - HT or otherwise */ 202270206Sadrian /* Pick something low that's likely to succeed */ 203252727Sadrian for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; 204252727Sadrian amn->amn_rix--) { 205252727Sadrian /* legacy - anything < 36mbit, stop searching */ 206270206Sadrian /* 11n - stop at MCS4 */ 207257881Sadrian if (amrr_node_is_11n(ni)) { 208270206Sadrian if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) 209257881Sadrian break; 210257881Sadrian } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) 211252727Sadrian break; 212252727Sadrian } 213257881Sadrian rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 214252727Sadrian 215252727Sadrian /* if the rate is an 11n rate, ensure the MCS bit is set */ 216252727Sadrian if (amrr_node_is_11n(ni)) 217252727Sadrian rate |= IEEE80211_RATE_MCS; 218252727Sadrian 219252727Sadrian /* Assign initial rate from the rateset */ 220252727Sadrian ni->ni_txrate = rate; 221178354Ssam amn->amn_ticks = ticks; 222178354Ssam 223302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 224302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 225178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 226302307Sadrian "AMRR: nrates=%d, initial rate %s%d", 227252727Sadrian rs->rs_nrates, 228302307Sadrian amrr_node_is_11n(ni) ? "MCS " : "", 229302307Sadrian rate & IEEE80211_RATE_VAL); 230164633Ssam} 231164633Ssam 232206358Srpaulostatic void 233206358Srpauloamrr_node_deinit(struct ieee80211_node *ni) 234206358Srpaulo{ 235283538Sadrian IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); 236206358Srpaulo} 237206358Srpaulo 238178354Ssamstatic int 239178354Ssamamrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 240178354Ssam struct ieee80211_node *ni) 241164633Ssam{ 242178354Ssam int rix = amn->amn_rix; 243252727Sadrian const struct ieee80211_rateset *rs = NULL; 244164633Ssam 245178354Ssam KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); 246178354Ssam 247252727Sadrian /* 11n or not? Pick the right rateset */ 248252727Sadrian if (amrr_node_is_11n(ni)) { 249252727Sadrian /* XXX ew */ 250252727Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 251252727Sadrian } else { 252252727Sadrian rs = &ni->ni_rates; 253252727Sadrian } 254252727Sadrian 255302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 256302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 257252727Sadrian IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 258252727Sadrian "AMRR: current rate %d, txcnt=%d, retrycnt=%d", 259252727Sadrian rs->rs_rates[rix] & IEEE80211_RATE_VAL, 260252727Sadrian amn->amn_txcnt, 261252727Sadrian amn->amn_retrycnt); 262252727Sadrian 263252736Sadrian /* 264252736Sadrian * XXX This is totally bogus for 11n, as although high MCS 265252736Sadrian * rates for each stream may be failing, the next stream 266252736Sadrian * should be checked. 267252736Sadrian * 268252736Sadrian * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to 269252736Sadrian * MCS23, we should skip 6/7 and try 8 onwards. 270252736Sadrian */ 271178354Ssam if (is_success(amn)) { 272164633Ssam amn->amn_success++; 273164633Ssam if (amn->amn_success >= amn->amn_success_threshold && 274252727Sadrian rix + 1 < rs->rs_nrates) { 275164633Ssam amn->amn_recovery = 1; 276164633Ssam amn->amn_success = 0; 277178354Ssam rix++; 278302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 279302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 280178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 281178354Ssam "AMRR increasing rate %d (txcnt=%d retrycnt=%d)", 282252727Sadrian rs->rs_rates[rix] & IEEE80211_RATE_VAL, 283164633Ssam amn->amn_txcnt, amn->amn_retrycnt); 284164633Ssam } else { 285164633Ssam amn->amn_recovery = 0; 286164633Ssam } 287164633Ssam } else if (is_failure(amn)) { 288164633Ssam amn->amn_success = 0; 289178354Ssam if (rix > 0) { 290164633Ssam if (amn->amn_recovery) { 291164633Ssam amn->amn_success_threshold *= 2; 292164633Ssam if (amn->amn_success_threshold > 293164633Ssam amrr->amrr_max_success_threshold) 294164633Ssam amn->amn_success_threshold = 295164633Ssam amrr->amrr_max_success_threshold; 296164633Ssam } else { 297164633Ssam amn->amn_success_threshold = 298164633Ssam amrr->amrr_min_success_threshold; 299164633Ssam } 300178354Ssam rix--; 301302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 302302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 303178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 304178354Ssam "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", 305252727Sadrian rs->rs_rates[rix] & IEEE80211_RATE_VAL, 306164633Ssam amn->amn_txcnt, amn->amn_retrycnt); 307164633Ssam } 308164633Ssam amn->amn_recovery = 0; 309164633Ssam } 310164633Ssam 311178354Ssam /* reset counters */ 312178354Ssam amn->amn_txcnt = 0; 313178354Ssam amn->amn_retrycnt = 0; 314178354Ssam 315178354Ssam return rix; 316164633Ssam} 317164633Ssam 318164633Ssam/* 319178354Ssam * Return the rate index to use in sending a data frame. 320178354Ssam * Update our internal state if it's been long enough. 321178354Ssam * If the rate changes we also update ni_txrate to match. 322164633Ssam */ 323206358Srpaulostatic int 324206358Srpauloamrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) 325178354Ssam{ 326206358Srpaulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 327178354Ssam struct ieee80211_amrr *amrr = amn->amn_amrr; 328252727Sadrian const struct ieee80211_rateset *rs = NULL; 329178354Ssam int rix; 330178354Ssam 331252727Sadrian /* 11n or not? Pick the right rateset */ 332252727Sadrian if (amrr_node_is_11n(ni)) { 333252727Sadrian /* XXX ew */ 334252727Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 335252727Sadrian } else { 336252727Sadrian rs = &ni->ni_rates; 337252727Sadrian } 338252727Sadrian 339178354Ssam if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { 340178354Ssam rix = amrr_update(amrr, amn, ni); 341178354Ssam if (rix != amn->amn_rix) { 342178354Ssam /* update public rate */ 343252727Sadrian ni->ni_txrate = rs->rs_rates[rix]; 344252727Sadrian /* XXX strip basic rate flag from txrate, if non-11n */ 345252727Sadrian if (amrr_node_is_11n(ni)) 346252727Sadrian ni->ni_txrate |= IEEE80211_RATE_MCS; 347252727Sadrian else 348252727Sadrian ni->ni_txrate &= IEEE80211_RATE_VAL; 349178354Ssam amn->amn_rix = rix; 350178354Ssam } 351178354Ssam amn->amn_ticks = ticks; 352178354Ssam } else 353178354Ssam rix = amn->amn_rix; 354178354Ssam return rix; 355178354Ssam} 356178354Ssam 357206358Srpaulo/* 358206358Srpaulo * Update statistics with tx complete status. Ok is non-zero 359206358Srpaulo * if the packet is known to be ACK'd. Retries has the number 360206358Srpaulo * retransmissions (i.e. xmit attempts - 1). 361206358Srpaulo */ 362206358Srpaulostatic void 363206358Srpauloamrr_tx_complete(const struct ieee80211vap *vap, 364206358Srpaulo const struct ieee80211_node *ni, int ok, 365206358Srpaulo void *arg1, void *arg2 __unused) 366206358Srpaulo{ 367206358Srpaulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 368206358Srpaulo int retries = *(int *)arg1; 369206358Srpaulo 370206358Srpaulo amn->amn_txcnt++; 371206358Srpaulo if (ok) 372206358Srpaulo amn->amn_success++; 373206358Srpaulo amn->amn_retrycnt += retries; 374206358Srpaulo} 375206358Srpaulo 376206358Srpaulo/* 377206358Srpaulo * Set tx count/retry statistics explicitly. Intended for 378206358Srpaulo * drivers that poll the device for statistics maintained 379206358Srpaulo * in the device. 380206358Srpaulo */ 381206358Srpaulostatic void 382206358Srpauloamrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni, 383206358Srpaulo void *arg1, void *arg2, void *arg3) 384206358Srpaulo{ 385206358Srpaulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 386206358Srpaulo int txcnt = *(int *)arg1, success = *(int *)arg2, retrycnt = *(int *)arg3; 387206358Srpaulo 388206358Srpaulo amn->amn_txcnt = txcnt; 389206358Srpaulo amn->amn_success = success; 390206358Srpaulo amn->amn_retrycnt = retrycnt; 391206358Srpaulo} 392206358Srpaulo 393164633Ssamstatic int 394178354Ssamamrr_sysctl_interval(SYSCTL_HANDLER_ARGS) 395164633Ssam{ 396206358Srpaulo struct ieee80211vap *vap = arg1; 397206358Srpaulo struct ieee80211_amrr *amrr = vap->iv_rs; 398178354Ssam int msecs = ticks_to_msecs(amrr->amrr_interval); 399178354Ssam int error; 400178354Ssam 401178354Ssam error = sysctl_handle_int(oidp, &msecs, 0, req); 402178354Ssam if (error || !req->newptr) 403178354Ssam return error; 404206358Srpaulo amrr_setinterval(vap, msecs); 405178354Ssam return 0; 406164633Ssam} 407164633Ssam 408178354Ssamstatic void 409206358Srpauloamrr_sysctlattach(struct ieee80211vap *vap, 410178354Ssam struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) 411178354Ssam{ 412206358Srpaulo struct ieee80211_amrr *amrr = vap->iv_rs; 413178354Ssam 414178354Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 415206358Srpaulo "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap, 416178354Ssam 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); 417178354Ssam /* XXX bounds check values */ 418217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 419178354Ssam "amrr_max_sucess_threshold", CTLFLAG_RW, 420178354Ssam &amrr->amrr_max_success_threshold, 0, ""); 421217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 422178354Ssam "amrr_min_sucess_threshold", CTLFLAG_RW, 423178354Ssam &amrr->amrr_min_success_threshold, 0, ""); 424178354Ssam} 425296925Sadrian 426296925Sadrianstatic void 427296925Sadrianamrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) 428296925Sadrian{ 429296925Sadrian int rate; 430296925Sadrian struct ieee80211_amrr_node *amn = ni->ni_rctls; 431296925Sadrian struct ieee80211_rateset *rs; 432296925Sadrian 433296925Sadrian /* XXX TODO: check locking? */ 434296925Sadrian 435296925Sadrian /* XXX TODO: this should be a method */ 436296925Sadrian if (amrr_node_is_11n(ni)) { 437296925Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 438296925Sadrian rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 439296925Sadrian sbuf_printf(s, "rate: MCS %d\n", rate); 440296925Sadrian } else { 441296925Sadrian rs = &ni->ni_rates; 442296925Sadrian rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 443296925Sadrian sbuf_printf(s, "rate: %d Mbit\n", rate / 2); 444296925Sadrian } 445296925Sadrian 446296925Sadrian sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); 447296925Sadrian sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); 448296925Sadrian sbuf_printf(s, "success: %u\n", amn->amn_success); 449296925Sadrian sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); 450296925Sadrian sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); 451296925Sadrian sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); 452296925Sadrian} 453