/* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ath_hal.h" #include "ath_impl.h" #include "ath_rate.h" void ath_rate_update(ath_t *asc, struct ieee80211_node *in, int32_t rate) { struct ath_node *an = ATH_NODE(in); const HAL_RATE_TABLE *rt = asc->asc_currates; uint8_t rix; in->in_txrate = rate; /* management/control frames always go at the lowest speed */ an->an_tx_mgtrate = rt->info[0].rateCode; an->an_tx_mgtratesp = an->an_tx_mgtrate | rt->info[0].shortPreamble; ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_update(): " "mgtrate=%d mgtratesp=%d\n", an->an_tx_mgtrate, an->an_tx_mgtratesp)); /* * Before associating a node has no rate set setup * so we can't calculate any transmit codes to use. * This is ok since we should never be sending anything * but management frames and those always go at the * lowest hardware rate. */ if (in->in_rates.ir_nrates == 0) goto done; an->an_tx_rix0 = asc->asc_rixmap[ in->in_rates.ir_rates[rate] & IEEE80211_RATE_VAL]; an->an_tx_rate0 = rt->info[an->an_tx_rix0].rateCode; an->an_tx_rate0sp = an->an_tx_rate0 | rt->info[an->an_tx_rix0].shortPreamble; if (asc->asc_mrretry) { /* * Hardware supports multi-rate retry; setup two * step-down retry rates and make the lowest rate * be the ``last chance''. We use 4, 2, 2, 2 tries * respectively (4 is set here, the rest are fixed * in the xmit routine). */ an->an_tx_try0 = 1 + 3; /* 4 tries at rate 0 */ if (--rate >= 0) { rix = asc->asc_rixmap[ in->in_rates.ir_rates[rate]&IEEE80211_RATE_VAL]; an->an_tx_rate1 = rt->info[rix].rateCode; an->an_tx_rate1sp = an->an_tx_rate1 | rt->info[rix].shortPreamble; } else { an->an_tx_rate1 = an->an_tx_rate1sp = 0; } if (--rate >= 0) { rix = asc->asc_rixmap[ in->in_rates.ir_rates[rate]&IEEE80211_RATE_VAL]; an->an_tx_rate2 = rt->info[rix].rateCode; an->an_tx_rate2sp = an->an_tx_rate2 | rt->info[rix].shortPreamble; } else { an->an_tx_rate2 = an->an_tx_rate2sp = 0; } if (rate > 0) { an->an_tx_rate3 = rt->info[0].rateCode; an->an_tx_rate3sp = an->an_tx_mgtrate | rt->info[0].shortPreamble; } else { an->an_tx_rate3 = an->an_tx_rate3sp = 0; } } else { an->an_tx_try0 = ATH_TXMAXTRY; /* max tries at rate 0 */ an->an_tx_rate1 = an->an_tx_rate1sp = 0; an->an_tx_rate2 = an->an_tx_rate2sp = 0; an->an_tx_rate3 = an->an_tx_rate3sp = 0; } done: an->an_tx_ok = an->an_tx_err = an->an_tx_retr = an->an_tx_upper = 0; } /* * Set the starting transmit rate for a node. */ void ath_rate_ctl_start(ath_t *asc, struct ieee80211_node *in) { ieee80211com_t *ic = (ieee80211com_t *)asc; int32_t srate; if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { /* * No fixed rate is requested. For 11b start with * the highest negotiated rate; otherwise, for 11g * and 11a, we start "in the middle" at 24Mb or 36Mb. */ srate = in->in_rates.ir_nrates - 1; if (asc->asc_curmode != IEEE80211_MODE_11B) { /* * Scan the negotiated rate set to find the * closest rate. */ /* NB: the rate set is assumed sorted */ for (; srate >= 0 && IEEE80211_RATE(srate) > 72; srate--) {} } } else { /* * A fixed rate is to be used; We know the rate is * there because the rate set is checked when the * station associates. */ /* NB: the rate set is assumed sorted */ srate = in->in_rates.ir_nrates - 1; for (; srate >= 0 && IEEE80211_RATE(srate) != ic->ic_fixed_rate; srate--) {} } ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_ctl_start(): " "srate=%d rate=%d\n", srate, IEEE80211_RATE(srate))); ath_rate_update(asc, in, srate); } void ath_rate_cb(void *arg, struct ieee80211_node *in) { ath_rate_update((ath_t *)arg, in, 0); } /* * Reset the rate control state for each 802.11 state transition. */ void ath_rate_ctl_reset(ath_t *asc, enum ieee80211_state state) { ieee80211com_t *ic = (ieee80211com_t *)asc; struct ieee80211_node *in; if (ic->ic_opmode == IEEE80211_M_STA) { /* * Reset local xmit state; this is really only * meaningful when operating in station mode. */ in = (struct ieee80211_node *)ic->ic_bss; if (state == IEEE80211_S_RUN) { ath_rate_ctl_start(asc, in); } else { ath_rate_update(asc, in, 0); } } else { /* * When operating as a station the node table holds * the AP's that were discovered during scanning. * For any other operating mode we want to reset the * tx rate state of each node. */ ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, asc); ath_rate_update(asc, ic->ic_bss, 0); } } /* * Examine and potentially adjust the transmit rate. */ void ath_rate_ctl(ieee80211com_t *isc, struct ieee80211_node *in) { ath_t *asc = (ath_t *)isc; struct ath_node *an = ATH_NODE(in); struct ieee80211_rateset *rs = &in->in_rates; int32_t mod = 0, nrate, enough; /* * Rate control(very primitive version). */ asc->asc_stats.ast_rate_calls++; enough = (an->an_tx_ok + an->an_tx_err >= 10); /* no packet reached -> down */ if (an->an_tx_err > 0 && an->an_tx_ok == 0) mod = -1; /* all packets needs retry in average -> down */ if (enough && an->an_tx_ok < an->an_tx_retr) mod = -1; /* no error and less than 10% of packets needs retry -> up */ if (enough && an->an_tx_err == 0 && an->an_tx_ok > an->an_tx_retr * 10) mod = 1; nrate = in->in_txrate; switch (mod) { case 0: if (enough && an->an_tx_upper > 0) an->an_tx_upper--; break; case -1: if (nrate > 0) { nrate--; asc->asc_stats.ast_rate_drop++; } an->an_tx_upper = 0; break; case 1: if (++an->an_tx_upper < 10) break; an->an_tx_upper = 0; if (nrate + 1 < rs->ir_nrates) { nrate++; asc->asc_stats.ast_rate_raise++; } break; } if (nrate != in->in_txrate) { ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_ctl(): %dM -> %dM " "(%d ok, %d err, %d retr)\n", (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2, (rs->ir_rates[nrate] & IEEE80211_RATE_VAL) / 2, an->an_tx_ok, an->an_tx_err, an->an_tx_retr)); ath_rate_update(asc, in, nrate); } else if (enough) an->an_tx_ok = an->an_tx_err = an->an_tx_retr = 0; } /* * Read rate table from the HAL, and then * copy the table to the driver's data structure. */ void ath_rate_setup(ath_t *asc, uint32_t mode) { int32_t i; uint8_t maxrates; struct ieee80211_rateset *rs; struct ath_hal *ah = asc->asc_ah; ieee80211com_t *ic = (ieee80211com_t *)asc; const HAL_RATE_TABLE *rt; switch (mode) { case IEEE80211_MODE_11A: asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_11A); break; case IEEE80211_MODE_11B: asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_11B); break; case IEEE80211_MODE_11G: asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_11G); break; case IEEE80211_MODE_TURBO_A: asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_TURBO); break; case IEEE80211_MODE_TURBO_G: asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_108G); break; default: ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_setup(): " "invalid mode %u\n", mode)); return; } rt = asc->asc_rates[mode]; if (rt == NULL) return; if (rt->rateCount > IEEE80211_RATE_MAXSIZE) { ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_setup(): " "rate table too small (%u > %u)\n", rt->rateCount, IEEE80211_RATE_MAXSIZE)); maxrates = IEEE80211_RATE_MAXSIZE; } else maxrates = rt->rateCount; rs = &ic->ic_sup_rates[mode]; for (i = 0; i < maxrates; i++) rs->ir_rates[i] = rt->info[i].dot11Rate; rs->ir_nrates = maxrates; }