amrr.c revision 184347
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 184347 2008-10-27 17:03:24Z sam $"); 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/module.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> 81138569Ssam#include <contrib/dev/ath/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 118138569Ssamvoid 119138569Ssamath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, 120144546Ssam struct ath_desc *ds, int shortPreamble, u_int8_t rix) 121138569Ssam{ 122138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 123138569Ssam 124138569Ssam ath_hal_setupxtxdesc(sc->sc_ah, ds 125138569Ssam , amn->amn_tx_rate1sp, amn->amn_tx_try1 /* series 1 */ 126138569Ssam , amn->amn_tx_rate2sp, amn->amn_tx_try2 /* series 2 */ 127138569Ssam , amn->amn_tx_rate3sp, amn->amn_tx_try3 /* series 3 */ 128138569Ssam ); 129138569Ssam} 130138569Ssam 131138569Ssamvoid 132144347Ssamath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, 133165185Ssam const struct ath_buf *bf) 134138569Ssam{ 135138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 136165185Ssam const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; 137165185Ssam int sr = ts->ts_shortretry; 138165185Ssam int lr = ts->ts_longretry; 139138569Ssam int retry_count = sr + lr; 140138569Ssam 141138569Ssam amn->amn_tx_try0_cnt++; 142138569Ssam if (retry_count == 1) { 143138569Ssam amn->amn_tx_try1_cnt++; 144138569Ssam } else if (retry_count == 2) { 145138569Ssam amn->amn_tx_try1_cnt++; 146138569Ssam amn->amn_tx_try2_cnt++; 147138569Ssam } else if (retry_count == 3) { 148138569Ssam amn->amn_tx_try1_cnt++; 149138569Ssam amn->amn_tx_try2_cnt++; 150138569Ssam amn->amn_tx_try3_cnt++; 151138569Ssam } else if (retry_count > 3) { 152138569Ssam amn->amn_tx_try1_cnt++; 153138569Ssam amn->amn_tx_try2_cnt++; 154138569Ssam amn->amn_tx_try3_cnt++; 155138569Ssam amn->amn_tx_failure_cnt++; 156138569Ssam } 157178354Ssam if (amn->amn_interval != 0 && 158178354Ssam ticks - amn->amn_ticks > amn->amn_interval) { 159178354Ssam ath_rate_ctl(sc, &an->an_node); 160178354Ssam amn->amn_ticks = ticks; 161178354Ssam } 162138569Ssam} 163138569Ssam 164138569Ssamvoid 165138569Ssamath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew) 166138569Ssam{ 167138569Ssam if (isnew) 168138569Ssam ath_rate_ctl_start(sc, &an->an_node); 169138569Ssam} 170138569Ssam 171138569Ssamstatic void 172178354Ssamnode_reset(struct amrr_node *amn) 173138569Ssam{ 174138569Ssam amn->amn_tx_try0_cnt = 0; 175138569Ssam amn->amn_tx_try1_cnt = 0; 176138569Ssam amn->amn_tx_try2_cnt = 0; 177138569Ssam amn->amn_tx_try3_cnt = 0; 178138569Ssam amn->amn_tx_failure_cnt = 0; 179138569Ssam amn->amn_success = 0; 180138569Ssam amn->amn_recovery = 0; 181138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 182138569Ssam} 183138569Ssam 184138569Ssam 185138569Ssam/** 186138569Ssam * The code below assumes that we are dealing with hardware multi rate retry 187138569Ssam * I have no idea what will happen if you try to use this module with another 188138569Ssam * type of hardware. Your machine might catch fire or it might work with 189138569Ssam * horrible performance... 190138569Ssam */ 191138569Ssamstatic void 192138569Ssamath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate) 193138569Ssam{ 194138569Ssam struct ath_node *an = ATH_NODE(ni); 195138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(an); 196178354Ssam struct ieee80211vap *vap = ni->ni_vap; 197138569Ssam const HAL_RATE_TABLE *rt = sc->sc_currates; 198138569Ssam u_int8_t rix; 199138569Ssam 200138569Ssam KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); 201138569Ssam 202178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni, 203178354Ssam "%s: set xmit rate to %dM", __func__, 204138569Ssam ni->ni_rates.rs_nrates > 0 ? 205138569Ssam (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0); 206138569Ssam 207178354Ssam amn->amn_rix = rate; 208138569Ssam /* 209138569Ssam * Before associating a node has no rate set setup 210138569Ssam * so we can't calculate any transmit codes to use. 211138569Ssam * This is ok since we should never be sending anything 212138569Ssam * but management frames and those always go at the 213138569Ssam * lowest hardware rate. 214138569Ssam */ 215138569Ssam if (ni->ni_rates.rs_nrates > 0) { 216178354Ssam ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL; 217178354Ssam amn->amn_tx_rix0 = sc->sc_rixmap[ni->ni_txrate]; 218138569Ssam amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode; 219138569Ssam amn->amn_tx_rate0sp = amn->amn_tx_rate0 | 220138569Ssam rt->info[amn->amn_tx_rix0].shortPreamble; 221138569Ssam if (sc->sc_mrretry) { 222138569Ssam amn->amn_tx_try0 = 1; 223138569Ssam amn->amn_tx_try1 = 1; 224138569Ssam amn->amn_tx_try2 = 1; 225138569Ssam amn->amn_tx_try3 = 1; 226138569Ssam if (--rate >= 0) { 227138569Ssam rix = sc->sc_rixmap[ 228138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 229138569Ssam amn->amn_tx_rate1 = rt->info[rix].rateCode; 230138569Ssam amn->amn_tx_rate1sp = amn->amn_tx_rate1 | 231138569Ssam rt->info[rix].shortPreamble; 232138569Ssam } else { 233138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 234138569Ssam } 235138569Ssam if (--rate >= 0) { 236138569Ssam rix = sc->sc_rixmap[ 237138569Ssam ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; 238138569Ssam amn->amn_tx_rate2 = rt->info[rix].rateCode; 239138569Ssam amn->amn_tx_rate2sp = amn->amn_tx_rate2 | 240138569Ssam rt->info[rix].shortPreamble; 241138569Ssam } else { 242138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 243138569Ssam } 244138569Ssam if (rate > 0) { 245138569Ssam /* NB: only do this if we didn't already do it above */ 246138569Ssam amn->amn_tx_rate3 = rt->info[0].rateCode; 247138569Ssam amn->amn_tx_rate3sp = 248155477Ssam amn->amn_tx_rate3 | rt->info[0].shortPreamble; 249138569Ssam } else { 250138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 251138569Ssam } 252138569Ssam } else { 253138569Ssam amn->amn_tx_try0 = ATH_TXMAXTRY; 254138569Ssam /* theorically, these statements are useless because 255138569Ssam * the code which uses them tests for an_tx_try0 == ATH_TXMAXTRY 256138569Ssam */ 257138569Ssam amn->amn_tx_try1 = 0; 258138569Ssam amn->amn_tx_try2 = 0; 259138569Ssam amn->amn_tx_try3 = 0; 260138569Ssam amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; 261138569Ssam amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; 262138569Ssam amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; 263138569Ssam } 264138569Ssam } 265178354Ssam node_reset(amn); 266178354Ssam 267178354Ssam amn->amn_interval = ath_rateinterval; 268178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 269178354Ssam amn->amn_interval /= 2; 270178354Ssam amn->amn_interval = (amn->amn_interval * hz) / 1000; 271138569Ssam} 272138569Ssam 273138569Ssam/* 274138569Ssam * Set the starting transmit rate for a node. 275138569Ssam */ 276138569Ssamstatic void 277138569Ssamath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni) 278138569Ssam{ 279138569Ssam#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) 280184347Ssam const struct ieee80211_txparam *tp = ni->ni_txparms; 281138569Ssam int srate; 282138569Ssam 283138569Ssam KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates")); 284178354Ssam if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { 285138569Ssam /* 286138569Ssam * No fixed rate is requested. For 11b start with 287138569Ssam * the highest negotiated rate; otherwise, for 11g 288138569Ssam * and 11a, we start "in the middle" at 24Mb or 36Mb. 289138569Ssam */ 290138569Ssam srate = ni->ni_rates.rs_nrates - 1; 291138569Ssam if (sc->sc_curmode != IEEE80211_MODE_11B) { 292138569Ssam /* 293138569Ssam * Scan the negotiated rate set to find the 294138569Ssam * closest rate. 295138569Ssam */ 296138569Ssam /* NB: the rate set is assumed sorted */ 297138569Ssam for (; srate >= 0 && RATE(srate) > 72; srate--) 298138569Ssam ; 299138569Ssam } 300138569Ssam } else { 301138569Ssam /* 302170530Ssam * A fixed rate is to be used; ic_fixed_rate is the 303170530Ssam * IEEE code for this rate (sans basic bit). Convert this 304138569Ssam * to the index into the negotiated rate set for 305138569Ssam * the node. We know the rate is there because the 306138569Ssam * rate set is checked when the station associates. 307138569Ssam */ 308138569Ssam /* NB: the rate set is assumed sorted */ 309138569Ssam srate = ni->ni_rates.rs_nrates - 1; 310178354Ssam for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--) 311138569Ssam ; 312138569Ssam } 313170530Ssam /* 314170530Ssam * The selected rate may not be available due to races 315170530Ssam * and mode settings. Also orphaned nodes created in 316170530Ssam * adhoc mode may not have any rate set so this lookup 317170530Ssam * can fail. This is not fatal. 318170530Ssam */ 319170530Ssam ath_rate_update(sc, ni, srate < 0 ? 0 : srate); 320138569Ssam#undef RATE 321138569Ssam} 322138569Ssam 323138569Ssam/* 324138569Ssam * Examine and potentially adjust the transmit rate. 325138569Ssam */ 326138569Ssamstatic void 327138569Ssamath_rate_ctl(void *arg, struct ieee80211_node *ni) 328138569Ssam{ 329138569Ssam struct ath_softc *sc = arg; 330138569Ssam struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni)); 331178354Ssam int rix; 332138569Ssam 333138569Ssam#define is_success(amn) \ 334138569Ssam(amn->amn_tx_try1_cnt < (amn->amn_tx_try0_cnt/10)) 335138569Ssam#define is_enough(amn) \ 336138569Ssam(amn->amn_tx_try0_cnt > 10) 337138569Ssam#define is_failure(amn) \ 338138569Ssam(amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3)) 339138569Ssam 340178354Ssam rix = amn->amn_rix; 341138569Ssam 342178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 343178354Ssam "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d", 344178354Ssam amn->amn_tx_try0_cnt, amn->amn_tx_try1_cnt, amn->amn_tx_try2_cnt, 345178354Ssam amn->amn_tx_try3_cnt, amn->amn_success_threshold); 346138569Ssam if (is_success (amn) && is_enough (amn)) { 347138569Ssam amn->amn_success++; 348138569Ssam if (amn->amn_success == amn->amn_success_threshold && 349178354Ssam rix + 1 < ni->ni_rates.rs_nrates) { 350138569Ssam amn->amn_recovery = 1; 351138569Ssam amn->amn_success = 0; 352178354Ssam rix++; 353178354Ssam IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 354178354Ssam "increase rate to %d", rix); 355138569Ssam } else { 356138569Ssam amn->amn_recovery = 0; 357138569Ssam } 358138569Ssam } else if (is_failure (amn)) { 359138569Ssam amn->amn_success = 0; 360178354Ssam if (rix > 0) { 361138569Ssam if (amn->amn_recovery) { 362138569Ssam /* recovery failure. */ 363138569Ssam amn->amn_success_threshold *= 2; 364138569Ssam amn->amn_success_threshold = min (amn->amn_success_threshold, 365138569Ssam (u_int)ath_rate_max_success_threshold); 366178354Ssam IEEE80211_NOTE(ni->ni_vap, 367178354Ssam IEEE80211_MSG_RATECTL, ni, 368178354Ssam "decrease rate recovery thr: %d", 369178354Ssam amn->amn_success_threshold); 370138569Ssam } else { 371138569Ssam /* simple failure. */ 372138569Ssam amn->amn_success_threshold = ath_rate_min_success_threshold; 373178354Ssam IEEE80211_NOTE(ni->ni_vap, 374178354Ssam IEEE80211_MSG_RATECTL, ni, 375178354Ssam "decrease rate normal thr: %d", 376178354Ssam amn->amn_success_threshold); 377138569Ssam } 378138569Ssam amn->amn_recovery = 0; 379178354Ssam rix--; 380138569Ssam } else { 381138569Ssam amn->amn_recovery = 0; 382138569Ssam } 383138569Ssam 384138569Ssam } 385178354Ssam if (is_enough (amn) || rix != amn->amn_rix) { 386138569Ssam /* reset counters. */ 387138569Ssam amn->amn_tx_try0_cnt = 0; 388138569Ssam amn->amn_tx_try1_cnt = 0; 389138569Ssam amn->amn_tx_try2_cnt = 0; 390138569Ssam amn->amn_tx_try3_cnt = 0; 391138569Ssam amn->amn_tx_failure_cnt = 0; 392138569Ssam } 393178354Ssam if (rix != amn->amn_rix) { 394178354Ssam ath_rate_update(sc, ni, rix); 395138569Ssam } 396138569Ssam} 397138569Ssam 398138569Ssamstatic void 399138569Ssamath_rate_sysctlattach(struct ath_softc *sc) 400138569Ssam{ 401138569Ssam struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 402138569Ssam struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 403138569Ssam 404138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 405138569Ssam "rate_interval", CTLFLAG_RW, &ath_rateinterval, 0, 406138569Ssam "rate control: operation interval (ms)"); 407138569Ssam /* XXX bounds check values */ 408138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 409138569Ssam "max_sucess_threshold", CTLFLAG_RW, 410138569Ssam &ath_rate_max_success_threshold, 0, ""); 411138569Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 412138569Ssam "min_sucess_threshold", CTLFLAG_RW, 413138569Ssam &ath_rate_min_success_threshold, 0, ""); 414138569Ssam} 415138569Ssam 416138569Ssamstruct ath_ratectrl * 417138569Ssamath_rate_attach(struct ath_softc *sc) 418138569Ssam{ 419138569Ssam struct amrr_softc *asc; 420138569Ssam 421138569Ssam asc = malloc(sizeof(struct amrr_softc), M_DEVBUF, M_NOWAIT|M_ZERO); 422138569Ssam if (asc == NULL) 423138569Ssam return NULL; 424138569Ssam asc->arc.arc_space = sizeof(struct amrr_node); 425138569Ssam ath_rate_sysctlattach(sc); 426138569Ssam 427138569Ssam return &asc->arc; 428138569Ssam} 429138569Ssam 430138569Ssamvoid 431138569Ssamath_rate_detach(struct ath_ratectrl *arc) 432138569Ssam{ 433138569Ssam struct amrr_softc *asc = (struct amrr_softc *) arc; 434138569Ssam 435138569Ssam free(asc, M_DEVBUF); 436138569Ssam} 437138569Ssam 438138569Ssam/* 439138569Ssam * Module glue. 440138569Ssam */ 441138569Ssamstatic int 442138569Ssamamrr_modevent(module_t mod, int type, void *unused) 443138569Ssam{ 444138569Ssam switch (type) { 445138569Ssam case MOD_LOAD: 446138569Ssam if (bootverbose) 447138569Ssam printf("ath_rate: <AMRR rate control algorithm> version 0.1\n"); 448138569Ssam return 0; 449138569Ssam case MOD_UNLOAD: 450138569Ssam return 0; 451138569Ssam } 452138569Ssam return EINVAL; 453138569Ssam} 454138569Ssam 455138569Ssamstatic moduledata_t amrr_mod = { 456138569Ssam "ath_rate", 457138569Ssam amrr_modevent, 458138569Ssam 0 459138569Ssam}; 460138569SsamDECLARE_MODULE(ath_rate, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 461138569SsamMODULE_VERSION(ath_rate, 1); 462138569SsamMODULE_DEPEND(ath_rate, wlan, 1, 1, 1); 463