blob: 61a2a4eb7140db39d2fa8d01f8735e27f75bb8fc [file] [log] [blame]
/*
*************************************************************************
* Ralink Tech Inc.
* 5F., No.36, Taiyuan St., Jhubei City,
* Hsinchu County 302,
* Taiwan, R.O.C.
*
* (c) Copyright 2002-2007, Ralink Technology, Inc.
*
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
*************************************************************************
Module Name:
mlme.c
Abstract:
Revision History:
Who When What
-------- ---------- ----------------------------------------------
John Chang 2004-08-25 Modify from RT2500 code base
John Chang 2004-09-06 modified for RT2600
*/
#include "../rt_config.h"
#include <stdarg.h>
UCHAR CISCO_OUI[] = {0x00, 0x40, 0x96};
UCHAR WPA_OUI[] = {0x00, 0x50, 0xf2, 0x01};
UCHAR RSN_OUI[] = {0x00, 0x0f, 0xac};
UCHAR WME_INFO_ELEM[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01};
UCHAR WME_PARM_ELEM[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01};
UCHAR Ccx2QosInfo[] = {0x00, 0x40, 0x96, 0x04};
UCHAR RALINK_OUI[] = {0x00, 0x0c, 0x43};
UCHAR BROADCOM_OUI[] = {0x00, 0x90, 0x4c};
UCHAR WPS_OUI[] = {0x00, 0x50, 0xf2, 0x04};
UCHAR PRE_N_HT_OUI[] = {0x00, 0x90, 0x4c};
UCHAR RateSwitchTable[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x11, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x00, 0, 40, 101,
0x01, 0x00, 1, 40, 50,
0x02, 0x00, 2, 35, 45,
0x03, 0x00, 3, 20, 45,
0x04, 0x21, 0, 30, 50,
0x05, 0x21, 1, 20, 50,
0x06, 0x21, 2, 20, 50,
0x07, 0x21, 3, 15, 50,
0x08, 0x21, 4, 15, 30,
0x09, 0x21, 5, 10, 25,
0x0a, 0x21, 6, 8, 25,
0x0b, 0x21, 7, 8, 25,
0x0c, 0x20, 12, 15, 30,
0x0d, 0x20, 13, 8, 20,
0x0e, 0x20, 14, 8, 20,
0x0f, 0x20, 15, 8, 25,
0x10, 0x22, 15, 8, 25,
0x11, 0x00, 0, 0, 0,
0x12, 0x00, 0, 0, 0,
0x13, 0x00, 0, 0, 0,
0x14, 0x00, 0, 0, 0,
0x15, 0x00, 0, 0, 0,
0x16, 0x00, 0, 0, 0,
0x17, 0x00, 0, 0, 0,
0x18, 0x00, 0, 0, 0,
0x19, 0x00, 0, 0, 0,
0x1a, 0x00, 0, 0, 0,
0x1b, 0x00, 0, 0, 0,
0x1c, 0x00, 0, 0, 0,
0x1d, 0x00, 0, 0, 0,
0x1e, 0x00, 0, 0, 0,
0x1f, 0x00, 0, 0, 0,
};
UCHAR RateSwitchTable11B[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x04, 0x03, 0, 0, 0, // Initial used item after association
0x00, 0x00, 0, 40, 101,
0x01, 0x00, 1, 40, 50,
0x02, 0x00, 2, 35, 45,
0x03, 0x00, 3, 20, 45,
};
UCHAR RateSwitchTable11BG[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0a, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x00, 0, 40, 101,
0x01, 0x00, 1, 40, 50,
0x02, 0x00, 2, 35, 45,
0x03, 0x00, 3, 20, 45,
0x04, 0x10, 2, 20, 35,
0x05, 0x10, 3, 16, 35,
0x06, 0x10, 4, 10, 25,
0x07, 0x10, 5, 16, 25,
0x08, 0x10, 6, 10, 25,
0x09, 0x10, 7, 10, 13,
};
UCHAR RateSwitchTable11G[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x08, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x10, 0, 20, 101,
0x01, 0x10, 1, 20, 35,
0x02, 0x10, 2, 20, 35,
0x03, 0x10, 3, 16, 35,
0x04, 0x10, 4, 10, 25,
0x05, 0x10, 5, 16, 25,
0x06, 0x10, 6, 10, 25,
0x07, 0x10, 7, 10, 13,
};
UCHAR RateSwitchTable11N1S[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x09, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30, 101,
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x21, 5, 10, 25,
0x06, 0x21, 6, 8, 14,
0x07, 0x21, 7, 8, 14,
0x08, 0x23, 7, 8, 14,
};
UCHAR RateSwitchTable11N2S[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0a, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30, 101,
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x20, 12, 15, 30,
0x06, 0x20, 13, 8, 20,
0x07, 0x20, 14, 8, 20,
0x08, 0x20, 15, 8, 25,
0x09, 0x22, 15, 8, 25,
};
UCHAR RateSwitchTable11N3S[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0a, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30, 101,
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x20, 12, 15, 30,
0x06, 0x20, 13, 8, 20,
0x07, 0x20, 14, 8, 20,
0x08, 0x20, 15, 8, 25,
0x09, 0x22, 15, 8, 25,
};
UCHAR RateSwitchTable11N2SForABand[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0b, 0x09, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30, 101,
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x21, 5, 15, 30,
0x06, 0x20, 12, 15, 30,
0x07, 0x20, 13, 8, 20,
0x08, 0x20, 14, 8, 20,
0x09, 0x20, 15, 8, 25,
0x0a, 0x22, 15, 8, 25,
};
UCHAR RateSwitchTable11N3SForABand[] = { // 3*3
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0b, 0x09, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30, 101,
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x21, 5, 15, 30,
0x06, 0x20, 12, 15, 30,
0x07, 0x20, 13, 8, 20,
0x08, 0x20, 14, 8, 20,
0x09, 0x20, 15, 8, 25,
0x0a, 0x22, 15, 8, 25,
};
UCHAR RateSwitchTable11BGN1S[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0d, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x00, 0, 40, 101,
0x01, 0x00, 1, 40, 50,
0x02, 0x00, 2, 35, 45,
0x03, 0x00, 3, 20, 45,
0x04, 0x21, 0, 30,101, //50
0x05, 0x21, 1, 20, 50,
0x06, 0x21, 2, 20, 50,
0x07, 0x21, 3, 15, 50,
0x08, 0x21, 4, 15, 30,
0x09, 0x21, 5, 10, 25,
0x0a, 0x21, 6, 8, 14,
0x0b, 0x21, 7, 8, 14,
0x0c, 0x23, 7, 8, 14,
};
UCHAR RateSwitchTable11BGN2S[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0a, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30,101, //50
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x20, 12, 15, 30,
0x06, 0x20, 13, 8, 20,
0x07, 0x20, 14, 8, 20,
0x08, 0x20, 15, 8, 25,
0x09, 0x22, 15, 8, 25,
};
UCHAR RateSwitchTable11BGN3S[] = { // 3*3
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0a, 0x00, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30,101, //50
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 20, 50,
0x04, 0x21, 4, 15, 50,
0x05, 0x20, 20, 15, 30,
0x06, 0x20, 21, 8, 20,
0x07, 0x20, 22, 8, 20,
0x08, 0x20, 23, 8, 25,
0x09, 0x22, 23, 8, 25,
};
UCHAR RateSwitchTable11BGN2SForABand[] = {
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0b, 0x09, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30,101, //50
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x21, 5, 15, 30,
0x06, 0x20, 12, 15, 30,
0x07, 0x20, 13, 8, 20,
0x08, 0x20, 14, 8, 20,
0x09, 0x20, 15, 8, 25,
0x0a, 0x22, 15, 8, 25,
};
UCHAR RateSwitchTable11BGN3SForABand[] = { // 3*3
// Item No. Mode Curr-MCS TrainUp TrainDown // Mode- Bit0: STBC, Bit1: Short GI, Bit4,5: Mode(0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF)
0x0c, 0x09, 0, 0, 0, // Initial used item after association
0x00, 0x21, 0, 30,101, //50
0x01, 0x21, 1, 20, 50,
0x02, 0x21, 2, 20, 50,
0x03, 0x21, 3, 15, 50,
0x04, 0x21, 4, 15, 30,
0x05, 0x21, 5, 15, 30,
0x06, 0x21, 12, 15, 30,
0x07, 0x20, 20, 15, 30,
0x08, 0x20, 21, 8, 20,
0x09, 0x20, 22, 8, 20,
0x0a, 0x20, 23, 8, 25,
0x0b, 0x22, 23, 8, 25,
};
PUCHAR ReasonString[] = {
/* 0 */ "Reserved",
/* 1 */ "Unspecified Reason",
/* 2 */ "Previous Auth no longer valid",
/* 3 */ "STA is leaving / has left",
/* 4 */ "DIS-ASSOC due to inactivity",
/* 5 */ "AP unable to hanle all associations",
/* 6 */ "class 2 error",
/* 7 */ "class 3 error",
/* 8 */ "STA is leaving / has left",
/* 9 */ "require auth before assoc/re-assoc",
/* 10 */ "Reserved",
/* 11 */ "Reserved",
/* 12 */ "Reserved",
/* 13 */ "invalid IE",
/* 14 */ "MIC error",
/* 15 */ "4-way handshake timeout",
/* 16 */ "2-way (group key) handshake timeout",
/* 17 */ "4-way handshake IE diff among AssosReq/Rsp/Beacon",
/* 18 */
};
extern UCHAR OfdmRateToRxwiMCS[];
// since RT61 has better RX sensibility, we have to limit TX ACK rate not to exceed our normal data TX rate.
// otherwise the WLAN peer may not be able to receive the ACK thus downgrade its data TX rate
ULONG BasicRateMask[12] = {0xfffff001 /* 1-Mbps */, 0xfffff003 /* 2 Mbps */, 0xfffff007 /* 5.5 */, 0xfffff00f /* 11 */,
0xfffff01f /* 6 */ , 0xfffff03f /* 9 */ , 0xfffff07f /* 12 */ , 0xfffff0ff /* 18 */,
0xfffff1ff /* 24 */ , 0xfffff3ff /* 36 */ , 0xfffff7ff /* 48 */ , 0xffffffff /* 54 */};
UCHAR MULTICAST_ADDR[MAC_ADDR_LEN] = {0x1, 0x00, 0x00, 0x00, 0x00, 0x00};
UCHAR BROADCAST_ADDR[MAC_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
UCHAR ZERO_MAC_ADDR[MAC_ADDR_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// e.g. RssiSafeLevelForTxRate[RATE_36]" means if the current RSSI is greater than
// this value, then it's quaranteed capable of operating in 36 mbps TX rate in
// clean environment.
// TxRate: 1 2 5.5 11 6 9 12 18 24 36 48 54 72 100
CHAR RssiSafeLevelForTxRate[] ={ -92, -91, -90, -87, -88, -86, -85, -83, -81, -78, -72, -71, -40, -40 };
UCHAR RateIdToMbps[] = { 1, 2, 5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 72, 100};
USHORT RateIdTo500Kbps[] = { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108, 144, 200};
UCHAR SsidIe = IE_SSID;
UCHAR SupRateIe = IE_SUPP_RATES;
UCHAR ExtRateIe = IE_EXT_SUPP_RATES;
UCHAR HtCapIe = IE_HT_CAP;
UCHAR AddHtInfoIe = IE_ADD_HT;
UCHAR NewExtChanIe = IE_SECONDARY_CH_OFFSET;
UCHAR ErpIe = IE_ERP;
UCHAR DsIe = IE_DS_PARM;
UCHAR TimIe = IE_TIM;
UCHAR WpaIe = IE_WPA;
UCHAR Wpa2Ie = IE_WPA2;
UCHAR IbssIe = IE_IBSS_PARM;
UCHAR Ccx2Ie = IE_CCX_V2;
extern UCHAR WPA_OUI[];
UCHAR SES_OUI[] = {0x00, 0x90, 0x4c};
UCHAR ZeroSsid[32] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
// Reset the RFIC setting to new series
RTMP_RF_REGS RF2850RegTable[] = {
// ch R1 R2 R3(TX0~4=0) R4
{1, 0x98402ecc, 0x984c0786, 0x9816b455, 0x9800510b},
{2, 0x98402ecc, 0x984c0786, 0x98168a55, 0x9800519f},
{3, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800518b},
{4, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800519f},
{5, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800518b},
{6, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800519f},
{7, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800518b},
{8, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800519f},
{9, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800518b},
{10, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800519f},
{11, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800518b},
{12, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800519f},
{13, 0x98402ecc, 0x984c079e, 0x98168a55, 0x9800518b},
{14, 0x98402ecc, 0x984c07a2, 0x98168a55, 0x98005193},
// 802.11 UNI / HyperLan 2
{36, 0x98402ecc, 0x984c099a, 0x98158a55, 0x980ed1a3},
{38, 0x98402ecc, 0x984c099e, 0x98158a55, 0x980ed193},
{40, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed183},
{44, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed1a3},
{46, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed18b},
{48, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed19b},
{52, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed193},
{54, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed1a3},
{56, 0x98402ec8, 0x984c068e, 0x98158a55, 0x980ed18b},
{60, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed183},
{62, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed193},
{64, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed1a3}, // Plugfest#4, Day4, change RFR3 left4th 9->5.
// 802.11 HyperLan 2
{100, 0x98402ec8, 0x984c06b2, 0x98178a55, 0x980ed783},
// 2008.04.30 modified
// The system team has AN to improve the EVM value
// for channel 102 to 108 for the RT2850/RT2750 dual band solution.
{102, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed793},
{104, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed1a3},
{108, 0x98402ecc, 0x985c0a32, 0x98578a55, 0x980ed193},
{110, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed183},
{112, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed19b},
{116, 0x98402ecc, 0x984c0a3a, 0x98178a55, 0x980ed1a3},
{118, 0x98402ecc, 0x984c0a3e, 0x98178a55, 0x980ed193},
{120, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed183},
{124, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed193},
{126, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed15b}, // 0x980ed1bb->0x980ed15b required by Rory 20070927
{128, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed1a3},
{132, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed18b},
{134, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed193},
{136, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed19b},
{140, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed183},
// 802.11 UNII
{149, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed1a7},
{151, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed187},
{153, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed18f},
{157, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed19f},
{159, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed1a7},
{161, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed187},
{165, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed197},
// Japan
{184, 0x95002ccc, 0x9500491e, 0x9509be55, 0x950c0a0b},
{188, 0x95002ccc, 0x95004922, 0x9509be55, 0x950c0a13},
{192, 0x95002ccc, 0x95004926, 0x9509be55, 0x950c0a1b},
{196, 0x95002ccc, 0x9500492a, 0x9509be55, 0x950c0a23},
{208, 0x95002ccc, 0x9500493a, 0x9509be55, 0x950c0a13},
{212, 0x95002ccc, 0x9500493e, 0x9509be55, 0x950c0a1b},
{216, 0x95002ccc, 0x95004982, 0x9509be55, 0x950c0a23},
// still lack of MMAC(Japan) ch 34,38,42,46
};
UCHAR NUM_OF_2850_CHNL = (sizeof(RF2850RegTable) / sizeof(RTMP_RF_REGS));
FREQUENCY_ITEM FreqItems3020[] =
{
/**************************************************/
// ISM : 2.4 to 2.483 GHz //
/**************************************************/
// 11g
/**************************************************/
//-CH---N-------R---K-----------
{1, 241, 2, 2},
{2, 241, 2, 7},
{3, 242, 2, 2},
{4, 242, 2, 7},
{5, 243, 2, 2},
{6, 243, 2, 7},
{7, 244, 2, 2},
{8, 244, 2, 7},
{9, 245, 2, 2},
{10, 245, 2, 7},
{11, 246, 2, 2},
{12, 246, 2, 7},
{13, 247, 2, 2},
{14, 248, 2, 4},
};
UCHAR NUM_OF_3020_CHNL=(sizeof(FreqItems3020) / sizeof(FREQUENCY_ITEM));
/*
==========================================================================
Description:
initialize the MLME task and its data structure (queue, spinlock,
timer, state machines).
IRQL = PASSIVE_LEVEL
Return:
always return NDIS_STATUS_SUCCESS
==========================================================================
*/
NDIS_STATUS MlmeInit(
IN PRTMP_ADAPTER pAd)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
DBGPRINT(RT_DEBUG_TRACE, ("--> MLME Initialize\n"));
do
{
Status = MlmeQueueInit(&pAd->Mlme.Queue);
if(Status != NDIS_STATUS_SUCCESS)
break;
pAd->Mlme.bRunning = FALSE;
NdisAllocateSpinLock(&pAd->Mlme.TaskLock);
{
BssTableInit(&pAd->ScanTab);
// init STA state machines
AssocStateMachineInit(pAd, &pAd->Mlme.AssocMachine, pAd->Mlme.AssocFunc);
AuthStateMachineInit(pAd, &pAd->Mlme.AuthMachine, pAd->Mlme.AuthFunc);
AuthRspStateMachineInit(pAd, &pAd->Mlme.AuthRspMachine, pAd->Mlme.AuthRspFunc);
SyncStateMachineInit(pAd, &pAd->Mlme.SyncMachine, pAd->Mlme.SyncFunc);
WpaPskStateMachineInit(pAd, &pAd->Mlme.WpaPskMachine, pAd->Mlme.WpaPskFunc);
AironetStateMachineInit(pAd, &pAd->Mlme.AironetMachine, pAd->Mlme.AironetFunc);
// Since we are using switch/case to implement it, the init is different from the above
// state machine init
MlmeCntlInit(pAd, &pAd->Mlme.CntlMachine, NULL);
}
ActionStateMachineInit(pAd, &pAd->Mlme.ActMachine, pAd->Mlme.ActFunc);
// Init mlme periodic timer
RTMPInitTimer(pAd, &pAd->Mlme.PeriodicTimer, GET_TIMER_FUNCTION(MlmePeriodicExec), pAd, TRUE);
// Set mlme periodic timer
RTMPSetTimer(&pAd->Mlme.PeriodicTimer, MLME_TASK_EXEC_INTV);
// software-based RX Antenna diversity
RTMPInitTimer(pAd, &pAd->Mlme.RxAntEvalTimer, GET_TIMER_FUNCTION(AsicRxAntEvalTimeout), pAd, FALSE);
#ifdef RT2860
{
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
// only PCIe cards need these two timers
RTMPInitTimer(pAd, &pAd->Mlme.PsPollTimer, GET_TIMER_FUNCTION(PsPollWakeExec), pAd, FALSE);
RTMPInitTimer(pAd, &pAd->Mlme.RadioOnOffTimer, GET_TIMER_FUNCTION(RadioOnExec), pAd, FALSE);
}
}
#endif
} while (FALSE);
DBGPRINT(RT_DEBUG_TRACE, ("<-- MLME Initialize\n"));
return Status;
}
/*
==========================================================================
Description:
main loop of the MLME
Pre:
Mlme has to be initialized, and there are something inside the queue
Note:
This function is invoked from MPSetInformation and MPReceive;
This task guarantee only one MlmeHandler will run.
IRQL = DISPATCH_LEVEL
==========================================================================
*/
VOID MlmeHandler(
IN PRTMP_ADAPTER pAd)
{
MLME_QUEUE_ELEM *Elem = NULL;
// Only accept MLME and Frame from peer side, no other (control/data) frame should
// get into this state machine
NdisAcquireSpinLock(&pAd->Mlme.TaskLock);
if(pAd->Mlme.bRunning)
{
NdisReleaseSpinLock(&pAd->Mlme.TaskLock);
return;
}
else
{
pAd->Mlme.bRunning = TRUE;
}
NdisReleaseSpinLock(&pAd->Mlme.TaskLock);
while (!MlmeQueueEmpty(&pAd->Mlme.Queue))
{
if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_MLME_RESET_IN_PROGRESS) ||
RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS) ||
RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_NIC_NOT_EXIST))
{
DBGPRINT(RT_DEBUG_TRACE, ("Device Halted or Removed or MlmeRest, exit MlmeHandler! (queue num = %ld)\n", pAd->Mlme.Queue.Num));
break;
}
//From message type, determine which state machine I should drive
if (MlmeDequeue(&pAd->Mlme.Queue, &Elem))
{
#ifdef RT2870
if (Elem->MsgType == MT2_RESET_CONF)
{
DBGPRINT_RAW(RT_DEBUG_TRACE, ("!!! reset MLME state machine !!!\n"));
MlmeRestartStateMachine(pAd);
Elem->Occupied = FALSE;
Elem->MsgLen = 0;
continue;
}
#endif // RT2870 //
// if dequeue success
switch (Elem->Machine)
{
// STA state machines
case ASSOC_STATE_MACHINE:
StateMachinePerformAction(pAd, &pAd->Mlme.AssocMachine, Elem);
break;
case AUTH_STATE_MACHINE:
StateMachinePerformAction(pAd, &pAd->Mlme.AuthMachine, Elem);
break;
case AUTH_RSP_STATE_MACHINE:
StateMachinePerformAction(pAd, &pAd->Mlme.AuthRspMachine, Elem);
break;
case SYNC_STATE_MACHINE:
StateMachinePerformAction(pAd, &pAd->Mlme.SyncMachine, Elem);
break;
case MLME_CNTL_STATE_MACHINE:
MlmeCntlMachinePerformAction(pAd, &pAd->Mlme.CntlMachine, Elem);
break;
case WPA_PSK_STATE_MACHINE:
StateMachinePerformAction(pAd, &pAd->Mlme.WpaPskMachine, Elem);
break;
case AIRONET_STATE_MACHINE:
StateMachinePerformAction(pAd, &pAd->Mlme.AironetMachine, Elem);
break;
case ACTION_STATE_MACHINE:
StateMachinePerformAction(pAd, &pAd->Mlme.ActMachine, Elem);
break;
default:
DBGPRINT(RT_DEBUG_TRACE, ("ERROR: Illegal machine %ld in MlmeHandler()\n", Elem->Machine));
break;
} // end of switch
// free MLME element
Elem->Occupied = FALSE;
Elem->MsgLen = 0;
}
else {
DBGPRINT_ERR(("MlmeHandler: MlmeQueue empty\n"));
}
}
NdisAcquireSpinLock(&pAd->Mlme.TaskLock);
pAd->Mlme.bRunning = FALSE;
NdisReleaseSpinLock(&pAd->Mlme.TaskLock);
}
/*
==========================================================================
Description:
Destructor of MLME (Destroy queue, state machine, spin lock and timer)
Parameters:
Adapter - NIC Adapter pointer
Post:
The MLME task will no longer work properly
IRQL = PASSIVE_LEVEL
==========================================================================
*/
VOID MlmeHalt(
IN PRTMP_ADAPTER pAd)
{
BOOLEAN Cancelled;
#ifdef RT3070
UINT32 TxPinCfg = 0x00050F0F;
#endif // RT3070 //
DBGPRINT(RT_DEBUG_TRACE, ("==> MlmeHalt\n"));
if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_NIC_NOT_EXIST))
{
// disable BEACON generation and other BEACON related hardware timers
AsicDisableSync(pAd);
}
{
// Cancel pending timers
RTMPCancelTimer(&pAd->MlmeAux.AssocTimer, &Cancelled);
RTMPCancelTimer(&pAd->MlmeAux.ReassocTimer, &Cancelled);
RTMPCancelTimer(&pAd->MlmeAux.DisassocTimer, &Cancelled);
RTMPCancelTimer(&pAd->MlmeAux.AuthTimer, &Cancelled);
RTMPCancelTimer(&pAd->MlmeAux.BeaconTimer, &Cancelled);
RTMPCancelTimer(&pAd->MlmeAux.ScanTimer, &Cancelled);
#ifdef RT2860
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
RTMPCancelTimer(&pAd->Mlme.RadioOnOffTimer, &Cancelled);
}
#endif
}
RTMPCancelTimer(&pAd->Mlme.PeriodicTimer, &Cancelled);
RTMPCancelTimer(&pAd->Mlme.RxAntEvalTimer, &Cancelled);
if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_NIC_NOT_EXIST))
{
// Set LED
RTMPSetLED(pAd, LED_HALT);
RTMPSetSignalLED(pAd, -100); // Force signal strength Led to be turned off, firmware is not done it.
#ifdef RT2870
{
LED_CFG_STRUC LedCfg;
RTMP_IO_READ32(pAd, LED_CFG, &LedCfg.word);
LedCfg.field.LedPolar = 0;
LedCfg.field.RLedMode = 0;
LedCfg.field.GLedMode = 0;
LedCfg.field.YLedMode = 0;
RTMP_IO_WRITE32(pAd, LED_CFG, LedCfg.word);
}
#endif // RT2870 //
#ifdef RT3070
//
// Turn off LNA_PE
//
if (IS_RT3070(pAd) || IS_RT3071(pAd))
{
TxPinCfg &= 0xFFFFF0F0;
RTUSBWriteMACRegister(pAd, TX_PIN_CFG, TxPinCfg);
}
#endif // RT3070 //
}
RTMPusecDelay(5000); // 5 msec to gurantee Ant Diversity timer canceled
MlmeQueueDestroy(&pAd->Mlme.Queue);
NdisFreeSpinLock(&pAd->Mlme.TaskLock);
DBGPRINT(RT_DEBUG_TRACE, ("<== MlmeHalt\n"));
}
VOID MlmeResetRalinkCounters(
IN PRTMP_ADAPTER pAd)
{
pAd->RalinkCounters.LastOneSecRxOkDataCnt = pAd->RalinkCounters.OneSecRxOkDataCnt;
// clear all OneSecxxx counters.
pAd->RalinkCounters.OneSecBeaconSentCnt = 0;
pAd->RalinkCounters.OneSecFalseCCACnt = 0;
pAd->RalinkCounters.OneSecRxFcsErrCnt = 0;
pAd->RalinkCounters.OneSecRxOkCnt = 0;
pAd->RalinkCounters.OneSecTxFailCount = 0;
pAd->RalinkCounters.OneSecTxNoRetryOkCount = 0;
pAd->RalinkCounters.OneSecTxRetryOkCount = 0;
pAd->RalinkCounters.OneSecRxOkDataCnt = 0;
// TODO: for debug only. to be removed
pAd->RalinkCounters.OneSecOsTxCount[QID_AC_BE] = 0;
pAd->RalinkCounters.OneSecOsTxCount[QID_AC_BK] = 0;
pAd->RalinkCounters.OneSecOsTxCount[QID_AC_VI] = 0;
pAd->RalinkCounters.OneSecOsTxCount[QID_AC_VO] = 0;
pAd->RalinkCounters.OneSecDmaDoneCount[QID_AC_BE] = 0;
pAd->RalinkCounters.OneSecDmaDoneCount[QID_AC_BK] = 0;
pAd->RalinkCounters.OneSecDmaDoneCount[QID_AC_VI] = 0;
pAd->RalinkCounters.OneSecDmaDoneCount[QID_AC_VO] = 0;
pAd->RalinkCounters.OneSecTxDoneCount = 0;
pAd->RalinkCounters.OneSecRxCount = 0;
pAd->RalinkCounters.OneSecTxAggregationCount = 0;
pAd->RalinkCounters.OneSecRxAggregationCount = 0;
return;
}
unsigned long rx_AMSDU;
unsigned long rx_Total;
/*
==========================================================================
Description:
This routine is executed periodically to -
1. Decide if it's a right time to turn on PwrMgmt bit of all
outgoiing frames
2. Calculate ChannelQuality based on statistics of the last
period, so that TX rate won't toggling very frequently between a
successful TX and a failed TX.
3. If the calculated ChannelQuality indicated current connection not
healthy, then a ROAMing attempt is tried here.
IRQL = DISPATCH_LEVEL
==========================================================================
*/
#define ADHOC_BEACON_LOST_TIME (8*OS_HZ) // 8 sec
VOID MlmePeriodicExec(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3)
{
ULONG TxTotalCnt;
PRTMP_ADAPTER pAd = (RTMP_ADAPTER *)FunctionContext;
#ifdef RT2860
//Baron 2008/07/10
//printk("Baron_Test:\t%s", RTMPGetRalinkEncryModeStr(pAd->StaCfg.WepStatus));
//If the STA security setting is OPEN or WEP, pAd->StaCfg.WpaSupplicantUP = 0.
//If the STA security setting is WPAPSK or WPA2PSK, pAd->StaCfg.WpaSupplicantUP = 1.
if(pAd->StaCfg.WepStatus<2)
{
pAd->StaCfg.WpaSupplicantUP = 0;
}
else
{
pAd->StaCfg.WpaSupplicantUP = 1;
}
{
// If Hardware controlled Radio enabled, we have to check GPIO pin2 every 2 second.
// Move code to here, because following code will return when radio is off
if ((pAd->Mlme.PeriodicRound % (MLME_TASK_EXEC_MULTIPLE * 2) == 0) &&
(pAd->StaCfg.bHardwareRadio == TRUE) &&
(RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_START_UP)) &&
(!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_NIC_NOT_EXIST)) &&
(!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS)))
{
UINT32 data = 0;
// Read GPIO pin2 as Hardware controlled radio state
RTMP_IO_FORCE_READ32(pAd, GPIO_CTRL_CFG, &data);
if (data & 0x04)
{
pAd->StaCfg.bHwRadio = TRUE;
}
else
{
pAd->StaCfg.bHwRadio = FALSE;
}
if (pAd->StaCfg.bRadio != (pAd->StaCfg.bHwRadio && pAd->StaCfg.bSwRadio))
{
pAd->StaCfg.bRadio = (pAd->StaCfg.bHwRadio && pAd->StaCfg.bSwRadio);
if (pAd->StaCfg.bRadio == TRUE)
{
MlmeRadioOn(pAd);
// Update extra information
pAd->ExtraInfo = EXTRA_INFO_CLEAR;
}
else
{
MlmeRadioOff(pAd);
// Update extra information
pAd->ExtraInfo = HW_RADIO_OFF;
}
}
}
}
#endif /* RT2860 */
// Do nothing if the driver is starting halt state.
// This might happen when timer already been fired before cancel timer with mlmehalt
if ((RTMP_TEST_FLAG(pAd, (fRTMP_ADAPTER_HALT_IN_PROGRESS |
fRTMP_ADAPTER_RADIO_OFF |
fRTMP_ADAPTER_RADIO_MEASUREMENT |
fRTMP_ADAPTER_RESET_IN_PROGRESS))))
return;
#ifdef RT2860
{
if ((pAd->RalinkCounters.LastReceivedByteCount == pAd->RalinkCounters.ReceivedByteCount) && (pAd->StaCfg.bRadio == TRUE))
{
// If ReceiveByteCount doesn't change, increase SameRxByteCount by 1.
pAd->SameRxByteCount++;
}
else
pAd->SameRxByteCount = 0;
// If after BBP, still not work...need to check to reset PBF&MAC.
if (pAd->SameRxByteCount == 702)
{
pAd->SameRxByteCount = 0;
AsicResetPBF(pAd);
AsicResetMAC(pAd);
}
// If SameRxByteCount keeps happens for 2 second in infra mode, or for 60 seconds in idle mode.
if (((INFRA_ON(pAd)) && (pAd->SameRxByteCount > 20)) || ((IDLE_ON(pAd)) && (pAd->SameRxByteCount > 600)))
{
if ((pAd->StaCfg.bRadio == TRUE) && (pAd->SameRxByteCount < 700))
{
DBGPRINT(RT_DEBUG_TRACE, ("---> SameRxByteCount = %lu !!!!!!!!!!!!!!! \n", pAd->SameRxByteCount));
pAd->SameRxByteCount = 700;
AsicResetBBP(pAd);
}
}
// Update lastReceiveByteCount.
pAd->RalinkCounters.LastReceivedByteCount = pAd->RalinkCounters.ReceivedByteCount;
if ((pAd->CheckDmaBusyCount > 3) && (IDLE_ON(pAd)))
{
pAd->CheckDmaBusyCount = 0;
AsicResetFromDMABusy(pAd);
}
}
#endif /* RT2860 */
RT28XX_MLME_PRE_SANITY_CHECK(pAd);
{
// Do nothing if monitor mode is on
if (MONITOR_ON(pAd))
return;
if (pAd->Mlme.PeriodicRound & 0x1)
{
// This is the fix for wifi 11n extension channel overlapping test case. for 2860D
if (((pAd->MACVersion & 0xffff) == 0x0101) &&
(STA_TGN_WIFI_ON(pAd)) &&
(pAd->CommonCfg.IOTestParm.bToggle == FALSE))
{
RTMP_IO_WRITE32(pAd, TXOP_CTRL_CFG, 0x24Bf);
pAd->CommonCfg.IOTestParm.bToggle = TRUE;
}
else if ((STA_TGN_WIFI_ON(pAd)) &&
((pAd->MACVersion & 0xffff) == 0x0101))
{
RTMP_IO_WRITE32(pAd, TXOP_CTRL_CFG, 0x243f);
pAd->CommonCfg.IOTestParm.bToggle = FALSE;
}
}
}
pAd->bUpdateBcnCntDone = FALSE;
// RECBATimerTimeout(SystemSpecific1,FunctionContext,SystemSpecific2,SystemSpecific3);
pAd->Mlme.PeriodicRound ++;
#ifdef RT3070
// execute every 100ms, update the Tx FIFO Cnt for update Tx Rate.
NICUpdateFifoStaCounters(pAd);
#endif // RT3070 //
// execute every 500ms
if ((pAd->Mlme.PeriodicRound % 5 == 0) && RTMPAutoRateSwitchCheck(pAd)/*(OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_TX_RATE_SWITCH_ENABLED))*/)
{
// perform dynamic tx rate switching based on past TX history
{
if ((OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_MEDIA_STATE_CONNECTED)
)
&& (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_DOZE)))
MlmeDynamicTxRateSwitching(pAd);
}
}
// Normal 1 second Mlme PeriodicExec.
if (pAd->Mlme.PeriodicRound %MLME_TASK_EXEC_MULTIPLE == 0)
{
pAd->Mlme.OneSecPeriodicRound ++;
if (rx_Total)
{
// reset counters
rx_AMSDU = 0;
rx_Total = 0;
}
// Media status changed, report to NDIS
if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_MEDIA_STATE_CHANGE))
{
RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_MEDIA_STATE_CHANGE);
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_MEDIA_STATE_CONNECTED))
{
pAd->IndicateMediaState = NdisMediaStateConnected;
RTMP_IndicateMediaState(pAd);
}
else
{
pAd->IndicateMediaState = NdisMediaStateDisconnected;
RTMP_IndicateMediaState(pAd);
}
}
NdisGetSystemUpTime(&pAd->Mlme.Now32);
// add the most up-to-date h/w raw counters into software variable, so that
// the dynamic tuning mechanism below are based on most up-to-date information
NICUpdateRawCounters(pAd);
#ifdef RT2870
RT2870_WatchDog(pAd);
#endif // RT2870 //
// Need statistics after read counter. So put after NICUpdateRawCounters
ORIBATimerTimeout(pAd);
// The time period for checking antenna is according to traffic
if (pAd->Mlme.bEnableAutoAntennaCheck)
{
TxTotalCnt = pAd->RalinkCounters.OneSecTxNoRetryOkCount +
pAd->RalinkCounters.OneSecTxRetryOkCount +
pAd->RalinkCounters.OneSecTxFailCount;
// dynamic adjust antenna evaluation period according to the traffic
if (TxTotalCnt > 50)
{
if (pAd->Mlme.OneSecPeriodicRound % 10 == 0)
{
AsicEvaluateRxAnt(pAd);
}
}
else
{
if (pAd->Mlme.OneSecPeriodicRound % 3 == 0)
{
AsicEvaluateRxAnt(pAd);
}
}
}
STAMlmePeriodicExec(pAd);
MlmeResetRalinkCounters(pAd);
{
#ifdef RT2860
if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_NIC_NOT_EXIST) && (pAd->bPCIclkOff == FALSE))
#endif
{
// When Adhoc beacon is enabled and RTS/CTS is enabled, there is a chance that hardware MAC FSM will run into a deadlock
// and sending CTS-to-self over and over.
// Software Patch Solution:
// 1. Polling debug state register 0x10F4 every one second.
// 2. If in 0x10F4 the ((bit29==1) && (bit7==1)) OR ((bit29==1) && (bit5==1)), it means the deadlock has occurred.
// 3. If the deadlock occurred, reset MAC/BBP by setting 0x1004 to 0x0001 for a while then setting it back to 0x000C again.
UINT32 MacReg = 0;
RTMP_IO_READ32(pAd, 0x10F4, &MacReg);
if (((MacReg & 0x20000000) && (MacReg & 0x80)) || ((MacReg & 0x20000000) && (MacReg & 0x20)))
{
RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL, 0x1);
RTMPusecDelay(1);
RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL, 0xC);
DBGPRINT(RT_DEBUG_WARN,("Warning, MAC specific condition occurs \n"));
}
}
}
RT28XX_MLME_HANDLER(pAd);
}
pAd->bUpdateBcnCntDone = FALSE;
}
VOID STAMlmePeriodicExec(
PRTMP_ADAPTER pAd)
{
#ifdef RT2860
ULONG TxTotalCnt;
#endif
#ifdef RT2870
ULONG TxTotalCnt;
int i;
#endif
if (pAd->StaCfg.WpaSupplicantUP == WPA_SUPPLICANT_DISABLE)
{
// WPA MIC error should block association attempt for 60 seconds
if (pAd->StaCfg.bBlockAssoc && (pAd->StaCfg.LastMicErrorTime + (60 * OS_HZ) < pAd->Mlme.Now32))
pAd->StaCfg.bBlockAssoc = FALSE;
}
#ifdef RT2860
//Baron 2008/07/10
//printk("Baron_Test:\t%s", RTMPGetRalinkEncryModeStr(pAd->StaCfg.WepStatus));
//If the STA security setting is OPEN or WEP, pAd->StaCfg.WpaSupplicantUP = 0.
//If the STA security setting is WPAPSK or WPA2PSK, pAd->StaCfg.WpaSupplicantUP = 1.
if(pAd->StaCfg.WepStatus<2)
{
pAd->StaCfg.WpaSupplicantUP = 0;
}
else
{
pAd->StaCfg.WpaSupplicantUP = 1;
}
#endif
if ((pAd->PreMediaState != pAd->IndicateMediaState) && (pAd->CommonCfg.bWirelessEvent))
{
if (pAd->IndicateMediaState == NdisMediaStateConnected)
{
RTMPSendWirelessEvent(pAd, IW_STA_LINKUP_EVENT_FLAG, pAd->MacTab.Content[BSSID_WCID].Addr, BSS0, 0);
}
pAd->PreMediaState = pAd->IndicateMediaState;
}
#ifdef RT2860
if ((pAd->OpMode == OPMODE_STA) && (IDLE_ON(pAd)) &&
(OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)) &&
(pAd->Mlme.SyncMachine.CurrState == SYNC_IDLE) &&
(pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE) &&
(RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_START_UP)) &&
(!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF)))
{
RT28xxPciAsicRadioOff(pAd, GUI_IDLE_POWER_SAVE, 0);
}
#endif
AsicStaBbpTuning(pAd);
TxTotalCnt = pAd->RalinkCounters.OneSecTxNoRetryOkCount +
pAd->RalinkCounters.OneSecTxRetryOkCount +
pAd->RalinkCounters.OneSecTxFailCount;
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_MEDIA_STATE_CONNECTED))
{
// update channel quality for Roaming and UI LinkQuality display
MlmeCalculateChannelQuality(pAd, pAd->Mlme.Now32);
}
// must be AFTER MlmeDynamicTxRateSwitching() because it needs to know if
// Radio is currently in noisy environment
if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS))
AsicAdjustTxPower(pAd);
if (INFRA_ON(pAd))
{
// Is PSM bit consistent with user power management policy?
// This is the only place that will set PSM bit ON.
if (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_DOZE))
MlmeCheckPsmChange(pAd, pAd->Mlme.Now32);
pAd->RalinkCounters.LastOneSecTotalTxCount = TxTotalCnt;
if ((pAd->StaCfg.LastBeaconRxTime + 1*OS_HZ < pAd->Mlme.Now32) &&
(!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS)) &&
((TxTotalCnt + pAd->RalinkCounters.OneSecRxOkCnt < 600)))
{
RTMPSetAGCInitValue(pAd, BW_20);
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - No BEACON. restore R66 to the low bound(%d) \n", (0x2E + GET_LNA_GAIN(pAd))));
}
{
if (pAd->CommonCfg.bAPSDCapable && pAd->CommonCfg.APEdcaParm.bAPSDCapable)
{
// When APSD is enabled, the period changes as 20 sec
if ((pAd->Mlme.OneSecPeriodicRound % 20) == 8)
RTMPSendNullFrame(pAd, pAd->CommonCfg.TxRate, TRUE);
}
else
{
// Send out a NULL frame every 10 sec to inform AP that STA is still alive (Avoid being age out)
if ((pAd->Mlme.OneSecPeriodicRound % 10) == 8)
{
if (pAd->CommonCfg.bWmmCapable)
RTMPSendNullFrame(pAd, pAd->CommonCfg.TxRate, TRUE);
else
RTMPSendNullFrame(pAd, pAd->CommonCfg.TxRate, FALSE);
}
}
}
if (CQI_IS_DEAD(pAd->Mlme.ChannelQuality))
{
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - No BEACON. Dead CQI. Auto Recovery attempt #%ld\n", pAd->RalinkCounters.BadCQIAutoRecoveryCount));
pAd->StaCfg.CCXAdjacentAPReportFlag = TRUE;
pAd->StaCfg.CCXAdjacentAPLinkDownTime = pAd->StaCfg.LastBeaconRxTime;
// Lost AP, send disconnect & link down event
LinkDown(pAd, FALSE);
{
union iwreq_data wrqu;
memset(wrqu.ap_addr.sa_data, 0, MAC_ADDR_LEN);
wireless_send_event(pAd->net_dev, SIOCGIWAP, &wrqu, NULL);
}
MlmeAutoReconnectLastSSID(pAd);
}
else if (CQI_IS_BAD(pAd->Mlme.ChannelQuality))
{
pAd->RalinkCounters.BadCQIAutoRecoveryCount ++;
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - Bad CQI. Auto Recovery attempt #%ld\n", pAd->RalinkCounters.BadCQIAutoRecoveryCount));
MlmeAutoReconnectLastSSID(pAd);
}
// Add auto seamless roaming
if (pAd->StaCfg.bFastRoaming)
{
SHORT dBmToRoam = (SHORT)pAd->StaCfg.dBmToRoam;
DBGPRINT(RT_DEBUG_TRACE, ("Rssi=%d, dBmToRoam=%d\n", RTMPMaxRssi(pAd, pAd->StaCfg.RssiSample.LastRssi0, pAd->StaCfg.RssiSample.LastRssi1, pAd->StaCfg.RssiSample.LastRssi2), (CHAR)dBmToRoam));
if (RTMPMaxRssi(pAd, pAd->StaCfg.RssiSample.LastRssi0, pAd->StaCfg.RssiSample.LastRssi1, pAd->StaCfg.RssiSample.LastRssi2) <= (CHAR)dBmToRoam)
{
MlmeCheckForFastRoaming(pAd, pAd->Mlme.Now32);
}
}
}
else if (ADHOC_ON(pAd))
{
#ifdef RT2860
// 2003-04-17 john. this is a patch that driver forces a BEACON out if ASIC fails
// the "TX BEACON competition" for the entire past 1 sec.
// So that even when ASIC's BEACONgen engine been blocked
// by peer's BEACON due to slower system clock, this STA still can send out
// minimum BEACON to tell the peer I'm alive.
// drawback is that this BEACON won't be well aligned at TBTT boundary.
// EnqueueBeaconFrame(pAd); // software send BEACON
// if all 11b peers leave this BSS more than 5 seconds, update Tx rate,
// restore outgoing BEACON to support B/G-mixed mode
if ((pAd->CommonCfg.Channel <= 14) &&
(pAd->CommonCfg.MaxTxRate <= RATE_11) &&
(pAd->CommonCfg.MaxDesiredRate > RATE_11) &&
((pAd->StaCfg.Last11bBeaconRxTime + 5*OS_HZ) < pAd->Mlme.Now32))
{
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - last 11B peer left, update Tx rates\n"));
NdisMoveMemory(pAd->StaActive.SupRate, pAd->CommonCfg.SupRate, MAX_LEN_OF_SUPPORTED_RATES);
pAd->StaActive.SupRateLen = pAd->CommonCfg.SupRateLen;
MlmeUpdateTxRates(pAd, FALSE, 0);
MakeIbssBeacon(pAd); // re-build BEACON frame
AsicEnableIbssSync(pAd); // copy to on-chip memory
pAd->StaCfg.AdhocBOnlyJoined = FALSE;
}
if (pAd->CommonCfg.PhyMode >= PHY_11ABGN_MIXED)
{
if ((pAd->StaCfg.AdhocBGJoined) &&
((pAd->StaCfg.Last11gBeaconRxTime + 5 * OS_HZ) < pAd->Mlme.Now32))
{
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - last 11G peer left\n"));
pAd->StaCfg.AdhocBGJoined = FALSE;
}
if ((pAd->StaCfg.Adhoc20NJoined) &&
((pAd->StaCfg.Last20NBeaconRxTime + 5 * OS_HZ) < pAd->Mlme.Now32))
{
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - last 20MHz N peer left\n"));
pAd->StaCfg.Adhoc20NJoined = FALSE;
}
}
#endif /* RT2860 */
//radar detect
if ((pAd->CommonCfg.Channel > 14)
&& (pAd->CommonCfg.bIEEE80211H == 1)
&& RadarChannelCheck(pAd, pAd->CommonCfg.Channel))
{
RadarDetectPeriodic(pAd);
}
// If all peers leave, and this STA becomes the last one in this IBSS, then change MediaState
// to DISCONNECTED. But still holding this IBSS (i.e. sending BEACON) so that other STAs can
// join later.
if ((pAd->StaCfg.LastBeaconRxTime + ADHOC_BEACON_LOST_TIME < pAd->Mlme.Now32) &&
OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_MEDIA_STATE_CONNECTED))
{
MLME_START_REQ_STRUCT StartReq;
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - excessive BEACON lost, last STA in this IBSS, MediaState=Disconnected\n"));
LinkDown(pAd, FALSE);
StartParmFill(pAd, &StartReq, pAd->MlmeAux.Ssid, pAd->MlmeAux.SsidLen);
MlmeEnqueue(pAd, SYNC_STATE_MACHINE, MT2_MLME_START_REQ, sizeof(MLME_START_REQ_STRUCT), &StartReq);
pAd->Mlme.CntlMachine.CurrState = CNTL_WAIT_START;
}
#ifdef RT2870
for (i = 1; i < MAX_LEN_OF_MAC_TABLE; i++)
{
MAC_TABLE_ENTRY *pEntry = &pAd->MacTab.Content[i];
if (pEntry->ValidAsCLI == FALSE)
continue;
if (pEntry->LastBeaconRxTime + ADHOC_BEACON_LOST_TIME < pAd->Mlme.Now32)
MacTableDeleteEntry(pAd, pEntry->Aid, pEntry->Addr);
}
#endif
}
else // no INFRA nor ADHOC connection
{
if (pAd->StaCfg.bScanReqIsFromWebUI &&
((pAd->StaCfg.LastScanTime + 30 * OS_HZ) > pAd->Mlme.Now32))
goto SKIP_AUTO_SCAN_CONN;
else
pAd->StaCfg.bScanReqIsFromWebUI = FALSE;
if ((pAd->StaCfg.bAutoReconnect == TRUE)
&& RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_START_UP)
&& (MlmeValidateSSID(pAd->MlmeAux.AutoReconnectSsid, pAd->MlmeAux.AutoReconnectSsidLen) == TRUE))
{
if ((pAd->ScanTab.BssNr==0) && (pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE))
{
MLME_SCAN_REQ_STRUCT ScanReq;
if ((pAd->StaCfg.LastScanTime + 10 * OS_HZ) < pAd->Mlme.Now32)
{
DBGPRINT(RT_DEBUG_TRACE, ("STAMlmePeriodicExec():CNTL - ScanTab.BssNr==0, start a new ACTIVE scan SSID[%s]\n", pAd->MlmeAux.AutoReconnectSsid));
ScanParmFill(pAd, &ScanReq, pAd->MlmeAux.AutoReconnectSsid, pAd->MlmeAux.AutoReconnectSsidLen, BSS_ANY, SCAN_ACTIVE);
MlmeEnqueue(pAd, SYNC_STATE_MACHINE, MT2_MLME_SCAN_REQ, sizeof(MLME_SCAN_REQ_STRUCT), &ScanReq);
pAd->Mlme.CntlMachine.CurrState = CNTL_WAIT_OID_LIST_SCAN;
// Reset Missed scan number
pAd->StaCfg.LastScanTime = pAd->Mlme.Now32;
}
else if (pAd->StaCfg.BssType == BSS_ADHOC) // Quit the forever scan when in a very clean room
MlmeAutoReconnectLastSSID(pAd);
}
else if (pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE)
{
if ((pAd->Mlme.OneSecPeriodicRound % 7) == 0)
{
MlmeAutoScan(pAd);
pAd->StaCfg.LastScanTime = pAd->Mlme.Now32;
}
else
{
MlmeAutoReconnectLastSSID(pAd);
}
}
}
}
SKIP_AUTO_SCAN_CONN:
if ((pAd->MacTab.Content[BSSID_WCID].TXBAbitmap !=0) && (pAd->MacTab.fAnyBASession == FALSE))
{
pAd->MacTab.fAnyBASession = TRUE;
AsicUpdateProtect(pAd, HT_FORCERTSCTS, ALLN_SETPROTECT, FALSE, FALSE);
}
else if ((pAd->MacTab.Content[BSSID_WCID].TXBAbitmap ==0) && (pAd->MacTab.fAnyBASession == TRUE))
{
pAd->MacTab.fAnyBASession = FALSE;
AsicUpdateProtect(pAd, pAd->MlmeAux.AddHtInfo.AddHtInfo2.OperaionMode, ALLN_SETPROTECT, FALSE, FALSE);
}
return;
}
// Link down report
VOID LinkDownExec(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3)
{
RTMP_ADAPTER *pAd = (RTMP_ADAPTER *)FunctionContext;
pAd->IndicateMediaState = NdisMediaStateDisconnected;
RTMP_IndicateMediaState(pAd);
pAd->ExtraInfo = GENERAL_LINK_DOWN;
}
// IRQL = DISPATCH_LEVEL
VOID MlmeAutoScan(
IN PRTMP_ADAPTER pAd)
{
// check CntlMachine.CurrState to avoid collision with NDIS SetOID request
if (pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE)
{
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - Driver auto scan\n"));
MlmeEnqueue(pAd,
MLME_CNTL_STATE_MACHINE,
OID_802_11_BSSID_LIST_SCAN,
0,
NULL);
RT28XX_MLME_HANDLER(pAd);
}
}
// IRQL = DISPATCH_LEVEL
VOID MlmeAutoReconnectLastSSID(
IN PRTMP_ADAPTER pAd)
{
// check CntlMachine.CurrState to avoid collision with NDIS SetOID request
if ((pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE) &&
(MlmeValidateSSID(pAd->MlmeAux.AutoReconnectSsid, pAd->MlmeAux.AutoReconnectSsidLen) == TRUE))
{
NDIS_802_11_SSID OidSsid;
OidSsid.SsidLength = pAd->MlmeAux.AutoReconnectSsidLen;
NdisMoveMemory(OidSsid.Ssid, pAd->MlmeAux.AutoReconnectSsid, pAd->MlmeAux.AutoReconnectSsidLen);
DBGPRINT(RT_DEBUG_TRACE, ("Driver auto reconnect to last OID_802_11_SSID setting - %s, len - %d\n", pAd->MlmeAux.AutoReconnectSsid, pAd->MlmeAux.AutoReconnectSsidLen));
MlmeEnqueue(pAd,
MLME_CNTL_STATE_MACHINE,
OID_802_11_SSID,
sizeof(NDIS_802_11_SSID),
&OidSsid);
RT28XX_MLME_HANDLER(pAd);
}
}
/*
==========================================================================
Validate SSID for connection try and rescan purpose
Valid SSID will have visible chars only.
The valid length is from 0 to 32.
IRQL = DISPATCH_LEVEL
==========================================================================
*/
BOOLEAN MlmeValidateSSID(
IN PUCHAR pSsid,
IN UCHAR SsidLen)
{
int index;
if (SsidLen > MAX_LEN_OF_SSID)
return (FALSE);
// Check each character value
for (index = 0; index < SsidLen; index++)
{
if (pSsid[index] < 0x20)
return (FALSE);
}
// All checked
return (TRUE);
}
VOID MlmeSelectTxRateTable(
IN PRTMP_ADAPTER pAd,
IN PMAC_TABLE_ENTRY pEntry,
IN PUCHAR *ppTable,
IN PUCHAR pTableSize,
IN PUCHAR pInitTxRateIdx)
{
do
{
// decide the rate table for tuning
if (pAd->CommonCfg.TxRateTableSize > 0)
{
*ppTable = RateSwitchTable;
*pTableSize = RateSwitchTable[0];
*pInitTxRateIdx = RateSwitchTable[1];
break;
}
if ((pAd->OpMode == OPMODE_STA) && ADHOC_ON(pAd))
{
if ((pAd->CommonCfg.PhyMode >= PHY_11ABGN_MIXED) &&
#ifdef RT2860
!pAd->StaCfg.AdhocBOnlyJoined &&
!pAd->StaCfg.AdhocBGJoined &&
(pAd->StaActive.SupportedPhyInfo.MCSSet[0] == 0xff) &&
((pAd->StaActive.SupportedPhyInfo.MCSSet[1] == 0x00) || (pAd->Antenna.field.TxPath == 1)))
#endif
#ifdef RT2870
(pEntry->HTCapability.MCSSet[0] == 0xff) &&
((pEntry->HTCapability.MCSSet[1] == 0x00) || (pAd->Antenna.field.TxPath == 1)))
#endif
{// 11N 1S Adhoc
*ppTable = RateSwitchTable11N1S;
*pTableSize = RateSwitchTable11N1S[0];
*pInitTxRateIdx = RateSwitchTable11N1S[1];
}
else if ((pAd->CommonCfg.PhyMode >= PHY_11ABGN_MIXED) &&
#ifdef RT2860
!pAd->StaCfg.AdhocBOnlyJoined &&
!pAd->StaCfg.AdhocBGJoined &&
(pAd->StaActive.SupportedPhyInfo.MCSSet[0] == 0xff) &&
(pAd->StaActive.SupportedPhyInfo.MCSSet[1] == 0xff) &&
#endif
#ifdef RT2870
(pEntry->HTCapability.MCSSet[0] == 0xff) &&
(pEntry->HTCapability.MCSSet[1] == 0xff) &&
#endif
(pAd->Antenna.field.TxPath == 2))
{// 11N 2S Adhoc
if (pAd->LatchRfRegs.Channel <= 14)
{
*ppTable = RateSwitchTable11N2S;
*pTableSize = RateSwitchTable11N2S[0];
*pInitTxRateIdx = RateSwitchTable11N2S[1];
}
else
{
*ppTable = RateSwitchTable11N2SForABand;
*pTableSize = RateSwitchTable11N2SForABand[0];
*pInitTxRateIdx = RateSwitchTable11N2SForABand[1];
}
}
else
#ifdef RT2860
if (pAd->CommonCfg.PhyMode == PHY_11B)
{
*ppTable = RateSwitchTable11B;
*pTableSize = RateSwitchTable11B[0];
*pInitTxRateIdx = RateSwitchTable11B[1];
}
else if((pAd->LatchRfRegs.Channel <= 14) && (pAd->StaCfg.AdhocBOnlyJoined == TRUE))
#endif
#ifdef RT2870
if ((pEntry->RateLen == 4)
&& (pEntry->HTCapability.MCSSet[0] == 0) && (pEntry->HTCapability.MCSSet[1] == 0)
)
#endif
{
// USe B Table when Only b-only Station in my IBSS .
*ppTable = RateSwitchTable11B;
*pTableSize = RateSwitchTable11B[0];
*pInitTxRateIdx = RateSwitchTable11B[1];
}
else if (pAd->LatchRfRegs.Channel <= 14)
{
*ppTable = RateSwitchTable11BG;
*pTableSize = RateSwitchTable11BG[0];
*pInitTxRateIdx = RateSwitchTable11BG[1];
}
else
{
*ppTable = RateSwitchTable11G;
*pTableSize = RateSwitchTable11G[0];
*pInitTxRateIdx = RateSwitchTable11G[1];
}
break;
}
if ((pEntry->RateLen == 12) && (pEntry->HTCapability.MCSSet[0] == 0xff) &&
((pEntry->HTCapability.MCSSet[1] == 0x00) || (pAd->CommonCfg.TxStream == 1)))
{// 11BGN 1S AP
*ppTable = RateSwitchTable11BGN1S;
*pTableSize = RateSwitchTable11BGN1S[0];
*pInitTxRateIdx = RateSwitchTable11BGN1S[1];
break;
}
if ((pEntry->RateLen == 12) && (pEntry->HTCapability.MCSSet[0] == 0xff) &&
(pEntry->HTCapability.MCSSet[1] == 0xff) && (pAd->CommonCfg.TxStream == 2))
{// 11BGN 2S AP
if (pAd->LatchRfRegs.Channel <= 14)
{
*ppTable = RateSwitchTable11BGN2S;
*pTableSize = RateSwitchTable11BGN2S[0];
*pInitTxRateIdx = RateSwitchTable11BGN2S[1];
}
else
{
*ppTable = RateSwitchTable11BGN2SForABand;
*pTableSize = RateSwitchTable11BGN2SForABand[0];
*pInitTxRateIdx = RateSwitchTable11BGN2SForABand[1];
}
break;
}
if ((pEntry->HTCapability.MCSSet[0] == 0xff) && ((pEntry->HTCapability.MCSSet[1] == 0x00) || (pAd->CommonCfg.TxStream == 1)))
{// 11N 1S AP
*ppTable = RateSwitchTable11N1S;
*pTableSize = RateSwitchTable11N1S[0];
*pInitTxRateIdx = RateSwitchTable11N1S[1];
break;
}
if ((pEntry->HTCapability.MCSSet[0] == 0xff) && (pEntry->HTCapability.MCSSet[1] == 0xff) && (pAd->CommonCfg.TxStream == 2))
{// 11N 2S AP
if (pAd->LatchRfRegs.Channel <= 14)
{
*ppTable = RateSwitchTable11N2S;
*pTableSize = RateSwitchTable11N2S[0];
*pInitTxRateIdx = RateSwitchTable11N2S[1];
}
else
{
*ppTable = RateSwitchTable11N2SForABand;
*pTableSize = RateSwitchTable11N2SForABand[0];
*pInitTxRateIdx = RateSwitchTable11N2SForABand[1];
}
break;
}
//else if ((pAd->StaActive.SupRateLen == 4) && (pAd->StaActive.ExtRateLen == 0) && (pAd->StaActive.SupportedPhyInfo.MCSSet[0] == 0) && (pAd->StaActive.SupportedPhyInfo.MCSSet[1] == 0))
if (pEntry->RateLen == 4)
{// B only AP
*ppTable = RateSwitchTable11B;
*pTableSize = RateSwitchTable11B[0];
*pInitTxRateIdx = RateSwitchTable11B[1];
break;
}
//else if ((pAd->StaActive.SupRateLen + pAd->StaActive.ExtRateLen > 8) && (pAd->StaActive.SupportedPhyInfo.MCSSet[0] == 0) && (pAd->StaActive.SupportedPhyInfo.MCSSet[1] == 0))
if ((pEntry->RateLen > 8)
&& (pEntry->HTCapability.MCSSet[0] == 0) && (pEntry->HTCapability.MCSSet[1] == 0)
)
{// B/G mixed AP
*ppTable = RateSwitchTable11BG;
*pTableSize = RateSwitchTable11BG[0];
*pInitTxRateIdx = RateSwitchTable11BG[1];
break;
}
//else if ((pAd->StaActive.SupRateLen + pAd->StaActive.ExtRateLen == 8) && (pAd->StaActive.SupportedPhyInfo.MCSSet[0] == 0) && (pAd->StaActive.SupportedPhyInfo.MCSSet[1] == 0))
if ((pEntry->RateLen == 8)
&& (pEntry->HTCapability.MCSSet[0] == 0) && (pEntry->HTCapability.MCSSet[1] == 0)
)
{// G only AP
*ppTable = RateSwitchTable11G;
*pTableSize = RateSwitchTable11G[0];
*pInitTxRateIdx = RateSwitchTable11G[1];
break;
}
{
//else if ((pAd->StaActive.SupportedPhyInfo.MCSSet[0] == 0) && (pAd->StaActive.SupportedPhyInfo.MCSSet[1] == 0))
if ((pEntry->HTCapability.MCSSet[0] == 0) && (pEntry->HTCapability.MCSSet[1] == 0))
{ // Legacy mode
if (pAd->CommonCfg.MaxTxRate <= RATE_11)
{
*ppTable = RateSwitchTable11B;
*pTableSize = RateSwitchTable11B[0];
*pInitTxRateIdx = RateSwitchTable11B[1];
}
else if ((pAd->CommonCfg.MaxTxRate > RATE_11) && (pAd->CommonCfg.MinTxRate > RATE_11))
{
*ppTable = RateSwitchTable11G;
*pTableSize = RateSwitchTable11G[0];
*pInitTxRateIdx = RateSwitchTable11G[1];
}
else
{
*ppTable = RateSwitchTable11BG;
*pTableSize = RateSwitchTable11BG[0];
*pInitTxRateIdx = RateSwitchTable11BG[1];
}
break;
}
if (pAd->LatchRfRegs.Channel <= 14)
{
if (pAd->CommonCfg.TxStream == 1)
{
*ppTable = RateSwitchTable11N1S;
*pTableSize = RateSwitchTable11N1S[0];
*pInitTxRateIdx = RateSwitchTable11N1S[1];
DBGPRINT_RAW(RT_DEBUG_ERROR,("DRS: unkown mode,default use 11N 1S AP \n"));
}
else
{
*ppTable = RateSwitchTable11N2S;
*pTableSize = RateSwitchTable11N2S[0];
*pInitTxRateIdx = RateSwitchTable11N2S[1];
DBGPRINT_RAW(RT_DEBUG_ERROR,("DRS: unkown mode,default use 11N 2S AP \n"));
}
}
else
{
if (pAd->CommonCfg.TxStream == 1)
{
*ppTable = RateSwitchTable11N1S;
*pTableSize = RateSwitchTable11N1S[0];
*pInitTxRateIdx = RateSwitchTable11N1S[1];
DBGPRINT_RAW(RT_DEBUG_ERROR,("DRS: unkown mode,default use 11N 1S AP \n"));
}
else
{
*ppTable = RateSwitchTable11N2SForABand;
*pTableSize = RateSwitchTable11N2SForABand[0];
*pInitTxRateIdx = RateSwitchTable11N2SForABand[1];
DBGPRINT_RAW(RT_DEBUG_ERROR,("DRS: unkown mode,default use 11N 2S AP \n"));
}
}
DBGPRINT_RAW(RT_DEBUG_ERROR,("DRS: unkown mode (SupRateLen=%d, ExtRateLen=%d, MCSSet[0]=0x%x, MCSSet[1]=0x%x)\n",
pAd->StaActive.SupRateLen, pAd->StaActive.ExtRateLen, pAd->StaActive.SupportedPhyInfo.MCSSet[0], pAd->StaActive.SupportedPhyInfo.MCSSet[1]));
}
} while(FALSE);
}
/*
==========================================================================
Description:
This routine checks if there're other APs out there capable for
roaming. Caller should call this routine only when Link up in INFRA mode
and channel quality is below CQI_GOOD_THRESHOLD.
IRQL = DISPATCH_LEVEL
Output:
==========================================================================
*/
VOID MlmeCheckForRoaming(
IN PRTMP_ADAPTER pAd,
IN ULONG Now32)
{
USHORT i;
BSS_TABLE *pRoamTab = &pAd->MlmeAux.RoamTab;
BSS_ENTRY *pBss;
DBGPRINT(RT_DEBUG_TRACE, ("==> MlmeCheckForRoaming\n"));
// put all roaming candidates into RoamTab, and sort in RSSI order
BssTableInit(pRoamTab);
for (i = 0; i < pAd->ScanTab.BssNr; i++)
{
pBss = &pAd->ScanTab.BssEntry[i];
if ((pBss->LastBeaconRxTime + BEACON_LOST_TIME) < Now32)
continue; // AP disappear
if (pBss->Rssi <= RSSI_THRESHOLD_FOR_ROAMING)
continue; // RSSI too weak. forget it.
if (MAC_ADDR_EQUAL(pBss->Bssid, pAd->CommonCfg.Bssid))
continue; // skip current AP
if (pBss->Rssi < (pAd->StaCfg.RssiSample.LastRssi0 + RSSI_DELTA))
continue; // only AP with stronger RSSI is eligible for roaming
// AP passing all above rules is put into roaming candidate table
NdisMoveMemory(&pRoamTab->BssEntry[pRoamTab->BssNr], pBss, sizeof(BSS_ENTRY));
pRoamTab->BssNr += 1;
}
if (pRoamTab->BssNr > 0)
{
// check CntlMachine.CurrState to avoid collision with NDIS SetOID request
if (pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE)
{
pAd->RalinkCounters.PoorCQIRoamingCount ++;
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - Roaming attempt #%ld\n", pAd->RalinkCounters.PoorCQIRoamingCount));
MlmeEnqueue(pAd, MLME_CNTL_STATE_MACHINE, MT2_MLME_ROAMING_REQ, 0, NULL);
RT28XX_MLME_HANDLER(pAd);
}
}
DBGPRINT(RT_DEBUG_TRACE, ("<== MlmeCheckForRoaming(# of candidate= %d)\n",pRoamTab->BssNr));
}
/*
==========================================================================
Description:
This routine checks if there're other APs out there capable for
roaming. Caller should call this routine only when link up in INFRA mode
and channel quality is below CQI_GOOD_THRESHOLD.
IRQL = DISPATCH_LEVEL
Output:
==========================================================================
*/
VOID MlmeCheckForFastRoaming(
IN PRTMP_ADAPTER pAd,
IN ULONG Now)
{
USHORT i;
BSS_TABLE *pRoamTab = &pAd->MlmeAux.RoamTab;
BSS_ENTRY *pBss;
DBGPRINT(RT_DEBUG_TRACE, ("==> MlmeCheckForFastRoaming\n"));
// put all roaming candidates into RoamTab, and sort in RSSI order
BssTableInit(pRoamTab);
for (i = 0; i < pAd->ScanTab.BssNr; i++)
{
pBss = &pAd->ScanTab.BssEntry[i];
if ((pBss->Rssi <= -50) && (pBss->Channel == pAd->CommonCfg.Channel))
continue; // RSSI too weak. forget it.
if (MAC_ADDR_EQUAL(pBss->Bssid, pAd->CommonCfg.Bssid))
continue; // skip current AP
if (!SSID_EQUAL(pBss->Ssid, pBss->SsidLen, pAd->CommonCfg.Ssid, pAd->CommonCfg.SsidLen))
continue; // skip different SSID
if (pBss->Rssi < (RTMPMaxRssi(pAd, pAd->StaCfg.RssiSample.LastRssi0, pAd->StaCfg.RssiSample.LastRssi1, pAd->StaCfg.RssiSample.LastRssi2) + RSSI_DELTA))
continue; // skip AP without better RSSI
DBGPRINT(RT_DEBUG_TRACE, ("LastRssi0 = %d, pBss->Rssi = %d\n", RTMPMaxRssi(pAd, pAd->StaCfg.RssiSample.LastRssi0, pAd->StaCfg.RssiSample.LastRssi1, pAd->StaCfg.RssiSample.LastRssi2), pBss->Rssi));
// AP passing all above rules is put into roaming candidate table
NdisMoveMemory(&pRoamTab->BssEntry[pRoamTab->BssNr], pBss, sizeof(BSS_ENTRY));
pRoamTab->BssNr += 1;
}
if (pRoamTab->BssNr > 0)
{
// check CntlMachine.CurrState to avoid collision with NDIS SetOID request
if (pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE)
{
pAd->RalinkCounters.PoorCQIRoamingCount ++;
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - Roaming attempt #%ld\n", pAd->RalinkCounters.PoorCQIRoamingCount));
MlmeEnqueue(pAd, MLME_CNTL_STATE_MACHINE, MT2_MLME_ROAMING_REQ, 0, NULL);
RT28XX_MLME_HANDLER(pAd);
}
}
// Maybe site survey required
else
{
if ((pAd->StaCfg.LastScanTime + 10 * 1000) < Now)
{
// check CntlMachine.CurrState to avoid collision with NDIS SetOID request
DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - Roaming, No eligable entry, try new scan!\n"));
pAd->StaCfg.ScanCnt = 2;
pAd->StaCfg.LastScanTime = Now;
MlmeAutoScan(pAd);
}
}
DBGPRINT(RT_DEBUG_TRACE, ("<== MlmeCheckForFastRoaming (BssNr=%d)\n", pRoamTab->BssNr));
}
/*
==========================================================================
Description:
This routine calculates TxPER, RxPER of the past N-sec period. And
according to the calculation result, ChannelQuality is calculated here
to decide if current AP is still doing the job.
If ChannelQuality is not good, a ROAMing attempt may be tried later.
Output:
StaCfg.ChannelQuality - 0..100
IRQL = DISPATCH_LEVEL
NOTE: This routine decide channle quality based on RX CRC error ratio.
Caller should make sure a function call to NICUpdateRawCounters(pAd)
is performed right before this routine, so that this routine can decide
channel quality based on the most up-to-date information
==========================================================================
*/
VOID MlmeCalculateChannelQuality(
IN PRTMP_ADAPTER pAd,
IN ULONG Now32)
{
ULONG TxOkCnt, TxCnt, TxPER, TxPRR;
ULONG RxCnt, RxPER;
UCHAR NorRssi;
CHAR MaxRssi;
ULONG BeaconLostTime = BEACON_LOST_TIME;
MaxRssi = RTMPMaxRssi(pAd, pAd->StaCfg.RssiSample.LastRssi0, pAd->StaCfg.RssiSample.LastRssi1, pAd->StaCfg.RssiSample.LastRssi2);
//
// calculate TX packet error ratio and TX retry ratio - if too few TX samples, skip TX related statistics
//
TxOkCnt = pAd->RalinkCounters.OneSecTxNoRetryOkCount + pAd->RalinkCounters.OneSecTxRetryOkCount;
TxCnt = TxOkCnt + pAd->RalinkCounters.OneSecTxFailCount;
if (TxCnt < 5)
{
TxPER = 0;
TxPRR = 0;
}
else
{
TxPER = (pAd->RalinkCounters.OneSecTxFailCount * 100) / TxCnt;
TxPRR = ((TxCnt - pAd->RalinkCounters.OneSecTxNoRetryOkCount) * 100) / TxCnt;
}
//
// calculate RX PER - don't take RxPER into consideration if too few sample
//
RxCnt = pAd->RalinkCounters.OneSecRxOkCnt + pAd->RalinkCounters.OneSecRxFcsErrCnt;
if (RxCnt < 5)
RxPER = 0;
else
RxPER = (pAd->RalinkCounters.OneSecRxFcsErrCnt * 100) / RxCnt;
//
// decide ChannelQuality based on: 1)last BEACON received time, 2)last RSSI, 3)TxPER, and 4)RxPER
//
if (INFRA_ON(pAd) &&
(pAd->RalinkCounters.OneSecTxNoRetryOkCount < 2) && // no heavy traffic
(pAd->StaCfg.LastBeaconRxTime + BeaconLostTime < Now32))
{
DBGPRINT(RT_DEBUG_TRACE, ("BEACON lost > %ld msec with TxOkCnt=%ld -> CQI=0\n", BeaconLostTime, TxOkCnt));
pAd->Mlme.ChannelQuality = 0;
}
else
{
// Normalize Rssi
if (MaxRssi > -40)
NorRssi = 100;
else if (MaxRssi < -90)
NorRssi = 0;
else
NorRssi = (MaxRssi + 90) * 2;
// ChannelQuality = W1*RSSI + W2*TxPRR + W3*RxPER (RSSI 0..100), (TxPER 100..0), (RxPER 100..0)
pAd->Mlme.ChannelQuality = (RSSI_WEIGHTING * NorRssi +
TX_WEIGHTING * (100 - TxPRR) +
RX_WEIGHTING* (100 - RxPER)) / 100;
if (pAd->Mlme.ChannelQuality >= 100)
pAd->Mlme.ChannelQuality = 100;
}
}
VOID MlmeSetTxRate(
IN PRTMP_ADAPTER pAd,
IN PMAC_TABLE_ENTRY pEntry,
IN PRTMP_TX_RATE_SWITCH pTxRate)
{
UCHAR MaxMode = MODE_OFDM;
MaxMode = MODE_HTGREENFIELD;
if (pTxRate->STBC && (pAd->StaCfg.MaxHTPhyMode.field.STBC) && (pAd->Antenna.field.TxPath == 2))
pAd->StaCfg.HTPhyMode.field.STBC = STBC_USE;
else
pAd->StaCfg.HTPhyMode.field.STBC = STBC_NONE;
if (pTxRate->CurrMCS < MCS_AUTO)
pAd->StaCfg.HTPhyMode.field.MCS = pTxRate->CurrMCS;
if (pAd->StaCfg.HTPhyMode.field.MCS > 7)
pAd->StaCfg.HTPhyMode.field.STBC = STBC_NONE;
if (ADHOC_ON(pAd))
{
// If peer adhoc is b-only mode, we can't send 11g rate.
pAd->StaCfg.HTPhyMode.field.ShortGI = GI_800;
pEntry->HTPhyMode.field.STBC = STBC_NONE;
//
// For Adhoc MODE_CCK, driver will use AdhocBOnlyJoined flag to roll back to B only if necessary
//
pEntry->HTPhyMode.field.MODE = pTxRate->Mode;
pEntry->HTPhyMode.field.ShortGI = pAd->StaCfg.HTPhyMode.field.ShortGI;
pEntry->HTPhyMode.field.MCS = pAd->StaCfg.HTPhyMode.field.MCS;
// Patch speed error in status page
pAd->StaCfg.HTPhyMode.field.MODE = pEntry->HTPhyMode.field.MODE;
}
else
{
if (pTxRate->Mode <= MaxMode)
pAd->StaCfg.HTPhyMode.field.MODE = pTxRate->Mode;
if (pTxRate->ShortGI && (pAd->StaCfg.MaxHTPhyMode.field.ShortGI))
pAd->StaCfg.HTPhyMode.field.ShortGI = GI_400;
else
pAd->StaCfg.HTPhyMode.field.ShortGI = GI_800;
// Reexam each bandwidth's SGI support.
if (pAd->StaCfg.HTPhyMode.field.ShortGI == GI_400)
{
if ((pEntry->HTPhyMode.field.BW == BW_20) && (!CLIENT_STATUS_TEST_FLAG(pEntry, fCLIENT_STATUS_SGI20_CAPABLE)))
pAd->StaCfg.HTPhyMode.field.ShortGI = GI_800;
if ((pEntry->HTPhyMode.field.BW == BW_40) && (!CLIENT_STATUS_TEST_FLAG(pEntry, fCLIENT_STATUS_SGI40_CAPABLE)))
pAd->StaCfg.HTPhyMode.field.ShortGI = GI_800;
}
// Turn RTS/CTS rate to 6Mbps.
if ((pEntry->HTPhyMode.field.MCS == 0) && (pAd->StaCfg.HTPhyMode.field.MCS != 0))
{
pEntry->HTPhyMode.field.MCS = pAd->StaCfg.HTPhyMode.field.MCS;
if (pAd->MacTab.fAnyBASession)
{
AsicUpdateProtect(pAd, HT_FORCERTSCTS, ALLN_SETPROTECT, TRUE, (BOOLEAN)pAd->MlmeAux.AddHtInfo.AddHtInfo2.NonGfPresent);
}
else
{
AsicUpdateProtect(pAd, pAd->MlmeAux.AddHtInfo.AddHtInfo2.OperaionMode, ALLN_SETPROTECT, TRUE, (BOOLEAN)pAd->MlmeAux.AddHtInfo.AddHtInfo2.NonGfPresent);
}
}
else if ((pEntry->HTPhyMode.field.MCS == 8) && (pAd->StaCfg.HTPhyMode.field.MCS != 8))
{
pEntry->HTPhyMode.field.MCS = pAd->StaCfg.HTPhyMode.field.MCS;
if (pAd->MacTab.fAnyBASession)
{
AsicUpdateProtect(pAd, HT_FORCERTSCTS, ALLN_SETPROTECT, TRUE, (BOOLEAN)pAd->MlmeAux.AddHtInfo.AddHtInfo2.NonGfPresent);
}
else
{
AsicUpdateProtect(pAd, pAd->MlmeAux.AddHtInfo.AddHtInfo2.OperaionMode, ALLN_SETPROTECT, TRUE, (BOOLEAN)pAd->MlmeAux.AddHtInfo.AddHtInfo2.NonGfPresent);
}
}
else if ((pEntry->HTPhyMode.field.MCS != 0) && (pAd->StaCfg.HTPhyMode.field.MCS == 0))
{
AsicUpdateProtect(pAd, HT_RTSCTS_6M, ALLN_SETPROTECT, TRUE, (BOOLEAN)pAd->MlmeAux.AddHtInfo.AddHtInfo2.NonGfPresent);
}
else if ((pEntry->HTPhyMode.field.MCS != 8) && (pAd->StaCfg.HTPhyMode.field.MCS == 8))
{
AsicUpdateProtect(pAd, HT_RTSCTS_6M, ALLN_SETPROTECT, TRUE, (BOOLEAN)pAd->MlmeAux.AddHtInfo.AddHtInfo2.NonGfPresent);
}
pEntry->HTPhyMode.field.STBC = pAd->StaCfg.HTPhyMode.field.STBC;
pEntry->HTPhyMode.field.ShortGI = pAd->StaCfg.HTPhyMode.field.ShortGI;
pEntry->HTPhyMode.field.MCS = pAd->StaCfg.HTPhyMode.field.MCS;
pEntry->HTPhyMode.field.MODE = pAd->StaCfg.HTPhyMode.field.MODE;
if ((pAd->StaCfg.MaxHTPhyMode.field.MODE == MODE_HTGREENFIELD) &&
pAd->WIFItestbed.bGreenField)
pEntry->HTPhyMode.field.MODE = MODE_HTGREENFIELD;
}
pAd->LastTxRate = (USHORT)(pEntry->HTPhyMode.word);
}
/*
==========================================================================
Description:
This routine calculates the acumulated TxPER of eaxh TxRate. And
according to the calculation result, change CommonCfg.TxRate which
is the stable TX Rate we expect the Radio situation could sustained.
CommonCfg.TxRate will change dynamically within {RATE_1/RATE_6, MaxTxRate}
Output:
CommonCfg.TxRate -
IRQL = DISPATCH_LEVEL
NOTE:
call this routine every second
==========================================================================
*/
VOID MlmeDynamicTxRateSwitching(
IN PRTMP_ADAPTER pAd)
{
UCHAR UpRateIdx = 0, DownRateIdx = 0, CurrRateIdx;
ULONG i, AccuTxTotalCnt = 0, TxTotalCnt;
ULONG TxErrorRatio = 0;
BOOLEAN bTxRateChanged, bUpgradeQuality = FALSE;
PRTMP_TX_RATE_SWITCH pCurrTxRate, pNextTxRate = NULL;
PUCHAR pTable;
UCHAR TableSize = 0;
UCHAR InitTxRateIdx = 0, TrainUp, TrainDown;
CHAR Rssi, RssiOffset = 0;
TX_STA_CNT1_STRUC StaTx1;
TX_STA_CNT0_STRUC TxStaCnt0;
ULONG TxRetransmit = 0, TxSuccess = 0, TxFailCount = 0;
MAC_TABLE_ENTRY *pEntry;
//
// walk through MAC table, see if need to change AP's TX rate toward each entry
//
for (i = 1; i < MAX_LEN_OF_MAC_TABLE; i++)
{
pEntry = &pAd->MacTab.Content[i];
// check if this entry need to switch rate automatically
if (RTMPCheckEntryEnableAutoRateSwitch(pAd, pEntry) == FALSE)
continue;
if ((pAd->MacTab.Size == 1) || (pEntry->ValidAsDls))
{
#ifdef RT2860
Rssi = RTMPMaxRssi(pAd, (CHAR)pAd->StaCfg.RssiSample.AvgRssi0, (CHAR)pAd->StaCfg.RssiSample.AvgRssi1, (CHAR)pAd->StaCfg.RssiSample.AvgRssi2);
#endif
#ifdef RT2870
Rssi = RTMPMaxRssi(pAd,
pAd->StaCfg.RssiSample.AvgRssi0,
pAd->StaCfg.RssiSample.AvgRssi1,
pAd->StaCfg.RssiSample.AvgRssi2);
#endif
// Update statistic counter
RTMP_IO_READ32(pAd, TX_STA_CNT0, &TxStaCnt0.word);
RTMP_IO_READ32(pAd, TX_STA_CNT1, &StaTx1.word);
pAd->bUpdateBcnCntDone = TRUE;
TxRetransmit = StaTx1.field.TxRetransmit;
TxSuccess = StaTx1.field.TxSuccess;
TxFailCount = TxStaCnt0.field.TxFailCount;
TxTotalCnt = TxRetransmit + TxSuccess + TxFailCount;
pAd->RalinkCounters.OneSecTxRetryOkCount += StaTx1.field.TxRetransmit;
pAd->RalinkCounters.OneSecTxNoRetryOkCount += StaTx1.field.TxSuccess;
pAd->RalinkCounters.OneSecTxFailCount += TxStaCnt0.field.TxFailCount;
pAd->WlanCounters.TransmittedFragmentCount.u.LowPart += StaTx1.field.TxSuccess;
pAd->WlanCounters.RetryCount.u.LowPart += StaTx1.field.TxRetransmit;
pAd->WlanCounters.FailedCount.u.LowPart += TxStaCnt0.field.TxFailCount;
// if no traffic in the past 1-sec period, don't change TX rate,
// but clear all bad history. because the bad history may affect the next
// Chariot throughput test
AccuTxTotalCnt = pAd->RalinkCounters.OneSecTxNoRetryOkCount +
pAd->RalinkCounters.OneSecTxRetryOkCount +
pAd->RalinkCounters.OneSecTxFailCount;
if (TxTotalCnt)
TxErrorRatio = ((TxRetransmit + TxFailCount) * 100) / TxTotalCnt;
}
else
{
#ifdef RT2860
Rssi = RTMPMaxRssi(pAd, (CHAR)pEntry->RssiSample.AvgRssi0, (CHAR)pEntry->RssiSample.AvgRssi1, (CHAR)pEntry->RssiSample.AvgRssi2);
#endif
#ifdef RT2870
if (INFRA_ON(pAd) && (i == 1))
Rssi = RTMPMaxRssi(pAd,
pAd->StaCfg.RssiSample.AvgRssi0,
pAd->StaCfg.RssiSample.AvgRssi1,
pAd->StaCfg.RssiSample.AvgRssi2);
else
Rssi = RTMPMaxRssi(pAd,
pEntry->RssiSample.AvgRssi0,
pEntry->RssiSample.AvgRssi1,
pEntry->RssiSample.AvgRssi2);
#endif
TxTotalCnt = pEntry->OneSecTxNoRetryOkCount +
pEntry->OneSecTxRetryOkCount +
pEntry->OneSecTxFailCount;
if (TxTotalCnt)
TxErrorRatio = ((pEntry->OneSecTxRetryOkCount + pEntry->OneSecTxFailCount) * 100) / TxTotalCnt;
}
CurrRateIdx = pEntry->CurrTxRateIndex;
MlmeSelectTxRateTable(pAd, pEntry, &pTable, &TableSize, &InitTxRateIdx);
if (CurrRateIdx >= TableSize)
{
CurrRateIdx = TableSize - 1;
}
// When switch from Fixed rate -> auto rate, the REAL TX rate might be different from pAd->CommonCfg.TxRateIndex.
// So need to sync here.
pCurrTxRate = (PRTMP_TX_RATE_SWITCH) &pTable[(CurrRateIdx+1)*5];
if ((pEntry->HTPhyMode.field.MCS != pCurrTxRate->CurrMCS)
//&& (pAd->StaCfg.bAutoTxRateSwitch == TRUE)
)
{
// Need to sync Real Tx rate and our record.
// Then return for next DRS.
pCurrTxRate = (PRTMP_TX_RATE_SWITCH) &pTable[(InitTxRateIdx+1)*5];
pEntry->CurrTxRateIndex = InitTxRateIdx;
MlmeSetTxRate(pAd, pEntry, pCurrTxRate);
// reset all OneSecTx counters
RESET_ONE_SEC_TX_CNT(pEntry);
continue;
}
// decide the next upgrade rate and downgrade rate, if any
if ((CurrRateIdx > 0) && (CurrRateIdx < (TableSize - 1)))
{
UpRateIdx = CurrRateIdx + 1;
DownRateIdx = CurrRateIdx -1;
}
else if (CurrRateIdx == 0)
{
UpRateIdx = CurrRateIdx + 1;
DownRateIdx = CurrRateIdx;
}
else if (CurrRateIdx == (TableSize - 1))
{
UpRateIdx = CurrRateIdx;
DownRateIdx = CurrRateIdx - 1;
}
pCurrTxRate = (PRTMP_TX_RATE_SWITCH) &pTable[(CurrRateIdx+1)*5];
if ((Rssi > -65) && (pCurrTxRate->Mode >= MODE_HTMIX))
{
TrainUp = (pCurrTxRate->TrainUp + (pCurrTxRate->TrainUp >> 1));
TrainDown = (pCurrTxRate->TrainDown + (pCurrTxRate->TrainDown >> 1));
}
else
{
TrainUp = pCurrTxRate->TrainUp;
TrainDown = pCurrTxRate->TrainDown;
}
//pAd->DrsCounters.LastTimeTxRateChangeAction = pAd->DrsCounters.LastSecTxRateChangeAction;
//
// Keep the last time TxRateChangeAction status.
//
pEntry->LastTimeTxRateChangeAction = pEntry->LastSecTxRateChangeAction;
//
// CASE 1. when TX samples are fewer than 15, then decide TX rate solely on RSSI
// (criteria copied from RT2500 for Netopia case)
//
if (TxTotalCnt <= 15)
{
CHAR idx = 0;
UCHAR TxRateIdx;
//UCHAR MCS0 = 0, MCS1 = 0, MCS2 = 0, MCS3 = 0, MCS4 = 0, MCS7 = 0, MCS12 = 0, MCS13 = 0, MCS14 = 0, MCS15 = 0;
UCHAR MCS0 = 0, MCS1 = 0, MCS2 = 0, MCS3 = 0, MCS4 = 0, MCS5 =0, MCS6 = 0, MCS7 = 0;
UCHAR MCS12 = 0, MCS13 = 0, MCS14 = 0, MCS15 = 0;
UCHAR MCS20 = 0, MCS21 = 0, MCS22 = 0, MCS23 = 0; // 3*3
// check the existence and index of each needed MCS
while (idx < pTable[0])
{
pCurrTxRate = (PRTMP_TX_RATE_SWITCH) &pTable[(idx+1)*5];
if (pCurrTxRate->CurrMCS == MCS_0)
{
MCS0 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_1)
{
MCS1 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_2)
{
MCS2 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_3)
{
MCS3 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_4)
{
MCS4 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_5)
{
MCS5 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_6)
{
MCS6 = idx;
}
//else if (pCurrTxRate->CurrMCS == MCS_7)
else if ((pCurrTxRate->CurrMCS == MCS_7) && (pCurrTxRate->ShortGI == GI_800)) // prevent the highest MCS using short GI when 1T and low throughput
{
MCS7 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_12)
{
MCS12 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_13)
{
MCS13 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_14)
{
MCS14 = idx;
}
else if ((pCurrTxRate->CurrMCS == MCS_15) && (pCurrTxRate->ShortGI == GI_800)) //we hope to use ShortGI as initial rate, however Atheros's chip has bugs when short GI
{
MCS15 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_20) // 3*3
{
MCS20 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_21)
{
MCS21 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_22)
{
MCS22 = idx;
}
else if (pCurrTxRate->CurrMCS == MCS_23)
{
MCS23 = idx;
}
idx ++;
}
if (pAd->LatchRfRegs.Channel <= 14)
{
if (pAd->NicConfig2.field.ExternalLNAForG)
{
RssiOffset = 2;
}
else
{
RssiOffset = 5;
}
}
else
{
if (pAd->NicConfig2.field.ExternalLNAForA)
{
RssiOffset = 5;
}
else
{
RssiOffset = 8;
}
}
/*if (MCS15)*/
if ((pTable == RateSwitchTable11BGN3S) ||
(pTable == RateSwitchTable11N3S) ||
(pTable == RateSwitchTable))
{// N mode with 3 stream // 3*3
if (MCS23 && (Rssi >= -70))
TxRateIdx = MCS15;
else if (MCS22 && (Rssi >= -72))
TxRateIdx = MCS14;
else if (MCS21 && (Rssi >= -76))
TxRateIdx = MCS13;
else if (MCS20 && (Rssi >= -78))
TxRateIdx = MCS12;
else if (MCS4 && (Rssi >= -82))
TxRateIdx = MCS4;
else if (MCS3 && (Rssi >= -84))
TxRateIdx = MCS3;
else if (MCS2 && (Rssi >= -86))
TxRateIdx = MCS2;
else if (MCS1 && (Rssi >= -88))
TxRateIdx = MCS1;
else
TxRateIdx = MCS0;
}
else if ((pTable == RateSwitchTable11BGN2S) || (pTable == RateSwitchTable11BGN2SForABand) ||(pTable == RateSwitchTable11N2S) ||(pTable == RateSwitchTable11N2SForABand)) // 3*3
{// N mode with 2 stream
if (MCS15 && (Rssi >= (-70+RssiOffset)))
TxRateIdx = MCS15;
else if (MCS14 && (Rssi >= (-72+RssiOffset)))
TxRateIdx = MCS14;
else if (MCS13 && (Rssi >= (-76+RssiOffset)))
TxRateIdx = MCS13;
else if (MCS12 && (Rssi >= (-78+RssiOffset)))
TxRateIdx = MCS12;
else if (MCS4 && (Rssi >= (-82+RssiOffset)))
TxRateIdx = MCS4;
else if (MCS3 && (Rssi >= (-84+RssiOffset)))
TxRateIdx = MCS3;
else if (MCS2 && (Rssi >= (-86+RssiOffset)))
TxRateIdx = MCS2;
else if (MCS1 && (Rssi >= (-88+RssiOffset)))
TxRateIdx = MCS1;
else
TxRateIdx = MCS0;
}
else if ((pTable == RateSwitchTable11BGN1S) || (pTable == RateSwitchTable11N1S))
{// N mode with 1 stream
if (MCS7 && (Rssi > (-72+RssiOffset)))
TxRateIdx = MCS7;
else if (MCS6 && (Rssi > (-74+RssiOffset)))
TxRateIdx = MCS6;
else if (MCS5 && (Rssi > (-77+RssiOffset)))
TxRateIdx = MCS5;
else if (MCS4 && (Rssi > (-79+RssiOffset)))
TxRateIdx = MCS4;
else if (MCS3 && (Rssi > (-81+RssiOffset)))
TxRateIdx = MCS3;
else if (MCS2 && (Rssi > (-83+RssiOffset)))
TxRateIdx = MCS2;
else if (MCS1 && (Rssi > (-86+RssiOffset)))
TxRateIdx = MCS1;
else
TxRateIdx = MCS0;
}
else
{// Legacy mode
if (MCS7 && (Rssi > -70))
TxRateIdx = MCS7;
else if (MCS6 && (Rssi > -74))
TxRateIdx = MCS6;
else if (MCS5 && (Rssi > -78))
TxRateIdx = MCS5;
else if (MCS4 && (Rssi > -82))
TxRateIdx = MCS4;
else if (MCS4 == 0) // for B-only mode
TxRateIdx = MCS3;
else if (MCS3 && (Rssi > -85))
TxRateIdx = MCS3;
else if (MCS2 && (Rssi > -87))
TxRateIdx = MCS2;
else if (MCS1 && (Rssi > -90))
TxRateIdx = MCS1;
else
TxRateIdx = MCS0;
}
{
pEntry->CurrTxRateIndex = TxRateIdx;
pNextTxRate = (PRTMP_TX_RATE_SWITCH) &pTable[(pEntry->CurrTxRateIndex+1)*5];
MlmeSetTxRate(pAd, pEntry, pNextTxRate);
}
NdisZeroMemory(pEntry->TxQuality, sizeof(USHORT) * MAX_STEP_OF_TX_RATE_SWITCH);
NdisZeroMemory(pEntry->PER, sizeof(UCHAR) * MAX_STEP_OF_TX_RATE_SWITCH);
pEntry->fLastSecAccordingRSSI = TRUE;
// reset all OneSecTx counters
RESET_ONE_SEC_TX_CNT(pEntry);
continue;
}
if (pEntry->fLastSecAccordingRSSI == TRUE)
{
pEntry->fLastSecAccordingRSSI = FALSE;
pEntry->LastSecTxRateChangeAction = 0;
// reset all OneSecTx counters
RESET_ONE_SEC_TX_CNT(pEntry);
continue;
}
do
{
BOOLEAN bTrainUpDown = FALSE;
pEntry->CurrTxRateStableTime ++;
// downgrade TX quality if PER >= Rate-Down threshold
if (TxErrorRatio >= TrainDown)
{
bTrainUpDown = TRUE;
pEntry->TxQuality[CurrRateIdx] = DRS_TX_QUALITY_WORST_BOUND;
}
// upgrade TX quality if PER <= Rate-Up threshold
else if (TxErrorRatio <= TrainUp)
{
bTrainUpDown = TRUE;
bUpgradeQuality = TRUE;
if (pEntry->TxQuality[CurrRateIdx])
pEntry->TxQuality[CurrRateIdx] --; // quality very good in CurrRate
if (pEntry->TxRateUpPenalty)
pEntry->TxRateUpPenalty --;
else if (pEntry->TxQuality[UpRateIdx])
pEntry->TxQuality[UpRateIdx] --; // may improve next UP rate's quality
}
pEntry->PER[CurrRateIdx] = (UCHAR)TxErrorRatio;
if (bTrainUpDown)
{
// perform DRS - consider TxRate Down first, then rate up.
if ((CurrRateIdx != DownRateIdx) && (pEntry->TxQuality[CurrRateIdx] >= DRS_TX_QUALITY_WORST_BOUND))
{
pEntry->CurrTxRateIndex = DownRateIdx;
}
else if ((CurrRateIdx != UpRateIdx) && (pEntry->TxQuality[UpRateIdx] <= 0))
{
pEntry->CurrTxRateIndex = UpRateIdx;
}
}
} while (FALSE);
// if rate-up happen, clear all bad history of all TX rates
if (pEntry->CurrTxRateIndex > CurrRateIdx)
{
pEntry->CurrTxRateStableTime = 0;
pEntry->TxRateUpPenalty = 0;
pEntry->LastSecTxRateChangeAction = 1; // rate UP
NdisZeroMemory(pEntry->TxQuality, sizeof(USHORT) * MAX_STEP_OF_TX_RATE_SWITCH);
NdisZeroMemory(pEntry->PER, sizeof(UCHAR) * MAX_STEP_OF_TX_RATE_SWITCH);
//
// For TxRate fast train up
//
if (!pAd->StaCfg.StaQuickResponeForRateUpTimerRunning)
{
RTMPSetTimer(&pAd->StaCfg.StaQuickResponeForRateUpTimer, 100);
pAd->StaCfg.StaQuickResponeForRateUpTimerRunning = TRUE;
}
bTxRateChanged = TRUE;
}
// if rate-down happen, only clear DownRate's bad history
else if (pEntry->CurrTxRateIndex < CurrRateIdx)
{
pEntry->CurrTxRateStableTime = 0;
pEntry->TxRateUpPenalty = 0; // no penalty
pEntry->LastSecTxRateChangeAction = 2; // rate DOWN
pEntry->TxQuality[pEntry->CurrTxRateIndex] = 0;
pEntry->PER[pEntry->CurrTxRateIndex] = 0;
//
// For TxRate fast train down
//
if (!pAd->StaCfg.StaQuickResponeForRateUpTimerRunning)
{
RTMPSetTimer(&pAd->StaCfg.StaQuickResponeForRateUpTimer, 100);
pAd->StaCfg.StaQuickResponeForRateUpTimerRunning = TRUE;
}
bTxRateChanged = TRUE;
}
else
{
pEntry->LastSecTxRateChangeAction = 0; // rate no change
bTxRateChanged = FALSE;
}
pEntry->LastTxOkCount = TxSuccess;
// reset all OneSecTx counters
RESET_ONE_SEC_TX_CNT(pEntry);
pNextTxRate = (PRTMP_TX_RATE_SWITCH) &pTable[(pEntry->CurrTxRateIndex+1)*5];
if (bTxRateChanged && pNextTxRate)
{
MlmeSetTxRate(pAd, pEntry, pNextTxRate);
}
}
}
/*
========================================================================
Routine Description:
Station side, Auto TxRate faster train up timer call back function.
Arguments:
SystemSpecific1 - Not used.
FunctionContext - Pointer to our Adapter context.
SystemSpecific2 - Not used.
SystemSpecific3 - Not used.
Return Value:
None
========================================================================
*/
VOID StaQuickResponeForRateUpExec(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3)
{
PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)FunctionContext;
UCHAR UpRateIdx = 0, DownRateIdx = 0, CurrRateIdx = 0;
ULONG TxTotalCnt;
ULONG TxErrorRatio = 0;
#ifdef RT2860
BOOLEAN bTxRateChanged = TRUE; //, bUpgradeQuality = FALSE;
#endif
#ifdef RT2870
BOOLEAN bTxRateChanged; //, bUpgradeQuality = FALSE;
#endif
PRTMP_TX_RATE_SWITCH pCurrTxRate, pNextTxRate = NULL;
PUCHAR pTable;
UCHAR TableSize = 0;
UCHAR InitTxRateIdx = 0, TrainUp, TrainDown;
TX_STA_CNT1_STRUC StaTx1;
TX_STA_CNT0_STRUC TxStaCnt0;
CHAR Rssi, ratio;
ULONG TxRetransmit = 0, TxSuccess = 0, TxFailCount = 0;
MAC_TABLE_ENTRY *pEntry;
ULONG i;
pAd->StaCfg.StaQuickResponeForRateUpTimerRunning = FALSE;
//
// walk through MAC table, see if need to change AP's TX rate toward each entry
//
for (i = 1; i < MAX_LEN_OF_MAC_TABLE; i++)
{
pEntry = &pAd->MacTab.Content[i];
// check if this entry need to switch rate automatically
if (RTMPCheckEntryEnableAutoRateSwitch(pAd, pEntry) == FALSE)
continue;
#ifdef RT2860
//Rssi = RTMPMaxRssi(pAd, (CHAR)pAd->StaCfg.AvgRssi0, (CHAR)pAd->StaCfg.AvgRssi1, (CHAR)pAd->StaCfg.AvgRssi2);
if (pAd->Antenna.field.TxPath > 1)
Rssi = (pAd->StaCfg.RssiSample.AvgRssi0 + pAd->StaCfg.RssiSample.AvgRssi1) >> 1;
else
Rssi = pAd->StaCfg.RssiSample.AvgRssi0;
#endif
#ifdef RT2870
if (INFRA_ON(pAd) && (i == 1))
Rssi = RTMPMaxRssi(pAd,
pAd->StaCfg.RssiSample.AvgRssi0,
pAd->StaCfg.RssiSample.AvgRssi1,
pAd->StaCfg.RssiSample.AvgRssi2);
else
Rssi = RTMPMaxRssi(pAd,
pEntry->RssiSample.AvgRssi0,
pEntry->RssiSample.AvgRssi1,
pEntry->RssiSample.AvgRssi2);
#endif
CurrRateIdx = pAd->CommonCfg.TxRateIndex;
MlmeSelectTxRateTable(pAd, pEntry, &pTable, &TableSize, &InitTxRateIdx);
// decide the next upgrade rate and downgrade rate, if any
if ((CurrRateIdx > 0) && (CurrRateIdx < (TableSize - 1)))
{
UpRateIdx = CurrRateIdx + 1;
DownRateIdx = CurrRateIdx -1;
}
else if (CurrRateIdx == 0)
{
UpRateIdx = CurrRateIdx + 1;
DownRateIdx = CurrRateIdx;
}
else if (CurrRateIdx == (TableSize - 1))
{
UpRateIdx = CurrRateIdx;
DownRateIdx = CurrRateIdx - 1;
}
pCurrTxRate = (PRTMP_TX_RATE_SWITCH) &pTable[(CurrRateIdx+1)*5];
if ((Rssi > -65) && (pCurrTxRate->Mode >= MODE_HTMIX))
{
TrainUp = (pCurrTxRate->TrainUp + (pCurrTxRate->TrainUp >> 1));
TrainDown = (pCurrTxRate->TrainDown + (pCurrTxRate->TrainDown >> 1));
}
else
{
TrainUp = pCurrTxRate->TrainUp;
TrainDown = pCurrTxRate->TrainDown;
}
if (pAd->MacTab.Size == 1)
{
// Update statistic counter
RTMP_IO_READ32(pAd, TX_STA_CNT0, &TxStaCnt0.word);
RTMP_IO_READ32(pAd, TX_STA_CNT1, &StaTx1.word);
TxRetransmit = StaTx1.field.TxRetransmit;
TxSuccess = StaTx1.field.TxSuccess;
TxFailCount = TxStaCnt0.field.TxFailCount;
TxTotalCnt = TxRetransmit + TxSuccess + TxFailCount;
pAd->RalinkCounters.OneSecTxRetryOkCount += StaTx1.field.TxRetransmit;
pAd->RalinkCounters.OneSecTxNoRetryOkCount += StaTx1.field.TxSuccess;
pAd->RalinkCounters.OneSecTxFailCount += TxStaCnt0.field.TxFailCount;
pAd->WlanCounters.TransmittedFragmentCount.u.LowPart += StaTx1.field.TxSuccess;
pAd->WlanCounters.RetryCount.u.LowPart += StaTx1.field.TxRetransmit;
pAd->WlanCounters.FailedCount.u.LowPart += TxStaCnt0.field.TxFailCount;
if (TxTotalCnt)
TxErrorRatio = ((TxRetransmit + TxFailCount) * 100) / TxTotalCnt;
}
else
{
TxTotalCnt = pEntry->OneSecTxNoRetryOkCount +
pEntry->OneSecTxRetryOkCount +
pEntry->OneSecTxFailCount;
if (TxTotalCnt)
TxErrorRatio = ((pEntry->OneSecTxRetryOkCount + pEntry->OneSecTxFailCount) * 100) / TxTotalCnt;
}
//
// CASE 1. when TX samples are fewer than 15, then decide TX rate solely on RSSI
// (criteria copied from RT2500 for Netopia case)
//
if (TxTotalCnt <= 12)
{
NdisZeroMemory(pAd->DrsCounters.TxQuality, sizeof(USHORT) * MAX_STEP_OF_TX_RATE_SWITCH);
NdisZeroMemory(pAd->DrsCounters.PER, sizeof(UCHAR) * MAX_STEP_OF_TX_RATE_SWITCH);
if ((pAd->DrsCounters.LastSecTxRateChangeAction == 1) && (CurrRateIdx != DownRateIdx))
{
pAd->CommonCfg.TxRateIndex = DownRateIdx;
pAd->DrsCounters.TxQuality[CurrRateIdx] = DRS_TX_QUALITY_WORST_BOUND;
}
else if ((pAd->DrsCounters.LastSecTxRateChangeAction == 2) && (CurrRateIdx != UpRateIdx))
{
pAd->CommonCfg.TxRateIndex = UpRateIdx;
}
DBGPRINT_RAW(RT_DEBUG_TRACE,("QuickDRS: TxTotalCnt <= 15, train back to original rate \n"));
return;
}
do
{
ULONG OneSecTxNoRetryOKRationCount;
if (pAd->DrsCounters.LastTimeTxRateChangeAction == 0)
ratio = 5;
else
ratio = 4;
// downgrade TX quality if PER >= Rate-Down threshold
if (TxErrorRatio >= TrainDown)
{
pAd->DrsCounters.TxQuality[CurrRateIdx] = DRS_TX_QUALITY_WORST_BOUND;
}
pAd->DrsCounters.PER[CurrRateIdx] = (UCHAR)TxErrorRatio;
OneSecTxNoRetryOKRationCount = (TxSuccess * ratio);
// perform DRS - consider TxRate Down first, then rate up.
if ((pAd->DrsCounters.LastSecTxRateChangeAction == 1) && (CurrRateIdx != DownRateIdx))
{
if ((pAd->DrsCounters.LastTxOkCount + 2) >= OneSecTxNoRetryOKRationCount)
{
pAd->CommonCfg.TxRateIndex = DownRateIdx;
pAd->DrsCounters.TxQuality[CurrRateIdx] = DRS_TX_QUALITY_WORST_BOUND;
}
}
else if ((pAd->DrsCounters.LastSecTxRateChangeAction == 2) && (