xref: /onnv-gate/usr/src/uts/common/io/arn/arn_rc.c (revision 11729:8922e660c576)
19999SWang.Lin@Sun.COM /*
2*11729SWang.Lin@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
39999SWang.Lin@Sun.COM  * Use is subject to license terms.
49999SWang.Lin@Sun.COM  */
59999SWang.Lin@Sun.COM 
69999SWang.Lin@Sun.COM /*
79999SWang.Lin@Sun.COM  * Copyright (c) 2004 Video54 Technologies, Inc.
89999SWang.Lin@Sun.COM  * Copyright (c) 2004-2008 Atheros Communications, Inc.
99999SWang.Lin@Sun.COM  *
109999SWang.Lin@Sun.COM  * Permission to use, copy, modify, and/or distribute this software for any
119999SWang.Lin@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
129999SWang.Lin@Sun.COM  * copyright notice and this permission notice appear in all copies.
139999SWang.Lin@Sun.COM  *
149999SWang.Lin@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
159999SWang.Lin@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
169999SWang.Lin@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
179999SWang.Lin@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
189999SWang.Lin@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
199999SWang.Lin@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
209999SWang.Lin@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
219999SWang.Lin@Sun.COM  */
229999SWang.Lin@Sun.COM #include <sys/time.h>
239999SWang.Lin@Sun.COM #include <sys/types.h>
249999SWang.Lin@Sun.COM #include <sys/ddi.h>
25*11729SWang.Lin@Sun.COM #include <sys/net80211_ht.h>
269999SWang.Lin@Sun.COM 
279999SWang.Lin@Sun.COM #include "arn_core.h"
28*11729SWang.Lin@Sun.COM #include "arn_hw.h"
29*11729SWang.Lin@Sun.COM #include "arn_reg.h"
309999SWang.Lin@Sun.COM 
319999SWang.Lin@Sun.COM static struct ath_rate_table ar5416_11na_ratetable = {
329999SWang.Lin@Sun.COM 	42,
339999SWang.Lin@Sun.COM 	{0},
349999SWang.Lin@Sun.COM 	{
359999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
369999SWang.Lin@Sun.COM 			5400, 0x0b, 0x00, 12,
379999SWang.Lin@Sun.COM 			0, 2, 1, 0, 0, 0, 0, 0 },
389999SWang.Lin@Sun.COM 		{ VALID,	VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
399999SWang.Lin@Sun.COM 			7800,  0x0f, 0x00, 18,
409999SWang.Lin@Sun.COM 			0, 3, 1, 1, 1, 1, 1, 0 },
419999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
429999SWang.Lin@Sun.COM 			10000, 0x0a, 0x00, 24,
439999SWang.Lin@Sun.COM 			2, 4, 2, 2, 2, 2, 2, 0 },
449999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
459999SWang.Lin@Sun.COM 			13900, 0x0e, 0x00, 36,
469999SWang.Lin@Sun.COM 			2, 6,  2, 3, 3, 3, 3, 0 },
479999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
489999SWang.Lin@Sun.COM 			17300, 0x09, 0x00, 48,
499999SWang.Lin@Sun.COM 			4, 10, 3, 4, 4, 4, 4, 0 },
509999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
519999SWang.Lin@Sun.COM 			23000, 0x0d, 0x00, 72,
529999SWang.Lin@Sun.COM 			4, 14, 3, 5, 5, 5, 5, 0 },
539999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
549999SWang.Lin@Sun.COM 			27400, 0x08, 0x00, 96,
559999SWang.Lin@Sun.COM 			4, 20, 3, 6, 6, 6, 6, 0 },
569999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
579999SWang.Lin@Sun.COM 			29300, 0x0c, 0x00, 108,
589999SWang.Lin@Sun.COM 			4, 23, 3, 7, 7, 7, 7, 0 },
599999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
609999SWang.Lin@Sun.COM 			6400, 0x80, 0x00, 0,
619999SWang.Lin@Sun.COM 			0, 2, 3, 8, 24, 8, 24, 3216 },
629999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
639999SWang.Lin@Sun.COM 			12700, 0x81, 0x00, 1,
649999SWang.Lin@Sun.COM 			2, 4, 3, 9, 25, 9, 25, 6434 },
659999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
669999SWang.Lin@Sun.COM 			18800, 0x82, 0x00, 2,
679999SWang.Lin@Sun.COM 			2, 6, 3, 10, 26, 10, 26, 9650 },
689999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
699999SWang.Lin@Sun.COM 			25000, 0x83, 0x00, 3,
709999SWang.Lin@Sun.COM 			4, 10, 3, 11, 27, 11, 27, 12868 },
719999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
729999SWang.Lin@Sun.COM 			36700, 0x84, 0x00, 4,
739999SWang.Lin@Sun.COM 			4, 14, 3, 12, 28, 12, 28, 19304 },
749999SWang.Lin@Sun.COM 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
759999SWang.Lin@Sun.COM 			48100, 0x85, 0x00, 5,
769999SWang.Lin@Sun.COM 			4, 20, 3, 13, 29, 13, 29, 25740 },
779999SWang.Lin@Sun.COM 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
789999SWang.Lin@Sun.COM 			53500, 0x86, 0x00, 6,
799999SWang.Lin@Sun.COM 			4, 23, 3, 14, 30, 14, 30,  28956 },
809999SWang.Lin@Sun.COM 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
819999SWang.Lin@Sun.COM 			59000, 0x87, 0x00, 7,
829999SWang.Lin@Sun.COM 			4, 25, 3, 15, 31, 15, 32, 32180 },
839999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
849999SWang.Lin@Sun.COM 			12700, 0x88, 0x00,
859999SWang.Lin@Sun.COM 			8, 0, 2, 3, 16, 33, 16, 33, 6430 },
869999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
879999SWang.Lin@Sun.COM 			24800, 0x89, 0x00, 9,
889999SWang.Lin@Sun.COM 			2, 4, 3, 17, 34, 17, 34, 12860 },
899999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
909999SWang.Lin@Sun.COM 			36600, 0x8a, 0x00, 10,
919999SWang.Lin@Sun.COM 			2, 6, 3, 18, 35, 18, 35, 19300 },
929999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
939999SWang.Lin@Sun.COM 			48100, 0x8b, 0x00, 11,
949999SWang.Lin@Sun.COM 			4, 10, 3, 19, 36, 19, 36, 25736 },
959999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
969999SWang.Lin@Sun.COM 			69500, 0x8c, 0x00, 12,
979999SWang.Lin@Sun.COM 			4, 14, 3, 20, 37, 20, 37, 38600 },
989999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
999999SWang.Lin@Sun.COM 			89500, 0x8d, 0x00, 13,
1009999SWang.Lin@Sun.COM 			4, 20, 3, 21, 38, 21, 38, 51472 },
1019999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
1029999SWang.Lin@Sun.COM 			98900, 0x8e, 0x00, 14,
1039999SWang.Lin@Sun.COM 			4, 23, 3, 22, 39, 22, 39, 57890 },
1049999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
1059999SWang.Lin@Sun.COM 			108300, 0x8f, 0x00, 15,
1069999SWang.Lin@Sun.COM 			4, 25, 3, 23, 40, 23, 41, 64320 },
1079999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
1089999SWang.Lin@Sun.COM 			13200, 0x80, 0x00, 0,
1099999SWang.Lin@Sun.COM 			0, 2, 3, 8, 24, 24, 24, 6684 },
1109999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
1119999SWang.Lin@Sun.COM 			25900, 0x81, 0x00, 1,
1129999SWang.Lin@Sun.COM 			2, 4, 3, 9, 25, 25, 25, 13368 },
1139999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
1149999SWang.Lin@Sun.COM 			38600, 0x82, 0x00, 2,
1159999SWang.Lin@Sun.COM 			2, 6, 3, 10, 26, 26, 26, 20052 },
1169999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
1179999SWang.Lin@Sun.COM 			49800, 0x83, 0x00, 3,
1189999SWang.Lin@Sun.COM 			4, 10, 3, 11, 27, 27, 27, 26738 },
1199999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
1209999SWang.Lin@Sun.COM 			72200, 0x84, 0x00, 4,
1219999SWang.Lin@Sun.COM 			4, 14, 3, 12, 28, 28, 28, 40104 },
1229999SWang.Lin@Sun.COM 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
1239999SWang.Lin@Sun.COM 			92900, 0x85, 0x00, 5,
1249999SWang.Lin@Sun.COM 			4, 20, 3, 13, 29, 29, 29, 53476 },
1259999SWang.Lin@Sun.COM 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5Mb */
1269999SWang.Lin@Sun.COM 			102700, 0x86, 0x00, 6,
1279999SWang.Lin@Sun.COM 			4, 23, 3, 14, 30, 30, 30, 60156 },
1289999SWang.Lin@Sun.COM 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
1299999SWang.Lin@Sun.COM 			112000, 0x87, 0x00, 7,
1309999SWang.Lin@Sun.COM 			4, 25, 3, 15, 31, 32, 32, 66840 },
1319999SWang.Lin@Sun.COM 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI,
1329999SWang.Lin@Sun.COM 			150000, /* 150Mb */
1339999SWang.Lin@Sun.COM 			122000, 0x87, 0x00, 7,
1349999SWang.Lin@Sun.COM 			4, 25, 3, 15, 31, 32, 32, 74200 },
1359999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
1369999SWang.Lin@Sun.COM 			25800, 0x88, 0x00, 8,
1379999SWang.Lin@Sun.COM 			0, 2, 3, 16, 33, 33, 33, 13360 },
1389999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
1399999SWang.Lin@Sun.COM 			49800, 0x89, 0x00, 9,
1409999SWang.Lin@Sun.COM 			2, 4, 3, 17, 34, 34, 34, 26720 },
1419999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
1429999SWang.Lin@Sun.COM 			71900, 0x8a, 0x00, 10,
1439999SWang.Lin@Sun.COM 			2, 6, 3, 18, 35, 35, 35, 40080 },
1449999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
1459999SWang.Lin@Sun.COM 			92500, 0x8b, 0x00, 11,
1469999SWang.Lin@Sun.COM 			4, 10, 3, 19, 36, 36, 36, 53440 },
1479999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
1489999SWang.Lin@Sun.COM 			130300, 0x8c, 0x00, 12,
1499999SWang.Lin@Sun.COM 			4, 14, 3, 20, 37, 37, 37, 80160 },
1509999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
1519999SWang.Lin@Sun.COM 			162800, 0x8d, 0x00, 13,
1529999SWang.Lin@Sun.COM 			4, 20, 3, 21, 38, 38, 38, 106880 },
1539999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
1549999SWang.Lin@Sun.COM 			178200, 0x8e, 0x00, 14,
1559999SWang.Lin@Sun.COM 			4, 23, 3, 22, 39, 39, 39, 120240 },
1569999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
1579999SWang.Lin@Sun.COM 			192100, 0x8f, 0x00, 15,
1589999SWang.Lin@Sun.COM 			4, 25, 3, 23, 40, 41, 41, 133600 },
1599999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI,
1609999SWang.Lin@Sun.COM 			300000, /* 300 Mb */
1619999SWang.Lin@Sun.COM 			207000, 0x8f, 0x00, 15,
1629999SWang.Lin@Sun.COM 			4, 25, 3, 23, 40, 41, 41, 148400 },
1639999SWang.Lin@Sun.COM 	},
1649999SWang.Lin@Sun.COM 	50,  /* probe interval */
1659999SWang.Lin@Sun.COM 	50,  /* rssi reduce interval */
1669999SWang.Lin@Sun.COM 	WLAN_RC_HT_FLAG,  /* Phy rates allowed initially */
1679999SWang.Lin@Sun.COM };
1689999SWang.Lin@Sun.COM 
1699999SWang.Lin@Sun.COM /*
1709999SWang.Lin@Sun.COM  * 4ms frame limit not used for NG mode.  The values filled
1719999SWang.Lin@Sun.COM  * for HT are the 64K max aggregate limit
1729999SWang.Lin@Sun.COM  */
1739999SWang.Lin@Sun.COM 
1749999SWang.Lin@Sun.COM static struct ath_rate_table ar5416_11ng_ratetable = {
1759999SWang.Lin@Sun.COM 	46,
1769999SWang.Lin@Sun.COM 	{0},
1779999SWang.Lin@Sun.COM 	{
1789999SWang.Lin@Sun.COM 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
1799999SWang.Lin@Sun.COM 			900, 0x1b, 0x00, 2,
1809999SWang.Lin@Sun.COM 			0, 0, 1, 0, 0, 0, 0, 0 },
1819999SWang.Lin@Sun.COM 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
1829999SWang.Lin@Sun.COM 			1900, 0x1a, 0x04, 4,
1839999SWang.Lin@Sun.COM 			1, 1, 1, 1, 1, 1, 1, 0 },
1849999SWang.Lin@Sun.COM 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
1859999SWang.Lin@Sun.COM 			4900, 0x19, 0x04, 11,
1869999SWang.Lin@Sun.COM 			2, 2, 2, 2, 2, 2, 2, 0 },
1879999SWang.Lin@Sun.COM 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
1889999SWang.Lin@Sun.COM 			8100, 0x18, 0x04, 22,
1899999SWang.Lin@Sun.COM 			3, 3, 2, 3, 3, 3, 3, 0 },
1909999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
1919999SWang.Lin@Sun.COM 			5400, 0x0b, 0x00, 12,
1929999SWang.Lin@Sun.COM 			4, 2, 1, 4, 4, 4, 4, 0 },
1939999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
1949999SWang.Lin@Sun.COM 			7800, 0x0f, 0x00, 18,
1959999SWang.Lin@Sun.COM 			4, 3, 1, 5, 5, 5, 5, 0 },
1969999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
1979999SWang.Lin@Sun.COM 			10100, 0x0a, 0x00, 24,
1989999SWang.Lin@Sun.COM 			6, 4, 1, 6, 6, 6, 6, 0 },
1999999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
2009999SWang.Lin@Sun.COM 			14100,  0x0e, 0x00, 36,
2019999SWang.Lin@Sun.COM 			6, 6, 2, 7, 7, 7, 7, 0 },
2029999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
2039999SWang.Lin@Sun.COM 			17700, 0x09, 0x00, 48,
2049999SWang.Lin@Sun.COM 			8, 10, 3, 8, 8, 8, 8, 0 },
2059999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
2069999SWang.Lin@Sun.COM 			23700, 0x0d, 0x00, 72,
2079999SWang.Lin@Sun.COM 			8, 14, 3, 9, 9, 9, 9, 0 },
2089999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
2099999SWang.Lin@Sun.COM 			27400, 0x08, 0x00, 96,
2109999SWang.Lin@Sun.COM 			8, 20, 3, 10, 10, 10, 10, 0 },
2119999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
2129999SWang.Lin@Sun.COM 			30900, 0x0c, 0x00, 108,
2139999SWang.Lin@Sun.COM 			8, 23, 3, 11, 11, 11, 11, 0 },
2149999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
2159999SWang.Lin@Sun.COM 			6400, 0x80, 0x00, 0,
2169999SWang.Lin@Sun.COM 			4, 2, 3, 12, 28, 12, 28, 3216 },
2179999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
2189999SWang.Lin@Sun.COM 			12700, 0x81, 0x00, 1,
2199999SWang.Lin@Sun.COM 			6, 4, 3, 13, 29, 13, 29, 6434 },
2209999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
2219999SWang.Lin@Sun.COM 			18800, 0x82, 0x00, 2,
2229999SWang.Lin@Sun.COM 			6, 6, 3, 14, 30, 14, 30, 9650 },
2239999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
2249999SWang.Lin@Sun.COM 			25000, 0x83, 0x00, 3,
2259999SWang.Lin@Sun.COM 			8, 10, 3, 15, 31, 15, 31, 12868 },
2269999SWang.Lin@Sun.COM 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
2279999SWang.Lin@Sun.COM 			36700, 0x84, 0x00, 4,
2289999SWang.Lin@Sun.COM 			8, 14, 3, 16, 32, 16, 32, 19304 },
2299999SWang.Lin@Sun.COM 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
2309999SWang.Lin@Sun.COM 			48100, 0x85, 0x00, 5,
2319999SWang.Lin@Sun.COM 			8, 20, 3, 17, 33, 17, 33, 25740 },
2329999SWang.Lin@Sun.COM 		{ INVALID,  VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
2339999SWang.Lin@Sun.COM 			53500, 0x86, 0x00, 6,
2349999SWang.Lin@Sun.COM 			8, 23, 3, 18, 34, 18, 34, 28956 },
2359999SWang.Lin@Sun.COM 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
2369999SWang.Lin@Sun.COM 			59000, 0x87, 0x00, 7,
2379999SWang.Lin@Sun.COM 			8, 25, 3, 19, 35, 19, 36, 32180 },
2389999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
2399999SWang.Lin@Sun.COM 			12700, 0x88, 0x00, 8,
2409999SWang.Lin@Sun.COM 			4, 2, 3, 20, 37, 20, 37, 6430 },
2419999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
2429999SWang.Lin@Sun.COM 			24800, 0x89, 0x00, 9,
2439999SWang.Lin@Sun.COM 			6, 4, 3, 21, 38, 21, 38, 12860 },
2449999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
2459999SWang.Lin@Sun.COM 			36600, 0x8a, 0x00, 10,
2469999SWang.Lin@Sun.COM 			6, 6, 3, 22, 39, 22, 39, 19300 },
2479999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
2489999SWang.Lin@Sun.COM 			48100, 0x8b, 0x00, 11,
2499999SWang.Lin@Sun.COM 			8, 10, 3, 23, 40, 23, 40, 25736 },
2509999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
2519999SWang.Lin@Sun.COM 			69500, 0x8c, 0x00, 12,
2529999SWang.Lin@Sun.COM 			8, 14, 3, 24, 41, 24, 41, 38600 },
2539999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
2549999SWang.Lin@Sun.COM 			89500, 0x8d, 0x00, 13,
2559999SWang.Lin@Sun.COM 			8, 20, 3, 25, 42, 25, 42, 51472 },
2569999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
2579999SWang.Lin@Sun.COM 			98900, 0x8e, 0x00, 14,
2589999SWang.Lin@Sun.COM 			8, 23, 3, 26, 43, 26, 44, 57890 },
2599999SWang.Lin@Sun.COM 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
2609999SWang.Lin@Sun.COM 			108300, 0x8f, 0x00, 15,
2619999SWang.Lin@Sun.COM 			8, 25, 3, 27, 44, 27, 45, 64320 },
2629999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
2639999SWang.Lin@Sun.COM 			13200, 0x80, 0x00, 0,
2649999SWang.Lin@Sun.COM 			8, 2, 3, 12, 28, 28, 28, 6684 },
2659999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
2669999SWang.Lin@Sun.COM 			25900, 0x81, 0x00, 1,
2679999SWang.Lin@Sun.COM 			8, 4, 3, 13, 29, 29, 29, 13368 },
2689999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
2699999SWang.Lin@Sun.COM 			38600, 0x82, 0x00, 2,
2709999SWang.Lin@Sun.COM 			8, 6, 3, 14, 30, 30, 30, 20052 },
2719999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
2729999SWang.Lin@Sun.COM 			49800, 0x83, 0x00, 3,
2739999SWang.Lin@Sun.COM 			8, 10, 3, 15, 31, 31, 31, 26738 },
2749999SWang.Lin@Sun.COM 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
2759999SWang.Lin@Sun.COM 			72200, 0x84, 0x00, 4,
2769999SWang.Lin@Sun.COM 			8, 14, 3, 16, 32, 32, 32, 40104 },
2779999SWang.Lin@Sun.COM 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
2789999SWang.Lin@Sun.COM 			92900, 0x85, 0x00, 5,
2799999SWang.Lin@Sun.COM 			8, 20, 3, 17, 33, 33, 33, 53476 },
2809999SWang.Lin@Sun.COM 		{ INVALID,  VALID_40, WLAN_RC_PHY_HT_40_SS,
2819999SWang.Lin@Sun.COM 			121500, /* 121.5 Mb */
2829999SWang.Lin@Sun.COM 			102700, 0x86, 0x00, 6,
2839999SWang.Lin@Sun.COM 			8, 23, 3, 18, 34, 34, 34, 60156 },
2849999SWang.Lin@Sun.COM 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
2859999SWang.Lin@Sun.COM 			112000, 0x87, 0x00, 7,
2869999SWang.Lin@Sun.COM 			8, 23, 3, 19, 35, 36, 36, 66840 },
2879999SWang.Lin@Sun.COM 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI,
2889999SWang.Lin@Sun.COM 			150000, /* 150 Mb */
2899999SWang.Lin@Sun.COM 			122000, 0x87, 0x00, 7,
2909999SWang.Lin@Sun.COM 			8, 25, 3, 19, 35, 36, 36, 74200 },
2919999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
2929999SWang.Lin@Sun.COM 			25800, 0x88, 0x00, 8,
2939999SWang.Lin@Sun.COM 			8, 2, 3, 20, 37, 37, 37, 13360 },
2949999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
2959999SWang.Lin@Sun.COM 			49800, 0x89, 0x00, 9,
2969999SWang.Lin@Sun.COM 			8, 4, 3, 21, 38, 38, 38, 26720 },
2979999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
2989999SWang.Lin@Sun.COM 			71900, 0x8a, 0x00, 10,
2999999SWang.Lin@Sun.COM 			8, 6, 3, 22, 39, 39, 39, 40080 },
3009999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
3019999SWang.Lin@Sun.COM 			92500, 0x8b, 0x00, 11,
3029999SWang.Lin@Sun.COM 			8, 10, 3, 23, 40, 40, 40, 53440 },
3039999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
3049999SWang.Lin@Sun.COM 			130300, 0x8c, 0x00, 12,
3059999SWang.Lin@Sun.COM 			8, 14, 3, 24, 41, 41, 41, 80160 },
3069999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
3079999SWang.Lin@Sun.COM 			162800, 0x8d, 0x00, 13,
3089999SWang.Lin@Sun.COM 			8, 20, 3, 25, 42, 42, 42, 106880 },
3099999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
3109999SWang.Lin@Sun.COM 			178200, 0x8e, 0x00, 14,
3119999SWang.Lin@Sun.COM 			8, 23, 3, 26, 43, 43, 43, 120240 },
3129999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
3139999SWang.Lin@Sun.COM 			192100, 0x8f, 0x00, 15,
3149999SWang.Lin@Sun.COM 			8, 23, 3, 27, 44, 45, 45, 133600 },
3159999SWang.Lin@Sun.COM 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI,
3169999SWang.Lin@Sun.COM 			300000, /* 300 Mb */
3179999SWang.Lin@Sun.COM 			207000, 0x8f, 0x00, 15,
3189999SWang.Lin@Sun.COM 			8, 25, 3, 27, 44, 45, 45, 148400 },
3199999SWang.Lin@Sun.COM 		},
3209999SWang.Lin@Sun.COM 	50,  /* probe interval */
3219999SWang.Lin@Sun.COM 	50,  /* rssi reduce interval */
3229999SWang.Lin@Sun.COM 	WLAN_RC_HT_FLAG,  /* Phy rates allowed initially */
3239999SWang.Lin@Sun.COM };
3249999SWang.Lin@Sun.COM 
3259999SWang.Lin@Sun.COM static struct ath_rate_table ar5416_11a_ratetable = {
3269999SWang.Lin@Sun.COM 	8,
3279999SWang.Lin@Sun.COM 	{0},
3289999SWang.Lin@Sun.COM 	{
3299999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
3309999SWang.Lin@Sun.COM 			5400, 0x0b, 0x00, (0x80|12),
3319999SWang.Lin@Sun.COM 			0, 2, 1, 0, 0 },
3329999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
3339999SWang.Lin@Sun.COM 			7800, 0x0f, 0x00, 18,
3349999SWang.Lin@Sun.COM 			0, 3, 1, 1, 0 },
3359999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
3369999SWang.Lin@Sun.COM 			10000, 0x0a, 0x00, (0x80|24),
3379999SWang.Lin@Sun.COM 			2, 4, 2, 2, 0 },
3389999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
3399999SWang.Lin@Sun.COM 			13900, 0x0e, 0x00, 36,
3409999SWang.Lin@Sun.COM 			2, 6, 2, 3, 0 },
3419999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
3429999SWang.Lin@Sun.COM 			17300, 0x09, 0x00, (0x80|48),
3439999SWang.Lin@Sun.COM 			4, 10, 3, 4, 0 },
3449999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
3459999SWang.Lin@Sun.COM 			23000, 0x0d, 0x00, 72,
3469999SWang.Lin@Sun.COM 			4, 14, 3, 5, 0 },
3479999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
3489999SWang.Lin@Sun.COM 			27400, 0x08, 0x00, 96,
3499999SWang.Lin@Sun.COM 			4, 19, 3, 6, 0 },
3509999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
3519999SWang.Lin@Sun.COM 			29300, 0x0c, 0x00, 108,
3529999SWang.Lin@Sun.COM 			4, 23, 3, 7, 0 },
3539999SWang.Lin@Sun.COM 	},
3549999SWang.Lin@Sun.COM 	50,  /* probe interval */
3559999SWang.Lin@Sun.COM 	50,  /* rssi reduce interval */
3569999SWang.Lin@Sun.COM 	0,   /* Phy rates allowed initially */
3579999SWang.Lin@Sun.COM };
3589999SWang.Lin@Sun.COM 
3599999SWang.Lin@Sun.COM static struct ath_rate_table ar5416_11g_ratetable = {
3609999SWang.Lin@Sun.COM 	12,
3619999SWang.Lin@Sun.COM 	{0},
3629999SWang.Lin@Sun.COM 	{
3639999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
3649999SWang.Lin@Sun.COM 			900, 0x1b, 0x00, 2,
3659999SWang.Lin@Sun.COM 			0, 0, 1, 0, 0 },
3669999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
3679999SWang.Lin@Sun.COM 			1900, 0x1a, 0x04, 4,
3689999SWang.Lin@Sun.COM 			1, 1, 1, 1, 0 },
3699999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
3709999SWang.Lin@Sun.COM 			4900, 0x19, 0x04, 11,
3719999SWang.Lin@Sun.COM 			2, 2, 2, 2, 0 },
3729999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
3739999SWang.Lin@Sun.COM 			8100, 0x18, 0x04, 22,
3749999SWang.Lin@Sun.COM 			3, 3, 2, 3, 0 },
3759999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
3769999SWang.Lin@Sun.COM 			5400, 0x0b, 0x00, 12,
3779999SWang.Lin@Sun.COM 			4, 2, 1, 4, 0 },
3789999SWang.Lin@Sun.COM 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
3799999SWang.Lin@Sun.COM 			7800, 0x0f, 0x00, 18,
3809999SWang.Lin@Sun.COM 			4, 3, 1, 5, 0 },
3819999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
3829999SWang.Lin@Sun.COM 			10000, 0x0a, 0x00, 24,
3839999SWang.Lin@Sun.COM 			6, 4, 1, 6, 0 },
3849999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
3859999SWang.Lin@Sun.COM 			13900, 0x0e, 0x00, 36,
3869999SWang.Lin@Sun.COM 			6, 6, 2, 7, 0 },
3879999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
3889999SWang.Lin@Sun.COM 			17300, 0x09, 0x00, 48,
3899999SWang.Lin@Sun.COM 			8, 10, 3, 8, 0 },
3909999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
3919999SWang.Lin@Sun.COM 			23000, 0x0d, 0x00, 72,
3929999SWang.Lin@Sun.COM 			8, 14, 3, 9, 0 },
3939999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
3949999SWang.Lin@Sun.COM 			27400, 0x08, 0x00, 96,
3959999SWang.Lin@Sun.COM 			8, 19, 3, 10, 0 },
3969999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
3979999SWang.Lin@Sun.COM 			29300, 0x0c, 0x00, 108,
3989999SWang.Lin@Sun.COM 			8, 23, 3, 11, 0 },
3999999SWang.Lin@Sun.COM 	},
4009999SWang.Lin@Sun.COM 	50,  /* probe interval */
4019999SWang.Lin@Sun.COM 	50,  /* rssi reduce interval */
4029999SWang.Lin@Sun.COM 	0,   /* Phy rates allowed initially */
4039999SWang.Lin@Sun.COM };
4049999SWang.Lin@Sun.COM 
4059999SWang.Lin@Sun.COM static struct ath_rate_table ar5416_11b_ratetable = {
4069999SWang.Lin@Sun.COM 	4,
4079999SWang.Lin@Sun.COM 	{0},
4089999SWang.Lin@Sun.COM 	{
4099999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
4109999SWang.Lin@Sun.COM 			900, 0x1b,  0x00, (0x80|2),
4119999SWang.Lin@Sun.COM 			0, 0, 1, 0, 0 },
4129999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
4139999SWang.Lin@Sun.COM 			1800, 0x1a, 0x04, (0x80|4),
4149999SWang.Lin@Sun.COM 			1, 1, 1, 1, 0 },
4159999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
4169999SWang.Lin@Sun.COM 			4300, 0x19, 0x04, (0x80|11),
4179999SWang.Lin@Sun.COM 			1, 2, 2, 2, 0 },
4189999SWang.Lin@Sun.COM 		{ VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
4199999SWang.Lin@Sun.COM 			7100, 0x18, 0x04, (0x80|22),
4209999SWang.Lin@Sun.COM 			1, 4, 100, 3, 0 },
4219999SWang.Lin@Sun.COM 	},
4229999SWang.Lin@Sun.COM 	100, /* probe interval */
4239999SWang.Lin@Sun.COM 	100, /* rssi reduce interval */
4249999SWang.Lin@Sun.COM 	0,   /* Phy rates allowed initially */
4259999SWang.Lin@Sun.COM };
4269999SWang.Lin@Sun.COM 
427*11729SWang.Lin@Sun.COM static inline int8_t
median(int8_t a,int8_t b,int8_t c)428*11729SWang.Lin@Sun.COM median(int8_t a, int8_t b, int8_t c)
429*11729SWang.Lin@Sun.COM {
430*11729SWang.Lin@Sun.COM 	if (a >= b) {
431*11729SWang.Lin@Sun.COM 		if (b >= c)
432*11729SWang.Lin@Sun.COM 			return (b);
433*11729SWang.Lin@Sun.COM 		else if (a > c)
434*11729SWang.Lin@Sun.COM 			return (c);
435*11729SWang.Lin@Sun.COM 		else
436*11729SWang.Lin@Sun.COM 			return (a);
437*11729SWang.Lin@Sun.COM 	} else {
438*11729SWang.Lin@Sun.COM 		if (a >= c)
439*11729SWang.Lin@Sun.COM 			return (a);
440*11729SWang.Lin@Sun.COM 		else if (b >= c)
441*11729SWang.Lin@Sun.COM 			return (c);
442*11729SWang.Lin@Sun.COM 		else
443*11729SWang.Lin@Sun.COM 			return (b);
444*11729SWang.Lin@Sun.COM 	}
445*11729SWang.Lin@Sun.COM }
446*11729SWang.Lin@Sun.COM 
447*11729SWang.Lin@Sun.COM static void
arn_rc_sort_validrates(struct ath_rate_table * rate_table,struct ath_rate_priv * ath_rc_priv)448*11729SWang.Lin@Sun.COM arn_rc_sort_validrates(struct ath_rate_table *rate_table,
449*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv)
450*11729SWang.Lin@Sun.COM {
451*11729SWang.Lin@Sun.COM 	uint8_t i, j, idx, idx_next;
452*11729SWang.Lin@Sun.COM 
453*11729SWang.Lin@Sun.COM 	for (i = ath_rc_priv->max_valid_rate - 1; i > 0; i--) {
454*11729SWang.Lin@Sun.COM 		for (j = 0; j <= i-1; j++) {
455*11729SWang.Lin@Sun.COM 			idx = ath_rc_priv->valid_rate_index[j];
456*11729SWang.Lin@Sun.COM 			idx_next = ath_rc_priv->valid_rate_index[j+1];
457*11729SWang.Lin@Sun.COM 
458*11729SWang.Lin@Sun.COM 			if (rate_table->info[idx].ratekbps >
459*11729SWang.Lin@Sun.COM 			    rate_table->info[idx_next].ratekbps) {
460*11729SWang.Lin@Sun.COM 				ath_rc_priv->valid_rate_index[j] = idx_next;
461*11729SWang.Lin@Sun.COM 				ath_rc_priv->valid_rate_index[j+1] = idx;
462*11729SWang.Lin@Sun.COM 			}
463*11729SWang.Lin@Sun.COM 		}
464*11729SWang.Lin@Sun.COM 	}
465*11729SWang.Lin@Sun.COM }
466*11729SWang.Lin@Sun.COM 
467*11729SWang.Lin@Sun.COM static void
arn_rc_init_valid_txmask(struct ath_rate_priv * ath_rc_priv)468*11729SWang.Lin@Sun.COM arn_rc_init_valid_txmask(struct ath_rate_priv *ath_rc_priv)
469*11729SWang.Lin@Sun.COM {
470*11729SWang.Lin@Sun.COM 	uint8_t i;
471*11729SWang.Lin@Sun.COM 
472*11729SWang.Lin@Sun.COM 	for (i = 0; i < ath_rc_priv->rate_table_size; i++)
473*11729SWang.Lin@Sun.COM 		ath_rc_priv->valid_rate_index[i] = 0;
474*11729SWang.Lin@Sun.COM }
475*11729SWang.Lin@Sun.COM 
476*11729SWang.Lin@Sun.COM static inline void
arn_rc_set_valid_txmask(struct ath_rate_priv * ath_rc_priv,uint8_t index,int valid_tx_rate)477*11729SWang.Lin@Sun.COM arn_rc_set_valid_txmask(struct ath_rate_priv *ath_rc_priv,
478*11729SWang.Lin@Sun.COM     uint8_t index, int valid_tx_rate)
479*11729SWang.Lin@Sun.COM {
480*11729SWang.Lin@Sun.COM 	ASSERT(index <= ath_rc_priv->rate_table_size);
481*11729SWang.Lin@Sun.COM 	ath_rc_priv->valid_rate_index[index] = valid_tx_rate ? 1 : 0;
482*11729SWang.Lin@Sun.COM }
483*11729SWang.Lin@Sun.COM 
484*11729SWang.Lin@Sun.COM static inline int
485*11729SWang.Lin@Sun.COM /* LINTED E_STATIC_UNUSED */
arn_rc_isvalid_txmask(struct ath_rate_priv * ath_rc_priv,uint8_t index)486*11729SWang.Lin@Sun.COM arn_rc_isvalid_txmask(struct ath_rate_priv *ath_rc_priv, uint8_t index)
487*11729SWang.Lin@Sun.COM {
488*11729SWang.Lin@Sun.COM 	ASSERT(index <= ath_rc_priv->rate_table_size);
489*11729SWang.Lin@Sun.COM 	return (ath_rc_priv->valid_rate_index[index]);
490*11729SWang.Lin@Sun.COM }
491*11729SWang.Lin@Sun.COM 
492*11729SWang.Lin@Sun.COM /* ARGSUSED */
493*11729SWang.Lin@Sun.COM static inline int
arn_rc_get_nextvalid_txrate(struct ath_rate_table * rate_table,struct ath_rate_priv * ath_rc_priv,uint8_t cur_valid_txrate,uint8_t * next_idx)494*11729SWang.Lin@Sun.COM arn_rc_get_nextvalid_txrate(struct ath_rate_table *rate_table,
495*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv,
496*11729SWang.Lin@Sun.COM     uint8_t cur_valid_txrate,
497*11729SWang.Lin@Sun.COM     uint8_t *next_idx)
498*11729SWang.Lin@Sun.COM {
499*11729SWang.Lin@Sun.COM 	uint8_t i;
500*11729SWang.Lin@Sun.COM 
501*11729SWang.Lin@Sun.COM 	for (i = 0; i < ath_rc_priv->max_valid_rate - 1; i++) {
502*11729SWang.Lin@Sun.COM 		if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
503*11729SWang.Lin@Sun.COM 			*next_idx = ath_rc_priv->valid_rate_index[i+1];
504*11729SWang.Lin@Sun.COM 			return (1);
505*11729SWang.Lin@Sun.COM 		}
506*11729SWang.Lin@Sun.COM 	}
507*11729SWang.Lin@Sun.COM 
508*11729SWang.Lin@Sun.COM 	/* No more valid rates */
509*11729SWang.Lin@Sun.COM 	*next_idx = 0;
510*11729SWang.Lin@Sun.COM 
511*11729SWang.Lin@Sun.COM 	return (0);
512*11729SWang.Lin@Sun.COM }
513*11729SWang.Lin@Sun.COM 
514*11729SWang.Lin@Sun.COM /* Return true only for single stream */
515*11729SWang.Lin@Sun.COM static int
arn_rc_valid_phyrate(uint32_t phy,uint32_t capflag,int ignore_cw)516*11729SWang.Lin@Sun.COM arn_rc_valid_phyrate(uint32_t phy, uint32_t capflag, int ignore_cw)
517*11729SWang.Lin@Sun.COM {
518*11729SWang.Lin@Sun.COM 	if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG))
519*11729SWang.Lin@Sun.COM 		return (0);
520*11729SWang.Lin@Sun.COM 	if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG))
521*11729SWang.Lin@Sun.COM 		return (0);
522*11729SWang.Lin@Sun.COM 	if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG))
523*11729SWang.Lin@Sun.COM 		return (0);
524*11729SWang.Lin@Sun.COM 	if (!ignore_cw && WLAN_RC_PHY_HT(phy))
525*11729SWang.Lin@Sun.COM 		if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG))
526*11729SWang.Lin@Sun.COM 			return (0);
527*11729SWang.Lin@Sun.COM 		if (!WLAN_RC_PHY_40(phy) && (capflag & WLAN_RC_40_FLAG))
528*11729SWang.Lin@Sun.COM 			return (0);
529*11729SWang.Lin@Sun.COM 
530*11729SWang.Lin@Sun.COM 	return (1);
531*11729SWang.Lin@Sun.COM }
532*11729SWang.Lin@Sun.COM 
533*11729SWang.Lin@Sun.COM /* ARGSUSED */
534*11729SWang.Lin@Sun.COM static inline int
arn_rc_get_nextlowervalid_txrate(struct ath_rate_table * rate_table,struct ath_rate_priv * ath_rc_priv,uint8_t cur_valid_txrate,uint8_t * next_idx)535*11729SWang.Lin@Sun.COM arn_rc_get_nextlowervalid_txrate(struct ath_rate_table *rate_table,
536*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv,
537*11729SWang.Lin@Sun.COM     uint8_t cur_valid_txrate, uint8_t *next_idx)
538*11729SWang.Lin@Sun.COM {
539*11729SWang.Lin@Sun.COM 	int8_t i;
540*11729SWang.Lin@Sun.COM 
541*11729SWang.Lin@Sun.COM 	for (i = 1; i < ath_rc_priv->max_valid_rate; i++) {
542*11729SWang.Lin@Sun.COM 		if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
543*11729SWang.Lin@Sun.COM 			*next_idx = ath_rc_priv->valid_rate_index[i-1];
544*11729SWang.Lin@Sun.COM 			return (1);
545*11729SWang.Lin@Sun.COM 		}
546*11729SWang.Lin@Sun.COM 	}
547*11729SWang.Lin@Sun.COM 
548*11729SWang.Lin@Sun.COM 	return (0);
549*11729SWang.Lin@Sun.COM }
550*11729SWang.Lin@Sun.COM 
551*11729SWang.Lin@Sun.COM static uint8_t
arn_rc_init_validrates(struct ath_rate_priv * ath_rc_priv,struct ath_rate_table * rate_table,uint32_t capflag)552*11729SWang.Lin@Sun.COM arn_rc_init_validrates(struct ath_rate_priv *ath_rc_priv,
553*11729SWang.Lin@Sun.COM     struct ath_rate_table *rate_table, uint32_t capflag)
554*11729SWang.Lin@Sun.COM {
555*11729SWang.Lin@Sun.COM 	uint8_t i, hi = 0;
556*11729SWang.Lin@Sun.COM 	uint32_t valid;
557*11729SWang.Lin@Sun.COM 
558*11729SWang.Lin@Sun.COM 	for (i = 0; i < rate_table->rate_cnt; i++) {
559*11729SWang.Lin@Sun.COM 		valid = (ath_rc_priv->single_stream ?
560*11729SWang.Lin@Sun.COM 		    rate_table->info[i].valid_single_stream :
561*11729SWang.Lin@Sun.COM 		    rate_table->info[i].valid);
562*11729SWang.Lin@Sun.COM 		if (valid == 1) {
563*11729SWang.Lin@Sun.COM 			uint32_t phy = rate_table->info[i].phy;
564*11729SWang.Lin@Sun.COM 			uint8_t valid_rate_count = 0;
565*11729SWang.Lin@Sun.COM 
566*11729SWang.Lin@Sun.COM 			if (!arn_rc_valid_phyrate(phy, capflag, 0))
567*11729SWang.Lin@Sun.COM 				continue;
568*11729SWang.Lin@Sun.COM 
569*11729SWang.Lin@Sun.COM 			valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy];
570*11729SWang.Lin@Sun.COM 
571*11729SWang.Lin@Sun.COM 			ath_rc_priv->
572*11729SWang.Lin@Sun.COM 			    valid_phy_rateidx[phy][valid_rate_count] = i;
573*11729SWang.Lin@Sun.COM 			ath_rc_priv->valid_phy_ratecnt[phy] += 1;
574*11729SWang.Lin@Sun.COM 			arn_rc_set_valid_txmask(ath_rc_priv, i, 1);
575*11729SWang.Lin@Sun.COM 			hi = A_MAX(hi, i);
576*11729SWang.Lin@Sun.COM 		}
577*11729SWang.Lin@Sun.COM 	}
578*11729SWang.Lin@Sun.COM 
579*11729SWang.Lin@Sun.COM 	return (hi);
580*11729SWang.Lin@Sun.COM }
581*11729SWang.Lin@Sun.COM 
582*11729SWang.Lin@Sun.COM static uint8_t
arn_rc_setvalid_rates(struct ath_rate_priv * ath_rc_priv,struct ath_rate_table * rate_table,struct ath_rateset * rateset,uint32_t capflag)583*11729SWang.Lin@Sun.COM arn_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv,
584*11729SWang.Lin@Sun.COM     struct ath_rate_table *rate_table,
585*11729SWang.Lin@Sun.COM     struct ath_rateset *rateset,
586*11729SWang.Lin@Sun.COM     uint32_t capflag)
587*11729SWang.Lin@Sun.COM {
588*11729SWang.Lin@Sun.COM 	uint8_t i, j, hi = 0;
589*11729SWang.Lin@Sun.COM 
590*11729SWang.Lin@Sun.COM 	/* Use intersection of working rates and valid rates */
591*11729SWang.Lin@Sun.COM 	for (i = 0; i < rateset->rs_nrates; i++) {
592*11729SWang.Lin@Sun.COM 		for (j = 0; j < rate_table->rate_cnt; j++) {
593*11729SWang.Lin@Sun.COM 			uint32_t phy = rate_table->info[j].phy;
594*11729SWang.Lin@Sun.COM 			uint32_t valid = (ath_rc_priv->single_stream ?
595*11729SWang.Lin@Sun.COM 			    rate_table->info[j].valid_single_stream :
596*11729SWang.Lin@Sun.COM 			    rate_table->info[j].valid);
597*11729SWang.Lin@Sun.COM 			uint8_t rate = rateset->rs_rates[i];
598*11729SWang.Lin@Sun.COM 			uint8_t dot11rate = rate_table->info[j].dot11rate;
599*11729SWang.Lin@Sun.COM 
600*11729SWang.Lin@Sun.COM 			/*
601*11729SWang.Lin@Sun.COM 			 * We allow a rate only if its valid and the
602*11729SWang.Lin@Sun.COM 			 * capflag matches one of the validity
603*11729SWang.Lin@Sun.COM 			 * (VALID/VALID_20/VALID_40) flags
604*11729SWang.Lin@Sun.COM 			 */
605*11729SWang.Lin@Sun.COM 			if (((rate & 0x7F) == (dot11rate & 0x7F)) &&
606*11729SWang.Lin@Sun.COM 			    ((valid & WLAN_RC_CAP_MODE(capflag)) ==
607*11729SWang.Lin@Sun.COM 			    WLAN_RC_CAP_MODE(capflag)) &&
608*11729SWang.Lin@Sun.COM 			    !WLAN_RC_PHY_HT(phy)) {
609*11729SWang.Lin@Sun.COM 				uint8_t valid_rate_count = 0;
610*11729SWang.Lin@Sun.COM 
611*11729SWang.Lin@Sun.COM 				if (!arn_rc_valid_phyrate(phy, capflag, 0))
612*11729SWang.Lin@Sun.COM 					continue;
613*11729SWang.Lin@Sun.COM 
614*11729SWang.Lin@Sun.COM 				valid_rate_count =
615*11729SWang.Lin@Sun.COM 				    ath_rc_priv->valid_phy_ratecnt[phy];
616*11729SWang.Lin@Sun.COM 
617*11729SWang.Lin@Sun.COM 				ath_rc_priv->valid_phy_rateidx[phy]
618*11729SWang.Lin@Sun.COM 				    [valid_rate_count] = j;
619*11729SWang.Lin@Sun.COM 				ath_rc_priv->valid_phy_ratecnt[phy] += 1;
620*11729SWang.Lin@Sun.COM 				arn_rc_set_valid_txmask(ath_rc_priv, j, 1);
621*11729SWang.Lin@Sun.COM 				hi = A_MAX(hi, j);
622*11729SWang.Lin@Sun.COM 			}
623*11729SWang.Lin@Sun.COM 		}
624*11729SWang.Lin@Sun.COM 	}
625*11729SWang.Lin@Sun.COM 
626*11729SWang.Lin@Sun.COM 	return (hi);
627*11729SWang.Lin@Sun.COM }
628*11729SWang.Lin@Sun.COM 
629*11729SWang.Lin@Sun.COM static uint8_t
arn_rc_setvalid_htrates(struct ath_rate_priv * ath_rc_priv,struct ath_rate_table * rate_table,uint8_t * mcs_set,uint32_t capflag)630*11729SWang.Lin@Sun.COM arn_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv,
631*11729SWang.Lin@Sun.COM     struct ath_rate_table *rate_table,
632*11729SWang.Lin@Sun.COM     uint8_t *mcs_set, uint32_t capflag)
633*11729SWang.Lin@Sun.COM {
634*11729SWang.Lin@Sun.COM 	struct ath_rateset *rateset = (struct ath_rateset *)mcs_set;
635*11729SWang.Lin@Sun.COM 
636*11729SWang.Lin@Sun.COM 	uint8_t i, j, hi = 0;
637*11729SWang.Lin@Sun.COM 
638*11729SWang.Lin@Sun.COM 	/* Use intersection of working rates and valid rates */
639*11729SWang.Lin@Sun.COM 	for (i = 0; i < rateset->rs_nrates; i++) {
640*11729SWang.Lin@Sun.COM 		for (j = 0; j < rate_table->rate_cnt; j++) {
641*11729SWang.Lin@Sun.COM 			uint32_t phy = rate_table->info[j].phy;
642*11729SWang.Lin@Sun.COM 			uint32_t valid = (ath_rc_priv->single_stream ?
643*11729SWang.Lin@Sun.COM 			    rate_table->info[j].valid_single_stream :
644*11729SWang.Lin@Sun.COM 			    rate_table->info[j].valid);
645*11729SWang.Lin@Sun.COM 			uint8_t rate = rateset->rs_rates[i];
646*11729SWang.Lin@Sun.COM 			uint8_t dot11rate = rate_table->info[j].dot11rate;
647*11729SWang.Lin@Sun.COM 
648*11729SWang.Lin@Sun.COM 			if (((rate & 0x7F) != (dot11rate & 0x7F)) ||
649*11729SWang.Lin@Sun.COM 			    !WLAN_RC_PHY_HT(phy) ||
650*11729SWang.Lin@Sun.COM 			    !WLAN_RC_PHY_HT_VALID(valid, capflag))
651*11729SWang.Lin@Sun.COM 				continue;
652*11729SWang.Lin@Sun.COM 
653*11729SWang.Lin@Sun.COM 			if (!arn_rc_valid_phyrate(phy, capflag, 0))
654*11729SWang.Lin@Sun.COM 				continue;
655*11729SWang.Lin@Sun.COM 
656*11729SWang.Lin@Sun.COM 			ath_rc_priv->valid_phy_rateidx[phy]
657*11729SWang.Lin@Sun.COM 			    [ath_rc_priv->valid_phy_ratecnt[phy]] = j;
658*11729SWang.Lin@Sun.COM 			ath_rc_priv->valid_phy_ratecnt[phy] += 1;
659*11729SWang.Lin@Sun.COM 			arn_rc_set_valid_txmask(ath_rc_priv, j, 1);
660*11729SWang.Lin@Sun.COM 			hi = A_MAX(hi, j);
661*11729SWang.Lin@Sun.COM 		}
662*11729SWang.Lin@Sun.COM 	}
663*11729SWang.Lin@Sun.COM 
664*11729SWang.Lin@Sun.COM 	return (hi);
665*11729SWang.Lin@Sun.COM }
666*11729SWang.Lin@Sun.COM 
667*11729SWang.Lin@Sun.COM /* ARGSUSED */
668*11729SWang.Lin@Sun.COM static uint8_t
arn_rc_ratefind_ht(struct arn_softc * sc,struct ath_rate_priv * ath_rc_priv,struct ath_rate_table * rate_table,int probe_allowed,int * is_probing,int is_retry)669*11729SWang.Lin@Sun.COM arn_rc_ratefind_ht(struct arn_softc *sc,
670*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv,
671*11729SWang.Lin@Sun.COM     struct ath_rate_table *rate_table,
672*11729SWang.Lin@Sun.COM     int probe_allowed, int *is_probing,
673*11729SWang.Lin@Sun.COM     int is_retry)
674*11729SWang.Lin@Sun.COM {
675*11729SWang.Lin@Sun.COM 	uint32_t dt, best_thruput, this_thruput, now_msec;
676*11729SWang.Lin@Sun.COM 	uint8_t rate, next_rate, best_rate, maxindex, minindex;
677*11729SWang.Lin@Sun.COM 	int8_t  rssi_last, rssi_reduce = 0, index = 0;
678*11729SWang.Lin@Sun.COM 
679*11729SWang.Lin@Sun.COM 	*is_probing = 0;
680*11729SWang.Lin@Sun.COM 
681*11729SWang.Lin@Sun.COM 	rssi_last = median(ath_rc_priv->rssi_last,
682*11729SWang.Lin@Sun.COM 	    ath_rc_priv->rssi_last_prev,
683*11729SWang.Lin@Sun.COM 	    ath_rc_priv->rssi_last_prev2);
684*11729SWang.Lin@Sun.COM 
685*11729SWang.Lin@Sun.COM 	/*
686*11729SWang.Lin@Sun.COM 	 * Age (reduce) last ack rssi based on how old it is.
687*11729SWang.Lin@Sun.COM 	 * The bizarre numbers are so the delta is 160msec,
688*11729SWang.Lin@Sun.COM 	 * meaning we divide by 16.
689*11729SWang.Lin@Sun.COM 	 * 0msec   <= dt <= 25msec: don't derate
690*11729SWang.Lin@Sun.COM 	 * 25msec  <= dt <= 185msec: derate linearly from 0 to 10dB
691*11729SWang.Lin@Sun.COM 	 * 185msec <= dt: derate by 10dB
692*11729SWang.Lin@Sun.COM 	 */
693*11729SWang.Lin@Sun.COM 
694*11729SWang.Lin@Sun.COM 	/* now_msec = jiffies_to_msecs(jiffies); */
695*11729SWang.Lin@Sun.COM 	now_msec = drv_hztousec(ddi_get_lbolt())/1000; /* mescs ? */
696*11729SWang.Lin@Sun.COM 	dt = now_msec - ath_rc_priv->rssi_time;
697*11729SWang.Lin@Sun.COM 
698*11729SWang.Lin@Sun.COM 	if (dt >= 185)
699*11729SWang.Lin@Sun.COM 		rssi_reduce = 10;
700*11729SWang.Lin@Sun.COM 	else if (dt >= 25)
701*11729SWang.Lin@Sun.COM 		rssi_reduce = (uint8_t)((dt - 25) >> 4);
702*11729SWang.Lin@Sun.COM 
703*11729SWang.Lin@Sun.COM 	/* Now reduce rssi_last by rssi_reduce */
704*11729SWang.Lin@Sun.COM 	if (rssi_last < rssi_reduce)
705*11729SWang.Lin@Sun.COM 		rssi_last = 0;
706*11729SWang.Lin@Sun.COM 	else
707*11729SWang.Lin@Sun.COM 		rssi_last -= rssi_reduce;
708*11729SWang.Lin@Sun.COM 
709*11729SWang.Lin@Sun.COM 	/*
710*11729SWang.Lin@Sun.COM 	 * Now look up the rate in the rssi table and return it.
711*11729SWang.Lin@Sun.COM 	 * If no rates match then we return 0 (lowest rate)
712*11729SWang.Lin@Sun.COM 	 */
713*11729SWang.Lin@Sun.COM 
714*11729SWang.Lin@Sun.COM 	best_thruput = 0;
715*11729SWang.Lin@Sun.COM 	maxindex = ath_rc_priv->max_valid_rate-1;
716*11729SWang.Lin@Sun.COM 
717*11729SWang.Lin@Sun.COM 	minindex = 0;
718*11729SWang.Lin@Sun.COM 	best_rate = minindex;
719*11729SWang.Lin@Sun.COM 
720*11729SWang.Lin@Sun.COM 	/*
721*11729SWang.Lin@Sun.COM 	 * Try the higher rate first. It will reduce memory moving time
722*11729SWang.Lin@Sun.COM 	 * if we have very good channel characteristics.
723*11729SWang.Lin@Sun.COM 	 */
724*11729SWang.Lin@Sun.COM 	for (index = maxindex; index >= minindex; index--) {
725*11729SWang.Lin@Sun.COM 		uint8_t per_thres;
726*11729SWang.Lin@Sun.COM 
727*11729SWang.Lin@Sun.COM 		rate = ath_rc_priv->valid_rate_index[index];
728*11729SWang.Lin@Sun.COM 		if (rate > ath_rc_priv->rate_max_phy)
729*11729SWang.Lin@Sun.COM 			continue;
730*11729SWang.Lin@Sun.COM 
731*11729SWang.Lin@Sun.COM 		/*
732*11729SWang.Lin@Sun.COM 		 * For TCP the average collision rate is around 11%,
733*11729SWang.Lin@Sun.COM 		 * so we ignore PERs less than this.  This is to
734*11729SWang.Lin@Sun.COM 		 * prevent the rate we are currently using (whose
735*11729SWang.Lin@Sun.COM 		 * PER might be in the 10-15 range because of TCP
736*11729SWang.Lin@Sun.COM 		 * collisions) looking worse than the next lower
737*11729SWang.Lin@Sun.COM 		 * rate whose PER has decayed close to 0.  If we
738*11729SWang.Lin@Sun.COM 		 * used to next lower rate, its PER would grow to
739*11729SWang.Lin@Sun.COM 		 * 10-15 and we would be worse off then staying
740*11729SWang.Lin@Sun.COM 		 * at the current rate.
741*11729SWang.Lin@Sun.COM 		 */
742*11729SWang.Lin@Sun.COM 		per_thres = ath_rc_priv->state[rate].per;
743*11729SWang.Lin@Sun.COM 		if (per_thres < 12)
744*11729SWang.Lin@Sun.COM 			per_thres = 12;
745*11729SWang.Lin@Sun.COM 
746*11729SWang.Lin@Sun.COM 		this_thruput = rate_table->info[rate].user_ratekbps *
747*11729SWang.Lin@Sun.COM 		    (100 - per_thres);
748*11729SWang.Lin@Sun.COM 
749*11729SWang.Lin@Sun.COM 		if (best_thruput <= this_thruput) {
750*11729SWang.Lin@Sun.COM 			best_thruput = this_thruput;
751*11729SWang.Lin@Sun.COM 			best_rate    = rate;
752*11729SWang.Lin@Sun.COM 		}
753*11729SWang.Lin@Sun.COM 	}
754*11729SWang.Lin@Sun.COM 
755*11729SWang.Lin@Sun.COM 	rate = best_rate;
756*11729SWang.Lin@Sun.COM 
757*11729SWang.Lin@Sun.COM 	/*
758*11729SWang.Lin@Sun.COM 	 * if we are retrying for more than half the number
759*11729SWang.Lin@Sun.COM 	 * of max retries, use the min rate for the next retry
760*11729SWang.Lin@Sun.COM 	 */
761*11729SWang.Lin@Sun.COM 	if (is_retry)
762*11729SWang.Lin@Sun.COM 		rate = ath_rc_priv->valid_rate_index[minindex];
763*11729SWang.Lin@Sun.COM 
764*11729SWang.Lin@Sun.COM 	ath_rc_priv->rssi_last_lookup = rssi_last;
765*11729SWang.Lin@Sun.COM 
766*11729SWang.Lin@Sun.COM 	/*
767*11729SWang.Lin@Sun.COM 	 * Must check the actual rate (ratekbps) to account for
768*11729SWang.Lin@Sun.COM 	 * non-monoticity of 11g's rate table
769*11729SWang.Lin@Sun.COM 	 */
770*11729SWang.Lin@Sun.COM 
771*11729SWang.Lin@Sun.COM 	if (rate >= ath_rc_priv->rate_max_phy && probe_allowed) {
772*11729SWang.Lin@Sun.COM 		rate = ath_rc_priv->rate_max_phy;
773*11729SWang.Lin@Sun.COM 
774*11729SWang.Lin@Sun.COM 		/* Probe the next allowed phy state */
775*11729SWang.Lin@Sun.COM 		/* FIXME:XXXX Check to make sure ratMax is checked properly */
776*11729SWang.Lin@Sun.COM 		if (arn_rc_get_nextvalid_txrate(rate_table,
777*11729SWang.Lin@Sun.COM 		    ath_rc_priv, rate, &next_rate) &&
778*11729SWang.Lin@Sun.COM 		    (now_msec - ath_rc_priv->probe_time >
779*11729SWang.Lin@Sun.COM 		    rate_table->probe_interval) &&
780*11729SWang.Lin@Sun.COM 		    (ath_rc_priv->hw_maxretry_pktcnt >= 1)) {
781*11729SWang.Lin@Sun.COM 			rate = next_rate;
782*11729SWang.Lin@Sun.COM 			ath_rc_priv->probe_rate = rate;
783*11729SWang.Lin@Sun.COM 			ath_rc_priv->probe_time = now_msec;
784*11729SWang.Lin@Sun.COM 			ath_rc_priv->hw_maxretry_pktcnt = 0;
785*11729SWang.Lin@Sun.COM 			*is_probing = 1;
786*11729SWang.Lin@Sun.COM 		}
787*11729SWang.Lin@Sun.COM 	}
788*11729SWang.Lin@Sun.COM 
789*11729SWang.Lin@Sun.COM 	if (rate > (ath_rc_priv->rate_table_size - 1))
790*11729SWang.Lin@Sun.COM 		rate = ath_rc_priv->rate_table_size - 1;
791*11729SWang.Lin@Sun.COM 
792*11729SWang.Lin@Sun.COM 	ASSERT((rate_table->info[rate].valid && !ath_rc_priv->single_stream) ||
793*11729SWang.Lin@Sun.COM 	    (rate_table->info[rate].valid_single_stream &&
794*11729SWang.Lin@Sun.COM 	    ath_rc_priv->single_stream));
795*11729SWang.Lin@Sun.COM 
796*11729SWang.Lin@Sun.COM 	return (rate);
797*11729SWang.Lin@Sun.COM }
798*11729SWang.Lin@Sun.COM 
799*11729SWang.Lin@Sun.COM static void
arn_rc_rate_set_series(struct ath_rate_table * rate_table,struct ath9k_tx_rate * rate,uint8_t tries,uint8_t rix,int rtsctsenable)800*11729SWang.Lin@Sun.COM arn_rc_rate_set_series(struct ath_rate_table *rate_table,
801*11729SWang.Lin@Sun.COM     struct ath9k_tx_rate *rate,
802*11729SWang.Lin@Sun.COM     uint8_t tries,
803*11729SWang.Lin@Sun.COM     uint8_t rix,
804*11729SWang.Lin@Sun.COM     int rtsctsenable)
805*11729SWang.Lin@Sun.COM {
806*11729SWang.Lin@Sun.COM #if 0
807*11729SWang.Lin@Sun.COM 	struct ieee80211_node *in;
808*11729SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
809*11729SWang.Lin@Sun.COM #endif
810*11729SWang.Lin@Sun.COM 	rate->count = tries;
811*11729SWang.Lin@Sun.COM 	rate->idx = rix;
812*11729SWang.Lin@Sun.COM 
813*11729SWang.Lin@Sun.COM 	if (rtsctsenable)
814*11729SWang.Lin@Sun.COM 		rate->flags |= ATH9K_TX_RC_USE_RTS_CTS;
815*11729SWang.Lin@Sun.COM #if 0
816*11729SWang.Lin@Sun.COM 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
817*11729SWang.Lin@Sun.COM 	    (in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
818*11729SWang.Lin@Sun.COM 		rate->flags |= ATH9K_TX_RC_USE_SHORT_PREAMBLE;
819*11729SWang.Lin@Sun.COM 	}
820*11729SWang.Lin@Sun.COM #endif
821*11729SWang.Lin@Sun.COM 	if (WLAN_RC_PHY_40(rate_table->info[rix].phy))
822*11729SWang.Lin@Sun.COM 		rate->flags |= ATH9K_TX_RC_40_MHZ_WIDTH;
823*11729SWang.Lin@Sun.COM 	if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy))
824*11729SWang.Lin@Sun.COM 		rate->flags |= ATH9K_TX_RC_SHORT_GI;
825*11729SWang.Lin@Sun.COM 	if (WLAN_RC_PHY_HT(rate_table->info[rix].phy))
826*11729SWang.Lin@Sun.COM 		rate->flags |= ATH9K_TX_RC_MCS;
827*11729SWang.Lin@Sun.COM }
828*11729SWang.Lin@Sun.COM 
829*11729SWang.Lin@Sun.COM /* ARGSUSED */
830*11729SWang.Lin@Sun.COM static uint8_t
arn_rc_rate_getidx(struct arn_softc * sc,struct ath_rate_priv * ath_rc_priv,struct ath_rate_table * rate_table,uint8_t rix,uint16_t stepdown,uint16_t min_rate)831*11729SWang.Lin@Sun.COM arn_rc_rate_getidx(struct arn_softc *sc,
832*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv,
833*11729SWang.Lin@Sun.COM     struct ath_rate_table *rate_table,
834*11729SWang.Lin@Sun.COM     uint8_t rix, uint16_t stepdown,
835*11729SWang.Lin@Sun.COM     uint16_t min_rate)
836*11729SWang.Lin@Sun.COM {
837*11729SWang.Lin@Sun.COM 	uint32_t j;
838*11729SWang.Lin@Sun.COM 	uint8_t nextindex;
839*11729SWang.Lin@Sun.COM 
840*11729SWang.Lin@Sun.COM 	if (min_rate) {
841*11729SWang.Lin@Sun.COM 		for (j = RATE_TABLE_SIZE; j > 0; j--) {
842*11729SWang.Lin@Sun.COM 			if (arn_rc_get_nextlowervalid_txrate(rate_table,
843*11729SWang.Lin@Sun.COM 			    ath_rc_priv, rix, &nextindex))
844*11729SWang.Lin@Sun.COM 				rix = nextindex;
845*11729SWang.Lin@Sun.COM 			else
846*11729SWang.Lin@Sun.COM 				break;
847*11729SWang.Lin@Sun.COM 		}
848*11729SWang.Lin@Sun.COM 	} else {
849*11729SWang.Lin@Sun.COM 		for (j = stepdown; j > 0; j--) {
850*11729SWang.Lin@Sun.COM 			if (arn_rc_get_nextlowervalid_txrate(rate_table,
851*11729SWang.Lin@Sun.COM 			    ath_rc_priv, rix, &nextindex))
852*11729SWang.Lin@Sun.COM 				rix = nextindex;
853*11729SWang.Lin@Sun.COM 			else
854*11729SWang.Lin@Sun.COM 				break;
855*11729SWang.Lin@Sun.COM 		}
856*11729SWang.Lin@Sun.COM 	}
857*11729SWang.Lin@Sun.COM 	return (rix);
858*11729SWang.Lin@Sun.COM }
859*11729SWang.Lin@Sun.COM 
860*11729SWang.Lin@Sun.COM static void
arn_rc_ratefind(struct arn_softc * sc,struct ath_rate_priv * ath_rc_priv,struct ath_buf * bf,int num_tries,int num_rates,int * is_probe,boolean_t is_retry)861*11729SWang.Lin@Sun.COM arn_rc_ratefind(struct arn_softc *sc, struct ath_rate_priv *ath_rc_priv,
862*11729SWang.Lin@Sun.COM     struct ath_buf *bf, int num_tries, int num_rates, int *is_probe,
863*11729SWang.Lin@Sun.COM     boolean_t is_retry)
864*11729SWang.Lin@Sun.COM {
865*11729SWang.Lin@Sun.COM 	uint8_t try_per_rate = 0, i = 0, rix, nrix;
866*11729SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table;
867*11729SWang.Lin@Sun.COM 	struct ath9k_tx_rate *rates = bf->rates;
868*11729SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
869*11729SWang.Lin@Sun.COM 
870*11729SWang.Lin@Sun.COM 	rate_table = sc->sc_currates;
871*11729SWang.Lin@Sun.COM 	rix = arn_rc_ratefind_ht(sc, ath_rc_priv, rate_table, 1,
872*11729SWang.Lin@Sun.COM 	    is_probe, is_retry);
873*11729SWang.Lin@Sun.COM 	nrix = rix;
874*11729SWang.Lin@Sun.COM 
875*11729SWang.Lin@Sun.COM 	if (*is_probe) {
876*11729SWang.Lin@Sun.COM 		/*
877*11729SWang.Lin@Sun.COM 		 * set one try for probe rates. For the
878*11729SWang.Lin@Sun.COM 		 * probes don't enable rts
879*11729SWang.Lin@Sun.COM 		 */
880*11729SWang.Lin@Sun.COM 		arn_rc_rate_set_series(rate_table,
881*11729SWang.Lin@Sun.COM 		    &rates[i++], 1, nrix, 0);
882*11729SWang.Lin@Sun.COM 
883*11729SWang.Lin@Sun.COM 		try_per_rate = (num_tries/num_rates);
884*11729SWang.Lin@Sun.COM 		/*
885*11729SWang.Lin@Sun.COM 		 * Get the next tried/allowed rate. No RTS for the next series
886*11729SWang.Lin@Sun.COM 		 * after the probe rate
887*11729SWang.Lin@Sun.COM 		 */
888*11729SWang.Lin@Sun.COM 		nrix = arn_rc_rate_getidx(sc,
889*11729SWang.Lin@Sun.COM 		    ath_rc_priv, rate_table, nrix, 1, 0);
890*11729SWang.Lin@Sun.COM 		arn_rc_rate_set_series(rate_table,
891*11729SWang.Lin@Sun.COM 		    &rates[i++], try_per_rate, nrix, 0);
892*11729SWang.Lin@Sun.COM 	} else {
893*11729SWang.Lin@Sun.COM 		try_per_rate = (num_tries/num_rates);
894*11729SWang.Lin@Sun.COM 		/* Set the choosen rate. No RTS for first series entry. */
895*11729SWang.Lin@Sun.COM 		arn_rc_rate_set_series(rate_table,
896*11729SWang.Lin@Sun.COM 		    &rates[i++], try_per_rate, nrix, 0);
897*11729SWang.Lin@Sun.COM 	}
898*11729SWang.Lin@Sun.COM 
899*11729SWang.Lin@Sun.COM 	/* Fill in the other rates for multirate retry */
900*11729SWang.Lin@Sun.COM 	for (; i < num_rates; i++) {
901*11729SWang.Lin@Sun.COM 		uint8_t try_num;
902*11729SWang.Lin@Sun.COM 		uint8_t min_rate;
903*11729SWang.Lin@Sun.COM 
904*11729SWang.Lin@Sun.COM 		try_num = ((i + 1) == num_rates) ?
905*11729SWang.Lin@Sun.COM 		    num_tries - (try_per_rate * i) : try_per_rate;
906*11729SWang.Lin@Sun.COM 		/* LINTED E_FALSE_LOGICAL_EXPR */
907*11729SWang.Lin@Sun.COM 		min_rate = (((i + 1) == num_rates) && 0);
908*11729SWang.Lin@Sun.COM 
909*11729SWang.Lin@Sun.COM 		nrix = arn_rc_rate_getidx(sc, ath_rc_priv,
910*11729SWang.Lin@Sun.COM 		    rate_table, nrix, 1, min_rate);
911*11729SWang.Lin@Sun.COM 		/* All other rates in the series have RTS enabled */
912*11729SWang.Lin@Sun.COM 		arn_rc_rate_set_series(rate_table, &rates[i], try_num, nrix, 1);
913*11729SWang.Lin@Sun.COM 	}
914*11729SWang.Lin@Sun.COM 
915*11729SWang.Lin@Sun.COM 	/*
916*11729SWang.Lin@Sun.COM 	 * NB:Change rate series to enable aggregation when operating
917*11729SWang.Lin@Sun.COM 	 * at lower MCS rates. When first rate in series is MCS2
918*11729SWang.Lin@Sun.COM 	 * in HT40 @ 2.4GHz, series should look like:
919*11729SWang.Lin@Sun.COM 	 *
920*11729SWang.Lin@Sun.COM 	 * {MCS2, MCS1, MCS0, MCS0}.
921*11729SWang.Lin@Sun.COM 	 *
922*11729SWang.Lin@Sun.COM 	 * When first rate in series is MCS3 in HT20 @ 2.4GHz, series should
923*11729SWang.Lin@Sun.COM 	 * look like:
924*11729SWang.Lin@Sun.COM 	 *
925*11729SWang.Lin@Sun.COM 	 * {MCS3, MCS2, MCS1, MCS1}
926*11729SWang.Lin@Sun.COM 	 *
927*11729SWang.Lin@Sun.COM 	 * So, set fourth rate in series to be same as third one for
928*11729SWang.Lin@Sun.COM 	 * above conditions.
929*11729SWang.Lin@Sun.COM 	 */
930*11729SWang.Lin@Sun.COM 
931*11729SWang.Lin@Sun.COM 	if (IEEE80211_IS_CHAN_HTG(ic->ic_curchan)) {
932*11729SWang.Lin@Sun.COM 		uint8_t dot11rate = rate_table->info[rix].dot11rate;
933*11729SWang.Lin@Sun.COM 		uint8_t phy = rate_table->info[rix].phy;
934*11729SWang.Lin@Sun.COM 		if (i == 4 &&
935*11729SWang.Lin@Sun.COM 		    ((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) ||
936*11729SWang.Lin@Sun.COM 		    (dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) {
937*11729SWang.Lin@Sun.COM 			rates[3].idx = rates[2].idx;
938*11729SWang.Lin@Sun.COM 			rates[3].flags = rates[2].flags;
939*11729SWang.Lin@Sun.COM 		}
940*11729SWang.Lin@Sun.COM 	}
941*11729SWang.Lin@Sun.COM }
942*11729SWang.Lin@Sun.COM 
943*11729SWang.Lin@Sun.COM /* ARGSUSED */
944*11729SWang.Lin@Sun.COM static boolean_t
arn_rc_update_per(struct arn_softc * sc,struct ath_rate_table * rate_table,struct ath_rate_priv * ath_rc_priv,struct ath_tx_info_priv * tx_info_priv,int tx_rate,int xretries,int retries,uint32_t now_msec)945*11729SWang.Lin@Sun.COM arn_rc_update_per(struct arn_softc *sc,
946*11729SWang.Lin@Sun.COM     struct ath_rate_table *rate_table,
947*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv,
948*11729SWang.Lin@Sun.COM     struct ath_tx_info_priv *tx_info_priv,
949*11729SWang.Lin@Sun.COM     int tx_rate, int xretries, int retries,
950*11729SWang.Lin@Sun.COM 	uint32_t now_msec)
951*11729SWang.Lin@Sun.COM {
952*11729SWang.Lin@Sun.COM 	boolean_t state_change = B_FALSE;
953*11729SWang.Lin@Sun.COM 	int count;
954*11729SWang.Lin@Sun.COM 	uint8_t last_per;
955*11729SWang.Lin@Sun.COM 	static uint32_t nretry_to_per_lookup[10] = {
956*11729SWang.Lin@Sun.COM 		100 * 0 / 1,
957*11729SWang.Lin@Sun.COM 		100 * 1 / 4,
958*11729SWang.Lin@Sun.COM 		100 * 1 / 2,
959*11729SWang.Lin@Sun.COM 		100 * 3 / 4,
960*11729SWang.Lin@Sun.COM 		100 * 4 / 5,
961*11729SWang.Lin@Sun.COM 		100 * 5 / 6,
962*11729SWang.Lin@Sun.COM 		100 * 6 / 7,
963*11729SWang.Lin@Sun.COM 		100 * 7 / 8,
964*11729SWang.Lin@Sun.COM 		100 * 8 / 9,
965*11729SWang.Lin@Sun.COM 		100 * 9 / 10
966*11729SWang.Lin@Sun.COM 	};
967*11729SWang.Lin@Sun.COM 
968*11729SWang.Lin@Sun.COM 	last_per = ath_rc_priv->state[tx_rate].per;
969*11729SWang.Lin@Sun.COM 
970*11729SWang.Lin@Sun.COM 	if (xretries) {
971*11729SWang.Lin@Sun.COM 		if (xretries == 1) {
972*11729SWang.Lin@Sun.COM 			ath_rc_priv->state[tx_rate].per += 30;
973*11729SWang.Lin@Sun.COM 			if (ath_rc_priv->state[tx_rate].per > 100)
974*11729SWang.Lin@Sun.COM 				ath_rc_priv->state[tx_rate].per = 100;
975*11729SWang.Lin@Sun.COM 		} else {
976*11729SWang.Lin@Sun.COM 			/* xretries == 2 */
977*11729SWang.Lin@Sun.COM 			count = ARRAY_SIZE(nretry_to_per_lookup);
978*11729SWang.Lin@Sun.COM 			if (retries >= count)
979*11729SWang.Lin@Sun.COM 				retries = count - 1;
980*11729SWang.Lin@Sun.COM 
981*11729SWang.Lin@Sun.COM 			/* new_PER = 7/8*old_PER + 1/8*(currentPER) */
982*11729SWang.Lin@Sun.COM 			ath_rc_priv->state[tx_rate].per =
983*11729SWang.Lin@Sun.COM 			    (uint8_t)(last_per - (last_per >> 3) + (100 >> 3));
984*11729SWang.Lin@Sun.COM 		}
985*11729SWang.Lin@Sun.COM 
986*11729SWang.Lin@Sun.COM 		/* xretries == 1 or 2 */
987*11729SWang.Lin@Sun.COM 
988*11729SWang.Lin@Sun.COM 		if (ath_rc_priv->probe_rate == tx_rate)
989*11729SWang.Lin@Sun.COM 			ath_rc_priv->probe_rate = 0;
990*11729SWang.Lin@Sun.COM 
991*11729SWang.Lin@Sun.COM 	} else { /* xretries == 0 */
992*11729SWang.Lin@Sun.COM 		count = ARRAY_SIZE(nretry_to_per_lookup);
993*11729SWang.Lin@Sun.COM 		if (retries >= count)
994*11729SWang.Lin@Sun.COM 			retries = count - 1;
995*11729SWang.Lin@Sun.COM 
996*11729SWang.Lin@Sun.COM 		if (tx_info_priv->n_bad_frames) {
997*11729SWang.Lin@Sun.COM 			/*
998*11729SWang.Lin@Sun.COM 			 * new_PER = 7/8*old_PER + 1/8*(currentPER)
999*11729SWang.Lin@Sun.COM 			 * Assuming that n_frames is not 0.  The current PER
1000*11729SWang.Lin@Sun.COM 			 * from the retries is 100 * retries / (retries+1),
1001*11729SWang.Lin@Sun.COM 			 * since the first retries attempts failed, and the
1002*11729SWang.Lin@Sun.COM 			 * next one worked.  For the one that worked,
1003*11729SWang.Lin@Sun.COM 			 * n_bad_frames subframes out of n_frames wored,
1004*11729SWang.Lin@Sun.COM 			 * so the PER for that part is
1005*11729SWang.Lin@Sun.COM 			 * 100 * n_bad_frames / n_frames, and it contributes
1006*11729SWang.Lin@Sun.COM 			 * 100 * n_bad_frames / (n_frames * (retries+1)) to
1007*11729SWang.Lin@Sun.COM 			 * the above PER.  The expression below is a
1008*11729SWang.Lin@Sun.COM 			 * simplified version of the sum of these two terms.
1009*11729SWang.Lin@Sun.COM 			 */
1010*11729SWang.Lin@Sun.COM 			if (tx_info_priv->n_frames > 0) {
1011*11729SWang.Lin@Sun.COM 				int n_frames, n_bad_frames;
1012*11729SWang.Lin@Sun.COM 				uint8_t cur_per, new_per;
1013*11729SWang.Lin@Sun.COM 
1014*11729SWang.Lin@Sun.COM 				n_bad_frames = retries *
1015*11729SWang.Lin@Sun.COM 				    tx_info_priv->n_frames +
1016*11729SWang.Lin@Sun.COM 				    tx_info_priv->n_bad_frames;
1017*11729SWang.Lin@Sun.COM 				n_frames =
1018*11729SWang.Lin@Sun.COM 				    tx_info_priv->n_frames * (retries + 1);
1019*11729SWang.Lin@Sun.COM 				cur_per =
1020*11729SWang.Lin@Sun.COM 				    (100 * n_bad_frames / n_frames) >> 3;
1021*11729SWang.Lin@Sun.COM 				new_per = (uint8_t)
1022*11729SWang.Lin@Sun.COM 				    (last_per - (last_per >> 3) + cur_per);
1023*11729SWang.Lin@Sun.COM 				ath_rc_priv->state[tx_rate].per = new_per;
1024*11729SWang.Lin@Sun.COM 			}
1025*11729SWang.Lin@Sun.COM 		} else {
1026*11729SWang.Lin@Sun.COM 			ath_rc_priv->state[tx_rate].per =
1027*11729SWang.Lin@Sun.COM 			    (uint8_t)(last_per - (last_per >> 3) +
1028*11729SWang.Lin@Sun.COM 			    (nretry_to_per_lookup[retries] >> 3));
1029*11729SWang.Lin@Sun.COM 		}
1030*11729SWang.Lin@Sun.COM 
1031*11729SWang.Lin@Sun.COM 		ath_rc_priv->rssi_last_prev2 = ath_rc_priv->rssi_last_prev;
1032*11729SWang.Lin@Sun.COM 		ath_rc_priv->rssi_last_prev  = ath_rc_priv->rssi_last;
1033*11729SWang.Lin@Sun.COM 		ath_rc_priv->rssi_last = tx_info_priv->tx.ts_rssi;
1034*11729SWang.Lin@Sun.COM 		ath_rc_priv->rssi_time = now_msec;
1035*11729SWang.Lin@Sun.COM 
1036*11729SWang.Lin@Sun.COM 		/*
1037*11729SWang.Lin@Sun.COM 		 * If we got at most one retry then increase the max rate if
1038*11729SWang.Lin@Sun.COM 		 * this was a probe.  Otherwise, ignore the probe.
1039*11729SWang.Lin@Sun.COM 		 */
1040*11729SWang.Lin@Sun.COM 		if (ath_rc_priv->probe_rate &&
1041*11729SWang.Lin@Sun.COM 		    ath_rc_priv->probe_rate == tx_rate) {
1042*11729SWang.Lin@Sun.COM 			if (retries > 0 || 2 * tx_info_priv->n_bad_frames >
1043*11729SWang.Lin@Sun.COM 			    tx_info_priv->n_frames) {
1044*11729SWang.Lin@Sun.COM 				/*
1045*11729SWang.Lin@Sun.COM 				 * Since we probed with just a single attempt,
1046*11729SWang.Lin@Sun.COM 				 * any retries means the probe failed.  Also,
1047*11729SWang.Lin@Sun.COM 				 * if the attempt worked, but more than half
1048*11729SWang.Lin@Sun.COM 				 * the subframes were bad then also consider
1049*11729SWang.Lin@Sun.COM 				 * the probe a failure.
1050*11729SWang.Lin@Sun.COM 				 */
1051*11729SWang.Lin@Sun.COM 				ath_rc_priv->probe_rate = 0;
1052*11729SWang.Lin@Sun.COM 			} else {
1053*11729SWang.Lin@Sun.COM 				uint8_t probe_rate = 0;
1054*11729SWang.Lin@Sun.COM 
1055*11729SWang.Lin@Sun.COM 				ath_rc_priv->rate_max_phy =
1056*11729SWang.Lin@Sun.COM 				    ath_rc_priv->probe_rate;
1057*11729SWang.Lin@Sun.COM 				probe_rate = ath_rc_priv->probe_rate;
1058*11729SWang.Lin@Sun.COM 
1059*11729SWang.Lin@Sun.COM 				if (ath_rc_priv->state[probe_rate].per > 30)
1060*11729SWang.Lin@Sun.COM 					ath_rc_priv->state[probe_rate].per = 20;
1061*11729SWang.Lin@Sun.COM 
1062*11729SWang.Lin@Sun.COM 				ath_rc_priv->probe_rate = 0;
1063*11729SWang.Lin@Sun.COM 
1064*11729SWang.Lin@Sun.COM 				/*
1065*11729SWang.Lin@Sun.COM 				 * Since this probe succeeded, we allow the next
1066*11729SWang.Lin@Sun.COM 				 * probe twice as soon.  This allows the maxRate
1067*11729SWang.Lin@Sun.COM 				 * to move up faster if the probes are
1068*11729SWang.Lin@Sun.COM 				 * succesful.
1069*11729SWang.Lin@Sun.COM 				 */
1070*11729SWang.Lin@Sun.COM 				ath_rc_priv->probe_time =
1071*11729SWang.Lin@Sun.COM 				    now_msec - rate_table->probe_interval / 2;
1072*11729SWang.Lin@Sun.COM 			}
1073*11729SWang.Lin@Sun.COM 		}
1074*11729SWang.Lin@Sun.COM 
1075*11729SWang.Lin@Sun.COM 		if (retries > 0) {
1076*11729SWang.Lin@Sun.COM 			/*
1077*11729SWang.Lin@Sun.COM 			 * Don't update anything.  We don't know if
1078*11729SWang.Lin@Sun.COM 			 * this was because of collisions or poor signal.
1079*11729SWang.Lin@Sun.COM 			 *
1080*11729SWang.Lin@Sun.COM 			 * Later: if rssi_ack is close to
1081*11729SWang.Lin@Sun.COM 			 * ath_rc_priv->state[txRate].rssi_thres and we see lots
1082*11729SWang.Lin@Sun.COM 			 * of retries, then we could increase
1083*11729SWang.Lin@Sun.COM 			 * ath_rc_priv->state[txRate].rssi_thres.
1084*11729SWang.Lin@Sun.COM 			 */
1085*11729SWang.Lin@Sun.COM 			ath_rc_priv->hw_maxretry_pktcnt = 0;
1086*11729SWang.Lin@Sun.COM 		} else {
1087*11729SWang.Lin@Sun.COM 			int32_t rssi_ackAvg;
1088*11729SWang.Lin@Sun.COM 			int8_t rssi_thres;
1089*11729SWang.Lin@Sun.COM 			int8_t rssi_ack_vmin;
1090*11729SWang.Lin@Sun.COM 
1091*11729SWang.Lin@Sun.COM 			/*
1092*11729SWang.Lin@Sun.COM 			 * It worked with no retries. First ignore bogus (small)
1093*11729SWang.Lin@Sun.COM 			 * rssi_ack values.
1094*11729SWang.Lin@Sun.COM 			 */
1095*11729SWang.Lin@Sun.COM 			if (tx_rate == ath_rc_priv->rate_max_phy &&
1096*11729SWang.Lin@Sun.COM 			    ath_rc_priv->hw_maxretry_pktcnt < 255) {
1097*11729SWang.Lin@Sun.COM 				ath_rc_priv->hw_maxretry_pktcnt++;
1098*11729SWang.Lin@Sun.COM 			}
1099*11729SWang.Lin@Sun.COM 
1100*11729SWang.Lin@Sun.COM 			if (tx_info_priv->tx.ts_rssi <
1101*11729SWang.Lin@Sun.COM 			    rate_table->info[tx_rate].rssi_ack_validmin)
1102*11729SWang.Lin@Sun.COM 				goto exit;
1103*11729SWang.Lin@Sun.COM 
1104*11729SWang.Lin@Sun.COM 			/* Average the rssi */
1105*11729SWang.Lin@Sun.COM 			if (tx_rate != ath_rc_priv->rssi_sum_rate) {
1106*11729SWang.Lin@Sun.COM 				ath_rc_priv->rssi_sum_rate = tx_rate;
1107*11729SWang.Lin@Sun.COM 				ath_rc_priv->rssi_sum =
1108*11729SWang.Lin@Sun.COM 				    ath_rc_priv->rssi_sum_cnt = 0;
1109*11729SWang.Lin@Sun.COM 			}
1110*11729SWang.Lin@Sun.COM 
1111*11729SWang.Lin@Sun.COM 			ath_rc_priv->rssi_sum += tx_info_priv->tx.ts_rssi;
1112*11729SWang.Lin@Sun.COM 			ath_rc_priv->rssi_sum_cnt++;
1113*11729SWang.Lin@Sun.COM 
1114*11729SWang.Lin@Sun.COM 			if (ath_rc_priv->rssi_sum_cnt < 4)
1115*11729SWang.Lin@Sun.COM 				goto exit;
1116*11729SWang.Lin@Sun.COM 
1117*11729SWang.Lin@Sun.COM 			rssi_ackAvg =
1118*11729SWang.Lin@Sun.COM 			    (ath_rc_priv->rssi_sum + 2) / 4;
1119*11729SWang.Lin@Sun.COM 			rssi_thres =
1120*11729SWang.Lin@Sun.COM 			    ath_rc_priv->state[tx_rate].rssi_thres;
1121*11729SWang.Lin@Sun.COM 			rssi_ack_vmin =
1122*11729SWang.Lin@Sun.COM 			    rate_table->info[tx_rate].rssi_ack_validmin;
1123*11729SWang.Lin@Sun.COM 
1124*11729SWang.Lin@Sun.COM 			ath_rc_priv->rssi_sum =
1125*11729SWang.Lin@Sun.COM 			    ath_rc_priv->rssi_sum_cnt = 0;
1126*11729SWang.Lin@Sun.COM 
1127*11729SWang.Lin@Sun.COM 			/* Now reduce the current rssi threshold */
1128*11729SWang.Lin@Sun.COM 			if ((rssi_ackAvg < rssi_thres + 2) &&
1129*11729SWang.Lin@Sun.COM 			    (rssi_thres > rssi_ack_vmin)) {
1130*11729SWang.Lin@Sun.COM 				ath_rc_priv->state[tx_rate].rssi_thres--;
1131*11729SWang.Lin@Sun.COM 			}
1132*11729SWang.Lin@Sun.COM 
1133*11729SWang.Lin@Sun.COM 			state_change = B_TRUE;
1134*11729SWang.Lin@Sun.COM 		}
1135*11729SWang.Lin@Sun.COM 	}
1136*11729SWang.Lin@Sun.COM exit:
1137*11729SWang.Lin@Sun.COM 	return (state_change);
1138*11729SWang.Lin@Sun.COM }
1139*11729SWang.Lin@Sun.COM 
1140*11729SWang.Lin@Sun.COM /*
1141*11729SWang.Lin@Sun.COM  * Update PER, RSSI and whatever else that the code thinks
1142*11729SWang.Lin@Sun.COM  * it is doing. If you can make sense of all this, you really
1143*11729SWang.Lin@Sun.COM  * need to go out more.
1144*11729SWang.Lin@Sun.COM  */
1145*11729SWang.Lin@Sun.COM static void
arn_rc_update_ht(struct arn_softc * sc,struct ath_rate_priv * ath_rc_priv,struct ath_tx_info_priv * tx_info_priv,int tx_rate,int xretries,int retries)1146*11729SWang.Lin@Sun.COM arn_rc_update_ht(struct arn_softc *sc,
1147*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv,
1148*11729SWang.Lin@Sun.COM     struct ath_tx_info_priv *tx_info_priv,
1149*11729SWang.Lin@Sun.COM     int tx_rate, int xretries, int retries)
1150*11729SWang.Lin@Sun.COM {
1151*11729SWang.Lin@Sun.COM #define	CHK_RSSI(rate)					\
1152*11729SWang.Lin@Sun.COM 	((ath_rc_priv->state[(rate)].rssi_thres +	\
1153*11729SWang.Lin@Sun.COM 	    rate_table->info[(rate)].rssi_ack_deltamin) > \
1154*11729SWang.Lin@Sun.COM 	    ath_rc_priv->state[(rate)+1].rssi_thres)
1155*11729SWang.Lin@Sun.COM 
1156*11729SWang.Lin@Sun.COM 	/* u32 now_msec = jiffies_to_msecs(jiffies); */
1157*11729SWang.Lin@Sun.COM 	uint32_t now_msec = drv_hztousec(ddi_get_lbolt())/1000; /* mescs ? */
1158*11729SWang.Lin@Sun.COM 	int rate;
1159*11729SWang.Lin@Sun.COM 	uint8_t last_per;
1160*11729SWang.Lin@Sun.COM 	boolean_t state_change = B_FALSE;
1161*11729SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table = sc->sc_currates;
1162*11729SWang.Lin@Sun.COM 	int size = ath_rc_priv->rate_table_size;
1163*11729SWang.Lin@Sun.COM 
1164*11729SWang.Lin@Sun.COM 	if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt))
1165*11729SWang.Lin@Sun.COM 		return;
1166*11729SWang.Lin@Sun.COM 
1167*11729SWang.Lin@Sun.COM 	/* To compensate for some imbalance between ctrl and ext. channel */
1168*11729SWang.Lin@Sun.COM 
1169*11729SWang.Lin@Sun.COM 	if (WLAN_RC_PHY_40(rate_table->info[tx_rate].phy))
1170*11729SWang.Lin@Sun.COM 		tx_info_priv->tx.ts_rssi =
1171*11729SWang.Lin@Sun.COM 		    tx_info_priv->tx.ts_rssi < 3 ? 0 :
1172*11729SWang.Lin@Sun.COM 		    tx_info_priv->tx.ts_rssi - 3;
1173*11729SWang.Lin@Sun.COM 
1174*11729SWang.Lin@Sun.COM 	last_per = ath_rc_priv->state[tx_rate].per;
1175*11729SWang.Lin@Sun.COM 
1176*11729SWang.Lin@Sun.COM 	/* Update PER first */
1177*11729SWang.Lin@Sun.COM 	state_change = arn_rc_update_per(sc, rate_table, ath_rc_priv,
1178*11729SWang.Lin@Sun.COM 	    tx_info_priv, tx_rate, xretries,
1179*11729SWang.Lin@Sun.COM 	    retries, now_msec);
1180*11729SWang.Lin@Sun.COM 
1181*11729SWang.Lin@Sun.COM 	/*
1182*11729SWang.Lin@Sun.COM 	 * If this rate looks bad (high PER) then stop using it for
1183*11729SWang.Lin@Sun.COM 	 * a while (except if we are probing).
1184*11729SWang.Lin@Sun.COM 	 */
1185*11729SWang.Lin@Sun.COM 	if (ath_rc_priv->state[tx_rate].per >= 55 && tx_rate > 0 &&
1186*11729SWang.Lin@Sun.COM 	    rate_table->info[tx_rate].ratekbps <=
1187*11729SWang.Lin@Sun.COM 	    rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) {
1188*11729SWang.Lin@Sun.COM 		(void) arn_rc_get_nextlowervalid_txrate(rate_table,
1189*11729SWang.Lin@Sun.COM 		    ath_rc_priv,
1190*11729SWang.Lin@Sun.COM 		    (uint8_t)tx_rate,
1191*11729SWang.Lin@Sun.COM 		    &ath_rc_priv->rate_max_phy);
1192*11729SWang.Lin@Sun.COM 
1193*11729SWang.Lin@Sun.COM 		/* Don't probe for a little while. */
1194*11729SWang.Lin@Sun.COM 		ath_rc_priv->probe_time = now_msec;
1195*11729SWang.Lin@Sun.COM 	}
1196*11729SWang.Lin@Sun.COM 
1197*11729SWang.Lin@Sun.COM 	if (state_change) {
1198*11729SWang.Lin@Sun.COM 		/*
1199*11729SWang.Lin@Sun.COM 		 * Make sure the rates above this have higher rssi thresholds.
1200*11729SWang.Lin@Sun.COM 		 * (Note:  Monotonicity is kept within the OFDM rates and
1201*11729SWang.Lin@Sun.COM 		 * within the CCK rates. However, no adjustment is
1202*11729SWang.Lin@Sun.COM 		 * made to keep the rssi thresholds monotonically
1203*11729SWang.Lin@Sun.COM 		 * increasing between the CCK and OFDM rates.)
1204*11729SWang.Lin@Sun.COM 		 */
1205*11729SWang.Lin@Sun.COM 		for (rate = tx_rate; rate < size - 1; rate++) {
1206*11729SWang.Lin@Sun.COM 			if (rate_table->info[rate+1].phy !=
1207*11729SWang.Lin@Sun.COM 			    rate_table->info[tx_rate].phy)
1208*11729SWang.Lin@Sun.COM 				break;
1209*11729SWang.Lin@Sun.COM 
1210*11729SWang.Lin@Sun.COM 			if (CHK_RSSI(rate)) {
1211*11729SWang.Lin@Sun.COM 				ath_rc_priv->state[rate+1].rssi_thres =
1212*11729SWang.Lin@Sun.COM 				    ath_rc_priv->state[rate].rssi_thres +
1213*11729SWang.Lin@Sun.COM 				    rate_table->info[rate].rssi_ack_deltamin;
1214*11729SWang.Lin@Sun.COM 			}
1215*11729SWang.Lin@Sun.COM 		}
1216*11729SWang.Lin@Sun.COM 
1217*11729SWang.Lin@Sun.COM 		/* Make sure the rates below this have lower rssi thresholds. */
1218*11729SWang.Lin@Sun.COM 		for (rate = tx_rate - 1; rate >= 0; rate--) {
1219*11729SWang.Lin@Sun.COM 			if (rate_table->info[rate].phy !=
1220*11729SWang.Lin@Sun.COM 			    rate_table->info[tx_rate].phy)
1221*11729SWang.Lin@Sun.COM 				break;
1222*11729SWang.Lin@Sun.COM 
1223*11729SWang.Lin@Sun.COM 			if (CHK_RSSI(rate)) {
1224*11729SWang.Lin@Sun.COM 				if (ath_rc_priv->state[rate+1].rssi_thres <
1225*11729SWang.Lin@Sun.COM 				    rate_table->info[rate].rssi_ack_deltamin)
1226*11729SWang.Lin@Sun.COM 					ath_rc_priv->state[rate].rssi_thres = 0;
1227*11729SWang.Lin@Sun.COM 				else {
1228*11729SWang.Lin@Sun.COM 					ath_rc_priv->state[rate].rssi_thres =
1229*11729SWang.Lin@Sun.COM 					    ath_rc_priv->state[rate+1].
1230*11729SWang.Lin@Sun.COM 					    rssi_thres -
1231*11729SWang.Lin@Sun.COM 					    rate_table->info[rate].
1232*11729SWang.Lin@Sun.COM 					    rssi_ack_deltamin;
1233*11729SWang.Lin@Sun.COM 				}
1234*11729SWang.Lin@Sun.COM 
1235*11729SWang.Lin@Sun.COM 				if (ath_rc_priv->state[rate].rssi_thres <
1236*11729SWang.Lin@Sun.COM 				    rate_table->info[rate].rssi_ack_validmin) {
1237*11729SWang.Lin@Sun.COM 					ath_rc_priv->state[rate].rssi_thres =
1238*11729SWang.Lin@Sun.COM 					    rate_table->info[rate].
1239*11729SWang.Lin@Sun.COM 					    rssi_ack_validmin;
1240*11729SWang.Lin@Sun.COM 				}
1241*11729SWang.Lin@Sun.COM 			}
1242*11729SWang.Lin@Sun.COM 		}
1243*11729SWang.Lin@Sun.COM 	}
1244*11729SWang.Lin@Sun.COM 
1245*11729SWang.Lin@Sun.COM 	/* Make sure the rates below this have lower PER */
1246*11729SWang.Lin@Sun.COM 	/* Monotonicity is kept only for rates below the current rate. */
1247*11729SWang.Lin@Sun.COM 	if (ath_rc_priv->state[tx_rate].per < last_per) {
1248*11729SWang.Lin@Sun.COM 		for (rate = tx_rate - 1; rate >= 0; rate--) {
1249*11729SWang.Lin@Sun.COM 			if (rate_table->info[rate].phy !=
1250*11729SWang.Lin@Sun.COM 			    rate_table->info[tx_rate].phy)
1251*11729SWang.Lin@Sun.COM 				break;
1252*11729SWang.Lin@Sun.COM 
1253*11729SWang.Lin@Sun.COM 			if (ath_rc_priv->state[rate].per >
1254*11729SWang.Lin@Sun.COM 			    ath_rc_priv->state[rate+1].per) {
1255*11729SWang.Lin@Sun.COM 				ath_rc_priv->state[rate].per =
1256*11729SWang.Lin@Sun.COM 				    ath_rc_priv->state[rate+1].per;
1257*11729SWang.Lin@Sun.COM 			}
1258*11729SWang.Lin@Sun.COM 		}
1259*11729SWang.Lin@Sun.COM 	}
1260*11729SWang.Lin@Sun.COM 
1261*11729SWang.Lin@Sun.COM 	/* Maintain monotonicity for rates above the current rate */
1262*11729SWang.Lin@Sun.COM 	for (rate = tx_rate; rate < size - 1; rate++) {
1263*11729SWang.Lin@Sun.COM 		if (ath_rc_priv->state[rate+1].per <
1264*11729SWang.Lin@Sun.COM 		    ath_rc_priv->state[rate].per)
1265*11729SWang.Lin@Sun.COM 			ath_rc_priv->state[rate+1].per =
1266*11729SWang.Lin@Sun.COM 			    ath_rc_priv->state[rate].per;
1267*11729SWang.Lin@Sun.COM 	}
1268*11729SWang.Lin@Sun.COM 
1269*11729SWang.Lin@Sun.COM 	/*
1270*11729SWang.Lin@Sun.COM 	 * Every so often, we reduce the thresholds and
1271*11729SWang.Lin@Sun.COM 	 * PER (different for CCK and OFDM).
1272*11729SWang.Lin@Sun.COM 	 */
1273*11729SWang.Lin@Sun.COM 	if (now_msec - ath_rc_priv->rssi_down_time >=
1274*11729SWang.Lin@Sun.COM 	    rate_table->rssi_reduce_interval) {
1275*11729SWang.Lin@Sun.COM 
1276*11729SWang.Lin@Sun.COM 		for (rate = 0; rate < size; rate++) {
1277*11729SWang.Lin@Sun.COM 			if (ath_rc_priv->state[rate].rssi_thres >
1278*11729SWang.Lin@Sun.COM 			    rate_table->info[rate].rssi_ack_validmin)
1279*11729SWang.Lin@Sun.COM 				ath_rc_priv->state[rate].rssi_thres -= 1;
1280*11729SWang.Lin@Sun.COM 		}
1281*11729SWang.Lin@Sun.COM 		ath_rc_priv->rssi_down_time = now_msec;
1282*11729SWang.Lin@Sun.COM 	}
1283*11729SWang.Lin@Sun.COM 
1284*11729SWang.Lin@Sun.COM 	/*
1285*11729SWang.Lin@Sun.COM 	 * Every so often, we reduce the thresholds
1286*11729SWang.Lin@Sun.COM 	 * and PER (different for CCK and OFDM).
1287*11729SWang.Lin@Sun.COM 	 */
1288*11729SWang.Lin@Sun.COM 	if (now_msec - ath_rc_priv->per_down_time >=
1289*11729SWang.Lin@Sun.COM 	    rate_table->rssi_reduce_interval) {
1290*11729SWang.Lin@Sun.COM 		for (rate = 0; rate < size; rate++) {
1291*11729SWang.Lin@Sun.COM 			ath_rc_priv->state[rate].per =
1292*11729SWang.Lin@Sun.COM 			    7 * ath_rc_priv->state[rate].per / 8;
1293*11729SWang.Lin@Sun.COM 		}
1294*11729SWang.Lin@Sun.COM 
1295*11729SWang.Lin@Sun.COM 		ath_rc_priv->per_down_time = now_msec;
1296*11729SWang.Lin@Sun.COM 	}
1297*11729SWang.Lin@Sun.COM 
1298*11729SWang.Lin@Sun.COM #undef CHK_RSSI
1299*11729SWang.Lin@Sun.COM }
1300*11729SWang.Lin@Sun.COM 
1301*11729SWang.Lin@Sun.COM static int
ath_rc_get_rateindex(struct ath_rate_table * rate_table,struct ath9k_tx_rate * rate)1302*11729SWang.Lin@Sun.COM ath_rc_get_rateindex(struct ath_rate_table *rate_table,
1303*11729SWang.Lin@Sun.COM     struct ath9k_tx_rate *rate)
1304*11729SWang.Lin@Sun.COM {
1305*11729SWang.Lin@Sun.COM 	int rix;
1306*11729SWang.Lin@Sun.COM 
1307*11729SWang.Lin@Sun.COM 	if ((rate->flags & ATH9K_TX_RC_40_MHZ_WIDTH) &&
1308*11729SWang.Lin@Sun.COM 	    (rate->flags & ATH9K_TX_RC_SHORT_GI))
1309*11729SWang.Lin@Sun.COM 		rix = rate_table->info[rate->idx].ht_index;
1310*11729SWang.Lin@Sun.COM 	else if (rate->flags & ATH9K_TX_RC_SHORT_GI)
1311*11729SWang.Lin@Sun.COM 		rix = rate_table->info[rate->idx].sgi_index;
1312*11729SWang.Lin@Sun.COM 	else if (rate->flags & ATH9K_TX_RC_40_MHZ_WIDTH)
1313*11729SWang.Lin@Sun.COM 		rix = rate_table->info[rate->idx].cw40index;
1314*11729SWang.Lin@Sun.COM 	else
1315*11729SWang.Lin@Sun.COM 		rix = rate_table->info[rate->idx].base_index;
1316*11729SWang.Lin@Sun.COM 
1317*11729SWang.Lin@Sun.COM 	return (rix);
1318*11729SWang.Lin@Sun.COM }
1319*11729SWang.Lin@Sun.COM 
1320*11729SWang.Lin@Sun.COM static void
ath_rc_tx_status(struct arn_softc * sc,struct ath_rate_priv * ath_rc_priv,struct ath_buf * bf,int final_ts_idx,int xretries,int long_retry)1321*11729SWang.Lin@Sun.COM ath_rc_tx_status(struct arn_softc *sc, struct ath_rate_priv *ath_rc_priv,
1322*11729SWang.Lin@Sun.COM     struct ath_buf *bf, int final_ts_idx, int xretries, int long_retry)
1323*11729SWang.Lin@Sun.COM {
1324*11729SWang.Lin@Sun.COM 	struct ath_tx_info_priv *tx_info_priv =
1325*11729SWang.Lin@Sun.COM 	    (struct ath_tx_info_priv *)&bf->tx_info_priv;
1326*11729SWang.Lin@Sun.COM 	struct ath9k_tx_rate *rates = bf->rates;
1327*11729SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table;
1328*11729SWang.Lin@Sun.COM 	uint32_t i = 0, rix;
1329*11729SWang.Lin@Sun.COM 	uint8_t flags;
1330*11729SWang.Lin@Sun.COM 
1331*11729SWang.Lin@Sun.COM 	rate_table = sc->sc_currates;
1332*11729SWang.Lin@Sun.COM 
1333*11729SWang.Lin@Sun.COM 	/*
1334*11729SWang.Lin@Sun.COM 	 * If the first rate is not the final index, there
1335*11729SWang.Lin@Sun.COM 	 * are intermediate rate failures to be processed.
1336*11729SWang.Lin@Sun.COM 	 */
1337*11729SWang.Lin@Sun.COM 	if (final_ts_idx != 0) {
1338*11729SWang.Lin@Sun.COM 		/* Process intermediate rates that failed. */
1339*11729SWang.Lin@Sun.COM 		for (i = 0; i < final_ts_idx; i++) {
1340*11729SWang.Lin@Sun.COM 			if (rates[i].count != 0 && (rates[i].idx >= 0)) {
1341*11729SWang.Lin@Sun.COM 				flags = rates[i].flags;
1342*11729SWang.Lin@Sun.COM 
1343*11729SWang.Lin@Sun.COM 				/*
1344*11729SWang.Lin@Sun.COM 				 * If HT40 and we have switched mode from
1345*11729SWang.Lin@Sun.COM 				 * 40 to 20 => don't update
1346*11729SWang.Lin@Sun.COM 				 */
1347*11729SWang.Lin@Sun.COM 
1348*11729SWang.Lin@Sun.COM 				if ((flags & ATH9K_TX_RC_40_MHZ_WIDTH) &&
1349*11729SWang.Lin@Sun.COM 				    (ath_rc_priv->rc_phy_mode !=
1350*11729SWang.Lin@Sun.COM 				    WLAN_RC_40_FLAG))
1351*11729SWang.Lin@Sun.COM 					return;
1352*11729SWang.Lin@Sun.COM 
1353*11729SWang.Lin@Sun.COM 				rix =
1354*11729SWang.Lin@Sun.COM 				    ath_rc_get_rateindex(rate_table, &rates[i]);
1355*11729SWang.Lin@Sun.COM 				arn_rc_update_ht(sc, ath_rc_priv,
1356*11729SWang.Lin@Sun.COM 				    tx_info_priv, rix,
1357*11729SWang.Lin@Sun.COM 				    xretries ? 1 : 2,
1358*11729SWang.Lin@Sun.COM 				    rates[i].count);
1359*11729SWang.Lin@Sun.COM 			}
1360*11729SWang.Lin@Sun.COM 		}
1361*11729SWang.Lin@Sun.COM 	} else {
1362*11729SWang.Lin@Sun.COM 		/*
1363*11729SWang.Lin@Sun.COM 		 * Handle the special case of MIMO PS burst, where the second
1364*11729SWang.Lin@Sun.COM 		 * aggregate is sent out with only one rate and one try.
1365*11729SWang.Lin@Sun.COM 		 * Treating it as an excessive retry penalizes the rate
1366*11729SWang.Lin@Sun.COM 		 * inordinately.
1367*11729SWang.Lin@Sun.COM 		 */
1368*11729SWang.Lin@Sun.COM 		if (rates[0].count == 1 && xretries == 1)
1369*11729SWang.Lin@Sun.COM 			xretries = 2;
1370*11729SWang.Lin@Sun.COM 	}
1371*11729SWang.Lin@Sun.COM 
1372*11729SWang.Lin@Sun.COM 	flags = rates[i].flags;
1373*11729SWang.Lin@Sun.COM 
1374*11729SWang.Lin@Sun.COM 	/* If HT40 and we have switched mode from 40 to 20 => don't update */
1375*11729SWang.Lin@Sun.COM 	if ((flags & ATH9K_TX_RC_40_MHZ_WIDTH) &&
1376*11729SWang.Lin@Sun.COM 	    (ath_rc_priv->rc_phy_mode != WLAN_RC_40_FLAG)) {
1377*11729SWang.Lin@Sun.COM 		return;
1378*11729SWang.Lin@Sun.COM 	}
1379*11729SWang.Lin@Sun.COM 
1380*11729SWang.Lin@Sun.COM 	rix = ath_rc_get_rateindex(rate_table, &rates[i]);
1381*11729SWang.Lin@Sun.COM 	arn_rc_update_ht(sc, ath_rc_priv, tx_info_priv, rix,
1382*11729SWang.Lin@Sun.COM 	    xretries, long_retry);
1383*11729SWang.Lin@Sun.COM }
1384*11729SWang.Lin@Sun.COM 
1385*11729SWang.Lin@Sun.COM static struct ath_rate_table *
arn_choose_rate_table(struct arn_softc * sc,uint32_t cur_mode,boolean_t is_ht,boolean_t is_cw_40)1386*11729SWang.Lin@Sun.COM arn_choose_rate_table(struct arn_softc *sc, uint32_t cur_mode,
1387*11729SWang.Lin@Sun.COM     boolean_t is_ht, boolean_t is_cw_40)
1388*11729SWang.Lin@Sun.COM {
1389*11729SWang.Lin@Sun.COM 	int ath9k_mode;
1390*11729SWang.Lin@Sun.COM 	switch (cur_mode) {
1391*11729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
1392*11729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA:
1393*11729SWang.Lin@Sun.COM 		ath9k_mode = ATH9K_MODE_11A;
1394*11729SWang.Lin@Sun.COM 		if (is_ht)
1395*11729SWang.Lin@Sun.COM 			ath9k_mode = ATH9K_MODE_11NA_HT20;
1396*11729SWang.Lin@Sun.COM 		if (is_cw_40)
1397*11729SWang.Lin@Sun.COM 			ath9k_mode = ATH9K_MODE_11NA_HT40PLUS;
1398*11729SWang.Lin@Sun.COM 		break;
1399*11729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
1400*11729SWang.Lin@Sun.COM 		ath9k_mode = ATH9K_MODE_11B;
1401*11729SWang.Lin@Sun.COM 		break;
1402*11729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
1403*11729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG:
1404*11729SWang.Lin@Sun.COM 		ath9k_mode = ATH9K_MODE_11G;
1405*11729SWang.Lin@Sun.COM 		if (is_ht)
1406*11729SWang.Lin@Sun.COM 			ath9k_mode = ATH9K_MODE_11NG_HT20;
1407*11729SWang.Lin@Sun.COM 		if (is_cw_40)
1408*11729SWang.Lin@Sun.COM 			ath9k_mode = ATH9K_MODE_11NG_HT40PLUS;
1409*11729SWang.Lin@Sun.COM 		break;
1410*11729SWang.Lin@Sun.COM 	default:
1411*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "Invalid band\n"));
1412*11729SWang.Lin@Sun.COM 		return (NULL);
1413*11729SWang.Lin@Sun.COM 	}
1414*11729SWang.Lin@Sun.COM 
1415*11729SWang.Lin@Sun.COM 	switch (ath9k_mode) {
1416*11729SWang.Lin@Sun.COM 	case ATH9K_MODE_11A:
1417*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "choose rate table:ATH9K_MODE_11A\n"));
1418*11729SWang.Lin@Sun.COM 		break;
1419*11729SWang.Lin@Sun.COM 	case ATH9K_MODE_11B:
1420*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "choose rate table:ATH9K_MODE_11B\n"));
1421*11729SWang.Lin@Sun.COM 		break;
1422*11729SWang.Lin@Sun.COM 	case ATH9K_MODE_11G:
1423*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "choose rate table:ATH9K_MODE_11G\n"));
1424*11729SWang.Lin@Sun.COM 		break;
1425*11729SWang.Lin@Sun.COM 	case ATH9K_MODE_11NA_HT20:
1426*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE,
1427*11729SWang.Lin@Sun.COM 		    "choose rate table:ATH9K_MODE_11NA_HT20\n"));
1428*11729SWang.Lin@Sun.COM 		break;
1429*11729SWang.Lin@Sun.COM 	case ATH9K_MODE_11NA_HT40PLUS:
1430*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE,
1431*11729SWang.Lin@Sun.COM 		    "choose rate table:ATH9K_MODE_11NA_HT40PLUS\n"));
1432*11729SWang.Lin@Sun.COM 		break;
1433*11729SWang.Lin@Sun.COM 	case ATH9K_MODE_11NG_HT20:
1434*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE,
1435*11729SWang.Lin@Sun.COM 		    "choose rate table:ATH9K_MODE_11NG_HT20\n"));
1436*11729SWang.Lin@Sun.COM 		break;
1437*11729SWang.Lin@Sun.COM 	case ATH9K_MODE_11NG_HT40PLUS:
1438*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE,
1439*11729SWang.Lin@Sun.COM 		    "choose rate table:ATH9K_MODE_11NG_HT40PLUS\n"));
1440*11729SWang.Lin@Sun.COM 		break;
1441*11729SWang.Lin@Sun.COM 	default:
1442*11729SWang.Lin@Sun.COM 		arn_problem("Invalid band\n");
1443*11729SWang.Lin@Sun.COM 		break;
1444*11729SWang.Lin@Sun.COM 	}
1445*11729SWang.Lin@Sun.COM 
1446*11729SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_RATE, "Choosing rate table for mode: %d\n",
1447*11729SWang.Lin@Sun.COM 	    ath9k_mode));
1448*11729SWang.Lin@Sun.COM 	return (sc->hw_rate_table[ath9k_mode]);
1449*11729SWang.Lin@Sun.COM }
1450*11729SWang.Lin@Sun.COM 
1451*11729SWang.Lin@Sun.COM /* Private rate contral initialization */
1452*11729SWang.Lin@Sun.COM static void
arn_rc_init(struct arn_softc * sc,struct ath_rate_priv * ath_rc_priv,struct ieee80211_node * in)1453*11729SWang.Lin@Sun.COM arn_rc_init(struct arn_softc *sc,
1454*11729SWang.Lin@Sun.COM     struct ath_rate_priv *ath_rc_priv,
1455*11729SWang.Lin@Sun.COM     struct ieee80211_node *in)
1456*11729SWang.Lin@Sun.COM {
1457*11729SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table = NULL;
1458*11729SWang.Lin@Sun.COM 	struct ath_rateset *rateset = &ath_rc_priv->neg_rates;
1459*11729SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
1460*11729SWang.Lin@Sun.COM 	uint32_t cur_mode = ic->ic_curmode;
1461*11729SWang.Lin@Sun.COM 	uint8_t *ht_mcs = (uint8_t *)&ath_rc_priv->neg_ht_rates;
1462*11729SWang.Lin@Sun.COM 	uint8_t i, j, k, hi = 0, hthi = 0;
1463*11729SWang.Lin@Sun.COM 	boolean_t is_rc_ds;
1464*11729SWang.Lin@Sun.COM 
1465*11729SWang.Lin@Sun.COM 	/* FIXME: Adhoc */
1466*11729SWang.Lin@Sun.COM 	if ((sc->sc_ah->ah_opmode == ATH9K_M_STA) ||
1467*11729SWang.Lin@Sun.COM 	    (sc->sc_ah->ah_opmode == ATH9K_M_IBSS)) {
1468*11729SWang.Lin@Sun.COM 		boolean_t is_ht = in->in_flags & IEEE80211_NODE_HT;
1469*11729SWang.Lin@Sun.COM 		/* 20/40 support */
1470*11729SWang.Lin@Sun.COM 		boolean_t is_cw_40 =
1471*11729SWang.Lin@Sun.COM 		    in->in_htcap & IEEE80211_HTCAP_CHWIDTH40;
1472*11729SWang.Lin@Sun.COM 		rate_table =
1473*11729SWang.Lin@Sun.COM 		    arn_choose_rate_table(sc, cur_mode, is_ht, is_cw_40);
1474*11729SWang.Lin@Sun.COM 	} else if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
1475*11729SWang.Lin@Sun.COM 		/* cur_rate_table would be set on init */
1476*11729SWang.Lin@Sun.COM 		rate_table = sc->sc_currates;
1477*11729SWang.Lin@Sun.COM 	}
1478*11729SWang.Lin@Sun.COM 
1479*11729SWang.Lin@Sun.COM 	if (!rate_table) {
1480*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "Rate table not initialized\n"));
1481*11729SWang.Lin@Sun.COM 		return;
1482*11729SWang.Lin@Sun.COM 	}
1483*11729SWang.Lin@Sun.COM 
1484*11729SWang.Lin@Sun.COM 	if (in->in_flags & IEEE80211_NODE_HT) {
1485*11729SWang.Lin@Sun.COM 		/* 2.6.30 */
1486*11729SWang.Lin@Sun.COM 		ath_rc_priv->ht_cap = WLAN_RC_HT_FLAG;
1487*11729SWang.Lin@Sun.COM 		is_rc_ds = (AR_SREV_9280_20_OR_LATER(sc->sc_ah) &&
1488*11729SWang.Lin@Sun.COM 		    (ath9k_hw_get_eeprom(sc->sc_ah, EEP_RC_CHAIN_MASK) == 1)) ?
1489*11729SWang.Lin@Sun.COM 		    B_FALSE: B_TRUE;
1490*11729SWang.Lin@Sun.COM 		if (sc->sc_ah->ah_caps.tx_chainmask != 1 && is_rc_ds) {
1491*11729SWang.Lin@Sun.COM 			if (sc->sc_ht_conf.rx_mcs_mask[1]) {
1492*11729SWang.Lin@Sun.COM 				ath_rc_priv->ht_cap |= WLAN_RC_DS_FLAG;
1493*11729SWang.Lin@Sun.COM 			}
1494*11729SWang.Lin@Sun.COM 		}
1495*11729SWang.Lin@Sun.COM 
1496*11729SWang.Lin@Sun.COM 		if (in->in_htcap & IEEE80211_HTCAP_CHWIDTH40)
1497*11729SWang.Lin@Sun.COM 			ath_rc_priv->ht_cap |= WLAN_RC_40_FLAG;
1498*11729SWang.Lin@Sun.COM 		if (in->in_htcap & IEEE80211_HTCAP_SHORTGI40)
1499*11729SWang.Lin@Sun.COM 			ath_rc_priv->ht_cap |= WLAN_RC_SGI_FLAG;
1500*11729SWang.Lin@Sun.COM 	}
1501*11729SWang.Lin@Sun.COM 
1502*11729SWang.Lin@Sun.COM 	/*
1503*11729SWang.Lin@Sun.COM 	 * Initial rate table size. Will change depending
1504*11729SWang.Lin@Sun.COM 	 * on the working rate set
1505*11729SWang.Lin@Sun.COM 	 */
1506*11729SWang.Lin@Sun.COM 	ath_rc_priv->rate_table_size = RATE_TABLE_SIZE;
1507*11729SWang.Lin@Sun.COM 
1508*11729SWang.Lin@Sun.COM 	/* Initialize thresholds according to the global rate table */
1509*11729SWang.Lin@Sun.COM 	for (i = 0; i < ath_rc_priv->rate_table_size; i++) {
1510*11729SWang.Lin@Sun.COM 		ath_rc_priv->state[i].rssi_thres =
1511*11729SWang.Lin@Sun.COM 		    rate_table->info[i].rssi_ack_validmin;
1512*11729SWang.Lin@Sun.COM 		ath_rc_priv->state[i].per = 0;
1513*11729SWang.Lin@Sun.COM 	}
1514*11729SWang.Lin@Sun.COM 
1515*11729SWang.Lin@Sun.COM 	/* Determine the valid rates */
1516*11729SWang.Lin@Sun.COM 	arn_rc_init_valid_txmask(ath_rc_priv);
1517*11729SWang.Lin@Sun.COM 
1518*11729SWang.Lin@Sun.COM 	for (i = 0; i < WLAN_RC_PHY_MAX; i++) {
1519*11729SWang.Lin@Sun.COM 		for (j = 0; j < MAX_TX_RATE_PHY; j++)
1520*11729SWang.Lin@Sun.COM 			ath_rc_priv->valid_phy_rateidx[i][j] = 0;
1521*11729SWang.Lin@Sun.COM 		ath_rc_priv->valid_phy_ratecnt[i] = 0;
1522*11729SWang.Lin@Sun.COM 	}
1523*11729SWang.Lin@Sun.COM 	ath_rc_priv->rc_phy_mode = (ath_rc_priv->ht_cap & WLAN_RC_40_FLAG);
1524*11729SWang.Lin@Sun.COM 
1525*11729SWang.Lin@Sun.COM 	/* Set stream capability */
1526*11729SWang.Lin@Sun.COM 	ath_rc_priv->single_stream =
1527*11729SWang.Lin@Sun.COM 	    (ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG) ? 0 : 1;
1528*11729SWang.Lin@Sun.COM 
1529*11729SWang.Lin@Sun.COM 	if (!rateset->rs_nrates) {
1530*11729SWang.Lin@Sun.COM 		/* No working rate, just initialize valid rates */
1531*11729SWang.Lin@Sun.COM 		hi = arn_rc_init_validrates(ath_rc_priv, rate_table,
1532*11729SWang.Lin@Sun.COM 		    ath_rc_priv->ht_cap);
1533*11729SWang.Lin@Sun.COM 	} else {
1534*11729SWang.Lin@Sun.COM 		/* Use intersection of working rates and valid rates */
1535*11729SWang.Lin@Sun.COM 		hi = arn_rc_setvalid_rates(ath_rc_priv, rate_table,
1536*11729SWang.Lin@Sun.COM 		    rateset, ath_rc_priv->ht_cap);
1537*11729SWang.Lin@Sun.COM 		if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG) {
1538*11729SWang.Lin@Sun.COM 			hthi = arn_rc_setvalid_htrates(ath_rc_priv,
1539*11729SWang.Lin@Sun.COM 			    rate_table,
1540*11729SWang.Lin@Sun.COM 			    ht_mcs,
1541*11729SWang.Lin@Sun.COM 			    ath_rc_priv->ht_cap);
1542*11729SWang.Lin@Sun.COM 		}
1543*11729SWang.Lin@Sun.COM 		hi = A_MAX(hi, hthi);
1544*11729SWang.Lin@Sun.COM 	}
1545*11729SWang.Lin@Sun.COM 
1546*11729SWang.Lin@Sun.COM 	ath_rc_priv->rate_table_size = hi + 1;
1547*11729SWang.Lin@Sun.COM 	ath_rc_priv->rate_max_phy = 0;
1548*11729SWang.Lin@Sun.COM 	ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE);
1549*11729SWang.Lin@Sun.COM 
1550*11729SWang.Lin@Sun.COM 	for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) {
1551*11729SWang.Lin@Sun.COM 		for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) {
1552*11729SWang.Lin@Sun.COM 			ath_rc_priv->valid_rate_index[k++] =
1553*11729SWang.Lin@Sun.COM 			    ath_rc_priv->valid_phy_rateidx[i][j];
1554*11729SWang.Lin@Sun.COM 		}
1555*11729SWang.Lin@Sun.COM 
1556*11729SWang.Lin@Sun.COM 		if (!arn_rc_valid_phyrate(i, rate_table->initial_ratemax, 1) ||
1557*11729SWang.Lin@Sun.COM 		    !ath_rc_priv->valid_phy_ratecnt[i])
1558*11729SWang.Lin@Sun.COM 			continue;
1559*11729SWang.Lin@Sun.COM 
1560*11729SWang.Lin@Sun.COM 		ath_rc_priv->rate_max_phy =
1561*11729SWang.Lin@Sun.COM 		    ath_rc_priv->valid_phy_rateidx[i][j-1];
1562*11729SWang.Lin@Sun.COM 	}
1563*11729SWang.Lin@Sun.COM 	ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE);
1564*11729SWang.Lin@Sun.COM 	ASSERT(k <= RATE_TABLE_SIZE);
1565*11729SWang.Lin@Sun.COM 
1566*11729SWang.Lin@Sun.COM 	ath_rc_priv->max_valid_rate = k;
1567*11729SWang.Lin@Sun.COM 	arn_rc_sort_validrates(rate_table, ath_rc_priv);
1568*11729SWang.Lin@Sun.COM 	ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4];
1569*11729SWang.Lin@Sun.COM 	sc->sc_currates = rate_table;
1570*11729SWang.Lin@Sun.COM }
1571*11729SWang.Lin@Sun.COM 
1572*11729SWang.Lin@Sun.COM void
arn_tx_status(struct arn_softc * sc,struct ath_buf * bf,boolean_t is_data)1573*11729SWang.Lin@Sun.COM arn_tx_status(struct arn_softc *sc, struct ath_buf *bf, boolean_t is_data)
1574*11729SWang.Lin@Sun.COM {
1575*11729SWang.Lin@Sun.COM 	struct ieee80211_node *in = (struct ieee80211_node *)(bf->bf_in);
1576*11729SWang.Lin@Sun.COM 	struct ath_node *an = ATH_NODE(in);
1577*11729SWang.Lin@Sun.COM 	struct ath_rate_priv *ath_rc_priv =
1578*11729SWang.Lin@Sun.COM 	    (struct ath_rate_priv *)&an->rate_priv;
1579*11729SWang.Lin@Sun.COM 	struct ath_tx_info_priv *tx_info_priv =
1580*11729SWang.Lin@Sun.COM 	    (struct ath_tx_info_priv *)&bf->tx_info_priv;
1581*11729SWang.Lin@Sun.COM 	int final_ts_idx, tx_status = 0, is_underrun = 0;
1582*11729SWang.Lin@Sun.COM 
1583*11729SWang.Lin@Sun.COM 	final_ts_idx = tx_info_priv->tx.ts_rateindex;
1584*11729SWang.Lin@Sun.COM 
1585*11729SWang.Lin@Sun.COM 	if (!is_data || !tx_info_priv->update_rc)
1586*11729SWang.Lin@Sun.COM 		return;
1587*11729SWang.Lin@Sun.COM 
1588*11729SWang.Lin@Sun.COM 	if (tx_info_priv->tx.ts_status & ATH9K_TXERR_FILT)
1589*11729SWang.Lin@Sun.COM 		return;
1590*11729SWang.Lin@Sun.COM 
1591*11729SWang.Lin@Sun.COM 	/*
1592*11729SWang.Lin@Sun.COM 	 * If underrun error is seen assume it as an excessive retry only
1593*11729SWang.Lin@Sun.COM 	 * if prefetch trigger level have reached the max (0x3f for 5416)
1594*11729SWang.Lin@Sun.COM 	 * Adjust the long retry as if the frame was tried ATH_11N_TXMAXTRY
1595*11729SWang.Lin@Sun.COM 	 * times. This affects how ratectrl updates PER for the failed rate.
1596*11729SWang.Lin@Sun.COM 	 */
1597*11729SWang.Lin@Sun.COM 	if (tx_info_priv->tx.ts_flags &
1598*11729SWang.Lin@Sun.COM 	    (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) &&
1599*11729SWang.Lin@Sun.COM 	    ((sc->sc_ah->ah_txTrigLevel) >= ath_rc_priv->tx_triglevel_max)) {
1600*11729SWang.Lin@Sun.COM 		tx_status = 1;
1601*11729SWang.Lin@Sun.COM 		is_underrun = 1;
1602*11729SWang.Lin@Sun.COM 	}
1603*11729SWang.Lin@Sun.COM 
1604*11729SWang.Lin@Sun.COM 	if ((tx_info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) ||
1605*11729SWang.Lin@Sun.COM 	    (tx_info_priv->tx.ts_status & ATH9K_TXERR_FIFO))
1606*11729SWang.Lin@Sun.COM 		tx_status = 1;
1607*11729SWang.Lin@Sun.COM 
1608*11729SWang.Lin@Sun.COM 	ath_rc_tx_status(sc,
1609*11729SWang.Lin@Sun.COM 	    ath_rc_priv,
1610*11729SWang.Lin@Sun.COM 	    bf,
1611*11729SWang.Lin@Sun.COM 	    final_ts_idx,
1612*11729SWang.Lin@Sun.COM 	    tx_status,
1613*11729SWang.Lin@Sun.COM 	    (is_underrun) ? ATH_11N_TXMAXTRY : tx_info_priv->tx.ts_longretry);
1614*11729SWang.Lin@Sun.COM }
1615*11729SWang.Lin@Sun.COM 
1616*11729SWang.Lin@Sun.COM void
arn_get_rate(struct arn_softc * sc,struct ath_buf * bf,struct ieee80211_frame * wh)1617*11729SWang.Lin@Sun.COM arn_get_rate(struct arn_softc *sc, struct ath_buf *bf,
1618*11729SWang.Lin@Sun.COM     struct ieee80211_frame *wh)
1619*11729SWang.Lin@Sun.COM {
1620*11729SWang.Lin@Sun.COM 	struct ieee80211_node *in = (struct ieee80211_node *)(bf->bf_in);
1621*11729SWang.Lin@Sun.COM 	struct ath_node *an = ATH_NODE(in);
1622*11729SWang.Lin@Sun.COM 	struct ath_rate_priv *ath_rc_priv =
1623*11729SWang.Lin@Sun.COM 	    (struct ath_rate_priv *)&an->rate_priv;
1624*11729SWang.Lin@Sun.COM 	struct ath_rate_table *rt = sc->sc_currates;
1625*11729SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
1626*11729SWang.Lin@Sun.COM 	int is_probe = 0;
1627*11729SWang.Lin@Sun.COM 	uint8_t i;
1628*11729SWang.Lin@Sun.COM 
1629*11729SWang.Lin@Sun.COM 	/* lowest rate for management and multicast/broadcast frames */
1630*11729SWang.Lin@Sun.COM 	if (!IEEE80211_IS_DATA(wh) || IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1631*11729SWang.Lin@Sun.COM 		bf->rates[0].idx = 0; /* xxx Fix me */
1632*11729SWang.Lin@Sun.COM 		bf->rates[0].count =
1633*11729SWang.Lin@Sun.COM 		    IEEE80211_IS_MULTICAST(wh->i_addr1) ?
1634*11729SWang.Lin@Sun.COM 		    1 : ATH_MGT_TXMAXTRY;
1635*11729SWang.Lin@Sun.COM 		return;
1636*11729SWang.Lin@Sun.COM 	}
1637*11729SWang.Lin@Sun.COM 
1638*11729SWang.Lin@Sun.COM 	/* Find tx rate for unicast frames */
1639*11729SWang.Lin@Sun.COM 	arn_rc_ratefind(sc, ath_rc_priv, bf, ATH_11N_TXMAXTRY, 4,
1640*11729SWang.Lin@Sun.COM 	    &is_probe, B_FALSE);
1641*11729SWang.Lin@Sun.COM 
1642*11729SWang.Lin@Sun.COM 	/* Temporary workaround for 'dladm show-wifi' */
1643*11729SWang.Lin@Sun.COM 	for (i = 0; i < in->in_rates.ir_nrates; i++) {
1644*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_get_rate(): "
1645*11729SWang.Lin@Sun.COM 		    "in->in_rates.ir_rates[%d] = %d,"
1646*11729SWang.Lin@Sun.COM 		    "bf->rates[0].idx = %d,"
1647*11729SWang.Lin@Sun.COM 		    "rt->info[bf->rates[0].idx].dot11rate = %d\n",
1648*11729SWang.Lin@Sun.COM 		    i,
1649*11729SWang.Lin@Sun.COM 		    in->in_rates.ir_rates[i],
1650*11729SWang.Lin@Sun.COM 		    bf->rates[0].idx,
1651*11729SWang.Lin@Sun.COM 		    rt->info[bf->rates[0].idx].dot11rate));
1652*11729SWang.Lin@Sun.COM 		if (rt->info[bf->rates[0].idx].dot11rate ==
1653*11729SWang.Lin@Sun.COM 		    in->in_rates.ir_rates[i])
1654*11729SWang.Lin@Sun.COM 			break;
1655*11729SWang.Lin@Sun.COM 	}
1656*11729SWang.Lin@Sun.COM 	in->in_txrate = i;
1657*11729SWang.Lin@Sun.COM 	if (ic->ic_curmode == IEEE80211_MODE_11NA ||
1658*11729SWang.Lin@Sun.COM 	    ic->ic_curmode == IEEE80211_MODE_11NG)
1659*11729SWang.Lin@Sun.COM 		in->in_txrate = in->in_rates.ir_nrates - 1;
1660*11729SWang.Lin@Sun.COM 
1661*11729SWang.Lin@Sun.COM 	/* Check if aggregation has to be enabled for this tid */
1662*11729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
1663*11729SWang.Lin@Sun.COM 	/* should check if enabled, not supported */
1664*11729SWang.Lin@Sun.COM 	if (sc->sc_ht_conf.ht_supported) {
1665*11729SWang.Lin@Sun.COM 		if (ieee80211_is_data_qos(wh)) {
1666*11729SWang.Lin@Sun.COM 			uint8_t *qc, tid;
1667*11729SWang.Lin@Sun.COM 			struct ath_node *an;
1668*11729SWang.Lin@Sun.COM 			struct ieee80211_qosframe *qwh = NULL;
1669*11729SWang.Lin@Sun.COM 
1670*11729SWang.Lin@Sun.COM 			qwh = (struct ieee80211_qosframe *)wh;
1671*11729SWang.Lin@Sun.COM 			tid = qc[0] & 0xf;
1672*11729SWang.Lin@Sun.COM 			an = (struct ath_node *)sta->drv_priv;
1673*11729SWang.Lin@Sun.COM 
1674*11729SWang.Lin@Sun.COM 			if (arn_tx_aggr_check(sc, an, tid))
1675*11729SWang.Lin@Sun.COM 				/* to do */
1676*11729SWang.Lin@Sun.COM 		}
1677*11729SWang.Lin@Sun.COM 	}
1678*11729SWang.Lin@Sun.COM #endif /* ARN_TX_AGGREGATION */
1679*11729SWang.Lin@Sun.COM }
1680*11729SWang.Lin@Sun.COM 
1681*11729SWang.Lin@Sun.COM void
arn_rate_init(struct arn_softc * sc,struct ieee80211_node * in)1682*11729SWang.Lin@Sun.COM arn_rate_init(struct arn_softc *sc, struct ieee80211_node *in)
1683*11729SWang.Lin@Sun.COM {
1684*11729SWang.Lin@Sun.COM 	int i;
1685*11729SWang.Lin@Sun.COM 	struct ath_node *an = ATH_NODE(in);
1686*11729SWang.Lin@Sun.COM 	struct ath_rate_priv *ath_rc_priv =
1687*11729SWang.Lin@Sun.COM 	    (struct ath_rate_priv *)&an->rate_priv;
1688*11729SWang.Lin@Sun.COM 
1689*11729SWang.Lin@Sun.COM 	/* should be moved to arn_node_init later */
1690*11729SWang.Lin@Sun.COM 	ath_rc_priv->rssi_down_time =
1691*11729SWang.Lin@Sun.COM 	    drv_hztousec(ddi_get_lbolt())/1000; /* mesc */
1692*11729SWang.Lin@Sun.COM 	ath_rc_priv->tx_triglevel_max =
1693*11729SWang.Lin@Sun.COM 	    sc->sc_ah->ah_caps.tx_triglevel_max;
1694*11729SWang.Lin@Sun.COM 
1695*11729SWang.Lin@Sun.COM 	for (i = 0; i < in->in_rates.ir_nrates; i++) {
1696*11729SWang.Lin@Sun.COM 		ath_rc_priv->neg_rates.rs_rates[i] = in->in_rates.ir_rates[i];
1697*11729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn:arn_rate_init()"
1698*11729SWang.Lin@Sun.COM 		    "ath_rc_priv->neg_rates.rs_rates[%d] = %d\n",
1699*11729SWang.Lin@Sun.COM 		    i, ath_rc_priv->neg_rates.rs_rates[i]));
1700*11729SWang.Lin@Sun.COM 	}
1701*11729SWang.Lin@Sun.COM 	ath_rc_priv->neg_rates.rs_nrates = in->in_rates.ir_nrates;
1702*11729SWang.Lin@Sun.COM 
1703*11729SWang.Lin@Sun.COM 	/* negotiated ht rate set ??? */
1704*11729SWang.Lin@Sun.COM 	if (in->in_flags & IEEE80211_NODE_HT) {
1705*11729SWang.Lin@Sun.COM 		for (i = 0; i < in->in_htrates.rs_nrates; i++) {
1706*11729SWang.Lin@Sun.COM 			ath_rc_priv->neg_ht_rates.rs_rates[i] =
1707*11729SWang.Lin@Sun.COM 			    in->in_htrates.rs_rates[i];
1708*11729SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_RATE, "arn:arn_rate_init()"
1709*11729SWang.Lin@Sun.COM 			    "ath_rc_priv->neg_ht_rates.rs_rates[%d] = %d\n",
1710*11729SWang.Lin@Sun.COM 			    i, ath_rc_priv->neg_ht_rates.rs_rates[i]));
1711*11729SWang.Lin@Sun.COM 		}
1712*11729SWang.Lin@Sun.COM 		ath_rc_priv->neg_ht_rates.rs_nrates = in->in_htrates.rs_nrates;
1713*11729SWang.Lin@Sun.COM 
1714*11729SWang.Lin@Sun.COM 		/* arn_update_chainmask(sc); */
1715*11729SWang.Lin@Sun.COM 	}
1716*11729SWang.Lin@Sun.COM 
1717*11729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
1718*11729SWang.Lin@Sun.COM 	/* Temply put the following ht info init here */
1719*11729SWang.Lin@Sun.COM 	uint8_t ampdu_factor, ampdu_density;
1720*11729SWang.Lin@Sun.COM 	if (sc->sc_ht_conf.ht_support &&
1721*11729SWang.Lin@Sun.COM 	    (in->in_htcap_ie != NULL) &&
1722*11729SWang.Lin@Sun.COM 	    (in->in_htcap != 0) &&
1723*11729SWang.Lin@Sun.COM 	    (in->in_htparam != 0)) {
1724*11729SWang.Lin@Sun.COM 		ampdu_factor = in->in_htparam & HT_RX_AMPDU_FACTOR_MSK;
1725*11729SWang.Lin@Sun.COM 		ampdu_density = (in->in_htparam & HT_MPDU_DENSITY_MSK) >>
1726*11729SWang.Lin@Sun.COM 		    HT_MPDU_DENSITY_POS;
1727*11729SWang.Lin@Sun.COM 		an->maxampdu =
1728*11729SWang.Lin@Sun.COM 		    1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + ampdu_factor);
1729*11729SWang.Lin@Sun.COM 		an->mpdudensity = parse_mpdudensity(ampdu_density);
1730*11729SWang.Lin@Sun.COM 	}
1731*11729SWang.Lin@Sun.COM 	/* end */
1732*11729SWang.Lin@Sun.COM #endif /* ARN_TX_AGGREGATION */
1733*11729SWang.Lin@Sun.COM 
1734*11729SWang.Lin@Sun.COM 	arn_rc_init(sc, ath_rc_priv, in);
1735*11729SWang.Lin@Sun.COM }
1736*11729SWang.Lin@Sun.COM 
17379999SWang.Lin@Sun.COM static void
arn_setup_rate_table(struct arn_softc * sc,struct ath_rate_table * rate_table)17389999SWang.Lin@Sun.COM arn_setup_rate_table(struct arn_softc *sc,
17399999SWang.Lin@Sun.COM     struct ath_rate_table *rate_table)
17409999SWang.Lin@Sun.COM {
17419999SWang.Lin@Sun.COM 	int i;
17429999SWang.Lin@Sun.COM 
17439999SWang.Lin@Sun.COM 	for (i = 0; i < 256; i++)
17449999SWang.Lin@Sun.COM 		rate_table->rateCodeToIndex[i] = (uint8_t)-1;
17459999SWang.Lin@Sun.COM 
17469999SWang.Lin@Sun.COM 	for (i = 0; i < rate_table->rate_cnt; i++) {
17479999SWang.Lin@Sun.COM 		uint8_t code = rate_table->info[i].ratecode;
17489999SWang.Lin@Sun.COM 		uint8_t cix = rate_table->info[i].ctrl_rate;
17499999SWang.Lin@Sun.COM 		uint8_t sh = rate_table->info[i].short_preamble;
17509999SWang.Lin@Sun.COM 
17519999SWang.Lin@Sun.COM 		rate_table->rateCodeToIndex[code] = (int)i;
17529999SWang.Lin@Sun.COM 		rate_table->rateCodeToIndex[code | sh] = (int)i;
17539999SWang.Lin@Sun.COM 
17549999SWang.Lin@Sun.COM 		rate_table->info[i].lpAckDuration =
17559999SWang.Lin@Sun.COM 		    ath9k_hw_computetxtime(sc->sc_ah, rate_table,
17569999SWang.Lin@Sun.COM 		    WLAN_CTRL_FRAME_SIZE,
17579999SWang.Lin@Sun.COM 		    cix,
17589999SWang.Lin@Sun.COM 		    B_FALSE);
17599999SWang.Lin@Sun.COM 		rate_table->info[i].spAckDuration =
17609999SWang.Lin@Sun.COM 		    ath9k_hw_computetxtime(sc->sc_ah, rate_table,
17619999SWang.Lin@Sun.COM 		    WLAN_CTRL_FRAME_SIZE,
17629999SWang.Lin@Sun.COM 		    cix,
17639999SWang.Lin@Sun.COM 		    B_TRUE);
17649999SWang.Lin@Sun.COM 	}
17659999SWang.Lin@Sun.COM }
17669999SWang.Lin@Sun.COM 
17679999SWang.Lin@Sun.COM void
arn_rate_attach(struct arn_softc * sc)17689999SWang.Lin@Sun.COM arn_rate_attach(struct arn_softc *sc)
17699999SWang.Lin@Sun.COM {
17709999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11B] =
17719999SWang.Lin@Sun.COM 	    &ar5416_11b_ratetable;
17729999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11A] =
17739999SWang.Lin@Sun.COM 	    &ar5416_11a_ratetable;
17749999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11G] =
17759999SWang.Lin@Sun.COM 	    &ar5416_11g_ratetable;
17769999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11NA_HT20] =
17779999SWang.Lin@Sun.COM 	    &ar5416_11na_ratetable;
17789999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11NG_HT20] =
17799999SWang.Lin@Sun.COM 	    &ar5416_11ng_ratetable;
17809999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] =
17819999SWang.Lin@Sun.COM 	    &ar5416_11na_ratetable;
17829999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] =
17839999SWang.Lin@Sun.COM 	    &ar5416_11na_ratetable;
17849999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] =
17859999SWang.Lin@Sun.COM 	    &ar5416_11ng_ratetable;
17869999SWang.Lin@Sun.COM 	sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] =
17879999SWang.Lin@Sun.COM 	    &ar5416_11ng_ratetable;
17889999SWang.Lin@Sun.COM 
17899999SWang.Lin@Sun.COM 	arn_setup_rate_table(sc, &ar5416_11b_ratetable);
17909999SWang.Lin@Sun.COM 	arn_setup_rate_table(sc, &ar5416_11a_ratetable);
17919999SWang.Lin@Sun.COM 	arn_setup_rate_table(sc, &ar5416_11g_ratetable);
17929999SWang.Lin@Sun.COM 	arn_setup_rate_table(sc, &ar5416_11na_ratetable);
17939999SWang.Lin@Sun.COM 	arn_setup_rate_table(sc, &ar5416_11ng_ratetable);
17949999SWang.Lin@Sun.COM }
17959999SWang.Lin@Sun.COM 
1796*11729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
17979999SWang.Lin@Sun.COM void
arn_rate_update(struct arn_softc * sc,struct ieee80211_node * in,int32_t rate)17989999SWang.Lin@Sun.COM arn_rate_update(struct arn_softc *sc, struct ieee80211_node *in, int32_t rate)
17999999SWang.Lin@Sun.COM {
18009999SWang.Lin@Sun.COM 	struct ath_node *an = ATH_NODE(in);
18019999SWang.Lin@Sun.COM 	const struct ath_rate_table *rt = sc->sc_currates;
18029999SWang.Lin@Sun.COM 	uint8_t rix;
18039999SWang.Lin@Sun.COM 
18049999SWang.Lin@Sun.COM 	ASSERT(rt != NULL);
18059999SWang.Lin@Sun.COM 
18069999SWang.Lin@Sun.COM 	in->in_txrate = rate;
18079999SWang.Lin@Sun.COM 
18089999SWang.Lin@Sun.COM 	/* management/control frames always go at the lowest speed */
18099999SWang.Lin@Sun.COM 	an->an_tx_mgtrate = rt->info[0].ratecode;
18109999SWang.Lin@Sun.COM 	an->an_tx_mgtratesp = an->an_tx_mgtrate | rt->info[0].short_preamble;
18119999SWang.Lin@Sun.COM 
18129999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_update(): "
18139999SWang.Lin@Sun.COM 	    "mgtrate=%d mgtratesp=%d\n",
18149999SWang.Lin@Sun.COM 	    an->an_tx_mgtrate, an->an_tx_mgtratesp));
18159999SWang.Lin@Sun.COM 
18169999SWang.Lin@Sun.COM 	/*
18179999SWang.Lin@Sun.COM 	 * Before associating a node has no rate set setup
18189999SWang.Lin@Sun.COM 	 * so we can't calculate any transmit codes to use.
18199999SWang.Lin@Sun.COM 	 * This is ok since we should never be sending anything
18209999SWang.Lin@Sun.COM 	 * but management frames and those always go at the
18219999SWang.Lin@Sun.COM 	 * lowest hardware rate.
18229999SWang.Lin@Sun.COM 	 */
18239999SWang.Lin@Sun.COM 	if (in->in_rates.ir_nrates == 0)
18249999SWang.Lin@Sun.COM 		goto done;
18259999SWang.Lin@Sun.COM 	an->an_tx_rix0 = sc->asc_rixmap[
18269999SWang.Lin@Sun.COM 	    in->in_rates.ir_rates[rate] & IEEE80211_RATE_VAL];
18279999SWang.Lin@Sun.COM 	an->an_tx_rate0 = rt->info[an->an_tx_rix0].ratecode;
18289999SWang.Lin@Sun.COM 	an->an_tx_rate0sp = an->an_tx_rate0 |
18299999SWang.Lin@Sun.COM 	    rt->info[an->an_tx_rix0].short_preamble;
18309999SWang.Lin@Sun.COM 	if (sc->sc_mrretry) {
18319999SWang.Lin@Sun.COM 		/*
18329999SWang.Lin@Sun.COM 		 * Hardware supports multi-rate retry; setup two
18339999SWang.Lin@Sun.COM 		 * step-down retry rates and make the lowest rate
18349999SWang.Lin@Sun.COM 		 * be the ``last chance''.  We use 4, 2, 2, 2 tries
18359999SWang.Lin@Sun.COM 		 * respectively (4 is set here, the rest are fixed
18369999SWang.Lin@Sun.COM 		 * in the xmit routine).
18379999SWang.Lin@Sun.COM 		 */
18389999SWang.Lin@Sun.COM 		an->an_tx_try0 = 1 + 3;		/* 4 tries at rate 0 */
18399999SWang.Lin@Sun.COM 		if (--rate >= 0) {
18409999SWang.Lin@Sun.COM 			rix = sc->asc_rixmap[
18419999SWang.Lin@Sun.COM 			    in->in_rates.ir_rates[rate]&IEEE80211_RATE_VAL];
18429999SWang.Lin@Sun.COM 			an->an_tx_rate1 = rt->info[rix].ratecode;
18439999SWang.Lin@Sun.COM 			an->an_tx_rate1sp = an->an_tx_rate1 |
18449999SWang.Lin@Sun.COM 			    rt->info[rix].short_preamble;
18459999SWang.Lin@Sun.COM 		} else {
18469999SWang.Lin@Sun.COM 			an->an_tx_rate1 = an->an_tx_rate1sp = 0;
18479999SWang.Lin@Sun.COM 		}
18489999SWang.Lin@Sun.COM 		if (--rate >= 0) {
18499999SWang.Lin@Sun.COM 			rix = sc->asc_rixmap[
18509999SWang.Lin@Sun.COM 			    in->in_rates.ir_rates[rate]&IEEE80211_RATE_VAL];
18519999SWang.Lin@Sun.COM 			an->an_tx_rate2 = rt->info[rix].ratecode;
18529999SWang.Lin@Sun.COM 			an->an_tx_rate2sp = an->an_tx_rate2 |
18539999SWang.Lin@Sun.COM 			    rt->info[rix].short_preamble;
18549999SWang.Lin@Sun.COM 		} else {
18559999SWang.Lin@Sun.COM 			an->an_tx_rate2 = an->an_tx_rate2sp = 0;
18569999SWang.Lin@Sun.COM 		}
18579999SWang.Lin@Sun.COM 		if (rate > 0) {
18589999SWang.Lin@Sun.COM 			an->an_tx_rate3 = rt->info[0].ratecode;
18599999SWang.Lin@Sun.COM 			an->an_tx_rate3sp =
18609999SWang.Lin@Sun.COM 			    an->an_tx_mgtrate | rt->info[0].short_preamble;
18619999SWang.Lin@Sun.COM 		} else {
18629999SWang.Lin@Sun.COM 			an->an_tx_rate3 = an->an_tx_rate3sp = 0;
18639999SWang.Lin@Sun.COM 		}
18649999SWang.Lin@Sun.COM 	} else {
18659999SWang.Lin@Sun.COM 		an->an_tx_try0 = ATH_TXMAXTRY;  /* max tries at rate 0 */
18669999SWang.Lin@Sun.COM 		an->an_tx_rate1 = an->an_tx_rate1sp = 0;
18679999SWang.Lin@Sun.COM 		an->an_tx_rate2 = an->an_tx_rate2sp = 0;
18689999SWang.Lin@Sun.COM 		an->an_tx_rate3 = an->an_tx_rate3sp = 0;
18699999SWang.Lin@Sun.COM 	}
18709999SWang.Lin@Sun.COM done:
18719999SWang.Lin@Sun.COM 	an->an_tx_ok = an->an_tx_err = an->an_tx_retr = an->an_tx_upper = 0;
18729999SWang.Lin@Sun.COM }
18739999SWang.Lin@Sun.COM 
18749999SWang.Lin@Sun.COM /*
18759999SWang.Lin@Sun.COM  * Set the starting transmit rate for a node.
18769999SWang.Lin@Sun.COM  */
18779999SWang.Lin@Sun.COM void
arn_rate_ctl_start(struct arn_softc * sc,struct ieee80211_node * in)18789999SWang.Lin@Sun.COM arn_rate_ctl_start(struct arn_softc *sc, struct ieee80211_node *in)
18799999SWang.Lin@Sun.COM {
18809999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
18819999SWang.Lin@Sun.COM 	int32_t srate;
18829999SWang.Lin@Sun.COM 
18839999SWang.Lin@Sun.COM 	if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
18849999SWang.Lin@Sun.COM 		/*
18859999SWang.Lin@Sun.COM 		 * No fixed rate is requested. For 11b start with
18869999SWang.Lin@Sun.COM 		 * the highest negotiated rate; otherwise, for 11g
18879999SWang.Lin@Sun.COM 		 * and 11a, we start "in the middle" at 24Mb or 36Mb.
18889999SWang.Lin@Sun.COM 		 */
18899999SWang.Lin@Sun.COM 		srate = in->in_rates.ir_nrates - 1;
18909999SWang.Lin@Sun.COM 		if (sc->sc_curmode != IEEE80211_MODE_11B) {
18919999SWang.Lin@Sun.COM 			/*
18929999SWang.Lin@Sun.COM 			 * Scan the negotiated rate set to find the
18939999SWang.Lin@Sun.COM 			 * closest rate.
18949999SWang.Lin@Sun.COM 			 */
18959999SWang.Lin@Sun.COM 			/* NB: the rate set is assumed sorted */
18969999SWang.Lin@Sun.COM 			for (; srate >= 0 && IEEE80211_RATE(srate) > 72;
18979999SWang.Lin@Sun.COM 			    srate--) {}
18989999SWang.Lin@Sun.COM 		}
18999999SWang.Lin@Sun.COM 	} else {
19009999SWang.Lin@Sun.COM 		/*
19019999SWang.Lin@Sun.COM 		 * A fixed rate is to be used; We know the rate is
19029999SWang.Lin@Sun.COM 		 * there because the rate set is checked when the
19039999SWang.Lin@Sun.COM 		 * station associates.
19049999SWang.Lin@Sun.COM 		 */
19059999SWang.Lin@Sun.COM 		/* NB: the rate set is assumed sorted */
19069999SWang.Lin@Sun.COM 		srate = in->in_rates.ir_nrates - 1;
19079999SWang.Lin@Sun.COM 		for (; srate >= 0 && IEEE80211_RATE(srate) != ic->ic_fixed_rate;
19089999SWang.Lin@Sun.COM 		    srate--) {}
19099999SWang.Lin@Sun.COM 	}
19109999SWang.Lin@Sun.COM 
19119999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_ctl_start(): "
19129999SWang.Lin@Sun.COM 	    "srate=%d rate=%d\n", srate, IEEE80211_RATE(srate)));
19139999SWang.Lin@Sun.COM 
19149999SWang.Lin@Sun.COM 	arn_rate_update(sc, in, srate);
19159999SWang.Lin@Sun.COM }
19169999SWang.Lin@Sun.COM 
19179999SWang.Lin@Sun.COM void
arn_rate_cb(void * arg,struct ieee80211_node * in)19189999SWang.Lin@Sun.COM arn_rate_cb(void *arg, struct ieee80211_node *in)
19199999SWang.Lin@Sun.COM {
19209999SWang.Lin@Sun.COM 	arn_rate_update((struct arn_softc *)arg, in, 0);
19219999SWang.Lin@Sun.COM }
1922*11729SWang.Lin@Sun.COM #endif /* ARN_LEGACY_RC */
19239999SWang.Lin@Sun.COM /*
19249999SWang.Lin@Sun.COM  * Reset the rate control state for each 802.11 state transition.
19259999SWang.Lin@Sun.COM  */
19269999SWang.Lin@Sun.COM void
arn_rate_ctl_reset(struct arn_softc * sc,enum ieee80211_state state)19279999SWang.Lin@Sun.COM arn_rate_ctl_reset(struct arn_softc *sc, enum ieee80211_state state)
19289999SWang.Lin@Sun.COM {
19299999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
19309999SWang.Lin@Sun.COM 	struct ieee80211_node *in;
19319999SWang.Lin@Sun.COM 
19329999SWang.Lin@Sun.COM 	if (ic->ic_opmode == IEEE80211_M_STA) {
19339999SWang.Lin@Sun.COM 		/*
19349999SWang.Lin@Sun.COM 		 * Reset local xmit state; this is really only
19359999SWang.Lin@Sun.COM 		 * meaningful when operating in station mode.
19369999SWang.Lin@Sun.COM 		 */
19379999SWang.Lin@Sun.COM 		in = (struct ieee80211_node *)ic->ic_bss;
1938*11729SWang.Lin@Sun.COM 
1939*11729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
19409999SWang.Lin@Sun.COM 		if (state == IEEE80211_S_RUN) {
19419999SWang.Lin@Sun.COM 			arn_rate_ctl_start(sc, in);
19429999SWang.Lin@Sun.COM 		} else {
19439999SWang.Lin@Sun.COM 			arn_rate_update(sc, in, 0);
19449999SWang.Lin@Sun.COM 		}
1945*11729SWang.Lin@Sun.COM #else
1946*11729SWang.Lin@Sun.COM 		if (state == IEEE80211_S_RUN)
1947*11729SWang.Lin@Sun.COM 			arn_rate_init(sc, in);
1948*11729SWang.Lin@Sun.COM #endif
1949*11729SWang.Lin@Sun.COM 	/* LINTED E_NOP_ELSE_STMT */
19509999SWang.Lin@Sun.COM 	} else {
19519999SWang.Lin@Sun.COM 		/*
19529999SWang.Lin@Sun.COM 		 * When operating as a station the node table holds
19539999SWang.Lin@Sun.COM 		 * the AP's that were discovered during scanning.
19549999SWang.Lin@Sun.COM 		 * For any other operating mode we want to reset the
19559999SWang.Lin@Sun.COM 		 * tx rate state of each node.
19569999SWang.Lin@Sun.COM 		 */
1957*11729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
19589999SWang.Lin@Sun.COM 		ieee80211_iterate_nodes(&ic->ic_sta, arn_rate_cb, sc);
1959*11729SWang.Lin@Sun.COM #endif
19609999SWang.Lin@Sun.COM 	}
19619999SWang.Lin@Sun.COM }
19629999SWang.Lin@Sun.COM 
1963*11729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
19649999SWang.Lin@Sun.COM /*
19659999SWang.Lin@Sun.COM  * Examine and potentially adjust the transmit rate.
19669999SWang.Lin@Sun.COM  */
19679999SWang.Lin@Sun.COM void
arn_rate_ctl(void * arg,struct ieee80211_node * in)19689999SWang.Lin@Sun.COM arn_rate_ctl(void *arg, struct ieee80211_node *in)
19699999SWang.Lin@Sun.COM {
19709999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
19719999SWang.Lin@Sun.COM 	struct ath_node *an = ATH_NODE(in);
19729999SWang.Lin@Sun.COM 	struct ieee80211_rateset *rs = &in->in_rates;
19739999SWang.Lin@Sun.COM 	int32_t mod = 0, nrate, enough;
19749999SWang.Lin@Sun.COM 
19759999SWang.Lin@Sun.COM 	/*
19769999SWang.Lin@Sun.COM 	 * Rate control(very primitive version).
19779999SWang.Lin@Sun.COM 	 */
19789999SWang.Lin@Sun.COM 	sc->sc_stats.ast_rate_calls++;
19799999SWang.Lin@Sun.COM 
19809999SWang.Lin@Sun.COM 	enough = (an->an_tx_ok + an->an_tx_err >= 10);
19819999SWang.Lin@Sun.COM 
19829999SWang.Lin@Sun.COM 	/* no packet reached -> down */
19839999SWang.Lin@Sun.COM 	if (an->an_tx_err > 0 && an->an_tx_ok == 0)
19849999SWang.Lin@Sun.COM 		mod = -1;
19859999SWang.Lin@Sun.COM 
19869999SWang.Lin@Sun.COM 	/* all packets needs retry in average -> down */
19879999SWang.Lin@Sun.COM 	if (enough && an->an_tx_ok < an->an_tx_retr)
19889999SWang.Lin@Sun.COM 		mod = -1;
19899999SWang.Lin@Sun.COM 
19909999SWang.Lin@Sun.COM 	/* no error and less than 10% of packets needs retry -> up */
19919999SWang.Lin@Sun.COM 	if (enough && an->an_tx_err == 0 && an->an_tx_ok > an->an_tx_retr * 10)
19929999SWang.Lin@Sun.COM 		mod = 1;
19939999SWang.Lin@Sun.COM 
19949999SWang.Lin@Sun.COM 	nrate = in->in_txrate;
19959999SWang.Lin@Sun.COM 	switch (mod) {
19969999SWang.Lin@Sun.COM 	case 0:
19979999SWang.Lin@Sun.COM 		if (enough && an->an_tx_upper > 0)
19989999SWang.Lin@Sun.COM 			an->an_tx_upper--;
19999999SWang.Lin@Sun.COM 		break;
20009999SWang.Lin@Sun.COM 	case -1:
20019999SWang.Lin@Sun.COM 		if (nrate > 0) {
20029999SWang.Lin@Sun.COM 			nrate--;
20039999SWang.Lin@Sun.COM 			sc->sc_stats.ast_rate_drop++;
20049999SWang.Lin@Sun.COM 		}
20059999SWang.Lin@Sun.COM 		an->an_tx_upper = 0;
20069999SWang.Lin@Sun.COM 		break;
20079999SWang.Lin@Sun.COM 	case 1:
20089999SWang.Lin@Sun.COM 		if (++an->an_tx_upper < 10)
20099999SWang.Lin@Sun.COM 			break;
20109999SWang.Lin@Sun.COM 		an->an_tx_upper = 0;
20119999SWang.Lin@Sun.COM 		if (nrate + 1 < rs->ir_nrates) {
20129999SWang.Lin@Sun.COM 			nrate++;
20139999SWang.Lin@Sun.COM 			sc->sc_stats.ast_rate_raise++;
20149999SWang.Lin@Sun.COM 		}
20159999SWang.Lin@Sun.COM 		break;
20169999SWang.Lin@Sun.COM 	}
20179999SWang.Lin@Sun.COM 
20189999SWang.Lin@Sun.COM 	if (nrate != in->in_txrate) {
20199999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_ctl(): %dM -> %dM "
20209999SWang.Lin@Sun.COM 		    "(%d ok, %d err, %d retr)\n",
20219999SWang.Lin@Sun.COM 		    (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2,
20229999SWang.Lin@Sun.COM 		    (rs->ir_rates[nrate] & IEEE80211_RATE_VAL) / 2,
20239999SWang.Lin@Sun.COM 		    an->an_tx_ok, an->an_tx_err, an->an_tx_retr));
20249999SWang.Lin@Sun.COM 		arn_rate_update(sc, in, nrate);
20259999SWang.Lin@Sun.COM 	} else if (enough)
20269999SWang.Lin@Sun.COM 		an->an_tx_ok = an->an_tx_err = an->an_tx_retr = 0;
20279999SWang.Lin@Sun.COM }
2028*11729SWang.Lin@Sun.COM #endif /* ARN_LEGACY_RC */
2029