1138569Ssam/*- 2138569Ssam * Copyright (c) 2004 INRIA 3139530Ssam * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting 4138569Ssam * All rights reserved. 5138569Ssam * 6138569Ssam * Redistribution and use in source and binary forms, with or without 7138569Ssam * modification, are permitted provided that the following conditions 8138569Ssam * are met: 9138569Ssam * 1. Redistributions of source code must retain the above copyright 10138569Ssam * notice, this list of conditions and the following disclaimer, 11138569Ssam * without modification. 12138569Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer 13138569Ssam * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 14138569Ssam * redistribution must be conditioned upon including a substantially 15138569Ssam * similar Disclaimer requirement for further binary redistribution. 16138569Ssam * 3. Neither the names of the above-listed copyright holders nor the names 17138569Ssam * of any contributors may be used to endorse or promote products derived 18138569Ssam * from this software without specific prior written permission. 19138569Ssam * 20138569Ssam * Alternatively, this software may be distributed under the terms of the 21138569Ssam * GNU General Public License ("GPL") version 2 as published by the Free 22138569Ssam * Software Foundation. 23138569Ssam * 24138569Ssam * NO WARRANTY 25138569Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26138569Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27138569Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 28138569Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 29138569Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 30138569Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31138569Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32138569Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 33138569Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34138569Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 35138569Ssam * THE POSSIBILITY OF SUCH DAMAGES. 36138569Ssam * 37138569Ssam */ 38138569Ssam 39138569Ssam#include <sys/cdefs.h> 40138569Ssam__FBSDID("$FreeBSD: releng/10.2/sys/dev/ath/ath_rate/amrr/amrr.c 238633 2012-07-20 01:36:02Z adrian $"); 41138569Ssam 42138569Ssam/* 43138569Ssam * AMRR rate control. See: 44138569Ssam * http://www-sop.inria.fr/rapports/sophia/RR-5208.html 45138569Ssam * "IEEE 802.11 Rate Adaptation: A Practical Approach" by 46138569Ssam * Mathieu Lacage, Hossein Manshaei, Thierry Turletti 47138569Ssam */ 48237529Sadrian#include "opt_ath.h" 49138569Ssam#include "opt_inet.h" 50178354Ssam#include "opt_wlan.h" 51138569Ssam 52138569Ssam#include <sys/param.h> 53138569Ssam#include <sys/systm.h> 54138569Ssam#include <sys/sysctl.h> 55138569Ssam#include <sys/kernel.h> 56138569Ssam#include <sys/lock.h> 57138569Ssam#include <sys/mutex.h> 58138569Ssam#include <sys/errno.h> 59138569Ssam 60138569Ssam#include <machine/bus.h> 61138569Ssam#include <machine/resource.h> 62138569Ssam#include <sys/bus.h> 63138569Ssam 64138569Ssam#include <sys/socket.h> 65138569Ssam 66138569Ssam#include <net/if.h> 67138569Ssam#include <net/if_media.h> 68138569Ssam#include <net/if_arp.h> 69138569Ssam 70138569Ssam#include <net80211/ieee80211_var.h> 71138569Ssam 72138569Ssam#include <net/bpf.h> 73138569Ssam 74138569Ssam#ifdef INET 75138569Ssam#include <netinet/in.h> 76138569Ssam#include <netinet/if_ether.h> 77138569Ssam#endif 78138569Ssam 79138569Ssam#include <dev/ath/if_athvar.h> 80138569Ssam#include <dev/ath/ath_rate/amrr/amrr.h> 81185522Ssam#include <dev/ath/ath_hal/ah_desc.h> 82138569Ssam 83138569Ssamstatic int ath_rateinterval = 1000; /* rate ctl interval (ms) */ 84138569Ssamstatic int ath_rate_max_success_threshold = 10; 85138569Ssamstatic int ath_rate_min_success_threshold = 1; 86138569Ssam 87138569Ssamstatic void ath_rate_update(struct ath_softc *, struct ieee80211_node *, 88138569Ssam int rate); 89138569Ssamstatic void ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *); 90138569Ssamstatic void ath_rate_ctl(void *, struct ieee80211_node *); 91138569Ssam 92138569Ssamvoid 93138569Ssamath_rate_node_init(struct ath_softc *sc, struct ath_node *an) 94138569Ssam{ 95138569Ssam /* NB: assumed to be zero'd by caller */ 96138569Ssam} 97138569Ssam 98138569Ssamvoid 99138569Ssamath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an) 100138569Ssam{ 101138569Ssam} 102138569Ssam 103138569Ssamvoid 104138569Ssamath_rate_findrate(struct ath_softc *sc, struct ath_node *an, 105144546Ssam int shortPreamble, size_t frameLen, 106138569Ssam u_int8_t *rix, int *try0, u_int8_t *txrate) 107138569Ssam{ 108138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 109138569Ssam 110138569Ssam *rix = amn->amn_tx_rix0; 111138569Ssam *try0 = amn->amn_tx_try0; 112138569Ssam if (shortPreamble) 113138569Ssam *txrate = amn->amn_tx_rate0sp; 114138569Ssam else 115138569Ssam *txrate = amn->amn_tx_rate0; 116138569Ssam} 117138569Ssam 118218160Sadrian/* 119218160Sadrian * Get the TX rates. 120218160Sadrian * 121218160Sadrian * The short preamble bits aren't set here; the caller should augment 122218160Sadrian * the returned rate with the relevant preamble rate flag. 123218160Sadrian */ 124138569Ssamvoid 125218160Sadrianath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an, 126227364Sadrian uint8_t rix0, struct ath_rc_series *rc) 127218160Sadrian{ 128218160Sadrian struct amrr_node *amn = ATH_NODE_AMRR(an); 129218160Sadrian 130227364Sadrian rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; 131218160Sadrian 132227364Sadrian rc[0].rix = amn->amn_tx_rate0; 133227364Sadrian rc[1].rix = amn->amn_tx_rate1; 134227364Sadrian rc[2].rix = amn->amn_tx_rate2; 135227364Sadrian rc[3].rix = amn->amn_tx_rate3; 136227364Sadrian 137227364Sadrian rc[0].tries = amn->amn_tx_try0; 138227364Sadrian rc[1].tries = amn->amn_tx_try1; 139227364Sadrian rc[2].tries = amn->amn_tx_try2; 140227364Sadrian rc[3].tries = amn->amn_tx_try3; 141218160Sadrian} 142218160Sadrian 143218160Sadrian 144218160Sadrianvoid 145138569Ssamath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, 146144546Ssam struct ath_desc *ds, int shortPreamble, u_int8_t rix) 147138569Ssam{ 148138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 149138569Ssam 150138569Ssam ath_hal_setupxtxdesc(sc->sc_ah, ds 151138569Ssam , amn->amn_tx_rate1sp, amn->amn_tx_try1 /* series 1 */ 152138569Ssam , amn->amn_tx_rate2sp, amn->amn_tx_try2 /* series 2 */ 153138569Ssam , amn->amn_tx_rate3sp, amn->amn_tx_try3 /* series 3 */ 154138569Ssam ); 155138569Ssam} 156138569Ssam 157138569Ssamvoid 158144347Ssamath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, 159227364Sadrian const struct ath_rc_series *rc, const struct ath_tx_status *ts, 160227364Sadrian int frame_size, int nframes, int nbad) 161138569Ssam{ 162138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 163165185Ssam int sr = ts->ts_shortretry; 164165185Ssam int lr = ts->ts_longretry; 165138569Ssam int retry_count = sr + lr; 166138569Ssam 167138569Ssam amn->amn_tx_try0_cnt++; 168138569Ssam if (retry_count == 1) { 169138569Ssam amn->amn_tx_try1_cnt++; 170138569Ssam } else if (retry_count == 2) { 171138569Ssam amn->amn_tx_try1_cnt++; 172138569Ssam amn->amn_tx_try2_cnt++; 173138569Ssam } else if (retry_count == 3) { 174138569Ssam amn->amn_tx_try1_cnt++; 175138569Ssam amn->amn_tx_try2_cnt++; 176138569Ssam amn->amn_tx_try3_cnt++; 177138569Ssam } else if (retry_count > 3) { 178138569Ssam amn->amn_tx_try1_cnt++; 179138569Ssam amn->amn_tx_try2_cnt++; 180138569Ssam amn->amn_tx_try3_cnt++; 181138569Ssam amn->amn_tx_failure_cnt++; 182138569Ssam } 183178354Ssam if (amn->amn_interval != 0 && 184178354Ssam ticks - amn->amn_ticks > amn->amn_interval) { 185178354Ssam ath_rate_ctl(sc, &an->an_node); 186178354Ssam amn->amn_ticks = ticks; 187178354Ssam } 188138569Ssam} 189138569Ssam 190138569Ssamvoid 191138569Ssamath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew) 192138569Ssam{ 193138569Ssam if (isnew) 194138569Ssam ath_rate_ctl_start(sc, &an->an_node); 195138569Ssam} 196138569Ssam 197138569Ssamstatic void 198178354Ssamnode_reset(struct amrr_node *amn) 199138569Ssam{ 200138569Ssam amn->amn_tx_try0_cnt = 0; 201138569Ssam amn->amn_tx_try1_cnt = 0; 202138569Ssam amn->amn_tx_try2_cnt = 0; 203138569Ssam amn->amn_tx_try3_cnt = 0; 204138569Ssam amn->amn_tx_failure_cnt = 0; 205138569Ssam amn->amn_success = 0; 206138569Ssam amn->amn_recovery = 0; 207138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 208138569Ssam} 209138569Ssam 210138569Ssam 211138569Ssam/** 212138569Ssam * The code below assumes that we are dealing with hardware multi rate retry 213138569Ssam * I have no idea what will happen if you try to use this module with another 214138569Ssam * type of hardware. Your machine might catch fire or it might work with 215138569Ssam * horrible performance... 216138569Ssam */ 217138569Ssamstatic void 218138569Ssamath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate) 219138569Ssam{ 220138569Ssam struct ath_node *an = ATH_NODE(ni); 221138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 222178354Ssam struct ieee80211vap *vap = ni->ni_vap; 223138569Ssam const HAL_RATE_TABLE *rt = sc->sc_currates; 224138569Ssam u_int8_t rix; 225138569Ssam 226138569Ssam KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 227138569Ssam 228178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni, 229178354Ssam "%s: set xmit rate to %dM", __func__, 230138569Ssam ni->ni_rates.rs_nrates > 0 ? 231138569Ssam (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0); 232138569Ssam 233178354Ssam amn->amn_rix = rate; 234138569Ssam /* 235138569Ssam * Before associating a node has no rate set setup 236138569Ssam * so we can't calculate any transmit codes to use. 237138569Ssam * This is ok since we should never be sending anything 238138569Ssam * but management frames and those always go at the 239138569Ssam * lowest hardware rate. 240138569Ssam */ 241138569Ssam if (ni->ni_rates.rs_nrates > 0) { 242178354Ssam ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL; 243178354Ssam amn->amn_tx_rix0 = sc->sc_rixmap[ni->ni_txrate]; 244138569Ssam amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode; 245138569Ssam amn->amn_tx_rate0sp = amn->amn_tx_rate0 | 246138569Ssam rt->info[amn->amn_tx_rix0].shortPreamble; 247138569Ssam if (sc->sc_mrretry) { 248138569Ssam amn->amn_tx_try0 = 1; 249138569Ssam amn->amn_tx_try1 = 1; 250138569Ssam amn->amn_tx_try2 = 1; 251138569Ssam amn->amn_tx_try3 = 1; 252138569Ssam if (--rate >= 0) { 253138569Ssam rix = sc->sc_rixmap[ 254138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 255138569Ssam amn->amn_tx_rate1 = rt->info[rix].rateCode; 256138569Ssam amn->amn_tx_rate1sp = amn->amn_tx_rate1 | 257138569Ssam rt->info[rix].shortPreamble; 258138569Ssam } else { 259138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 260138569Ssam } 261138569Ssam if (--rate >= 0) { 262138569Ssam rix = sc->sc_rixmap[ 263138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 264138569Ssam amn->amn_tx_rate2 = rt->info[rix].rateCode; 265138569Ssam amn->amn_tx_rate2sp = amn->amn_tx_rate2 | 266138569Ssam rt->info[rix].shortPreamble; 267138569Ssam } else { 268138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 269138569Ssam } 270138569Ssam if (rate > 0) { 271138569Ssam /* NB: only do this if we didn't already do it above */ 272138569Ssam amn->amn_tx_rate3 = rt->info[0].rateCode; 273138569Ssam amn->amn_tx_rate3sp = 274155477Ssam amn->amn_tx_rate3 | rt->info[0].shortPreamble; 275138569Ssam } else { 276138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 277138569Ssam } 278138569Ssam } else { 279138569Ssam amn->amn_tx_try0 = ATH_TXMAXTRY; 280138569Ssam /* theorically, these statements are useless because 281138569Ssam * the code which uses them tests for an_tx_try0 == ATH_TXMAXTRY 282138569Ssam */ 283138569Ssam amn->amn_tx_try1 = 0; 284138569Ssam amn->amn_tx_try2 = 0; 285138569Ssam amn->amn_tx_try3 = 0; 286138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 287138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 288138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 289138569Ssam } 290138569Ssam } 291178354Ssam node_reset(amn); 292178354Ssam 293178354Ssam amn->amn_interval = ath_rateinterval; 294178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 295178354Ssam amn->amn_interval /= 2; 296178354Ssam amn->amn_interval = (amn->amn_interval * hz) / 1000; 297138569Ssam} 298138569Ssam 299138569Ssam/* 300138569Ssam * Set the starting transmit rate for a node. 301138569Ssam */ 302138569Ssamstatic void 303138569Ssamath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni) 304138569Ssam{ 305138569Ssam#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) 306184347Ssam const struct ieee80211_txparam *tp = ni->ni_txparms; 307138569Ssam int srate; 308138569Ssam 309138569Ssam KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates")); 310178354Ssam if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { 311138569Ssam /* 312138569Ssam * No fixed rate is requested. For 11b start with 313138569Ssam * the highest negotiated rate; otherwise, for 11g 314138569Ssam * and 11a, we start "in the middle" at 24Mb or 36Mb. 315138569Ssam */ 316138569Ssam srate = ni->ni_rates.rs_nrates - 1; 317138569Ssam if (sc->sc_curmode != IEEE80211_MODE_11B) { 318138569Ssam /* 319138569Ssam * Scan the negotiated rate set to find the 320138569Ssam * closest rate. 321138569Ssam */ 322138569Ssam /* NB: the rate set is assumed sorted */ 323138569Ssam for (; srate >= 0 && RATE(srate) > 72; srate--) 324138569Ssam ; 325138569Ssam } 326138569Ssam } else { 327138569Ssam /* 328170530Ssam * A fixed rate is to be used; ic_fixed_rate is the 329170530Ssam * IEEE code for this rate (sans basic bit). Convert this 330138569Ssam * to the index into the negotiated rate set for 331138569Ssam * the node. We know the rate is there because the 332138569Ssam * rate set is checked when the station associates. 333138569Ssam */ 334138569Ssam /* NB: the rate set is assumed sorted */ 335138569Ssam srate = ni->ni_rates.rs_nrates - 1; 336178354Ssam for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--) 337138569Ssam ; 338138569Ssam } 339170530Ssam /* 340170530Ssam * The selected rate may not be available due to races 341170530Ssam * and mode settings. Also orphaned nodes created in 342170530Ssam * adhoc mode may not have any rate set so this lookup 343170530Ssam * can fail. This is not fatal. 344170530Ssam */ 345170530Ssam ath_rate_update(sc, ni, srate < 0 ? 0 : srate); 346138569Ssam#undef RATE 347138569Ssam} 348138569Ssam 349138569Ssam/* 350138569Ssam * Examine and potentially adjust the transmit rate. 351138569Ssam */ 352138569Ssamstatic void 353138569Ssamath_rate_ctl(void *arg, struct ieee80211_node *ni) 354138569Ssam{ 355138569Ssam struct ath_softc *sc = arg; 356138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni)); 357178354Ssam int rix; 358138569Ssam 359138569Ssam#define is_success(amn) \ 360138569Ssam(amn->amn_tx_try1_cnt < (amn->amn_tx_try0_cnt/10)) 361138569Ssam#define is_enough(amn) \ 362138569Ssam(amn->amn_tx_try0_cnt > 10) 363138569Ssam#define is_failure(amn) \ 364138569Ssam(amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3)) 365138569Ssam 366178354Ssam rix = amn->amn_rix; 367138569Ssam 368178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 369178354Ssam "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d", 370178354Ssam amn->amn_tx_try0_cnt, amn->amn_tx_try1_cnt, amn->amn_tx_try2_cnt, 371178354Ssam amn->amn_tx_try3_cnt, amn->amn_success_threshold); 372138569Ssam if (is_success (amn) && is_enough (amn)) { 373138569Ssam amn->amn_success++; 374138569Ssam if (amn->amn_success == amn->amn_success_threshold && 375178354Ssam rix + 1 < ni->ni_rates.rs_nrates) { 376138569Ssam amn->amn_recovery = 1; 377138569Ssam amn->amn_success = 0; 378178354Ssam rix++; 379178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 380178354Ssam "increase rate to %d", rix); 381138569Ssam } else { 382138569Ssam amn->amn_recovery = 0; 383138569Ssam } 384138569Ssam } else if (is_failure (amn)) { 385138569Ssam amn->amn_success = 0; 386178354Ssam if (rix > 0) { 387138569Ssam if (amn->amn_recovery) { 388138569Ssam /* recovery failure. */ 389138569Ssam amn->amn_success_threshold *= 2; 390138569Ssam amn->amn_success_threshold = min (amn->amn_success_threshold, 391138569Ssam (u_int)ath_rate_max_success_threshold); 392178354Ssam IEEE80211_NOTE(ni->ni_vap, 393178354Ssam IEEE80211_MSG_RATECTL, ni, 394178354Ssam "decrease rate recovery thr: %d", 395178354Ssam amn->amn_success_threshold); 396138569Ssam } else { 397138569Ssam /* simple failure. */ 398138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 399178354Ssam IEEE80211_NOTE(ni->ni_vap, 400178354Ssam IEEE80211_MSG_RATECTL, ni, 401178354Ssam "decrease rate normal thr: %d", 402178354Ssam amn->amn_success_threshold); 403138569Ssam } 404138569Ssam amn->amn_recovery = 0; 405178354Ssam rix--; 406138569Ssam } else { 407138569Ssam amn->amn_recovery = 0; 408138569Ssam } 409138569Ssam 410138569Ssam } 411178354Ssam if (is_enough (amn) || rix != amn->amn_rix) { 412138569Ssam /* reset counters. */ 413138569Ssam amn->amn_tx_try0_cnt = 0; 414138569Ssam amn->amn_tx_try1_cnt = 0; 415138569Ssam amn->amn_tx_try2_cnt = 0; 416138569Ssam amn->amn_tx_try3_cnt = 0; 417138569Ssam amn->amn_tx_failure_cnt = 0; 418138569Ssam } 419178354Ssam if (rix != amn->amn_rix) { 420178354Ssam ath_rate_update(sc, ni, rix); 421138569Ssam } 422138569Ssam} 423138569Ssam 424238633Sadrianstatic int 425238633Sadrianath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, 426238633Sadrian struct ath_rateioctl *re) 427238633Sadrian{ 428238633Sadrian 429238633Sadrian return (EINVAL); 430238633Sadrian} 431238633Sadrian 432138569Ssamstatic void 433138569Ssamath_rate_sysctlattach(struct ath_softc *sc) 434138569Ssam{ 435138569Ssam struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 436138569Ssam struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 437138569Ssam 438138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 439138569Ssam "rate_interval", CTLFLAG_RW, &ath_rateinterval, 0, 440138569Ssam "rate control: operation interval (ms)"); 441138569Ssam /* XXX bounds check values */ 442138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 443138569Ssam "max_sucess_threshold", CTLFLAG_RW, 444138569Ssam &ath_rate_max_success_threshold, 0, ""); 445138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 446138569Ssam "min_sucess_threshold", CTLFLAG_RW, 447138569Ssam &ath_rate_min_success_threshold, 0, ""); 448138569Ssam} 449138569Ssam 450138569Ssamstruct ath_ratectrl * 451138569Ssamath_rate_attach(struct ath_softc *sc) 452138569Ssam{ 453138569Ssam struct amrr_softc *asc; 454138569Ssam 455138569Ssam asc = malloc(sizeof(struct amrr_softc), M_DEVBUF, M_NOWAIT|M_ZERO); 456138569Ssam if (asc == NULL) 457138569Ssam return NULL; 458138569Ssam asc->arc.arc_space = sizeof(struct amrr_node); 459138569Ssam ath_rate_sysctlattach(sc); 460138569Ssam 461138569Ssam return &asc->arc; 462138569Ssam} 463138569Ssam 464138569Ssamvoid 465138569Ssamath_rate_detach(struct ath_ratectrl *arc) 466138569Ssam{ 467138569Ssam struct amrr_softc *asc = (struct amrr_softc *) arc; 468138569Ssam 469138569Ssam free(asc, M_DEVBUF); 470138569Ssam} 471