blob: 12d2125148ba0a2d4d2522977be9e996be600a74 [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:
action.c
Abstract:
Handle association related requests either from WSTA or from local MLME
Revision History:
Who When What
--------- ---------- ----------------------------------------------
Fonchi Wu 2008 created for 802.11h
*/
#include "../rt_config.h"
#include "../action.h"
/* The regulatory information in the USA (US) */
DOT11_REGULATORY_INFORMATION USARegulatoryInfo[] =
{
/* "regulatory class" "number of channels" "Max Tx Pwr" "channel list" */
{0, {0, 0, {0}}}, // Invlid entry
{1, {4, 16, {36, 40, 44, 48}}},
{2, {4, 23, {52, 56, 60, 64}}},
{3, {4, 29, {149, 153, 157, 161}}},
{4, {11, 23, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}},
{5, {5, 30, {149, 153, 157, 161, 165}}},
{6, {10, 14, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}},
{7, {10, 27, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}},
{8, {5, 17, {11, 13, 15, 17, 19}}},
{9, {5, 30, {11, 13, 15, 17, 19}}},
{10, {2, 20, {21, 25}}},
{11, {2, 33, {21, 25}}},
{12, {11, 30, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}}
};
#define USA_REGULATORY_INFO_SIZE (sizeof(USARegulatoryInfo) / sizeof(DOT11_REGULATORY_INFORMATION))
/* The regulatory information in Europe */
DOT11_REGULATORY_INFORMATION EuropeRegulatoryInfo[] =
{
/* "regulatory class" "number of channels" "Max Tx Pwr" "channel list" */
{0, {0, 0, {0}}}, // Invalid entry
{1, {4, 20, {36, 40, 44, 48}}},
{2, {4, 20, {52, 56, 60, 64}}},
{3, {11, 30, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}},
{4, {13, 20, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}}
};
#define EU_REGULATORY_INFO_SIZE (sizeof(EuropeRegulatoryInfo) / sizeof(DOT11_REGULATORY_INFORMATION))
/* The regulatory information in Japan */
DOT11_REGULATORY_INFORMATION JapanRegulatoryInfo[] =
{
/* "regulatory class" "number of channels" "Max Tx Pwr" "channel list" */
{0, {0, 0, {0}}}, // Invalid entry
{1, {4, 22, {34, 38, 42, 46}}},
{2, {3, 24, {8, 12, 16}}},
{3, {3, 24, {8, 12, 16}}},
{4, {3, 24, {8, 12, 16}}},
{5, {3, 24, {8, 12, 16}}},
{6, {3, 22, {8, 12, 16}}},
{7, {4, 24, {184, 188, 192, 196}}},
{8, {4, 24, {184, 188, 192, 196}}},
{9, {4, 24, {184, 188, 192, 196}}},
{10, {4, 24, {184, 188, 192, 196}}},
{11, {4, 22, {184, 188, 192, 196}}},
{12, {4, 24, {7, 8, 9, 11}}},
{13, {4, 24, {7, 8, 9, 11}}},
{14, {4, 24, {7, 8, 9, 11}}},
{15, {4, 24, {7, 8, 9, 11}}},
{16, {6, 24, {183, 184, 185, 187, 188, 189}}},
{17, {6, 24, {183, 184, 185, 187, 188, 189}}},
{18, {6, 24, {183, 184, 185, 187, 188, 189}}},
{19, {6, 24, {183, 184, 185, 187, 188, 189}}},
{20, {6, 17, {183, 184, 185, 187, 188, 189}}},
{21, {6, 24, {6, 7, 8, 9, 10, 11}}},
{22, {6, 24, {6, 7, 8, 9, 10, 11}}},
{23, {6, 24, {6, 7, 8, 9, 10, 11}}},
{24, {6, 24, {6, 7, 8, 9, 10, 11}}},
{25, {8, 24, {182, 183, 184, 185, 186, 187, 188, 189}}},
{26, {8, 24, {182, 183, 184, 185, 186, 187, 188, 189}}},
{27, {8, 24, {182, 183, 184, 185, 186, 187, 188, 189}}},
{28, {8, 24, {182, 183, 184, 185, 186, 187, 188, 189}}},
{29, {8, 17, {182, 183, 184, 185, 186, 187, 188, 189}}},
{30, {13, 23, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}},
{31, {1, 23, {14}}},
{32, {4, 22, {52, 56, 60, 64}}}
};
#define JP_REGULATORY_INFO_SIZE (sizeof(JapanRegulatoryInfo) / sizeof(DOT11_REGULATORY_INFORMATION))
CHAR RTMP_GetTxPwr(
IN PRTMP_ADAPTER pAd,
IN HTTRANSMIT_SETTING HTTxMode)
{
typedef struct __TX_PWR_CFG
{
UINT8 Mode;
UINT8 MCS;
UINT16 req;
UINT8 shift;
UINT32 BitMask;
} TX_PWR_CFG;
UINT32 Value;
INT Idx;
UINT8 PhyMode;
CHAR CurTxPwr;
UINT8 TxPwrRef = 0;
CHAR DaltaPwr;
ULONG TxPwr[5];
TX_PWR_CFG TxPwrCfg[] = {
{MODE_CCK, 0, 0, 4, 0x000000f0},
{MODE_CCK, 1, 0, 0, 0x0000000f},
{MODE_CCK, 2, 0, 12, 0x0000f000},
{MODE_CCK, 3, 0, 8, 0x00000f00},
{MODE_OFDM, 0, 0, 20, 0x00f00000},
{MODE_OFDM, 1, 0, 16, 0x000f0000},
{MODE_OFDM, 2, 0, 28, 0xf0000000},
{MODE_OFDM, 3, 0, 24, 0x0f000000},
{MODE_OFDM, 4, 1, 4, 0x000000f0},
{MODE_OFDM, 5, 1, 0, 0x0000000f},
{MODE_OFDM, 6, 1, 12, 0x0000f000},
{MODE_OFDM, 7, 1, 8, 0x00000f00}
#ifdef DOT11_N_SUPPORT
,{MODE_HTMIX, 0, 1, 20, 0x00f00000},
{MODE_HTMIX, 1, 1, 16, 0x000f0000},
{MODE_HTMIX, 2, 1, 28, 0xf0000000},
{MODE_HTMIX, 3, 1, 24, 0x0f000000},
{MODE_HTMIX, 4, 2, 4, 0x000000f0},
{MODE_HTMIX, 5, 2, 0, 0x0000000f},
{MODE_HTMIX, 6, 2, 12, 0x0000f000},
{MODE_HTMIX, 7, 2, 8, 0x00000f00},
{MODE_HTMIX, 8, 2, 20, 0x00f00000},
{MODE_HTMIX, 9, 2, 16, 0x000f0000},
{MODE_HTMIX, 10, 2, 28, 0xf0000000},
{MODE_HTMIX, 11, 2, 24, 0x0f000000},
{MODE_HTMIX, 12, 3, 4, 0x000000f0},
{MODE_HTMIX, 13, 3, 0, 0x0000000f},
{MODE_HTMIX, 14, 3, 12, 0x0000f000},
{MODE_HTMIX, 15, 3, 8, 0x00000f00}
#endif // DOT11_N_SUPPORT //
};
#define MAX_TXPWR_TAB_SIZE (sizeof(TxPwrCfg) / sizeof(TX_PWR_CFG))
#ifdef SINGLE_SKU
CurTxPwr = pAd->CommonCfg.DefineMaxTxPwr;
#else
CurTxPwr = 19;
#endif
/* check Tx Power setting from UI. */
if (pAd->CommonCfg.TxPowerPercentage > 90)
;
else if (pAd->CommonCfg.TxPowerPercentage > 60) /* reduce Pwr for 1 dB. */
CurTxPwr -= 1;
else if (pAd->CommonCfg.TxPowerPercentage > 30) /* reduce Pwr for 3 dB. */
CurTxPwr -= 3;
else if (pAd->CommonCfg.TxPowerPercentage > 15) /* reduce Pwr for 6 dB. */
CurTxPwr -= 6;
else if (pAd->CommonCfg.TxPowerPercentage > 9) /* reduce Pwr for 9 dB. */
CurTxPwr -= 9;
else /* reduce Pwr for 12 dB. */
CurTxPwr -= 12;
if (pAd->CommonCfg.BBPCurrentBW == BW_40)
{
if (pAd->CommonCfg.CentralChannel > 14)
{
TxPwr[0] = pAd->Tx40MPwrCfgABand[0];
TxPwr[1] = pAd->Tx40MPwrCfgABand[1];
TxPwr[2] = pAd->Tx40MPwrCfgABand[2];
TxPwr[3] = pAd->Tx40MPwrCfgABand[3];
TxPwr[4] = pAd->Tx40MPwrCfgABand[4];
}
else
{
TxPwr[0] = pAd->Tx40MPwrCfgGBand[0];
TxPwr[1] = pAd->Tx40MPwrCfgGBand[1];
TxPwr[2] = pAd->Tx40MPwrCfgGBand[2];
TxPwr[3] = pAd->Tx40MPwrCfgGBand[3];
TxPwr[4] = pAd->Tx40MPwrCfgGBand[4];
}
}
else
{
if (pAd->CommonCfg.Channel > 14)
{
TxPwr[0] = pAd->Tx20MPwrCfgABand[0];
TxPwr[1] = pAd->Tx20MPwrCfgABand[1];
TxPwr[2] = pAd->Tx20MPwrCfgABand[2];
TxPwr[3] = pAd->Tx20MPwrCfgABand[3];
TxPwr[4] = pAd->Tx20MPwrCfgABand[4];
}
else
{
TxPwr[0] = pAd->Tx20MPwrCfgGBand[0];
TxPwr[1] = pAd->Tx20MPwrCfgGBand[1];
TxPwr[2] = pAd->Tx20MPwrCfgGBand[2];
TxPwr[3] = pAd->Tx20MPwrCfgGBand[3];
TxPwr[4] = pAd->Tx20MPwrCfgGBand[4];
}
}
switch(HTTxMode.field.MODE)
{
case MODE_CCK:
case MODE_OFDM:
Value = TxPwr[1];
TxPwrRef = (Value & 0x00000f00) >> 8;
break;
#ifdef DOT11_N_SUPPORT
case MODE_HTMIX:
case MODE_HTGREENFIELD:
if (pAd->CommonCfg.TxStream == 1)
{
Value = TxPwr[2];
TxPwrRef = (Value & 0x00000f00) >> 8;
}
else if (pAd->CommonCfg.TxStream == 2)
{
Value = TxPwr[3];
TxPwrRef = (Value & 0x00000f00) >> 8;
}
break;
#endif // DOT11_N_SUPPORT //
}
PhyMode =
#ifdef DOT11_N_SUPPORT
(HTTxMode.field.MODE == MODE_HTGREENFIELD)
? MODE_HTMIX :
#endif // DOT11_N_SUPPORT //
HTTxMode.field.MODE;
for (Idx = 0; Idx < MAX_TXPWR_TAB_SIZE; Idx++)
{
if ((TxPwrCfg[Idx].Mode == PhyMode)
&& (TxPwrCfg[Idx].MCS == HTTxMode.field.MCS))
{
Value = TxPwr[TxPwrCfg[Idx].req];
DaltaPwr = TxPwrRef - (CHAR)((Value & TxPwrCfg[Idx].BitMask)
>> TxPwrCfg[Idx].shift);
CurTxPwr -= DaltaPwr;
break;
}
}
return CurTxPwr;
}
VOID MeasureReqTabInit(
IN PRTMP_ADAPTER pAd)
{
NdisAllocateSpinLock(&pAd->CommonCfg.MeasureReqTabLock);
pAd->CommonCfg.pMeasureReqTab = kmalloc(sizeof(MEASURE_REQ_TAB), GFP_ATOMIC);
if (pAd->CommonCfg.pMeasureReqTab)
NdisZeroMemory(pAd->CommonCfg.pMeasureReqTab, sizeof(MEASURE_REQ_TAB));
else
DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __FUNCTION__));
return;
}
VOID MeasureReqTabExit(
IN PRTMP_ADAPTER pAd)
{
NdisFreeSpinLock(&pAd->CommonCfg.MeasureReqTabLock);
if (pAd->CommonCfg.pMeasureReqTab)
kfree(pAd->CommonCfg.pMeasureReqTab);
pAd->CommonCfg.pMeasureReqTab = NULL;
return;
}
PMEASURE_REQ_ENTRY MeasureReqLookUp(
IN PRTMP_ADAPTER pAd,
IN UINT8 DialogToken)
{
UINT HashIdx;
PMEASURE_REQ_TAB pTab = pAd->CommonCfg.pMeasureReqTab;
PMEASURE_REQ_ENTRY pEntry = NULL;
PMEASURE_REQ_ENTRY pPrevEntry = NULL;
if (pTab == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__));
return NULL;
}
RTMP_SEM_LOCK(&pAd->CommonCfg.MeasureReqTabLock);
HashIdx = MQ_DIALOGTOKEN_HASH_INDEX(DialogToken);
pEntry = pTab->Hash[HashIdx];
while (pEntry)
{
if (pEntry->DialogToken == DialogToken)
break;
else
{
pPrevEntry = pEntry;
pEntry = pEntry->pNext;
}
}
RTMP_SEM_UNLOCK(&pAd->CommonCfg.MeasureReqTabLock);
return pEntry;
}
PMEASURE_REQ_ENTRY MeasureReqInsert(
IN PRTMP_ADAPTER pAd,
IN UINT8 DialogToken)
{
INT i;
ULONG HashIdx;
PMEASURE_REQ_TAB pTab = pAd->CommonCfg.pMeasureReqTab;
PMEASURE_REQ_ENTRY pEntry = NULL, pCurrEntry;
ULONG Now;
if(pTab == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__));
return NULL;
}
pEntry = MeasureReqLookUp(pAd, DialogToken);
if (pEntry == NULL)
{
RTMP_SEM_LOCK(&pAd->CommonCfg.MeasureReqTabLock);
for (i = 0; i < MAX_MEASURE_REQ_TAB_SIZE; i++)
{
NdisGetSystemUpTime(&Now);
pEntry = &pTab->Content[i];
if ((pEntry->Valid == TRUE)
&& RTMP_TIME_AFTER((unsigned long)Now, (unsigned long)(pEntry->lastTime + MQ_REQ_AGE_OUT)))
{
PMEASURE_REQ_ENTRY pPrevEntry = NULL;
ULONG HashIdx = MQ_DIALOGTOKEN_HASH_INDEX(pEntry->DialogToken);
PMEASURE_REQ_ENTRY pProbeEntry = pTab->Hash[HashIdx];
// update Hash list
do
{
if (pProbeEntry == pEntry)
{
if (pPrevEntry == NULL)
{
pTab->Hash[HashIdx] = pEntry->pNext;
}
else
{
pPrevEntry->pNext = pEntry->pNext;
}
break;
}
pPrevEntry = pProbeEntry;
pProbeEntry = pProbeEntry->pNext;
} while (pProbeEntry);
NdisZeroMemory(pEntry, sizeof(MEASURE_REQ_ENTRY));
pTab->Size--;
break;
}
if (pEntry->Valid == FALSE)
break;
}
if (i < MAX_MEASURE_REQ_TAB_SIZE)
{
NdisGetSystemUpTime(&Now);
pEntry->lastTime = Now;
pEntry->Valid = TRUE;
pEntry->DialogToken = DialogToken;
pTab->Size++;
}
else
{
pEntry = NULL;
DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __FUNCTION__));
}
// add this Neighbor entry into HASH table
if (pEntry)
{
HashIdx = MQ_DIALOGTOKEN_HASH_INDEX(DialogToken);
if (pTab->Hash[HashIdx] == NULL)
{
pTab->Hash[HashIdx] = pEntry;
}
else
{
pCurrEntry = pTab->Hash[HashIdx];
while (pCurrEntry->pNext != NULL)
pCurrEntry = pCurrEntry->pNext;
pCurrEntry->pNext = pEntry;
}
}
RTMP_SEM_UNLOCK(&pAd->CommonCfg.MeasureReqTabLock);
}
return pEntry;
}
VOID MeasureReqDelete(
IN PRTMP_ADAPTER pAd,
IN UINT8 DialogToken)
{
PMEASURE_REQ_TAB pTab = pAd->CommonCfg.pMeasureReqTab;
PMEASURE_REQ_ENTRY pEntry = NULL;
if(pTab == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__));
return;
}
// if empty, return
if (pTab->Size == 0)
{
DBGPRINT(RT_DEBUG_ERROR, ("pMeasureReqTab empty.\n"));
return;
}
pEntry = MeasureReqLookUp(pAd, DialogToken);
if (pEntry != NULL)
{
PMEASURE_REQ_ENTRY pPrevEntry = NULL;
ULONG HashIdx = MQ_DIALOGTOKEN_HASH_INDEX(pEntry->DialogToken);
PMEASURE_REQ_ENTRY pProbeEntry = pTab->Hash[HashIdx];
RTMP_SEM_LOCK(&pAd->CommonCfg.MeasureReqTabLock);
// update Hash list
do
{
if (pProbeEntry == pEntry)
{
if (pPrevEntry == NULL)
{
pTab->Hash[HashIdx] = pEntry->pNext;
}
else
{
pPrevEntry->pNext = pEntry->pNext;
}
break;
}
pPrevEntry = pProbeEntry;
pProbeEntry = pProbeEntry->pNext;
} while (pProbeEntry);
NdisZeroMemory(pEntry, sizeof(MEASURE_REQ_ENTRY));
pTab->Size--;
RTMP_SEM_UNLOCK(&pAd->CommonCfg.MeasureReqTabLock);
}
return;
}
VOID TpcReqTabInit(
IN PRTMP_ADAPTER pAd)
{
NdisAllocateSpinLock(&pAd->CommonCfg.TpcReqTabLock);
pAd->CommonCfg.pTpcReqTab = kmalloc(sizeof(TPC_REQ_TAB), GFP_ATOMIC);
if (pAd->CommonCfg.pTpcReqTab)
NdisZeroMemory(pAd->CommonCfg.pTpcReqTab, sizeof(TPC_REQ_TAB));
else
DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __FUNCTION__));
return;
}
VOID TpcReqTabExit(
IN PRTMP_ADAPTER pAd)
{
NdisFreeSpinLock(&pAd->CommonCfg.TpcReqTabLock);
if (pAd->CommonCfg.pTpcReqTab)
kfree(pAd->CommonCfg.pTpcReqTab);
pAd->CommonCfg.pTpcReqTab = NULL;
return;
}
static PTPC_REQ_ENTRY TpcReqLookUp(
IN PRTMP_ADAPTER pAd,
IN UINT8 DialogToken)
{
UINT HashIdx;
PTPC_REQ_TAB pTab = pAd->CommonCfg.pTpcReqTab;
PTPC_REQ_ENTRY pEntry = NULL;
PTPC_REQ_ENTRY pPrevEntry = NULL;
if (pTab == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__));
return NULL;
}
RTMP_SEM_LOCK(&pAd->CommonCfg.TpcReqTabLock);
HashIdx = TPC_DIALOGTOKEN_HASH_INDEX(DialogToken);
pEntry = pTab->Hash[HashIdx];
while (pEntry)
{
if (pEntry->DialogToken == DialogToken)
break;
else
{
pPrevEntry = pEntry;
pEntry = pEntry->pNext;
}
}
RTMP_SEM_UNLOCK(&pAd->CommonCfg.TpcReqTabLock);
return pEntry;
}
static PTPC_REQ_ENTRY TpcReqInsert(
IN PRTMP_ADAPTER pAd,
IN UINT8 DialogToken)
{
INT i;
ULONG HashIdx;
PTPC_REQ_TAB pTab = pAd->CommonCfg.pTpcReqTab;
PTPC_REQ_ENTRY pEntry = NULL, pCurrEntry;
ULONG Now;
if(pTab == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__));
return NULL;
}
pEntry = TpcReqLookUp(pAd, DialogToken);
if (pEntry == NULL)
{
RTMP_SEM_LOCK(&pAd->CommonCfg.TpcReqTabLock);
for (i = 0; i < MAX_TPC_REQ_TAB_SIZE; i++)
{
NdisGetSystemUpTime(&Now);
pEntry = &pTab->Content[i];
if ((pEntry->Valid == TRUE)
&& RTMP_TIME_AFTER((unsigned long)Now, (unsigned long)(pEntry->lastTime + TPC_REQ_AGE_OUT)))
{
PTPC_REQ_ENTRY pPrevEntry = NULL;
ULONG HashIdx = TPC_DIALOGTOKEN_HASH_INDEX(pEntry->DialogToken);
PTPC_REQ_ENTRY pProbeEntry = pTab->Hash[HashIdx];
// update Hash list
do
{
if (pProbeEntry == pEntry)
{
if (pPrevEntry == NULL)
{
pTab->Hash[HashIdx] = pEntry->pNext;
}
else
{
pPrevEntry->pNext = pEntry->pNext;
}
break;
}
pPrevEntry = pProbeEntry;
pProbeEntry = pProbeEntry->pNext;
} while (pProbeEntry);
NdisZeroMemory(pEntry, sizeof(TPC_REQ_ENTRY));
pTab->Size--;
break;
}
if (pEntry->Valid == FALSE)
break;
}
if (i < MAX_TPC_REQ_TAB_SIZE)
{
NdisGetSystemUpTime(&Now);
pEntry->lastTime = Now;
pEntry->Valid = TRUE;
pEntry->DialogToken = DialogToken;
pTab->Size++;
}
else
{
pEntry = NULL;
DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __FUNCTION__));
}
// add this Neighbor entry into HASH table
if (pEntry)
{
HashIdx = TPC_DIALOGTOKEN_HASH_INDEX(DialogToken);
if (pTab->Hash[HashIdx] == NULL)
{
pTab->Hash[HashIdx] = pEntry;
}
else
{
pCurrEntry = pTab->Hash[HashIdx];
while (pCurrEntry->pNext != NULL)
pCurrEntry = pCurrEntry->pNext;
pCurrEntry->pNext = pEntry;
}
}
RTMP_SEM_UNLOCK(&pAd->CommonCfg.TpcReqTabLock);
}
return pEntry;
}
static VOID TpcReqDelete(
IN PRTMP_ADAPTER pAd,
IN UINT8 DialogToken)
{
PTPC_REQ_TAB pTab = pAd->CommonCfg.pTpcReqTab;
PTPC_REQ_ENTRY pEntry = NULL;
if(pTab == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__));
return;
}
// if empty, return
if (pTab->Size == 0)
{
DBGPRINT(RT_DEBUG_ERROR, ("pTpcReqTab empty.\n"));
return;
}
pEntry = TpcReqLookUp(pAd, DialogToken);
if (pEntry != NULL)
{
PTPC_REQ_ENTRY pPrevEntry = NULL;
ULONG HashIdx = TPC_DIALOGTOKEN_HASH_INDEX(pEntry->DialogToken);
PTPC_REQ_ENTRY pProbeEntry = pTab->Hash[HashIdx];
RTMP_SEM_LOCK(&pAd->CommonCfg.TpcReqTabLock);
// update Hash list
do
{
if (pProbeEntry == pEntry)
{
if (pPrevEntry == NULL)
{
pTab->Hash[HashIdx] = pEntry->pNext;
}
else
{
pPrevEntry->pNext = pEntry->pNext;
}
break;
}
pPrevEntry = pProbeEntry;
pProbeEntry = pProbeEntry->pNext;
} while (pProbeEntry);
NdisZeroMemory(pEntry, sizeof(TPC_REQ_ENTRY));
pTab->Size--;
RTMP_SEM_UNLOCK(&pAd->CommonCfg.TpcReqTabLock);
}
return;
}
/*
==========================================================================
Description:
Get Current TimeS tamp.
Parametrs:
Return : Current Time Stamp.
==========================================================================
*/
static UINT64 GetCurrentTimeStamp(
IN PRTMP_ADAPTER pAd)
{
// get current time stamp.
return 0;
}
/*
==========================================================================
Description:
Get Current Transmit Power.
Parametrs:
Return : Current Time Stamp.
==========================================================================
*/
static UINT8 GetCurTxPwr(
IN PRTMP_ADAPTER pAd,
IN UINT8 Wcid)
{
return 16; /* 16 dBm */
}
/*
==========================================================================
Description:
Get Current Transmit Power.
Parametrs:
Return : Current Time Stamp.
==========================================================================
*/
VOID InsertChannelRepIE(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pFrameBuf,
OUT PULONG pFrameLen,
IN PSTRING pCountry,
IN UINT8 RegulatoryClass)
{
ULONG TempLen;
UINT8 Len;
UINT8 IEId = IE_AP_CHANNEL_REPORT;
PUCHAR pChListPtr = NULL;
Len = 1;
if (strncmp(pCountry, "US", 2) == 0)
{
if (RegulatoryClass >= USA_REGULATORY_INFO_SIZE)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: USA Unknow Requlatory class (%d)\n",
__FUNCTION__, RegulatoryClass));
return;
}
Len += USARegulatoryInfo[RegulatoryClass].ChannelSet.NumberOfChannels;
pChListPtr = USARegulatoryInfo[RegulatoryClass].ChannelSet.ChannelList;
}
else if (strncmp(pCountry, "JP", 2) == 0)
{
if (RegulatoryClass >= JP_REGULATORY_INFO_SIZE)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: JP Unknow Requlatory class (%d)\n",
__FUNCTION__, RegulatoryClass));
return;
}
Len += JapanRegulatoryInfo[RegulatoryClass].ChannelSet.NumberOfChannels;
pChListPtr = JapanRegulatoryInfo[RegulatoryClass].ChannelSet.ChannelList;
}
else
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: Unknow Country (%s)\n",
__FUNCTION__, pCountry));
return;
}
MakeOutgoingFrame(pFrameBuf, &TempLen,
1, &IEId,
1, &Len,
1, &RegulatoryClass,
Len -1, pChListPtr,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
return;
}
/*
==========================================================================
Description:
Insert Dialog Token into frame.
Parametrs:
1. frame buffer pointer.
2. frame length.
3. Dialog token.
Return : None.
==========================================================================
*/
VOID InsertDialogToken(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pFrameBuf,
OUT PULONG pFrameLen,
IN UINT8 DialogToken)
{
ULONG TempLen;
MakeOutgoingFrame(pFrameBuf, &TempLen,
1, &DialogToken,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
return;
}
/*
==========================================================================
Description:
Insert TPC Request IE into frame.
Parametrs:
1. frame buffer pointer.
2. frame length.
Return : None.
==========================================================================
*/
static VOID InsertTpcReqIE(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pFrameBuf,
OUT PULONG pFrameLen)
{
ULONG TempLen;
ULONG Len = 0;
UINT8 ElementID = IE_TPC_REQUEST;
MakeOutgoingFrame(pFrameBuf, &TempLen,
1, &ElementID,
1, &Len,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
return;
}
/*
==========================================================================
Description:
Insert TPC Report IE into frame.
Parametrs:
1. frame buffer pointer.
2. frame length.
3. Transmit Power.
4. Link Margin.
Return : None.
==========================================================================
*/
VOID InsertTpcReportIE(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pFrameBuf,
OUT PULONG pFrameLen,
IN UINT8 TxPwr,
IN UINT8 LinkMargin)
{
ULONG TempLen;
ULONG Len = sizeof(TPC_REPORT_INFO);
UINT8 ElementID = IE_TPC_REPORT;
TPC_REPORT_INFO TpcReportIE;
TpcReportIE.TxPwr = TxPwr;
TpcReportIE.LinkMargin = LinkMargin;
MakeOutgoingFrame(pFrameBuf, &TempLen,
1, &ElementID,
1, &Len,
Len, &TpcReportIE,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
return;
}
/*
==========================================================================
Description:
Insert Channel Switch Announcement IE into frame.
Parametrs:
1. frame buffer pointer.
2. frame length.
3. channel switch announcement mode.
4. new selected channel.
5. channel switch announcement count.
Return : None.
==========================================================================
*/
static VOID InsertChSwAnnIE(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pFrameBuf,
OUT PULONG pFrameLen,
IN UINT8 ChSwMode,
IN UINT8 NewChannel,
IN UINT8 ChSwCnt)
{
ULONG TempLen;
ULONG Len = sizeof(CH_SW_ANN_INFO);
UINT8 ElementID = IE_CHANNEL_SWITCH_ANNOUNCEMENT;
CH_SW_ANN_INFO ChSwAnnIE;
ChSwAnnIE.ChSwMode = ChSwMode;
ChSwAnnIE.Channel = NewChannel;
ChSwAnnIE.ChSwCnt = ChSwCnt;
MakeOutgoingFrame(pFrameBuf, &TempLen,
1, &ElementID,
1, &Len,
Len, &ChSwAnnIE,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
return;
}
/*
==========================================================================
Description:
Insert Measure Request IE into frame.
Parametrs:
1. frame buffer pointer.
2. frame length.
3. Measure Token.
4. Measure Request Mode.
5. Measure Request Type.
6. Measure Channel.
7. Measure Start time.
8. Measure Duration.
Return : None.
==========================================================================
*/
static VOID InsertMeasureReqIE(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pFrameBuf,
OUT PULONG pFrameLen,
IN UINT8 Len,
IN PMEASURE_REQ_INFO pMeasureReqIE)
{
ULONG TempLen;
UINT8 ElementID = IE_MEASUREMENT_REQUEST;
MakeOutgoingFrame(pFrameBuf, &TempLen,
1, &ElementID,
1, &Len,
sizeof(MEASURE_REQ_INFO), pMeasureReqIE,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
return;
}
/*
==========================================================================
Description:
Insert Measure Report IE into frame.
Parametrs:
1. frame buffer pointer.
2. frame length.
3. Measure Token.
4. Measure Request Mode.
5. Measure Request Type.
6. Length of Report Infomation
7. Pointer of Report Infomation Buffer.
Return : None.
==========================================================================
*/
static VOID InsertMeasureReportIE(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pFrameBuf,
OUT PULONG pFrameLen,
IN PMEASURE_REPORT_INFO pMeasureReportIE,
IN UINT8 ReportLnfoLen,
IN PUINT8 pReportInfo)
{
ULONG TempLen;
ULONG Len;
UINT8 ElementID = IE_MEASUREMENT_REPORT;
Len = sizeof(MEASURE_REPORT_INFO) + ReportLnfoLen;
MakeOutgoingFrame(pFrameBuf, &TempLen,
1, &ElementID,
1, &Len,
Len, pMeasureReportIE,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
if ((ReportLnfoLen > 0) && (pReportInfo != NULL))
{
MakeOutgoingFrame(pFrameBuf + *pFrameLen, &TempLen,
ReportLnfoLen, pReportInfo,
END_OF_ARGS);
*pFrameLen = *pFrameLen + TempLen;
}
return;
}
/*
==========================================================================
Description:
Prepare Measurement request action frame and enqueue it into
management queue waiting for transmition.
Parametrs:
1. the destination mac address of the frame.
Return : None.
==========================================================================
*/
VOID MakeMeasurementReqFrame(
IN PRTMP_ADAPTER pAd,
OUT PUCHAR pOutBuffer,
OUT PULONG pFrameLen,
IN UINT8 TotalLen,
IN UINT8 Category,
IN UINT8 Action,
IN UINT8 MeasureToken,
IN UINT8 MeasureReqMode,
IN UINT8 MeasureReqType,
IN UINT8 NumOfRepetitions)
{
ULONG TempLen;
MEASURE_REQ_INFO MeasureReqIE;
InsertActField(pAd, (pOutBuffer + *pFrameLen), pFrameLen, Category, Action);
// fill Dialog Token
InsertDialogToken(pAd, (pOutBuffer + *pFrameLen), pFrameLen, MeasureToken);
/* fill Number of repetitions. */
if (Category == CATEGORY_RM)
{
MakeOutgoingFrame((pOutBuffer+*pFrameLen), &TempLen,
2, &NumOfRepetitions,
END_OF_ARGS);
*pFrameLen += TempLen;
}
// prepare Measurement IE.
NdisZeroMemory(&MeasureReqIE, sizeof(MEASURE_REQ_INFO));
MeasureReqIE.Token = MeasureToken;
MeasureReqIE.ReqMode.word = MeasureReqMode;
MeasureReqIE.ReqType = MeasureReqType;
InsertMeasureReqIE(pAd, (pOutBuffer+*pFrameLen), pFrameLen,
TotalLen, &MeasureReqIE);
return;
}
/*
==========================================================================
Description:
Prepare Measurement report action frame and enqueue it into
management queue waiting for transmition.
Parametrs:
1. the destination mac address of the frame.
Return : None.
==========================================================================
*/
VOID EnqueueMeasurementRep(
IN PRTMP_ADAPTER pAd,
IN PUCHAR pDA,
IN UINT8 DialogToken,
IN UINT8 MeasureToken,
IN UINT8 MeasureReqMode,
IN UINT8 MeasureReqType,
IN UINT8 ReportInfoLen,
IN PUINT8 pReportInfo)
{
PUCHAR pOutBuffer = NULL;
NDIS_STATUS NStatus;
ULONG FrameLen;
HEADER_802_11 ActHdr;
MEASURE_REPORT_INFO MeasureRepIE;
// build action frame header.
MgtMacHeaderInit(pAd, &ActHdr, SUBTYPE_ACTION, 0, pDA,
pAd->CurrentAddress);
NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory
if(NStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__));
return;
}
NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11));
FrameLen = sizeof(HEADER_802_11);
InsertActField(pAd, (pOutBuffer + FrameLen), &FrameLen, CATEGORY_SPECTRUM, SPEC_MRP);
// fill Dialog Token
InsertDialogToken(pAd, (pOutBuffer + FrameLen), &FrameLen, DialogToken);
// prepare Measurement IE.
NdisZeroMemory(&MeasureRepIE, sizeof(MEASURE_REPORT_INFO));
MeasureRepIE.Token = MeasureToken;
MeasureRepIE.ReportMode = MeasureReqMode;
MeasureRepIE.ReportType = MeasureReqType;
InsertMeasureReportIE(pAd, (pOutBuffer + FrameLen), &FrameLen, &MeasureRepIE, ReportInfoLen, pReportInfo);
MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen);
MlmeFreeMemory(pAd, pOutBuffer);
return;
}
/*
==========================================================================
Description:
Prepare TPC Request action frame and enqueue it into
management queue waiting for transmition.
Parametrs:
1. the destination mac address of the frame.
Return : None.
==========================================================================
*/
VOID EnqueueTPCReq(
IN PRTMP_ADAPTER pAd,
IN PUCHAR pDA,
IN UCHAR DialogToken)
{
PUCHAR pOutBuffer = NULL;
NDIS_STATUS NStatus;
ULONG FrameLen;
HEADER_802_11 ActHdr;
// build action frame header.
MgtMacHeaderInit(pAd, &ActHdr, SUBTYPE_ACTION, 0, pDA,
pAd->CurrentAddress);
NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory
if(NStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__));
return;
}
NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11));
FrameLen = sizeof(HEADER_802_11);
InsertActField(pAd, (pOutBuffer + FrameLen), &FrameLen, CATEGORY_SPECTRUM, SPEC_TPCRQ);
// fill Dialog Token
InsertDialogToken(pAd, (pOutBuffer + FrameLen), &FrameLen, DialogToken);
// Insert TPC Request IE.
InsertTpcReqIE(pAd, (pOutBuffer + FrameLen), &FrameLen);
MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen);
MlmeFreeMemory(pAd, pOutBuffer);
return;
}
/*
==========================================================================
Description:
Prepare TPC Report action frame and enqueue it into
management queue waiting for transmition.
Parametrs:
1. the destination mac address of the frame.
Return : None.
==========================================================================
*/
VOID EnqueueTPCRep(
IN PRTMP_ADAPTER pAd,
IN PUCHAR pDA,
IN UINT8 DialogToken,
IN UINT8 TxPwr,
IN UINT8 LinkMargin)
{
PUCHAR pOutBuffer = NULL;
NDIS_STATUS NStatus;
ULONG FrameLen;
HEADER_802_11 ActHdr;
// build action frame header.
MgtMacHeaderInit(pAd, &ActHdr, SUBTYPE_ACTION, 0, pDA,
pAd->CurrentAddress);
NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory
if(NStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__));
return;
}
NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11));
FrameLen = sizeof(HEADER_802_11);
InsertActField(pAd, (pOutBuffer + FrameLen), &FrameLen, CATEGORY_SPECTRUM, SPEC_TPCRP);
// fill Dialog Token
InsertDialogToken(pAd, (pOutBuffer + FrameLen), &FrameLen, DialogToken);
// Insert TPC Request IE.
InsertTpcReportIE(pAd, (pOutBuffer + FrameLen), &FrameLen, TxPwr, LinkMargin);
MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen);
MlmeFreeMemory(pAd, pOutBuffer);
return;
}
/*
==========================================================================
Description:
Prepare Channel Switch Announcement action frame and enqueue it into
management queue waiting for transmition.
Parametrs:
1. the destination mac address of the frame.
2. Channel switch announcement mode.
2. a New selected channel.
Return : None.
==========================================================================
*/
VOID EnqueueChSwAnn(
IN PRTMP_ADAPTER pAd,
IN PUCHAR pDA,
IN UINT8 ChSwMode,
IN UINT8 NewCh)
{
PUCHAR pOutBuffer = NULL;
NDIS_STATUS NStatus;
ULONG FrameLen;
HEADER_802_11 ActHdr;
// build action frame header.
MgtMacHeaderInit(pAd, &ActHdr, SUBTYPE_ACTION, 0, pDA,
pAd->CurrentAddress);
NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory
if(NStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__));
return;
}
NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11));
FrameLen = sizeof(HEADER_802_11);
InsertActField(pAd, (pOutBuffer + FrameLen), &FrameLen, CATEGORY_SPECTRUM, SPEC_CHANNEL_SWITCH);
InsertChSwAnnIE(pAd, (pOutBuffer + FrameLen), &FrameLen, ChSwMode, NewCh, 0);
MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen);
MlmeFreeMemory(pAd, pOutBuffer);
return;
}
static BOOLEAN DfsRequirementCheck(
IN PRTMP_ADAPTER pAd,
IN UINT8 Channel)
{
BOOLEAN Result = FALSE;
INT i;
do
{
// check DFS procedure is running.
// make sure DFS procedure won't start twice.
if (pAd->CommonCfg.RadarDetect.RDMode != RD_NORMAL_MODE)
{
Result = FALSE;
break;
}
// check the new channel carried from Channel Switch Announcemnet is valid.
for (i=0; i<pAd->ChannelListNum; i++)
{
if ((Channel == pAd->ChannelList[i].Channel)
&&(pAd->ChannelList[i].RemainingTimeForUse == 0))
{
// found radar signal in the channel. the channel can't use at least for 30 minutes.
pAd->ChannelList[i].RemainingTimeForUse = 1800;//30 min = 1800 sec
Result = TRUE;
break;
}
}
} while(FALSE);
return Result;
}
VOID NotifyChSwAnnToPeerAPs(
IN PRTMP_ADAPTER pAd,
IN PUCHAR pRA,
IN PUCHAR pTA,
IN UINT8 ChSwMode,
IN UINT8 Channel)
{
#ifdef WDS_SUPPORT
if (!((pRA[0] & 0xff) == 0xff)) // is pRA a broadcase address.
{
INT i;
// info neighbor APs that Radar signal found throgh WDS link.
for (i = 0; i < MAX_WDS_ENTRY; i++)
{
if (ValidWdsEntry(pAd, i))
{
PUCHAR pDA = pAd->WdsTab.WdsEntry[i].PeerWdsAddr;
// DA equal to SA. have no necessary orignal AP which found Radar signal.
if (MAC_ADDR_EQUAL(pTA, pDA))
continue;
// send Channel Switch Action frame to info Neighbro APs.
EnqueueChSwAnn(pAd, pDA, ChSwMode, Channel);
}
}
}
#endif // WDS_SUPPORT //
}
static VOID StartDFSProcedure(
IN PRTMP_ADAPTER pAd,
IN UCHAR Channel,
IN UINT8 ChSwMode)
{
// start DFS procedure
pAd->CommonCfg.Channel = Channel;
#ifdef DOT11_N_SUPPORT
N_ChannelCheck(pAd);
#endif // DOT11_N_SUPPORT //
pAd->CommonCfg.RadarDetect.RDMode = RD_SWITCHING_MODE;
pAd->CommonCfg.RadarDetect.CSCount = 0;
}
/*
==========================================================================
Description:
Channel Switch Announcement action frame sanity check.
Parametrs:
1. MLME message containing the received frame
2. message length.
3. Channel switch announcement infomation buffer.
Return : None.
==========================================================================
*/
/*
Channel Switch Announcement IE.
+----+-----+-----------+------------+-----------+
| ID | Len |Ch Sw Mode | New Ch Num | Ch Sw Cnt |
+----+-----+-----------+------------+-----------+
1 1 1 1 1
*/
static BOOLEAN PeerChSwAnnSanity(
IN PRTMP_ADAPTER pAd,
IN VOID *pMsg,
IN ULONG MsgLen,
OUT PCH_SW_ANN_INFO pChSwAnnInfo)
{
PFRAME_802_11 Fr = (PFRAME_802_11)pMsg;
PUCHAR pFramePtr = Fr->Octet;
BOOLEAN result = FALSE;
PEID_STRUCT eid_ptr;
// skip 802.11 header.
MsgLen -= sizeof(HEADER_802_11);
// skip category and action code.
pFramePtr += 2;
MsgLen -= 2;
if (pChSwAnnInfo == NULL)
return result;
eid_ptr = (PEID_STRUCT)pFramePtr;
while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pFramePtr + MsgLen))
{
switch(eid_ptr->Eid)
{
case IE_CHANNEL_SWITCH_ANNOUNCEMENT:
NdisMoveMemory(&pChSwAnnInfo->ChSwMode, eid_ptr->Octet, 1);
NdisMoveMemory(&pChSwAnnInfo->Channel, eid_ptr->Octet + 1, 1);
NdisMoveMemory(&pChSwAnnInfo->ChSwCnt, eid_ptr->Octet + 2, 1);
result = TRUE;
break;
default:
break;
}
eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);
}
return result;
}
/*
==========================================================================
Description:
Measurement request action frame sanity check.
Parametrs:
1. MLME message containing the received frame
2. message length.
3. Measurement request infomation buffer.
Return : None.
==========================================================================
*/
static BOOLEAN PeerMeasureReqSanity(
IN PRTMP_ADAPTER pAd,
IN VOID *pMsg,
IN ULONG MsgLen,
OUT PUINT8 pDialogToken,
OUT PMEASURE_REQ_INFO pMeasureReqInfo,
OUT PMEASURE_REQ pMeasureReq)
{
PFRAME_802_11 Fr = (PFRAME_802_11)pMsg;
PUCHAR pFramePtr = Fr->Octet;
BOOLEAN result = FALSE;
PEID_STRUCT eid_ptr;
PUCHAR ptr;
UINT64 MeasureStartTime;
UINT16 MeasureDuration;
// skip 802.11 header.
MsgLen -= sizeof(HEADER_802_11);
// skip category and action code.
pFramePtr += 2;
MsgLen -= 2;
if (pMeasureReqInfo == NULL)
return result;
NdisMoveMemory(pDialogToken, pFramePtr, 1);
pFramePtr += 1;
MsgLen -= 1;
eid_ptr = (PEID_STRUCT)pFramePtr;
while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pFramePtr + MsgLen))
{
switch(eid_ptr->Eid)
{
case IE_MEASUREMENT_REQUEST:
NdisMoveMemory(&pMeasureReqInfo->Token, eid_ptr->Octet, 1);
NdisMoveMemory(&pMeasureReqInfo->ReqMode.word, eid_ptr->Octet + 1, 1);
NdisMoveMemory(&pMeasureReqInfo->ReqType, eid_ptr->Octet + 2, 1);
ptr = (PUCHAR)(eid_ptr->Octet + 3);
NdisMoveMemory(&pMeasureReq->ChNum, ptr, 1);
NdisMoveMemory(&MeasureStartTime, ptr + 1, 8);
pMeasureReq->MeasureStartTime = SWAP64(MeasureStartTime);
NdisMoveMemory(&MeasureDuration, ptr + 9, 2);
pMeasureReq->MeasureDuration = SWAP16(MeasureDuration);
result = TRUE;
break;
default:
break;
}
eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);
}
return result;
}
/*
==========================================================================
Description:
Measurement report action frame sanity check.
Parametrs:
1. MLME message containing the received frame
2. message length.
3. Measurement report infomation buffer.
4. basic report infomation buffer.
Return : None.
==========================================================================
*/
/*
Measurement Report IE.
+----+-----+-------+-------------+--------------+----------------+
| ID | Len | Token | Report Mode | Measure Type | Measure Report |
+----+-----+-------+-------------+--------------+----------------+
1 1 1 1 1 variable
Basic Report.
+--------+------------+----------+-----+
| Ch Num | Start Time | Duration | Map |
+--------+------------+----------+-----+
1 8 2 1
Map Field Bit Format.
+-----+---------------+---------------------+-------+------------+----------+
| Bss | OFDM Preamble | Unidentified signal | Radar | Unmeasured | Reserved |
+-----+---------------+---------------------+-------+------------+----------+
0 1 2 3 4 5-7
*/
static BOOLEAN PeerMeasureReportSanity(
IN PRTMP_ADAPTER pAd,
IN VOID *pMsg,
IN ULONG MsgLen,
OUT PUINT8 pDialogToken,
OUT PMEASURE_REPORT_INFO pMeasureReportInfo,
OUT PUINT8 pReportBuf)
{
PFRAME_802_11 Fr = (PFRAME_802_11)pMsg;
PUCHAR pFramePtr = Fr->Octet;
BOOLEAN result = FALSE;
PEID_STRUCT eid_ptr;
PUCHAR ptr;
// skip 802.11 header.
MsgLen -= sizeof(HEADER_802_11);
// skip category and action code.
pFramePtr += 2;
MsgLen -= 2;
if (pMeasureReportInfo == NULL)
return result;
NdisMoveMemory(pDialogToken, pFramePtr, 1);
pFramePtr += 1;
MsgLen -= 1;
eid_ptr = (PEID_STRUCT)pFramePtr;
while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pFramePtr + MsgLen))
{
switch(eid_ptr->Eid)
{
case IE_MEASUREMENT_REPORT:
NdisMoveMemory(&pMeasureReportInfo->Token, eid_ptr->Octet, 1);
NdisMoveMemory(&pMeasureReportInfo->ReportMode, eid_ptr->Octet + 1, 1);
NdisMoveMemory(&pMeasureReportInfo->ReportType, eid_ptr->Octet + 2, 1);
if (pMeasureReportInfo->ReportType == RM_BASIC)
{
PMEASURE_BASIC_REPORT pReport = (PMEASURE_BASIC_REPORT)pReportBuf;
ptr = (PUCHAR)(eid_ptr->Octet + 3);
NdisMoveMemory(&pReport->ChNum, ptr, 1);
NdisMoveMemory(&pReport->MeasureStartTime, ptr + 1, 8);
NdisMoveMemory(&pReport->MeasureDuration, ptr + 9, 2);
NdisMoveMemory(&pReport->Map, ptr + 11, 1);
}
else if (pMeasureReportInfo->ReportType == RM_CCA)
{
PMEASURE_CCA_REPORT pReport = (PMEASURE_CCA_REPORT)pReportBuf;
ptr = (PUCHAR)(eid_ptr->Octet + 3);
NdisMoveMemory(&pReport->ChNum, ptr, 1);
NdisMoveMemory(&pReport->MeasureStartTime, ptr + 1, 8);
NdisMoveMemory(&pReport->MeasureDuration, ptr + 9, 2);
NdisMoveMemory(&pReport->CCA_Busy_Fraction, ptr + 11, 1);
}
else if (pMeasureReportInfo->ReportType == RM_RPI_HISTOGRAM)
{
PMEASURE_RPI_REPORT pReport = (PMEASURE_RPI_REPORT)pReportBuf;
ptr = (PUCHAR)(eid_ptr->Octet + 3);
NdisMoveMemory(&pReport->ChNum, ptr, 1);
NdisMoveMemory(&pReport->MeasureStartTime, ptr + 1, 8);
NdisMoveMemory(&pReport->MeasureDuration, ptr + 9, 2);
NdisMoveMemory(&pReport->RPI_Density, ptr + 11, 8);
}
result = TRUE;
break;
default:
break;
}
eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);
}
return result;
}
/*
==========================================================================
Description:
TPC Request action frame sanity check.
Parametrs:
1. MLME message containing the received frame
2. message length.
3. Dialog Token.
Return : None.
==========================================================================
*/
static BOOLEAN PeerTpcReqSanity(
IN PRTMP_ADAPTER pAd,
IN VOID *pMsg,
IN ULONG MsgLen,
OUT PUINT8 pDialogToken)
{
PFRAME_802_11 Fr = (PFRAME_802_11)pMsg;
PUCHAR pFramePtr = Fr->Octet;
BOOLEAN result = FALSE;
PEID_STRUCT eid_ptr;
MsgLen -= sizeof(HEADER_802_11);
// skip category and action code.
pFramePtr += 2;
MsgLen -= 2;
if (pDialogToken == NULL)
return result;
NdisMoveMemory(pDialogToken, pFramePtr, 1);
pFramePtr += 1;
MsgLen -= 1;
eid_ptr = (PEID_STRUCT)pFramePtr;
while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pFramePtr + MsgLen))
{
switch(eid_ptr->Eid)
{
case IE_TPC_REQUEST:
result = TRUE;
break;
default:
break;
}
eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);
}
return result;
}
/*
==========================================================================
Description:
TPC Report action frame sanity check.
Parametrs:
1. MLME message containing the received frame
2. message length.
3. Dialog Token.
4. TPC Report IE.
Return : None.
==========================================================================
*/
static BOOLEAN PeerTpcRepSanity(
IN PRTMP_ADAPTER pAd,
IN VOID *pMsg,
IN ULONG MsgLen,
OUT PUINT8 pDialogToken,
OUT PTPC_REPORT_INFO pTpcRepInfo)
{
PFRAME_802_11 Fr = (PFRAME_802_11)pMsg;
PUCHAR pFramePtr = Fr->Octet;
BOOLEAN result = FALSE;
PEID_STRUCT eid_ptr;
MsgLen -= sizeof(HEADER_802_11);
// skip category and action code.
pFramePtr += 2;
MsgLen -= 2;
if (pDialogToken == NULL)
return result;
NdisMoveMemory(pDialogToken, pFramePtr, 1);
pFramePtr += 1;
MsgLen -= 1;
eid_ptr = (PEID_STRUCT)pFramePtr;
while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pFramePtr + MsgLen))
{
switch(eid_ptr->Eid)
{
case IE_TPC_REPORT:
NdisMoveMemory(&pTpcRepInfo->TxPwr, eid_ptr->Octet, 1);
NdisMoveMemory(&pTpcRepInfo->LinkMargin, eid_ptr->Octet + 1, 1);
result = TRUE;
break;
default:
break;
}
eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);
}
return result;
}
/*
==========================================================================
Description:
Channel Switch Announcement action frame handler.
Parametrs:
Elme - MLME message containing the received frame
Return : None.
==========================================================================
*/
static VOID PeerChSwAnnAction(
IN PRTMP_ADAPTER pAd,
IN MLME_QUEUE_ELEM *Elem)
{
CH_SW_ANN_INFO ChSwAnnInfo;
PFRAME_802_11 pFr = (PFRAME_802_11)Elem->Msg;
#ifdef CONFIG_STA_SUPPORT
UCHAR index = 0, Channel = 0, NewChannel = 0;
ULONG Bssidx = 0;
#endif // CONFIG_STA_SUPPORT //
NdisZeroMemory(&ChSwAnnInfo, sizeof(CH_SW_ANN_INFO));
if (! PeerChSwAnnSanity(pAd, Elem->Msg, Elem->MsgLen, &ChSwAnnInfo))
{
DBGPRINT(RT_DEBUG_TRACE, ("Invalid Channel Switch Action Frame.\n"));
return;
}
#ifdef CONFIG_STA_SUPPORT
if (pAd->OpMode == OPMODE_STA)
{
Bssidx = BssTableSearch(&pAd->ScanTab, pFr->Hdr.Addr3, pAd->CommonCfg.Channel);
if (Bssidx == BSS_NOT_FOUND)
{
DBGPRINT(RT_DEBUG_TRACE, ("PeerChSwAnnAction - Bssidx is not found\n"));
return;
}
DBGPRINT(RT_DEBUG_TRACE, ("\n****Bssidx is %d, Channel = %d\n", index, pAd->ScanTab.BssEntry[Bssidx].Channel));
hex_dump("SSID",pAd->ScanTab.BssEntry[Bssidx].Bssid ,6);
Channel = pAd->CommonCfg.Channel;
NewChannel = ChSwAnnInfo.Channel;
if ((pAd->CommonCfg.bIEEE80211H == 1) && (NewChannel != 0) && (Channel != NewChannel))
{
// Switching to channel 1 can prevent from rescanning the current channel immediately (by auto reconnection).
// In addition, clear the MLME queue and the scan table to discard the RX packets and previous scanning results.
AsicSwitchChannel(pAd, 1, FALSE);
AsicLockChannel(pAd, 1);
LinkDown(pAd, FALSE);
MlmeQueueInit(&pAd->Mlme.Queue);
BssTableInit(&pAd->ScanTab);
RTMPusecDelay(1000000); // use delay to prevent STA do reassoc
// channel sanity check
for (index = 0 ; index < pAd->ChannelListNum; index++)
{
if (pAd->ChannelList[index].Channel == NewChannel)
{
pAd->ScanTab.BssEntry[Bssidx].Channel = NewChannel;
pAd->CommonCfg.Channel = NewChannel;
AsicSwitchChannel(pAd, pAd->CommonCfg.Channel, FALSE);
AsicLockChannel(pAd, pAd->CommonCfg.Channel);
DBGPRINT(RT_DEBUG_TRACE, ("&&&&&&&&&&&&&&&&PeerChSwAnnAction - STA receive channel switch announcement IE (New Channel =%d)\n", NewChannel));
break;
}
}
if (index >= pAd->ChannelListNum)
{
DBGPRINT_ERR(("&&&&&&&&&&&&&&&&&&&&&&&&&&PeerChSwAnnAction(can not find New Channel=%d in ChannelList[%d]\n", pAd->CommonCfg.Channel, pAd->ChannelListNum));
}
}
}
#endif // CONFIG_STA_SUPPORT //
return;
}
/*
==========================================================================
Description:
Measurement Request action frame handler.
Parametrs:
Elme - MLME message containing the received frame
Return : None.
==========================================================================
*/
static VOID PeerMeasureReqAction(
IN PRTMP_ADAPTER pAd,
IN MLME_QUEUE_ELEM *Elem)
{
PFRAME_802_11 pFr = (PFRAME_802_11)Elem->Msg;
UINT8 DialogToken;
MEASURE_REQ_INFO MeasureReqInfo;
MEASURE_REQ MeasureReq;
MEASURE_REPORT_MODE ReportMode;
if(PeerMeasureReqSanity(pAd, Elem->Msg, Elem->MsgLen, &DialogToken, &MeasureReqInfo, &MeasureReq))
{
ReportMode.word = 0;
ReportMode.field.Incapable = 1;
EnqueueMeasurementRep(pAd, pFr->Hdr.Addr2, DialogToken, MeasureReqInfo.Token, ReportMode.word, MeasureReqInfo.ReqType, 0, NULL);
}
return;
}
/*
==========================================================================
Description:
Measurement Report action frame handler.
Parametrs:
Elme - MLME message containing the received frame
Return : None.
==========================================================================
*/
static VOID PeerMeasureReportAction(
IN PRTMP_ADAPTER pAd,
IN MLME_QUEUE_ELEM *Elem)
{
MEASURE_REPORT_INFO MeasureReportInfo;
PFRAME_802_11 pFr = (PFRAME_802_11)Elem->Msg;
UINT8 DialogToken;
PUINT8 pMeasureReportInfo;
// if (pAd->CommonCfg.bIEEE80211H != TRUE)
// return;
if ((pMeasureReportInfo = kmalloc(sizeof(MEASURE_RPI_REPORT), GFP_ATOMIC)) == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __FUNCTION__, sizeof(MEASURE_RPI_REPORT)));
return;
}
NdisZeroMemory(&MeasureReportInfo, sizeof(MEASURE_REPORT_INFO));
NdisZeroMemory(pMeasureReportInfo, sizeof(MEASURE_RPI_REPORT));
if (PeerMeasureReportSanity(pAd, Elem->Msg, Elem->MsgLen, &DialogToken, &MeasureReportInfo, pMeasureReportInfo))
{
do {
PMEASURE_REQ_ENTRY pEntry = NULL;
// Not a autonomous measure report.
// check the dialog token field. drop it if the dialog token doesn't match.
if ((DialogToken != 0)
&& ((pEntry = MeasureReqLookUp(pAd, DialogToken)) == NULL))
break;
if (pEntry != NULL)
MeasureReqDelete(pAd, pEntry->DialogToken);
if (MeasureReportInfo.ReportType == RM_BASIC)
{
PMEASURE_BASIC_REPORT pBasicReport = (PMEASURE_BASIC_REPORT)pMeasureReportInfo;
if ((pBasicReport->Map.field.Radar)
&& (DfsRequirementCheck(pAd, pBasicReport->ChNum) == TRUE))
{
NotifyChSwAnnToPeerAPs(pAd, pFr->Hdr.Addr1, pFr->Hdr.Addr2, 1, pBasicReport->ChNum);
StartDFSProcedure(pAd, pBasicReport->ChNum, 1);
}
}
} while (FALSE);
}
else
DBGPRINT(RT_DEBUG_TRACE, ("Invalid Measurement Report Frame.\n"));
kfree(pMeasureReportInfo);
return;
}
/*
==========================================================================
Description:
TPC Request action frame handler.
Parametrs:
Elme - MLME message containing the received frame
Return : None.
==========================================================================
*/
static VOID PeerTpcReqAction(
IN PRTMP_ADAPTER pAd,
IN MLME_QUEUE_ELEM *Elem)
{
PFRAME_802_11 pFr = (PFRAME_802_11)Elem->Msg;
PUCHAR pFramePtr = pFr->Octet;
UINT8 DialogToken;
UINT8 TxPwr = GetCurTxPwr(pAd, Elem->Wcid);
UINT8 LinkMargin = 0;
CHAR RealRssi;
// link margin: Ratio of the received signal power to the minimum desired by the station (STA). The
// STA may incorporate rate information and channel conditions, including interference, into its computation
// of link margin.
RealRssi = RTMPMaxRssi(pAd, ConvertToRssi(pAd, Elem->Rssi0, RSSI_0),
ConvertToRssi(pAd, Elem->Rssi1, RSSI_1),
ConvertToRssi(pAd, Elem->Rssi2, RSSI_2));
// skip Category and action code.
pFramePtr += 2;
// Dialog token.
NdisMoveMemory(&DialogToken, pFramePtr, 1);
LinkMargin = (RealRssi / MIN_RCV_PWR);
if (PeerTpcReqSanity(pAd, Elem->Msg, Elem->MsgLen, &DialogToken))
EnqueueTPCRep(pAd, pFr->Hdr.Addr2, DialogToken, TxPwr, LinkMargin);
return;
}
/*
==========================================================================
Description:
TPC Report action frame handler.
Parametrs:
Elme - MLME message containing the received frame
Return : None.
==========================================================================
*/
static VOID PeerTpcRepAction(
IN PRTMP_ADAPTER pAd,
IN MLME_QUEUE_ELEM *Elem)
{
UINT8 DialogToken;
TPC_REPORT_INFO TpcRepInfo;
PTPC_REQ_ENTRY pEntry = NULL;
NdisZeroMemory(&TpcRepInfo, sizeof(TPC_REPORT_INFO));
if (PeerTpcRepSanity(pAd, Elem->Msg, Elem->MsgLen, &DialogToken, &TpcRepInfo))
{
if ((pEntry = TpcReqLookUp(pAd, DialogToken)) != NULL)
{
TpcReqDelete(pAd, pEntry->DialogToken);
DBGPRINT(RT_DEBUG_TRACE, ("%s: DialogToken=%x, TxPwr=%d, LinkMargin=%d\n",
__FUNCTION__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin));
}
}
return;
}
/*
==========================================================================
Description:
Spectrun action frames Handler such as channel switch annoucement,
measurement report, measurement request actions frames.
Parametrs:
Elme - MLME message containing the received frame
Return : None.
==========================================================================
*/
VOID PeerSpectrumAction(
IN PRTMP_ADAPTER pAd,
IN MLME_QUEUE_ELEM *Elem)
{
UCHAR Action = Elem->Msg[LENGTH_802_11+1];
if (pAd->CommonCfg.bIEEE80211H != TRUE)
return;
switch(Action)
{
case SPEC_MRQ:
// current rt2860 unable do such measure specified in Measurement Request.
// reject all measurement request.
PeerMeasureReqAction(pAd, Elem);
break;
case SPEC_MRP:
PeerMeasureReportAction(pAd, Elem);
break;
case SPEC_TPCRQ:
PeerTpcReqAction(pAd, Elem);
break;
case SPEC_TPCRP:
PeerTpcRepAction(pAd, Elem);
break;
case SPEC_CHANNEL_SWITCH:
#ifdef DOT11N_DRAFT3
{
SEC_CHA_OFFSET_IE Secondary;
CHA_SWITCH_ANNOUNCE_IE ChannelSwitch;
// 802.11h only has Channel Switch Announcement IE.
RTMPMoveMemory(&ChannelSwitch, &Elem->Msg[LENGTH_802_11+4], sizeof (CHA_SWITCH_ANNOUNCE_IE));
// 802.11n D3.03 adds secondary channel offset element in the end.
if (Elem->MsgLen == (LENGTH_802_11 + 2 + sizeof (CHA_SWITCH_ANNOUNCE_IE) + sizeof (SEC_CHA_OFFSET_IE)))
{
RTMPMoveMemory(&Secondary, &Elem->Msg[LENGTH_802_11+9], sizeof (SEC_CHA_OFFSET_IE));
}
else
{
Secondary.SecondaryChannelOffset = 0;
}
if ((Elem->Msg[LENGTH_802_11+2] == IE_CHANNEL_SWITCH_ANNOUNCEMENT) && (Elem->Msg[LENGTH_802_11+3] == 3))
{
ChannelSwitchAction(pAd, Elem->Wcid, ChannelSwitch.NewChannel, Secondary.SecondaryChannelOffset);
}
}
#endif // DOT11N_DRAFT3 //
PeerChSwAnnAction(pAd, Elem);
break;
}
return;
}
/*
==========================================================================
Description:
Parametrs:
Return : None.
==========================================================================
*/
INT Set_MeasureReq_Proc(
IN PRTMP_ADAPTER pAd,
IN PSTRING arg)
{
UINT Aid = 1;
UINT ArgIdx;
PSTRING thisChar;
MEASURE_REQ_MODE MeasureReqMode;
UINT8 MeasureReqToken = RandomByte(pAd);
UINT8 MeasureReqType = RM_BASIC;
UINT8 MeasureCh = 1;
UINT64 MeasureStartTime = GetCurrentTimeStamp(pAd);
MEASURE_REQ MeasureReq;
UINT8 TotalLen;
HEADER_802_11 ActHdr;
PUCHAR pOutBuffer = NULL;
NDIS_STATUS NStatus;
ULONG FrameLen;
NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory
if(NStatus != NDIS_STATUS_SUCCESS)
{
DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__));
goto END_OF_MEASURE_REQ;
}
ArgIdx = 1;
while ((thisChar = strsep((char **)&arg, "-")) != NULL)
{
switch(ArgIdx)
{
case 1: // Aid.
Aid = (UINT8) simple_strtol(thisChar, 0, 16);
break;
case 2: // Measurement Request Type.
MeasureReqType = simple_strtol(thisChar, 0, 16);
if (MeasureReqType > 3)
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __FUNCTION__, MeasureReqType));
goto END_OF_MEASURE_REQ;
}
break;
case 3: // Measurement channel.
MeasureCh = (UINT8) simple_strtol(thisChar, 0, 16);
break;
}
ArgIdx++;
}
DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __FUNCTION__, Aid, MeasureReqType, MeasureCh));
if (!VALID_WCID(Aid))
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid));
goto END_OF_MEASURE_REQ;
}
MeasureReqMode.word = 0;
MeasureReqMode.field.Enable = 1;
MeasureReqInsert(pAd, MeasureReqToken);
// build action frame header.
MgtMacHeaderInit(pAd, &ActHdr, SUBTYPE_ACTION, 0, pAd->MacTab.Content[Aid].Addr,
pAd->CurrentAddress);
NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11));
FrameLen = sizeof(HEADER_802_11);
TotalLen = sizeof(MEASURE_REQ_INFO) + sizeof(MEASURE_REQ);
MakeMeasurementReqFrame(pAd, pOutBuffer, &FrameLen,
sizeof(MEASURE_REQ_INFO), CATEGORY_RM, RM_BASIC,
MeasureReqToken, MeasureReqMode.word,
MeasureReqType, 0);
MeasureReq.ChNum = MeasureCh;
MeasureReq.MeasureStartTime = cpu2le64(MeasureStartTime);
MeasureReq.MeasureDuration = cpu2le16(2000);
{
ULONG TempLen;
MakeOutgoingFrame( pOutBuffer+FrameLen, &TempLen,
sizeof(MEASURE_REQ), &MeasureReq,
END_OF_ARGS);
FrameLen += TempLen;
}
MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, (UINT)FrameLen);
END_OF_MEASURE_REQ:
MlmeFreeMemory(pAd, pOutBuffer);
return TRUE;
}
INT Set_TpcReq_Proc(
IN PRTMP_ADAPTER pAd,
IN PSTRING arg)
{
UINT Aid;
UINT8 TpcReqToken = RandomByte(pAd);
Aid = (UINT) simple_strtol(arg, 0, 16);
DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __FUNCTION__, Aid));
if (!VALID_WCID(Aid))
{
DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid));
return TRUE;
}
TpcReqInsert(pAd, TpcReqToken);
EnqueueTPCReq(pAd, pAd->MacTab.Content[Aid].Addr, TpcReqToken);
return TRUE;
}