xref: /illumos-gate/usr/src/uts/common/io/ath/ath_rate.c (revision 129d67ac)
17a1306a7Sxc /*
2ff3124efSff  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
37a1306a7Sxc  * Use is subject to license terms.
47a1306a7Sxc  */
57a1306a7Sxc 
67a1306a7Sxc /*
77a1306a7Sxc  * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
87a1306a7Sxc  * All rights reserved.
97a1306a7Sxc  *
107a1306a7Sxc  * Redistribution and use in source and binary forms, with or without
117a1306a7Sxc  * modification, are permitted provided that the following conditions
127a1306a7Sxc  * are met:
137a1306a7Sxc  * 1. Redistributions of source code must retain the above copyright
147a1306a7Sxc  * notice, this list of conditions and the following disclaimer,
157a1306a7Sxc  * without modification.
167a1306a7Sxc  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
177a1306a7Sxc  * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
187a1306a7Sxc  * redistribution must be conditioned upon including a substantially
197a1306a7Sxc  * similar Disclaimer requirement for further binary redistribution.
207a1306a7Sxc  * 3. Neither the names of the above-listed copyright holders nor the names
217a1306a7Sxc  * of any contributors may be used to endorse or promote products derived
227a1306a7Sxc  * from this software without specific prior written permission.
237a1306a7Sxc  *
247a1306a7Sxc  * NO WARRANTY
257a1306a7Sxc  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
267a1306a7Sxc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
277a1306a7Sxc  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
287a1306a7Sxc  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
297a1306a7Sxc  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
307a1306a7Sxc  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
317a1306a7Sxc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
327a1306a7Sxc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
337a1306a7Sxc  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
347a1306a7Sxc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
357a1306a7Sxc  * THE POSSIBILITY OF SUCH DAMAGES.
367a1306a7Sxc  */
377a1306a7Sxc 
387a1306a7Sxc #include <sys/param.h>
397a1306a7Sxc #include <sys/types.h>
407a1306a7Sxc #include <sys/signal.h>
417a1306a7Sxc #include <sys/stream.h>
427a1306a7Sxc #include <sys/termio.h>
437a1306a7Sxc #include <sys/errno.h>
447a1306a7Sxc #include <sys/file.h>
457a1306a7Sxc #include <sys/cmn_err.h>
467a1306a7Sxc #include <sys/stropts.h>
477a1306a7Sxc #include <sys/strsubr.h>
487a1306a7Sxc #include <sys/strtty.h>
497a1306a7Sxc #include <sys/kbio.h>
507a1306a7Sxc #include <sys/cred.h>
517a1306a7Sxc #include <sys/stat.h>
527a1306a7Sxc #include <sys/consdev.h>
537a1306a7Sxc #include <sys/kmem.h>
547a1306a7Sxc #include <sys/modctl.h>
557a1306a7Sxc #include <sys/ddi.h>
567a1306a7Sxc #include <sys/sunddi.h>
577a1306a7Sxc #include <sys/pci.h>
587a1306a7Sxc #include <sys/errno.h>
597a1306a7Sxc #include <sys/dlpi.h>
607a1306a7Sxc #include <sys/ethernet.h>
617a1306a7Sxc #include <sys/list.h>
627a1306a7Sxc #include <sys/byteorder.h>
637a1306a7Sxc #include <sys/strsun.h>
647a1306a7Sxc #include <inet/common.h>
657a1306a7Sxc #include <inet/nd.h>
667a1306a7Sxc #include <inet/mi.h>
677a1306a7Sxc #include <inet/wifi_ioctl.h>
687a1306a7Sxc #include "ath_hal.h"
697a1306a7Sxc #include "ath_impl.h"
700ba2cbe9Sxc #include "ath_rate.h"
717a1306a7Sxc 
727a1306a7Sxc void
ath_rate_update(ath_t * asc,struct ieee80211_node * in,int32_t rate)737a1306a7Sxc ath_rate_update(ath_t *asc, struct ieee80211_node *in, int32_t rate)
747a1306a7Sxc {
757a1306a7Sxc 	struct ath_node *an = ATH_NODE(in);
767a1306a7Sxc 	const HAL_RATE_TABLE *rt = asc->asc_currates;
777a1306a7Sxc 	uint8_t rix;
787a1306a7Sxc 
79*129d67acSlin wang - Sun Microsystems - Beijing China 	ASSERT(rt != NULL);
80*129d67acSlin wang - Sun Microsystems - Beijing China 
817a1306a7Sxc 	in->in_txrate = rate;
82*129d67acSlin wang - Sun Microsystems - Beijing China 
837a1306a7Sxc 	/* management/control frames always go at the lowest speed */
847a1306a7Sxc 	an->an_tx_mgtrate = rt->info[0].rateCode;
857a1306a7Sxc 	an->an_tx_mgtratesp = an->an_tx_mgtrate | rt->info[0].shortPreamble;
867a1306a7Sxc 	ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_update(): "
877a1306a7Sxc 	    "mgtrate=%d mgtratesp=%d\n",
887a1306a7Sxc 	    an->an_tx_mgtrate, an->an_tx_mgtratesp));
897a1306a7Sxc 	/*
907a1306a7Sxc 	 * Before associating a node has no rate set setup
917a1306a7Sxc 	 * so we can't calculate any transmit codes to use.
927a1306a7Sxc 	 * This is ok since we should never be sending anything
937a1306a7Sxc 	 * but management frames and those always go at the
947a1306a7Sxc 	 * lowest hardware rate.
957a1306a7Sxc 	 */
967a1306a7Sxc 	if (in->in_rates.ir_nrates == 0)
977a1306a7Sxc 		goto done;
987a1306a7Sxc 	an->an_tx_rix0 = asc->asc_rixmap[
997a1306a7Sxc 	    in->in_rates.ir_rates[rate] & IEEE80211_RATE_VAL];
1007a1306a7Sxc 	an->an_tx_rate0 = rt->info[an->an_tx_rix0].rateCode;
1017a1306a7Sxc 	an->an_tx_rate0sp = an->an_tx_rate0 |
1027a1306a7Sxc 	    rt->info[an->an_tx_rix0].shortPreamble;
1037a1306a7Sxc 	if (asc->asc_mrretry) {
1047a1306a7Sxc 		/*
1057a1306a7Sxc 		 * Hardware supports multi-rate retry; setup two
1067a1306a7Sxc 		 * step-down retry rates and make the lowest rate
1077a1306a7Sxc 		 * be the ``last chance''.  We use 4, 2, 2, 2 tries
1087a1306a7Sxc 		 * respectively (4 is set here, the rest are fixed
1097a1306a7Sxc 		 * in the xmit routine).
1107a1306a7Sxc 		 */
1117a1306a7Sxc 		an->an_tx_try0 = 1 + 3;		/* 4 tries at rate 0 */
1127a1306a7Sxc 		if (--rate >= 0) {
1137a1306a7Sxc 			rix = asc->asc_rixmap[
1147a1306a7Sxc 			    in->in_rates.ir_rates[rate]&IEEE80211_RATE_VAL];
1157a1306a7Sxc 			an->an_tx_rate1 = rt->info[rix].rateCode;
1167a1306a7Sxc 			an->an_tx_rate1sp = an->an_tx_rate1 |
1177a1306a7Sxc 			    rt->info[rix].shortPreamble;
1187a1306a7Sxc 		} else {
1197a1306a7Sxc 			an->an_tx_rate1 = an->an_tx_rate1sp = 0;
1207a1306a7Sxc 		}
1217a1306a7Sxc 		if (--rate >= 0) {
1227a1306a7Sxc 			rix = asc->asc_rixmap[
1237a1306a7Sxc 			    in->in_rates.ir_rates[rate]&IEEE80211_RATE_VAL];
1247a1306a7Sxc 			an->an_tx_rate2 = rt->info[rix].rateCode;
1257a1306a7Sxc 			an->an_tx_rate2sp = an->an_tx_rate2 |
1267a1306a7Sxc 			    rt->info[rix].shortPreamble;
1277a1306a7Sxc 		} else {
1287a1306a7Sxc 			an->an_tx_rate2 = an->an_tx_rate2sp = 0;
1297a1306a7Sxc 		}
1307a1306a7Sxc 		if (rate > 0) {
1317a1306a7Sxc 			an->an_tx_rate3 = rt->info[0].rateCode;
1327a1306a7Sxc 			an->an_tx_rate3sp =
1337a1306a7Sxc 			    an->an_tx_mgtrate | rt->info[0].shortPreamble;
1347a1306a7Sxc 		} else {
1357a1306a7Sxc 			an->an_tx_rate3 = an->an_tx_rate3sp = 0;
1367a1306a7Sxc 		}
1377a1306a7Sxc 	} else {
1387a1306a7Sxc 		an->an_tx_try0 = ATH_TXMAXTRY;  /* max tries at rate 0 */
1397a1306a7Sxc 		an->an_tx_rate1 = an->an_tx_rate1sp = 0;
1407a1306a7Sxc 		an->an_tx_rate2 = an->an_tx_rate2sp = 0;
1417a1306a7Sxc 		an->an_tx_rate3 = an->an_tx_rate3sp = 0;
1427a1306a7Sxc 	}
1437a1306a7Sxc done:
1447a1306a7Sxc 	an->an_tx_ok = an->an_tx_err = an->an_tx_retr = an->an_tx_upper = 0;
1457a1306a7Sxc }
1467a1306a7Sxc 
1477a1306a7Sxc 
1487a1306a7Sxc /*
1497a1306a7Sxc  * Set the starting transmit rate for a node.
1507a1306a7Sxc  */
1517a1306a7Sxc void
ath_rate_ctl_start(ath_t * asc,struct ieee80211_node * in)1527a1306a7Sxc ath_rate_ctl_start(ath_t *asc, struct ieee80211_node *in)
1537a1306a7Sxc {
1540ba2cbe9Sxc 	ieee80211com_t *ic = (ieee80211com_t *)asc;
1557a1306a7Sxc 	int32_t srate;
1567a1306a7Sxc 
1570ba2cbe9Sxc 	if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
1587a1306a7Sxc 		/*
1597a1306a7Sxc 		 * No fixed rate is requested. For 11b start with
1607a1306a7Sxc 		 * the highest negotiated rate; otherwise, for 11g
1617a1306a7Sxc 		 * and 11a, we start "in the middle" at 24Mb or 36Mb.
1627a1306a7Sxc 		 */
1637a1306a7Sxc 		srate = in->in_rates.ir_nrates - 1;
1647a1306a7Sxc 		if (asc->asc_curmode != IEEE80211_MODE_11B) {
1657a1306a7Sxc 			/*
1667a1306a7Sxc 			 * Scan the negotiated rate set to find the
1677a1306a7Sxc 			 * closest rate.
1687a1306a7Sxc 			 */
1697a1306a7Sxc 			/* NB: the rate set is assumed sorted */
1707a1306a7Sxc 			for (; srate >= 0 && IEEE80211_RATE(srate) > 72;
171ff3124efSff 			    srate--) {}
1727a1306a7Sxc 		}
1737a1306a7Sxc 	} else {
1747a1306a7Sxc 		/*
1750ba2cbe9Sxc 		 * A fixed rate is to be used; We know the rate is
1760ba2cbe9Sxc 		 * there because the rate set is checked when the
1770ba2cbe9Sxc 		 * station associates.
1787a1306a7Sxc 		 */
1797a1306a7Sxc 		/* NB: the rate set is assumed sorted */
1807a1306a7Sxc 		srate = in->in_rates.ir_nrates - 1;
1810ba2cbe9Sxc 		for (; srate >= 0 && IEEE80211_RATE(srate) != ic->ic_fixed_rate;
182ff3124efSff 		    srate--) {}
1837a1306a7Sxc 	}
1847a1306a7Sxc 	ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_ctl_start(): "
1857a1306a7Sxc 	    "srate=%d rate=%d\n", srate, IEEE80211_RATE(srate)));
1867a1306a7Sxc 	ath_rate_update(asc, in, srate);
1877a1306a7Sxc }
1887a1306a7Sxc 
1890ba2cbe9Sxc void
ath_rate_cb(void * arg,struct ieee80211_node * in)1900ba2cbe9Sxc ath_rate_cb(void *arg, struct ieee80211_node *in)
1910ba2cbe9Sxc {
1920ba2cbe9Sxc 	ath_rate_update((ath_t *)arg, in, 0);
1930ba2cbe9Sxc }
1947a1306a7Sxc 
1957a1306a7Sxc /*
1967a1306a7Sxc  * Reset the rate control state for each 802.11 state transition.
1977a1306a7Sxc  */
1987a1306a7Sxc void
ath_rate_ctl_reset(ath_t * asc,enum ieee80211_state state)1997a1306a7Sxc ath_rate_ctl_reset(ath_t *asc, enum ieee80211_state state)
2007a1306a7Sxc {
2010ba2cbe9Sxc 	ieee80211com_t *ic = (ieee80211com_t *)asc;
2027a1306a7Sxc 	struct ieee80211_node *in;
2037a1306a7Sxc 
2040ba2cbe9Sxc 	if (ic->ic_opmode == IEEE80211_M_STA) {
2057a1306a7Sxc 		/*
2067a1306a7Sxc 		 * Reset local xmit state; this is really only
2077a1306a7Sxc 		 * meaningful when operating in station mode.
2087a1306a7Sxc 		 */
2090ba2cbe9Sxc 		in = (struct ieee80211_node *)ic->ic_bss;
2107a1306a7Sxc 		if (state == IEEE80211_S_RUN) {
2117a1306a7Sxc 			ath_rate_ctl_start(asc, in);
2127a1306a7Sxc 		} else {
2137a1306a7Sxc 			ath_rate_update(asc, in, 0);
2147a1306a7Sxc 		}
2157a1306a7Sxc 	} else {
2167a1306a7Sxc 		/*
2177a1306a7Sxc 		 * When operating as a station the node table holds
2187a1306a7Sxc 		 * the AP's that were discovered during scanning.
2197a1306a7Sxc 		 * For any other operating mode we want to reset the
2207a1306a7Sxc 		 * tx rate state of each node.
2217a1306a7Sxc 		 */
2220ba2cbe9Sxc 		ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, asc);
2230ba2cbe9Sxc 		ath_rate_update(asc, ic->ic_bss, 0);
2247a1306a7Sxc 	}
2257a1306a7Sxc }
2267a1306a7Sxc 
2277a1306a7Sxc 
2287a1306a7Sxc /*
2297a1306a7Sxc  * Examine and potentially adjust the transmit rate.
2307a1306a7Sxc  */
2317a1306a7Sxc void
ath_rate_ctl(void * arg,struct ieee80211_node * in)232*129d67acSlin wang - Sun Microsystems - Beijing China ath_rate_ctl(void *arg, struct ieee80211_node *in)
2337a1306a7Sxc {
234*129d67acSlin wang - Sun Microsystems - Beijing China 	ath_t *asc = arg;
2357a1306a7Sxc 	struct ath_node *an = ATH_NODE(in);
2367a1306a7Sxc 	struct ieee80211_rateset *rs = &in->in_rates;
2377a1306a7Sxc 	int32_t mod = 0, nrate, enough;
2387a1306a7Sxc 
2397a1306a7Sxc 	/*
2407a1306a7Sxc 	 * Rate control(very primitive version).
2417a1306a7Sxc 	 */
2427a1306a7Sxc 	asc->asc_stats.ast_rate_calls++;
2437a1306a7Sxc 
2447a1306a7Sxc 	enough = (an->an_tx_ok + an->an_tx_err >= 10);
2457a1306a7Sxc 
2467a1306a7Sxc 	/* no packet reached -> down */
2477a1306a7Sxc 	if (an->an_tx_err > 0 && an->an_tx_ok == 0)
2487a1306a7Sxc 		mod = -1;
2497a1306a7Sxc 
2507a1306a7Sxc 	/* all packets needs retry in average -> down */
2517a1306a7Sxc 	if (enough && an->an_tx_ok < an->an_tx_retr)
2527a1306a7Sxc 		mod = -1;
2537a1306a7Sxc 
2547a1306a7Sxc 	/* no error and less than 10% of packets needs retry -> up */
2557a1306a7Sxc 	if (enough && an->an_tx_err == 0 && an->an_tx_ok > an->an_tx_retr * 10)
2567a1306a7Sxc 		mod = 1;
2577a1306a7Sxc 
2587a1306a7Sxc 	nrate = in->in_txrate;
2597a1306a7Sxc 	switch (mod) {
2607a1306a7Sxc 	case 0:
2617a1306a7Sxc 		if (enough && an->an_tx_upper > 0)
2627a1306a7Sxc 			an->an_tx_upper--;
2637a1306a7Sxc 		break;
2647a1306a7Sxc 	case -1:
2657a1306a7Sxc 		if (nrate > 0) {
2667a1306a7Sxc 			nrate--;
2677a1306a7Sxc 			asc->asc_stats.ast_rate_drop++;
2687a1306a7Sxc 		}
2697a1306a7Sxc 		an->an_tx_upper = 0;
2707a1306a7Sxc 		break;
2717a1306a7Sxc 	case 1:
2727a1306a7Sxc 		if (++an->an_tx_upper < 10)
2737a1306a7Sxc 			break;
2747a1306a7Sxc 		an->an_tx_upper = 0;
2757a1306a7Sxc 		if (nrate + 1 < rs->ir_nrates) {
2767a1306a7Sxc 			nrate++;
2777a1306a7Sxc 			asc->asc_stats.ast_rate_raise++;
2787a1306a7Sxc 		}
2797a1306a7Sxc 		break;
2807a1306a7Sxc 	}
2817a1306a7Sxc 
2827a1306a7Sxc 	if (nrate != in->in_txrate) {
2837a1306a7Sxc 		ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_ctl(): %dM -> %dM "
2847a1306a7Sxc 		    "(%d ok, %d err, %d retr)\n",
2857a1306a7Sxc 		    (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2,
2867a1306a7Sxc 		    (rs->ir_rates[nrate] & IEEE80211_RATE_VAL) / 2,
2877a1306a7Sxc 		    an->an_tx_ok, an->an_tx_err, an->an_tx_retr));
2887a1306a7Sxc 		ath_rate_update(asc, in, nrate);
2897a1306a7Sxc 	} else if (enough)
2907a1306a7Sxc 		an->an_tx_ok = an->an_tx_err = an->an_tx_retr = 0;
2917a1306a7Sxc }
2927a1306a7Sxc 
2937a1306a7Sxc 
2947a1306a7Sxc /*
2957a1306a7Sxc  * Read rate table from the HAL, and then
2967a1306a7Sxc  * copy the table to the driver's data structure.
2977a1306a7Sxc  */
2987a1306a7Sxc void
ath_rate_setup(ath_t * asc,uint32_t mode)2997a1306a7Sxc ath_rate_setup(ath_t *asc, uint32_t mode)
3007a1306a7Sxc {
301ff3124efSff 	int32_t i;
302ff3124efSff 	uint8_t maxrates;
3037a1306a7Sxc 	struct ieee80211_rateset *rs;
3047a1306a7Sxc 	struct ath_hal *ah = asc->asc_ah;
3050ba2cbe9Sxc 	ieee80211com_t *ic = (ieee80211com_t *)asc;
3067a1306a7Sxc 	const HAL_RATE_TABLE *rt;
3077a1306a7Sxc 
3087a1306a7Sxc 	switch (mode) {
3097a1306a7Sxc 	case IEEE80211_MODE_11A:
3107a1306a7Sxc 		asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_11A);
3117a1306a7Sxc 		break;
3127a1306a7Sxc 	case IEEE80211_MODE_11B:
3137a1306a7Sxc 		asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_11B);
3147a1306a7Sxc 		break;
3157a1306a7Sxc 	case IEEE80211_MODE_11G:
3167a1306a7Sxc 		asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_11G);
3177a1306a7Sxc 		break;
3180ba2cbe9Sxc 	case IEEE80211_MODE_TURBO_A:
3197a1306a7Sxc 		asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_TURBO);
3207a1306a7Sxc 		break;
3210ba2cbe9Sxc 	case IEEE80211_MODE_TURBO_G:
3220ba2cbe9Sxc 		asc->asc_rates[mode] = ATH_HAL_GETRATETABLE(ah, HAL_MODE_108G);
3230ba2cbe9Sxc 		break;
3247a1306a7Sxc 	default:
3257a1306a7Sxc 		ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_setup(): "
3267a1306a7Sxc 		    "invalid mode %u\n", mode));
3277a1306a7Sxc 		return;
3287a1306a7Sxc 	}
3297a1306a7Sxc 
3307a1306a7Sxc 	rt = asc->asc_rates[mode];
3317a1306a7Sxc 	if (rt == NULL)
3327a1306a7Sxc 		return;
3337a1306a7Sxc 	if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
3347a1306a7Sxc 		ATH_DEBUG((ATH_DBG_RATE, "ath: ath_rate_setup(): "
3357a1306a7Sxc 		    "rate table too small (%u > %u)\n",
3367a1306a7Sxc 		    rt->rateCount, IEEE80211_RATE_MAXSIZE));
3377a1306a7Sxc 		maxrates = IEEE80211_RATE_MAXSIZE;
3387a1306a7Sxc 	} else
3397a1306a7Sxc 		maxrates = rt->rateCount;
3400ba2cbe9Sxc 	rs = &ic->ic_sup_rates[mode];
3417a1306a7Sxc 	for (i = 0; i < maxrates; i++)
3427a1306a7Sxc 		rs->ir_rates[i] = rt->info[i].dot11Rate;
3437a1306a7Sxc 	rs->ir_nrates = maxrates;
3447a1306a7Sxc }
345