amrr.c revision 218160
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 218160 2011-02-01 08:10:18Z 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, 125218160Sadrian uint8_t rix0, uint8_t *rix, uint8_t *try) 126218160Sadrian{ 127218160Sadrian struct amrr_node *amn = ATH_NODE_AMRR(an); 128218160Sadrian 129218160Sadrian/* rix[0] = amn->amn_tx_rate0; */ 130218160Sadrian rix[1] = amn->amn_tx_rate1; 131218160Sadrian rix[2] = amn->amn_tx_rate2; 132218160Sadrian rix[3] = amn->amn_tx_rate3; 133218160Sadrian 134218160Sadrian try[0] = amn->amn_tx_try0; 135218160Sadrian try[1] = amn->amn_tx_try1; 136218160Sadrian try[2] = amn->amn_tx_try2; 137218160Sadrian try[3] = amn->amn_tx_try3; 138218160Sadrian} 139218160Sadrian 140218160Sadrian 141218160Sadrianvoid 142138569Ssamath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, 143144546Ssam struct ath_desc *ds, int shortPreamble, u_int8_t rix) 144138569Ssam{ 145138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 146138569Ssam 147138569Ssam ath_hal_setupxtxdesc(sc->sc_ah, ds 148138569Ssam , amn->amn_tx_rate1sp, amn->amn_tx_try1 /* series 1 */ 149138569Ssam , amn->amn_tx_rate2sp, amn->amn_tx_try2 /* series 2 */ 150138569Ssam , amn->amn_tx_rate3sp, amn->amn_tx_try3 /* series 3 */ 151138569Ssam ); 152138569Ssam} 153138569Ssam 154138569Ssamvoid 155144347Ssamath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, 156165185Ssam const struct ath_buf *bf) 157138569Ssam{ 158138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 159165185Ssam const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; 160165185Ssam int sr = ts->ts_shortretry; 161165185Ssam int lr = ts->ts_longretry; 162138569Ssam int retry_count = sr + lr; 163138569Ssam 164138569Ssam amn->amn_tx_try0_cnt++; 165138569Ssam if (retry_count == 1) { 166138569Ssam amn->amn_tx_try1_cnt++; 167138569Ssam } else if (retry_count == 2) { 168138569Ssam amn->amn_tx_try1_cnt++; 169138569Ssam amn->amn_tx_try2_cnt++; 170138569Ssam } else if (retry_count == 3) { 171138569Ssam amn->amn_tx_try1_cnt++; 172138569Ssam amn->amn_tx_try2_cnt++; 173138569Ssam amn->amn_tx_try3_cnt++; 174138569Ssam } else if (retry_count > 3) { 175138569Ssam amn->amn_tx_try1_cnt++; 176138569Ssam amn->amn_tx_try2_cnt++; 177138569Ssam amn->amn_tx_try3_cnt++; 178138569Ssam amn->amn_tx_failure_cnt++; 179138569Ssam } 180178354Ssam if (amn->amn_interval != 0 && 181178354Ssam ticks - amn->amn_ticks > amn->amn_interval) { 182178354Ssam ath_rate_ctl(sc, &an->an_node); 183178354Ssam amn->amn_ticks = ticks; 184178354Ssam } 185138569Ssam} 186138569Ssam 187138569Ssamvoid 188138569Ssamath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew) 189138569Ssam{ 190138569Ssam if (isnew) 191138569Ssam ath_rate_ctl_start(sc, &an->an_node); 192138569Ssam} 193138569Ssam 194138569Ssamstatic void 195178354Ssamnode_reset(struct amrr_node *amn) 196138569Ssam{ 197138569Ssam amn->amn_tx_try0_cnt = 0; 198138569Ssam amn->amn_tx_try1_cnt = 0; 199138569Ssam amn->amn_tx_try2_cnt = 0; 200138569Ssam amn->amn_tx_try3_cnt = 0; 201138569Ssam amn->amn_tx_failure_cnt = 0; 202138569Ssam amn->amn_success = 0; 203138569Ssam amn->amn_recovery = 0; 204138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 205138569Ssam} 206138569Ssam 207138569Ssam 208138569Ssam/** 209138569Ssam * The code below assumes that we are dealing with hardware multi rate retry 210138569Ssam * I have no idea what will happen if you try to use this module with another 211138569Ssam * type of hardware. Your machine might catch fire or it might work with 212138569Ssam * horrible performance... 213138569Ssam */ 214138569Ssamstatic void 215138569Ssamath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate) 216138569Ssam{ 217138569Ssam struct ath_node *an = ATH_NODE(ni); 218138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 219178354Ssam struct ieee80211vap *vap = ni->ni_vap; 220138569Ssam const HAL_RATE_TABLE *rt = sc->sc_currates; 221138569Ssam u_int8_t rix; 222138569Ssam 223138569Ssam KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 224138569Ssam 225178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni, 226178354Ssam "%s: set xmit rate to %dM", __func__, 227138569Ssam ni->ni_rates.rs_nrates > 0 ? 228138569Ssam (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0); 229138569Ssam 230178354Ssam amn->amn_rix = rate; 231138569Ssam /* 232138569Ssam * Before associating a node has no rate set setup 233138569Ssam * so we can't calculate any transmit codes to use. 234138569Ssam * This is ok since we should never be sending anything 235138569Ssam * but management frames and those always go at the 236138569Ssam * lowest hardware rate. 237138569Ssam */ 238138569Ssam if (ni->ni_rates.rs_nrates > 0) { 239178354Ssam ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL; 240178354Ssam amn->amn_tx_rix0 = sc->sc_rixmap[ni->ni_txrate]; 241138569Ssam amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode; 242138569Ssam amn->amn_tx_rate0sp = amn->amn_tx_rate0 | 243138569Ssam rt->info[amn->amn_tx_rix0].shortPreamble; 244138569Ssam if (sc->sc_mrretry) { 245138569Ssam amn->amn_tx_try0 = 1; 246138569Ssam amn->amn_tx_try1 = 1; 247138569Ssam amn->amn_tx_try2 = 1; 248138569Ssam amn->amn_tx_try3 = 1; 249138569Ssam if (--rate >= 0) { 250138569Ssam rix = sc->sc_rixmap[ 251138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 252138569Ssam amn->amn_tx_rate1 = rt->info[rix].rateCode; 253138569Ssam amn->amn_tx_rate1sp = amn->amn_tx_rate1 | 254138569Ssam rt->info[rix].shortPreamble; 255138569Ssam } else { 256138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 257138569Ssam } 258138569Ssam if (--rate >= 0) { 259138569Ssam rix = sc->sc_rixmap[ 260138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 261138569Ssam amn->amn_tx_rate2 = rt->info[rix].rateCode; 262138569Ssam amn->amn_tx_rate2sp = amn->amn_tx_rate2 | 263138569Ssam rt->info[rix].shortPreamble; 264138569Ssam } else { 265138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 266138569Ssam } 267138569Ssam if (rate > 0) { 268138569Ssam /* NB: only do this if we didn't already do it above */ 269138569Ssam amn->amn_tx_rate3 = rt->info[0].rateCode; 270138569Ssam amn->amn_tx_rate3sp = 271155477Ssam amn->amn_tx_rate3 | rt->info[0].shortPreamble; 272138569Ssam } else { 273138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 274138569Ssam } 275138569Ssam } else { 276138569Ssam amn->amn_tx_try0 = ATH_TXMAXTRY; 277138569Ssam /* theorically, these statements are useless because 278138569Ssam * the code which uses them tests for an_tx_try0 == ATH_TXMAXTRY 279138569Ssam */ 280138569Ssam amn->amn_tx_try1 = 0; 281138569Ssam amn->amn_tx_try2 = 0; 282138569Ssam amn->amn_tx_try3 = 0; 283138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 284138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 285138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 286138569Ssam } 287138569Ssam } 288178354Ssam node_reset(amn); 289178354Ssam 290178354Ssam amn->amn_interval = ath_rateinterval; 291178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 292178354Ssam amn->amn_interval /= 2; 293178354Ssam amn->amn_interval = (amn->amn_interval * hz) / 1000; 294138569Ssam} 295138569Ssam 296138569Ssam/* 297138569Ssam * Set the starting transmit rate for a node. 298138569Ssam */ 299138569Ssamstatic void 300138569Ssamath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni) 301138569Ssam{ 302138569Ssam#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) 303184347Ssam const struct ieee80211_txparam *tp = ni->ni_txparms; 304138569Ssam int srate; 305138569Ssam 306138569Ssam KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates")); 307178354Ssam if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { 308138569Ssam /* 309138569Ssam * No fixed rate is requested. For 11b start with 310138569Ssam * the highest negotiated rate; otherwise, for 11g 311138569Ssam * and 11a, we start "in the middle" at 24Mb or 36Mb. 312138569Ssam */ 313138569Ssam srate = ni->ni_rates.rs_nrates - 1; 314138569Ssam if (sc->sc_curmode != IEEE80211_MODE_11B) { 315138569Ssam /* 316138569Ssam * Scan the negotiated rate set to find the 317138569Ssam * closest rate. 318138569Ssam */ 319138569Ssam /* NB: the rate set is assumed sorted */ 320138569Ssam for (; srate >= 0 && RATE(srate) > 72; srate--) 321138569Ssam ; 322138569Ssam } 323138569Ssam } else { 324138569Ssam /* 325170530Ssam * A fixed rate is to be used; ic_fixed_rate is the 326170530Ssam * IEEE code for this rate (sans basic bit). Convert this 327138569Ssam * to the index into the negotiated rate set for 328138569Ssam * the node. We know the rate is there because the 329138569Ssam * rate set is checked when the station associates. 330138569Ssam */ 331138569Ssam /* NB: the rate set is assumed sorted */ 332138569Ssam srate = ni->ni_rates.rs_nrates - 1; 333178354Ssam for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--) 334138569Ssam ; 335138569Ssam } 336170530Ssam /* 337170530Ssam * The selected rate may not be available due to races 338170530Ssam * and mode settings. Also orphaned nodes created in 339170530Ssam * adhoc mode may not have any rate set so this lookup 340170530Ssam * can fail. This is not fatal. 341170530Ssam */ 342170530Ssam ath_rate_update(sc, ni, srate < 0 ? 0 : srate); 343138569Ssam#undef RATE 344138569Ssam} 345138569Ssam 346138569Ssam/* 347138569Ssam * Examine and potentially adjust the transmit rate. 348138569Ssam */ 349138569Ssamstatic void 350138569Ssamath_rate_ctl(void *arg, struct ieee80211_node *ni) 351138569Ssam{ 352138569Ssam struct ath_softc *sc = arg; 353138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni)); 354178354Ssam int rix; 355138569Ssam 356138569Ssam#define is_success(amn) \ 357138569Ssam(amn->amn_tx_try1_cnt < (amn->amn_tx_try0_cnt/10)) 358138569Ssam#define is_enough(amn) \ 359138569Ssam(amn->amn_tx_try0_cnt > 10) 360138569Ssam#define is_failure(amn) \ 361138569Ssam(amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3)) 362138569Ssam 363178354Ssam rix = amn->amn_rix; 364138569Ssam 365178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 366178354Ssam "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d", 367178354Ssam amn->amn_tx_try0_cnt, amn->amn_tx_try1_cnt, amn->amn_tx_try2_cnt, 368178354Ssam amn->amn_tx_try3_cnt, amn->amn_success_threshold); 369138569Ssam if (is_success (amn) && is_enough (amn)) { 370138569Ssam amn->amn_success++; 371138569Ssam if (amn->amn_success == amn->amn_success_threshold && 372178354Ssam rix + 1 < ni->ni_rates.rs_nrates) { 373138569Ssam amn->amn_recovery = 1; 374138569Ssam amn->amn_success = 0; 375178354Ssam rix++; 376178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 377178354Ssam "increase rate to %d", rix); 378138569Ssam } else { 379138569Ssam amn->amn_recovery = 0; 380138569Ssam } 381138569Ssam } else if (is_failure (amn)) { 382138569Ssam amn->amn_success = 0; 383178354Ssam if (rix > 0) { 384138569Ssam if (amn->amn_recovery) { 385138569Ssam /* recovery failure. */ 386138569Ssam amn->amn_success_threshold *= 2; 387138569Ssam amn->amn_success_threshold = min (amn->amn_success_threshold, 388138569Ssam (u_int)ath_rate_max_success_threshold); 389178354Ssam IEEE80211_NOTE(ni->ni_vap, 390178354Ssam IEEE80211_MSG_RATECTL, ni, 391178354Ssam "decrease rate recovery thr: %d", 392178354Ssam amn->amn_success_threshold); 393138569Ssam } else { 394138569Ssam /* simple failure. */ 395138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 396178354Ssam IEEE80211_NOTE(ni->ni_vap, 397178354Ssam IEEE80211_MSG_RATECTL, ni, 398178354Ssam "decrease rate normal thr: %d", 399178354Ssam amn->amn_success_threshold); 400138569Ssam } 401138569Ssam amn->amn_recovery = 0; 402178354Ssam rix--; 403138569Ssam } else { 404138569Ssam amn->amn_recovery = 0; 405138569Ssam } 406138569Ssam 407138569Ssam } 408178354Ssam if (is_enough (amn) || rix != amn->amn_rix) { 409138569Ssam /* reset counters. */ 410138569Ssam amn->amn_tx_try0_cnt = 0; 411138569Ssam amn->amn_tx_try1_cnt = 0; 412138569Ssam amn->amn_tx_try2_cnt = 0; 413138569Ssam amn->amn_tx_try3_cnt = 0; 414138569Ssam amn->amn_tx_failure_cnt = 0; 415138569Ssam } 416178354Ssam if (rix != amn->amn_rix) { 417178354Ssam ath_rate_update(sc, ni, rix); 418138569Ssam } 419138569Ssam} 420138569Ssam 421138569Ssamstatic void 422138569Ssamath_rate_sysctlattach(struct ath_softc *sc) 423138569Ssam{ 424138569Ssam struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 425138569Ssam struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 426138569Ssam 427138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 428138569Ssam "rate_interval", CTLFLAG_RW, &ath_rateinterval, 0, 429138569Ssam "rate control: operation interval (ms)"); 430138569Ssam /* XXX bounds check values */ 431138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 432138569Ssam "max_sucess_threshold", CTLFLAG_RW, 433138569Ssam &ath_rate_max_success_threshold, 0, ""); 434138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 435138569Ssam "min_sucess_threshold", CTLFLAG_RW, 436138569Ssam &ath_rate_min_success_threshold, 0, ""); 437138569Ssam} 438138569Ssam 439138569Ssamstruct ath_ratectrl * 440138569Ssamath_rate_attach(struct ath_softc *sc) 441138569Ssam{ 442138569Ssam struct amrr_softc *asc; 443138569Ssam 444138569Ssam asc = malloc(sizeof(struct amrr_softc), M_DEVBUF, M_NOWAIT|M_ZERO); 445138569Ssam if (asc == NULL) 446138569Ssam return NULL; 447138569Ssam asc->arc.arc_space = sizeof(struct amrr_node); 448138569Ssam ath_rate_sysctlattach(sc); 449138569Ssam 450138569Ssam return &asc->arc; 451138569Ssam} 452138569Ssam 453138569Ssamvoid 454138569Ssamath_rate_detach(struct ath_ratectrl *arc) 455138569Ssam{ 456138569Ssam struct amrr_softc *asc = (struct amrr_softc *) arc; 457138569Ssam 458138569Ssam free(asc, M_DEVBUF); 459138569Ssam} 460