amrr.c revision 227364
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: head/sys/dev/ath/ath_rate/amrr/amrr.c 227364 2011-11-08 22:43:13Z 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 */ 48138569Ssam#include "opt_inet.h" 49178354Ssam#include "opt_wlan.h" 50138569Ssam 51138569Ssam#include <sys/param.h> 52138569Ssam#include <sys/systm.h> 53138569Ssam#include <sys/sysctl.h> 54138569Ssam#include <sys/kernel.h> 55138569Ssam#include <sys/lock.h> 56138569Ssam#include <sys/mutex.h> 57138569Ssam#include <sys/errno.h> 58138569Ssam 59138569Ssam#include <machine/bus.h> 60138569Ssam#include <machine/resource.h> 61138569Ssam#include <sys/bus.h> 62138569Ssam 63138569Ssam#include <sys/socket.h> 64138569Ssam 65138569Ssam#include <net/if.h> 66138569Ssam#include <net/if_media.h> 67138569Ssam#include <net/if_arp.h> 68138569Ssam 69138569Ssam#include <net80211/ieee80211_var.h> 70138569Ssam 71138569Ssam#include <net/bpf.h> 72138569Ssam 73138569Ssam#ifdef INET 74138569Ssam#include <netinet/in.h> 75138569Ssam#include <netinet/if_ether.h> 76138569Ssam#endif 77138569Ssam 78138569Ssam#include <dev/ath/if_athvar.h> 79138569Ssam#include <dev/ath/ath_rate/amrr/amrr.h> 80185522Ssam#include <dev/ath/ath_hal/ah_desc.h> 81138569Ssam 82138569Ssamstatic int ath_rateinterval = 1000; /* rate ctl interval (ms) */ 83138569Ssamstatic int ath_rate_max_success_threshold = 10; 84138569Ssamstatic int ath_rate_min_success_threshold = 1; 85138569Ssam 86138569Ssamstatic void ath_rate_update(struct ath_softc *, struct ieee80211_node *, 87138569Ssam int rate); 88138569Ssamstatic void ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *); 89138569Ssamstatic void ath_rate_ctl(void *, struct ieee80211_node *); 90138569Ssam 91138569Ssamvoid 92138569Ssamath_rate_node_init(struct ath_softc *sc, struct ath_node *an) 93138569Ssam{ 94138569Ssam /* NB: assumed to be zero'd by caller */ 95138569Ssam} 96138569Ssam 97138569Ssamvoid 98138569Ssamath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an) 99138569Ssam{ 100138569Ssam} 101138569Ssam 102138569Ssamvoid 103138569Ssamath_rate_findrate(struct ath_softc *sc, struct ath_node *an, 104144546Ssam int shortPreamble, size_t frameLen, 105138569Ssam u_int8_t *rix, int *try0, u_int8_t *txrate) 106138569Ssam{ 107138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 108138569Ssam 109138569Ssam *rix = amn->amn_tx_rix0; 110138569Ssam *try0 = amn->amn_tx_try0; 111138569Ssam if (shortPreamble) 112138569Ssam *txrate = amn->amn_tx_rate0sp; 113138569Ssam else 114138569Ssam *txrate = amn->amn_tx_rate0; 115138569Ssam} 116138569Ssam 117218160Sadrian/* 118218160Sadrian * Get the TX rates. 119218160Sadrian * 120218160Sadrian * The short preamble bits aren't set here; the caller should augment 121218160Sadrian * the returned rate with the relevant preamble rate flag. 122218160Sadrian */ 123138569Ssamvoid 124218160Sadrianath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an, 125227364Sadrian uint8_t rix0, struct ath_rc_series *rc) 126218160Sadrian{ 127218160Sadrian struct amrr_node *amn = ATH_NODE_AMRR(an); 128218160Sadrian 129227364Sadrian rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; 130218160Sadrian 131227364Sadrian rc[0].rix = amn->amn_tx_rate0; 132227364Sadrian rc[1].rix = amn->amn_tx_rate1; 133227364Sadrian rc[2].rix = amn->amn_tx_rate2; 134227364Sadrian rc[3].rix = amn->amn_tx_rate3; 135227364Sadrian 136227364Sadrian rc[0].tries = amn->amn_tx_try0; 137227364Sadrian rc[1].tries = amn->amn_tx_try1; 138227364Sadrian rc[2].tries = amn->amn_tx_try2; 139227364Sadrian rc[3].tries = amn->amn_tx_try3; 140218160Sadrian} 141218160Sadrian 142218160Sadrian 143218160Sadrianvoid 144138569Ssamath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, 145144546Ssam struct ath_desc *ds, int shortPreamble, u_int8_t rix) 146138569Ssam{ 147138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 148138569Ssam 149138569Ssam ath_hal_setupxtxdesc(sc->sc_ah, ds 150138569Ssam , amn->amn_tx_rate1sp, amn->amn_tx_try1 /* series 1 */ 151138569Ssam , amn->amn_tx_rate2sp, amn->amn_tx_try2 /* series 2 */ 152138569Ssam , amn->amn_tx_rate3sp, amn->amn_tx_try3 /* series 3 */ 153138569Ssam ); 154138569Ssam} 155138569Ssam 156138569Ssamvoid 157144347Ssamath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, 158227364Sadrian const struct ath_rc_series *rc, const struct ath_tx_status *ts, 159227364Sadrian int frame_size, int nframes, int nbad) 160138569Ssam{ 161138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 162165185Ssam int sr = ts->ts_shortretry; 163165185Ssam int lr = ts->ts_longretry; 164138569Ssam int retry_count = sr + lr; 165138569Ssam 166138569Ssam amn->amn_tx_try0_cnt++; 167138569Ssam if (retry_count == 1) { 168138569Ssam amn->amn_tx_try1_cnt++; 169138569Ssam } else if (retry_count == 2) { 170138569Ssam amn->amn_tx_try1_cnt++; 171138569Ssam amn->amn_tx_try2_cnt++; 172138569Ssam } else if (retry_count == 3) { 173138569Ssam amn->amn_tx_try1_cnt++; 174138569Ssam amn->amn_tx_try2_cnt++; 175138569Ssam amn->amn_tx_try3_cnt++; 176138569Ssam } else if (retry_count > 3) { 177138569Ssam amn->amn_tx_try1_cnt++; 178138569Ssam amn->amn_tx_try2_cnt++; 179138569Ssam amn->amn_tx_try3_cnt++; 180138569Ssam amn->amn_tx_failure_cnt++; 181138569Ssam } 182178354Ssam if (amn->amn_interval != 0 && 183178354Ssam ticks - amn->amn_ticks > amn->amn_interval) { 184178354Ssam ath_rate_ctl(sc, &an->an_node); 185178354Ssam amn->amn_ticks = ticks; 186178354Ssam } 187138569Ssam} 188138569Ssam 189138569Ssamvoid 190138569Ssamath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew) 191138569Ssam{ 192138569Ssam if (isnew) 193138569Ssam ath_rate_ctl_start(sc, &an->an_node); 194138569Ssam} 195138569Ssam 196138569Ssamstatic void 197178354Ssamnode_reset(struct amrr_node *amn) 198138569Ssam{ 199138569Ssam amn->amn_tx_try0_cnt = 0; 200138569Ssam amn->amn_tx_try1_cnt = 0; 201138569Ssam amn->amn_tx_try2_cnt = 0; 202138569Ssam amn->amn_tx_try3_cnt = 0; 203138569Ssam amn->amn_tx_failure_cnt = 0; 204138569Ssam amn->amn_success = 0; 205138569Ssam amn->amn_recovery = 0; 206138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 207138569Ssam} 208138569Ssam 209138569Ssam 210138569Ssam/** 211138569Ssam * The code below assumes that we are dealing with hardware multi rate retry 212138569Ssam * I have no idea what will happen if you try to use this module with another 213138569Ssam * type of hardware. Your machine might catch fire or it might work with 214138569Ssam * horrible performance... 215138569Ssam */ 216138569Ssamstatic void 217138569Ssamath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate) 218138569Ssam{ 219138569Ssam struct ath_node *an = ATH_NODE(ni); 220138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 221178354Ssam struct ieee80211vap *vap = ni->ni_vap; 222138569Ssam const HAL_RATE_TABLE *rt = sc->sc_currates; 223138569Ssam u_int8_t rix; 224138569Ssam 225138569Ssam KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 226138569Ssam 227178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni, 228178354Ssam "%s: set xmit rate to %dM", __func__, 229138569Ssam ni->ni_rates.rs_nrates > 0 ? 230138569Ssam (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0); 231138569Ssam 232178354Ssam amn->amn_rix = rate; 233138569Ssam /* 234138569Ssam * Before associating a node has no rate set setup 235138569Ssam * so we can't calculate any transmit codes to use. 236138569Ssam * This is ok since we should never be sending anything 237138569Ssam * but management frames and those always go at the 238138569Ssam * lowest hardware rate. 239138569Ssam */ 240138569Ssam if (ni->ni_rates.rs_nrates > 0) { 241178354Ssam ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL; 242178354Ssam amn->amn_tx_rix0 = sc->sc_rixmap[ni->ni_txrate]; 243138569Ssam amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode; 244138569Ssam amn->amn_tx_rate0sp = amn->amn_tx_rate0 | 245138569Ssam rt->info[amn->amn_tx_rix0].shortPreamble; 246138569Ssam if (sc->sc_mrretry) { 247138569Ssam amn->amn_tx_try0 = 1; 248138569Ssam amn->amn_tx_try1 = 1; 249138569Ssam amn->amn_tx_try2 = 1; 250138569Ssam amn->amn_tx_try3 = 1; 251138569Ssam if (--rate >= 0) { 252138569Ssam rix = sc->sc_rixmap[ 253138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 254138569Ssam amn->amn_tx_rate1 = rt->info[rix].rateCode; 255138569Ssam amn->amn_tx_rate1sp = amn->amn_tx_rate1 | 256138569Ssam rt->info[rix].shortPreamble; 257138569Ssam } else { 258138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 259138569Ssam } 260138569Ssam if (--rate >= 0) { 261138569Ssam rix = sc->sc_rixmap[ 262138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 263138569Ssam amn->amn_tx_rate2 = rt->info[rix].rateCode; 264138569Ssam amn->amn_tx_rate2sp = amn->amn_tx_rate2 | 265138569Ssam rt->info[rix].shortPreamble; 266138569Ssam } else { 267138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 268138569Ssam } 269138569Ssam if (rate > 0) { 270138569Ssam /* NB: only do this if we didn't already do it above */ 271138569Ssam amn->amn_tx_rate3 = rt->info[0].rateCode; 272138569Ssam amn->amn_tx_rate3sp = 273155477Ssam amn->amn_tx_rate3 | rt->info[0].shortPreamble; 274138569Ssam } else { 275138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 276138569Ssam } 277138569Ssam } else { 278138569Ssam amn->amn_tx_try0 = ATH_TXMAXTRY; 279138569Ssam /* theorically, these statements are useless because 280138569Ssam * the code which uses them tests for an_tx_try0 == ATH_TXMAXTRY 281138569Ssam */ 282138569Ssam amn->amn_tx_try1 = 0; 283138569Ssam amn->amn_tx_try2 = 0; 284138569Ssam amn->amn_tx_try3 = 0; 285138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 286138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 287138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 288138569Ssam } 289138569Ssam } 290178354Ssam node_reset(amn); 291178354Ssam 292178354Ssam amn->amn_interval = ath_rateinterval; 293178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 294178354Ssam amn->amn_interval /= 2; 295178354Ssam amn->amn_interval = (amn->amn_interval * hz) / 1000; 296138569Ssam} 297138569Ssam 298138569Ssam/* 299138569Ssam * Set the starting transmit rate for a node. 300138569Ssam */ 301138569Ssamstatic void 302138569Ssamath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni) 303138569Ssam{ 304138569Ssam#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) 305184347Ssam const struct ieee80211_txparam *tp = ni->ni_txparms; 306138569Ssam int srate; 307138569Ssam 308138569Ssam KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates")); 309178354Ssam if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { 310138569Ssam /* 311138569Ssam * No fixed rate is requested. For 11b start with 312138569Ssam * the highest negotiated rate; otherwise, for 11g 313138569Ssam * and 11a, we start "in the middle" at 24Mb or 36Mb. 314138569Ssam */ 315138569Ssam srate = ni->ni_rates.rs_nrates - 1; 316138569Ssam if (sc->sc_curmode != IEEE80211_MODE_11B) { 317138569Ssam /* 318138569Ssam * Scan the negotiated rate set to find the 319138569Ssam * closest rate. 320138569Ssam */ 321138569Ssam /* NB: the rate set is assumed sorted */ 322138569Ssam for (; srate >= 0 && RATE(srate) > 72; srate--) 323138569Ssam ; 324138569Ssam } 325138569Ssam } else { 326138569Ssam /* 327170530Ssam * A fixed rate is to be used; ic_fixed_rate is the 328170530Ssam * IEEE code for this rate (sans basic bit). Convert this 329138569Ssam * to the index into the negotiated rate set for 330138569Ssam * the node. We know the rate is there because the 331138569Ssam * rate set is checked when the station associates. 332138569Ssam */ 333138569Ssam /* NB: the rate set is assumed sorted */ 334138569Ssam srate = ni->ni_rates.rs_nrates - 1; 335178354Ssam for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--) 336138569Ssam ; 337138569Ssam } 338170530Ssam /* 339170530Ssam * The selected rate may not be available due to races 340170530Ssam * and mode settings. Also orphaned nodes created in 341170530Ssam * adhoc mode may not have any rate set so this lookup 342170530Ssam * can fail. This is not fatal. 343170530Ssam */ 344170530Ssam ath_rate_update(sc, ni, srate < 0 ? 0 : srate); 345138569Ssam#undef RATE 346138569Ssam} 347138569Ssam 348138569Ssam/* 349138569Ssam * Examine and potentially adjust the transmit rate. 350138569Ssam */ 351138569Ssamstatic void 352138569Ssamath_rate_ctl(void *arg, struct ieee80211_node *ni) 353138569Ssam{ 354138569Ssam struct ath_softc *sc = arg; 355138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni)); 356178354Ssam int rix; 357138569Ssam 358138569Ssam#define is_success(amn) \ 359138569Ssam(amn->amn_tx_try1_cnt < (amn->amn_tx_try0_cnt/10)) 360138569Ssam#define is_enough(amn) \ 361138569Ssam(amn->amn_tx_try0_cnt > 10) 362138569Ssam#define is_failure(amn) \ 363138569Ssam(amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3)) 364138569Ssam 365178354Ssam rix = amn->amn_rix; 366138569Ssam 367178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 368178354Ssam "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d", 369178354Ssam amn->amn_tx_try0_cnt, amn->amn_tx_try1_cnt, amn->amn_tx_try2_cnt, 370178354Ssam amn->amn_tx_try3_cnt, amn->amn_success_threshold); 371138569Ssam if (is_success (amn) && is_enough (amn)) { 372138569Ssam amn->amn_success++; 373138569Ssam if (amn->amn_success == amn->amn_success_threshold && 374178354Ssam rix + 1 < ni->ni_rates.rs_nrates) { 375138569Ssam amn->amn_recovery = 1; 376138569Ssam amn->amn_success = 0; 377178354Ssam rix++; 378178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 379178354Ssam "increase rate to %d", rix); 380138569Ssam } else { 381138569Ssam amn->amn_recovery = 0; 382138569Ssam } 383138569Ssam } else if (is_failure (amn)) { 384138569Ssam amn->amn_success = 0; 385178354Ssam if (rix > 0) { 386138569Ssam if (amn->amn_recovery) { 387138569Ssam /* recovery failure. */ 388138569Ssam amn->amn_success_threshold *= 2; 389138569Ssam amn->amn_success_threshold = min (amn->amn_success_threshold, 390138569Ssam (u_int)ath_rate_max_success_threshold); 391178354Ssam IEEE80211_NOTE(ni->ni_vap, 392178354Ssam IEEE80211_MSG_RATECTL, ni, 393178354Ssam "decrease rate recovery thr: %d", 394178354Ssam amn->amn_success_threshold); 395138569Ssam } else { 396138569Ssam /* simple failure. */ 397138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 398178354Ssam IEEE80211_NOTE(ni->ni_vap, 399178354Ssam IEEE80211_MSG_RATECTL, ni, 400178354Ssam "decrease rate normal thr: %d", 401178354Ssam amn->amn_success_threshold); 402138569Ssam } 403138569Ssam amn->amn_recovery = 0; 404178354Ssam rix--; 405138569Ssam } else { 406138569Ssam amn->amn_recovery = 0; 407138569Ssam } 408138569Ssam 409138569Ssam } 410178354Ssam if (is_enough (amn) || rix != amn->amn_rix) { 411138569Ssam /* reset counters. */ 412138569Ssam amn->amn_tx_try0_cnt = 0; 413138569Ssam amn->amn_tx_try1_cnt = 0; 414138569Ssam amn->amn_tx_try2_cnt = 0; 415138569Ssam amn->amn_tx_try3_cnt = 0; 416138569Ssam amn->amn_tx_failure_cnt = 0; 417138569Ssam } 418178354Ssam if (rix != amn->amn_rix) { 419178354Ssam ath_rate_update(sc, ni, rix); 420138569Ssam } 421138569Ssam} 422138569Ssam 423138569Ssamstatic void 424138569Ssamath_rate_sysctlattach(struct ath_softc *sc) 425138569Ssam{ 426138569Ssam struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 427138569Ssam struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 428138569Ssam 429138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 430138569Ssam "rate_interval", CTLFLAG_RW, &ath_rateinterval, 0, 431138569Ssam "rate control: operation interval (ms)"); 432138569Ssam /* XXX bounds check values */ 433138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 434138569Ssam "max_sucess_threshold", CTLFLAG_RW, 435138569Ssam &ath_rate_max_success_threshold, 0, ""); 436138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 437138569Ssam "min_sucess_threshold", CTLFLAG_RW, 438138569Ssam &ath_rate_min_success_threshold, 0, ""); 439138569Ssam} 440138569Ssam 441138569Ssamstruct ath_ratectrl * 442138569Ssamath_rate_attach(struct ath_softc *sc) 443138569Ssam{ 444138569Ssam struct amrr_softc *asc; 445138569Ssam 446138569Ssam asc = malloc(sizeof(struct amrr_softc), M_DEVBUF, M_NOWAIT|M_ZERO); 447138569Ssam if (asc == NULL) 448138569Ssam return NULL; 449138569Ssam asc->arc.arc_space = sizeof(struct amrr_node); 450138569Ssam ath_rate_sysctlattach(sc); 451138569Ssam 452138569Ssam return &asc->arc; 453138569Ssam} 454138569Ssam 455138569Ssamvoid 456138569Ssamath_rate_detach(struct ath_ratectrl *arc) 457138569Ssam{ 458138569Ssam struct amrr_softc *asc = (struct amrr_softc *) arc; 459138569Ssam 460138569Ssam free(asc, M_DEVBUF); 461138569Ssam} 462