/* ************************************************************************* * 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" 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", __func__)); return; } VOID MeasureReqTabExit( IN PRTMP_ADAPTER pAd) { NdisFreeSpinLock(pAd->CommonCfg.MeasureReqTabLock); if (pAd->CommonCfg.pMeasureReqTab) kfree(pAd->CommonCfg.pMeasureReqTab); pAd->CommonCfg.pMeasureReqTab = NULL; return; } static 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", __func__)); 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; } static 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", __func__)); 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", __func__)); } // 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; } static 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", __func__)); 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", __func__)); 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", __func__)); 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", __func__)); 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", __func__)); } // 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", __func__)); 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: Insert Dialog Token into frame. Parametrs: 1. frame buffer pointer. 2. frame length. 3. Dialog token. Return : None. ========================================================================== */ static 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. ========================================================================== */ static 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 PMEASURE_REQ_INFO pMeasureReqIE) { ULONG TempLen; UINT8 Len = sizeof(MEASURE_REQ_INFO); UINT8 ElementID = IE_MEASUREMENT_REQUEST; MakeOutgoingFrame(pFrameBuf, &TempLen, 1, &ElementID, 1, &Len, Len, 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 EnqueueMeasurementReq( IN PRTMP_ADAPTER pAd, IN PUCHAR pDA, IN UINT8 MeasureToken, IN UINT8 MeasureReqMode, IN UINT8 MeasureReqType, IN UINT8 MeasureCh, IN UINT16 MeasureDuration) { PUCHAR pOutBuffer = NULL; NDIS_STATUS NStatus; ULONG FrameLen; HEADER_802_11 ActHdr; MEASURE_REQ_INFO MeasureReqIE; UINT8 RmReqDailogToken = RandomByte(pAd); UINT64 MeasureStartTime = GetCurrentTimeStamp(pAd); // 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", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); FrameLen = sizeof(HEADER_802_11); InsertActField(pAd, (pOutBuffer + FrameLen), &FrameLen, CATEGORY_SPECTRUM, SPEC_MRQ); // fill Dialog Token InsertDialogToken(pAd, (pOutBuffer + FrameLen), &FrameLen, MeasureToken); // prepare Measurement IE. NdisZeroMemory(&MeasureReqIE, sizeof(MEASURE_REQ_INFO)); MeasureReqIE.Token = RmReqDailogToken; MeasureReqIE.ReqMode.word = MeasureReqMode; MeasureReqIE.ReqType = MeasureReqType; MeasureReqIE.MeasureReq.ChNum = MeasureCh; MeasureReqIE.MeasureReq.MeasureStartTime = cpu2le64(MeasureStartTime); MeasureReqIE.MeasureReq.MeasureDuration = cpu2le16(MeasureDuration); InsertMeasureReqIE(pAd, (pOutBuffer + FrameLen), &FrameLen, &MeasureReqIE); MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen); MlmeFreeMemory(pAd, pOutBuffer); 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", __func__)); 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.word = 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", __func__)); 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", __func__)); 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", __func__)); 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; iChannelListNum; 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) { 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 = eid_ptr->Octet + 3; NdisMoveMemory(&pMeasureReqInfo->MeasureReq.ChNum, ptr, 1); NdisMoveMemory(&MeasureStartTime, ptr + 1, 8); pMeasureReqInfo->MeasureReq.MeasureStartTime = SWAP64(MeasureStartTime); NdisMoveMemory(&MeasureDuration, ptr + 9, 2); pMeasureReqInfo->MeasureReq.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 = 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 = 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 = 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_REPORT_MODE ReportMode; if(PeerMeasureReqSanity(pAd, Elem->Msg, Elem->MsgLen, &DialogToken, &MeasureReqInfo)) { 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", __func__, 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", __func__, 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 PUCHAR arg) { UINT Aid = 1; UINT ArgIdx; PUCHAR thisChar; MEASURE_REQ_MODE MeasureReqMode; UINT8 MeasureReqToken = RandomByte(pAd); UINT8 MeasureReqType = RM_BASIC; UINT8 MeasureCh = 1; ArgIdx = 1; while ((thisChar = strsep((char **)&arg, "-")) != NULL) { switch(ArgIdx) { case 1: // Aid. Aid = 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", __func__, MeasureReqType)); return TRUE; } break; case 3: // Measurement channel. MeasureCh = simple_strtol(thisChar, 0, 16); break; } ArgIdx++; } DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __func__, Aid, MeasureReqType, MeasureCh)); if (!VALID_WCID(Aid)) { DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } MeasureReqMode.word = 0; MeasureReqMode.field.Enable = 1; MeasureReqInsert(pAd, MeasureReqToken); EnqueueMeasurementReq(pAd, pAd->MacTab.Content[Aid].Addr, MeasureReqToken, MeasureReqMode.word, MeasureReqType, MeasureCh, 2000); return TRUE; } INT Set_TpcReq_Proc( IN PRTMP_ADAPTER pAd, IN PUCHAR arg) { UINT Aid; UINT8 TpcReqToken = RandomByte(pAd); Aid = simple_strtol(arg, 0, 16); DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __func__, Aid)); if (!VALID_WCID(Aid)) { DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } TpcReqInsert(pAd, TpcReqToken); EnqueueTPCReq(pAd, pAd->MacTab.Content[Aid].Addr, TpcReqToken); return TRUE; }