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: stable/11/sys/net80211/ieee80211_amrr.c 359746 2020-04-09 15:30:21Z eugen $"); 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 107343035Savos if (!amrr) 108343035Savos return; 109343035Savos 110178354Ssam if (msecs < 100) 111178354Ssam msecs = 100; 112178354Ssam t = msecs_to_ticks(msecs); 113178354Ssam amrr->amrr_interval = (t < 1) ? 1 : t; 114178354Ssam} 115178354Ssam 116206358Srpaulostatic void 117206358Srpauloamrr_init(struct ieee80211vap *vap) 118164633Ssam{ 119206358Srpaulo struct ieee80211_amrr *amrr; 120178354Ssam 121206358Srpaulo KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); 122206358Srpaulo 123321724Savos nrefs++; /* XXX locking */ 124283538Sadrian amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr), 125283538Sadrian M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 126206419Srpaulo if (amrr == NULL) { 127206419Srpaulo if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); 128206419Srpaulo return; 129206419Srpaulo } 130206358Srpaulo amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD; 131206358Srpaulo amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD; 132206358Srpaulo amrr_setinterval(vap, 500 /* ms */); 133206358Srpaulo amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); 134164633Ssam} 135164633Ssam 136206358Srpaulostatic void 137206358Srpauloamrr_deinit(struct ieee80211vap *vap) 138178354Ssam{ 139283538Sadrian IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); 140321724Savos KASSERT(nrefs > 0, ("imbalanced attach/detach")); 141321724Savos nrefs--; /* XXX locking */ 142178354Ssam} 143178354Ssam 144257412Sadrian/* 145257412Sadrian * Return whether 11n rates are possible. 146257412Sadrian * 147257412Sadrian * Some 11n devices may return HT information but no HT rates. 148257412Sadrian * Thus, we shouldn't treat them as an 11n node. 149257412Sadrian */ 150252727Sadrianstatic int 151252727Sadrianamrr_node_is_11n(struct ieee80211_node *ni) 152252727Sadrian{ 153252727Sadrian 154252727Sadrian if (ni->ni_chan == NULL) 155252727Sadrian return (0); 156252727Sadrian if (ni->ni_chan == IEEE80211_CHAN_ANYC) 157252727Sadrian return (0); 158257412Sadrian if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0) 159257412Sadrian return (0); 160252727Sadrian return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); 161252727Sadrian} 162252727Sadrian 163206358Srpaulostatic void 164206358Srpauloamrr_node_init(struct ieee80211_node *ni) 165164633Ssam{ 166252727Sadrian const struct ieee80211_rateset *rs = NULL; 167206358Srpaulo struct ieee80211vap *vap = ni->ni_vap; 168206358Srpaulo struct ieee80211_amrr *amrr = vap->iv_rs; 169206358Srpaulo struct ieee80211_amrr_node *amn; 170252727Sadrian uint8_t rate; 171178354Ssam 172343035Savos if (!amrr) { 173343035Savos if_printf(vap->iv_ifp, "ratectl structure was not allocated, " 174343035Savos "per-node structure allocation skipped\n"); 175343035Savos return; 176343035Savos } 177343035Savos 178207323Srpaulo if (ni->ni_rctls == NULL) { 179283538Sadrian ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), 180283538Sadrian M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 181207323Srpaulo if (amn == NULL) { 182207323Srpaulo if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " 183207323Srpaulo "structure\n"); 184207323Srpaulo return; 185207323Srpaulo } 186207323Srpaulo } else 187207323Srpaulo amn = ni->ni_rctls; 188178354Ssam amn->amn_amrr = amrr; 189164633Ssam amn->amn_success = 0; 190164633Ssam amn->amn_recovery = 0; 191164633Ssam amn->amn_txcnt = amn->amn_retrycnt = 0; 192164633Ssam amn->amn_success_threshold = amrr->amrr_min_success_threshold; 193178354Ssam 194252727Sadrian /* 11n or not? Pick the right rateset */ 195252727Sadrian if (amrr_node_is_11n(ni)) { 196252727Sadrian /* XXX ew */ 197252727Sadrian IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 198252727Sadrian "%s: 11n node", __func__); 199252727Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 200252727Sadrian } else { 201252727Sadrian IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 202252727Sadrian "%s: non-11n node", __func__); 203252727Sadrian rs = &ni->ni_rates; 204252727Sadrian } 205252727Sadrian 206252727Sadrian /* Initial rate - lowest */ 207252727Sadrian rate = rs->rs_rates[0]; 208252727Sadrian 209252727Sadrian /* XXX clear the basic rate flag if it's not 11n */ 210252727Sadrian if (! amrr_node_is_11n(ni)) 211252727Sadrian rate &= IEEE80211_RATE_VAL; 212252727Sadrian 213252727Sadrian /* pick initial rate from the rateset - HT or otherwise */ 214270206Sadrian /* Pick something low that's likely to succeed */ 215252727Sadrian for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; 216252727Sadrian amn->amn_rix--) { 217252727Sadrian /* legacy - anything < 36mbit, stop searching */ 218270206Sadrian /* 11n - stop at MCS4 */ 219257881Sadrian if (amrr_node_is_11n(ni)) { 220270206Sadrian if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) 221257881Sadrian break; 222257881Sadrian } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) 223252727Sadrian break; 224252727Sadrian } 225257881Sadrian rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 226252727Sadrian 227252727Sadrian /* if the rate is an 11n rate, ensure the MCS bit is set */ 228252727Sadrian if (amrr_node_is_11n(ni)) 229252727Sadrian rate |= IEEE80211_RATE_MCS; 230252727Sadrian 231252727Sadrian /* Assign initial rate from the rateset */ 232252727Sadrian ni->ni_txrate = rate; 233178354Ssam amn->amn_ticks = ticks; 234178354Ssam 235302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 236302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 237178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 238302307Sadrian "AMRR: nrates=%d, initial rate %s%d", 239252727Sadrian rs->rs_nrates, 240302307Sadrian amrr_node_is_11n(ni) ? "MCS " : "", 241302307Sadrian rate & IEEE80211_RATE_VAL); 242164633Ssam} 243164633Ssam 244206358Srpaulostatic void 245206358Srpauloamrr_node_deinit(struct ieee80211_node *ni) 246206358Srpaulo{ 247283538Sadrian IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); 248206358Srpaulo} 249206358Srpaulo 250178354Ssamstatic int 251178354Ssamamrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 252178354Ssam struct ieee80211_node *ni) 253164633Ssam{ 254178354Ssam int rix = amn->amn_rix; 255252727Sadrian const struct ieee80211_rateset *rs = NULL; 256164633Ssam 257178354Ssam KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); 258178354Ssam 259252727Sadrian /* 11n or not? Pick the right rateset */ 260252727Sadrian if (amrr_node_is_11n(ni)) { 261252727Sadrian /* XXX ew */ 262252727Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 263252727Sadrian } else { 264252727Sadrian rs = &ni->ni_rates; 265252727Sadrian } 266252727Sadrian 267302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 268302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 269252727Sadrian IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 270252727Sadrian "AMRR: current rate %d, txcnt=%d, retrycnt=%d", 271252727Sadrian rs->rs_rates[rix] & IEEE80211_RATE_VAL, 272252727Sadrian amn->amn_txcnt, 273252727Sadrian amn->amn_retrycnt); 274252727Sadrian 275252736Sadrian /* 276252736Sadrian * XXX This is totally bogus for 11n, as although high MCS 277252736Sadrian * rates for each stream may be failing, the next stream 278252736Sadrian * should be checked. 279252736Sadrian * 280252736Sadrian * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to 281252736Sadrian * MCS23, we should skip 6/7 and try 8 onwards. 282252736Sadrian */ 283178354Ssam if (is_success(amn)) { 284164633Ssam amn->amn_success++; 285164633Ssam if (amn->amn_success >= amn->amn_success_threshold && 286252727Sadrian rix + 1 < rs->rs_nrates) { 287164633Ssam amn->amn_recovery = 1; 288164633Ssam amn->amn_success = 0; 289178354Ssam rix++; 290302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 291302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 292178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 293178354Ssam "AMRR increasing rate %d (txcnt=%d retrycnt=%d)", 294252727Sadrian rs->rs_rates[rix] & IEEE80211_RATE_VAL, 295164633Ssam amn->amn_txcnt, amn->amn_retrycnt); 296164633Ssam } else { 297164633Ssam amn->amn_recovery = 0; 298164633Ssam } 299164633Ssam } else if (is_failure(amn)) { 300164633Ssam amn->amn_success = 0; 301178354Ssam if (rix > 0) { 302164633Ssam if (amn->amn_recovery) { 303164633Ssam amn->amn_success_threshold *= 2; 304164633Ssam if (amn->amn_success_threshold > 305164633Ssam amrr->amrr_max_success_threshold) 306164633Ssam amn->amn_success_threshold = 307164633Ssam amrr->amrr_max_success_threshold; 308164633Ssam } else { 309164633Ssam amn->amn_success_threshold = 310164633Ssam amrr->amrr_min_success_threshold; 311164633Ssam } 312178354Ssam rix--; 313302307Sadrian /* XXX TODO: we really need a rate-to-string method */ 314302307Sadrian /* XXX TODO: non-11n rate should be divided by two.. */ 315178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 316178354Ssam "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", 317252727Sadrian rs->rs_rates[rix] & IEEE80211_RATE_VAL, 318164633Ssam amn->amn_txcnt, amn->amn_retrycnt); 319164633Ssam } 320164633Ssam amn->amn_recovery = 0; 321164633Ssam } 322164633Ssam 323178354Ssam /* reset counters */ 324178354Ssam amn->amn_txcnt = 0; 325178354Ssam amn->amn_retrycnt = 0; 326178354Ssam 327178354Ssam return rix; 328164633Ssam} 329164633Ssam 330164633Ssam/* 331178354Ssam * Return the rate index to use in sending a data frame. 332178354Ssam * Update our internal state if it's been long enough. 333178354Ssam * If the rate changes we also update ni_txrate to match. 334164633Ssam */ 335206358Srpaulostatic int 336206358Srpauloamrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) 337178354Ssam{ 338206358Srpaulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 339343035Savos struct ieee80211_amrr *amrr; 340252727Sadrian const struct ieee80211_rateset *rs = NULL; 341178354Ssam int rix; 342178354Ssam 343343035Savos /* XXX should return -1 here, but drivers may not expect this... */ 344343035Savos if (!amn) 345343035Savos { 346343035Savos ni->ni_txrate = ni->ni_rates.rs_rates[0]; 347343035Savos return 0; 348343035Savos } 349343035Savos 350343035Savos amrr = amn->amn_amrr; 351343035Savos 352252727Sadrian /* 11n or not? Pick the right rateset */ 353252727Sadrian if (amrr_node_is_11n(ni)) { 354252727Sadrian /* XXX ew */ 355252727Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 356252727Sadrian } else { 357252727Sadrian rs = &ni->ni_rates; 358252727Sadrian } 359252727Sadrian 360178354Ssam if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { 361178354Ssam rix = amrr_update(amrr, amn, ni); 362178354Ssam if (rix != amn->amn_rix) { 363178354Ssam /* update public rate */ 364252727Sadrian ni->ni_txrate = rs->rs_rates[rix]; 365252727Sadrian /* XXX strip basic rate flag from txrate, if non-11n */ 366252727Sadrian if (amrr_node_is_11n(ni)) 367252727Sadrian ni->ni_txrate |= IEEE80211_RATE_MCS; 368252727Sadrian else 369252727Sadrian ni->ni_txrate &= IEEE80211_RATE_VAL; 370178354Ssam amn->amn_rix = rix; 371178354Ssam } 372178354Ssam amn->amn_ticks = ticks; 373178354Ssam } else 374178354Ssam rix = amn->amn_rix; 375178354Ssam return rix; 376178354Ssam} 377178354Ssam 378206358Srpaulo/* 379206358Srpaulo * Update statistics with tx complete status. Ok is non-zero 380206358Srpaulo * if the packet is known to be ACK'd. Retries has the number 381206358Srpaulo * retransmissions (i.e. xmit attempts - 1). 382206358Srpaulo */ 383206358Srpaulostatic void 384206358Srpauloamrr_tx_complete(const struct ieee80211vap *vap, 385206358Srpaulo const struct ieee80211_node *ni, int ok, 386206358Srpaulo void *arg1, void *arg2 __unused) 387206358Srpaulo{ 388206358Srpaulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 389206358Srpaulo int retries = *(int *)arg1; 390206358Srpaulo 391343035Savos if (!amn) 392343035Savos return; 393343035Savos 394206358Srpaulo amn->amn_txcnt++; 395206358Srpaulo if (ok) 396206358Srpaulo amn->amn_success++; 397206358Srpaulo amn->amn_retrycnt += retries; 398206358Srpaulo} 399206358Srpaulo 400206358Srpaulo/* 401206358Srpaulo * Set tx count/retry statistics explicitly. Intended for 402206358Srpaulo * drivers that poll the device for statistics maintained 403206358Srpaulo * in the device. 404206358Srpaulo */ 405206358Srpaulostatic void 406206358Srpauloamrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni, 407206358Srpaulo void *arg1, void *arg2, void *arg3) 408206358Srpaulo{ 409206358Srpaulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 410206358Srpaulo int txcnt = *(int *)arg1, success = *(int *)arg2, retrycnt = *(int *)arg3; 411206358Srpaulo 412359746Seugen if (!amn) 413359746Seugen return; 414359746Seugen 415206358Srpaulo amn->amn_txcnt = txcnt; 416206358Srpaulo amn->amn_success = success; 417206358Srpaulo amn->amn_retrycnt = retrycnt; 418206358Srpaulo} 419206358Srpaulo 420164633Ssamstatic int 421178354Ssamamrr_sysctl_interval(SYSCTL_HANDLER_ARGS) 422164633Ssam{ 423206358Srpaulo struct ieee80211vap *vap = arg1; 424206358Srpaulo struct ieee80211_amrr *amrr = vap->iv_rs; 425343035Savos int msecs, error; 426178354Ssam 427343035Savos if (!amrr) 428343035Savos return ENOMEM; 429343035Savos 430343035Savos msecs = ticks_to_msecs(amrr->amrr_interval); 431178354Ssam error = sysctl_handle_int(oidp, &msecs, 0, req); 432178354Ssam if (error || !req->newptr) 433178354Ssam return error; 434206358Srpaulo amrr_setinterval(vap, msecs); 435178354Ssam return 0; 436164633Ssam} 437164633Ssam 438178354Ssamstatic void 439206358Srpauloamrr_sysctlattach(struct ieee80211vap *vap, 440178354Ssam struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) 441178354Ssam{ 442206358Srpaulo struct ieee80211_amrr *amrr = vap->iv_rs; 443178354Ssam 444343035Savos if (!amrr) 445343035Savos return; 446343035Savos 447178354Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 448206358Srpaulo "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap, 449178354Ssam 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); 450178354Ssam /* XXX bounds check values */ 451217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 452178354Ssam "amrr_max_sucess_threshold", CTLFLAG_RW, 453178354Ssam &amrr->amrr_max_success_threshold, 0, ""); 454217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 455178354Ssam "amrr_min_sucess_threshold", CTLFLAG_RW, 456178354Ssam &amrr->amrr_min_success_threshold, 0, ""); 457178354Ssam} 458296925Sadrian 459296925Sadrianstatic void 460296925Sadrianamrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) 461296925Sadrian{ 462296925Sadrian int rate; 463296925Sadrian struct ieee80211_amrr_node *amn = ni->ni_rctls; 464296925Sadrian struct ieee80211_rateset *rs; 465296925Sadrian 466296925Sadrian /* XXX TODO: check locking? */ 467296925Sadrian 468343035Savos if (!amn) 469343035Savos return; 470343035Savos 471296925Sadrian /* XXX TODO: this should be a method */ 472296925Sadrian if (amrr_node_is_11n(ni)) { 473296925Sadrian rs = (struct ieee80211_rateset *) &ni->ni_htrates; 474296925Sadrian rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 475296925Sadrian sbuf_printf(s, "rate: MCS %d\n", rate); 476296925Sadrian } else { 477296925Sadrian rs = &ni->ni_rates; 478296925Sadrian rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 479296925Sadrian sbuf_printf(s, "rate: %d Mbit\n", rate / 2); 480296925Sadrian } 481296925Sadrian 482296925Sadrian sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); 483296925Sadrian sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); 484296925Sadrian sbuf_printf(s, "success: %u\n", amn->amn_success); 485296925Sadrian sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); 486296925Sadrian sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); 487296925Sadrian sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); 488296925Sadrian} 489