| /****************************************************************************** |
| * |
| * This file is provided under a dual license. When you use or |
| * distribute this software, you may choose to be licensed under |
| * version 2 of the GNU General Public License ("GPLv2 License") |
| * or BSD License. |
| * |
| * GPLv2 License |
| * |
| * Copyright(C) 2016 MediaTek Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. |
| * |
| * BSD LICENSE |
| * |
| * Copyright(C) 2016 MediaTek Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| *****************************************************************************/ |
| /* |
| ** Id: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/mgmt/rlm.c#3 |
| */ |
| |
| /*! \file "rlm.c" |
| * \brief |
| * |
| */ |
| |
| /******************************************************************************* |
| * C O M P I L E R F L A G S |
| ******************************************************************************* |
| */ |
| |
| /******************************************************************************* |
| * E X T E R N A L R E F E R E N C E S |
| ******************************************************************************* |
| */ |
| #include "precomp.h" |
| |
| /******************************************************************************* |
| * C O N S T A N T S |
| ******************************************************************************* |
| */ |
| |
| /* Retry limit of sending operation notification frame */ |
| #define OPERATION_NOTICATION_TX_LIMIT 2 |
| |
| /******************************************************************************* |
| * D A T A T Y P E S |
| ******************************************************************************* |
| */ |
| |
| /******************************************************************************* |
| * P U B L I C D A T A |
| ******************************************************************************* |
| */ |
| |
| /******************************************************************************* |
| * P R I V A T E D A T A |
| ******************************************************************************* |
| */ |
| /* |
| ** Should Not Force to BW 20 after Channel Switch. |
| ** Enable for DFS Certification |
| */ |
| #ifdef CFG_DFS_CHSW_FORCE_BW20 |
| u_int8_t g_fgHasChannelSwitchIE = FALSE; |
| #endif |
| u_int8_t g_fgHasStopTx = FALSE; |
| u_int8_t g_fgFowardBcn2Supplicant = FALSE; |
| |
| #if CFG_SUPPORT_CAL_RESULT_BACKUP_TO_HOST |
| struct RLM_CAL_RESULT_ALL_V2 g_rBackupCalDataAllV2; |
| #endif |
| |
| struct TIMER rBeaconReqTimer; |
| |
| struct REF_TSF { |
| uint32_t au4Tsf[2]; |
| OS_SYSTIME rTime; |
| }; |
| static struct REF_TSF rTsf; |
| /******************************************************************************* |
| * M A C R O S |
| ******************************************************************************* |
| */ |
| |
| /******************************************************************************* |
| * F U N C T I O N D E C L A R A T I O N S |
| ******************************************************************************* |
| */ |
| static void rlmFillHtCapIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo); |
| |
| static void rlmFillExtCapIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo); |
| |
| static void rlmFillHtOpIE(struct ADAPTER *prAdapter, struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo); |
| |
| static uint8_t rlmRecIeInfoForClient(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, uint8_t *pucIE, |
| uint16_t u2IELength); |
| |
| static u_int8_t rlmRecBcnFromNeighborForClient(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct SW_RFB *prSwRfb, |
| uint8_t *pucIE, |
| uint16_t u2IELength); |
| |
| static u_int8_t rlmRecBcnInfoForClient(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct SW_RFB *prSwRfb, uint8_t *pucIE, |
| uint16_t u2IELength); |
| |
| static void rlmBssReset(struct ADAPTER *prAdapter, struct BSS_INFO *prBssInfo); |
| |
| #if CFG_SUPPORT_802_11AC |
| static void rlmFillVhtCapIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo); |
| static void rlmFillVhtOpNotificationIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo, |
| u_int8_t fgIsMaxCap); |
| |
| #endif |
| |
| /* Operating BW/Nss change and notification */ |
| static void rlmOpModeTxDoneHandler(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo, |
| uint8_t ucOpChangeType, |
| u_int8_t fgIsSuccess); |
| static void rlmChangeOwnOpInfo(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo); |
| static void rlmCompleteOpModeChange(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| u_int8_t fgIsSuccess); |
| static void rlmRollbackOpChangeParam(struct BSS_INFO *prBssInfo, |
| u_int8_t fgIsRollbackBw, |
| u_int8_t fgIsRollbackNss); |
| static uint8_t rlmGetOpModeBwByVhtAndHtOpInfo(struct BSS_INFO *prBssInfo); |
| static u_int8_t rlmCheckOpChangeParamValid(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| uint8_t ucChannelWidth, |
| uint8_t ucNss); |
| static void rlmRecOpModeBwForClient(uint8_t ucVhtOpModeChannelWidth, |
| struct BSS_INFO *prBssInfo); |
| |
| /* 11K */ |
| static u_int8_t |
| rlmAllMeasurementIssued(struct RADIO_MEASUREMENT_REQ_PARAMS *prReq); |
| |
| static void rlmCalibrateRepetions(struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq); |
| |
| static void rlmCollectBeaconReport(IN struct ADAPTER *prAdapter, |
| uint8_t *pucIEBuf, uint16_t u2IELength, |
| uint8_t *pucBssid, |
| struct RM_BEACON_REPORT_PARAMS *prRepParams); |
| |
| /******************************************************************************* |
| * F U N C T I O N S |
| ******************************************************************************* |
| */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmFsmEventInit(struct ADAPTER *prAdapter) |
| { |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReqParam = |
| &prAdapter->rWifiVar.rRmReqParams; |
| struct RADIO_MEASUREMENT_REPORT_PARAMS *prRmRepParam = |
| &prAdapter->rWifiVar.rRmRepParams; |
| |
| ASSERT(prAdapter); |
| |
| /* Note: assume struct TIMER structures are reset to zero or stopped |
| * before invoking this function. |
| */ |
| |
| /* Initialize OBSS FSM */ |
| rlmObssInit(prAdapter); |
| |
| #if CFG_SUPPORT_PWR_LIMIT_COUNTRY |
| rlmDomainCheckCountryPowerLimitTable(prAdapter); |
| #endif |
| |
| #ifdef CFG_DFS_CHSW_FORCE_BW20 |
| g_fgHasChannelSwitchIE = FALSE; |
| #endif |
| |
| kalMemZero(prRmRepParam, sizeof(*prRmRepParam)); |
| kalMemZero(prRmReqParam, sizeof(*prRmReqParam)); |
| prRmReqParam->rBcnRmParam.eState = RM_NO_REQUEST; |
| prRmReqParam->fgRmIsOngoing = FALSE; |
| LINK_INITIALIZE(&prRmRepParam->rFreeReportLink); |
| LINK_INITIALIZE(&prRmRepParam->rReportLink); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmFsmEventUninit(struct ADAPTER *prAdapter) |
| { |
| struct BSS_INFO *prBssInfo; |
| uint8_t i; |
| |
| ASSERT(prAdapter); |
| |
| for (i = 0; i < prAdapter->ucHwBssIdNum; i++) { |
| prBssInfo = prAdapter->aprBssInfo[i]; |
| |
| /* Note: all RLM timers will also be stopped. |
| * Now only one OBSS scan timer. |
| */ |
| rlmBssReset(prAdapter, prBssInfo); |
| } |
| rlmFreeMeasurementResources(prAdapter); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For association request, power capability |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmReqGeneratePowerCapIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| uint8_t *pucBuffer; |
| struct BSS_INFO *prBssInfo; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prMsduInfo->ucBssIndex); |
| |
| /* We should add power capability IE in assoc/reassoc req if |
| * the spectrum management bit is set to 1 in Capability Info |
| * field, or the connection will be rejected by Marvell APs in |
| * some TGn items. (e.g. 5.2.32). Spectrum management related |
| * feature (802.11h) is for 5G band. |
| */ |
| if (!prBssInfo || prBssInfo->eBand != BAND_5G) |
| return; |
| |
| pucBuffer = |
| (uint8_t *)(prMsduInfo->prPacket + prMsduInfo->u2FrameLength); |
| |
| POWER_CAP_IE(pucBuffer)->ucId = ELEM_ID_PWR_CAP; |
| POWER_CAP_IE(pucBuffer)->ucLength = ELEM_MAX_LEN_POWER_CAP; |
| POWER_CAP_IE(pucBuffer)->cMinTxPowerCap = 15; |
| POWER_CAP_IE(pucBuffer)->cMaxTxPowerCap = 20; |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(pucBuffer); |
| pucBuffer += IE_SIZE(pucBuffer); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For association request, supported channels |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmReqGenerateSupportedChIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| uint8_t *pucBuffer; |
| struct BSS_INFO *prBssInfo; |
| struct RF_CHANNEL_INFO auc2gChannelList[MAX_2G_BAND_CHN_NUM]; |
| struct RF_CHANNEL_INFO auc5gChannelList[MAX_5G_BAND_CHN_NUM]; |
| uint8_t ucNumOf2gChannel = 0; |
| uint8_t ucNumOf5gChannel = 0; |
| uint8_t ucChIdx = 0; |
| uint8_t ucIdx = 0; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prMsduInfo->ucBssIndex); |
| |
| /* We should add supported channels IE in assoc/reassoc request |
| * if the spectrum management bit is set to 1 in Capability Info |
| * field, or the connection will be rejected by Marvell APs in |
| * some TGn items. (e.g. 5.2.3). Spectrum management related |
| * feature (802.11h) is for 5G band. |
| */ |
| if (!prBssInfo || prBssInfo->eBand != BAND_5G) |
| return; |
| |
| pucBuffer = |
| (uint8_t *)(prMsduInfo->prPacket + prMsduInfo->u2FrameLength); |
| |
| rlmDomainGetChnlList(prAdapter, BAND_2G4, TRUE, MAX_2G_BAND_CHN_NUM, |
| &ucNumOf2gChannel, auc2gChannelList); |
| rlmDomainGetChnlList(prAdapter, BAND_5G, TRUE, MAX_5G_BAND_CHN_NUM, |
| &ucNumOf5gChannel, auc5gChannelList); |
| |
| SUP_CH_IE(pucBuffer)->ucId = ELEM_ID_SUP_CHS; |
| SUP_CH_IE(pucBuffer)->ucLength = |
| (ucNumOf2gChannel + ucNumOf5gChannel) * 2; |
| |
| for (ucIdx = 0; ucIdx < ucNumOf2gChannel; ucIdx++, ucChIdx += 2) { |
| SUP_CH_IE(pucBuffer)->ucChannelNum[ucChIdx] = |
| auc2gChannelList[ucIdx].ucChannelNum; |
| SUP_CH_IE(pucBuffer)->ucChannelNum[ucChIdx + 1] = 1; |
| } |
| |
| for (ucIdx = 0; ucIdx < ucNumOf5gChannel; ucIdx++, ucChIdx += 2) { |
| SUP_CH_IE(pucBuffer)->ucChannelNum[ucChIdx] = |
| auc5gChannelList[ucIdx].ucChannelNum; |
| SUP_CH_IE(pucBuffer)->ucChannelNum[ucChIdx + 1] = 1; |
| } |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(pucBuffer); |
| pucBuffer += IE_SIZE(pucBuffer); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe request, association request |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmReqGenerateHtCapIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| if ((prAdapter->rWifiVar.ucAvailablePhyTypeSet & |
| PHY_TYPE_SET_802_11N) && |
| (!prStaRec || (prStaRec->ucPhyTypeSet & PHY_TYPE_SET_802_11N))) |
| rlmFillHtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe request, association request |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmReqGenerateExtCapIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| if ((prAdapter->rWifiVar.ucAvailablePhyTypeSet & |
| PHY_TYPE_SET_802_11N) && |
| (!prStaRec || (prStaRec->ucPhyTypeSet & PHY_TYPE_SET_802_11N))) |
| rlmFillExtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| #if CFG_SUPPORT_PASSPOINT |
| else if (prAdapter->prGlueInfo->fgConnectHS20AP == TRUE) |
| hs20FillExtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| #endif /* CFG_SUPPORT_PASSPOINT */ |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe response (GO, IBSS) and association response |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmRspGenerateHtCapIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint8_t ucPhyTypeSet; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| if (!IS_BSS_ACTIVE(prBssInfo)) |
| return; |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| /* Decide PHY type set source */ |
| if (prStaRec) { |
| /* Get PHY type set from target STA */ |
| ucPhyTypeSet = prStaRec->ucPhyTypeSet; |
| } else { |
| /* Get PHY type set from current BSS */ |
| ucPhyTypeSet = prBssInfo->ucPhyTypeSet; |
| } |
| |
| if (RLM_NET_IS_11N(prBssInfo) && |
| (ucPhyTypeSet & PHY_TYPE_SET_802_11N) && |
| (!prBssInfo->fgIsWepCipherGroup)) |
| rlmFillHtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe response (GO, IBSS) and association response |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmRspGenerateExtCapIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint8_t ucPhyTypeSet; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| if (!IS_BSS_ACTIVE(prBssInfo)) |
| return; |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| /* Decide PHY type set source */ |
| if (prStaRec) { |
| /* Get PHY type set from target STA */ |
| ucPhyTypeSet = prStaRec->ucPhyTypeSet; |
| } else { |
| /* Get PHY type set from current BSS */ |
| ucPhyTypeSet = prBssInfo->ucPhyTypeSet; |
| } |
| |
| if (RLM_NET_IS_11N(prBssInfo) && (ucPhyTypeSet & PHY_TYPE_SET_802_11N)) |
| rlmFillExtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe response (GO, IBSS) and association response |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmRspGenerateHtOpIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint8_t ucPhyTypeSet; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| if (!IS_BSS_ACTIVE(prBssInfo)) |
| return; |
| |
| /* Decide PHY type set source */ |
| if (prStaRec) { |
| /* Get PHY type set from target STA */ |
| ucPhyTypeSet = prStaRec->ucPhyTypeSet; |
| } else { |
| /* Get PHY type set from current BSS */ |
| ucPhyTypeSet = prBssInfo->ucPhyTypeSet; |
| } |
| |
| if (RLM_NET_IS_11N(prBssInfo) && |
| (ucPhyTypeSet & PHY_TYPE_SET_802_11N) && |
| (!prBssInfo->fgIsWepCipherGroup)) |
| rlmFillHtOpIE(prAdapter, prBssInfo, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe response (GO, IBSS) and association response |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmRspGenerateErpIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| struct IE_ERP *prErpIe; |
| uint8_t ucPhyTypeSet; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| if (!IS_BSS_ACTIVE(prBssInfo)) |
| return; |
| |
| /* Decide PHY type set source */ |
| if (prStaRec) { |
| /* Get PHY type set from target STA */ |
| ucPhyTypeSet = prStaRec->ucPhyTypeSet; |
| } else { |
| /* Get PHY type set from current BSS */ |
| ucPhyTypeSet = prBssInfo->ucPhyTypeSet; |
| } |
| |
| if (RLM_NET_IS_11GN(prBssInfo) && prBssInfo->eBand == BAND_2G4 && |
| (ucPhyTypeSet & PHY_TYPE_SET_802_11GN)) { |
| prErpIe = (struct IE_ERP *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| /* Add ERP IE */ |
| prErpIe->ucId = ELEM_ID_ERP_INFO; |
| prErpIe->ucLength = 1; |
| |
| prErpIe->ucERP = prBssInfo->fgObssErpProtectMode |
| ? ERP_INFO_USE_PROTECTION |
| : 0; |
| |
| if (prBssInfo->fgErpProtectMode) |
| prErpIe->ucERP |= (ERP_INFO_NON_ERP_PRESENT | |
| ERP_INFO_USE_PROTECTION); |
| |
| /* Handle barker preamble */ |
| if (!prBssInfo->fgUseShortPreamble) |
| prErpIe->ucERP |= ERP_INFO_BARKER_PREAMBLE_MODE; |
| |
| ASSERT(IE_SIZE(prErpIe) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_ERP)); |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prErpIe); |
| } |
| } |
| |
| #if CFG_SUPPORT_MTK_SYNERGY |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function is used to generate MTK Vendor Specific OUI |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmGenerateMTKOuiIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| uint8_t *pucBuffer; |
| uint8_t aucMtkOui[] = VENDOR_OUI_MTK; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| if (prAdapter->rWifiVar.ucMtkOui == FEATURE_DISABLED) |
| return; |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| pucBuffer = (uint8_t *)((unsigned long)prMsduInfo->prPacket + |
| (unsigned long)prMsduInfo->u2FrameLength); |
| |
| MTK_OUI_IE(pucBuffer)->ucId = ELEM_ID_VENDOR; |
| MTK_OUI_IE(pucBuffer)->ucLength = ELEM_MIN_LEN_MTK_OUI; |
| MTK_OUI_IE(pucBuffer)->aucOui[0] = aucMtkOui[0]; |
| MTK_OUI_IE(pucBuffer)->aucOui[1] = aucMtkOui[1]; |
| MTK_OUI_IE(pucBuffer)->aucOui[2] = aucMtkOui[2]; |
| MTK_OUI_IE(pucBuffer)->aucCapability[0] = |
| MTK_SYNERGY_CAP0 & (prAdapter->rWifiVar.aucMtkFeature[0]); |
| MTK_OUI_IE(pucBuffer)->aucCapability[1] = |
| MTK_SYNERGY_CAP1 & (prAdapter->rWifiVar.aucMtkFeature[1]); |
| MTK_OUI_IE(pucBuffer)->aucCapability[2] = |
| MTK_SYNERGY_CAP2 & (prAdapter->rWifiVar.aucMtkFeature[2]); |
| MTK_OUI_IE(pucBuffer)->aucCapability[3] = |
| MTK_SYNERGY_CAP3 & (prAdapter->rWifiVar.aucMtkFeature[3]); |
| |
| /* Disable the 2.4G 256QAM feature bit if chip doesn't support AC*/ |
| if (prAdapter->rWifiVar.ucHwNotSupportAC) { |
| MTK_OUI_IE(pucBuffer)->aucCapability[0] &= |
| ~(MTK_SYNERGY_CAP_SUPPORT_24G_MCS89); |
| DBGLOG(INIT, WARN, |
| "Disable 2.4G 256QAM support if N only chip\n"); |
| } |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(pucBuffer); |
| pucBuffer += IE_SIZE(pucBuffer); |
| } /* rlmGenerateMTKOuiIE */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * @brief This function is used to check MTK Vendor Specific OUI |
| * |
| * |
| * @return true: correct MTK OUI |
| * false: incorrect MTK OUI |
| */ |
| /*----------------------------------------------------------------------------*/ |
| u_int8_t rlmParseCheckMTKOuiIE(IN struct ADAPTER *prAdapter, IN uint8_t *pucBuf, |
| IN uint32_t *pu4Cap) |
| { |
| uint8_t aucMtkOui[] = VENDOR_OUI_MTK; |
| struct IE_MTK_OUI *prMtkOuiIE = (struct IE_MTK_OUI *)NULL; |
| |
| do { |
| ASSERT_BREAK((prAdapter != NULL) && (pucBuf != NULL)); |
| |
| prMtkOuiIE = (struct IE_MTK_OUI *)pucBuf; |
| |
| if (prAdapter->rWifiVar.ucMtkOui == FEATURE_DISABLED) |
| break; |
| else if (IE_LEN(pucBuf) < ELEM_MIN_LEN_MTK_OUI) |
| break; |
| else if (prMtkOuiIE->aucOui[0] != aucMtkOui[0] || |
| prMtkOuiIE->aucOui[1] != aucMtkOui[1] || |
| prMtkOuiIE->aucOui[2] != aucMtkOui[2]) |
| break; |
| /* apply NvRam setting */ |
| prMtkOuiIE->aucCapability[0] = |
| prMtkOuiIE->aucCapability[0] & |
| (prAdapter->rWifiVar.aucMtkFeature[0]); |
| prMtkOuiIE->aucCapability[1] = |
| prMtkOuiIE->aucCapability[1] & |
| (prAdapter->rWifiVar.aucMtkFeature[1]); |
| prMtkOuiIE->aucCapability[2] = |
| prMtkOuiIE->aucCapability[2] & |
| (prAdapter->rWifiVar.aucMtkFeature[2]); |
| prMtkOuiIE->aucCapability[3] = |
| prMtkOuiIE->aucCapability[3] & |
| (prAdapter->rWifiVar.aucMtkFeature[3]); |
| |
| /* Disable the 2.4G 256QAM feature bit if chip doesn't support |
| * AC. Otherwise, FW would choose wrong max rate of auto rate. |
| */ |
| if (prAdapter->rWifiVar.ucHwNotSupportAC) { |
| prMtkOuiIE->aucCapability[0] &= |
| ~(MTK_SYNERGY_CAP_SUPPORT_24G_MCS89); |
| DBGLOG(INIT, WARN, |
| "Disable 2.4G 256QAM support if N only chip\n"); |
| } |
| |
| kalMemCopy(pu4Cap, prMtkOuiIE->aucCapability, sizeof(uint32_t)); |
| |
| return TRUE; |
| } while (FALSE); |
| |
| return FALSE; |
| } /* rlmParseCheckMTKOuiIE */ |
| |
| #endif |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmGenerateCsaIE(struct ADAPTER *prAdapter, struct MSDU_INFO *prMsduInfo) |
| { |
| uint8_t *pucBuffer; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| if (prAdapter->rWifiVar.fgCsaInProgress) { |
| |
| pucBuffer = |
| (uint8_t *)((unsigned long)prMsduInfo->prPacket + |
| (unsigned long)prMsduInfo->u2FrameLength); |
| |
| CSA_IE(pucBuffer)->ucId = ELEM_ID_CH_SW_ANNOUNCEMENT; |
| CSA_IE(pucBuffer)->ucLength = ELEM_MIN_LEN_CSA; |
| CSA_IE(pucBuffer)->ucChannelSwitchMode = |
| prAdapter->rWifiVar.ucChannelSwitchMode; |
| CSA_IE(pucBuffer)->ucNewChannelNum = |
| prAdapter->rWifiVar.ucNewChannelNumber; |
| CSA_IE(pucBuffer)->ucChannelSwitchCount = |
| prAdapter->rWifiVar.ucChannelSwitchCount; |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(pucBuffer); |
| pucBuffer += IE_SIZE(pucBuffer); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmFillHtCapIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct IE_HT_CAP *prHtCap; |
| struct SUP_MCS_SET_FIELD *prSupMcsSet; |
| u_int8_t fg40mAllowed; |
| uint8_t ucIdx; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(prMsduInfo); |
| |
| fg40mAllowed = prBssInfo->fgAssoc40mBwAllowed; |
| |
| prHtCap = (struct IE_HT_CAP *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| /* Add HT capabilities IE */ |
| prHtCap->ucId = ELEM_ID_HT_CAP; |
| prHtCap->ucLength = sizeof(struct IE_HT_CAP) - ELEM_HDR_LEN; |
| |
| prHtCap->u2HtCapInfo = HT_CAP_INFO_DEFAULT_VAL; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxShortGI)) |
| prHtCap->u2HtCapInfo |= |
| (HT_CAP_INFO_SHORT_GI_20M | HT_CAP_INFO_SHORT_GI_40M); |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxLdpc)) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_LDPC_CAP; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucTxStbc)) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_TX_STBC; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxStbc)) { |
| |
| uint8_t tempRxStbcNss; |
| |
| tempRxStbcNss = prAdapter->rWifiVar.ucRxStbcNss; |
| tempRxStbcNss = |
| (tempRxStbcNss > |
| wlanGetSupportNss(prAdapter, prBssInfo->ucBssIndex)) |
| ? wlanGetSupportNss(prAdapter, |
| prBssInfo->ucBssIndex) |
| : (tempRxStbcNss); |
| if (tempRxStbcNss != prAdapter->rWifiVar.ucRxStbcNss) { |
| DBGLOG(RLM, WARN, "Apply Nss:%d as RxStbcNss in HT Cap", |
| wlanGetSupportNss(prAdapter, |
| prBssInfo->ucBssIndex)); |
| DBGLOG(RLM, WARN, |
| " due to set RxStbcNss more than Nss is not appropriate.\n"); |
| } |
| if (tempRxStbcNss == 1) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_RX_STBC_1_SS; |
| else if (tempRxStbcNss == 2) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_RX_STBC_2_SS; |
| else if (tempRxStbcNss >= 3) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_RX_STBC_3_SS; |
| } |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxGf)) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_HT_GF; |
| |
| if (prAdapter->rWifiVar.ucRxMaxMpduLen > VHT_CAP_INFO_MAX_MPDU_LEN_3K) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_MAX_AMSDU_LEN; |
| |
| if (!fg40mAllowed) |
| prHtCap->u2HtCapInfo &= ~(HT_CAP_INFO_SUP_CHNL_WIDTH | |
| HT_CAP_INFO_SHORT_GI_40M | |
| HT_CAP_INFO_DSSS_CCK_IN_40M); |
| |
| /* SM power saving */ /* TH3_Huang */ |
| if (prBssInfo->ucNss < |
| wlanGetSupportNss(prAdapter, prBssInfo->ucBssIndex)) |
| prHtCap->u2HtCapInfo &= |
| ~HT_CAP_INFO_SM_POWER_SAVE; /*Set as static power save*/ |
| |
| prHtCap->ucAmpduParam = AMPDU_PARAM_DEFAULT_VAL; |
| |
| prSupMcsSet = &prHtCap->rSupMcsSet; |
| kalMemZero((void *)&prSupMcsSet->aucRxMcsBitmask[0], |
| SUP_MCS_RX_BITMASK_OCTET_NUM); |
| |
| for (ucIdx = 0; |
| ucIdx < wlanGetSupportNss(prAdapter, prBssInfo->ucBssIndex); |
| ucIdx++) |
| prSupMcsSet->aucRxMcsBitmask[ucIdx] = BITS(0, 7); |
| |
| /* prSupMcsSet->aucRxMcsBitmask[0] = BITS(0, 7); */ |
| |
| if (fg40mAllowed && IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucMCS32)) |
| prSupMcsSet->aucRxMcsBitmask[32 / 8] = BIT(0); /* MCS32 */ |
| prSupMcsSet->u2RxHighestSupportedRate = SUP_MCS_RX_DEFAULT_HIGHEST_RATE; |
| prSupMcsSet->u4TxRateInfo = SUP_MCS_TX_DEFAULT_VAL; |
| |
| prHtCap->u2HtExtendedCap = HT_EXT_CAP_DEFAULT_VAL; |
| if (!fg40mAllowed || |
| prBssInfo->eCurrentOPMode != OP_MODE_INFRASTRUCTURE) |
| prHtCap->u2HtExtendedCap &= |
| ~(HT_EXT_CAP_PCO | HT_EXT_CAP_PCO_TRANS_TIME_NONE); |
| |
| prHtCap->u4TxBeamformingCap = TX_BEAMFORMING_CAP_DEFAULT_VAL; |
| if ((prAdapter->rWifiVar.eDbdcMode == ENUM_DBDC_MODE_DISABLED) || |
| (prBssInfo->eBand == BAND_5G)) { |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucStaHtBfee)) |
| prHtCap->u4TxBeamformingCap = TX_BEAMFORMING_CAP_BFEE; |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucStaHtBfer)) |
| prHtCap->u4TxBeamformingCap |= TX_BEAMFORMING_CAP_BFER; |
| } |
| |
| prHtCap->ucAselCap = ASEL_CAP_DEFAULT_VAL; |
| |
| ASSERT(IE_SIZE(prHtCap) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_HT_CAP)); |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prHtCap); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmFillExtCapIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| #if CFG_SUPPORT_PASSPOINT |
| struct IE_HS20_EXT_CAP_T *prHsExtCap; |
| #else |
| struct IE_EXT_CAP *prExtCap; |
| #endif |
| u_int8_t fg40mAllowed, fgAppendVhtCap; |
| struct STA_RECORD *prStaRec; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| fg40mAllowed = prBssInfo->fgAssoc40mBwAllowed; |
| |
| #if CFG_SUPPORT_PASSPOINT |
| prHsExtCap = |
| (struct IE_HS20_EXT_CAP_T *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| prHsExtCap->ucId = ELEM_ID_EXTENDED_CAP; |
| |
| if (prAdapter->prGlueInfo->fgConnectHS20AP == TRUE) |
| prHsExtCap->ucLength = ELEM_MAX_LEN_EXT_CAP; |
| else |
| prHsExtCap->ucLength = 3 - ELEM_HDR_LEN; |
| |
| kalMemZero(prHsExtCap->aucCapabilities, |
| sizeof(prHsExtCap->aucCapabilities)); |
| |
| prHsExtCap->aucCapabilities[0] = ELEM_EXT_CAP_DEFAULT_VAL; |
| |
| if (!fg40mAllowed) |
| prHsExtCap->aucCapabilities[0] &= |
| ~ELEM_EXT_CAP_20_40_COEXIST_SUPPORT; |
| |
| if (prBssInfo->eCurrentOPMode != OP_MODE_INFRASTRUCTURE) |
| prHsExtCap->aucCapabilities[0] &= ~ELEM_EXT_CAP_PSMP_CAP; |
| |
| #if CFG_SUPPORT_802_11AC |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| fgAppendVhtCap = FALSE; |
| |
| /* Check append rule */ |
| if (prAdapter->rWifiVar.ucAvailablePhyTypeSet |
| & PHY_TYPE_SET_802_11AC) { |
| /* Note: For AIS connecting state, |
| * structure in BSS_INFO will not be inited |
| * So, we check StaRec instead of BssInfo |
| */ |
| if (prStaRec) { |
| if (prStaRec->ucPhyTypeSet & PHY_TYPE_SET_802_11AC) |
| fgAppendVhtCap = TRUE; |
| } else if (RLM_NET_IS_11AC(prBssInfo) && |
| ((prBssInfo->eCurrentOPMode == |
| OP_MODE_INFRASTRUCTURE) || |
| (prBssInfo->eCurrentOPMode == |
| OP_MODE_ACCESS_POINT))) { |
| fgAppendVhtCap = TRUE; |
| } |
| |
| } |
| |
| if (fgAppendVhtCap) { |
| if (prHsExtCap->ucLength < ELEM_MAX_LEN_EXT_CAP) |
| prHsExtCap->ucLength = ELEM_MAX_LEN_EXT_CAP; |
| |
| SET_EXT_CAP(prHsExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_OP_MODE_NOTIFICATION_BIT); |
| } |
| #endif |
| |
| if (prAdapter->prGlueInfo->fgConnectHS20AP == TRUE) { |
| SET_EXT_CAP(prHsExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_INTERWORKING_BIT); |
| SET_EXT_CAP(prHsExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_QOSMAPSET_BIT); |
| SET_EXT_CAP(prHsExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_BSS_TRANSITION_BIT); |
| /* For R2 WNM-Notification */ |
| SET_EXT_CAP(prHsExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_WNM_NOTIFICATION_BIT); |
| } |
| |
| #if CFG_SUPPORT_802_11V_BSS_TRANSITION_MGT |
| prHsExtCap->ucLength = ELEM_MAX_LEN_EXT_CAP; |
| SET_EXT_CAP(prHsExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_BSS_TRANSITION_BIT); |
| #endif |
| |
| ASSERT(IE_SIZE(prHsExtCap) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_EXT_CAP)); |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prHsExtCap); |
| |
| #else |
| /* Add Extended Capabilities IE */ |
| prExtCap = (struct IE_EXT_CAP *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| prExtCap->ucId = ELEM_ID_EXTENDED_CAP; |
| |
| prExtCap->ucLength = 3 - ELEM_HDR_LEN; |
| kalMemZero(prExtCap->aucCapabilities, |
| sizeof(prExtCap->aucCapabilities)); |
| |
| prExtCap->aucCapabilities[0] = ELEM_EXT_CAP_DEFAULT_VAL; |
| |
| if (!fg40mAllowed) |
| prExtCap->aucCapabilities[0] &= |
| ~ELEM_EXT_CAP_20_40_COEXIST_SUPPORT; |
| |
| if (prBssInfo->eCurrentOPMode != OP_MODE_INFRASTRUCTURE) |
| prExtCap->aucCapabilities[0] &= ~ELEM_EXT_CAP_PSMP_CAP; |
| |
| #if CFG_SUPPORT_802_11AC |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| fgAppendVhtCap = FALSE; |
| |
| /* Check append rule */ |
| if (prAdapter->rWifiVar.ucAvailablePhyTypeSet |
| & PHY_TYPE_SET_802_11AC) { |
| /* Note: For AIS connecting state, |
| * structure in BSS_INFO will not be inited |
| * So, we check StaRec instead of BssInfo |
| */ |
| if (prStaRec) { |
| if (prStaRec->ucPhyTypeSet & PHY_TYPE_SET_802_11AC) |
| fgAppendVhtCap = TRUE; |
| } else if (RLM_NET_IS_11AC(prBssInfo) && |
| ((prBssInfo->eCurrentOPMode == |
| OP_MODE_INFRASTRUCTURE) || |
| (prBssInfo->eCurrentOPMode == |
| OP_MODE_ACCESS_POINT))) { |
| fgAppendVhtCap = TRUE; |
| } |
| } |
| |
| if (fgAppendVhtCap) { |
| if (prExtCap->ucLength < ELEM_MAX_LEN_EXT_CAP) |
| prExtCap->ucLength = ELEM_MAX_LEN_EXT_CAP; |
| |
| SET_EXT_CAP(prExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_OP_MODE_NOTIFICATION_BIT); |
| } |
| #endif |
| |
| #if CFG_SUPPORT_802_11V_BSS_TRANSITION_MGT |
| prExtCap->ucLength = ELEM_MAX_LEN_EXT_CAP; |
| SET_EXT_CAP(prExtCap->aucCapabilities, ELEM_MAX_LEN_EXT_CAP, |
| ELEM_EXT_CAP_BSS_TRANSITION_BIT); |
| #endif |
| |
| ASSERT(IE_SIZE(prExtCap) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_EXT_CAP)); |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prExtCap); |
| #endif |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmFillHtOpIE(struct ADAPTER *prAdapter, struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct IE_HT_OP *prHtOp; |
| uint16_t i; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(prMsduInfo); |
| |
| prHtOp = (struct IE_HT_OP *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| /* Add HT operation IE */ |
| prHtOp->ucId = ELEM_ID_HT_OP; |
| prHtOp->ucLength = sizeof(struct IE_HT_OP) - ELEM_HDR_LEN; |
| |
| /* RIFS and 20/40 bandwidth operations are included */ |
| prHtOp->ucPrimaryChannel = prBssInfo->ucPrimaryChannel; |
| prHtOp->ucInfo1 = prBssInfo->ucHtOpInfo1; |
| |
| /* Decide HT protection mode field */ |
| if (prBssInfo->eHtProtectMode == HT_PROTECT_MODE_NON_HT) |
| prHtOp->u2Info2 = (uint8_t)HT_PROTECT_MODE_NON_HT; |
| else if (prBssInfo->eObssHtProtectMode == HT_PROTECT_MODE_NON_MEMBER) |
| prHtOp->u2Info2 = (uint8_t)HT_PROTECT_MODE_NON_MEMBER; |
| else { |
| /* It may be SYS_PROTECT_MODE_NONE or SYS_PROTECT_MODE_20M */ |
| prHtOp->u2Info2 = (uint8_t)prBssInfo->eHtProtectMode; |
| } |
| |
| if (prBssInfo->eGfOperationMode != GF_MODE_NORMAL) { |
| /* It may be GF_MODE_PROTECT or GF_MODE_DISALLOWED |
| * Note: it will also be set in ad-hoc network |
| */ |
| prHtOp->u2Info2 |= HT_OP_INFO2_NON_GF_HT_STA_PRESENT; |
| } |
| |
| if (0 /* Regulatory class 16 */ && |
| prBssInfo->eObssHtProtectMode == HT_PROTECT_MODE_NON_MEMBER) { |
| /* (TBD) It is HT_PROTECT_MODE_NON_MEMBER, so require protection |
| * although it is possible to have no protection by spec. |
| */ |
| prHtOp->u2Info2 |= HT_OP_INFO2_OBSS_NON_HT_STA_PRESENT; |
| } |
| |
| prHtOp->u2Info3 = prBssInfo->u2HtOpInfo3; /* To do: handle L-SIG TXOP */ |
| |
| /* No basic MCSx are needed temporarily */ |
| for (i = 0; i < 16; i++) |
| prHtOp->aucBasicMcsSet[i] = 0; |
| |
| ASSERT(IE_SIZE(prHtOp) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_HT_OP)); |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prHtOp); |
| } |
| |
| #if CFG_SUPPORT_802_11AC |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe request, association request |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmReqGenerateVhtCapIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| #if CFG_SUPPORT_VHT_IE_IN_2G |
| struct BSS_DESC *prBssDesc = NULL; |
| u_int8_t fgIsVHTPresent = FALSE; |
| #endif |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| if (prAdapter) { |
| prBssDesc = prAdapter->rWifiVar.rAisFsmInfo.prTargetBssDesc; |
| if (prBssDesc) { |
| fgIsVHTPresent = prBssDesc->fgIsVHTPresent; |
| DBGLOG(RLM, TRACE, "fgIsVHTPresent=%d", fgIsVHTPresent); |
| } |
| } else { |
| DBGLOG(RLM, ERROR, "prAdapter is NULL, return!"); |
| return; |
| } |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) { |
| DBGLOG(RLM, ERROR, "prBssInfo is NULL, return!"); |
| return; |
| } |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| if (!prStaRec) { |
| DBGLOG(RLM, ERROR, "prStaRec is NULL, return!"); |
| return; |
| } |
| |
| if ((prAdapter->rWifiVar.ucAvailablePhyTypeSet & PHY_TYPE_SET_802_11AC) |
| && (prStaRec->ucPhyTypeSet & PHY_TYPE_SET_802_11AC)) |
| rlmFillVhtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| #if CFG_SUPPORT_VHT_IE_IN_2G |
| else if ((prBssInfo->eBand == BAND_2G4) && |
| (prStaRec->ucPhyTypeSet & PHY_TYPE_SET_802_11N) && |
| ((prAdapter->rWifiVar.ucVhtIeIn2g |
| == FEATURE_FORCE_ENABLED) || |
| ((prAdapter->rWifiVar.ucVhtIeIn2g |
| == FEATURE_ENABLED) && fgIsVHTPresent))) { |
| rlmFillVhtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| DBGLOG(RLM, TRACE, "Add VHT IE in 2.4G, ucPhyTypeSet=%02x", |
| prStaRec->ucPhyTypeSet); |
| } |
| #endif |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe response (GO, IBSS) and association response |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmRspGenerateVhtCapIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint8_t ucPhyTypeSet; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| if (!IS_BSS_ACTIVE(prBssInfo)) |
| return; |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| /* Decide PHY type set source */ |
| if (prStaRec) { |
| /* Get PHY type set from target STA */ |
| ucPhyTypeSet = prStaRec->ucPhyTypeSet; |
| } else { |
| /* Get PHY type set from current BSS */ |
| ucPhyTypeSet = prBssInfo->ucPhyTypeSet; |
| } |
| |
| if (RLM_NET_IS_11AC(prBssInfo) && |
| (ucPhyTypeSet & PHY_TYPE_SET_802_11AC)) |
| rlmFillVhtCapIE(prAdapter, prBssInfo, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmRspGenerateVhtOpIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint8_t ucPhyTypeSet; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| if (!IS_BSS_ACTIVE(prBssInfo)) |
| return; |
| |
| /* Decide PHY type set source */ |
| if (prStaRec) { |
| /* Get PHY type set from target STA */ |
| ucPhyTypeSet = prStaRec->ucPhyTypeSet; |
| } else { |
| /* Get PHY type set from current BSS */ |
| ucPhyTypeSet = prBssInfo->ucPhyTypeSet; |
| } |
| |
| if (RLM_NET_IS_11AC(prBssInfo) && |
| (ucPhyTypeSet & PHY_TYPE_SET_802_11AC)) |
| rlmFillVhtOpIE(prAdapter, prBssInfo, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief For probe request, association request |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmReqGenerateVhtOpNotificationIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| if ((prAdapter->rWifiVar.ucAvailablePhyTypeSet & |
| PHY_TYPE_SET_802_11AC) && |
| (!prStaRec || (prStaRec->ucPhyTypeSet & PHY_TYPE_SET_802_11AC))) { |
| /* Fill own capability in channel width field in OP mode element |
| * since we haven't filled in channel width info in BssInfo at |
| * current state |
| */ |
| rlmFillVhtOpNotificationIE(prAdapter, prBssInfo, prMsduInfo, |
| FALSE); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmRspGenerateVhtOpNotificationIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint8_t ucPhyTypeSet; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prMsduInfo->ucStaRecIndex); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| if (!prBssInfo) |
| return; |
| |
| if (!IS_BSS_ACTIVE(prBssInfo)) |
| return; |
| |
| /* Decide PHY type set source */ |
| if (prStaRec) { |
| /* Get PHY type set from target STA */ |
| ucPhyTypeSet = prStaRec->ucPhyTypeSet; |
| } else { |
| /* Get PHY type set from current BSS */ |
| ucPhyTypeSet = prBssInfo->ucPhyTypeSet; |
| } |
| |
| if (RLM_NET_IS_11AC(prBssInfo) && |
| (ucPhyTypeSet & PHY_TYPE_SET_802_11AC)) |
| rlmFillVhtOpNotificationIE(prAdapter, prBssInfo, prMsduInfo, |
| FALSE); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * add VHT operation notification IE for VHT-BW40 case specific |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmFillVhtOpNotificationIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo, |
| u_int8_t fgIsOwnCap) |
| { |
| struct IE_VHT_OP_MODE_NOTIFICATION *prVhtOpMode; |
| uint8_t ucOpModeBw = VHT_OP_MODE_CHANNEL_WIDTH_20; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(prMsduInfo); |
| |
| prVhtOpMode = (struct IE_VHT_OP_MODE_NOTIFICATION |
| *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| kalMemZero((void *)prVhtOpMode, |
| sizeof(struct IE_VHT_OP_MODE_NOTIFICATION)); |
| |
| prVhtOpMode->ucId = ELEM_ID_OP_MODE; |
| prVhtOpMode->ucLength = |
| sizeof(struct IE_VHT_OP_MODE_NOTIFICATION) - ELEM_HDR_LEN; |
| |
| DBGLOG(RLM, TRACE, "rlmFillVhtOpNotificationIE(%d) %u %u\n", |
| prBssInfo->ucBssIndex, fgIsOwnCap, prBssInfo->ucNss); |
| |
| if (fgIsOwnCap) { |
| ucOpModeBw = cnmGetDbdcBwCapability(prAdapter, |
| prBssInfo->ucBssIndex); |
| |
| /*handle 80P80 case*/ |
| if (ucOpModeBw >= MAX_BW_160MHZ) |
| ucOpModeBw = VHT_OP_MODE_CHANNEL_WIDTH_160_80P80; |
| |
| prVhtOpMode->ucOperatingMode |= ucOpModeBw; |
| prVhtOpMode->ucOperatingMode |= |
| (((prBssInfo->ucNss - 1) << VHT_OP_MODE_RX_NSS_OFFSET) & |
| VHT_OP_MODE_RX_NSS); |
| |
| } else { |
| |
| ucOpModeBw = rlmGetOpModeBwByVhtAndHtOpInfo(prBssInfo); |
| |
| prVhtOpMode->ucOperatingMode |= ucOpModeBw; |
| prVhtOpMode->ucOperatingMode |= |
| (((prBssInfo->ucNss - 1) << VHT_OP_MODE_RX_NSS_OFFSET) & |
| VHT_OP_MODE_RX_NSS); |
| } |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prVhtOpMode); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmFillVhtCapIE(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct IE_VHT_CAP *prVhtCap; |
| struct VHT_SUPPORTED_MCS_FIELD *prVhtSupportedMcsSet; |
| uint8_t i; |
| uint8_t ucMaxBw; |
| struct STA_RECORD *prStaRec; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(prMsduInfo); |
| |
| prVhtCap = (struct IE_VHT_CAP *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| prVhtCap->ucId = ELEM_ID_VHT_CAP; |
| prVhtCap->ucLength = sizeof(struct IE_VHT_CAP) - ELEM_HDR_LEN; |
| prVhtCap->u4VhtCapInfo = VHT_CAP_INFO_DEFAULT_VAL; |
| |
| ucMaxBw = cnmGetBssMaxBw(prAdapter, prBssInfo->ucBssIndex); |
| |
| prVhtCap->u4VhtCapInfo |= (prAdapter->rWifiVar.ucRxMaxMpduLen & |
| VHT_CAP_INFO_MAX_MPDU_LEN_MASK); |
| #if CFG_SUPPORT_VHT_IE_IN_2G |
| if (prBssInfo->eBand == BAND_2G4) { |
| prVhtCap->u4VhtCapInfo |= |
| VHT_CAP_INFO_MAX_SUP_CHANNEL_WIDTH_SET_NONE; |
| } else { |
| #endif |
| if (ucMaxBw == MAX_BW_160MHZ) |
| prVhtCap->u4VhtCapInfo |= |
| VHT_CAP_INFO_MAX_SUP_CHANNEL_WIDTH_SET_160; |
| else if (ucMaxBw == MAX_BW_80_80_MHZ) |
| prVhtCap->u4VhtCapInfo |= |
| VHT_CAP_INFO_MAX_SUP_CHANNEL_WIDTH_SET_160_80P80; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucStaVhtBfee)) { |
| prVhtCap->u4VhtCapInfo |= FIELD_VHT_CAP_INFO_BFEE; |
| #if CFG_SUPPORT_BFEE |
| prStaRec = cnmGetStaRecByIndex(prAdapter, |
| prMsduInfo->ucStaRecIndex); |
| |
| if (prStaRec && |
| (prStaRec->ucVhtCapNumSoundingDimensions == 0x2) && |
| !prAdapter->rWifiVar.fgForceSTSNum) { |
| /* For the compatibility with netgear R7000 AP */ |
| prVhtCap->u4VhtCapInfo |= |
| (((uint32_t)prStaRec->ucVhtCapNumSoundingDimensions) |
| << VHT_CAP_INFO_COMPRESSED_STEERING_NUMBER_OF_BEAMFORMER_ANTENNAS_SUP_OFF); |
| DBGLOG(RLM, INFO, "Set VHT Cap BFEE STS CAP=%d\n", |
| prStaRec->ucVhtCapNumSoundingDimensions); |
| } else { |
| /* For 11ac cert. VHT-5.2.63C MU-BFee step3, |
| * it requires STAUT to set its maximum STS capability here |
| */ |
| prVhtCap->u4VhtCapInfo |= |
| VHT_CAP_INFO_COMPRESSED_STEERING_NUMBER_OF_BEAMFORMER_ANTENNAS_4_SUP; |
| DBGLOG(RLM, TRACE, "Set VHT Cap BFEE STS CAP=%d\n", |
| VHT_CAP_INFO_BEAMFORMEE_STS_CAP_MAX); |
| } |
| /* DBGLOG(RLM, INFO, "VhtCapInfo=%x\n", prVhtCap->u4VhtCapInfo); */ |
| #endif |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucStaVhtMuBfee)) |
| prVhtCap->u4VhtCapInfo |= |
| VHT_CAP_INFO_MU_BEAMFOMEE_CAPABLE; |
| } |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucStaVhtBfer)) |
| prVhtCap->u4VhtCapInfo |= FIELD_VHT_CAP_INFO_BFER; |
| #if CFG_SUPPORT_VHT_IE_IN_2G |
| } |
| #endif |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxShortGI)) { |
| if (ucMaxBw >= MAX_BW_80MHZ) |
| prVhtCap->u4VhtCapInfo |= VHT_CAP_INFO_SHORT_GI_80; |
| |
| if (ucMaxBw >= MAX_BW_160MHZ) |
| prVhtCap->u4VhtCapInfo |= |
| VHT_CAP_INFO_SHORT_GI_160_80P80; |
| } |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxLdpc)) |
| prVhtCap->u4VhtCapInfo |= VHT_CAP_INFO_RX_LDPC; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxStbc)) { |
| uint8_t tempRxStbcNss; |
| |
| if (prAdapter->rWifiVar.u4SwTestMode == |
| ENUM_SW_TEST_MODE_SIGMA_AC) { |
| tempRxStbcNss = 1; |
| DBGLOG(RLM, INFO, |
| "Set RxStbcNss to 1 for 11ac certification.\n"); |
| } else { |
| tempRxStbcNss = prAdapter->rWifiVar.ucRxStbcNss; |
| tempRxStbcNss = |
| (tempRxStbcNss > |
| wlanGetSupportNss(prAdapter, |
| prBssInfo->ucBssIndex)) |
| ? wlanGetSupportNss( |
| prAdapter, |
| prBssInfo->ucBssIndex) |
| : (tempRxStbcNss); |
| if (tempRxStbcNss != prAdapter->rWifiVar.ucRxStbcNss) { |
| DBGLOG(RLM, WARN, |
| "Apply Nss:%d as RxStbcNss in VHT Cap", |
| wlanGetSupportNss( |
| prAdapter, |
| prBssInfo->ucBssIndex)); |
| DBGLOG(RLM, WARN, |
| "due to set RxStbcNss more than Nss is not appropriate.\n"); |
| } |
| } |
| prVhtCap->u4VhtCapInfo |= |
| ((tempRxStbcNss << VHT_CAP_INFO_RX_STBC_OFFSET) & |
| VHT_CAP_INFO_RX_STBC_MASK); |
| } |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucTxStbc)) |
| prVhtCap->u4VhtCapInfo |= VHT_CAP_INFO_TX_STBC; |
| |
| /*set MCS map */ |
| prVhtSupportedMcsSet = &prVhtCap->rVhtSupportedMcsSet; |
| kalMemZero((void *)prVhtSupportedMcsSet, |
| sizeof(struct VHT_SUPPORTED_MCS_FIELD)); |
| |
| for (i = 0; i < 8; i++) { |
| uint8_t ucOffset = i * 2; |
| uint8_t ucMcsMap; |
| |
| if (i < wlanGetSupportNss(prAdapter, prBssInfo->ucBssIndex)) |
| ucMcsMap = VHT_CAP_INFO_MCS_MAP_MCS9; |
| else |
| ucMcsMap = VHT_CAP_INFO_MCS_NOT_SUPPORTED; |
| |
| prVhtSupportedMcsSet->u2RxMcsMap |= (ucMcsMap << ucOffset); |
| prVhtSupportedMcsSet->u2TxMcsMap |= (ucMcsMap << ucOffset); |
| } |
| |
| #if 0 |
| for (i = 0; i < wlanGetSupportNss(prAdapter, |
| prBssInfo->ucBssIndex); i++) { |
| uint8_t ucOffset = i * 2; |
| |
| prVhtSupportedMcsSet->u2RxMcsMap &= |
| ((VHT_CAP_INFO_MCS_MAP_MCS9 << ucOffset) & |
| BITS(ucOffset, ucOffset + 1)); |
| prVhtSupportedMcsSet->u2TxMcsMap &= |
| ((VHT_CAP_INFO_MCS_MAP_MCS9 << ucOffset) & |
| BITS(ucOffset, ucOffset + 1)); |
| } |
| #endif |
| |
| prVhtSupportedMcsSet->u2RxHighestSupportedDataRate = |
| VHT_CAP_INFO_DEFAULT_HIGHEST_DATA_RATE; |
| prVhtSupportedMcsSet->u2TxHighestSupportedDataRate = |
| VHT_CAP_INFO_DEFAULT_HIGHEST_DATA_RATE; |
| |
| ASSERT(IE_SIZE(prVhtCap) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_VHT_CAP)); |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prVhtCap); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmFillVhtOpIE(struct ADAPTER *prAdapter, struct BSS_INFO *prBssInfo, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct IE_VHT_OP *prVhtOp; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(prMsduInfo); |
| |
| prVhtOp = (struct IE_VHT_OP *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| /* Add HT operation IE */ |
| prVhtOp->ucId = ELEM_ID_VHT_OP; |
| prVhtOp->ucLength = sizeof(struct IE_VHT_OP) - ELEM_HDR_LEN; |
| |
| ASSERT(IE_SIZE(prVhtOp) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_VHT_OP)); |
| |
| /* (UINT8)VHT_OP_CHANNEL_WIDTH_80; */ |
| prVhtOp->ucVhtOperation[0] = prBssInfo->ucVhtChannelWidth; |
| prVhtOp->ucVhtOperation[1] = prBssInfo->ucVhtChannelFrequencyS1; |
| prVhtOp->ucVhtOperation[2] = prBssInfo->ucVhtChannelFrequencyS2; |
| |
| #if 0 |
| if (cnmGetBssMaxBw(prAdapter, prBssInfo->ucBssIndex) < MAX_BW_80MHZ) { |
| prVhtOp->ucVhtOperation[0] = VHT_OP_CHANNEL_WIDTH_20_40; |
| prVhtOp->ucVhtOperation[1] = 0; |
| prVhtOp->ucVhtOperation[2] = 0; |
| } else if (cnmGetBssMaxBw(prAdapter, prBssInfo->ucBssIndex) == |
| MAX_BW_80MHZ) { |
| prVhtOp->ucVhtOperation[0] = VHT_OP_CHANNEL_WIDTH_80; |
| prVhtOp->ucVhtOperation[1] = |
| nicGetVhtS1(prBssInfo->ucPrimaryChannel); |
| prVhtOp->ucVhtOperation[2] = 0; |
| } else { |
| /* TODO: BW80 + 80/160 support */ |
| } |
| #endif |
| |
| prVhtOp->u2VhtBasicMcsSet = prBssInfo->u2VhtBasicMcsSet; |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(prVhtOp); |
| } |
| |
| #endif |
| |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmGenerateCountryIE(struct ADAPTER *prAdapter, |
| struct MSDU_INFO *prMsduInfo) |
| { |
| struct BSS_INFO *prBssInfo = |
| prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| unsigned char *pucBuf = |
| (((unsigned char *) prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| |
| if (prBssInfo->aucCountryStr[0] == 0) |
| return; |
| |
| COUNTRY_IE(pucBuf)->ucId = ELEM_ID_COUNTRY_INFO; |
| COUNTRY_IE(pucBuf)->ucLength = prBssInfo->ucCountryIELen; |
| COUNTRY_IE(pucBuf)->aucCountryStr[0] = prBssInfo->aucCountryStr[0]; |
| COUNTRY_IE(pucBuf)->aucCountryStr[1] = prBssInfo->aucCountryStr[1]; |
| COUNTRY_IE(pucBuf)->aucCountryStr[2] = prBssInfo->aucCountryStr[2]; |
| kalMemCopy(COUNTRY_IE(pucBuf)->arCountryStr, |
| prBssInfo->aucSubbandTriplet, |
| prBssInfo->ucCountryIELen - 3); |
| |
| prMsduInfo->u2FrameLength += IE_SIZE(pucBuf); |
| } |
| |
| #if CFG_SUPPORT_CAL_RESULT_BACKUP_TO_HOST |
| uint32_t rlmCalBackup(struct ADAPTER *prAdapter, uint8_t ucReason, |
| uint8_t ucAction, uint8_t ucRomRam) |
| { |
| uint32_t rStatus = WLAN_STATUS_FAILURE; |
| struct GLUE_INFO *prGlueInfo = NULL; |
| struct PARAM_CAL_BACKUP_STRUCT_V2 rCalBackupDataV2; |
| uint32_t u4BufLen = 0; |
| |
| ASSERT(prAdapter); |
| ASSERT(prAdapter->prGlueInfo); |
| |
| prGlueInfo = prAdapter->prGlueInfo; |
| |
| rCalBackupDataV2.ucReason = ucReason; |
| rCalBackupDataV2.ucAction = ucAction; |
| rCalBackupDataV2.ucNeedResp = 1; |
| rCalBackupDataV2.ucFragNum = 0; |
| rCalBackupDataV2.ucRomRam = ucRomRam; |
| rCalBackupDataV2.u4ThermalValue = 0; |
| rCalBackupDataV2.u4Address = 0; |
| rCalBackupDataV2.u4Length = 0; |
| rCalBackupDataV2.u4RemainLength = 0; |
| |
| if (ucReason == 0 && ucAction == 0) { |
| DBGLOG(RFTEST, INFO, "RLM CMD : Get Thermal Temp from FW.\n"); |
| /* Step 1 : Get Thermal Temp from FW */ |
| |
| rStatus = kalIoctl(prGlueInfo, wlanoidQueryCalBackupV2, |
| &rCalBackupDataV2, |
| sizeof(struct PARAM_CAL_BACKUP_STRUCT_V2), |
| TRUE, TRUE, TRUE, &u4BufLen); |
| |
| if (rStatus != WLAN_STATUS_SUCCESS) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Get Thermal Temp from FW Return Fail (0x%08x)!!!!!!!!!!!\n", |
| rStatus); |
| return rStatus; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "CMD : Get Thermal Temp (%d) from FW. Finish!!!!!!!!!!!\n", |
| rCalBackupDataV2.u4ThermalValue); |
| } else if (ucReason == 1 && ucAction == 2) { |
| DBGLOG(RFTEST, INFO, "RLM CMD : Trigger FW Do All Cal.\n"); |
| /* Step 2 : Trigger All Cal Function */ |
| |
| rStatus = kalIoctl(prGlueInfo, wlanoidSetCalBackup, |
| &rCalBackupDataV2, |
| sizeof(struct PARAM_CAL_BACKUP_STRUCT_V2), |
| FALSE, FALSE, TRUE, &u4BufLen); |
| |
| if (rStatus != WLAN_STATUS_SUCCESS) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Trigger FW Do All Cal Return Fail (0x%08x)!!!!!!!!!!!\n", |
| rStatus); |
| return rStatus; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "CMD : Trigger FW Do All Cal. Finish!!!!!!!!!!!\n"); |
| } else if (ucReason == 0 && ucAction == 1) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Get Cal Data (%s) Size from FW.\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| /* Step 3 : Get Cal Data Size from FW */ |
| |
| rStatus = kalIoctl(prGlueInfo, wlanoidQueryCalBackupV2, |
| &rCalBackupDataV2, |
| sizeof(struct PARAM_CAL_BACKUP_STRUCT_V2), |
| TRUE, TRUE, TRUE, &u4BufLen); |
| |
| if (rStatus != WLAN_STATUS_SUCCESS) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Get Cal Data (%s) Size from FW Return Fail (0x%08x)!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM", rStatus); |
| return rStatus; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "CMD : Get Cal Data (%s) Size from FW. Finish!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| } else if (ucReason == 2 && ucAction == 4) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Get Cal Data from FW (%s). Start!!!!!!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| DBGLOG(RFTEST, INFO, "Thermal Temp = %d\n", |
| g_rBackupCalDataAllV2.u4ThermalInfo); |
| /* Step 4 : Get Cal Data from FW */ |
| |
| rStatus = kalIoctl(prGlueInfo, wlanoidQueryCalBackupV2, |
| &rCalBackupDataV2, |
| sizeof(struct PARAM_CAL_BACKUP_STRUCT_V2), |
| TRUE, TRUE, TRUE, &u4BufLen); |
| if (rStatus != WLAN_STATUS_SUCCESS) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Get Cal Data (%s) Size from FW Return Fail (0x%08x)!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM", rStatus); |
| return rStatus; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "CMD : Get Cal Data from FW (%s). Finish!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| |
| if (ucRomRam == 0) { |
| DBGLOG(RFTEST, INFO, |
| "Check some of elements (0x%08x), (0x%08x), (0x%08x), (0x%08x), (0x%08x)\n", |
| g_rBackupCalDataAllV2.au4RomCalData[670], |
| g_rBackupCalDataAllV2.au4RomCalData[671], |
| g_rBackupCalDataAllV2.au4RomCalData[672], |
| g_rBackupCalDataAllV2.au4RomCalData[673], |
| g_rBackupCalDataAllV2.au4RomCalData[674]); |
| DBGLOG(RFTEST, INFO, |
| "Check some of elements (0x%08x), (0x%08x), (0x%08x), (0x%08x), (0x%08x)\n", |
| g_rBackupCalDataAllV2.au4RomCalData[675], |
| g_rBackupCalDataAllV2.au4RomCalData[676], |
| g_rBackupCalDataAllV2.au4RomCalData[677], |
| g_rBackupCalDataAllV2.au4RomCalData[678], |
| g_rBackupCalDataAllV2.au4RomCalData[679]); |
| } |
| } else if (ucReason == 4 && ucAction == 6) { |
| DBGLOG(RFTEST, INFO, "RLM CMD : Print Cal Data in FW (%s).\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| /* Debug Use : Print Cal Data in FW */ |
| |
| rStatus = kalIoctl(prGlueInfo, wlanoidSetCalBackup, |
| &rCalBackupDataV2, |
| sizeof(struct PARAM_CAL_BACKUP_STRUCT_V2), |
| TRUE, TRUE, TRUE, &u4BufLen); |
| |
| if (rStatus != WLAN_STATUS_SUCCESS) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Print Cal Data in FW (%s) Return Fail (0x%08x)!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM", rStatus); |
| return rStatus; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "CMD : Print Cal Data in FW (%s). Finish!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| } else if (ucReason == 3 && ucAction == 5) { |
| DBGLOG(RFTEST, INFO, "RLM CMD : Send Cal Data to FW (%s).\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| /* Send Cal Data to FW */ |
| |
| rStatus = kalIoctl(prGlueInfo, wlanoidSetCalBackup, |
| &rCalBackupDataV2, |
| sizeof(struct PARAM_CAL_BACKUP_STRUCT_V2), |
| TRUE, TRUE, TRUE, &u4BufLen); |
| |
| if (rStatus != WLAN_STATUS_SUCCESS) { |
| DBGLOG(RFTEST, INFO, |
| "RLM CMD : Send Cal Data to FW (%s) Return Fail (0x%08x)!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM", rStatus); |
| return rStatus; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "CMD : Send Cal Data to FW (%s). Finish!!!!!!!!!!!\n", |
| ucRomRam == 0 ? "ROM" : "RAM"); |
| } else { |
| DBGLOG(RFTEST, INFO, |
| "CMD : Undefined Reason (%d) and Action (%d) for Cal Backup in Host Side!\n", |
| ucReason, ucAction); |
| |
| return rStatus; |
| } |
| |
| return rStatus; |
| } |
| |
| uint32_t rlmTriggerCalBackup(struct ADAPTER *prAdapter, |
| u_int8_t fgIsCalDataBackuped) |
| { |
| uint32_t rStatus = WLAN_STATUS_SUCCESS; |
| |
| if (!fgIsCalDataBackuped) { |
| DBGLOG(RFTEST, INFO, |
| "======== Boot Time Wi-Fi Enable........\n"); |
| DBGLOG(RFTEST, INFO, |
| "Step 0 : Reset All Cal Data in Driver.\n"); |
| memset(&g_rBackupCalDataAllV2, 1, |
| sizeof(struct RLM_CAL_RESULT_ALL_V2)); |
| g_rBackupCalDataAllV2.u4MagicNum1 = 6632; |
| g_rBackupCalDataAllV2.u4MagicNum2 = 6632; |
| |
| DBGLOG(RFTEST, INFO, "Step 1 : Get Thermal Temp from FW.\n"); |
| if (rlmCalBackup(prAdapter, 0, 0, 0) == WLAN_STATUS_FAILURE) { |
| DBGLOG(RFTEST, INFO, "Step 1 : Return Failure.\n"); |
| return WLAN_STATUS_FAILURE; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "Step 2 : Get Rom Cal Data Size from FW.\n"); |
| if (rlmCalBackup(prAdapter, 0, 1, 0) == WLAN_STATUS_FAILURE) { |
| DBGLOG(RFTEST, INFO, "Step 2 : Return Failure.\n"); |
| return WLAN_STATUS_FAILURE; |
| } |
| |
| DBGLOG(RFTEST, INFO, |
| "Step 3 : Get Ram Cal Data Size from FW.\n"); |
| if (rlmCalBackup(prAdapter, 0, 1, 1) == WLAN_STATUS_FAILURE) { |
| DBGLOG(RFTEST, INFO, "Step 3 : Return Failure.\n"); |
| return WLAN_STATUS_FAILURE; |
| } |
| |
| DBGLOG(RFTEST, INFO, "Step 4 : Trigger FW Do Full Cal.\n"); |
| if (rlmCalBackup(prAdapter, 1, 2, 0) == WLAN_STATUS_FAILURE) { |
| DBGLOG(RFTEST, INFO, "Step 4 : Return Failure.\n"); |
| return WLAN_STATUS_FAILURE; |
| } |
| } else { |
| DBGLOG(RFTEST, INFO, "======== Normal Wi-Fi Enable........\n"); |
| DBGLOG(RFTEST, INFO, "Step 0 : Sent Rom Cal data to FW.\n"); |
| if (rlmCalBackup(prAdapter, 3, 5, 0) == WLAN_STATUS_FAILURE) { |
| DBGLOG(RFTEST, INFO, "Step 0 : Return Failure.\n"); |
| return WLAN_STATUS_FAILURE; |
| } |
| |
| DBGLOG(RFTEST, INFO, "Step 1 : Sent Ram Cal data to FW.\n"); |
| if (rlmCalBackup(prAdapter, 3, 5, 1) == WLAN_STATUS_FAILURE) { |
| DBGLOG(RFTEST, INFO, "Step 1 : Return Failure.\n"); |
| return WLAN_STATUS_FAILURE; |
| } |
| } |
| |
| return rStatus; |
| } |
| #endif |
| |
| void rlmModifyVhtBwPara(uint8_t *pucVhtChannelFrequencyS1, |
| uint8_t *pucVhtChannelFrequencyS2, |
| uint8_t *pucVhtChannelWidth) |
| { |
| uint8_t i = 0, ucTempS = 0; |
| |
| if ((*pucVhtChannelFrequencyS1 != 0) && |
| (*pucVhtChannelFrequencyS2 != 0)) { |
| |
| uint8_t ucBW160Inteval = 8; |
| |
| if (((*pucVhtChannelFrequencyS2 - *pucVhtChannelFrequencyS1) == |
| ucBW160Inteval) || |
| ((*pucVhtChannelFrequencyS1 - *pucVhtChannelFrequencyS2) == |
| ucBW160Inteval)) { |
| /*C160 case*/ |
| |
| /* NEW spec should set central ch of bw80 at S1, |
| * set central ch of bw160 at S2 |
| */ |
| for (i = 0; i < 2; i++) { |
| |
| if (i == 0) |
| ucTempS = *pucVhtChannelFrequencyS1; |
| else |
| ucTempS = *pucVhtChannelFrequencyS2; |
| |
| if ((ucTempS == 50) || (ucTempS == 82) || |
| (ucTempS == 114) || (ucTempS == 163)) |
| break; |
| } |
| |
| if (ucTempS == 0) { |
| DBGLOG(RLM, WARN, |
| "please check BW160 setting, find central freq fail\n"); |
| return; |
| } |
| |
| *pucVhtChannelFrequencyS1 = ucTempS; |
| *pucVhtChannelFrequencyS2 = 0; |
| *pucVhtChannelWidth = CW_160MHZ; |
| } else { |
| /*real 80P80 case*/ |
| } |
| } |
| } |
| |
| static void rlmRevisePreferBandwidthNss(struct ADAPTER *prAdapter, |
| uint8_t ucBssIndex, |
| struct STA_RECORD *prStaRec) |
| { |
| enum ENUM_CHANNEL_WIDTH eChannelWidth = CW_20_40MHZ; |
| struct BSS_INFO *prBssInfo; |
| |
| #define VHT_MCS_TX_RX_MAX_2SS BITS(2, 3) |
| #define VHT_MCS_TX_RX_MAX_2SS_SHIFT 2 |
| |
| #define AR_STA_2AC_MCS(prStaRec) \ |
| (((prStaRec)->u2VhtRxMcsMap & VHT_MCS_TX_RX_MAX_2SS) >> \ |
| VHT_MCS_TX_RX_MAX_2SS_SHIFT) |
| |
| #define AR_IS_STA_2SS_AC(prStaRec) ((AR_STA_2AC_MCS(prStaRec) != BITS(0, 1))) |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, ucBssIndex); |
| |
| eChannelWidth = prBssInfo->ucVhtChannelWidth; |
| |
| /* |
| * Prefer setting modification |
| * 80+80 1x1 and 80 2x2 have the same phy rate, choose the 80 2x2 |
| */ |
| |
| if (AR_IS_STA_2SS_AC(prStaRec)) { |
| /* |
| * DBGLOG(RLM, WARN, "support 2ss\n"); |
| */ |
| |
| if ((eChannelWidth == CW_80P80MHZ && |
| prBssInfo->ucVhtChannelFrequencyS2 != 0)) { |
| DBGLOG(RLM, WARN, "support (2Nss) and (80+80)\n"); |
| DBGLOG(RLM, WARN, |
| "choose (2Nss) and (80) for Bss_info\n"); |
| prBssInfo->ucVhtChannelWidth = CW_80MHZ; |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| } |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Revise operating BW by own maximum bandwidth capability |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmReviseMaxBw(struct ADAPTER *prAdapter, uint8_t ucBssIndex, |
| enum ENUM_CHNL_EXT *peExtend, |
| enum ENUM_CHANNEL_WIDTH *peChannelWidth, uint8_t *pucS1, |
| uint8_t *pucPrimaryCh) |
| { |
| uint8_t ucMaxBandwidth = MAX_BW_80MHZ; |
| uint8_t ucCurrentBandwidth = MAX_BW_20MHZ; |
| uint8_t ucOffset = (MAX_BW_80MHZ - CW_80MHZ); |
| |
| ucMaxBandwidth = cnmGetDbdcBwCapability(prAdapter, ucBssIndex); |
| |
| if (*peChannelWidth > CW_20_40MHZ) { |
| /*case BW > 80 , 160 80P80 */ |
| ucCurrentBandwidth = (uint8_t)*peChannelWidth + ucOffset; |
| } else { |
| /*case BW20 BW40 */ |
| if (*peExtend != CHNL_EXT_SCN) { |
| /*case BW40 */ |
| ucCurrentBandwidth = MAX_BW_40MHZ; |
| } |
| } |
| |
| if (ucCurrentBandwidth > ucMaxBandwidth) { |
| DBGLOG(RLM, TRACE, "Decreasse the BW to (%d)\n", ucMaxBandwidth); |
| |
| if (ucMaxBandwidth <= MAX_BW_40MHZ) { |
| /*BW20 * BW40*/ |
| *peChannelWidth = CW_20_40MHZ; |
| |
| if (ucMaxBandwidth == MAX_BW_20MHZ) |
| *peExtend = CHNL_EXT_SCN; |
| } else { |
| /* BW80, BW160, BW80P80 |
| * ucMaxBandwidth Must be |
| * MAX_BW_80MHZ,MAX_BW_160MHZ,MAX_BW_80MHZ |
| * peExtend should not change |
| */ |
| *peChannelWidth = (ucMaxBandwidth - ucOffset); |
| |
| if (ucMaxBandwidth == MAX_BW_80MHZ) { |
| /* modify S1 for Bandwidth 160 downgrade 80 case |
| */ |
| if (ucCurrentBandwidth == MAX_BW_160MHZ) { |
| if ((*pucPrimaryCh >= 36) && |
| (*pucPrimaryCh <= 48)) |
| *pucS1 = 42; |
| else if ((*pucPrimaryCh >= 52) && |
| (*pucPrimaryCh <= 64)) |
| *pucS1 = 58; |
| else if ((*pucPrimaryCh >= 100) && |
| (*pucPrimaryCh <= 112)) |
| *pucS1 = 106; |
| else if ((*pucPrimaryCh >= 116) && |
| (*pucPrimaryCh <= 128)) |
| *pucS1 = 122; |
| else if ((*pucPrimaryCh >= 132) && |
| (*pucPrimaryCh <= 144)) |
| /* 160 downgrade should not in |
| * this case |
| */ |
| *pucS1 = 138; |
| else if ((*pucPrimaryCh >= 149) && |
| (*pucPrimaryCh <= 161)) |
| /* 160 downgrade should not in |
| * this case |
| */ |
| *pucS1 = 155; |
| else |
| DBGLOG(RLM, TRACE, |
| "Check connect 160 downgrde (%d) case\n", |
| ucMaxBandwidth); |
| |
| DBGLOG(RLM, TRACE, |
| "Decreasse the BW160 to BW80, shift S1 to (%d)\n", |
| *pucS1); |
| } |
| } |
| } |
| |
| DBGLOG(RLM, TRACE, "Modify ChannelWidth (%d) and Extend (%d)\n", |
| *peChannelWidth, *peExtend); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Fill VHT Operation Information(VHT BW, S1, S2) by BSS operating |
| * channel width |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmFillVhtOpInfoByBssOpBw(struct BSS_INFO *prBssInfo, uint8_t ucBssOpBw) |
| { |
| ASSERT(prBssInfo); |
| |
| if (ucBssOpBw < MAX_BW_80MHZ || prBssInfo->eBand == BAND_2G4) { |
| prBssInfo->ucVhtChannelWidth = VHT_OP_CHANNEL_WIDTH_20_40; |
| prBssInfo->ucVhtChannelFrequencyS1 = 0; |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| } else if (ucBssOpBw == MAX_BW_80MHZ) { |
| prBssInfo->ucVhtChannelWidth = VHT_OP_CHANNEL_WIDTH_80; |
| prBssInfo->ucVhtChannelFrequencyS1 = nicGetVhtS1( |
| prBssInfo->ucPrimaryChannel, VHT_OP_CHANNEL_WIDTH_80); |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| } else if (ucBssOpBw == MAX_BW_160MHZ) { |
| prBssInfo->ucVhtChannelWidth = VHT_OP_CHANNEL_WIDTH_160; |
| prBssInfo->ucVhtChannelFrequencyS1 = nicGetVhtS1( |
| prBssInfo->ucPrimaryChannel, VHT_OP_CHANNEL_WIDTH_160); |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| } else { |
| /* 4 TODO: / BW80+80 support */ |
| DBGLOG(RLM, INFO, "Unsupport BW setting, back to VHT20_40\n"); |
| |
| prBssInfo->ucVhtChannelWidth = VHT_OP_CHANNEL_WIDTH_20_40; |
| prBssInfo->ucVhtChannelFrequencyS1 = 0; |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function should be invoked to update parameters of associated AP. |
| * (Association response and Beacon) |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static uint8_t rlmRecIeInfoForClient(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, uint8_t *pucIE, |
| uint16_t u2IELength) |
| { |
| uint16_t u2Offset; |
| struct STA_RECORD *prStaRec; |
| struct IE_HT_CAP *prHtCap; |
| struct IE_HT_OP *prHtOp; |
| struct IE_OBSS_SCAN_PARAM *prObssScnParam; |
| uint8_t ucERP, ucPrimaryChannel; |
| struct WIFI_VAR *prWifiVar = &prAdapter->rWifiVar; |
| u_int8_t fgHasQuietIE = FALSE; |
| u_int8_t IsfgHtCapChange = FALSE; |
| |
| #if CFG_SUPPORT_802_11AC |
| struct IE_VHT_OP *prVhtOp; |
| struct IE_VHT_CAP *prVhtCap; |
| struct IE_OP_MODE_NOTIFICATION |
| *prOPModeNotification; /* Operation Mode Notification */ |
| u_int8_t fgHasOPModeIE = FALSE; |
| uint8_t ucVhtOpModeChannelWidth = 0; |
| uint8_t ucVhtOpModeRxNss = 0; |
| uint8_t ucMaxBwAllowed; |
| uint8_t ucInitVhtOpMode = 0; |
| #endif |
| |
| #if CFG_SUPPORT_DFS |
| u_int8_t fgHasWideBandIE = FALSE; |
| u_int8_t fgHasSCOIE = FALSE; |
| u_int8_t fgHasChannelSwitchIE = FALSE; |
| u_int8_t fgNeedSwitchChannel = FALSE; |
| uint8_t ucChannelAnnouncePri; |
| enum ENUM_CHNL_EXT eChannelAnnounceSco; |
| uint8_t ucChannelAnnounceChannelS1 = 0; |
| uint8_t ucChannelAnnounceChannelS2 = 0; |
| uint8_t ucChannelAnnounceVhtBw; |
| struct IE_CHANNEL_SWITCH *prChannelSwitchAnnounceIE; |
| struct IE_SECONDARY_OFFSET *prSecondaryOffsetIE; |
| struct IE_WIDE_BAND_CHANNEL *prWideBandChannelIE; |
| #if CFG_DFS_NEWCH_DFS_FORCE_DISCONNECT |
| struct ieee80211_channel *Channel = NULL; |
| #endif |
| #endif |
| uint8_t *pucDumpIE; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(pucIE); |
| |
| prStaRec = prBssInfo->prStaRecOfAP; |
| ASSERT(prStaRec); |
| if (!prStaRec) |
| return 0; |
| |
| prBssInfo->fgUseShortPreamble = prBssInfo->fgIsShortPreambleAllowed; |
| ucPrimaryChannel = 0; |
| prObssScnParam = NULL; |
| ucMaxBwAllowed = cnmGetBssMaxBw(prAdapter, prBssInfo->ucBssIndex); |
| pucDumpIE = pucIE; |
| |
| /* Note: HT-related members in staRec may not be zero before, so |
| * if following IE does not exist, they are still not zero. |
| * These HT-related parameters are valid only when the |
| * corresponding |
| * BssInfo supports 802.11n, i.e., RLM_NET_IS_11N() |
| */ |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| switch (IE_ID(pucIE)) { |
| case ELEM_ID_HT_CAP: |
| if (!RLM_NET_IS_11N(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_HT_CAP) - 2)) |
| break; |
| prHtCap = (struct IE_HT_CAP *)pucIE; |
| prStaRec->ucMcsSet = |
| prHtCap->rSupMcsSet.aucRxMcsBitmask[0]; |
| prStaRec->fgSupMcs32 = |
| (prHtCap->rSupMcsSet.aucRxMcsBitmask[32 / 8] & |
| BIT(0)) |
| ? TRUE |
| : FALSE; |
| |
| kalMemCopy( |
| prStaRec->aucRxMcsBitmask, |
| prHtCap->rSupMcsSet.aucRxMcsBitmask, |
| /*SUP_MCS_RX_BITMASK_OCTET_NUM */ |
| sizeof(prStaRec->aucRxMcsBitmask)); |
| |
| prStaRec->u2RxHighestSupportedRate = |
| prHtCap->rSupMcsSet.u2RxHighestSupportedRate; |
| prStaRec->u4TxRateInfo = |
| prHtCap->rSupMcsSet.u4TxRateInfo; |
| |
| if ((prStaRec->u2HtCapInfo & |
| HT_CAP_INFO_SM_POWER_SAVE) != |
| (prHtCap->u2HtCapInfo & HT_CAP_INFO_SM_POWER_SAVE)) |
| /* Purpose : To detect SMPS change */ |
| IsfgHtCapChange = TRUE; |
| |
| prStaRec->u2HtCapInfo = prHtCap->u2HtCapInfo; |
| /* Set LDPC Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxLdpc)) |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_LDPC_CAP; |
| else if (IS_FEATURE_DISABLED(prWifiVar->ucTxLdpc)) |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_LDPC_CAP; |
| |
| /* Set STBC Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxStbc)) |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_RX_STBC; |
| else if (IS_FEATURE_DISABLED(prWifiVar->ucTxStbc)) |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_RX_STBC; |
| |
| /* Set Short GI Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxShortGI)) { |
| prStaRec->u2HtCapInfo |= |
| HT_CAP_INFO_SHORT_GI_20M; |
| prStaRec->u2HtCapInfo |= |
| HT_CAP_INFO_SHORT_GI_40M; |
| } else if (IS_FEATURE_DISABLED( |
| prWifiVar->ucTxShortGI)) { |
| prStaRec->u2HtCapInfo &= |
| ~HT_CAP_INFO_SHORT_GI_20M; |
| prStaRec->u2HtCapInfo &= |
| ~HT_CAP_INFO_SHORT_GI_40M; |
| } |
| |
| /* Set HT Greenfield Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxGf)) |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_HT_GF; |
| else if (IS_FEATURE_DISABLED(prWifiVar->ucTxGf)) |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_HT_GF; |
| |
| prStaRec->ucAmpduParam = prHtCap->ucAmpduParam; |
| prStaRec->u2HtExtendedCap = prHtCap->u2HtExtendedCap; |
| prStaRec->u4TxBeamformingCap = |
| prHtCap->u4TxBeamformingCap; |
| prStaRec->ucAselCap = prHtCap->ucAselCap; |
| break; |
| |
| case ELEM_ID_HT_OP: |
| if (!RLM_NET_IS_11N(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_HT_OP) - 2)) |
| break; |
| prHtOp = (struct IE_HT_OP *)pucIE; |
| /* Workaround that some APs fill primary channel field |
| * by its |
| * secondary channel, but its DS IE is correct 20110610 |
| */ |
| if (ucPrimaryChannel == 0) |
| ucPrimaryChannel = prHtOp->ucPrimaryChannel; |
| prBssInfo->ucHtOpInfo1 = prHtOp->ucInfo1; |
| prBssInfo->u2HtOpInfo2 = prHtOp->u2Info2; |
| prBssInfo->u2HtOpInfo3 = prHtOp->u2Info3; |
| |
| /*Backup peer HT OP Info*/ |
| prStaRec->ucHtPeerOpInfo1 = prHtOp->ucInfo1; |
| |
| if (!prBssInfo->fg40mBwAllowed) |
| prBssInfo->ucHtOpInfo1 &= |
| ~(HT_OP_INFO1_SCO | |
| HT_OP_INFO1_STA_CHNL_WIDTH); |
| |
| if ((prBssInfo->ucHtOpInfo1 & HT_OP_INFO1_SCO) != |
| CHNL_EXT_RES) |
| prBssInfo->eBssSCO = (enum ENUM_CHNL_EXT)( |
| prBssInfo->ucHtOpInfo1 & |
| HT_OP_INFO1_SCO); |
| |
| /* Revise by own OP BW */ |
| if (prBssInfo->fgIsOpChangeChannelWidth && |
| prBssInfo->ucOpChangeChannelWidth == MAX_BW_20MHZ) { |
| prBssInfo->ucHtOpInfo1 &= |
| ~(HT_OP_INFO1_SCO | |
| HT_OP_INFO1_STA_CHNL_WIDTH); |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| } |
| |
| prBssInfo->eHtProtectMode = (enum ENUM_HT_PROTECT_MODE)( |
| prBssInfo->u2HtOpInfo2 & |
| HT_OP_INFO2_HT_PROTECTION); |
| |
| /* To do: process regulatory class 16 */ |
| if ((prBssInfo->u2HtOpInfo2 & |
| HT_OP_INFO2_OBSS_NON_HT_STA_PRESENT) && |
| 0 /* && regulatory class is 16 */) |
| prBssInfo->eGfOperationMode = |
| GF_MODE_DISALLOWED; |
| else if (prBssInfo->u2HtOpInfo2 & |
| HT_OP_INFO2_NON_GF_HT_STA_PRESENT) |
| prBssInfo->eGfOperationMode = GF_MODE_PROTECT; |
| else |
| prBssInfo->eGfOperationMode = GF_MODE_NORMAL; |
| |
| prBssInfo->eRifsOperationMode = |
| (prBssInfo->ucHtOpInfo1 & HT_OP_INFO1_RIFS_MODE) |
| ? RIFS_MODE_NORMAL |
| : RIFS_MODE_DISALLOWED; |
| |
| break; |
| |
| #if CFG_SUPPORT_802_11AC |
| case ELEM_ID_VHT_CAP: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_VHT_CAP) - 2)) |
| break; |
| |
| prVhtCap = (struct IE_VHT_CAP *)pucIE; |
| |
| prStaRec->u4VhtCapInfo = prVhtCap->u4VhtCapInfo; |
| /* Set Tx LDPC capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxLdpc)) |
| prStaRec->u4VhtCapInfo |= VHT_CAP_INFO_RX_LDPC; |
| else if (IS_FEATURE_DISABLED(prWifiVar->ucTxLdpc)) |
| prStaRec->u4VhtCapInfo &= ~VHT_CAP_INFO_RX_LDPC; |
| |
| /* Set Tx STBC capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxStbc)) |
| prStaRec->u4VhtCapInfo |= |
| VHT_CAP_INFO_RX_STBC_MASK; |
| else if (IS_FEATURE_DISABLED(prWifiVar->ucTxStbc)) |
| prStaRec->u4VhtCapInfo &= |
| ~VHT_CAP_INFO_RX_STBC_MASK; |
| |
| /* Set Tx TXOP PS capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxopPsTx)) |
| prStaRec->u4VhtCapInfo |= |
| VHT_CAP_INFO_VHT_TXOP_PS; |
| else if (IS_FEATURE_DISABLED(prWifiVar->ucTxopPsTx)) |
| prStaRec->u4VhtCapInfo &= |
| ~VHT_CAP_INFO_VHT_TXOP_PS; |
| |
| /* Set Tx Short GI capability */ |
| if (IS_FEATURE_FORCE_ENABLED(prWifiVar->ucTxShortGI)) { |
| prStaRec->u4VhtCapInfo |= |
| VHT_CAP_INFO_SHORT_GI_80; |
| prStaRec->u4VhtCapInfo |= |
| VHT_CAP_INFO_SHORT_GI_160_80P80; |
| } else if (IS_FEATURE_DISABLED( |
| prWifiVar->ucTxShortGI)) { |
| prStaRec->u4VhtCapInfo &= |
| ~VHT_CAP_INFO_SHORT_GI_80; |
| prStaRec->u4VhtCapInfo &= |
| ~VHT_CAP_INFO_SHORT_GI_160_80P80; |
| } |
| |
| prStaRec->u2VhtRxMcsMap = |
| prVhtCap->rVhtSupportedMcsSet.u2RxMcsMap; |
| |
| prStaRec->u2VhtRxHighestSupportedDataRate = |
| prVhtCap->rVhtSupportedMcsSet |
| .u2RxHighestSupportedDataRate; |
| prStaRec->u2VhtTxMcsMap = |
| prVhtCap->rVhtSupportedMcsSet.u2TxMcsMap; |
| prStaRec->u2VhtTxHighestSupportedDataRate = |
| prVhtCap->rVhtSupportedMcsSet |
| .u2TxHighestSupportedDataRate; |
| |
| break; |
| |
| case ELEM_ID_VHT_OP: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_VHT_OP) - 2)) |
| break; |
| |
| prVhtOp = (struct IE_VHT_OP *)pucIE; |
| |
| /*Backup peer VHT OpInfo*/ |
| prStaRec->ucVhtOpChannelWidth = |
| prVhtOp->ucVhtOperation[0]; |
| prStaRec->ucVhtOpChannelFrequencyS1 = |
| prVhtOp->ucVhtOperation[1]; |
| prStaRec->ucVhtOpChannelFrequencyS2 = |
| prVhtOp->ucVhtOperation[2]; |
| |
| rlmModifyVhtBwPara(&prStaRec->ucVhtOpChannelFrequencyS1, |
| &prStaRec->ucVhtOpChannelFrequencyS2, |
| &prStaRec->ucVhtOpChannelWidth); |
| |
| prBssInfo->ucVhtChannelWidth = |
| prVhtOp->ucVhtOperation[0]; |
| prBssInfo->ucVhtChannelFrequencyS1 = |
| prVhtOp->ucVhtOperation[1]; |
| prBssInfo->ucVhtChannelFrequencyS2 = |
| prVhtOp->ucVhtOperation[2]; |
| prBssInfo->u2VhtBasicMcsSet = prVhtOp->u2VhtBasicMcsSet; |
| |
| rlmModifyVhtBwPara(&prBssInfo->ucVhtChannelFrequencyS1, |
| &prBssInfo->ucVhtChannelFrequencyS2, |
| &prBssInfo->ucVhtChannelWidth); |
| |
| /* Set initial value of VHT OP mode */ |
| ucInitVhtOpMode = 0; |
| ucInitVhtOpMode |= |
| rlmGetOpModeBwByVhtAndHtOpInfo(prBssInfo); |
| ucInitVhtOpMode |= ((prBssInfo->ucNss - 1) |
| << VHT_OP_MODE_RX_NSS_OFFSET) & |
| VHT_OP_MODE_RX_NSS; |
| |
| /* Revise by own OP BW if needed */ |
| if ((prBssInfo->fgIsOpChangeChannelWidth) && |
| (rlmGetVhtOpBwByBssOpBw( |
| prBssInfo->ucOpChangeChannelWidth) < |
| prBssInfo->ucVhtChannelWidth)) { |
| rlmFillVhtOpInfoByBssOpBw( |
| prBssInfo, |
| prBssInfo->ucOpChangeChannelWidth); |
| } |
| |
| break; |
| case ELEM_ID_OP_MODE: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != |
| (sizeof(struct IE_OP_MODE_NOTIFICATION) - |
| 2)) |
| break; |
| prOPModeNotification = |
| (struct IE_OP_MODE_NOTIFICATION *)pucIE; |
| |
| if ((prOPModeNotification->ucOpMode & |
| VHT_OP_MODE_RX_NSS_TYPE) != |
| VHT_OP_MODE_RX_NSS_TYPE) { |
| if (prStaRec->ucVhtOpMode != |
| prOPModeNotification->ucOpMode) { |
| prStaRec->ucVhtOpMode = |
| prOPModeNotification->ucOpMode; |
| fgHasOPModeIE = TRUE; |
| ucVhtOpModeChannelWidth = |
| ((prOPModeNotification |
| ->ucOpMode) & |
| VHT_OP_MODE_CHANNEL_WIDTH); |
| ucVhtOpModeRxNss = |
| ((prOPModeNotification |
| ->ucOpMode) & |
| VHT_OP_MODE_RX_NSS) >> |
| VHT_OP_MODE_RX_NSS_OFFSET; |
| } else |
| /* Let the further flow not to update |
| * VhtOpMode |
| */ |
| ucInitVhtOpMode = prStaRec->ucVhtOpMode; |
| } |
| |
| break; |
| #if CFG_SUPPORT_DFS |
| case ELEM_ID_WIDE_BAND_CHANNEL_SWITCH: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != |
| (sizeof(struct IE_WIDE_BAND_CHANNEL) - 2)) |
| break; |
| DBGLOG(RLM, INFO, |
| "[Channel Switch] ELEM_ID_WIDE_BAND_CHANNEL_SWITCH, 11AC\n"); |
| prWideBandChannelIE = |
| (struct IE_WIDE_BAND_CHANNEL *)pucIE; |
| ucChannelAnnounceVhtBw = |
| prWideBandChannelIE->ucNewChannelWidth; |
| ucChannelAnnounceChannelS1 = |
| prWideBandChannelIE->ucChannelS1; |
| ucChannelAnnounceChannelS2 = |
| prWideBandChannelIE->ucChannelS2; |
| fgHasWideBandIE = TRUE; |
| DBGLOG(RLM, INFO, "[Ch] BW=%d, s1=%d, s2=%d\n", |
| ucChannelAnnounceVhtBw, |
| ucChannelAnnounceChannelS1, |
| ucChannelAnnounceChannelS2); |
| break; |
| #endif |
| |
| #endif |
| case ELEM_ID_20_40_BSS_COEXISTENCE: |
| if (!RLM_NET_IS_11N(prBssInfo)) |
| break; |
| /* To do: store if scanning exemption grant to BssInfo |
| */ |
| break; |
| |
| case ELEM_ID_OBSS_SCAN_PARAMS: |
| if (!RLM_NET_IS_11N(prBssInfo) || |
| IE_LEN(pucIE) != |
| (sizeof(struct IE_OBSS_SCAN_PARAM) - 2)) |
| break; |
| /* Store OBSS parameters to BssInfo */ |
| prObssScnParam = (struct IE_OBSS_SCAN_PARAM *)pucIE; |
| break; |
| |
| case ELEM_ID_EXTENDED_CAP: |
| if (!RLM_NET_IS_11N(prBssInfo)) |
| break; |
| /* To do: store extended capability (PSMP, coexist) to |
| * BssInfo |
| */ |
| break; |
| |
| case ELEM_ID_ERP_INFO: |
| if (IE_LEN(pucIE) != (sizeof(struct IE_ERP) - 2) || |
| prBssInfo->eBand != BAND_2G4) |
| break; |
| ucERP = ERP_INFO_IE(pucIE)->ucERP; |
| prBssInfo->fgErpProtectMode = |
| (ucERP & ERP_INFO_USE_PROTECTION) ? TRUE |
| : FALSE; |
| |
| if (ucERP & ERP_INFO_BARKER_PREAMBLE_MODE) |
| prBssInfo->fgUseShortPreamble = FALSE; |
| break; |
| |
| case ELEM_ID_DS_PARAM_SET: |
| if (IE_LEN(pucIE) == ELEM_MAX_LEN_DS_PARAMETER_SET) |
| ucPrimaryChannel = |
| DS_PARAM_IE(pucIE)->ucCurrChnl; |
| break; |
| #if CFG_SUPPORT_DFS |
| case ELEM_ID_CH_SW_ANNOUNCEMENT: |
| if (IE_LEN(pucIE) != |
| (sizeof(struct IE_CHANNEL_SWITCH) - 2)) |
| break; |
| |
| prChannelSwitchAnnounceIE = |
| (struct IE_CHANNEL_SWITCH *)pucIE; |
| |
| DBGLOG(RLM, INFO, "[Ch] Count=%d\n", |
| prChannelSwitchAnnounceIE->ucChannelSwitchCount); |
| |
| if (prChannelSwitchAnnounceIE |
| ->ucChannelSwitchMode == 1 |
| || prChannelSwitchAnnounceIE |
| ->ucChannelSwitchCount <= 3) { |
| /* Need to stop data transmission immediately */ |
| fgHasChannelSwitchIE = TRUE; |
| if (!g_fgHasStopTx) { |
| g_fgHasStopTx = TRUE; |
| #if CFG_SUPPORT_TDLS |
| /* TDLS peers */ |
| TdlsTxCtrl(prAdapter, |
| prBssInfo, |
| FALSE); |
| #endif |
| /* AP */ |
| qmSetStaRecTxAllowed(prAdapter, |
| prStaRec, |
| FALSE); |
| DBGLOG(RLM, EVENT, |
| "[Ch] TxAllowed = FALSE\n"); |
| } |
| } |
| |
| if (prChannelSwitchAnnounceIE |
| ->ucChannelSwitchCount <= 3) { |
| DBGLOG(RLM, INFO, |
| "[Ch] switch channel [%d]->[%d]\n", |
| prBssInfo->ucPrimaryChannel, |
| prChannelSwitchAnnounceIE |
| ->ucNewChannelNum); |
| ucChannelAnnouncePri = |
| prChannelSwitchAnnounceIE |
| ->ucNewChannelNum; |
| #if CFG_DFS_NEWCH_DFS_FORCE_DISCONNECT |
| Channel = ieee80211_get_channel( |
| priv_to_wiphy(prAdapter->prGlueInfo), |
| ieee80211_channel_to_frequency(ucChannelAnnouncePri, KAL_BAND_5GHZ)); |
| DBGLOG(RLM, INFO, "[DFS][CSA][CLIENT] Switch to DFS channel: new ChNum = [%d]\n",ucChannelAnnouncePri); |
| if (Channel && |
| (Channel->flags & IEEE80211_CHAN_RADAR)) { |
| DBGLOG(RLM, INFO, "[DFS][CSA][CLIENT] New channel is DFS channel!"); |
| aisBssLinkDown(prAdapter); |
| } |
| else |
| #endif |
| fgNeedSwitchChannel = TRUE; |
| #ifdef CFG_DFS_CHSW_FORCE_BW20 |
| g_fgHasChannelSwitchIE = TRUE; |
| } |
| |
| if (prChannelSwitchAnnounceIE |
| ->ucChannelSwitchMode == 1){ |
| if (RLM_NET_IS_11AC(prBssInfo)) { |
| DBGLOG(RLM, INFO, |
| "Send Operation Action Frame"); |
| rlmSendOpModeNotificationFrame( |
| prAdapter, prStaRec, |
| VHT_OP_MODE_CHANNEL_WIDTH_20, |
| 1); |
| } else { |
| DBGLOG(RLM, INFO, |
| "Skip Send Operation Action Frame"); |
| } |
| } |
| #else |
| } |
| #endif |
| |
| break; |
| case ELEM_ID_SCO: |
| if (IE_LEN(pucIE) != |
| (sizeof(struct IE_SECONDARY_OFFSET) - 2)) |
| break; |
| |
| prSecondaryOffsetIE = |
| (struct IE_SECONDARY_OFFSET *)pucIE; |
| DBGLOG(RLM, INFO, "[Channel Switch] SCO [%d]->[%d]\n", |
| prBssInfo->eBssSCO, |
| prSecondaryOffsetIE->ucSecondaryOffset); |
| eChannelAnnounceSco = |
| (enum ENUM_CHNL_EXT) |
| prSecondaryOffsetIE->ucSecondaryOffset; |
| fgHasSCOIE = TRUE; |
| break; |
| #endif |
| |
| /* Note: RRM code should be moved to independent RRM function by |
| * component design rule. But we attach it to RLM |
| * temporarily |
| */ |
| case ELEM_ID_QUIET: |
| #if CFG_SUPPORT_QUIET && 0 |
| rrmQuietHandleQuietIE(prBssInfo, |
| (struct IE_QUIET *)pucIE); |
| #endif |
| fgHasQuietIE = TRUE; |
| break; |
| default: |
| break; |
| } /* end of switch */ |
| } /* end of IE_FOR_EACH */ |
| |
| if (IsfgHtCapChange && (prStaRec->ucStaState == STA_STATE_3)) |
| cnmStaSendUpdateCmd(prAdapter, prStaRec, NULL, FALSE); |
| |
| /* Some AP will have wrong channel number (255) when running time. |
| * Check if correct channel number information. 20110501 |
| */ |
| if ((prBssInfo->eBand == BAND_2G4 && ucPrimaryChannel > 14) || |
| (prBssInfo->eBand != BAND_2G4 && |
| (ucPrimaryChannel >= 200 || ucPrimaryChannel <= 14))) |
| ucPrimaryChannel = 0; |
| #if CFG_SUPPORT_802_11AC |
| /* Check whether the Operation Mode IE is exist or not. |
| * If exists, then the channel bandwidth of VHT operation field is |
| * changed |
| * with the channel bandwidth setting of Operation Mode field. |
| * The channel bandwidth of OP Mode IE is 0, represent as 20MHz. |
| * The channel bandwidth of OP Mode IE is 1, represent as 40MHz. |
| * The channel bandwidth of OP Mode IE is 2, represent as 80MHz. |
| * The channel bandwidth of OP Mode IE is 3, represent as |
| * 160/80+80MHz. |
| */ |
| if (fgHasOPModeIE == TRUE) { |
| if (prStaRec->ucStaState == STA_STATE_3) { |
| /* 1. Modify channel width parameters */ |
| rlmRecOpModeBwForClient(ucVhtOpModeChannelWidth, |
| prBssInfo); |
| |
| /* 2. Update StaRec to FW (BssInfo will be updated after |
| * return from this function) |
| */ |
| DBGLOG(RLM, INFO, |
| "Update OpMode to 0x%x, to FW due to OpMode Notificaition", |
| prStaRec->ucVhtOpMode); |
| cnmStaSendUpdateCmd(prAdapter, prStaRec, NULL, FALSE); |
| |
| /* 3. Revise by own OP BW if needed */ |
| if ((prBssInfo->fgIsOpChangeChannelWidth)) { |
| /* VHT */ |
| if (rlmGetVhtOpBwByBssOpBw( |
| prBssInfo->ucOpChangeChannelWidth) < |
| prBssInfo->ucVhtChannelWidth) |
| rlmFillVhtOpInfoByBssOpBw( |
| prBssInfo, |
| prBssInfo->ucOpChangeChannelWidth); |
| /* HT */ |
| if (prBssInfo->fgIsOpChangeChannelWidth && |
| prBssInfo->ucOpChangeChannelWidth == |
| MAX_BW_20MHZ) { |
| prBssInfo->ucHtOpInfo1 &= |
| ~(HT_OP_INFO1_SCO | |
| HT_OP_INFO1_STA_CHNL_WIDTH); |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| } |
| } |
| } |
| } else { /* Set Default if the VHT OP mode field is not present */ |
| if ((prStaRec->ucVhtOpMode != ucInitVhtOpMode) && |
| (prStaRec->ucStaState == STA_STATE_3)) { |
| prStaRec->ucVhtOpMode = ucInitVhtOpMode; |
| DBGLOG(RLM, INFO, "Update OpMode to 0x%x", |
| prStaRec->ucVhtOpMode); |
| DBGLOG(RLM, INFO, |
| "to FW due to NO OpMode Notificaition\n"); |
| cnmStaSendUpdateCmd(prAdapter, prStaRec, NULL, FALSE); |
| } else |
| prStaRec->ucVhtOpMode = ucInitVhtOpMode; |
| } |
| #endif |
| |
| #if CFG_SUPPORT_DFS |
| /* Check whether Channel Announcement IE, Secondary Offset IE & |
| * Wide Bandwidth Channel Switch IE exist or not. If exist, the |
| * priority is |
| * the highest. |
| */ |
| |
| if (fgNeedSwitchChannel) { |
| struct BSS_DESC *prBssDesc = NULL; |
| struct PARAM_SSID rSsid; |
| |
| prBssInfo->ucPrimaryChannel = ucChannelAnnouncePri; |
| /* Change to BW20 for certification issue due to signal sidelope |
| * leakage |
| */ |
| prBssInfo->ucVhtChannelWidth = 0; |
| prBssInfo->ucVhtChannelFrequencyS1 = 0; |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| prBssInfo->eBssSCO = 0; |
| COPY_SSID(rSsid.aucSsid, rSsid.u4SsidLen, prBssInfo->aucSSID, |
| prBssInfo->ucSSIDLen); |
| prBssDesc = scanSearchBssDescByBssidAndSsid( |
| prAdapter, prBssInfo->aucBSSID, TRUE, &rSsid); |
| |
| if (prBssDesc) { |
| DBGLOG(RLM, INFO, |
| "DFS: BSS: " MACSTR |
| " Desc found, channel from %u to %u\n ", |
| MAC2STR(prBssInfo->aucBSSID), |
| prBssDesc->ucChannelNum, ucChannelAnnouncePri); |
| prBssDesc->ucChannelNum = ucChannelAnnouncePri; |
| } else { |
| DBGLOG(RLM, INFO, |
| "DFS: BSS: " MACSTR " Desc is not found\n ", |
| MAC2STR(prBssInfo->aucBSSID)); |
| } |
| |
| if (fgHasWideBandIE != FALSE) { |
| prBssInfo->ucVhtChannelWidth = ucChannelAnnounceVhtBw; |
| prBssInfo->ucVhtChannelFrequencyS1 = |
| ucChannelAnnounceChannelS1; |
| prBssInfo->ucVhtChannelFrequencyS2 = |
| ucChannelAnnounceChannelS2; |
| |
| /* Revise by own OP BW if needed */ |
| if ((prBssInfo->fgIsOpChangeChannelWidth) && |
| (rlmGetVhtOpBwByBssOpBw( |
| prBssInfo->ucOpChangeChannelWidth) < |
| prBssInfo->ucVhtChannelWidth)) { |
| |
| DBGLOG(RLM, LOUD, |
| "Change to w:%d s1:%d s2:%d since own changed BW < peer's WideBand BW", |
| prBssInfo->ucVhtChannelWidth, |
| prBssInfo->ucVhtChannelFrequencyS1, |
| prBssInfo->ucVhtChannelFrequencyS2); |
| rlmFillVhtOpInfoByBssOpBw( |
| prBssInfo, |
| prBssInfo->ucOpChangeChannelWidth); |
| } |
| } |
| if (fgHasSCOIE != FALSE) |
| prBssInfo->eBssSCO = eChannelAnnounceSco; |
| |
| if (prBssDesc) { |
| kalIndicateChannelSwitch(prAdapter->prGlueInfo, |
| prBssInfo->eBssSCO, |
| prBssDesc->ucChannelNum); |
| } |
| } |
| |
| if (!fgHasChannelSwitchIE && g_fgHasStopTx) { |
| #if CFG_SUPPORT_TDLS |
| /* TDLS peers */ |
| TdlsTxCtrl(prAdapter, prBssInfo, TRUE); |
| #endif |
| /* AP */ |
| qmSetStaRecTxAllowed(prAdapter, prStaRec, TRUE); |
| |
| DBGLOG(RLM, EVENT, "[Ch] TxAllowed = TRUE\n"); |
| g_fgHasStopTx = FALSE; |
| } |
| |
| #ifdef CFG_DFS_CHSW_FORCE_BW20 |
| /*DFS Certification for Channel Bandwidth 20MHz */ |
| DBGLOG(RLM, INFO, "Ch : SwitchIE = %d\n", g_fgHasChannelSwitchIE); |
| if (g_fgHasChannelSwitchIE == TRUE) { |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| prBssInfo->ucVhtChannelWidth = CW_20_40MHZ; |
| prBssInfo->ucVhtChannelFrequencyS1 = 0; |
| prBssInfo->ucVhtChannelFrequencyS2 = 255; |
| prBssInfo->ucHtOpInfo1 &= |
| ~(HT_OP_INFO1_SCO | HT_OP_INFO1_STA_CHNL_WIDTH); |
| DBGLOG(RLM, INFO, "Ch : DFS has Appeared\n"); |
| } |
| #endif |
| #endif |
| rlmReviseMaxBw(prAdapter, prBssInfo->ucBssIndex, &prBssInfo->eBssSCO, |
| (enum ENUM_CHANNEL_WIDTH *)&prBssInfo->ucVhtChannelWidth, |
| &prBssInfo->ucVhtChannelFrequencyS1, |
| &prBssInfo->ucPrimaryChannel); |
| |
| rlmRevisePreferBandwidthNss(prAdapter, prBssInfo->ucBssIndex, prStaRec); |
| |
| if (!rlmDomainIsValidRfSetting( |
| prAdapter, prBssInfo->eBand, prBssInfo->ucPrimaryChannel, |
| prBssInfo->eBssSCO, prBssInfo->ucVhtChannelWidth, |
| prBssInfo->ucVhtChannelFrequencyS1, |
| prBssInfo->ucVhtChannelFrequencyS2)) { |
| |
| /*Dump IE Inforamtion */ |
| DBGLOG(RLM, WARN, "rlmRecIeInfoForClient IE Information\n"); |
| DBGLOG(RLM, WARN, "IE Length = %d\n", u2IELength); |
| DBGLOG_MEM8(RLM, WARN, pucDumpIE, u2IELength); |
| |
| /*Error Handling for Non-predicted IE - Fixed to set 20MHz */ |
| prBssInfo->ucVhtChannelWidth = CW_20_40MHZ; |
| prBssInfo->ucVhtChannelFrequencyS1 = 0; |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| prBssInfo->ucHtOpInfo1 &= |
| ~(HT_OP_INFO1_SCO | HT_OP_INFO1_STA_CHNL_WIDTH); |
| } |
| #if CFG_SUPPORT_QUIET && 0 |
| if (!fgHasQuietIE) |
| rrmQuietIeNotExist(prAdapter, prBssInfo); |
| #endif |
| |
| /* Check if OBSS scan process will launch */ |
| if (!prAdapter->fgEnOnlineScan || !prObssScnParam || |
| !(prStaRec->u2HtCapInfo & HT_CAP_INFO_SUP_CHNL_WIDTH) || |
| prBssInfo->eBand != BAND_2G4 || !prBssInfo->fg40mBwAllowed) { |
| |
| /* Note: it is ok not to stop rObssScanTimer() here */ |
| prBssInfo->u2ObssScanInterval = 0; |
| } else { |
| if (prObssScnParam->u2TriggerScanInterval < |
| OBSS_SCAN_MIN_INTERVAL) |
| prObssScnParam->u2TriggerScanInterval = |
| OBSS_SCAN_MIN_INTERVAL; |
| if (prBssInfo->u2ObssScanInterval != |
| prObssScnParam->u2TriggerScanInterval) { |
| |
| prBssInfo->u2ObssScanInterval = |
| prObssScnParam->u2TriggerScanInterval; |
| |
| /* Start timer to trigger OBSS scanning */ |
| cnmTimerStartTimer( |
| prAdapter, &prBssInfo->rObssScanTimer, |
| prBssInfo->u2ObssScanInterval * MSEC_PER_SEC); |
| } |
| } |
| |
| #if CFG_SUPPORT_DFS |
| g_fgFowardBcn2Supplicant = |
| fgHasQuietIE | |
| fgHasChannelSwitchIE | |
| fgHasWideBandIE; |
| #endif |
| |
| return ucPrimaryChannel; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Update parameters from channel width field in OP Mode IE/action frame |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmRecOpModeBwForClient(uint8_t ucVhtOpModeChannelWidth, |
| struct BSS_INFO *prBssInfo) |
| { |
| |
| struct STA_RECORD *prStaRec = NULL; |
| |
| if (!prBssInfo) |
| return; |
| |
| prStaRec = prBssInfo->prStaRecOfAP; |
| if (!prStaRec) |
| return; |
| |
| switch (ucVhtOpModeChannelWidth) { |
| case VHT_OP_MODE_CHANNEL_WIDTH_20: |
| prBssInfo->ucVhtChannelWidth = VHT_OP_CHANNEL_WIDTH_20_40; |
| prBssInfo->ucHtOpInfo1 &= ~HT_OP_INFO1_STA_CHNL_WIDTH; |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_SUP_CHNL_WIDTH; |
| |
| #if CFG_OPMODE_CONFLICT_OPINFO |
| if (prBssInfo->eBssSCO != CHNL_EXT_SCN) { |
| DBGLOG(RLM, WARN, |
| "HT_OP_Info != OPmode_Notifify, follow OPmode_Notify to BW20.\n"); |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| } |
| #endif |
| break; |
| case VHT_OP_MODE_CHANNEL_WIDTH_40: |
| prBssInfo->ucVhtChannelWidth = VHT_OP_CHANNEL_WIDTH_20_40; |
| prBssInfo->ucHtOpInfo1 |= HT_OP_INFO1_STA_CHNL_WIDTH; |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_SUP_CHNL_WIDTH; |
| |
| #if CFG_OPMODE_CONFLICT_OPINFO |
| if (prBssInfo->eBssSCO == CHNL_EXT_SCN) { |
| prBssInfo->ucHtOpInfo1 &= ~HT_OP_INFO1_STA_CHNL_WIDTH; |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_SUP_CHNL_WIDTH; |
| DBGLOG(RLM, WARN, |
| "HT_OP_Info != OPmode_Notifify, follow HT_OP_Info to BW20.\n"); |
| } |
| #endif |
| break; |
| case VHT_OP_MODE_CHANNEL_WIDTH_80: |
| #if CFG_OPMODE_CONFLICT_OPINFO |
| if (prBssInfo->ucVhtChannelWidth != |
| VHT_OP_MODE_CHANNEL_WIDTH_80) { |
| DBGLOG(RLM, WARN, |
| "VHT_OP != OPmode:%d, follow VHT_OP to VHT_OP:%d HT_OP:%d\n", |
| ucVhtOpModeChannelWidth, |
| prBssInfo->ucVhtChannelWidth, |
| (uint8_t)(prBssInfo->ucHtOpInfo1 & |
| HT_OP_INFO1_STA_CHNL_WIDTH) >> |
| HT_OP_INFO1_STA_CHNL_WIDTH_OFFSET); |
| } else |
| #endif |
| { |
| prBssInfo->ucVhtChannelWidth = VHT_OP_CHANNEL_WIDTH_80; |
| prBssInfo->ucHtOpInfo1 |= HT_OP_INFO1_STA_CHNL_WIDTH; |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_SUP_CHNL_WIDTH; |
| } |
| break; |
| case VHT_OP_MODE_CHANNEL_WIDTH_160_80P80: |
| /* Determine BW160 or BW80+BW80 by VHT OP Info */ |
| #if CFG_OPMODE_CONFLICT_OPINFO |
| if ((prBssInfo->ucVhtChannelWidth != |
| VHT_OP_CHANNEL_WIDTH_160) && |
| (prBssInfo->ucVhtChannelWidth != |
| VHT_OP_CHANNEL_WIDTH_80P80)) { |
| DBGLOG(RLM, WARN, |
| "VHT_OP != OPmode:%d, follow VHT_OP to VHT_OP:%d HT_OP:%d\n", |
| ucVhtOpModeChannelWidth, |
| prBssInfo->ucVhtChannelWidth, |
| (uint8_t)(prBssInfo->ucHtOpInfo1 & |
| HT_OP_INFO1_STA_CHNL_WIDTH) >> |
| HT_OP_INFO1_STA_CHNL_WIDTH_OFFSET); |
| } else |
| #endif |
| { |
| prBssInfo->ucHtOpInfo1 |= HT_OP_INFO1_STA_CHNL_WIDTH; |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_SUP_CHNL_WIDTH; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Update parameters from Association Response frame |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmRecAssocRespIeInfoForClient(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| uint8_t *pucIE, uint16_t u2IELength) |
| { |
| uint16_t u2Offset; |
| struct STA_RECORD *prStaRec; |
| u_int8_t fgIsHasHtCap = FALSE; |
| u_int8_t fgIsHasVhtCap = FALSE; |
| struct BSS_DESC *prBssDesc; |
| struct PARAM_SSID rSsid; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(pucIE); |
| |
| prStaRec = prBssInfo->prStaRecOfAP; |
| |
| ASSERT(prStaRec); |
| if (!prStaRec) |
| return; |
| COPY_SSID(rSsid.aucSsid, rSsid.u4SsidLen, prBssInfo->aucSSID, |
| prBssInfo->ucSSIDLen); |
| prBssDesc = scanSearchBssDescByBssidAndSsid( |
| prAdapter, prStaRec->aucMacAddr, TRUE, &rSsid); |
| |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| switch (IE_ID(pucIE)) { |
| case ELEM_ID_HT_CAP: |
| if (!RLM_NET_IS_11N(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_HT_CAP) - 2)) |
| break; |
| fgIsHasHtCap = TRUE; |
| break; |
| #if CFG_SUPPORT_802_11AC |
| case ELEM_ID_VHT_CAP: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_VHT_CAP) - 2)) |
| break; |
| fgIsHasVhtCap = TRUE; |
| break; |
| #endif |
| default: |
| break; |
| } /* end of switch */ |
| } /* end of IE_FOR_EACH */ |
| |
| if (!fgIsHasHtCap) { |
| prStaRec->ucDesiredPhyTypeSet &= ~PHY_TYPE_BIT_HT; |
| if (prBssDesc) { |
| if (prBssDesc->ucPhyTypeSet & PHY_TYPE_BIT_HT) { |
| DBGLOG(RLM, WARN, |
| "PhyTypeSet in Beacon and AssocResp are unsync. "); |
| DBGLOG(RLM, WARN, |
| "Follow AssocResp to disable HT.\n"); |
| } |
| } |
| } |
| if (!fgIsHasVhtCap) { |
| prStaRec->ucDesiredPhyTypeSet &= ~PHY_TYPE_BIT_VHT; |
| if (prBssDesc) { |
| if (prBssDesc->ucPhyTypeSet & PHY_TYPE_BIT_VHT) { |
| DBGLOG(RLM, WARN, |
| "PhyTypeSet in Beacon and AssocResp are unsync. "); |
| DBGLOG(RLM, WARN, |
| "Follow AssocResp to disable VHT.\n"); |
| } |
| } |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief AIS or P2P GC. |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static u_int8_t rlmRecBcnFromNeighborForClient(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct SW_RFB *prSwRfb, |
| uint8_t *pucIE, |
| uint16_t u2IELength) |
| { |
| uint16_t u2Offset, i; |
| uint8_t ucPriChannel, ucSecChannel; |
| enum ENUM_CHNL_EXT eSCO; |
| u_int8_t fgHtBss, fg20mReq; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo && prSwRfb); |
| ASSERT(pucIE); |
| |
| /* Record it to channel list to change 20/40 bandwidth */ |
| ucPriChannel = 0; |
| eSCO = CHNL_EXT_SCN; |
| |
| fgHtBss = FALSE; |
| fg20mReq = FALSE; |
| |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| switch (IE_ID(pucIE)) { |
| case ELEM_ID_HT_CAP: { |
| struct IE_HT_CAP *prHtCap; |
| |
| if (IE_LEN(pucIE) != (sizeof(struct IE_HT_CAP) - 2)) |
| break; |
| |
| prHtCap = (struct IE_HT_CAP *)pucIE; |
| if (prHtCap->u2HtCapInfo & HT_CAP_INFO_40M_INTOLERANT) |
| fg20mReq = TRUE; |
| fgHtBss = TRUE; |
| break; |
| } |
| case ELEM_ID_HT_OP: { |
| struct IE_HT_OP *prHtOp; |
| |
| if (IE_LEN(pucIE) != (sizeof(struct IE_HT_OP) - 2)) |
| break; |
| |
| prHtOp = (struct IE_HT_OP *)pucIE; |
| /* Workaround that some APs fill primary channel field |
| * by its |
| * secondary channel, but its DS IE is correct 20110610 |
| */ |
| if (ucPriChannel == 0) |
| ucPriChannel = prHtOp->ucPrimaryChannel; |
| |
| if ((prHtOp->ucInfo1 & HT_OP_INFO1_SCO) != CHNL_EXT_RES) |
| eSCO = (enum ENUM_CHNL_EXT)(prHtOp->ucInfo1 & |
| HT_OP_INFO1_SCO); |
| break; |
| } |
| case ELEM_ID_20_40_BSS_COEXISTENCE: { |
| struct IE_20_40_COEXIST *prCoexist; |
| |
| if (IE_LEN(pucIE) != |
| (sizeof(struct IE_20_40_COEXIST) - 2)) |
| break; |
| |
| prCoexist = (struct IE_20_40_COEXIST *)pucIE; |
| if (prCoexist->ucData & BSS_COEXIST_40M_INTOLERANT) |
| fg20mReq = TRUE; |
| break; |
| } |
| case ELEM_ID_DS_PARAM_SET: |
| if (IE_LEN(pucIE) != |
| (sizeof(struct IE_DS_PARAM_SET) - 2)) |
| break; |
| ucPriChannel = DS_PARAM_IE(pucIE)->ucCurrChnl; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /* To do: Update channel list and 5G band. All channel lists have the |
| * same |
| * update procedure. We should give it the entry pointer of desired |
| * channel list. |
| */ |
| if (HAL_RX_STATUS_GET_RF_BAND(prSwRfb->prRxStatus) != BAND_2G4) |
| return FALSE; |
| |
| if (ucPriChannel == 0 || ucPriChannel > 14) |
| ucPriChannel = HAL_RX_STATUS_GET_CHNL_NUM(prSwRfb->prRxStatus); |
| |
| if (fgHtBss) { |
| ASSERT(prBssInfo->auc2G_PriChnlList[0] <= CHNL_LIST_SZ_2G); |
| for (i = 1; i <= prBssInfo->auc2G_PriChnlList[0] && |
| i <= CHNL_LIST_SZ_2G; |
| i++) { |
| if (prBssInfo->auc2G_PriChnlList[i] == ucPriChannel) |
| break; |
| } |
| if ((i > prBssInfo->auc2G_PriChnlList[0]) && |
| (i <= CHNL_LIST_SZ_2G)) { |
| prBssInfo->auc2G_PriChnlList[i] = ucPriChannel; |
| prBssInfo->auc2G_PriChnlList[0]++; |
| } |
| |
| /* Update secondary channel */ |
| if (eSCO != CHNL_EXT_SCN) { |
| ucSecChannel = (eSCO == CHNL_EXT_SCA) |
| ? (ucPriChannel + 4) |
| : (ucPriChannel - 4); |
| |
| ASSERT(prBssInfo->auc2G_SecChnlList[0] <= |
| CHNL_LIST_SZ_2G); |
| for (i = 1; i <= prBssInfo->auc2G_SecChnlList[0] && |
| i <= CHNL_LIST_SZ_2G; |
| i++) { |
| if (prBssInfo->auc2G_SecChnlList[i] == |
| ucSecChannel) |
| break; |
| } |
| if ((i > prBssInfo->auc2G_SecChnlList[0]) && |
| (i <= CHNL_LIST_SZ_2G)) { |
| prBssInfo->auc2G_SecChnlList[i] = ucSecChannel; |
| prBssInfo->auc2G_SecChnlList[0]++; |
| } |
| } |
| |
| /* Update 20M bandwidth request channels */ |
| if (fg20mReq) { |
| ASSERT(prBssInfo->auc2G_20mReqChnlList[0] <= |
| CHNL_LIST_SZ_2G); |
| for (i = 1; i <= prBssInfo->auc2G_20mReqChnlList[0] && |
| i <= CHNL_LIST_SZ_2G; |
| i++) { |
| if (prBssInfo->auc2G_20mReqChnlList[i] == |
| ucPriChannel) |
| break; |
| } |
| if ((i > prBssInfo->auc2G_20mReqChnlList[0]) && |
| (i <= CHNL_LIST_SZ_2G)) { |
| prBssInfo->auc2G_20mReqChnlList[i] = |
| ucPriChannel; |
| prBssInfo->auc2G_20mReqChnlList[0]++; |
| } |
| } |
| } else { |
| /* Update non-HT channel list */ |
| ASSERT(prBssInfo->auc2G_NonHtChnlList[0] <= CHNL_LIST_SZ_2G); |
| for (i = 1; i <= prBssInfo->auc2G_NonHtChnlList[0] && |
| i <= CHNL_LIST_SZ_2G; |
| i++) { |
| if (prBssInfo->auc2G_NonHtChnlList[i] == ucPriChannel) |
| break; |
| } |
| if ((i > prBssInfo->auc2G_NonHtChnlList[0]) && |
| (i <= CHNL_LIST_SZ_2G)) { |
| prBssInfo->auc2G_NonHtChnlList[i] = ucPriChannel; |
| prBssInfo->auc2G_NonHtChnlList[0]++; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief AIS or P2P GC. |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static u_int8_t rlmRecBcnInfoForClient(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| struct SW_RFB *prSwRfb, uint8_t *pucIE, |
| uint16_t u2IELength) |
| { |
| /* For checking if syncing params are different from |
| * last syncing and need to sync again |
| */ |
| struct CMD_SET_BSS_RLM_PARAM rBssRlmParam; |
| u_int8_t fgNewParameter = FALSE; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo && prSwRfb); |
| ASSERT(pucIE); |
| |
| #if 0 /* SW migration 2010/8/20 */ |
| /* Note: we shall not update parameters when scanning, otherwise |
| * channel and bandwidth will not be correct or asserted failure |
| * during scanning. |
| * Note: remove channel checking. All received Beacons should be |
| * processed if measurement or other actions are executed in adjacent |
| * channels and Beacon content checking mechanism is not disabled. |
| */ |
| if (IS_SCAN_ACTIVE() |
| /* || prBssInfo->ucPrimaryChannel != CHNL_NUM_BY_SWRFB(prSwRfb) */ |
| ) { |
| return FALSE; |
| } |
| #endif |
| |
| /* Handle change of slot time */ |
| prBssInfo->u2CapInfo = |
| ((struct WLAN_BEACON_FRAME *)(prSwRfb->pvHeader))->u2CapInfo; |
| prBssInfo->fgUseShortSlotTime = |
| ((prBssInfo->u2CapInfo & CAP_INFO_SHORT_SLOT_TIME) || |
| (prBssInfo->eBand != BAND_2G4)) |
| ? TRUE |
| : FALSE; |
| |
| /* Check if syncing params are different from last syncing and need to |
| * sync again |
| * If yes, return TRUE and sync with FW; Otherwise, return FALSE. |
| */ |
| rBssRlmParam.ucRfBand = (u_int8_t)prBssInfo->eBand; |
| rBssRlmParam.ucPrimaryChannel = prBssInfo->ucPrimaryChannel; |
| rBssRlmParam.ucRfSco = (u_int8_t)prBssInfo->eBssSCO; |
| rBssRlmParam.ucErpProtectMode = (u_int8_t)prBssInfo->fgErpProtectMode; |
| rBssRlmParam.ucHtProtectMode = (u_int8_t)prBssInfo->eHtProtectMode; |
| rBssRlmParam.ucGfOperationMode = (u_int8_t)prBssInfo->eGfOperationMode; |
| rBssRlmParam.ucTxRifsMode = (u_int8_t)prBssInfo->eRifsOperationMode; |
| rBssRlmParam.u2HtOpInfo3 = prBssInfo->u2HtOpInfo3; |
| rBssRlmParam.u2HtOpInfo2 = prBssInfo->u2HtOpInfo2; |
| rBssRlmParam.ucHtOpInfo1 = prBssInfo->ucHtOpInfo1; |
| rBssRlmParam.ucUseShortPreamble = prBssInfo->fgUseShortPreamble; |
| rBssRlmParam.ucUseShortSlotTime = prBssInfo->fgUseShortSlotTime; |
| rBssRlmParam.ucVhtChannelWidth = prBssInfo->ucVhtChannelWidth; |
| rBssRlmParam.ucVhtChannelFrequencyS1 = |
| prBssInfo->ucVhtChannelFrequencyS1; |
| rBssRlmParam.ucVhtChannelFrequencyS2 = |
| prBssInfo->ucVhtChannelFrequencyS2; |
| rBssRlmParam.u2VhtBasicMcsSet = prBssInfo->u2VhtBasicMcsSet; |
| rBssRlmParam.ucNss = prBssInfo->ucNss; |
| |
| rlmRecIeInfoForClient(prAdapter, prBssInfo, pucIE, u2IELength); |
| |
| if (g_fgFowardBcn2Supplicant) { |
| kalIndicateRxMgmtFrame(prAdapter->prGlueInfo, prSwRfb); |
| g_fgFowardBcn2Supplicant = FALSE; |
| } |
| |
| if (rBssRlmParam.ucRfBand != prBssInfo->eBand || |
| rBssRlmParam.ucPrimaryChannel != prBssInfo->ucPrimaryChannel || |
| rBssRlmParam.ucRfSco != prBssInfo->eBssSCO || |
| rBssRlmParam.ucErpProtectMode != prBssInfo->fgErpProtectMode || |
| rBssRlmParam.ucHtProtectMode != prBssInfo->eHtProtectMode || |
| rBssRlmParam.ucGfOperationMode != prBssInfo->eGfOperationMode || |
| rBssRlmParam.ucTxRifsMode != prBssInfo->eRifsOperationMode || |
| rBssRlmParam.u2HtOpInfo3 != prBssInfo->u2HtOpInfo3 || |
| rBssRlmParam.u2HtOpInfo2 != prBssInfo->u2HtOpInfo2 || |
| rBssRlmParam.ucHtOpInfo1 != prBssInfo->ucHtOpInfo1 || |
| rBssRlmParam.ucUseShortPreamble != prBssInfo->fgUseShortPreamble || |
| rBssRlmParam.ucUseShortSlotTime != prBssInfo->fgUseShortSlotTime || |
| rBssRlmParam.ucVhtChannelWidth != prBssInfo->ucVhtChannelWidth || |
| rBssRlmParam.ucVhtChannelFrequencyS1 != |
| prBssInfo->ucVhtChannelFrequencyS1 || |
| rBssRlmParam.ucVhtChannelFrequencyS2 != |
| prBssInfo->ucVhtChannelFrequencyS2 || |
| rBssRlmParam.u2VhtBasicMcsSet != prBssInfo->u2VhtBasicMcsSet || |
| rBssRlmParam.ucNss != prBssInfo->ucNss) |
| fgNewParameter = TRUE; |
| else { |
| DBGLOG(RLM, TRACE, |
| "prBssInfo's params are all the same! not to sync!\n"); |
| fgNewParameter = FALSE; |
| } |
| |
| return fgNewParameter; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmProcessBcn(struct ADAPTER *prAdapter, struct SW_RFB *prSwRfb, |
| uint8_t *pucIE, uint16_t u2IELength) |
| { |
| struct BSS_INFO *prBssInfo; |
| u_int8_t fgNewParameter; |
| uint8_t i; |
| |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| ASSERT(pucIE); |
| |
| fgNewParameter = FALSE; |
| |
| /* When concurrent networks exist, GO shall have the same handle as |
| * the other BSS, so the Beacon shall be processed for bandwidth and |
| * protection mechanism. |
| * Note1: we do not have 2 AP (GO) cases simultaneously now. |
| * Note2: If we are GO, concurrent AIS AP should detect it and reflect |
| * action in its Beacon, so AIS STA just follows Beacon from AP. |
| */ |
| for (i = 0; i < prAdapter->ucHwBssIdNum; i++) { |
| prBssInfo = prAdapter->aprBssInfo[i]; |
| |
| if (IS_BSS_BOW(prBssInfo)) |
| continue; |
| |
| if (IS_BSS_ACTIVE(prBssInfo)) { |
| if (prBssInfo->eCurrentOPMode == |
| OP_MODE_INFRASTRUCTURE && |
| prBssInfo->eConnectionState == |
| PARAM_MEDIA_STATE_CONNECTED) { |
| /* P2P client or AIS infra STA */ |
| if (EQUAL_MAC_ADDR( |
| prBssInfo->aucBSSID, |
| ((struct WLAN_MAC_MGMT_HEADER |
| *)(prSwRfb->pvHeader)) |
| ->aucBSSID)) { |
| |
| fgNewParameter = rlmRecBcnInfoForClient( |
| prAdapter, prBssInfo, prSwRfb, |
| pucIE, u2IELength); |
| } else { |
| fgNewParameter = |
| rlmRecBcnFromNeighborForClient( |
| prAdapter, prBssInfo, |
| prSwRfb, pucIE, |
| u2IELength); |
| } |
| } |
| #if CFG_ENABLE_WIFI_DIRECT |
| else if (prAdapter->fgIsP2PRegistered && |
| (prBssInfo->eCurrentOPMode == |
| OP_MODE_ACCESS_POINT || |
| prBssInfo->eCurrentOPMode == |
| OP_MODE_P2P_DEVICE)) { |
| /* AP scan to check if 20/40M bandwidth is |
| * permitted |
| */ |
| rlmRecBcnFromNeighborForClient( |
| prAdapter, prBssInfo, prSwRfb, pucIE, |
| u2IELength); |
| } |
| #endif |
| else if (prBssInfo->eCurrentOPMode == OP_MODE_IBSS) { |
| /* To do: Nothing */ |
| /* To do: Ad-hoc */ |
| } |
| |
| /* Appy new parameters if necessary */ |
| if (fgNewParameter) { |
| rlmSyncOperationParams(prAdapter, prBssInfo); |
| fgNewParameter = FALSE; |
| } |
| } /* end of IS_BSS_ACTIVE() */ |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function should be invoked after judging successful association. |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmProcessAssocRsp(struct ADAPTER *prAdapter, struct SW_RFB *prSwRfb, |
| uint8_t *pucIE, uint16_t u2IELength) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint8_t ucPriChannel; |
| |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| ASSERT(pucIE); |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prSwRfb->ucStaRecIdx); |
| if (!prStaRec) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| if (!prBssInfo) |
| return; |
| |
| if (prStaRec != prBssInfo->prStaRecOfAP) |
| return; |
| |
| /* To do: the invoked function is used to clear all members. It may be |
| * done by center mechanism in invoker. |
| */ |
| rlmBssReset(prAdapter, prBssInfo); |
| |
| prBssInfo->fgUseShortSlotTime = |
| ((prBssInfo->u2CapInfo & CAP_INFO_SHORT_SLOT_TIME) || |
| (prBssInfo->eBand != BAND_2G4)) |
| ? TRUE |
| : FALSE; |
| ucPriChannel = |
| rlmRecIeInfoForClient(prAdapter, prBssInfo, pucIE, u2IELength); |
| |
| /*Update the parameters from Association Response only, |
| *if the parameters need to be updated by both Beacon and Association |
| *Response, |
| *user should use another function, rlmRecIeInfoForClient() |
| */ |
| rlmRecAssocRespIeInfoForClient(prAdapter, prBssInfo, pucIE, u2IELength); |
| |
| if (prBssInfo->ucPrimaryChannel != ucPriChannel) { |
| DBGLOG(RLM, INFO, |
| "Use RF pri channel[%u].Pri channel in HT OP IE is :[%u]\n", |
| prBssInfo->ucPrimaryChannel, ucPriChannel); |
| } |
| /* Avoid wrong primary channel info in HT operation |
| * IE info when accept association response |
| */ |
| #if 0 |
| if (ucPriChannel > 0) |
| prBssInfo->ucPrimaryChannel = ucPriChannel; |
| #endif |
| |
| if (!RLM_NET_IS_11N(prBssInfo) || |
| !(prStaRec->u2HtCapInfo & HT_CAP_INFO_SUP_CHNL_WIDTH)) |
| prBssInfo->fg40mBwAllowed = FALSE; |
| |
| /* Note: Update its capabilities to WTBL by cnmStaRecChangeState(), |
| * which |
| * shall be invoked afterwards. |
| * Update channel, bandwidth and protection mode by nicUpdateBss() |
| */ |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmProcessHtAction(struct ADAPTER *prAdapter, struct SW_RFB *prSwRfb) |
| { |
| struct ACTION_NOTIFY_CHNL_WIDTH_FRAME *prRxFrame; |
| struct ACTION_SM_POWER_SAVE_FRAME *prRxSmpsFrame; |
| struct STA_RECORD *prStaRec; |
| struct BSS_INFO *prBssInfo; |
| uint16_t u2HtCapInfoBitmask = 0; |
| |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| |
| prRxFrame = (struct ACTION_NOTIFY_CHNL_WIDTH_FRAME *)prSwRfb->pvHeader; |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prSwRfb->ucStaRecIdx); |
| |
| if (!prStaRec) |
| return; |
| |
| switch (prRxFrame->ucAction) { |
| case ACTION_HT_NOTIFY_CHANNEL_WIDTH: |
| if (prStaRec->ucStaState != STA_STATE_3 || |
| prSwRfb->u2PacketLen < |
| sizeof(struct ACTION_NOTIFY_CHNL_WIDTH_FRAME)) { |
| return; |
| } |
| |
| /* To do: depending regulation class 13 and 14 based on spec |
| * Note: (ucChannelWidth==1) shall restored back to original |
| * capability, not current setting to 40MHz BW here |
| */ |
| /* 1. Update StaRec for AP/STA mode */ |
| if (prRxFrame->ucChannelWidth == HT_NOTIFY_CHANNEL_WIDTH_20) |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_SUP_CHNL_WIDTH; |
| else if (prRxFrame->ucChannelWidth == |
| HT_NOTIFY_CHANNEL_WIDTH_ANY_SUPPORT_CAHNNAEL_WIDTH) |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_SUP_CHNL_WIDTH; |
| |
| cnmStaSendUpdateCmd(prAdapter, prStaRec, NULL, FALSE); |
| |
| /* 2. Update BssInfo for STA mode */ |
| prBssInfo = prAdapter->aprBssInfo[prStaRec->ucBssIndex]; |
| if (prBssInfo->eCurrentOPMode == OP_MODE_INFRASTRUCTURE) { |
| if (prRxFrame->ucChannelWidth == |
| HT_NOTIFY_CHANNEL_WIDTH_20) { |
| prBssInfo->ucHtOpInfo1 &= |
| ~HT_OP_INFO1_STA_CHNL_WIDTH; |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| } else if ( |
| prRxFrame->ucChannelWidth == |
| HT_NOTIFY_CHANNEL_WIDTH_ANY_SUPPORT_CAHNNAEL_WIDTH) |
| prBssInfo->ucHtOpInfo1 |= |
| HT_OP_INFO1_STA_CHNL_WIDTH; |
| |
| /* Revise by own OP BW if needed */ |
| if (prBssInfo->fgIsOpChangeChannelWidth && |
| prBssInfo->ucOpChangeChannelWidth == MAX_BW_20MHZ) { |
| prBssInfo->ucHtOpInfo1 &= |
| ~(HT_OP_INFO1_SCO | |
| HT_OP_INFO1_STA_CHNL_WIDTH); |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| } |
| |
| /* 3. Update OP BW to FW */ |
| rlmSyncOperationParams(prAdapter, prBssInfo); |
| } |
| break; |
| /* Support SM power save */ /* TH3_Huang */ |
| case ACTION_HT_SM_POWER_SAVE: |
| prRxSmpsFrame = |
| (struct ACTION_SM_POWER_SAVE_FRAME *)prSwRfb->pvHeader; |
| if (prStaRec->ucStaState != STA_STATE_3 || |
| prSwRfb->u2PacketLen < |
| sizeof(struct ACTION_SM_POWER_SAVE_FRAME)) { |
| return; |
| } |
| |
| /* The SM power enable bit is different definition in HtCap and |
| * SMpower IE field |
| */ |
| if (!(prRxSmpsFrame->ucSmPowerCtrl & |
| (HT_SM_POWER_SAVE_CONTROL_ENABLED | |
| HT_SM_POWER_SAVE_CONTROL_SM_MODE))) |
| u2HtCapInfoBitmask |= HT_CAP_INFO_SM_POWER_SAVE; |
| |
| /* Support SMPS action frame, TH3_Huang */ |
| /* Update StaRec if SM power state changed */ |
| if ((prStaRec->u2HtCapInfo & HT_CAP_INFO_SM_POWER_SAVE) != |
| u2HtCapInfoBitmask) { |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_SM_POWER_SAVE; |
| prStaRec->u2HtCapInfo |= u2HtCapInfoBitmask; |
| DBGLOG(RLM, INFO, |
| "rlmProcessHtAction -- SMPS change u2HtCapInfo to (%x)\n", |
| prStaRec->u2HtCapInfo); |
| cnmStaSendUpdateCmd(prAdapter, prStaRec, NULL, FALSE); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| #if CFG_SUPPORT_802_11AC |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmProcessVhtAction(struct ADAPTER *prAdapter, struct SW_RFB *prSwRfb) |
| { |
| struct ACTION_OP_MODE_NOTIFICATION_FRAME *prRxFrame; |
| struct STA_RECORD *prStaRec; |
| struct BSS_INFO *prBssInfo; |
| uint8_t ucVhtOpModeChannelWidth = 0; |
| |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| |
| prRxFrame = |
| (struct ACTION_OP_MODE_NOTIFICATION_FRAME *)prSwRfb->pvHeader; |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prSwRfb->ucStaRecIdx); |
| |
| if (!prStaRec) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| |
| if (!prBssInfo) |
| return; |
| |
| switch (prRxFrame->ucAction) { |
| /* Support Operating mode notification action frame, TH3_Huang */ |
| case ACTION_OPERATING_MODE_NOTIFICATION: |
| if (prStaRec->ucStaState != STA_STATE_3 || |
| prSwRfb->u2PacketLen < |
| sizeof(struct ACTION_OP_MODE_NOTIFICATION_FRAME)) { |
| return; |
| } |
| |
| if (((prRxFrame->ucOperatingMode & VHT_OP_MODE_RX_NSS_TYPE) != |
| VHT_OP_MODE_RX_NSS_TYPE) && |
| (prStaRec->ucVhtOpMode != prRxFrame->ucOperatingMode)) { |
| /* 1. Fill OP mode notification info */ |
| prStaRec->ucVhtOpMode = prRxFrame->ucOperatingMode; |
| DBGLOG(RLM, INFO, |
| "rlmProcessVhtAction -- Update ucVhtOpMode to 0x%x\n", |
| prStaRec->ucVhtOpMode); |
| |
| /* 2. Modify channel width parameters */ |
| ucVhtOpModeChannelWidth = prRxFrame->ucOperatingMode & |
| VHT_OP_MODE_CHANNEL_WIDTH; |
| if (prBssInfo->eCurrentOPMode == OP_MODE_ACCESS_POINT) { |
| if (ucVhtOpModeChannelWidth == |
| VHT_OP_MODE_CHANNEL_WIDTH_20) |
| prStaRec->u2HtCapInfo &= |
| ~HT_CAP_INFO_SUP_CHNL_WIDTH; |
| else /* for other 3 VHT cases: 40/80/160 */ |
| prStaRec->u2HtCapInfo |= |
| HT_CAP_INFO_SUP_CHNL_WIDTH; |
| } else if (prBssInfo->eCurrentOPMode == |
| OP_MODE_INFRASTRUCTURE) |
| rlmRecOpModeBwForClient(ucVhtOpModeChannelWidth, |
| prBssInfo); |
| |
| /* 3. Update StaRec to FW */ |
| cnmStaSendUpdateCmd(prAdapter, prStaRec, NULL, FALSE); |
| |
| /* 4. Update BW parameters in BssInfo for STA mode only |
| */ |
| if (prBssInfo->eCurrentOPMode == |
| OP_MODE_INFRASTRUCTURE) { |
| /* 4.1 Revise by own OP BW if needed for STA |
| * mode only |
| */ |
| if (prBssInfo->fgIsOpChangeChannelWidth) { |
| /* VHT */ |
| if (rlmGetVhtOpBwByBssOpBw( |
| prBssInfo->ucOpChangeChannelWidth) < |
| prBssInfo->ucVhtChannelWidth) |
| rlmFillVhtOpInfoByBssOpBw( |
| prBssInfo, |
| prBssInfo->ucOpChangeChannelWidth); |
| /* HT */ |
| if (prBssInfo |
| ->fgIsOpChangeChannelWidth && |
| prBssInfo->ucOpChangeChannelWidth == |
| MAX_BW_20MHZ) { |
| prBssInfo->ucHtOpInfo1 &= ~( |
| HT_OP_INFO1_SCO | |
| HT_OP_INFO1_STA_CHNL_WIDTH); |
| prBssInfo->eBssSCO = |
| CHNL_EXT_SCN; |
| } |
| } |
| |
| /* 4.2 Check if OP BW parameter valid */ |
| if (!rlmDomainIsValidRfSetting( |
| prAdapter, prBssInfo->eBand, |
| prBssInfo->ucPrimaryChannel, |
| prBssInfo->eBssSCO, |
| prBssInfo->ucVhtChannelWidth, |
| prBssInfo->ucVhtChannelFrequencyS1, |
| prBssInfo->ucVhtChannelFrequencyS2)) { |
| |
| DBGLOG(RLM, WARN, |
| "rlmProcessVhtAction invalid RF settings\n"); |
| |
| /* Error Handling for Non-predicted IE - |
| * Fixed to set 20MHz |
| */ |
| prBssInfo->ucVhtChannelWidth = |
| CW_20_40MHZ; |
| prBssInfo->ucVhtChannelFrequencyS1 = 0; |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| prBssInfo->ucHtOpInfo1 &= |
| ~(HT_OP_INFO1_SCO | |
| HT_OP_INFO1_STA_CHNL_WIDTH); |
| } |
| |
| /* 4.3 Update BSS OP BW to FW for STA mode only |
| */ |
| rlmSyncOperationParams(prAdapter, prBssInfo); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function should be invoked after judging successful association. |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmFillSyncCmdParam(struct CMD_SET_BSS_RLM_PARAM *prCmdBody, |
| struct BSS_INFO *prBssInfo) |
| { |
| ASSERT(prCmdBody && prBssInfo); |
| if (!prCmdBody || !prBssInfo) |
| return; |
| |
| prCmdBody->ucBssIndex = prBssInfo->ucBssIndex; |
| prCmdBody->ucRfBand = (uint8_t)prBssInfo->eBand; |
| prCmdBody->ucPrimaryChannel = prBssInfo->ucPrimaryChannel; |
| prCmdBody->ucRfSco = (uint8_t)prBssInfo->eBssSCO; |
| prCmdBody->ucErpProtectMode = (uint8_t)prBssInfo->fgErpProtectMode; |
| prCmdBody->ucHtProtectMode = (uint8_t)prBssInfo->eHtProtectMode; |
| prCmdBody->ucGfOperationMode = (uint8_t)prBssInfo->eGfOperationMode; |
| prCmdBody->ucTxRifsMode = (uint8_t)prBssInfo->eRifsOperationMode; |
| prCmdBody->u2HtOpInfo3 = prBssInfo->u2HtOpInfo3; |
| prCmdBody->u2HtOpInfo2 = prBssInfo->u2HtOpInfo2; |
| prCmdBody->ucHtOpInfo1 = prBssInfo->ucHtOpInfo1; |
| prCmdBody->ucUseShortPreamble = prBssInfo->fgUseShortPreamble; |
| prCmdBody->ucUseShortSlotTime = prBssInfo->fgUseShortSlotTime; |
| prCmdBody->ucVhtChannelWidth = prBssInfo->ucVhtChannelWidth; |
| prCmdBody->ucVhtChannelFrequencyS1 = prBssInfo->ucVhtChannelFrequencyS1; |
| prCmdBody->ucVhtChannelFrequencyS2 = prBssInfo->ucVhtChannelFrequencyS2; |
| prCmdBody->u2VhtBasicMcsSet = prBssInfo->u2BSSBasicRateSet; |
| prCmdBody->ucNss = prBssInfo->ucNss; |
| |
| if (RLM_NET_PARAM_VALID(prBssInfo)) { |
| DBGLOG(RLM, INFO, |
| "N=%d b=%d c=%d s=%d e=%d h=%d I=0x%02x l=%d p=%d w=%d s1=%d s2=%d n=%d\n", |
| prCmdBody->ucBssIndex, prCmdBody->ucRfBand, |
| prCmdBody->ucPrimaryChannel, prCmdBody->ucRfSco, |
| prCmdBody->ucErpProtectMode, prCmdBody->ucHtProtectMode, |
| prCmdBody->ucHtOpInfo1, prCmdBody->ucUseShortSlotTime, |
| prCmdBody->ucUseShortPreamble, |
| prCmdBody->ucVhtChannelWidth, |
| prCmdBody->ucVhtChannelFrequencyS1, |
| prCmdBody->ucVhtChannelFrequencyS2, prCmdBody->ucNss); |
| } else { |
| DBGLOG(RLM, INFO, "N=%d closed\n", prCmdBody->ucBssIndex); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function will operation parameters based on situations of |
| * concurrent networks. Channel, bandwidth, protection mode, supported |
| * rate will be modified. |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmSyncOperationParams(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo) |
| { |
| struct CMD_SET_BSS_RLM_PARAM *prCmdBody; |
| uint32_t rStatus; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| |
| prCmdBody = (struct CMD_SET_BSS_RLM_PARAM *)cnmMemAlloc( |
| prAdapter, RAM_TYPE_BUF, sizeof(struct CMD_SET_BSS_RLM_PARAM)); |
| |
| /* ASSERT(prCmdBody); */ |
| /* To do: exception handle */ |
| if (!prCmdBody) { |
| DBGLOG(RLM, WARN, "No buf for sync RLM params (Net=%d)\n", |
| prBssInfo->ucBssIndex); |
| return; |
| } |
| |
| rlmFillSyncCmdParam(prCmdBody, prBssInfo); |
| |
| rStatus = wlanSendSetQueryCmd( |
| prAdapter, /* prAdapter */ |
| CMD_ID_SET_BSS_RLM_PARAM, /* ucCID */ |
| TRUE, /* fgSetQuery */ |
| FALSE, /* fgNeedResp */ |
| FALSE, /* fgIsOid */ |
| NULL, /* pfCmdDoneHandler */ |
| NULL, /* pfCmdTimeoutHandler */ |
| sizeof(struct CMD_SET_BSS_RLM_PARAM), /* u4SetQueryInfoLen */ |
| (uint8_t *)prCmdBody, /* pucInfoBuffer */ |
| NULL, /* pvSetQueryBuffer */ |
| 0 /* u4SetQueryBufferLen */ |
| ); |
| |
| /* ASSERT(rStatus == WLAN_STATUS_PENDING); */ |
| if (rStatus != WLAN_STATUS_PENDING) |
| DBGLOG(RLM, WARN, "rlmSyncOperationParams set cmd fail\n"); |
| |
| cnmMemFree(prAdapter, prCmdBody); |
| } |
| |
| #if CFG_SUPPORT_AAA |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function should be invoked after judging successful association. |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmProcessAssocReq(struct ADAPTER *prAdapter, struct SW_RFB *prSwRfb, |
| uint8_t *pucIE, uint16_t u2IELength) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec; |
| uint16_t u2Offset; |
| struct IE_HT_CAP *prHtCap; |
| #if CFG_SUPPORT_802_11AC |
| struct IE_VHT_CAP *prVhtCap; |
| struct IE_OP_MODE_NOTIFICATION |
| *prOPModeNotification; /* Operation Mode Notification */ |
| u_int8_t fgHasOPModeIE = FALSE; |
| #endif |
| |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| ASSERT(pucIE); |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prSwRfb->ucStaRecIdx); |
| if (!prStaRec) |
| return; |
| ASSERT(prStaRec->ucBssIndex <= prAdapter->ucHwBssIdNum); |
| |
| prBssInfo = prAdapter->aprBssInfo[prStaRec->ucBssIndex]; |
| |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| switch (IE_ID(pucIE)) { |
| case ELEM_ID_HT_CAP: |
| if (!RLM_NET_IS_11N(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_HT_CAP) - 2)) |
| break; |
| prHtCap = (struct IE_HT_CAP *)pucIE; |
| prStaRec->ucMcsSet = |
| prHtCap->rSupMcsSet.aucRxMcsBitmask[0]; |
| prStaRec->fgSupMcs32 = |
| (prHtCap->rSupMcsSet.aucRxMcsBitmask[32 / 8] & |
| BIT(0)) |
| ? TRUE |
| : FALSE; |
| |
| kalMemCopy( |
| prStaRec->aucRxMcsBitmask, |
| prHtCap->rSupMcsSet.aucRxMcsBitmask, |
| /*SUP_MCS_RX_BITMASK_OCTET_NUM */ |
| sizeof(prStaRec->aucRxMcsBitmask)); |
| |
| prStaRec->u2HtCapInfo = prHtCap->u2HtCapInfo; |
| |
| /* Set Short LDPC Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxLdpc)) |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_LDPC_CAP; |
| else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxLdpc)) |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_LDPC_CAP; |
| |
| /* Set STBC Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxStbc)) |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_TX_STBC; |
| else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxStbc)) |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_TX_STBC; |
| /* Set Short GI Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxShortGI)) { |
| prStaRec->u2HtCapInfo |= |
| HT_CAP_INFO_SHORT_GI_20M; |
| prStaRec->u2HtCapInfo |= |
| HT_CAP_INFO_SHORT_GI_40M; |
| } else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxShortGI)) { |
| prStaRec->u2HtCapInfo &= |
| ~HT_CAP_INFO_SHORT_GI_20M; |
| prStaRec->u2HtCapInfo &= |
| ~HT_CAP_INFO_SHORT_GI_40M; |
| } |
| |
| /* Set HT Greenfield Tx capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxGf)) |
| prStaRec->u2HtCapInfo |= HT_CAP_INFO_HT_GF; |
| else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxGf)) |
| prStaRec->u2HtCapInfo &= ~HT_CAP_INFO_HT_GF; |
| |
| prStaRec->ucAmpduParam = prHtCap->ucAmpduParam; |
| prStaRec->u2HtExtendedCap = prHtCap->u2HtExtendedCap; |
| prStaRec->u4TxBeamformingCap = |
| prHtCap->u4TxBeamformingCap; |
| prStaRec->ucAselCap = prHtCap->ucAselCap; |
| break; |
| |
| #if CFG_SUPPORT_802_11AC |
| case ELEM_ID_VHT_CAP: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != (sizeof(struct IE_VHT_CAP) - 2)) |
| break; |
| |
| prVhtCap = (struct IE_VHT_CAP *)pucIE; |
| |
| prStaRec->u4VhtCapInfo = prVhtCap->u4VhtCapInfo; |
| |
| /* Set Tx LDPC capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxLdpc)) |
| prStaRec->u4VhtCapInfo |= VHT_CAP_INFO_RX_LDPC; |
| else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxLdpc)) |
| prStaRec->u4VhtCapInfo &= ~VHT_CAP_INFO_RX_LDPC; |
| |
| /* Set Tx STBC capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxStbc)) |
| prStaRec->u4VhtCapInfo |= VHT_CAP_INFO_TX_STBC; |
| else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxStbc)) |
| prStaRec->u4VhtCapInfo &= ~VHT_CAP_INFO_TX_STBC; |
| |
| /* Set Tx TXOP PS capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxopPsTx)) |
| prStaRec->u4VhtCapInfo |= |
| VHT_CAP_INFO_VHT_TXOP_PS; |
| else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxopPsTx)) |
| prStaRec->u4VhtCapInfo &= |
| ~VHT_CAP_INFO_VHT_TXOP_PS; |
| |
| /* Set Tx Short GI capability */ |
| if (IS_FEATURE_FORCE_ENABLED( |
| prAdapter->rWifiVar.ucTxShortGI)) { |
| prStaRec->u4VhtCapInfo |= |
| VHT_CAP_INFO_SHORT_GI_80; |
| prStaRec->u4VhtCapInfo |= |
| VHT_CAP_INFO_SHORT_GI_160_80P80; |
| } else if (IS_FEATURE_DISABLED( |
| prAdapter->rWifiVar.ucTxShortGI)) { |
| prStaRec->u4VhtCapInfo &= |
| ~VHT_CAP_INFO_SHORT_GI_80; |
| prStaRec->u4VhtCapInfo &= |
| ~VHT_CAP_INFO_SHORT_GI_160_80P80; |
| } |
| |
| prStaRec->u2VhtRxMcsMap = |
| prVhtCap->rVhtSupportedMcsSet.u2RxMcsMap; |
| |
| prStaRec->u2VhtRxHighestSupportedDataRate = |
| prVhtCap->rVhtSupportedMcsSet |
| .u2RxHighestSupportedDataRate; |
| prStaRec->u2VhtTxMcsMap = |
| prVhtCap->rVhtSupportedMcsSet.u2TxMcsMap; |
| prStaRec->u2VhtTxHighestSupportedDataRate = |
| prVhtCap->rVhtSupportedMcsSet |
| .u2TxHighestSupportedDataRate; |
| |
| /* Set initial value of VHT OP mode */ |
| prStaRec->ucVhtOpMode = 0; |
| prStaRec->ucVhtOpMode |= |
| rlmGetOpModeBwByVhtAndHtOpInfo(prBssInfo); |
| prStaRec->ucVhtOpMode |= |
| ((prBssInfo->ucNss - 1) |
| << VHT_OP_MODE_RX_NSS_OFFSET) & |
| VHT_OP_MODE_RX_NSS; |
| |
| break; |
| case ELEM_ID_OP_MODE: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != |
| (sizeof(struct IE_OP_MODE_NOTIFICATION) - |
| 2)) |
| break; |
| prOPModeNotification = |
| (struct IE_OP_MODE_NOTIFICATION *)pucIE; |
| |
| if ((prOPModeNotification->ucOpMode & |
| VHT_OP_MODE_RX_NSS_TYPE) != |
| VHT_OP_MODE_RX_NSS_TYPE) { |
| fgHasOPModeIE = TRUE; |
| } |
| |
| break; |
| |
| #endif |
| |
| default: |
| break; |
| } /* end of switch */ |
| } /* end of IE_FOR_EACH */ |
| #if CFG_SUPPORT_802_11AC |
| /*Fill by OP Mode IE after completing parsing all IE to make sure it |
| * won't be overwrite |
| */ |
| if (fgHasOPModeIE == TRUE) |
| prStaRec->ucVhtOpMode = prOPModeNotification->ucOpMode; |
| #endif |
| } |
| #endif /* CFG_SUPPORT_AAA */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief It is for both STA and AP modes |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmBssInitForAPandIbss(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo) |
| { |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| |
| #if CFG_ENABLE_WIFI_DIRECT |
| if (prAdapter->fgIsP2PRegistered && |
| prBssInfo->eCurrentOPMode == OP_MODE_ACCESS_POINT) |
| rlmBssInitForAP(prAdapter, prBssInfo); |
| #endif |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief It is for both STA and AP modes |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmBssAborted(struct ADAPTER *prAdapter, struct BSS_INFO *prBssInfo) |
| { |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| |
| rlmBssReset(prAdapter, prBssInfo); |
| |
| prBssInfo->fg40mBwAllowed = FALSE; |
| prBssInfo->fgAssoc40mBwAllowed = FALSE; |
| |
| /* Assume FW state is updated by CMD_ID_SET_BSS_INFO, so |
| * the sync CMD is not needed here. |
| */ |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief All RLM timers will also be stopped. |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmBssReset(struct ADAPTER *prAdapter, struct BSS_INFO *prBssInfo) |
| { |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| |
| /* HT related parameters */ |
| prBssInfo->ucHtOpInfo1 = 0; /* RIFS disabled. 20MHz */ |
| prBssInfo->u2HtOpInfo2 = 0; |
| prBssInfo->u2HtOpInfo3 = 0; |
| |
| #if CFG_SUPPORT_802_11AC |
| prBssInfo->ucVhtChannelWidth = 0; /* VHT_OP_CHANNEL_WIDTH_80; */ |
| prBssInfo->ucVhtChannelFrequencyS1 = 0; /* 42; */ |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| prBssInfo->u2VhtBasicMcsSet = 0; /* 0xFFFF; */ |
| #endif |
| |
| prBssInfo->eBssSCO = 0; |
| prBssInfo->fgErpProtectMode = 0; |
| prBssInfo->eHtProtectMode = 0; |
| prBssInfo->eGfOperationMode = 0; |
| prBssInfo->eRifsOperationMode = 0; |
| |
| /* OBSS related parameters */ |
| prBssInfo->auc2G_20mReqChnlList[0] = 0; |
| prBssInfo->auc2G_NonHtChnlList[0] = 0; |
| prBssInfo->auc2G_PriChnlList[0] = 0; |
| prBssInfo->auc2G_SecChnlList[0] = 0; |
| prBssInfo->auc5G_20mReqChnlList[0] = 0; |
| prBssInfo->auc5G_NonHtChnlList[0] = 0; |
| prBssInfo->auc5G_PriChnlList[0] = 0; |
| prBssInfo->auc5G_SecChnlList[0] = 0; |
| |
| /* All RLM timers will also be stopped */ |
| cnmTimerStopTimer(prAdapter, &prBssInfo->rObssScanTimer); |
| prBssInfo->u2ObssScanInterval = 0; |
| |
| prBssInfo->fgObssErpProtectMode = 0; /* GO only */ |
| prBssInfo->eObssHtProtectMode = 0; /* GO only */ |
| prBssInfo->eObssGfOperationMode = 0; /* GO only */ |
| prBssInfo->fgObssRifsOperationMode = 0; /* GO only */ |
| prBssInfo->fgObssActionForcedTo20M = 0; /* GO only */ |
| prBssInfo->fgObssBeaconForcedTo20M = 0; /* GO only */ |
| |
| /* OP mode change control parameters */ |
| prBssInfo->fgIsOpChangeChannelWidth = FALSE; |
| prBssInfo->fgIsOpChangeNss = FALSE; |
| |
| #ifdef CFG_DFS_CHSW_FORCE_BW20 |
| g_fgHasChannelSwitchIE = FALSE; |
| #endif |
| } |
| |
| #if CFG_SUPPORT_TDLS |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint32_t rlmFillVhtCapIEByAdapter(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, uint8_t *pOutBuf) |
| { |
| struct IE_VHT_CAP *prVhtCap; |
| struct VHT_SUPPORTED_MCS_FIELD *prVhtSupportedMcsSet; |
| uint8_t i; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| /* ASSERT(prMsduInfo); */ |
| |
| prVhtCap = (struct IE_VHT_CAP *)pOutBuf; |
| |
| prVhtCap->ucId = ELEM_ID_VHT_CAP; |
| prVhtCap->ucLength = sizeof(struct IE_VHT_CAP) - ELEM_HDR_LEN; |
| prVhtCap->u4VhtCapInfo = VHT_CAP_INFO_DEFAULT_VAL; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxShortGI)) |
| prVhtCap->u4VhtCapInfo |= VHT_CAP_INFO_SHORT_GI_80; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxLdpc)) |
| prVhtCap->u4VhtCapInfo |= VHT_CAP_INFO_RX_LDPC; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxStbc)) |
| prVhtCap->u4VhtCapInfo |= VHT_CAP_INFO_RX_STBC_ONE_STREAM; |
| |
| /*set MCS map */ |
| prVhtSupportedMcsSet = &prVhtCap->rVhtSupportedMcsSet; |
| kalMemZero((void *)prVhtSupportedMcsSet, |
| sizeof(struct VHT_SUPPORTED_MCS_FIELD)); |
| |
| for (i = 0; i < 8; i++) { |
| prVhtSupportedMcsSet->u2RxMcsMap |= BITS(2 * i, (2 * i + 1)); |
| prVhtSupportedMcsSet->u2TxMcsMap |= BITS(2 * i, (2 * i + 1)); |
| } |
| |
| prVhtSupportedMcsSet->u2RxMcsMap &= |
| (VHT_CAP_INFO_MCS_MAP_MCS9 << VHT_CAP_INFO_MCS_1SS_OFFSET); |
| prVhtSupportedMcsSet->u2TxMcsMap &= |
| (VHT_CAP_INFO_MCS_MAP_MCS9 << VHT_CAP_INFO_MCS_1SS_OFFSET); |
| prVhtSupportedMcsSet->u2RxHighestSupportedDataRate = |
| VHT_CAP_INFO_DEFAULT_HIGHEST_DATA_RATE; |
| prVhtSupportedMcsSet->u2TxHighestSupportedDataRate = |
| VHT_CAP_INFO_DEFAULT_HIGHEST_DATA_RATE; |
| |
| ASSERT(IE_SIZE(prVhtCap) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_VHT_CAP)); |
| |
| return IE_SIZE(prVhtCap); |
| } |
| #endif |
| |
| #if CFG_SUPPORT_TDLS |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint32_t rlmFillHtCapIEByParams(u_int8_t fg40mAllowed, |
| u_int8_t fgShortGIDisabled, |
| uint8_t u8SupportRxSgi20, |
| uint8_t u8SupportRxSgi40, uint8_t u8SupportRxGf, |
| enum ENUM_OP_MODE eCurrentOPMode, |
| uint8_t *pOutBuf) |
| { |
| struct IE_HT_CAP *prHtCap; |
| struct SUP_MCS_SET_FIELD *prSupMcsSet; |
| |
| ASSERT(pOutBuf); |
| |
| prHtCap = (struct IE_HT_CAP *)pOutBuf; |
| |
| /* Add HT capabilities IE */ |
| prHtCap->ucId = ELEM_ID_HT_CAP; |
| prHtCap->ucLength = sizeof(struct IE_HT_CAP) - ELEM_HDR_LEN; |
| |
| prHtCap->u2HtCapInfo = HT_CAP_INFO_DEFAULT_VAL; |
| if (!fg40mAllowed) { |
| prHtCap->u2HtCapInfo &= ~(HT_CAP_INFO_SUP_CHNL_WIDTH | |
| HT_CAP_INFO_SHORT_GI_40M | |
| HT_CAP_INFO_DSSS_CCK_IN_40M); |
| } |
| if (fgShortGIDisabled) |
| prHtCap->u2HtCapInfo &= |
| ~(HT_CAP_INFO_SHORT_GI_20M | HT_CAP_INFO_SHORT_GI_40M); |
| |
| if (u8SupportRxSgi20 == 2) |
| prHtCap->u2HtCapInfo &= ~(HT_CAP_INFO_SHORT_GI_20M); |
| if (u8SupportRxSgi40 == 2) |
| prHtCap->u2HtCapInfo &= ~(HT_CAP_INFO_SHORT_GI_40M); |
| if (u8SupportRxGf == 2) |
| prHtCap->u2HtCapInfo &= ~(HT_CAP_INFO_HT_GF); |
| |
| prHtCap->ucAmpduParam = AMPDU_PARAM_DEFAULT_VAL; |
| |
| prSupMcsSet = &prHtCap->rSupMcsSet; |
| kalMemZero((void *)&prSupMcsSet->aucRxMcsBitmask[0], |
| SUP_MCS_RX_BITMASK_OCTET_NUM); |
| |
| prSupMcsSet->aucRxMcsBitmask[0] = BITS(0, 7); |
| |
| if (fg40mAllowed) |
| prSupMcsSet->aucRxMcsBitmask[32 / 8] = BIT(0); /* MCS32 */ |
| prSupMcsSet->u2RxHighestSupportedRate = SUP_MCS_RX_DEFAULT_HIGHEST_RATE; |
| prSupMcsSet->u4TxRateInfo = SUP_MCS_TX_DEFAULT_VAL; |
| |
| prHtCap->u2HtExtendedCap = HT_EXT_CAP_DEFAULT_VAL; |
| if (!fg40mAllowed || eCurrentOPMode != OP_MODE_INFRASTRUCTURE) |
| prHtCap->u2HtExtendedCap &= |
| ~(HT_EXT_CAP_PCO | HT_EXT_CAP_PCO_TRANS_TIME_NONE); |
| |
| prHtCap->u4TxBeamformingCap = TX_BEAMFORMING_CAP_DEFAULT_VAL; |
| |
| prHtCap->ucAselCap = ASEL_CAP_DEFAULT_VAL; |
| |
| ASSERT(IE_SIZE(prHtCap) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_HT_CAP)); |
| |
| return IE_SIZE(prHtCap); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint32_t rlmFillHtCapIEByAdapter(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, uint8_t *pOutBuf) |
| { |
| struct IE_HT_CAP *prHtCap; |
| struct SUP_MCS_SET_FIELD *prSupMcsSet; |
| u_int8_t fg40mAllowed; |
| |
| ASSERT(prAdapter); |
| ASSERT(prBssInfo); |
| ASSERT(pOutBuf); |
| |
| fg40mAllowed = prBssInfo->fgAssoc40mBwAllowed; |
| |
| prHtCap = (struct IE_HT_CAP *)pOutBuf; |
| |
| /* Add HT capabilities IE */ |
| prHtCap->ucId = ELEM_ID_HT_CAP; |
| prHtCap->ucLength = sizeof(struct IE_HT_CAP) - ELEM_HDR_LEN; |
| |
| prHtCap->u2HtCapInfo = HT_CAP_INFO_DEFAULT_VAL; |
| if (!fg40mAllowed) { |
| prHtCap->u2HtCapInfo &= ~(HT_CAP_INFO_SUP_CHNL_WIDTH | |
| HT_CAP_INFO_SHORT_GI_40M | |
| HT_CAP_INFO_DSSS_CCK_IN_40M); |
| } |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxShortGI)) |
| prHtCap->u2HtCapInfo |= |
| (HT_CAP_INFO_SHORT_GI_20M | HT_CAP_INFO_SHORT_GI_40M); |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxLdpc)) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_LDPC_CAP; |
| |
| if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucRxStbc)) |
| prHtCap->u2HtCapInfo |= HT_CAP_INFO_RX_STBC_1_SS; |
| |
| prHtCap->ucAmpduParam = AMPDU_PARAM_DEFAULT_VAL; |
| |
| prSupMcsSet = &prHtCap->rSupMcsSet; |
| kalMemZero((void *)&prSupMcsSet->aucRxMcsBitmask[0], |
| SUP_MCS_RX_BITMASK_OCTET_NUM); |
| |
| prSupMcsSet->aucRxMcsBitmask[0] = BITS(0, 7); |
| |
| if (fg40mAllowed && IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucMCS32)) |
| prSupMcsSet->aucRxMcsBitmask[32 / 8] = BIT(0); /* MCS32 */ |
| prSupMcsSet->u2RxHighestSupportedRate = SUP_MCS_RX_DEFAULT_HIGHEST_RATE; |
| prSupMcsSet->u4TxRateInfo = SUP_MCS_TX_DEFAULT_VAL; |
| |
| prHtCap->u2HtExtendedCap = HT_EXT_CAP_DEFAULT_VAL; |
| if (!fg40mAllowed || |
| prBssInfo->eCurrentOPMode != OP_MODE_INFRASTRUCTURE) |
| prHtCap->u2HtExtendedCap &= |
| ~(HT_EXT_CAP_PCO | HT_EXT_CAP_PCO_TRANS_TIME_NONE); |
| |
| prHtCap->u4TxBeamformingCap = TX_BEAMFORMING_CAP_DEFAULT_VAL; |
| |
| prHtCap->ucAselCap = ASEL_CAP_DEFAULT_VAL; |
| |
| ASSERT(IE_SIZE(prHtCap) <= (ELEM_HDR_LEN + ELEM_MAX_LEN_HT_CAP)); |
| |
| return IE_SIZE(prHtCap); |
| } |
| |
| #endif |
| |
| #if CFG_SUPPORT_DFS |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * @brief This function will compose the TPC Report frame. |
| * |
| * @param[in] prAdapter Pointer to the Adapter structure. |
| * @param[in] prStaRec Pointer to the STA_RECORD_T. |
| * |
| * @return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void tpcComposeReportFrame(IN struct ADAPTER *prAdapter, |
| IN struct STA_RECORD *prStaRec, |
| IN PFN_TX_DONE_HANDLER pfTxDoneHandler) |
| { |
| struct MSDU_INFO *prMsduInfo; |
| struct BSS_INFO *prBssInfo; |
| struct ACTION_TPC_REPORT_FRAME *prTxFrame; |
| uint16_t u2PayloadLen; |
| |
| ASSERT(prAdapter); |
| ASSERT(prStaRec); |
| |
| prBssInfo = &prAdapter->rWifiVar.arBssInfoPool[prStaRec->ucBssIndex]; |
| ASSERT(prBssInfo); |
| |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc( |
| prAdapter, MAC_TX_RESERVED_FIELD + PUBLIC_ACTION_MAX_LEN); |
| |
| if (!prMsduInfo) |
| return; |
| |
| prTxFrame = (struct ACTION_TPC_REPORT_FRAME |
| *)((unsigned long)(prMsduInfo->prPacket) + |
| MAC_TX_RESERVED_FIELD); |
| |
| prTxFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| |
| COPY_MAC_ADDR(prTxFrame->aucDestAddr, prStaRec->aucMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucSrcAddr, prBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucBSSID, prBssInfo->aucBSSID); |
| |
| prTxFrame->ucCategory = CATEGORY_SPEC_MGT; |
| prTxFrame->ucAction = ACTION_TPC_REPORT; |
| |
| /* 3 Compose the frame body's frame. */ |
| prTxFrame->ucDialogToken = prStaRec->ucSmDialogToken; |
| prTxFrame->ucElemId = ELEM_ID_TPC_REPORT; |
| prTxFrame->ucLength = |
| sizeof(prTxFrame->ucLinkMargin) + sizeof(prTxFrame->ucTransPwr); |
| prTxFrame->ucTransPwr = prAdapter->u4GetTxPower; |
| prTxFrame->ucLinkMargin = |
| prAdapter->rLinkQuality.cRssi - (0 - MIN_RCV_PWR); |
| |
| u2PayloadLen = ACTION_SM_TPC_REPORT_LEN; |
| |
| /* 4 Update information of MSDU_INFO_T */ |
| TX_SET_MMPDU(prAdapter, prMsduInfo, prStaRec->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| WLAN_MAC_MGMT_HEADER_LEN + u2PayloadLen, pfTxDoneHandler, |
| MSDU_RATE_MODE_AUTO); |
| |
| DBGLOG(RLM, TRACE, "ucDialogToken %d ucTransPwr %d ucLinkMargin %d\n", |
| prTxFrame->ucDialogToken, prTxFrame->ucTransPwr, |
| prTxFrame->ucLinkMargin); |
| |
| /* 4 Enqueue the frame to send this action frame. */ |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| |
| return; |
| |
| } /* end of tpcComposeReportFrame() */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * @brief This function will compose the Measurement Report frame. |
| * |
| * @param[in] prAdapter Pointer to the Adapter structure. |
| * @param[in] prStaRec Pointer to the STA_RECORD_T. |
| * |
| * @return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void msmtComposeReportFrame(IN struct ADAPTER *prAdapter, |
| IN struct STA_RECORD *prStaRec, |
| IN PFN_TX_DONE_HANDLER pfTxDoneHandler) |
| { |
| struct MSDU_INFO *prMsduInfo; |
| struct BSS_INFO *prBssInfo; |
| struct ACTION_SM_REQ_FRAME *prTxFrame; |
| struct IE_MEASUREMENT_REPORT *prMeasurementRepIE; |
| uint8_t *pucIE; |
| uint16_t u2PayloadLen; |
| |
| ASSERT(prAdapter); |
| ASSERT(prStaRec); |
| |
| prBssInfo = &prAdapter->rWifiVar.arBssInfoPool[prStaRec->ucBssIndex]; |
| ASSERT(prBssInfo); |
| |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc( |
| prAdapter, MAC_TX_RESERVED_FIELD + PUBLIC_ACTION_MAX_LEN); |
| |
| if (!prMsduInfo) |
| return; |
| |
| prTxFrame = (struct ACTION_SM_REQ_FRAME |
| *)((unsigned long)(prMsduInfo->prPacket) + |
| MAC_TX_RESERVED_FIELD); |
| pucIE = prTxFrame->aucInfoElem; |
| prMeasurementRepIE = SM_MEASUREMENT_REP_IE(pucIE); |
| |
| prTxFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| |
| COPY_MAC_ADDR(prTxFrame->aucDestAddr, prStaRec->aucMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucSrcAddr, prBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucBSSID, prBssInfo->aucBSSID); |
| |
| prTxFrame->ucCategory = CATEGORY_SPEC_MGT; |
| prTxFrame->ucAction = ACTION_MEASUREMENT_REPORT; |
| |
| /* 3 Compose the frame body's frame. */ |
| prTxFrame->ucDialogToken = prStaRec->ucSmDialogToken; |
| prMeasurementRepIE->ucId = ELEM_ID_MEASUREMENT_REPORT; |
| |
| #if 0 |
| if (prStaRec->ucSmMsmtRequestMode == ELEM_RM_TYPE_BASIC_REQ) { |
| prMeasurementRepIE->ucLength = |
| sizeof(struct SM_BASIC_REPORT) + 3; |
| u2PayloadLen = ACTION_SM_MEASURE_REPORT_LEN+ |
| ACTION_SM_BASIC_REPORT_LEN; |
| } else if (prStaRec->ucSmMsmtRequestMode == ELEM_RM_TYPE_CCA_REQ) { |
| prMeasurementRepIE->ucLength = sizeof(struct SM_CCA_REPORT) + 3; |
| u2PayloadLen = ACTION_SM_MEASURE_REPORT_LEN+ |
| ACTION_SM_CCA_REPORT_LEN; |
| } else if (prStaRec->ucSmMsmtRequestMode == |
| ELEM_RM_TYPE_RPI_HISTOGRAM_REQ) { |
| prMeasurementRepIE->ucLength = sizeof(struct SM_RPI_REPORT) + 3; |
| u2PayloadLen = ACTION_SM_MEASURE_REPORT_LEN+ |
| ACTION_SM_PRI_REPORT_LEN; |
| } else { |
| prMeasurementRepIE->ucLength = 3; |
| u2PayloadLen = ACTION_SM_MEASURE_REPORT_LEN; |
| } |
| #else |
| prMeasurementRepIE->ucLength = 3; |
| u2PayloadLen = ACTION_SM_MEASURE_REPORT_LEN; |
| prMeasurementRepIE->ucToken = prStaRec->ucSmMsmtToken; |
| prMeasurementRepIE->ucReportMode = BIT(1); |
| prMeasurementRepIE->ucMeasurementType = prStaRec->ucSmMsmtRequestMode; |
| #endif |
| |
| /* 4 Update information of MSDU_INFO_T */ |
| TX_SET_MMPDU(prAdapter, prMsduInfo, prStaRec->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| WLAN_MAC_MGMT_HEADER_LEN + u2PayloadLen, pfTxDoneHandler, |
| MSDU_RATE_MODE_AUTO); |
| |
| DBGLOG(RLM, TRACE, |
| "ucDialogToken %d ucToken %d ucReportMode %d ucMeasurementType %d\n", |
| prTxFrame->ucDialogToken, prMeasurementRepIE->ucToken, |
| prMeasurementRepIE->ucReportMode, |
| prMeasurementRepIE->ucMeasurementType); |
| |
| /* 4 Enqueue the frame to send this action frame. */ |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| |
| return; |
| |
| } /* end of msmtComposeReportFrame() */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function handle spectrum management action frame |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmProcessSpecMgtAction(struct ADAPTER *prAdapter, struct SW_RFB *prSwRfb) |
| { |
| uint8_t *pucIE; |
| struct STA_RECORD *prStaRec; |
| struct BSS_INFO *prBssInfo; |
| uint16_t u2IELength; |
| uint16_t u2Offset = 0; |
| struct IE_CHANNEL_SWITCH *prChannelSwitchAnnounceIE; |
| struct IE_SECONDARY_OFFSET *prSecondaryOffsetIE; |
| struct IE_WIDE_BAND_CHANNEL *prWideBandChannelIE; |
| struct IE_TPC_REQ *prTpcReqIE; |
| struct IE_TPC_REPORT *prTpcRepIE; |
| struct IE_MEASUREMENT_REQ *prMeasurementReqIE; |
| struct IE_MEASUREMENT_REPORT *prMeasurementRepIE; |
| struct ACTION_SM_REQ_FRAME *prRxFrame; |
| u_int8_t fgHasWideBandIE = FALSE; |
| u_int8_t fgHasSCOIE = FALSE; |
| u_int8_t fgHasChannelSwitchIE = FALSE; |
| bool fgNeedSwitchChannel = FALSE; |
| #if CFG_DFS_NEWCH_DFS_FORCE_DISCONNECT |
| struct ieee80211_channel *Channel = NULL; |
| #endif |
| |
| DBGLOG(RLM, TRACE, "[Mgt Action]rlmProcessSpecMgtAction\n"); |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| |
| u2IELength = |
| prSwRfb->u2PacketLen - |
| (uint16_t)OFFSET_OF(struct ACTION_SM_REQ_FRAME, aucInfoElem[0]); |
| |
| prRxFrame = (struct ACTION_SM_REQ_FRAME *)prSwRfb->pvHeader; |
| pucIE = prRxFrame->aucInfoElem; |
| |
| prStaRec = cnmGetStaRecByIndex(prAdapter, prSwRfb->ucStaRecIdx); |
| if (!prStaRec) |
| return; |
| |
| if (prStaRec->ucBssIndex > prAdapter->ucHwBssIdNum) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| |
| prStaRec->ucSmDialogToken = prRxFrame->ucDialogToken; |
| |
| DBGLOG_MEM8(RLM, INFO, pucIE, u2IELength); |
| switch (prRxFrame->ucAction) { |
| case ACTION_MEASUREMENT_REQ: |
| DBGLOG(RLM, INFO, "[Mgt Action] Measure Request\n"); |
| prMeasurementReqIE = SM_MEASUREMENT_REQ_IE(pucIE); |
| if (prMeasurementReqIE->ucId == ELEM_ID_MEASUREMENT_REQ) { |
| /* Check IE length is valid */ |
| if (prMeasurementReqIE->ucLength != 0 && |
| (prMeasurementReqIE->ucLength >= |
| sizeof(struct IE_MEASUREMENT_REQ) - 2)) { |
| prStaRec->ucSmMsmtRequestMode = |
| prMeasurementReqIE->ucRequestMode; |
| prStaRec->ucSmMsmtToken = |
| prMeasurementReqIE->ucToken; |
| msmtComposeReportFrame(prAdapter, prStaRec, |
| NULL); |
| } |
| } |
| |
| break; |
| case ACTION_MEASUREMENT_REPORT: |
| DBGLOG(RLM, INFO, "[Mgt Action] Measure Report\n"); |
| prMeasurementRepIE = SM_MEASUREMENT_REP_IE(pucIE); |
| if (prMeasurementRepIE->ucId == ELEM_ID_MEASUREMENT_REPORT) |
| DBGLOG(RLM, TRACE, |
| "[Mgt Action] Correct Measurement report IE !!\n"); |
| break; |
| case ACTION_TPC_REQ: |
| DBGLOG(RLM, INFO, "[Mgt Action] TPC Request\n"); |
| prTpcReqIE = SM_TPC_REQ_IE(pucIE); |
| |
| if (prTpcReqIE->ucId == ELEM_ID_TPC_REQ) |
| tpcComposeReportFrame(prAdapter, prStaRec, NULL); |
| |
| break; |
| case ACTION_TPC_REPORT: |
| DBGLOG(RLM, INFO, "[Mgt Action] TPC Report\n"); |
| prTpcRepIE = SM_TPC_REP_IE(pucIE); |
| |
| if (prTpcRepIE->ucId == ELEM_ID_TPC_REPORT) |
| DBGLOG(RLM, TRACE, |
| "[Mgt Action] Correct TPC report IE !!\n"); |
| |
| break; |
| case ACTION_CHNL_SWITCH: |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| switch (IE_ID(pucIE)) { |
| |
| case ELEM_ID_WIDE_BAND_CHANNEL_SWITCH: |
| if (!RLM_NET_IS_11AC(prBssInfo) || |
| IE_LEN(pucIE) != |
| (sizeof(struct |
| IE_WIDE_BAND_CHANNEL) - |
| 2)) { |
| DBGLOG(RLM, INFO, |
| "[Mgt Action] ELEM_ID_WIDE_BAND_CHANNEL_SWITCH, Length\n"); |
| break; |
| } |
| DBGLOG(RLM, INFO, |
| "[Mgt Action] ELEM_ID_WIDE_BAND_CHANNEL_SWITCH, 11AC\n"); |
| prWideBandChannelIE = |
| (struct IE_WIDE_BAND_CHANNEL *)pucIE; |
| prBssInfo->ucVhtChannelWidth = |
| prWideBandChannelIE->ucNewChannelWidth; |
| prBssInfo->ucVhtChannelFrequencyS1 = |
| prWideBandChannelIE->ucChannelS1; |
| prBssInfo->ucVhtChannelFrequencyS2 = |
| prWideBandChannelIE->ucChannelS2; |
| |
| /* Revise by own OP BW if needed */ |
| if ((prBssInfo->fgIsOpChangeChannelWidth) && |
| (rlmGetVhtOpBwByBssOpBw( |
| prBssInfo |
| ->ucOpChangeChannelWidth) < |
| prBssInfo->ucVhtChannelWidth)) { |
| |
| DBGLOG(RLM, LOUD, |
| "Change to w:%d s1:%d s2:%d since own changed BW < peer's WideBand BW", |
| prBssInfo->ucVhtChannelWidth, |
| prBssInfo->ucVhtChannelFrequencyS1, |
| prBssInfo->ucVhtChannelFrequencyS2); |
| rlmFillVhtOpInfoByBssOpBw( |
| prBssInfo, |
| prBssInfo |
| ->ucOpChangeChannelWidth); |
| } |
| |
| fgHasWideBandIE = TRUE; |
| break; |
| |
| case ELEM_ID_CH_SW_ANNOUNCEMENT: |
| if (IE_LEN(pucIE) != |
| (sizeof(struct IE_CHANNEL_SWITCH) - 2)) { |
| DBGLOG(RLM, INFO, |
| "[Mgt Action] ELEM_ID_CH_SW_ANNOUNCEMENT, Length\n"); |
| break; |
| } |
| |
| prChannelSwitchAnnounceIE = |
| (struct IE_CHANNEL_SWITCH *)pucIE; |
| |
| if (prChannelSwitchAnnounceIE |
| ->ucChannelSwitchMode == 1) { |
| /* Need to stop data |
| * transmission immediately |
| */ |
| if (!g_fgHasStopTx) { |
| g_fgHasStopTx = TRUE; |
| #if CFG_SUPPORT_TDLS |
| /* TDLS peers */ |
| TdlsTxCtrl(prAdapter, |
| prBssInfo, |
| FALSE); |
| #endif |
| /* AP */ |
| qmSetStaRecTxAllowed(prAdapter, |
| prStaRec, |
| FALSE); |
| DBGLOG(RLM, EVENT, |
| "[Ch] TxAllowed = FALSE\n"); |
| } |
| |
| if (prChannelSwitchAnnounceIE |
| ->ucChannelSwitchCount <= 3) { |
| DBGLOG(RLM, INFO, |
| "[Mgt Action] switch channel [%d]->[%d]\n", |
| prBssInfo->ucPrimaryChannel, |
| prChannelSwitchAnnounceIE |
| ->ucNewChannelNum); |
| prBssInfo->ucPrimaryChannel = |
| prChannelSwitchAnnounceIE |
| ->ucNewChannelNum; |
| fgNeedSwitchChannel = TRUE; |
| } |
| } else { |
| DBGLOG(RLM, INFO, |
| "[Mgt Action] ucChannelSwitchMode = 0\n"); |
| } |
| |
| fgHasChannelSwitchIE = TRUE; |
| #if CFG_DFS_NEWCH_DFS_FORCE_DISCONNECT |
| Channel = ieee80211_get_channel( |
| priv_to_wiphy(prAdapter->prGlueInfo), |
| ieee80211_channel_to_frequency(prChannelSwitchAnnounceIE->ucNewChannelNum, |
| KAL_BAND_5GHZ)); |
| DBGLOG(RLM, INFO, "[DFS][CSA][CLIENT] Switch to DFS channel: new ChNum = [%d]\n",prChannelSwitchAnnounceIE->ucNewChannelNum); |
| if (Channel && |
| (Channel->flags & IEEE80211_CHAN_RADAR)) { |
| DBGLOG(RLM, INFO, "[DFS][CSA][CLIENT] New channel is DFS channel!"); |
| fgNeedSwitchChannel = FALSE; |
| fgHasChannelSwitchIE = FALSE; |
| } |
| #endif |
| break; |
| case ELEM_ID_SCO: |
| if (IE_LEN(pucIE) != |
| (sizeof(struct IE_SECONDARY_OFFSET) - 2)) { |
| DBGLOG(RLM, INFO, |
| "[Mgt Action] ELEM_ID_SCO, Length\n"); |
| break; |
| } |
| prSecondaryOffsetIE = |
| (struct IE_SECONDARY_OFFSET *)pucIE; |
| DBGLOG(RLM, INFO, |
| "[Mgt Action] SCO [%d]->[%d]\n", |
| prBssInfo->eBssSCO, |
| prSecondaryOffsetIE->ucSecondaryOffset); |
| prBssInfo->eBssSCO = |
| prSecondaryOffsetIE->ucSecondaryOffset; |
| fgHasSCOIE = TRUE; |
| break; |
| default: |
| break; |
| } /*end of switch IE_ID */ |
| } /*end of IE_FOR_EACH */ |
| if (fgHasChannelSwitchIE != FALSE) { |
| if (fgHasWideBandIE == FALSE) { |
| prBssInfo->ucVhtChannelWidth = 0; |
| prBssInfo->ucVhtChannelFrequencyS1 = |
| prBssInfo->ucPrimaryChannel; |
| prBssInfo->ucVhtChannelFrequencyS2 = 0; |
| } |
| if (fgHasSCOIE == FALSE) |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| if (fgNeedSwitchChannel) |
| kalIndicateChannelSwitch( |
| prAdapter->prGlueInfo, |
| prBssInfo->eBssSCO, |
| prBssInfo->ucPrimaryChannel); |
| } |
| nicUpdateBss(prAdapter, prBssInfo->ucBssIndex); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| #endif |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Send OpMode Norification frame (VHT action frame) |
| * |
| * \param[in] ucChannelWidth 0:20MHz, 1:40MHz, 2:80MHz, 3:160MHz or 80+80MHz |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmSendOpModeNotificationFrame(struct ADAPTER *prAdapter, |
| struct STA_RECORD *prStaRec, |
| uint8_t ucChannelWidth, uint8_t ucNss) |
| { |
| |
| struct MSDU_INFO *prMsduInfo; |
| struct ACTION_OP_MODE_NOTIFICATION_FRAME *prTxFrame; |
| struct BSS_INFO *prBssInfo; |
| uint16_t u2EstimatedFrameLen; |
| PFN_TX_DONE_HANDLER pfTxDoneHandler = (PFN_TX_DONE_HANDLER)NULL; |
| |
| /* Sanity Check */ |
| if (!prStaRec) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| if (!prBssInfo) |
| return; |
| |
| /* Calculate MSDU buffer length */ |
| u2EstimatedFrameLen = MAC_TX_RESERVED_FIELD + |
| sizeof(struct ACTION_OP_MODE_NOTIFICATION_FRAME); |
| |
| /* Alloc MSDU_INFO */ |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc(prAdapter, |
| u2EstimatedFrameLen); |
| |
| if (!prMsduInfo) |
| return; |
| |
| kalMemZero(prMsduInfo->prPacket, u2EstimatedFrameLen); |
| |
| prTxFrame = prMsduInfo->prPacket; |
| |
| /* Fill frame ctrl */ |
| prTxFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| |
| COPY_MAC_ADDR(prTxFrame->aucDestAddr, prStaRec->aucMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucSrcAddr, prBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucBSSID, prBssInfo->aucBSSID); |
| |
| /* 3 Compose the frame body's frame */ |
| prTxFrame->ucCategory = CATEGORY_VHT_ACTION; |
| prTxFrame->ucAction = ACTION_OPERATING_MODE_NOTIFICATION; |
| |
| prTxFrame->ucOperatingMode |= |
| (ucChannelWidth & VHT_OP_MODE_CHANNEL_WIDTH); |
| |
| if (ucNss == 0) |
| ucNss = 1; |
| prTxFrame->ucOperatingMode |= (((ucNss - 1) << 4) & VHT_OP_MODE_RX_NSS); |
| prTxFrame->ucOperatingMode &= ~VHT_OP_MODE_RX_NSS_TYPE; |
| |
| if (prBssInfo->pfOpChangeHandler) |
| pfTxDoneHandler = rlmNotifyVhtOpModeTxDone; |
| |
| /* 4 Update information of MSDU_INFO_T */ |
| TX_SET_MMPDU(prAdapter, prMsduInfo, prBssInfo->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| sizeof(struct ACTION_OP_MODE_NOTIFICATION_FRAME), |
| pfTxDoneHandler, MSDU_RATE_MODE_AUTO); |
| |
| /* 4 Enqueue the frame to send this action frame. */ |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Send SM Power Save frame (HT action frame) |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmSendSmPowerSaveFrame(struct ADAPTER *prAdapter, |
| struct STA_RECORD *prStaRec, uint8_t ucNss) |
| { |
| struct MSDU_INFO *prMsduInfo; |
| struct ACTION_SM_POWER_SAVE_FRAME *prTxFrame; |
| struct BSS_INFO *prBssInfo; |
| uint16_t u2EstimatedFrameLen; |
| PFN_TX_DONE_HANDLER pfTxDoneHandler = (PFN_TX_DONE_HANDLER)NULL; |
| |
| /* Sanity Check */ |
| if (!prStaRec) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| if (!prBssInfo) |
| return; |
| |
| /* Calculate MSDU buffer length */ |
| u2EstimatedFrameLen = MAC_TX_RESERVED_FIELD + |
| sizeof(struct ACTION_SM_POWER_SAVE_FRAME); |
| |
| /* Alloc MSDU_INFO */ |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc(prAdapter, |
| u2EstimatedFrameLen); |
| |
| if (!prMsduInfo) |
| return; |
| |
| kalMemZero(prMsduInfo->prPacket, u2EstimatedFrameLen); |
| |
| prTxFrame = prMsduInfo->prPacket; |
| |
| /* Fill frame ctrl */ |
| prTxFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| |
| COPY_MAC_ADDR(prTxFrame->aucDestAddr, prStaRec->aucMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucSrcAddr, prBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucBSSID, prBssInfo->aucBSSID); |
| |
| /* 3 Compose the frame body's frame */ |
| prTxFrame->ucCategory = CATEGORY_HT_ACTION; |
| prTxFrame->ucAction = ACTION_HT_SM_POWER_SAVE; |
| |
| if (ucNss == 1) |
| prTxFrame->ucSmPowerCtrl |= HT_SM_POWER_SAVE_CONTROL_ENABLED; |
| else if (ucNss == 2) |
| prTxFrame->ucSmPowerCtrl &= ~HT_SM_POWER_SAVE_CONTROL_ENABLED; |
| else { |
| DBGLOG(RLM, WARN, |
| "Can't switch to Nss = %d since we don't support.\n", |
| ucNss); |
| return; |
| } |
| |
| /* Static SM power save mode */ |
| prTxFrame->ucSmPowerCtrl &= |
| (~HT_SM_POWER_SAVE_CONTROL_SM_MODE); |
| |
| if (prBssInfo->pfOpChangeHandler) |
| pfTxDoneHandler = rlmSmPowerSaveTxDone; |
| |
| /* 4 Update information of MSDU_INFO_T */ |
| TX_SET_MMPDU(prAdapter, prMsduInfo, prBssInfo->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| sizeof(struct ACTION_SM_POWER_SAVE_FRAME), pfTxDoneHandler, |
| MSDU_RATE_MODE_AUTO); |
| |
| /* 4 Enqueue the frame to send this action frame. */ |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Send Notify Channel Width frame (HT action frame) |
| * |
| * \param[in] ucChannelWidth 0:20MHz, 1:Any channel width |
| * in the STAs Supported Channel Width Set subfield |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmSendNotifyChannelWidthFrame(struct ADAPTER *prAdapter, |
| struct STA_RECORD *prStaRec, |
| uint8_t ucChannelWidth) |
| { |
| struct MSDU_INFO *prMsduInfo; |
| struct ACTION_NOTIFY_CHANNEL_WIDTH_FRAME *prTxFrame; |
| struct BSS_INFO *prBssInfo; |
| uint16_t u2EstimatedFrameLen; |
| PFN_TX_DONE_HANDLER pfTxDoneHandler = (PFN_TX_DONE_HANDLER)NULL; |
| |
| /* Sanity Check */ |
| if (!prStaRec) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| if (!prBssInfo) |
| return; |
| |
| /* Calculate MSDU buffer length */ |
| u2EstimatedFrameLen = MAC_TX_RESERVED_FIELD + |
| sizeof(struct ACTION_NOTIFY_CHANNEL_WIDTH_FRAME); |
| |
| /* Alloc MSDU_INFO */ |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc(prAdapter, |
| u2EstimatedFrameLen); |
| |
| if (!prMsduInfo) |
| return; |
| |
| kalMemZero(prMsduInfo->prPacket, u2EstimatedFrameLen); |
| |
| prTxFrame = prMsduInfo->prPacket; |
| |
| /* Fill frame ctrl */ |
| prTxFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| |
| COPY_MAC_ADDR(prTxFrame->aucDestAddr, prStaRec->aucMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucSrcAddr, prBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucBSSID, prBssInfo->aucBSSID); |
| |
| /* 3 Compose the frame body's frame */ |
| prTxFrame->ucCategory = CATEGORY_HT_ACTION; |
| prTxFrame->ucAction = ACTION_HT_NOTIFY_CHANNEL_WIDTH; |
| |
| prTxFrame->ucChannelWidth = ucChannelWidth; |
| |
| if (prBssInfo->pfOpChangeHandler) |
| pfTxDoneHandler = rlmNotifyChannelWidthtTxDone; |
| |
| /* 4 Update information of MSDU_INFO_T */ |
| TX_SET_MMPDU(prAdapter, prMsduInfo, prBssInfo->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| sizeof(struct ACTION_NOTIFY_CHANNEL_WIDTH_FRAME), |
| pfTxDoneHandler, MSDU_RATE_MODE_AUTO); |
| |
| /* 4 Enqueue the frame to send this action frame. */ |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| * |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint32_t rlmNotifyVhtOpModeTxDone(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo, |
| IN enum ENUM_TX_RESULT_CODE rTxDoneStatus) |
| { |
| u_int8_t fgIsSuccess = FALSE; |
| |
| do { |
| ASSERT((prAdapter != NULL) && (prMsduInfo != NULL)); |
| |
| if (rTxDoneStatus == TX_RESULT_SUCCESS) |
| fgIsSuccess = TRUE; |
| |
| } while (FALSE); |
| |
| rlmOpModeTxDoneHandler(prAdapter, prMsduInfo, OP_NOTIFY_TYPE_VHT_NSS_BW, |
| fgIsSuccess); |
| |
| return WLAN_STATUS_SUCCESS; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint32_t rlmSmPowerSaveTxDone(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo, |
| IN enum ENUM_TX_RESULT_CODE rTxDoneStatus) |
| { |
| u_int8_t fgIsSuccess = FALSE; |
| |
| do { |
| ASSERT((prAdapter != NULL) && (prMsduInfo != NULL)); |
| |
| if (rTxDoneStatus == TX_RESULT_SUCCESS) |
| fgIsSuccess = TRUE; |
| |
| } while (FALSE); |
| |
| rlmOpModeTxDoneHandler(prAdapter, prMsduInfo, OP_NOTIFY_TYPE_HT_NSS, |
| fgIsSuccess); |
| |
| return WLAN_STATUS_SUCCESS; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint32_t rlmNotifyChannelWidthtTxDone(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo, |
| IN enum ENUM_TX_RESULT_CODE rTxDoneStatus) |
| { |
| u_int8_t fgIsSuccess = FALSE; |
| |
| do { |
| ASSERT((prAdapter != NULL) && (prMsduInfo != NULL)); |
| |
| if (rTxDoneStatus == TX_RESULT_SUCCESS) |
| fgIsSuccess = TRUE; |
| |
| } while (FALSE); |
| |
| rlmOpModeTxDoneHandler(prAdapter, prMsduInfo, OP_NOTIFY_TYPE_HT_BW, |
| fgIsSuccess); |
| |
| return WLAN_STATUS_SUCCESS; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Handle TX done for OP mode noritfication frame |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmOpModeTxDoneHandler(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo, |
| IN uint8_t ucOpChangeType, |
| IN u_int8_t fgIsSuccess) |
| { |
| struct BSS_INFO *prBssInfo = NULL; |
| struct STA_RECORD *prStaRec = NULL; |
| u_int8_t fgIsOpModeChangeSuccess = FALSE; /* OP change result */ |
| uint8_t ucRelatedFrameType = |
| OP_NOTIFY_TYPE_NUM; /* Used for HT notification frame */ |
| enum ENUM_OP_NOTIFY_STATE_T |
| *pucCurrOpState = NULL, |
| *pucRelatedOpState = NULL; /* Used for HT notification frame */ |
| |
| /* Sanity check */ |
| ASSERT((prAdapter != NULL) && (prMsduInfo != NULL)); |
| |
| prBssInfo = prAdapter->aprBssInfo[prMsduInfo->ucBssIndex]; |
| |
| ASSERT(prBssInfo); |
| |
| prStaRec = prBssInfo->prStaRecOfAP; |
| |
| ASSERT(prStaRec); |
| |
| DBGLOG(RLM, INFO, |
| "OP notification Tx done: BSS[%d] Type[%d] Status[%d] IsSuccess[%d]\n", |
| prBssInfo->ucBssIndex, ucOpChangeType, |
| prBssInfo->aucOpModeChangeState[ucOpChangeType], fgIsSuccess); |
| |
| do { |
| /* <1>handle abnormal case */ |
| if ((prBssInfo->aucOpModeChangeState[ucOpChangeType] != |
| OP_NOTIFY_STATE_KEEP) && |
| (prBssInfo->aucOpModeChangeState[ucOpChangeType] != |
| OP_NOTIFY_STATE_SENDING)) { |
| DBGLOG(RLM, WARN, |
| "Unexpected BSS[%d] OpModeChangeState[%d]\n", |
| prBssInfo->ucBssIndex, |
| prBssInfo->aucOpModeChangeState[ucOpChangeType]); |
| rlmRollbackOpChangeParam(prBssInfo, TRUE, TRUE); |
| fgIsOpModeChangeSuccess = FALSE; |
| break; |
| } |
| |
| if (ucOpChangeType >= OP_NOTIFY_TYPE_NUM) { |
| DBGLOG(RLM, WARN, |
| "Uxexpected Bss[%d] OpChangeType[%d]\n", |
| prMsduInfo->ucBssIndex, ucOpChangeType); |
| rlmRollbackOpChangeParam(prBssInfo, TRUE, TRUE); |
| fgIsOpModeChangeSuccess = FALSE; |
| break; |
| } |
| |
| /* <2>Assign Op notification Type/State for HT notification |
| * frame |
| */ |
| if ((ucOpChangeType == OP_NOTIFY_TYPE_HT_BW) || |
| (ucOpChangeType == OP_NOTIFY_TYPE_HT_NSS)) { |
| |
| ucRelatedFrameType = |
| (ucOpChangeType == OP_NOTIFY_TYPE_HT_BW) |
| ? OP_NOTIFY_TYPE_HT_NSS |
| : OP_NOTIFY_TYPE_HT_BW; |
| |
| pucCurrOpState = |
| (enum ENUM_OP_NOTIFY_STATE_T *)&prBssInfo |
| ->aucOpModeChangeState[ucOpChangeType]; |
| pucRelatedOpState = |
| (enum ENUM_OP_NOTIFY_STATE_T *)&prBssInfo |
| ->aucOpModeChangeState |
| [ucRelatedFrameType]; |
| } |
| |
| /* <3.1>handle TX done - SUCCESS */ |
| if (fgIsSuccess == TRUE) { |
| |
| /* Clear retry count */ |
| prBssInfo->aucOpModeChangeRetryCnt[ucOpChangeType] = 0; |
| |
| if (ucOpChangeType == OP_NOTIFY_TYPE_VHT_NSS_BW) { |
| /* VHT notification frame sent */ |
| fgIsOpModeChangeSuccess = TRUE; |
| break; |
| } |
| |
| /* HT notification frame sent */ |
| if (*pucCurrOpState == |
| OP_NOTIFY_STATE_SENDING) { /* Change OpMode */ |
| |
| *pucCurrOpState = OP_NOTIFY_STATE_SUCCESS; |
| |
| /* Case1: Wait for both HT BW/Nss notification |
| * frame TX done |
| */ |
| if (*pucRelatedOpState == |
| OP_NOTIFY_STATE_SENDING) |
| return; |
| |
| /* Case2: Both BW and Nss notification TX done |
| * or only change either BW or Nss |
| */ |
| if ((*pucRelatedOpState == |
| OP_NOTIFY_STATE_KEEP) || |
| (*pucRelatedOpState == |
| OP_NOTIFY_STATE_SUCCESS)) { |
| fgIsOpModeChangeSuccess = TRUE; |
| |
| /* Case3: One of the notification TX |
| * failed, |
| * re-send a notification frame to |
| * rollback the successful one |
| */ |
| } else if (*pucRelatedOpState == |
| OP_NOTIFY_STATE_FAIL) { |
| /*Rollback to keep the original BW/Nss |
| */ |
| *pucCurrOpState = OP_NOTIFY_STATE_KEEP; |
| if (ucOpChangeType == |
| OP_NOTIFY_TYPE_HT_BW) |
| rlmSendNotifyChannelWidthFrame( |
| prAdapter, prStaRec, |
| rlmGetBssOpBwByVhtAndHtOpInfo( |
| prBssInfo)); |
| else if (ucOpChangeType == |
| OP_NOTIFY_TYPE_HT_NSS) |
| rlmSendSmPowerSaveFrame( |
| prAdapter, prStaRec, |
| prBssInfo->ucNss); |
| |
| DBGLOG(RLM, INFO, |
| "Bss[%d] OpType[%d] Tx Failed, send OpType[%d] for roll back to BW[%d] Nss[%d]\n", |
| prMsduInfo->ucBssIndex, |
| ucRelatedFrameType, |
| ucOpChangeType, |
| rlmGetBssOpBwByVhtAndHtOpInfo( |
| prBssInfo), |
| prBssInfo->ucNss); |
| |
| return; |
| } |
| } else if (*pucCurrOpState == |
| OP_NOTIFY_STATE_KEEP) { /* Rollback OpMode */ |
| |
| /* Case4: Rollback success, keep original OP |
| * BW/Nss |
| */ |
| if (ucOpChangeType == OP_NOTIFY_TYPE_HT_BW) |
| rlmRollbackOpChangeParam(prBssInfo, |
| TRUE, FALSE); |
| else if (ucOpChangeType == |
| OP_NOTIFY_TYPE_HT_NSS) |
| rlmRollbackOpChangeParam(prBssInfo, |
| FALSE, TRUE); |
| |
| fgIsOpModeChangeSuccess = FALSE; |
| } |
| } /* End of processing TX success */ |
| /* <3.2>handle TX done - FAIL */ |
| else { |
| prBssInfo->aucOpModeChangeRetryCnt[ucOpChangeType]++; |
| |
| /* Re-send notification frame */ |
| if (prBssInfo |
| ->aucOpModeChangeRetryCnt[ucOpChangeType] <= |
| OPERATION_NOTICATION_TX_LIMIT) { |
| if (ucOpChangeType == OP_NOTIFY_TYPE_VHT_NSS_BW) |
| rlmSendOpModeNotificationFrame( |
| prAdapter, prStaRec, |
| prBssInfo->ucOpChangeChannelWidth, |
| prBssInfo->ucOpChangeNss); |
| else if (ucOpChangeType == |
| OP_NOTIFY_TYPE_HT_NSS) |
| rlmSendSmPowerSaveFrame( |
| prAdapter, prStaRec, |
| prBssInfo->ucOpChangeNss); |
| else if (ucOpChangeType == OP_NOTIFY_TYPE_HT_BW) |
| rlmSendNotifyChannelWidthFrame( |
| prAdapter, prStaRec, |
| prBssInfo->ucOpChangeChannelWidth); |
| return; |
| } |
| |
| /* Clear retry count when retry count > TX limit */ |
| prBssInfo->aucOpModeChangeRetryCnt[ucOpChangeType] = 0; |
| |
| /* VHT notification frame sent */ |
| if (ucOpChangeType == |
| OP_NOTIFY_TYPE_VHT_NSS_BW) { |
| /* Change failed, keep original OP BW/Nss */ |
| rlmRollbackOpChangeParam(prBssInfo, TRUE, TRUE); |
| fgIsOpModeChangeSuccess = FALSE; |
| break; |
| } |
| |
| /* HT notification frame sent */ |
| if (*pucCurrOpState == |
| OP_NOTIFY_STATE_SENDING) { /* Change OpMode */ |
| *pucCurrOpState = OP_NOTIFY_STATE_FAIL; |
| |
| /* Change failed, keep original OP BW/Nss */ |
| if (ucOpChangeType == OP_NOTIFY_TYPE_HT_BW) |
| rlmRollbackOpChangeParam(prBssInfo, |
| TRUE, FALSE); |
| else if (ucOpChangeType == |
| OP_NOTIFY_TYPE_HT_NSS) |
| rlmRollbackOpChangeParam(prBssInfo, |
| FALSE, TRUE); |
| |
| /* Case1: Wait for both HT BW/Nss notification |
| * frame TX done |
| */ |
| if (*pucRelatedOpState == |
| OP_NOTIFY_STATE_SENDING) { |
| return; |
| |
| /* Case2: Both BW and Nss notification |
| * TX done |
| * or only change either BW or Nss |
| */ |
| } else if ((*pucRelatedOpState == |
| OP_NOTIFY_STATE_KEEP) || |
| (*pucRelatedOpState == |
| OP_NOTIFY_STATE_FAIL)) { |
| fgIsOpModeChangeSuccess = FALSE; |
| |
| /* Case3: One of the notification TX |
| * failed, |
| * re-send a notification frame to |
| * rollback the successful one |
| */ |
| } else if (*pucRelatedOpState == |
| OP_NOTIFY_STATE_SUCCESS) { |
| /*Rollback to keep the original BW/Nss |
| */ |
| *pucRelatedOpState = |
| OP_NOTIFY_STATE_KEEP; |
| |
| if (ucRelatedFrameType == |
| OP_NOTIFY_TYPE_HT_BW) { |
| rlmSendNotifyChannelWidthFrame( |
| prAdapter, prStaRec, |
| rlmGetBssOpBwByVhtAndHtOpInfo( |
| prBssInfo)); |
| } else if (ucRelatedFrameType == |
| OP_NOTIFY_TYPE_HT_NSS) |
| rlmSendSmPowerSaveFrame( |
| prAdapter, prStaRec, |
| prBssInfo->ucNss); |
| |
| DBGLOG(RLM, INFO, |
| "Bss[%d] OpType[%d] Tx Failed, send a OpType[%d] for roll back to BW[%d] Nss[%d]\n", |
| prMsduInfo->ucBssIndex, |
| ucOpChangeType, |
| ucRelatedFrameType, |
| rlmGetBssOpBwByVhtAndHtOpInfo( |
| prBssInfo), |
| prBssInfo->ucNss); |
| |
| return; |
| } |
| } else if (*pucCurrOpState == |
| OP_NOTIFY_STATE_KEEP) /* Rollback OpMode */ |
| /* Case4: Rollback failed, keep changing OP |
| * BW/Nss |
| */ |
| fgIsOpModeChangeSuccess = FALSE; |
| } /* End of processing TX failed */ |
| |
| } while (FALSE); |
| |
| /* <4>Change own OP info */ |
| rlmCompleteOpModeChange(prAdapter, prBssInfo, fgIsOpModeChangeSuccess); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmRollbackOpChangeParam(struct BSS_INFO *prBssInfo, |
| u_int8_t fgIsRollbackBw, |
| u_int8_t fgIsRollbackNss) |
| { |
| |
| ASSERT(prBssInfo); |
| |
| if (fgIsRollbackBw == TRUE) { |
| prBssInfo->fgIsOpChangeChannelWidth = FALSE; |
| prBssInfo->ucOpChangeChannelWidth = |
| rlmGetBssOpBwByVhtAndHtOpInfo(prBssInfo); |
| } |
| |
| if (fgIsRollbackNss == TRUE) { |
| prBssInfo->fgIsOpChangeNss = FALSE; |
| prBssInfo->ucOpChangeNss = prBssInfo->ucNss; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Get BSS operating channel width by VHT and HT OP Info |
| * |
| * \param[in] |
| * |
| * \return ucBssOpBw 0:20MHz, 1:40MHz, 2:80MHz, 3:160MHz 4:80+80MHz |
| * |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint8_t rlmGetBssOpBwByVhtAndHtOpInfo(struct BSS_INFO *prBssInfo) |
| { |
| |
| uint8_t ucBssOpBw = MAX_BW_20MHZ; |
| |
| ASSERT(prBssInfo); |
| |
| switch (prBssInfo->ucVhtChannelWidth) { |
| case VHT_OP_CHANNEL_WIDTH_80P80: |
| ucBssOpBw = MAX_BW_80_80_MHZ; |
| break; |
| |
| case VHT_OP_CHANNEL_WIDTH_160: |
| ucBssOpBw = MAX_BW_160MHZ; |
| break; |
| |
| case VHT_OP_CHANNEL_WIDTH_80: |
| ucBssOpBw = MAX_BW_80MHZ; |
| break; |
| |
| case VHT_OP_CHANNEL_WIDTH_20_40: |
| if (prBssInfo->eBssSCO != CHNL_EXT_SCN) |
| ucBssOpBw = MAX_BW_40MHZ; |
| break; |
| default: |
| DBGLOG(RLM, WARN, "%s: unexpected VHT channel width: %d\n", |
| __func__, prBssInfo->ucVhtChannelWidth); |
| #if CFG_SUPPORT_802_11AC |
| if (RLM_NET_IS_11AC(prBssInfo)) |
| /*VHT default should support BW 80*/ |
| ucBssOpBw = MAX_BW_80MHZ; |
| #endif |
| break; |
| } |
| |
| return ucBssOpBw; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return ucVhtOpBw 0:20M/40Hz, 1:80MHz, 2:160MHz, 3:80+80MHz |
| * |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint8_t rlmGetVhtOpBwByBssOpBw(uint8_t ucBssOpBw) |
| { |
| uint8_t ucVhtOpBw = |
| VHT_OP_CHANNEL_WIDTH_80; /*VHT default should support BW 80*/ |
| |
| switch (ucBssOpBw) { |
| case MAX_BW_20MHZ: |
| case MAX_BW_40MHZ: |
| ucVhtOpBw = VHT_OP_CHANNEL_WIDTH_20_40; |
| break; |
| |
| case MAX_BW_80MHZ: |
| ucVhtOpBw = VHT_OP_CHANNEL_WIDTH_80; |
| break; |
| |
| case MAX_BW_160MHZ: |
| ucVhtOpBw = VHT_OP_CHANNEL_WIDTH_160; |
| break; |
| |
| case MAX_BW_80_80_MHZ: |
| ucVhtOpBw = VHT_OP_CHANNEL_WIDTH_80P80; |
| break; |
| default: |
| DBGLOG(RLM, WARN, "%s: unexpected Bss OP BW: %d\n", __func__, |
| ucBssOpBw); |
| break; |
| } |
| |
| return ucVhtOpBw; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Get operating notification channel width by VHT and HT operating Info |
| * |
| * \param[in] |
| * |
| * \return ucOpModeBw 0:20MHz, 1:40MHz, 2:80MHz, 3:160MHz/80+80MHz |
| * |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static uint8_t rlmGetOpModeBwByVhtAndHtOpInfo(struct BSS_INFO *prBssInfo) |
| { |
| uint8_t ucOpModeBw = VHT_OP_MODE_CHANNEL_WIDTH_20; |
| |
| ASSERT(prBssInfo); |
| |
| switch (prBssInfo->ucVhtChannelWidth) { |
| case VHT_OP_CHANNEL_WIDTH_20_40: |
| if (prBssInfo->eBssSCO != CHNL_EXT_SCN) |
| ucOpModeBw = VHT_OP_MODE_CHANNEL_WIDTH_40; |
| break; |
| case VHT_OP_CHANNEL_WIDTH_80: |
| ucOpModeBw = VHT_OP_MODE_CHANNEL_WIDTH_80; |
| break; |
| case VHT_OP_CHANNEL_WIDTH_160: |
| case VHT_OP_CHANNEL_WIDTH_80P80: |
| ucOpModeBw = VHT_OP_MODE_CHANNEL_WIDTH_160_80P80; |
| break; |
| default: |
| DBGLOG(RLM, WARN, "%s: unexpected VHT channel width: %d\n", |
| __func__, prBssInfo->ucVhtChannelWidth); |
| /*VHT default IE should support BW 80*/ |
| ucOpModeBw = VHT_OP_MODE_CHANNEL_WIDTH_80; |
| break; |
| } |
| |
| return ucOpModeBw; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmChangeOwnOpInfo(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo) |
| { |
| struct STA_RECORD *prStaRec; |
| |
| ASSERT((prAdapter != NULL) && (prBssInfo != NULL)); |
| |
| /* Update own operating channel Width */ |
| if (prBssInfo->fgIsOpChangeChannelWidth) { |
| if (prBssInfo->ucPhyTypeSet & PHY_TYPE_BIT_HT) { |
| #if CFG_SUPPORT_802_11AC |
| /* Update VHT OP Info*/ |
| if (prBssInfo->ucPhyTypeSet & PHY_TYPE_BIT_VHT) { |
| rlmFillVhtOpInfoByBssOpBw( |
| prBssInfo, |
| prBssInfo->ucOpChangeChannelWidth); |
| |
| DBGLOG(RLM, INFO, |
| "Update BSS[%d] VHT Channel Width Info to w=%d s1=%d s2=%d\n", |
| prBssInfo->ucBssIndex, |
| prBssInfo->ucVhtChannelWidth, |
| prBssInfo->ucVhtChannelFrequencyS1, |
| prBssInfo->ucVhtChannelFrequencyS2); |
| } |
| #endif |
| |
| /* Update HT OP Info*/ |
| if (prBssInfo->ucOpChangeChannelWidth == MAX_BW_20MHZ) { |
| prBssInfo->ucHtOpInfo1 &= |
| ~HT_OP_INFO1_STA_CHNL_WIDTH; |
| prBssInfo->eBssSCO = CHNL_EXT_SCN; |
| } else { |
| prBssInfo->ucHtOpInfo1 |= |
| HT_OP_INFO1_STA_CHNL_WIDTH; |
| |
| if (prBssInfo->eCurrentOPMode == |
| OP_MODE_INFRASTRUCTURE) { |
| prStaRec = prBssInfo->prStaRecOfAP; |
| if (!prStaRec) |
| return; |
| |
| if ((prStaRec->ucHtPeerOpInfo1 & |
| HT_OP_INFO1_SCO) != CHNL_EXT_RES) |
| prBssInfo->eBssSCO = |
| (enum ENUM_CHNL_EXT)( |
| prStaRec->ucHtPeerOpInfo1 & |
| HT_OP_INFO1_SCO); |
| } else if (prBssInfo->eCurrentOPMode == |
| OP_MODE_ACCESS_POINT) { |
| prBssInfo->eBssSCO = rlmDecideScoForAP( |
| prAdapter, prBssInfo); |
| } |
| } |
| |
| DBGLOG(RLM, INFO, |
| "Update BSS[%d] HT Channel Width Info to bw=%d sco=%d\n", |
| prBssInfo->ucBssIndex, |
| (uint8_t)((prBssInfo->ucHtOpInfo1 & |
| HT_OP_INFO1_STA_CHNL_WIDTH) >> |
| HT_OP_INFO1_STA_CHNL_WIDTH_OFFSET), |
| prBssInfo->eBssSCO); |
| } |
| } |
| |
| /* Update own operating Nss */ |
| if (prBssInfo->fgIsOpChangeNss) { |
| prBssInfo->ucNss = prBssInfo->ucOpChangeNss; |
| DBGLOG(RLM, INFO, "Update OP Nss = %d\n", prBssInfo->ucNss); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void rlmCompleteOpModeChange(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| u_int8_t fgIsSuccess) |
| { |
| |
| ASSERT((prAdapter != NULL) && (prBssInfo != NULL)); |
| |
| if ((prBssInfo->fgIsOpChangeChannelWidth) || |
| (prBssInfo->fgIsOpChangeNss)) { |
| |
| /* <1> Update own OP BW/Nss */ |
| rlmChangeOwnOpInfo(prAdapter, prBssInfo); |
| |
| /* <2> Update OP BW/Nss to FW */ |
| rlmSyncOperationParams(prAdapter, prBssInfo); |
| |
| /* <3> Update BCN/Probe Resp IE to notify peers our OP info is |
| * changed (AP mode) |
| */ |
| if (prBssInfo->eCurrentOPMode == OP_MODE_ACCESS_POINT) |
| bssUpdateBeaconContent(prAdapter, |
| prBssInfo->ucBssIndex); |
| } |
| |
| DBGLOG(RLM, INFO, "Complete BSS[%d] OP Mode change to BW[%d] Nss[%d]\n", |
| prBssInfo->ucBssIndex, rlmGetBssOpBwByVhtAndHtOpInfo(prBssInfo), |
| prBssInfo->ucNss); |
| |
| /* <4> Tell OpMode change caller the change result */ |
| if (prBssInfo->pfOpChangeHandler) { |
| prBssInfo->pfOpChangeHandler(prAdapter, prBssInfo->ucBssIndex, |
| fgIsSuccess); |
| /* Clear to NULL when handling OP Mode change request done */ |
| prBssInfo->pfOpChangeHandler = NULL; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief Change OpMode Nss/Channel Width |
| * |
| * \param[in] ucChannelWidth 0:20MHz, 1:40MHz, 2:80MHz, 3:160MHz 4:80+80MHz |
| * |
| * \return fgIsChangeOpMode |
| * TRUE: Can change/Don't need to change operation mode |
| * FALSE: Can't change operation mode |
| */ |
| /*----------------------------------------------------------------------------*/ |
| enum ENUM_OP_CHANGE_STATUS_T |
| rlmChangeOperationMode(struct ADAPTER *prAdapter, uint8_t ucBssIndex, |
| uint8_t ucChannelWidth, uint8_t ucNss, |
| PFN_OPMODE_NOTIFY_DONE_FUNC pfOpChangeHandler) |
| { |
| struct BSS_INFO *prBssInfo; |
| struct STA_RECORD *prStaRec = (struct STA_RECORD *)NULL; |
| u_int8_t fgIsChangeBw = TRUE, |
| fgIsChangeNss = TRUE; /* Indicate if need to change */ |
| uint8_t i; |
| |
| /* Sanity check */ |
| if (ucBssIndex >= prAdapter->ucHwBssIdNum) |
| return OP_CHANGE_STATUS_INVALID; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, ucBssIndex); |
| |
| if (!prBssInfo) |
| return OP_CHANGE_STATUS_INVALID; |
| |
| /* <1>Check if OP change parameter is valid */ |
| if (rlmCheckOpChangeParamValid(prAdapter, prBssInfo, ucChannelWidth, |
| ucNss) == FALSE) |
| return OP_CHANGE_STATUS_INVALID; |
| |
| /* <2>Check if OpMode notification is ongoing, if not, register the call |
| * back function |
| */ |
| if (prBssInfo->pfOpChangeHandler) { |
| DBGLOG(RLM, INFO, |
| "BSS[%d] OpMode change notification is ongoing\n", |
| ucBssIndex); |
| return OP_CHANGE_STATUS_INVALID; |
| } |
| |
| prBssInfo->pfOpChangeHandler = pfOpChangeHandler; |
| |
| /* <3>Check if the current operating BW/Nss is the same as the target |
| * one |
| */ |
| if (ucChannelWidth == rlmGetBssOpBwByVhtAndHtOpInfo(prBssInfo)) |
| fgIsChangeBw = FALSE; |
| |
| if (ucNss == prBssInfo->ucNss) |
| fgIsChangeNss = FALSE; |
| |
| if ((!fgIsChangeBw) && (!fgIsChangeNss)) { |
| if (prBssInfo->pfOpChangeHandler) { |
| /* (1) Don't need to call callback at no need to change |
| * OP mode case |
| * (2) Clear callback to NULL when handling OP Mode |
| * change request done |
| */ |
| prBssInfo->pfOpChangeHandler = NULL; |
| } |
| |
| DBGLOG(RLM, INFO, |
| "BSS[%d] target OpMode BW[%d] Nss[%d] is the same as cuurent\n", |
| ucBssIndex, ucChannelWidth, ucNss); |
| return OP_CHANGE_STATUS_VALID_NO_CHANGE; |
| } |
| |
| DBGLOG(RLM, INFO, |
| "Intend to change BSS[%d] OP Mode to BW[%d] Nss[%d]\n", |
| ucBssIndex, ucChannelWidth, ucNss); |
| |
| /* <4> Fill OP Change Info into BssInfo*/ |
| if (fgIsChangeBw) { |
| prBssInfo->ucOpChangeChannelWidth = ucChannelWidth; |
| prBssInfo->fgIsOpChangeChannelWidth = TRUE; |
| DBGLOG(RLM, INFO, "Intend to change BSS[%d] to BW[%d]\n", |
| ucBssIndex, ucChannelWidth); |
| } |
| if (fgIsChangeNss) { |
| prBssInfo->ucOpChangeNss = ucNss; |
| prBssInfo->fgIsOpChangeNss = TRUE; |
| DBGLOG(RLM, INFO, "Intend to change BSS[%d] to Nss[%d]\n", |
| ucBssIndex, ucNss); |
| } |
| |
| /* <5>Handling OP Info change for STA/GC */ |
| if ((prBssInfo->eCurrentOPMode == OP_MODE_INFRASTRUCTURE) && |
| (prBssInfo->prStaRecOfAP)) { |
| prStaRec = prBssInfo->prStaRecOfAP; |
| |
| /* <5.1>Initialize OP mode change parameters related to |
| * notification Tx done handler (STA mode) |
| */ |
| if (prBssInfo->pfOpChangeHandler) { |
| for (i = 0; i < OP_NOTIFY_TYPE_NUM; i++) { |
| prBssInfo->aucOpModeChangeState[i] = |
| OP_NOTIFY_STATE_KEEP; |
| prBssInfo->aucOpModeChangeRetryCnt[i] = 0; |
| } |
| } |
| |
| /* <5.2> Send operating mode notification frame (STA mode) */ |
| #if CFG_SUPPORT_802_11AC |
| if (RLM_NET_IS_11AC(prBssInfo)) { |
| if (prBssInfo->pfOpChangeHandler) |
| prBssInfo->aucOpModeChangeState |
| [OP_NOTIFY_TYPE_VHT_NSS_BW] = |
| OP_NOTIFY_STATE_SENDING; |
| |
| DBGLOG(RLM, INFO, |
| "Send VHT OP notification frame: BSS[%d] BW[%d] Nss[%d]\n", |
| ucBssIndex, ucChannelWidth, ucNss); |
| |
| rlmSendOpModeNotificationFrame(prAdapter, prStaRec, |
| ucChannelWidth, ucNss); |
| } else |
| #endif |
| { |
| if (RLM_NET_IS_11N(prBssInfo)) { |
| if (prBssInfo->pfOpChangeHandler) { |
| if (fgIsChangeNss) |
| prBssInfo->aucOpModeChangeState |
| [OP_NOTIFY_TYPE_HT_NSS] = |
| OP_NOTIFY_STATE_SENDING; |
| if (fgIsChangeBw) |
| prBssInfo->aucOpModeChangeState |
| [OP_NOTIFY_TYPE_HT_BW] = |
| OP_NOTIFY_STATE_SENDING; |
| } |
| |
| if (fgIsChangeNss) { |
| rlmSendSmPowerSaveFrame( |
| prAdapter, prStaRec, ucNss); |
| DBGLOG(RLM, INFO, |
| "Send HT SM Power Save frame: BSS[%d] Nss[%d]\n", |
| ucBssIndex, ucNss); |
| } |
| |
| if (fgIsChangeBw) { |
| rlmSendNotifyChannelWidthFrame( |
| prAdapter, prStaRec, |
| ucChannelWidth); |
| DBGLOG(RLM, INFO, |
| "Send HT Notify Channel Width frame: BSS[%d] BW[%d]\n", |
| ucBssIndex, ucChannelWidth); |
| } |
| } |
| } |
| |
| /* <5.3> Change OP Info w/o waiting for notification Tx done */ |
| if (prBssInfo->pfOpChangeHandler == NULL) { |
| rlmCompleteOpModeChange(prAdapter, prBssInfo, TRUE); |
| /* No callback */ |
| return OP_CHANGE_STATUS_VALID_CHANGE_CALLBACK_DONE; |
| } |
| } |
| /* <6>Handling OP Info change for AP/GO */ |
| else if (prBssInfo->eCurrentOPMode == OP_MODE_ACCESS_POINT) { |
| /* Complete OP Info change after notifying client by beacon */ |
| rlmCompleteOpModeChange(prAdapter, prBssInfo, TRUE); |
| return OP_CHANGE_STATUS_VALID_CHANGE_CALLBACK_DONE; |
| } |
| |
| return OP_CHANGE_STATUS_VALID_CHANGE_CALLBACK_WAIT; |
| } |
| |
| static u_int8_t rlmCheckOpChangeParamForClient(struct BSS_INFO *prBssInfo, |
| uint8_t ucChannelWidth, |
| uint8_t ucNss) |
| { |
| struct STA_RECORD *prStaRec; |
| |
| prStaRec = prBssInfo->prStaRecOfAP; |
| |
| if (!prStaRec) |
| return FALSE; |
| |
| #if CFG_SUPPORT_802_11AC |
| if (RLM_NET_IS_11AC(prBssInfo)) { /* VHT */ |
| /* Check peer OP Channel Width */ |
| switch (ucChannelWidth) { |
| case MAX_BW_80_80_MHZ: |
| if (prStaRec->ucVhtOpChannelWidth != |
| VHT_OP_CHANNEL_WIDTH_80P80) { |
| DBGLOG(RLM, INFO, |
| "Can't change BSS[%d] OP BW to:%d for peer VHT OP BW is:%d\n", |
| prBssInfo->ucBssIndex, ucChannelWidth, |
| prStaRec->ucVhtOpChannelWidth); |
| return FALSE; |
| } |
| break; |
| case MAX_BW_160MHZ: |
| if (prStaRec->ucVhtOpChannelWidth != |
| VHT_OP_CHANNEL_WIDTH_160) { |
| DBGLOG(RLM, INFO, |
| "Can't change BSS[%d] OP BW to:%d for peer VHT OP BW is:%d\n", |
| prBssInfo->ucBssIndex, ucChannelWidth, |
| prStaRec->ucVhtOpChannelWidth); |
| return FALSE; |
| } |
| break; |
| case MAX_BW_80MHZ: |
| if (prStaRec->ucVhtOpChannelWidth < |
| VHT_OP_CHANNEL_WIDTH_80) { |
| DBGLOG(RLM, INFO, |
| "Can't change BSS[%d] OP BW to:%d for peer VHT OP BW is:%d\n", |
| prBssInfo->ucBssIndex, ucChannelWidth, |
| prStaRec->ucVhtOpChannelWidth); |
| return FALSE; |
| } |
| break; |
| case MAX_BW_40MHZ: |
| if (!(prStaRec->ucHtPeerOpInfo1 & |
| HT_OP_INFO1_STA_CHNL_WIDTH) || |
| (!prBssInfo->fg40mBwAllowed)) { |
| DBGLOG(RLM, INFO, |
| "Can't change BSS[%d] OP BW to:%d for PeerOpBw:%d fg40mBwAllowed:%d\n", |
| prBssInfo->ucBssIndex, ucChannelWidth, |
| (uint8_t)(prStaRec->ucHtPeerOpInfo1 & |
| HT_OP_INFO1_STA_CHNL_WIDTH), |
| prBssInfo->fg40mBwAllowed); |
| return FALSE; |
| } |
| break; |
| case MAX_BW_20MHZ: |
| break; |
| default: |
| DBGLOG(RLM, WARN, |
| "BSS[%d] target OP BW:%d is invalid for VHT OpMode change\n", |
| prBssInfo->ucBssIndex, ucChannelWidth); |
| return FALSE; |
| } |
| |
| /* Check peer Rx Nss Cap */ |
| if (ucNss == 2 && |
| ((prStaRec->u2VhtRxMcsMap & VHT_CAP_INFO_MCS_2SS_MASK) >> |
| VHT_CAP_INFO_MCS_2SS_OFFSET) == |
| VHT_CAP_INFO_MCS_NOT_SUPPORTED) { |
| DBGLOG(RLM, INFO, |
| "Don't change Nss since VHT peer doesn't support 2ss\n"); |
| return FALSE; |
| } |
| |
| } else |
| #endif |
| { |
| if (RLM_NET_IS_11N(prBssInfo)) { /* HT */ |
| /* Check peer Channel Width */ |
| if (ucChannelWidth >= MAX_BW_80MHZ) { |
| DBGLOG(RLM, WARN, |
| "BSS[%d] target OP BW:%d is invalid for HT OpMode change\n", |
| prBssInfo->ucBssIndex, ucChannelWidth); |
| return FALSE; |
| } else if (ucChannelWidth == |
| MAX_BW_40MHZ) { |
| if (!(prStaRec->ucHtPeerOpInfo1 & |
| HT_OP_INFO1_STA_CHNL_WIDTH) || |
| (!prBssInfo->fg40mBwAllowed)) { |
| DBGLOG(RLM, INFO, |
| "Can't change BSS[%d] OP BW to:%d for PeerOpBw:%d fg40mBwAllowed:%d\n", |
| prBssInfo->ucBssIndex, |
| ucChannelWidth, |
| (uint8_t)( |
| prStaRec->ucHtPeerOpInfo1 & |
| HT_OP_INFO1_STA_CHNL_WIDTH), |
| prBssInfo->fg40mBwAllowed); |
| return FALSE; |
| } |
| } |
| |
| /* Check peer Rx Nss Cap */ |
| if (ucNss == 2 && (prStaRec->aucRxMcsBitmask[1] == 0)) { |
| DBGLOG(RLM, INFO, |
| "Don't change Nss since HT peer doesn't support 2ss\n"); |
| return FALSE; |
| } |
| } |
| } |
| return TRUE; |
| } |
| |
| static u_int8_t rlmCheckOpChangeParamValid(struct ADAPTER *prAdapter, |
| struct BSS_INFO *prBssInfo, |
| uint8_t ucChannelWidth, |
| uint8_t ucNss) |
| { |
| |
| ASSERT(prBssInfo); |
| |
| /* <1>Check if BSS PHY type is legacy mode */ |
| if (!RLM_NET_IS_11N(prBssInfo)) { |
| DBGLOG(RLM, WARN, |
| "Can't change BSS[%d] OP info for legacy BSS\n", |
| prBssInfo->ucBssIndex); |
| return FALSE; |
| } |
| |
| /* <2>Check network type */ |
| if ((prBssInfo->eCurrentOPMode != OP_MODE_INFRASTRUCTURE) && |
| (prBssInfo->eCurrentOPMode != OP_MODE_ACCESS_POINT)) { |
| DBGLOG(RLM, WARN, |
| "Can't change BSS[%d] OP info for OpMode:%d\n", |
| prBssInfo->ucBssIndex, prBssInfo->eCurrentOPMode); |
| return FALSE; |
| } |
| |
| /* <3>Check if target OP BW/Nss <= Own Cap BW/Nss */ |
| if (ucNss > wlanGetSupportNss(prAdapter, prBssInfo->ucBssIndex)) { |
| DBGLOG(RLM, WARN, |
| "Can't change BSS[%d] OP Nss to:%d since own Cap Nss is:%d\n", |
| prBssInfo->ucBssIndex, ucNss, |
| wlanGetSupportNss(prAdapter, prBssInfo->ucBssIndex)); |
| return FALSE; |
| } |
| |
| if (ucChannelWidth > cnmGetBssMaxBw(prAdapter, prBssInfo->ucBssIndex)) { |
| DBGLOG(RLM, WARN, |
| "Can't change BSS[%d] OP BW to:%d since own Cap BW is:%d\n", |
| prBssInfo->ucBssIndex, ucChannelWidth, |
| cnmGetBssMaxBw(prAdapter, prBssInfo->ucBssIndex)); |
| return FALSE; |
| } |
| |
| /* <4>Check if target OP BW is valid for band and primary channel of |
| * current BSS |
| */ |
| if (prBssInfo->eBand == BAND_2G4) { |
| if ((ucChannelWidth != MAX_BW_20MHZ) && |
| (ucChannelWidth != MAX_BW_40MHZ)) { |
| DBGLOG(RLM, WARN, |
| "Can't change BSS[%d] OP BW to:%d for 2.4G\n", |
| prBssInfo->ucBssIndex, ucChannelWidth); |
| return FALSE; |
| } |
| } else { |
| if (prBssInfo->ucPrimaryChannel == |
| 165) { /*It can only use BW20 for CH165*/ |
| DBGLOG(RLM, WARN, |
| "Can't change BSS[%d] OP BW for CH165\n", |
| prBssInfo->ucBssIndex); |
| return FALSE; |
| } |
| |
| if ((ucChannelWidth == MAX_BW_160MHZ) && |
| ((prBssInfo->ucPrimaryChannel < 36) || |
| ((prBssInfo->ucPrimaryChannel > 64) && |
| (prBssInfo->ucPrimaryChannel < 100)) || |
| (prBssInfo->ucPrimaryChannel > 128))) { |
| DBGLOG(RLM, WARN, |
| "Can't change BSS[%d] to OP BW160 for primary CH%d\n", |
| prBssInfo->ucBssIndex, |
| prBssInfo->ucPrimaryChannel); |
| return FALSE; |
| } |
| } |
| |
| /* <5>Check if target OP BW/Nss <= peer's BW/Nss (STA mode) */ |
| if (prBssInfo->eCurrentOPMode == OP_MODE_INFRASTRUCTURE) { |
| if (rlmCheckOpChangeParamForClient(prBssInfo, ucChannelWidth, |
| ucNss) == FALSE) |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| void rlmDummyChangeOpHandler(struct ADAPTER *prAdapter, uint8_t ucBssIndex, |
| u_int8_t fgIsChangeSuccess) |
| { |
| DBGLOG(RLM, INFO, "OP change done for BSS[%d] IsSuccess[%d]\n", |
| ucBssIndex, fgIsChangeSuccess); |
| } |
| |
| /* 11K */ |
| void rlmProcessNeighborReportResonse(struct ADAPTER *prAdapter, |
| struct WLAN_ACTION_FRAME *prAction, |
| uint16_t u2PacketLen) |
| { |
| struct ACTION_NEIGHBOR_REPORT_FRAME *prNeighborResponse = |
| (struct ACTION_NEIGHBOR_REPORT_FRAME *)prAction; |
| |
| ASSERT(prAdapter); |
| ASSERT(prNeighborResponse); |
| DBGLOG(RLM, INFO, "Neighbor Resp From " MACSTR ", DialogToken %d\n", |
| MAC2STR(prNeighborResponse->aucSrcAddr), |
| prNeighborResponse->ucDialogToken); |
| #if CFG_SUPPORT_802_11K |
| aisCollectNeighborAP( |
| prAdapter, &prNeighborResponse->aucInfoElem[0], |
| u2PacketLen - OFFSET_OF(struct ACTION_NEIGHBOR_REPORT_FRAME, |
| aucInfoElem), |
| 0); |
| #endif |
| } |
| |
| void rlmTxNeighborReportRequest(struct ADAPTER *prAdapter, |
| struct STA_RECORD *prStaRec, |
| struct SUB_ELEMENT_LIST *prSubIEs) |
| { |
| static uint8_t ucDialogToken = 1; |
| struct MSDU_INFO *prMsduInfo = NULL; |
| struct BSS_INFO *prBssInfo = NULL; |
| uint8_t *pucPayload = NULL; |
| struct ACTION_NEIGHBOR_REPORT_FRAME *prTxFrame = NULL; |
| uint16_t u2TxFrameLen = 500; |
| uint16_t u2FrameLen = 0; |
| |
| if (!prStaRec) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| ASSERT(prBssInfo); |
| /* 1 Allocate MSDU Info */ |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc( |
| prAdapter, MAC_TX_RESERVED_FIELD + u2TxFrameLen); |
| if (!prMsduInfo) |
| return; |
| prTxFrame = (struct ACTION_NEIGHBOR_REPORT_FRAME |
| *)((unsigned long)(prMsduInfo->prPacket) + |
| MAC_TX_RESERVED_FIELD); |
| |
| /* 2 Compose The Mac Header. */ |
| prTxFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| COPY_MAC_ADDR(prTxFrame->aucDestAddr, prStaRec->aucMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucSrcAddr, prBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucBSSID, prBssInfo->aucBSSID); |
| prTxFrame->ucCategory = CATEGORY_RM_ACTION; |
| prTxFrame->ucAction = RM_ACTION_NEIGHBOR_REQUEST; |
| u2FrameLen = |
| OFFSET_OF(struct ACTION_NEIGHBOR_REPORT_FRAME, aucInfoElem); |
| /* 3 Compose the frame body's frame. */ |
| prTxFrame->ucDialogToken = ucDialogToken++; |
| u2TxFrameLen -= sizeof(*prTxFrame) - 1; |
| pucPayload = &prTxFrame->aucInfoElem[0]; |
| while (prSubIEs && u2TxFrameLen >= (prSubIEs->rSubIE.ucLength + 2)) { |
| kalMemCopy(pucPayload, &prSubIEs->rSubIE, |
| prSubIEs->rSubIE.ucLength + 2); |
| pucPayload += prSubIEs->rSubIE.ucLength + 2; |
| u2FrameLen += prSubIEs->rSubIE.ucLength + 2; |
| prSubIEs = prSubIEs->prNext; |
| } |
| nicTxSetMngPacket(prAdapter, prMsduInfo, prStaRec->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| u2FrameLen, NULL, MSDU_RATE_MODE_AUTO); |
| |
| /* 5 Enqueue the frame to send this action frame. */ |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| } |
| |
| void rlmComposeEmptyBeaconReport(struct ADAPTER *prAdapter) |
| { |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq = |
| &prAdapter->rWifiVar.rRmReqParams; |
| struct RADIO_MEASUREMENT_REPORT_PARAMS *prRmRep = |
| &prAdapter->rWifiVar.rRmRepParams; |
| uint8_t *pucReportFrame = |
| prRmRep->pucReportFrameBuff + prRmRep->u2ReportFrameLen; |
| struct IE_MEASUREMENT_REPORT *prRepIE = |
| (struct IE_MEASUREMENT_REPORT *)pucReportFrame; |
| struct RM_BCN_REPORT *prBcnReport = |
| (struct RM_BCN_REPORT *)prRepIE->aucReportFields; |
| |
| /* fill in basic content of Measurement report IE */ |
| prRepIE->ucId = ELEM_ID_MEASUREMENT_REPORT; |
| prRepIE->ucToken = prRmReq->prCurrMeasElem->ucToken; |
| prRepIE->ucMeasurementType = prRmReq->prCurrMeasElem->ucMeasurementType; |
| prRepIE->ucReportMode = 0; |
| prRepIE->ucLength = 3 + OFFSET_OF(struct RM_BCN_REPORT, aucOptElem); |
| kalMemZero(prBcnReport, OFFSET_OF(struct RM_BCN_REPORT, aucOptElem)); |
| prBcnReport->ucRegulatoryClass = |
| 255; /* 255 means reglatory is not available */ |
| prBcnReport->ucChannel = 255; /* 255 means channel is not available */ |
| prBcnReport->ucReportInfo = |
| 255; /* 255 means report frame info is not available */ |
| prBcnReport->ucRSNI = 255; /* 255 means RSNI is not available */ |
| prBcnReport->ucAntennaID = 1; |
| |
| prRmRep->u2ReportFrameLen += IE_SIZE(&prRepIE); |
| } |
| |
| void rlmFreeMeasurementResources(struct ADAPTER *prAdapter) |
| { |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq = |
| &prAdapter->rWifiVar.rRmReqParams; |
| struct RADIO_MEASUREMENT_REPORT_PARAMS *prRmRep = |
| &prAdapter->rWifiVar.rRmRepParams; |
| struct RM_MEASURE_REPORT_ENTRY *prReportEntry = NULL; |
| struct LINK *prReportLink = &prRmRep->rReportLink; |
| struct LINK *prFreeReportLink = &prRmRep->rFreeReportLink; |
| u_int8_t fgHasBcnReqTimer = timerPendingTimer(&rBeaconReqTimer); |
| |
| DBGLOG(RLM, TRACE, "RRM: Free measurement, Beacon Req timer is %d\n", |
| fgHasBcnReqTimer); |
| if (fgHasBcnReqTimer) |
| cnmTimerStopTimer(prAdapter, &rBeaconReqTimer); |
| |
| kalMemFree(prRmReq->pucReqIeBuf, VIR_MEM_TYPE, prRmReq->u2ReqIeBufLen); |
| kalMemFree(prRmRep->pucReportFrameBuff, VIR_MEM_TYPE, |
| RM_REPORT_FRAME_MAX_LENGTH); |
| while (!LINK_IS_EMPTY(prReportLink)) { |
| LINK_REMOVE_HEAD(prReportLink, prReportEntry, |
| struct RM_MEASURE_REPORT_ENTRY *); |
| kalMemFree(prReportEntry, VIR_MEM_TYPE, sizeof(*prReportEntry)); |
| } |
| while (!LINK_IS_EMPTY(prFreeReportLink)) { |
| LINK_REMOVE_HEAD(prFreeReportLink, prReportEntry, |
| struct RM_MEASURE_REPORT_ENTRY *); |
| kalMemFree(prReportEntry, VIR_MEM_TYPE, sizeof(*prReportEntry)); |
| } |
| kalMemZero(prRmReq, sizeof(*prRmReq)); |
| kalMemZero(prRmRep, sizeof(*prRmRep)); |
| prRmReq->rBcnRmParam.eState = RM_NO_REQUEST; |
| prRmReq->fgRmIsOngoing = FALSE; |
| LINK_INITIALIZE(&prRmRep->rFreeReportLink); |
| LINK_INITIALIZE(&prRmRep->rReportLink); |
| } |
| |
| /* purpose: check if Radio Measurement is done */ |
| static u_int8_t |
| rlmAllMeasurementIssued(struct RADIO_MEASUREMENT_REQ_PARAMS *prReq) |
| { |
| return prReq->u2RemainReqLen > IE_SIZE(prReq->prCurrMeasElem) ? FALSE |
| : TRUE; |
| } |
| |
| void rlmComposeIncapableRmRep(struct RADIO_MEASUREMENT_REPORT_PARAMS *prRep, |
| uint8_t ucToken, uint8_t ucMeasType) |
| { |
| struct IE_MEASUREMENT_REPORT *prRepIE = |
| (struct IE_MEASUREMENT_REPORT *)(prRep->pucReportFrameBuff + |
| prRep->u2ReportFrameLen); |
| |
| prRepIE->ucId = ELEM_ID_MEASUREMENT_REPORT; |
| prRepIE->ucToken = ucToken; |
| prRepIE->ucMeasurementType = ucMeasType; |
| prRepIE->ucLength = 3; |
| prRepIE->ucReportMode = RM_REP_MODE_INCAPABLE; |
| prRep->u2ReportFrameLen += 5; |
| } |
| /* Purpose: Interative processing Measurement Request Element. If it is not the |
| ** first element, |
| ** will copy all collected report element to the report frame buffer. and |
| ** may tx the radio report frame. |
| ** prAdapter: pointer to the Adapter |
| ** fgNewStarted: if it is the first element in measurement request frame |
| */ |
| void rlmStartNextMeasurement(struct ADAPTER *prAdapter, u_int8_t fgNewStarted) |
| { |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq = |
| &prAdapter->rWifiVar.rRmReqParams; |
| struct RADIO_MEASUREMENT_REPORT_PARAMS *prRmRep = |
| &prAdapter->rWifiVar.rRmRepParams; |
| struct IE_MEASUREMENT_REQ *prCurrReq = prRmReq->prCurrMeasElem; |
| uint16_t u2RandomTime = 0; |
| |
| schedule_next: |
| if (!prRmReq->fgRmIsOngoing) { |
| DBGLOG(RLM, INFO, "RRM: Rm has been stopped\n"); |
| return; |
| } |
| /* we don't support parallel measurement now */ |
| if (prCurrReq->ucRequestMode & RM_REQ_MODE_PARALLEL_BIT) { |
| DBGLOG(RLM, WARN, |
| "RRM: Parallel request, compose incapable report\n"); |
| if (prRmRep->u2ReportFrameLen + 5 > RM_REPORT_FRAME_MAX_LENGTH) |
| rlmTxRadioMeasurementReport(prAdapter); |
| rlmComposeIncapableRmRep(prRmRep, prCurrReq->ucToken, |
| prCurrReq->ucMeasurementType); |
| if (rlmAllMeasurementIssued(prRmReq)) { |
| if (prRmReq->rBcnRmParam.fgExistBcnReq && |
| RM_EXIST_REPORT(prRmRep)) |
| rlmComposeEmptyBeaconReport(prAdapter); |
| rlmTxRadioMeasurementReport(prAdapter); |
| |
| /* repeat measurement if repetitions is required and not |
| * only parallel measurements. |
| * otherwise, no need to repeat, it is not make sense to |
| * do that. |
| */ |
| if (prRmReq->u2Repetitions > 0) { |
| prRmReq->fgInitialLoop = FALSE; |
| prRmReq->u2Repetitions--; |
| prCurrReq = prRmReq->prCurrMeasElem = |
| (struct IE_MEASUREMENT_REQ *) |
| prRmReq->pucReqIeBuf; |
| prRmReq->u2RemainReqLen = |
| prRmReq->u2ReqIeBufLen; |
| } else { |
| rlmFreeMeasurementResources(prAdapter); |
| DBGLOG(RLM, INFO, |
| "RRM: Radio Measurement done\n"); |
| return; |
| } |
| } else { |
| uint16_t u2IeSize = IE_SIZE(prRmReq->prCurrMeasElem); |
| |
| prCurrReq = prRmReq->prCurrMeasElem = |
| (struct IE_MEASUREMENT_REQ |
| *)((uint8_t *)prRmReq->prCurrMeasElem + |
| u2IeSize); |
| prRmReq->u2RemainReqLen -= u2IeSize; |
| } |
| fgNewStarted = FALSE; |
| goto schedule_next; |
| } |
| /* copy collected measurement report for specific measurement type */ |
| if (!fgNewStarted) { |
| struct RM_MEASURE_REPORT_ENTRY *prReportEntry = NULL; |
| struct LINK *prReportLink = &prRmRep->rReportLink; |
| struct LINK *prFreeReportLink = &prRmRep->rFreeReportLink; |
| uint8_t *pucReportFrame = |
| prRmRep->pucReportFrameBuff + prRmRep->u2ReportFrameLen; |
| uint16_t u2IeSize = 0; |
| u_int8_t fgNewLoop = FALSE; |
| |
| DBGLOG(RLM, INFO, |
| "RRM: total %u report element for current request\n", |
| prReportLink->u4NumElem); |
| /* copy collected report into the Measurement Report Frame |
| ** Buffer. |
| */ |
| while (1) { |
| LINK_REMOVE_HEAD(prReportLink, prReportEntry, |
| struct RM_MEASURE_REPORT_ENTRY *); |
| if (!prReportEntry) |
| break; |
| u2IeSize = IE_SIZE(prReportEntry->aucMeasReport); |
| /* if reach the max length of a MMPDU size, send a Rm |
| ** report first |
| */ |
| if (u2IeSize + prRmRep->u2ReportFrameLen > |
| RM_REPORT_FRAME_MAX_LENGTH) { |
| rlmTxRadioMeasurementReport(prAdapter); |
| pucReportFrame = prRmRep->pucReportFrameBuff + |
| prRmRep->u2ReportFrameLen; |
| } |
| kalMemCopy(pucReportFrame, prReportEntry->aucMeasReport, |
| u2IeSize); |
| pucReportFrame += u2IeSize; |
| prRmRep->u2ReportFrameLen += u2IeSize; |
| LINK_INSERT_TAIL(prFreeReportLink, |
| &prReportEntry->rLinkEntry); |
| } |
| /* if Measurement is done, free report element memory */ |
| if (rlmAllMeasurementIssued(prRmReq)) { |
| if (prRmReq->rBcnRmParam.fgExistBcnReq && |
| RM_EXIST_REPORT(prRmRep)) |
| rlmComposeEmptyBeaconReport(prAdapter); |
| rlmTxRadioMeasurementReport(prAdapter); |
| |
| /* repeat measurement if repetitions is required */ |
| if (prRmReq->u2Repetitions > 0) { |
| fgNewLoop = TRUE; |
| prRmReq->fgInitialLoop = FALSE; |
| prRmReq->u2Repetitions--; |
| prRmReq->prCurrMeasElem = |
| (struct IE_MEASUREMENT_REQ *) |
| prRmReq->pucReqIeBuf; |
| prRmReq->u2RemainReqLen = |
| prRmReq->u2ReqIeBufLen; |
| } else { |
| /* don't free radio measurement resource due to |
| ** TSM is running |
| */ |
| if (!wmmTsmIsOngoing(prAdapter)) { |
| rlmFreeMeasurementResources(prAdapter); |
| DBGLOG(RLM, INFO, |
| "RRM: Radio Measurement done\n"); |
| } |
| return; |
| } |
| } |
| if (!fgNewLoop) { |
| u2IeSize = IE_SIZE(prRmReq->prCurrMeasElem); |
| prCurrReq = prRmReq->prCurrMeasElem = |
| (struct IE_MEASUREMENT_REQ |
| *)((uint8_t *)prRmReq->prCurrMeasElem + |
| u2IeSize); |
| prRmReq->u2RemainReqLen -= u2IeSize; |
| } |
| } |
| |
| /* do specific measurement */ |
| switch (prCurrReq->ucMeasurementType) { |
| case ELEM_RM_TYPE_BEACON_REQ: { |
| struct RM_BCN_REQ *prBeaconReq = |
| (struct RM_BCN_REQ *)&prCurrReq->aucRequestFields[0]; |
| |
| if (!prRmReq->fgInitialLoop) { |
| /* If this is the repeating measurement, then wait next |
| ** scan done |
| */ |
| prRmReq->rBcnRmParam.eState = RM_WAITING; |
| break; |
| } |
| if (prBeaconReq->u2RandomInterval == 0) |
| rlmDoBeaconMeasurement(prAdapter, 0); |
| else { |
| get_random_bytes(&u2RandomTime, 2); |
| u2RandomTime = |
| (u2RandomTime * prBeaconReq->u2RandomInterval) / |
| 65535; |
| u2RandomTime = TU_TO_MSEC(u2RandomTime); |
| if (u2RandomTime > 0) { |
| cnmTimerStopTimer(prAdapter, &rBeaconReqTimer); |
| cnmTimerInitTimer(prAdapter, &rBeaconReqTimer, |
| rlmDoBeaconMeasurement, 0); |
| cnmTimerStartTimer(prAdapter, &rBeaconReqTimer, |
| u2RandomTime); |
| } else |
| rlmDoBeaconMeasurement(prAdapter, 0); |
| } |
| break; |
| } |
| #if 0 |
| case ELEM_RM_TYPE_TSM_REQ: |
| { |
| struct RM_TS_MEASURE_REQ *prTsmReqIE = |
| (struct RM_TS_MEASURE_REQ *) |
| &prCurrReq->aucRequestFields[0]; |
| struct RM_TSM_REQ *prTsmReq = NULL; |
| uint16_t u2OffSet = 0; |
| uint8_t *pucIE = prTsmReqIE->aucSubElements; |
| struct ACTION_RM_REPORT_FRAME *prReportFrame = NULL; |
| |
| /* In case of repeating measurement, no need to start |
| ** triggered measurement again. According to current |
| ** specification of Radio Measurement, only TSM has the |
| ** triggered type of measurement. |
| */ |
| if ((prCurrReq->ucRequestMode & RM_REQ_MODE_ENABLE_BIT) && |
| !prRmReq->fgInitialLoop) |
| goto schedule_next; |
| |
| /* if enable bit is 1 and report bit is 0, need to stop all |
| ** triggered TSM measurement |
| */ |
| if ((prCurrReq->ucRequestMode & |
| (RM_REQ_MODE_ENABLE_BIT|RM_REQ_MODE_REPORT_BIT)) == |
| RM_REQ_MODE_ENABLE_BIT) { |
| wmmRemoveAllTsmMeasurement(prAdapter, TRUE); |
| break; |
| } |
| prTsmReq = cnmMemAlloc(prAdapter, RAM_TYPE_BUF, |
| sizeof(struct RM_TSM_REQ)); |
| if (!prTsmReq) { |
| DBGLOG(RLM, ERROR, "No memory\n"); |
| break; |
| } |
| prTsmReq->ucToken = prCurrReq->ucToken; |
| prTsmReq->u2Duration = prTsmReqIE->u2Duration; |
| prTsmReq->ucTID = (prTsmReqIE->ucTrafficID & 0xf0) >> 4; |
| prTsmReq->ucB0Range = prTsmReqIE->ucBin0Range; |
| prReportFrame = (struct ACTION_RM_REPORT_FRAME *) |
| prRmRep->pucReportFrameBuff; |
| COPY_MAC_ADDR(prTsmReq->aucPeerAddr, |
| prReportFrame->aucDestAddr); |
| IE_FOR_EACH(pucIE, prCurrReq->ucLength - 3, u2OffSet) { |
| switch (IE_ID(pucIE)) { |
| case 1: /* Triggered Reporting */ |
| kalMemCopy(&prTsmReq->rTriggerCond, |
| pucIE+2, IE_LEN(pucIE)); |
| break; |
| case 221: /* Vendor Specified */ |
| break; /* No vendor IE now */ |
| default: |
| break; |
| } |
| } |
| if (!prTsmReqIE->u2RandomInterval) { |
| wmmStartTsmMeasurement(prAdapter, |
| (unsigned long)prTsmReq); |
| break; |
| } |
| get_random_bytes(&u2RandomTime, 2); |
| u2RandomTime = |
| (u2RandomTime * prTsmReqIE->u2RandomInterval) / 65535; |
| u2RandomTime = TU_TO_MSEC(u2RandomTime); |
| cnmTimerStopTimer(prAdapter, &rTSMReqTimer); |
| cnmTimerInitTimer(prAdapter, &rTSMReqTimer, |
| wmmStartTsmMeasurement, (unsigned long)prTsmReq); |
| cnmTimerStartTimer(prAdapter, &rTSMReqTimer, u2RandomTime); |
| break; |
| } |
| #endif |
| default: { |
| if (prRmRep->u2ReportFrameLen + 5 > RM_REPORT_FRAME_MAX_LENGTH) |
| rlmTxRadioMeasurementReport(prAdapter); |
| rlmComposeIncapableRmRep(prRmRep, prCurrReq->ucToken, |
| prCurrReq->ucMeasurementType); |
| fgNewStarted = FALSE; |
| DBGLOG(RLM, INFO, |
| "RRM: RM type %d is not supported on this chip\n", |
| prCurrReq->ucMeasurementType); |
| goto schedule_next; |
| } |
| } |
| } |
| |
| u_int8_t rlmBcnRmRunning(struct ADAPTER *prAdapter) |
| { |
| return prAdapter->rWifiVar.rRmReqParams.rBcnRmParam.eState == |
| RM_ON_GOING; |
| } |
| |
| u_int8_t rlmFillScanMsg(struct ADAPTER *prAdapter, |
| struct MSG_SCN_SCAN_REQ_V2 *prMsg) |
| { |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq = |
| &prAdapter->rWifiVar.rRmReqParams; |
| struct IE_MEASUREMENT_REQ *prCurrReq = NULL; |
| struct RM_BCN_REQ *prBeaconReq = NULL; |
| uint16_t u2RemainLen = 0; |
| uint8_t *pucSubIE = NULL; |
| |
| static struct PARAM_SSID rBcnReqSsid; |
| |
| if (prRmReq->rBcnRmParam.eState != RM_ON_GOING || !prMsg) |
| return FALSE; |
| |
| prCurrReq = prRmReq->prCurrMeasElem; |
| prBeaconReq = (struct RM_BCN_REQ *)&prCurrReq->aucRequestFields[0]; |
| prMsg->ucSSIDType = SCAN_REQ_SSID_WILDCARD; |
| switch (prBeaconReq->ucMeasurementMode) { |
| case RM_BCN_REQ_PASSIVE_MODE: |
| prMsg->eScanType = SCAN_TYPE_PASSIVE_SCAN; |
| break; |
| case RM_BCN_REQ_ACTIVE_MODE: |
| prMsg->eScanType = SCAN_TYPE_ACTIVE_SCAN; |
| break; |
| default: |
| DBGLOG(RLM, WARN, |
| "BCN REQ: Unexpect measure mode %d, use active mode as default\n", |
| prBeaconReq->ucMeasurementMode); |
| prMsg->eScanType = SCAN_TYPE_ACTIVE_SCAN; |
| break; |
| } |
| |
| WLAN_GET_FIELD_16(&prBeaconReq->u2Duration, &prMsg->u2ChannelDwellTime); |
| |
| COPY_MAC_ADDR(prMsg->aucBSSID, prBeaconReq->aucBssid); |
| |
| prMsg->u2ProbeDelay = 0; |
| prMsg->u2TimeoutValue = 0; |
| prMsg->ucSSIDNum = 0; |
| prMsg->u2IELen = 0; |
| /* if mandatory bit is set, we should do */ |
| if (prCurrReq->ucRequestMode & RM_REQ_MODE_DURATION_MANDATORY_BIT) |
| prMsg->u2ChannelMinDwellTime = prMsg->u2ChannelDwellTime; |
| else |
| prMsg->u2ChannelMinDwellTime = |
| (prMsg->u2ChannelDwellTime * 2) / 3; |
| if (prBeaconReq->ucChannel == 0) |
| prMsg->eScanChannel = SCAN_CHANNEL_FULL; |
| else if (prBeaconReq->ucChannel == 255) { /* latest Ap Channel Report */ |
| struct BSS_DESC *prBssDesc = |
| prAdapter->rWifiVar.rAisFsmInfo.prTargetBssDesc; |
| uint8_t *pucChnl = NULL; |
| uint8_t ucChnlNum = 0; |
| uint8_t ucIndex = 0; |
| struct RF_CHANNEL_INFO *prChnlInfo = prMsg->arChnlInfoList; |
| |
| prMsg->eScanChannel = SCAN_CHANNEL_SPECIFIED; |
| prMsg->ucChannelListNum = 0; |
| if (prBssDesc) { |
| uint8_t *pucIE = NULL; |
| uint16_t u2IELength = 0; |
| uint16_t u2Offset = 0; |
| |
| pucIE = prBssDesc->aucIEBuf; |
| u2IELength = prBssDesc->u2IELength; |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| if (IE_ID(pucIE) != ELEM_ID_AP_CHANNEL_REPORT) |
| continue; |
| pucChnl = ((struct IE_AP_CHNL_REPORT *)pucIE) |
| ->aucChnlList; |
| ucChnlNum = pucIE[1] - 1; |
| DBGLOG(RLM, INFO, |
| "BCN REQ: Channel number in latest AP channel report %d\n", |
| ucChnlNum); |
| while (ucIndex < ucChnlNum && |
| prMsg->ucChannelListNum < |
| MAXIMUM_OPERATION_CHANNEL_LIST) { |
| if (pucChnl[ucIndex] <= 14) |
| prChnlInfo |
| [prMsg->ucChannelListNum] |
| .eBand = |
| BAND_2G4; |
| else |
| prChnlInfo |
| [prMsg->ucChannelListNum] |
| .eBand = |
| BAND_5G; |
| prChnlInfo[prMsg->ucChannelListNum] |
| .ucChannelNum = |
| pucChnl[ucIndex]; |
| prMsg->ucChannelListNum++; |
| ucIndex++; |
| } |
| } |
| } |
| } else { |
| prMsg->eScanChannel = SCAN_CHANNEL_SPECIFIED; |
| prMsg->ucChannelListNum = 1; |
| prMsg->arChnlInfoList[0].ucChannelNum = prBeaconReq->ucChannel; |
| if (prBeaconReq->ucChannel <= 14) |
| prMsg->arChnlInfoList[0].eBand = BAND_2G4; |
| else |
| prMsg->arChnlInfoList[0].eBand = BAND_5G; |
| } |
| u2RemainLen = prCurrReq->ucLength - 3 - |
| OFFSET_OF(struct RM_BCN_REQ, aucSubElements); |
| pucSubIE = &prBeaconReq->aucSubElements[0]; |
| while (u2RemainLen > 0) { |
| if (IE_SIZE(pucSubIE) > u2RemainLen) |
| break; |
| switch (pucSubIE[0]) { |
| case 0: /* SSID */ |
| /* length of sub-element ssid is 0 or first byte is 0, |
| ** means wildcard ssid matching |
| */ |
| if (!IE_LEN(pucSubIE) || !pucSubIE[2]) |
| break; |
| prMsg->ucSSIDNum = 1; |
| prMsg->prSsid = &rBcnReqSsid; |
| COPY_SSID(&rBcnReqSsid.aucSsid[0], |
| rBcnReqSsid.u4SsidLen, &pucSubIE[2], |
| pucSubIE[1]); |
| prMsg->ucSSIDType = SCAN_REQ_SSID_SPECIFIED_ONLY; |
| break; |
| case 51: /* AP channel report */ |
| { |
| struct IE_AP_CHNL_REPORT *prApChnl = |
| (struct IE_AP_CHNL_REPORT *)pucSubIE; |
| uint8_t ucChannelCnt = prApChnl->ucLength - 1; |
| uint8_t ucIndex = 0; |
| |
| if (prBeaconReq->ucChannel == 0) |
| break; |
| prMsg->eScanChannel = SCAN_CHANNEL_SPECIFIED; |
| DBGLOG(RLM, INFO, |
| "BCN REQ: Channel number in measurement AP channel report %d\n", |
| ucChannelCnt); |
| while (ucIndex < ucChannelCnt && |
| prMsg->ucChannelListNum < |
| MAXIMUM_OPERATION_CHANNEL_LIST) { |
| if (prApChnl->aucChnlList[ucIndex] <= 14) |
| prMsg->arChnlInfoList |
| [prMsg->ucChannelListNum] |
| .eBand = BAND_2G4; |
| else |
| prMsg->arChnlInfoList |
| [prMsg->ucChannelListNum] |
| .eBand = BAND_5G; |
| prMsg->arChnlInfoList[prMsg->ucChannelListNum] |
| .ucChannelNum = |
| prApChnl->aucChnlList[ucIndex]; |
| prMsg->ucChannelListNum++; |
| ucIndex++; |
| } |
| break; |
| } |
| } |
| u2RemainLen -= IE_SIZE(pucSubIE); |
| pucSubIE += IE_SIZE(pucSubIE); |
| } |
| DBGLOG(RLM, INFO, |
| "BCN REQ: SSIDtype %d, ScanType %d, Dwell %d, MinDwell %d, ChnlType %d, ChnlNum %d\n", |
| prMsg->ucSSIDType, prMsg->eScanType, prMsg->u2ChannelDwellTime, |
| prMsg->u2ChannelMinDwellTime, prMsg->eScanChannel, |
| prMsg->ucChannelListNum); |
| return TRUE; |
| } |
| |
| void rlmDoBeaconMeasurement(struct ADAPTER *prAdapter, unsigned long ulParam) |
| { |
| struct CONNECTION_SETTINGS *prConnSettings = |
| &(prAdapter->rWifiVar.rConnSettings); |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq = |
| &prAdapter->rWifiVar.rRmReqParams; |
| struct RM_BCN_REQ *prBcnReq = |
| (struct RM_BCN_REQ *)&prRmReq->prCurrMeasElem |
| ->aucRequestFields[0]; |
| |
| if (prBcnReq->ucMeasurementMode == RM_BCN_REQ_TABLE_MODE) { |
| struct LINK *prBSSDescList = |
| &prAdapter->rWifiVar.rScanInfo.rBSSDescList; |
| struct BSS_DESC *prBssDesc = NULL; |
| struct RM_BEACON_REPORT_PARAMS rRepParams; |
| uint16_t *pu2BcnInterval = |
| (uint16_t *)&rRepParams.aucBcnFixedField[8]; |
| uint16_t *pu2CapInfo = |
| (uint16_t *)&rRepParams.aucBcnFixedField[10]; |
| |
| kalMemZero(&rRepParams, sizeof(rRepParams)); |
| /* if this is a one antenna only device, the antenna id is |
| ** always 1. 7.3.2.40 |
| */ |
| rRepParams.ucAntennaID = 1; |
| rRepParams.ucRSNI = |
| 255; /* 255 means RSNI not available. see 7.3.2.41 */ |
| rRepParams.ucFrameInfo = 255; |
| |
| prRmReq->rBcnRmParam.eState = RM_ON_GOING; |
| prBcnReq->ucChannel = 0; |
| DBGLOG(RLM, INFO, |
| "BCN REQ: Beacon Table Mode, Beacon Table Num %u\n", |
| prBSSDescList->u4NumElem); |
| LINK_FOR_EACH_ENTRY(prBssDesc, prBSSDescList, rLinkEntry, |
| struct BSS_DESC) |
| { |
| rRepParams.ucRCPI = prBssDesc->ucRCPI; |
| rRepParams.ucChannel = prBssDesc->ucChannelNum; |
| kalMemCopy(&rRepParams.aucBcnFixedField, |
| &prBssDesc->u8TimeStamp, 8); |
| *pu2BcnInterval = prBssDesc->u2BeaconInterval; |
| *pu2CapInfo = prBssDesc->u2CapInfo; |
| rlmCollectBeaconReport(prAdapter, prBssDesc->aucIEBuf, |
| prBssDesc->u2IELength, |
| prBssDesc->aucBSSID, |
| &rRepParams); |
| } |
| rlmStartNextMeasurement(prAdapter, FALSE); |
| return; |
| } |
| if (prConnSettings->fgIsScanReqIssued) { |
| prRmReq->rBcnRmParam.eState = RM_WAITING; |
| } else { |
| prRmReq->rBcnRmParam.eState = RM_ON_GOING; |
| GET_CURRENT_SYSTIME(&prRmReq->rStartTime); |
| aisFsmScanRequest(prAdapter, NULL, NULL, 0); |
| } |
| } |
| |
| /* |
| */ |
| static u_int8_t rlmRmFrameIsValid(struct SW_RFB *prSwRfb) |
| { |
| uint16_t u2ElemLen = 0; |
| uint16_t u2Offset = |
| (uint16_t)OFFSET_OF(struct ACTION_RM_REQ_FRAME, aucInfoElem); |
| uint8_t *pucIE = (uint8_t *)prSwRfb->pvHeader; |
| struct IE_MEASUREMENT_REQ *prCurrMeasElem = NULL; |
| uint16_t u2CalcIELen = 0; |
| uint16_t u2IELen = 0; |
| |
| if (prSwRfb->u2PacketLen <= u2Offset) { |
| DBGLOG(RLM, ERROR, "RRM: Rm Packet length %d is too short\n", |
| prSwRfb->u2PacketLen); |
| return FALSE; |
| } |
| pucIE += u2Offset; |
| u2ElemLen = prSwRfb->u2PacketLen - u2Offset; |
| IE_FOR_EACH(pucIE, u2ElemLen, u2Offset) |
| { |
| u2IELen = IE_LEN(pucIE); |
| |
| /* The minimum value of the Length field is 3 (based on a |
| ** minimum length for the Measurement Request field |
| ** of 0 octets |
| */ |
| if (u2IELen <= 3) { |
| DBGLOG(RLM, ERROR, "RRM: Abnormal RM IE length is %d\n", |
| u2IELen); |
| return FALSE; |
| } |
| |
| /* Check whether the length of each measurment request element |
| ** is reasonable |
| */ |
| prCurrMeasElem = (struct IE_MEASUREMENT_REQ *)pucIE; |
| switch (prCurrMeasElem->ucMeasurementType) { |
| case ELEM_RM_TYPE_BEACON_REQ: |
| if (u2IELen < (3 + OFFSET_OF(struct RM_BCN_REQ, |
| aucSubElements))) { |
| DBGLOG(RLM, ERROR, |
| "RRM: Abnormal Becaon Req IE length is %d\n", |
| u2IELen); |
| return FALSE; |
| } |
| break; |
| case ELEM_RM_TYPE_TSM_REQ: |
| if (u2IELen < (3 + OFFSET_OF(struct RM_TS_MEASURE_REQ, |
| aucSubElements))) { |
| DBGLOG(RLM, ERROR, |
| "RRM: Abnormal TSM Req IE length is %d\n", |
| u2IELen); |
| return FALSE; |
| } |
| break; |
| default: |
| DBGLOG(RLM, ERROR, |
| "RRM: Not support: MeasurementType is %d, IE length is %d\n", |
| prCurrMeasElem->ucMeasurementType, u2IELen); |
| return FALSE; |
| } |
| |
| u2CalcIELen += IE_SIZE(pucIE); |
| } |
| if (u2CalcIELen != u2ElemLen) { |
| DBGLOG(RLM, ERROR, |
| "RRM: Calculated Total IE len is not equal to received length\n"); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| /* |
| */ |
| void rlmProcessRadioMeasurementRequest(struct ADAPTER *prAdapter, |
| struct SW_RFB *prSwRfb) |
| { |
| struct ACTION_RM_REQ_FRAME *prRmReqFrame = NULL; |
| struct ACTION_RM_REPORT_FRAME *prReportFrame = NULL; |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReqParam = NULL; |
| struct RADIO_MEASUREMENT_REPORT_PARAMS *prRmRepParam = NULL; |
| enum RM_REQ_PRIORITY eNewPriority; |
| |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| if (prAdapter->prAisBssInfo == NULL) { |
| DBGLOG(RLM, INFO, "Ignored due to AIS isn't created\n"); |
| return; |
| } |
| prRmReqFrame = (struct ACTION_RM_REQ_FRAME *)prSwRfb->pvHeader; |
| prRmReqParam = &prAdapter->rWifiVar.rRmReqParams; |
| prRmRepParam = &prAdapter->rWifiVar.rRmRepParams; |
| |
| if (!rlmRmFrameIsValid(prSwRfb)) |
| return; |
| DBGLOG(RLM, INFO, "RRM: RM Request From %pM, DialogToken %d\n", |
| prRmReqFrame->aucSrcAddr, prRmReqFrame->ucDialogToken); |
| eNewPriority = rlmGetRmRequestPriority(prRmReqFrame->aucDestAddr); |
| if (prRmReqParam->ePriority > eNewPriority) { |
| DBGLOG(RLM, INFO, "RRM: ignore lower precedence rm request\n"); |
| return; |
| } |
| prRmReqParam->ePriority = eNewPriority; |
| /* */ |
| if (prRmReqParam->fgRmIsOngoing) { |
| DBGLOG(RLM, INFO, "RRM: Old RM is on-going, cancel it first\n"); |
| rlmTxRadioMeasurementReport(prAdapter); |
| wmmRemoveAllTsmMeasurement(prAdapter, FALSE); |
| rlmFreeMeasurementResources(prAdapter); |
| } |
| prRmReqParam->fgRmIsOngoing = TRUE; |
| /* Step1: Save Measurement Request Params */ |
| prRmReqParam->u2ReqIeBufLen = prRmReqParam->u2RemainReqLen = |
| prSwRfb->u2PacketLen - |
| OFFSET_OF(struct ACTION_RM_REQ_FRAME, aucInfoElem); |
| if (prRmReqParam->u2RemainReqLen <= sizeof(struct IE_MEASUREMENT_REQ)) { |
| DBGLOG(RLM, ERROR, |
| "RRM: empty Radio Measurement Request Frame, Elem Len %d\n", |
| prRmReqParam->u2RemainReqLen); |
| return; |
| } |
| WLAN_GET_FIELD_BE16(&prRmReqFrame->u2Repetitions, |
| &prRmReqParam->u2Repetitions); |
| prRmReqParam->pucReqIeBuf = |
| kalMemAlloc(prRmReqParam->u2RemainReqLen, VIR_MEM_TYPE); |
| if (!prRmReqParam->pucReqIeBuf) { |
| DBGLOG(RLM, ERROR, |
| "RRM: Alloc %d bytes Req IE Buffer failed, No Memory\n", |
| prRmReqParam->u2RemainReqLen); |
| return; |
| } |
| kalMemCopy(prRmReqParam->pucReqIeBuf, &prRmReqFrame->aucInfoElem[0], |
| prRmReqParam->u2RemainReqLen); |
| prRmReqParam->prCurrMeasElem = |
| (struct IE_MEASUREMENT_REQ *)prRmReqParam->pucReqIeBuf; |
| prRmReqParam->fgInitialLoop = TRUE; |
| |
| /* Step2: Prepare Report Frame and fill in Frame Header */ |
| prRmRepParam->pucReportFrameBuff = |
| kalMemAlloc(RM_REPORT_FRAME_MAX_LENGTH, VIR_MEM_TYPE); |
| if (!prRmRepParam->pucReportFrameBuff) { |
| DBGLOG(RLM, ERROR, |
| "RRM: Alloc Memory for Measurement Report Frame buffer failed\n"); |
| return; |
| } |
| kalMemZero(prRmRepParam->pucReportFrameBuff, |
| RM_REPORT_FRAME_MAX_LENGTH); |
| prReportFrame = (struct ACTION_RM_REPORT_FRAME *) |
| prRmRepParam->pucReportFrameBuff; |
| prReportFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| COPY_MAC_ADDR(prReportFrame->aucDestAddr, prRmReqFrame->aucSrcAddr); |
| COPY_MAC_ADDR(prReportFrame->aucSrcAddr, |
| prAdapter->prAisBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prReportFrame->aucBSSID, prRmReqFrame->aucBSSID); |
| prReportFrame->ucCategory = CATEGORY_RM_ACTION; |
| prReportFrame->ucAction = RM_ACTION_RM_REPORT; |
| prReportFrame->ucDialogToken = prRmReqFrame->ucDialogToken; |
| prRmRepParam->u2ReportFrameLen = |
| OFFSET_OF(struct ACTION_RM_REPORT_FRAME, aucInfoElem); |
| rlmCalibrateRepetions(prRmReqParam); |
| /* Step3: Start to process Measurement Request Element */ |
| rlmStartNextMeasurement(prAdapter, TRUE); |
| } |
| |
| void rlmTxRadioMeasurementReport(struct ADAPTER *prAdapter) |
| { |
| struct MSDU_INFO *prMsduInfo = NULL; |
| struct RADIO_MEASUREMENT_REPORT_PARAMS *prRmRepParam = |
| &prAdapter->rWifiVar.rRmRepParams; |
| struct STA_RECORD *prStaRec = NULL; |
| |
| if (prRmRepParam->u2ReportFrameLen <= |
| OFFSET_OF(struct ACTION_RM_REPORT_FRAME, aucInfoElem)) { |
| DBGLOG(RLM, INFO, "RRM: report frame length is too short, %d\n", |
| prRmRepParam->u2ReportFrameLen); |
| return; |
| } |
| if (!prAdapter->prAisBssInfo) { |
| DBGLOG(RLM, INFO, "RRM: ais bss info is NULL\n"); |
| return; |
| } |
| prStaRec = prAdapter->prAisBssInfo->prStaRecOfAP; |
| if (!prStaRec) { |
| DBGLOG(RLM, INFO, "RRM: StaRec of Ais is NULL\n"); |
| return; |
| } |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc( |
| prAdapter, prRmRepParam->u2ReportFrameLen); |
| if (!prMsduInfo) { |
| DBGLOG(RLM, INFO, |
| "RRM: Alloc MSDU Info failed, frame length %d\n", |
| prRmRepParam->u2ReportFrameLen); |
| return; |
| } |
| DBGLOG(RLM, INFO, "RRM: frame length %d\n", |
| prRmRepParam->u2ReportFrameLen); |
| kalMemCopy(prMsduInfo->prPacket, prRmRepParam->pucReportFrameBuff, |
| prRmRepParam->u2ReportFrameLen); |
| |
| /* 2 Update information of MSDU_INFO_T */ |
| TX_SET_MMPDU(prAdapter, prMsduInfo, prStaRec->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| prRmRepParam->u2ReportFrameLen, NULL, MSDU_RATE_MODE_AUTO); |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| /* reset u2ReportFrameLen after tx frame */ |
| prRmRepParam->u2ReportFrameLen = |
| OFFSET_OF(struct ACTION_RM_REPORT_FRAME, aucInfoElem); |
| } |
| |
| void rlmGenerateRRMEnabledCapIE(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo) |
| { |
| struct IE_RRM_ENABLED_CAP *prRrmEnabledCap = NULL; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prRrmEnabledCap = (struct IE_RRM_ENABLED_CAP *) |
| (((uint8_t *) prMsduInfo->prPacket) + prMsduInfo->u2FrameLength); |
| prRrmEnabledCap->ucId = ELEM_ID_RRM_ENABLED_CAP; |
| prRrmEnabledCap->ucLength = ELEM_MAX_LEN_RRM_CAP; |
| kalMemZero(&prRrmEnabledCap->aucCap[0], ELEM_MAX_LEN_RRM_CAP); |
| rlmFillRrmCapa(&prRrmEnabledCap->aucCap[0]); |
| prMsduInfo->u2FrameLength += IE_SIZE(prRrmEnabledCap); |
| } |
| |
| void rlmFillRrmCapa(uint8_t *pucCapa) |
| { |
| uint8_t ucIndex = 0; |
| uint8_t aucEnabledBits[] = {RRM_CAP_INFO_LINK_MEASURE_BIT, |
| RRM_CAP_INFO_NEIGHBOR_REPORT_BIT, |
| RRM_CAP_INFO_REPEATED_MEASUREMENT, |
| RRM_CAP_INFO_BEACON_PASSIVE_MEASURE_BIT, |
| RRM_CAP_INFO_BEACON_ACTIVE_MEASURE_BIT, |
| RRM_CAP_INFO_BEACON_TABLE_BIT, |
| RRM_CAP_INFO_RRM_BIT}; |
| |
| for (; ucIndex < sizeof(aucEnabledBits); ucIndex++) |
| SET_EXT_CAP(pucCapa, ELEM_MAX_LEN_RRM_CAP, |
| aucEnabledBits[ucIndex]); |
| } |
| |
| void rlmGeneratePowerCapIE(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo) |
| { |
| struct IE_POWER_CAP *prPwrCap = NULL; |
| uint8_t ucChannel = 0; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| ucChannel = |
| prAdapter->rWifiVar.rAisFsmInfo.prTargetBssDesc->ucChannelNum; |
| prPwrCap = (struct IE_POWER_CAP *)(((uint8_t *)prMsduInfo->prPacket) + |
| prMsduInfo->u2FrameLength); |
| prPwrCap->ucId = ELEM_ID_PWR_CAP; |
| prPwrCap->ucLength = 2; |
| prPwrCap->cMaxTxPowerCap = RLM_MAX_TX_PWR; |
| prPwrCap->cMinTxPowerCap = RLM_MIN_TX_PWR; |
| prMsduInfo->u2FrameLength += IE_SIZE(prPwrCap); |
| } |
| |
| void rlmSetMaxTxPwrLimit(IN struct ADAPTER *prAdapter, int8_t cLimit, |
| uint8_t ucEnable) |
| { |
| struct CMD_SET_AP_CONSTRAINT_PWR_LIMIT rTxPwrLimit; |
| |
| kalMemZero(&rTxPwrLimit, sizeof(rTxPwrLimit)); |
| rTxPwrLimit.ucCmdVer = 0x1; |
| rTxPwrLimit.ucPwrSetEnable = ucEnable; |
| if (ucEnable) { |
| if (cLimit > RLM_MAX_TX_PWR) { |
| DBGLOG(RLM, INFO, |
| "LM: Target MaxPwr %d Higher than Capability, reset to capability\n", |
| cLimit); |
| cLimit = RLM_MAX_TX_PWR; |
| } |
| if (cLimit < RLM_MIN_TX_PWR) { |
| DBGLOG(RLM, INFO, |
| "LM: Target MinPwr %d Lower than Capability, reset to capability\n", |
| cLimit); |
| cLimit = RLM_MIN_TX_PWR; |
| } |
| DBGLOG(RLM, INFO, |
| "LM: Set Max Tx Power Limit %d, Min Limit %d\n", cLimit, |
| RLM_MIN_TX_PWR); |
| rTxPwrLimit.cMaxTxPwr = |
| cLimit * 2; /* unit of cMaxTxPwr is 0.5 dBm */ |
| rTxPwrLimit.cMinTxPwr = RLM_MIN_TX_PWR * 2; |
| } else |
| DBGLOG(RLM, TRACE, "LM: Disable Tx Power Limit\n"); |
| wlanSendSetQueryCmd(prAdapter, CMD_ID_SET_AP_CONSTRAINT_PWR_LIMIT, TRUE, |
| FALSE, FALSE, nicCmdEventSetCommon, |
| nicOidCmdTimeoutCommon, |
| sizeof(struct CMD_SET_AP_CONSTRAINT_PWR_LIMIT), |
| (uint8_t *)&rTxPwrLimit, NULL, 0); |
| } |
| |
| enum RM_REQ_PRIORITY rlmGetRmRequestPriority(uint8_t *pucDestAddr) |
| { |
| if (IS_UCAST_MAC_ADDR(pucDestAddr)) |
| return RM_PRI_UNICAST; |
| else if (EQUAL_MAC_ADDR(pucDestAddr, "\xff\xff\xff\xff\xff\xff")) |
| return RM_PRI_BROADCAST; |
| return RM_PRI_MULTICAST; |
| } |
| |
| static void rlmCalibrateRepetions(struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq) |
| { |
| uint16_t u2IeSize = 0; |
| uint16_t u2RemainReqLen = prRmReq->u2ReqIeBufLen; |
| struct IE_MEASUREMENT_REQ *prCurrReq = |
| (struct IE_MEASUREMENT_REQ *)prRmReq->prCurrMeasElem; |
| |
| if (prRmReq->u2Repetitions == 0) |
| return; |
| |
| u2IeSize = IE_SIZE(prCurrReq); |
| while (u2RemainReqLen >= u2IeSize) { |
| /* 1. If all measurement request has enable bit, no need to |
| ** repeat |
| ** see 11.10.6 Measurement request elements with the enable bit |
| ** set to 1 shall be processed once |
| ** regardless of the value in the number of repetitions in the |
| ** measurement request. |
| ** 2. Due to we don't support parallel measurement, if all |
| ** request has parallel bit, no need to repeat |
| ** measurement, to avoid frequent composing incapable response |
| ** IE and exhauste CPU resource |
| ** and then cause watch dog timeout. |
| ** 3. if all measurements are not supported, no need to repeat. |
| ** currently we only support Beacon request |
| ** on this chip. |
| */ |
| if (!(prCurrReq->ucRequestMode & |
| (RM_REQ_MODE_ENABLE_BIT | RM_REQ_MODE_PARALLEL_BIT))) { |
| if (prCurrReq->ucMeasurementType == |
| ELEM_RM_TYPE_BEACON_REQ) |
| return; |
| } |
| u2RemainReqLen -= u2IeSize; |
| prCurrReq = (struct IE_MEASUREMENT_REQ *)((uint8_t *)prCurrReq + |
| u2IeSize); |
| u2IeSize = IE_SIZE(prCurrReq); |
| } |
| DBGLOG(RLM, INFO, |
| "RRM: All Measurement has set enable bit, or all are parallel or not supported, don't repeat\n"); |
| prRmReq->u2Repetitions = 0; |
| } |
| |
| void rlmRunEventProcessNextRm(struct ADAPTER *prAdapter, |
| struct MSG_HDR *prMsgHdr) |
| { |
| cnmMemFree(prAdapter, prMsgHdr); |
| rlmStartNextMeasurement(prAdapter, FALSE); |
| } |
| |
| void rlmScheduleNextRm(struct ADAPTER *prAdapter) |
| { |
| struct MSG_HDR *prMsg = NULL; |
| |
| prMsg = cnmMemAlloc(prAdapter, RAM_TYPE_MSG, sizeof(*prMsg)); |
| if (!prMsg) { |
| DBGLOG(RLM, ERROR, "[RRM] No memory\n"); |
| return; |
| } |
| prMsg->eMsgId = MID_RLM_RM_SCHEDULE; |
| mboxSendMsg(prAdapter, MBOX_ID_0, prMsg, MSG_SEND_METHOD_BUF); |
| } |
| |
| static void rlmCollectBeaconReport(IN struct ADAPTER *prAdapter, |
| uint8_t *pucIEBuf, uint16_t u2IELength, |
| uint8_t *pucBssid, |
| struct RM_BEACON_REPORT_PARAMS *prRepParams) |
| { |
| #define BEACON_FIXED_FIELD_LENGTH 12 |
| struct RADIO_MEASUREMENT_REQ_PARAMS *prRmReq = |
| &prAdapter->rWifiVar.rRmReqParams; |
| struct RADIO_MEASUREMENT_REPORT_PARAMS *prRmRep = |
| &prAdapter->rWifiVar.rRmRepParams; |
| struct RM_BCN_REQ *prBcnReq = |
| (struct RM_BCN_REQ *)&prRmReq->prCurrMeasElem |
| ->aucRequestFields[0]; |
| struct IE_MEASUREMENT_REPORT *prMeasReport = NULL; |
| /* Variables to process Beacon IE */ |
| uint8_t *pucIE; |
| uint16_t u2Offset = 0; |
| |
| /* Variables to collect report */ |
| uint8_t ucRSSI = 0; |
| uint8_t *pucSubIE = NULL; |
| uint8_t ucCondition = 0; |
| uint8_t ucRefValue = 0; |
| uint8_t ucReportDetail = 0; |
| uint8_t *pucReportIeIds = NULL; |
| uint8_t ucReportIeIdsLen = 0; |
| struct RM_BCN_REPORT *prBcnReport = NULL; |
| uint8_t ucBcnReportLen = 0; |
| struct RM_MEASURE_REPORT_ENTRY *prReportEntry = NULL; |
| uint16_t u2RemainLen = 0; |
| u_int8_t fgValidChannel = FALSE; |
| uint16_t u2IeSize = 0; |
| |
| if (!EQUAL_MAC_ADDR(prBcnReq->aucBssid, "\xff\xff\xff\xff\xff\xff") && |
| !EQUAL_MAC_ADDR(prBcnReq->aucBssid, pucBssid)) { |
| DBGLOG(RLM, INFO, |
| "BCN REQ: bssid mismatch, req %pM, actual %pM\n", |
| prBcnReq->aucBssid, pucBssid); |
| return; |
| } |
| |
| pucIE = pucIEBuf; |
| /* Step1: parsing Beacon Request sub element field to get Report |
| ** controlling information if match the channel that is in fixed |
| ** field, no need to check AP channel report |
| */ |
| if (prBcnReq->ucChannel == prRepParams->ucChannel) |
| fgValidChannel = TRUE; |
| |
| u2RemainLen = prRmReq->prCurrMeasElem->ucLength - 3 - |
| OFFSET_OF(struct RM_BCN_REQ, aucSubElements); |
| pucSubIE = &prBcnReq->aucSubElements[0]; |
| while (u2RemainLen > 0) { |
| u2IeSize = IE_SIZE(pucSubIE); |
| if (u2IeSize > u2RemainLen) |
| break; |
| switch (pucSubIE[0]) { |
| case 0: |
| /* checking if SSID is matched |
| ** length of sub-element ssid is 0 or first byte is 0, |
| ** means wildcard ssid matching |
| */ |
| if (!IE_LEN(pucSubIE) || !pucSubIE[2]) |
| break; |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| if (IE_ID(pucIE) == ELEM_ID_SSID) |
| break; |
| } |
| if (EQUAL_SSID(&pucIE[2], pucIE[1], &pucSubIE[2], |
| pucSubIE[1])) |
| break; |
| { |
| uint8_t aucReqSsid[33] = {0}; |
| uint8_t aucBcnSsid[33] = {0}; |
| uint8_t ucReqSsidLen = |
| pucSubIE[1] <= 32 ? pucSubIE[1] : 32; |
| uint8_t ucBcnSsidLen = |
| pucIE[1] <= 32 ? pucIE[1] : 32; |
| |
| kalMemCopy(aucReqSsid, &pucSubIE[2], |
| ucReqSsidLen); |
| kalMemCopy(aucBcnSsid, &pucIE[2], ucBcnSsidLen); |
| DBGLOG(RLM, TRACE, |
| "BCN REQ: SSID mismatch, req(len %d, %s), bcn(id %d, len %d, %s)\n", |
| ucReqSsidLen, aucReqSsid, pucIE[0], |
| ucBcnSsidLen, aucBcnSsid); |
| } |
| return; /* don't match SSID, don't report it */ |
| case 1: /* Beacon Reporting Information */ |
| ucCondition = pucSubIE[3]; |
| ucRefValue = pucSubIE[4]; |
| break; |
| case 2: /* Reporting Detail Element */ |
| ucReportDetail = pucSubIE[2]; |
| break; |
| case 10: /* Request Elements */ |
| { |
| struct IE_REQUEST *prIe = (struct IE_REQUEST *)pucSubIE; |
| |
| pucReportIeIds = prIe->aucReqIds; |
| ucReportIeIdsLen = prIe->ucLength; |
| break; |
| } |
| case 51: /* AP CHANNEL REPORT Element */ |
| { |
| /* channel info is starting with the fourth byte */ |
| uint8_t ucNumChannels = 3; |
| |
| if (fgValidChannel) |
| break; |
| /* try to match with AP channel report */ |
| while (ucNumChannels < u2IeSize) { |
| if (prRepParams->ucChannel == |
| pucSubIE[ucNumChannels]) { |
| fgValidChannel = TRUE; |
| break; |
| } |
| ucNumChannels++; |
| } |
| } |
| } |
| u2RemainLen -= u2IeSize; |
| pucSubIE += u2IeSize; |
| } |
| if (!fgValidChannel && prBcnReq->ucChannel > 0 && |
| prBcnReq->ucChannel < 255) { |
| DBGLOG(RLM, INFO, "BCN REQ: channel %d, valid %d\n", |
| prBcnReq->ucChannel, fgValidChannel); |
| return; |
| } |
| |
| /* Step2: check report condition */ |
| ucRSSI = RCPI_TO_dBm(prRepParams->ucRCPI); |
| switch (ucCondition) { |
| case 1: |
| if (ucRSSI <= ucRefValue/2) |
| return; |
| break; |
| case 2: |
| if (ucRSSI >= ucRefValue/2) |
| return; |
| break; |
| case 3: |
| break; |
| } |
| /* Step3: Compose Beacon Report in a temp buffer search in saved |
| ** reported link, check if we have saved a report for this AP |
| */ |
| LINK_FOR_EACH_ENTRY(prReportEntry, &prRmRep->rReportLink, rLinkEntry, |
| struct RM_MEASURE_REPORT_ENTRY) |
| { |
| struct IE_MEASUREMENT_REPORT *prReportElem = |
| (struct IE_MEASUREMENT_REPORT *) |
| prReportEntry->aucMeasReport; |
| |
| prBcnReport = |
| (struct RM_BCN_REPORT *)prReportElem->aucReportFields; |
| if (EQUAL_MAC_ADDR(prBcnReport->aucBSSID, pucBssid)) |
| break; |
| prBcnReport = NULL; |
| } |
| /* not found a entry in collected report link */ |
| if (!prBcnReport) { |
| LINK_REMOVE_HEAD(&prRmRep->rFreeReportLink, prReportEntry, |
| struct RM_MEASURE_REPORT_ENTRY *); |
| /* not found a entry in free report link */ |
| if (!prReportEntry) { |
| prReportEntry = kalMemAlloc(sizeof(*prReportEntry), |
| VIR_MEM_TYPE); |
| if (!prReportEntry)/* no memory to allocate in OS */ { |
| DBGLOG(RLM, ERROR, |
| "BCN REQ: Alloc Measurement Report Entry failed, No Memory\n"); |
| return; |
| } |
| } |
| DBGLOG(RLM, TRACE, |
| "BCN REQ: allocate entry for Bss %pM, total entry %u\n", |
| pucBssid, prRmRep->rReportLink.u4NumElem); |
| LINK_INSERT_TAIL(&prRmRep->rReportLink, |
| &prReportEntry->rLinkEntry); |
| } |
| kalMemZero(prReportEntry->aucMeasReport, |
| sizeof(prReportEntry->aucMeasReport)); |
| prMeasReport = |
| (struct IE_MEASUREMENT_REPORT *)prReportEntry->aucMeasReport; |
| prBcnReport = (struct RM_BCN_REPORT *)prMeasReport->aucReportFields; |
| /* Fixed length field */ |
| prBcnReport->ucRegulatoryClass = prBcnReq->ucRegulatoryClass; |
| prBcnReport->ucChannel = prRepParams->ucChannel; |
| prBcnReport->u2Duration = prBcnReq->u2Duration; |
| /* ucReportInfo: Bit 0 is the type of frame, 0 means beacon/probe |
| ** response, bit 1~7 means phy type |
| */ |
| prBcnReport->ucReportInfo = prRepParams->ucFrameInfo; |
| prBcnReport->ucRCPI = ucRSSI; |
| prBcnReport->ucRSNI = |
| prRepParams->ucRSNI; /* ToDo: no RSNI is supported now */ |
| COPY_MAC_ADDR(prBcnReport->aucBSSID, pucBssid); |
| prBcnReport->ucAntennaID = |
| prRepParams->ucAntennaID; /* only one Antenna now */ |
| { |
| OS_SYSTIME rCurrent; |
| uint64_t u8Tsf = *(uint64_t *)&rTsf.au4Tsf[0]; |
| |
| GET_CURRENT_SYSTIME(&rCurrent); |
| if (prRmReq->rStartTime >= rTsf.rTime) |
| u8Tsf += prRmReq->rStartTime - rTsf.rTime; |
| else |
| u8Tsf += rTsf.rTime - prRmReq->rStartTime; |
| kalMemCopy(prBcnReport->aucStartTime, &u8Tsf, |
| 8); /* ToDo: start time is not supported now */ |
| u8Tsf = *(uint64_t *)&rTsf.au4Tsf[0] + rCurrent - rTsf.rTime; |
| kalMemCopy(prBcnReport->aucParentTSF, &u8Tsf, |
| 4); /* low part of TSF */ |
| } |
| ucBcnReportLen = 0; |
| /* Optional Subelement Field all fixed length fields and IEs |
| ** in Request Sub Elements should be reported |
| */ |
| if (ucReportDetail == 1 && ucReportIeIdsLen > 0) { |
| pucSubIE = &prBcnReport->aucOptElem[2]; |
| kalMemCopy(pucSubIE, prRepParams->aucBcnFixedField, |
| BEACON_FIXED_FIELD_LENGTH); |
| pucSubIE += BEACON_FIXED_FIELD_LENGTH; |
| ucBcnReportLen += BEACON_FIXED_FIELD_LENGTH; |
| pucIE = pucIEBuf; |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| uint16_t i = 0; |
| uint8_t ucIncludedIESize = 0; |
| |
| for (; i < ucReportIeIdsLen; i++) |
| if (pucIE[0] == pucReportIeIds[i]) { |
| ucIncludedIESize = IE_SIZE(pucIE); |
| break; |
| } |
| /* the length of sub-element should less than 225, |
| ** and the included IE should be a complete one |
| */ |
| if (ucBcnReportLen + ucIncludedIESize > |
| RM_BCN_REPORT_SUB_ELEM_MAX_LENGTH) |
| break; |
| if (ucIncludedIESize == 0) |
| continue; |
| ucBcnReportLen += ucIncludedIESize; |
| kalMemCopy(pucSubIE, pucIE, ucIncludedIESize); |
| pucSubIE += ucIncludedIESize; |
| } |
| prBcnReport->aucOptElem[0] = |
| 1; /* sub-element id for reported frame body */ |
| prBcnReport->aucOptElem[1] = |
| ucBcnReportLen; /* length of the sub-element */ |
| ucBcnReportLen += 2; |
| } else if (ucReportDetail == |
| 2) { /* all fixed length fields and IEs should be reported */ |
| ucBcnReportLen += BEACON_FIXED_FIELD_LENGTH; |
| pucIE = pucIEBuf; |
| /* the length of sub-element should less than 225, and the |
| ** included IE should be a complete one |
| */ |
| IE_FOR_EACH(pucIE, u2IELength, u2Offset) |
| { |
| if (ucBcnReportLen + IE_SIZE(pucIE) > |
| RM_BCN_REPORT_SUB_ELEM_MAX_LENGTH) |
| break; |
| ucBcnReportLen += IE_SIZE(pucIE); |
| } |
| prBcnReport->aucOptElem[0] = |
| 1; /* sub-element id for reported frame body */ |
| prBcnReport->aucOptElem[1] = |
| ucBcnReportLen; /* length of the sub-element */ |
| pucIE = &prBcnReport->aucOptElem[2]; |
| kalMemCopy(pucIE, prRepParams->aucBcnFixedField, |
| BEACON_FIXED_FIELD_LENGTH); |
| pucIE += BEACON_FIXED_FIELD_LENGTH; |
| kalMemCopy(pucIE, pucIEBuf, |
| ucBcnReportLen - BEACON_FIXED_FIELD_LENGTH); |
| ucBcnReportLen += 2; |
| } |
| ucBcnReportLen += OFFSET_OF(struct RM_BCN_REPORT, aucOptElem); |
| /* Step4: fill in basic content of Measurement report IE */ |
| prMeasReport->ucId = ELEM_ID_MEASUREMENT_REPORT; |
| prMeasReport->ucToken = prRmReq->prCurrMeasElem->ucToken; |
| prMeasReport->ucMeasurementType = ELEM_RM_TYPE_BEACON_REPORT; |
| prMeasReport->ucReportMode = 0; |
| prMeasReport->ucLength = 3 + ucBcnReportLen; |
| DBGLOG(RLM, TRACE, |
| "BCN REQ: Bss %pM, ReportDeail %d, IncludeIE Num %d, chnl %d\n", |
| pucBssid, ucReportDetail, ucReportIeIdsLen, |
| prRepParams->ucChannel); |
| } |
| |
| static uint8_t rlmGetChannel(struct HW_MAC_RX_DESC *prRxStatus, uint8_t *pucIE, |
| uint16_t u2IELen) |
| { |
| uint8_t ucDsChannel = 0; |
| uint8_t ucHtChannel = 0; |
| uint8_t ucHwChannel = HAL_RX_STATUS_GET_CHNL_NUM(prRxStatus); |
| uint16_t u2Offset = 0; |
| enum ENUM_BAND eBand = HAL_RX_STATUS_GET_RF_BAND(prRxStatus); |
| |
| IE_FOR_EACH(pucIE, u2IELen, u2Offset) |
| { |
| switch (IE_ID(pucIE)) { |
| case ELEM_ID_DS_PARAM_SET: |
| if (IE_LEN(pucIE) == ELEM_MAX_LEN_DS_PARAMETER_SET) |
| ucDsChannel = DS_PARAM_IE(pucIE)->ucCurrChnl; |
| break; |
| case ELEM_ID_HT_OP: |
| if (IE_LEN(pucIE) == (sizeof(struct IE_HT_OP) - 2)) |
| ucHtChannel = ((struct IE_HT_OP *)pucIE) |
| ->ucPrimaryChannel; |
| break; |
| } |
| } |
| DBGLOG(RLM, INFO, "BCN REQ: band %d, hw channel %d, ds %d, ht %d\n", |
| eBand, ucHwChannel, ucDsChannel, ucHtChannel); |
| if (eBand == BAND_2G4) { |
| if (ucDsChannel >= 1 && ucDsChannel <= 14) |
| return ucDsChannel; |
| return (ucHtChannel >= 1 && ucHtChannel <= 14) ? ucHtChannel |
| : ucHwChannel; |
| } |
| return (ucHtChannel >= 1 && ucHtChannel < 200) ? ucHtChannel |
| : ucHwChannel; |
| } |
| |
| void rlmProcessBeaconAndProbeResp(struct ADAPTER *prAdapter, |
| IN struct SW_RFB *prSwRfb) |
| { |
| struct RM_BEACON_REPORT_PARAMS rRepParams; |
| struct WLAN_BEACON_FRAME *prWlanBeacon = |
| (struct WLAN_BEACON_FRAME *)prSwRfb->pvHeader; |
| uint16_t u2IELen = prSwRfb->u2PacketLen - |
| OFFSET_OF(struct WLAN_BEACON_FRAME, aucInfoElem); |
| |
| kalMemZero(&rRepParams, sizeof(rRepParams)); |
| if (u2IELen > CFG_IE_BUFFER_SIZE) |
| u2IELen = CFG_IE_BUFFER_SIZE; |
| |
| /* if this is a one antenna only device, the antenna id is always 1. |
| ** 7.3.2.40 |
| */ |
| rRepParams.ucAntennaID = 1; |
| rRepParams.ucChannel = rlmGetChannel( |
| prSwRfb->prRxStatus, prWlanBeacon->aucInfoElem, u2IELen); |
| ASSERT(prSwRfb->prRxStatusGroup3); |
| rRepParams.ucRCPI = nicRxGetRcpiValueFromRxv(RCPI_MODE_MAX, prSwRfb); |
| rRepParams.ucRSNI = |
| 255; /* 255 means RSNI not available. see 7.3.2.41 */ |
| rRepParams.ucFrameInfo = 0; |
| DBGLOG_MEM8(SW4, INFO, (uint8_t *)prWlanBeacon, |
| OFFSET_OF(struct WLAN_BEACON_FRAME, aucInfoElem)); |
| WLAN_GET_FIELD_64(&prWlanBeacon->au4Timestamp[0], |
| &rRepParams.aucBcnFixedField); |
| WLAN_GET_FIELD_16(&prWlanBeacon->u2BeaconInterval, |
| &rRepParams.aucBcnFixedField[8]); |
| WLAN_GET_FIELD_16(&prWlanBeacon->u2CapInfo, |
| &rRepParams.aucBcnFixedField[10]); |
| rlmCollectBeaconReport(prAdapter, prWlanBeacon->aucInfoElem, u2IELen, |
| prWlanBeacon->aucBSSID, &rRepParams); |
| } |
| |
| void rlmUpdateBssTimeTsf(struct ADAPTER *prAdapter, struct BSS_DESC *prBssDesc) |
| { |
| ASSERT(prAdapter); |
| ASSERT(prBssDesc); |
| |
| rTsf.rTime = prBssDesc->rUpdateTime; |
| kalMemCopy(&rTsf.au4Tsf[0], &prBssDesc->u8TimeStamp, 8); |
| } |
| |
| #if CFG_SUPPORT_BFER |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmBfStaRecPfmuUpdate(struct ADAPTER *prAdapter, |
| struct STA_RECORD *prStaRec) |
| { |
| uint8_t ucBFerMaxNr, ucBFeeMaxNr, ucMode; |
| struct BSS_INFO *prBssInfo; |
| struct CMD_STAREC_BF *prStaRecBF; |
| struct CMD_STAREC_UPDATE *prStaRecUpdateInfo; |
| uint32_t rWlanStatus = WLAN_STATUS_SUCCESS; |
| uint32_t u4SetBufferLen = sizeof(struct CMD_STAREC_BF); |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| ASSERT(prBssInfo); |
| |
| if (RLM_NET_IS_11AC(prBssInfo) && |
| IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucStaVhtBfer)) |
| ucMode = MODE_VHT; |
| else if (RLM_NET_IS_11N(prBssInfo) && |
| IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucStaHtBfer)) |
| ucMode = MODE_HT; |
| else |
| ucMode = MODE_LEGACY; |
| |
| prStaRecBF = |
| (struct CMD_STAREC_BF *) cnmMemAlloc(prAdapter, |
| RAM_TYPE_MSG, u4SetBufferLen); |
| |
| if (!prStaRecBF) { |
| DBGLOG(RLM, ERROR, "STA Rec memory alloc fail\n"); |
| return; |
| } |
| |
| prStaRecUpdateInfo = |
| (struct CMD_STAREC_UPDATE *) cnmMemAlloc(prAdapter, |
| RAM_TYPE_MSG, (CMD_STAREC_UPDATE_HDR_SIZE + u4SetBufferLen)); |
| |
| if (!prStaRecUpdateInfo) { |
| cnmMemFree(prAdapter, prStaRecBF); |
| DBGLOG(RLM, ERROR, "STA Rec Update Info memory alloc fail\n"); |
| return; |
| } |
| |
| prStaRec->rTxBfPfmuStaInfo.u2PfmuId = 0xFFFF; |
| |
| switch (ucMode) { |
| case MODE_VHT: |
| prStaRec->rTxBfPfmuStaInfo.fgSU_MU = FALSE; |
| prStaRec->rTxBfPfmuStaInfo.fgETxBfCap = |
| rlmClientSupportsVhtETxBF(prStaRec); |
| |
| if (prStaRec->rTxBfPfmuStaInfo.fgETxBfCap) { |
| /* OFDM, NDPA/Report Poll/CTS2Self tx mode */ |
| prStaRec->rTxBfPfmuStaInfo.ucSoundingPhy = |
| TX_RATE_MODE_OFDM; |
| |
| /* 9: OFDM 24M */ |
| prStaRec->rTxBfPfmuStaInfo.ucNdpaRate = PHY_RATE_24M; |
| |
| /* VHT mode, NDP tx mode */ |
| prStaRec->rTxBfPfmuStaInfo.ucTxMode = TX_RATE_MODE_VHT; |
| |
| /* 0: MCS0 */ |
| prStaRec->rTxBfPfmuStaInfo.ucNdpRate = PHY_RATE_MCS0; |
| |
| switch (prBssInfo->ucVhtChannelWidth) { |
| case VHT_OP_CHANNEL_WIDTH_80: |
| prStaRec->rTxBfPfmuStaInfo.ucCBW = MAX_BW_80MHZ; |
| break; |
| |
| case VHT_OP_CHANNEL_WIDTH_20_40: |
| default: |
| prStaRec->rTxBfPfmuStaInfo.ucCBW = MAX_BW_20MHZ; |
| if (prBssInfo->eBssSCO != CHNL_EXT_SCN) |
| prStaRec->rTxBfPfmuStaInfo.ucCBW = |
| MAX_BW_40MHZ; |
| break; |
| } |
| |
| ucBFerMaxNr = 1; /* 7668 is 2x2 */ |
| ucBFeeMaxNr = rlmClientSupportsVhtBfeeStsCap(prStaRec); |
| prStaRec->rTxBfPfmuStaInfo.ucNr = |
| (ucBFerMaxNr < ucBFeeMaxNr) ? |
| ucBFerMaxNr : ucBFeeMaxNr; |
| prStaRec->rTxBfPfmuStaInfo.ucNc = |
| ((prStaRec->u2VhtRxMcsMap & |
| VHT_CAP_INFO_MCS_2SS_MASK) != |
| BITS(2, 3)) ? 1 : 0; |
| } |
| break; |
| |
| case MODE_HT: |
| prStaRec->rTxBfPfmuStaInfo.fgSU_MU = FALSE; |
| prStaRec->rTxBfPfmuStaInfo.fgETxBfCap = |
| rlmClientSupportsHtETxBF(prStaRec); |
| |
| if (prStaRec->rTxBfPfmuStaInfo.fgETxBfCap) { |
| /* HT mode, NDPA/NDP tx mode */ |
| prStaRec->rTxBfPfmuStaInfo.ucTxMode = |
| TX_RATE_MODE_HTMIX; |
| |
| /* 0: HT MCS0 */ |
| prStaRec->rTxBfPfmuStaInfo.ucNdpaRate = PHY_RATE_MCS0; |
| |
| prStaRec->rTxBfPfmuStaInfo.ucCBW = MAX_BW_20MHZ; |
| if (prBssInfo->eBssSCO != CHNL_EXT_SCN) |
| prStaRec->rTxBfPfmuStaInfo.ucCBW = MAX_BW_40MHZ; |
| |
| ucBFerMaxNr = 1; /* 7668 is 2x2 */ |
| ucBFeeMaxNr = |
| (prStaRec->u4TxBeamformingCap & |
| TXBF_COMPRESSED_TX_ANTENNANUM_SUPPORTED) >> |
| TXBF_COMPRESSED_TX_ANTENNANUM_SUPPORTED_OFFSET; |
| prStaRec->rTxBfPfmuStaInfo.ucNr = |
| (ucBFerMaxNr < ucBFeeMaxNr) ? |
| ucBFerMaxNr : ucBFeeMaxNr; |
| prStaRec->rTxBfPfmuStaInfo.ucNc = |
| (prStaRec->aucRxMcsBitmask[1] > 0) ? 1 : 0; |
| prStaRec->rTxBfPfmuStaInfo.ucNdpRate = |
| prStaRec->rTxBfPfmuStaInfo.ucNr * 8; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| DBGLOG(RLM, INFO, "ucMode=%d\n", ucMode); |
| DBGLOG(RLM, INFO, "rlmClientSupportsVhtETxBF(prStaRec)=%d\n", |
| rlmClientSupportsVhtETxBF(prStaRec)); |
| DBGLOG(RLM, INFO, "rlmClientSupportsVhtBfeeStsCap(prStaRec)=%d\n", |
| rlmClientSupportsVhtBfeeStsCap(prStaRec)); |
| DBGLOG(RLM, INFO, "prStaRec->u2VhtRxMcsMap=%x\n", |
| prStaRec->u2VhtRxMcsMap); |
| |
| DBGLOG(RLM, INFO, |
| "====================== BF StaRec Info =====================\n"); |
| DBGLOG(RLM, INFO, "u2PfmuId =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.u2PfmuId); |
| DBGLOG(RLM, INFO, "fgSU_MU =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.fgSU_MU); |
| DBGLOG(RLM, INFO, "fgETxBfCap =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.fgETxBfCap); |
| DBGLOG(RLM, INFO, "ucSoundingPhy =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucSoundingPhy); |
| DBGLOG(RLM, INFO, "ucNdpaRate =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucNdpaRate); |
| DBGLOG(RLM, INFO, "ucNdpRate =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucNdpRate); |
| DBGLOG(RLM, INFO, "ucReptPollRate =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucReptPollRate); |
| DBGLOG(RLM, INFO, "ucTxMode =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucTxMode); |
| DBGLOG(RLM, INFO, "ucNc =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucNc); |
| DBGLOG(RLM, INFO, "ucNr =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucNr); |
| DBGLOG(RLM, INFO, "ucCBW =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucCBW); |
| DBGLOG(RLM, INFO, "ucTotMemRequire=%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucTotMemRequire); |
| DBGLOG(RLM, INFO, "ucMemRequire20M=%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemRequire20M); |
| DBGLOG(RLM, INFO, "ucMemRow0 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemRow0); |
| DBGLOG(RLM, INFO, "ucMemCol0 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemCol0); |
| DBGLOG(RLM, INFO, "ucMemRow1 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemRow1); |
| DBGLOG(RLM, INFO, "ucMemCol1 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemCol1); |
| DBGLOG(RLM, INFO, "ucMemRow2 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemRow2); |
| DBGLOG(RLM, INFO, "ucMemCol2 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemCol2); |
| DBGLOG(RLM, INFO, "ucMemRow3 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemRow3); |
| DBGLOG(RLM, INFO, "ucMemCol3 =%d\n", |
| prStaRec->rTxBfPfmuStaInfo.ucMemCol3); |
| DBGLOG(RLM, INFO, |
| "===========================================================\n"); |
| |
| |
| |
| prStaRecBF->u2Tag = STA_REC_BF; |
| prStaRecBF->u2Length = u4SetBufferLen; |
| kalMemCopy(&prStaRecBF->rTxBfPfmuInfo, |
| &prStaRec->rTxBfPfmuStaInfo, sizeof(struct TXBF_PFMU_STA_INFO)); |
| |
| |
| prStaRecUpdateInfo->ucBssIndex = prStaRec->ucBssIndex; |
| prStaRecUpdateInfo->ucWlanIdx = prStaRec->ucWlanIndex; |
| prStaRecUpdateInfo->u2TotalElementNum = 1; |
| kalMemCopy(prStaRecUpdateInfo->aucBuffer, prStaRecBF, u4SetBufferLen); |
| |
| |
| rWlanStatus = wlanSendSetQueryExtCmd(prAdapter, |
| CMD_ID_LAYER_0_EXT_MAGIC_NUM, |
| EXT_CMD_ID_STAREC_UPDATE, |
| TRUE, |
| FALSE, |
| FALSE, |
| nicCmdEventSetCommon, |
| nicOidCmdTimeoutCommon, |
| (CMD_STAREC_UPDATE_HDR_SIZE + u4SetBufferLen), |
| (uint8_t *) prStaRecUpdateInfo, NULL, 0); |
| |
| if (rWlanStatus == WLAN_STATUS_FAILURE) |
| DBGLOG(RLM, ERROR, "Send BF sounding cmd fail\n"); |
| |
| cnmMemFree(prAdapter, prStaRecBF); |
| cnmMemFree(prAdapter, prStaRecUpdateInfo); |
| |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void rlmETxBfTriggerPeriodicSounding(struct ADAPTER *prAdapter) |
| { |
| uint32_t u4SetBufferLen = sizeof(union PARAM_CUSTOM_TXBF_ACTION_STRUCT); |
| union PARAM_CUSTOM_TXBF_ACTION_STRUCT rTxBfActionInfo; |
| union CMD_TXBF_ACTION rCmdTxBfActionInfo; |
| uint32_t rWlanStatus = WLAN_STATUS_SUCCESS; |
| |
| DBGLOG(RLM, INFO, "rlmETxBfTriggerPeriodicSounding\n"); |
| |
| rTxBfActionInfo.rTxBfSoundingStart.rTxBfSounding. |
| rExtCmdExtBfSndPeriodicTriggerCtrl.ucCmdCategoryID = |
| BF_SOUNDING_ON; |
| |
| rTxBfActionInfo.rTxBfSoundingStart.rTxBfSounding. |
| rExtCmdExtBfSndPeriodicTriggerCtrl.ucSuMuSndMode = |
| AUTO_SU_PERIODIC_SOUNDING; |
| |
| kalMemCopy(&rCmdTxBfActionInfo, &rTxBfActionInfo, |
| sizeof(union CMD_TXBF_ACTION)); |
| |
| rWlanStatus = wlanSendSetQueryExtCmd(prAdapter, |
| CMD_ID_LAYER_0_EXT_MAGIC_NUM, |
| EXT_CMD_ID_BF_ACTION, |
| TRUE, |
| FALSE, |
| FALSE, |
| nicCmdEventSetCommon, |
| nicOidCmdTimeoutCommon, |
| sizeof(union CMD_TXBF_ACTION), |
| (uint8_t *) &rCmdTxBfActionInfo, |
| &rTxBfActionInfo, u4SetBufferLen); |
| |
| if (rWlanStatus == WLAN_STATUS_FAILURE) |
| DBGLOG(RLM, ERROR, "Send BF sounding cmd fail\n"); |
| |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| bool |
| rlmClientSupportsVhtETxBF(struct STA_RECORD *prStaRec) |
| { |
| uint8_t ucVhtCapSuBfeeCap; |
| |
| ucVhtCapSuBfeeCap = |
| (prStaRec->u4VhtCapInfo & VHT_CAP_INFO_SU_BEAMFORMEE_CAPABLE) |
| >> VHT_CAP_INFO_SU_BEAMFORMEE_CAPABLE_OFFSET; |
| |
| return (ucVhtCapSuBfeeCap) ? TRUE : FALSE; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| uint8_t |
| rlmClientSupportsVhtBfeeStsCap(struct STA_RECORD *prStaRec) |
| { |
| uint8_t ucVhtCapBfeeStsCap; |
| |
| ucVhtCapBfeeStsCap = |
| (prStaRec->u4VhtCapInfo & |
| VHT_CAP_INFO_COMPRESSED_STEERING_NUMBER_OF_BEAMFORMER_ANTENNAS_SUP) >> |
| VHT_CAP_INFO_COMPRESSED_STEERING_NUMBER_OF_BEAMFORMER_ANTENNAS_SUP_OFF; |
| |
| return ucVhtCapBfeeStsCap; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief |
| * |
| * \param[in] |
| * |
| * \return none |
| */ |
| /*----------------------------------------------------------------------------*/ |
| bool |
| rlmClientSupportsHtETxBF(struct STA_RECORD *prStaRec) |
| { |
| uint32_t u4RxNDPCap, u4ComBfFbkCap; |
| |
| u4RxNDPCap = (prStaRec->u4TxBeamformingCap & TXBF_RX_NDP_CAPABLE) |
| >> TXBF_RX_NDP_CAPABLE_OFFSET; |
| /* Support compress feedback */ |
| u4ComBfFbkCap = (prStaRec->u4TxBeamformingCap & |
| TXBF_EXPLICIT_COMPRESSED_FEEDBACK_IMMEDIATE_CAPABLE) |
| >> TXBF_EXPLICIT_COMPRESSED_FEEDBACK_CAPABLE_OFFSET; |
| |
| return (u4RxNDPCap == 1) && (u4ComBfFbkCap > 0); |
| } |
| |
| #endif |
| |
| #if CFG_SUPPORT_WAC |
| uint32_t rlmCalculateWAC_IELen(IN struct ADAPTER *prAdapter, |
| IN uint8_t ucBssIdx, IN struct STA_RECORD *prStaRec) |
| { |
| uint32_t u4IELen = 0; |
| |
| do { |
| ASSERT_BREAK((prAdapter != NULL) && |
| (ucBssIdx < BSS_DEFAULT_NUM)); |
| |
| if (!prAdapter->rWifiVar.fgEnableWACIE) { |
| DBGLOG(RLM, ERROR, "WAC IE disabled, return len=0.\n"); |
| return 0; |
| } |
| |
| if (!prAdapter->fgIsP2PRegistered) |
| break; |
| |
| /*WAC IE exist in Beacon or Pro Resp Frame */ |
| if (!p2pFuncIsAPMode((struct P2P_CONNECTION_SETTINGS *) |
| prAdapter->rWifiVar.prP2PConnSettings)) |
| break; |
| |
| u4IELen = prAdapter->rWifiVar.u2WACIELen; |
| } while (FALSE); |
| |
| DBGLOG(RLM, ERROR, "WAC IE len=%d.\n"); |
| return u4IELen; |
| } |
| |
| void rlmGenerateWAC_IE(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo) |
| { |
| uint8_t *pucIEBuf = (uint8_t *) NULL; |
| |
| do { |
| ASSERT_BREAK((prAdapter != NULL) && (prMsduInfo != NULL)); |
| |
| if (!prAdapter->rWifiVar.fgEnableWACIE) { |
| DBGLOG(RLM, ERROR, "WAC IE disabled, return null.\n"); |
| return; |
| } |
| |
| DBGLOG(RLM, ERROR, "Generate WAC IE: len=%d\n", |
| prAdapter->rWifiVar.u2WACIELen); |
| pucIEBuf = (uint8_t *) ((unsigned long) prMsduInfo->prPacket + |
| (unsigned long) prMsduInfo->u2FrameLength); |
| kalMemCopy(pucIEBuf, prAdapter->rWifiVar.aucWACIECache, |
| prAdapter->rWifiVar.u2WACIELen); |
| prMsduInfo->u2FrameLength += prAdapter->rWifiVar.u2WACIELen; |
| } while (FALSE); |
| } |
| |
| #endif |
| |
| #if IS_ENABLED(CFG_AP_80211K_SUPPORT) |
| static void rlmFillApRrmCapa(uint8_t *pucCapa) |
| { |
| uint8_t ucIndex = 0; |
| uint8_t aucEnabledBits[] = {RRM_CAP_INFO_BEACON_PASSIVE_MEASURE_BIT, |
| RRM_CAP_INFO_BEACON_ACTIVE_MEASURE_BIT, |
| RRM_CAP_INFO_BEACON_TABLE_BIT, |
| RRM_CAP_INFO_CHANNEL_LOAD_MEASURE_BIT, |
| RRM_CAP_INFO_NOISE_HISTOGRAM_MEASURE_BIT, |
| RRM_CAP_INFO_RRM_BIT}; |
| |
| for (; ucIndex < sizeof(aucEnabledBits); ucIndex++) |
| SET_EXT_CAP(pucCapa, ELEM_MAX_LEN_RRM_CAP, |
| aucEnabledBits[ucIndex]); |
| } |
| |
| void rlmGenerateApRRMEnabledCapIE(IN struct ADAPTER *prAdapter, |
| IN struct MSDU_INFO *prMsduInfo) |
| { |
| struct IE_RRM_ENABLED_CAP *prRrmEnabledCap = NULL; |
| |
| ASSERT(prAdapter); |
| ASSERT(prMsduInfo); |
| |
| prRrmEnabledCap = (struct IE_RRM_ENABLED_CAP *) |
| (((uint8_t *) prMsduInfo->prPacket) + prMsduInfo->u2FrameLength); |
| prRrmEnabledCap->ucId = ELEM_ID_RRM_ENABLED_CAP; |
| prRrmEnabledCap->ucLength = ELEM_MAX_LEN_RRM_CAP; |
| kalMemZero(&prRrmEnabledCap->aucCap[0], ELEM_MAX_LEN_RRM_CAP); |
| rlmFillApRrmCapa(&prRrmEnabledCap->aucCap[0]); |
| prMsduInfo->u2FrameLength += IE_SIZE(prRrmEnabledCap); |
| } |
| |
| void rlmTxMeasurementRequest(struct ADAPTER *prAdapter, |
| struct STA_RECORD *prStaRec, |
| struct SUB_ELEMENT_LIST *prSubIEs) |
| { |
| static uint8_t ucDialogToken = 1; |
| struct MSDU_INFO *prMsduInfo = NULL; |
| struct BSS_INFO *prBssInfo = NULL; |
| uint8_t *pucPayload = NULL; |
| struct ACTION_RM_REQ_FRAME *prTxFrame = NULL; |
| uint16_t u2TxFrameLen = 500; |
| uint16_t u2FrameLen = 0; |
| |
| if (!prStaRec) |
| return; |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, prStaRec->ucBssIndex); |
| ASSERT(prBssInfo); |
| /* 1 Allocate MSDU Info */ |
| prMsduInfo = (struct MSDU_INFO *)cnmMgtPktAlloc( |
| prAdapter, MAC_TX_RESERVED_FIELD + u2TxFrameLen); |
| if (!prMsduInfo) |
| return; |
| prTxFrame = (struct ACTION_RM_REQ_FRAME |
| *)((unsigned long)(prMsduInfo->prPacket) + |
| MAC_TX_RESERVED_FIELD); |
| |
| /* 2 Compose The Mac Header. */ |
| prTxFrame->u2FrameCtrl = MAC_FRAME_ACTION; |
| COPY_MAC_ADDR(prTxFrame->aucDestAddr, prStaRec->aucMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucSrcAddr, prBssInfo->aucOwnMacAddr); |
| COPY_MAC_ADDR(prTxFrame->aucBSSID, prBssInfo->aucBSSID); |
| prTxFrame->ucCategory = CATEGORY_RM_ACTION; |
| prTxFrame->ucAction = RM_ACTION_RM_REQUEST; |
| prTxFrame->u2Repetitions = HTONS(prStaRec->u2BcnReqRepetition); |
| u2FrameLen = OFFSET_OF(struct ACTION_RM_REQ_FRAME, aucInfoElem); |
| /* 3 Compose the frame body's frame. */ |
| prTxFrame->ucDialogToken = ucDialogToken++; |
| u2TxFrameLen -= (sizeof(*prTxFrame) - 1); |
| pucPayload = &prTxFrame->aucInfoElem[0]; |
| while (prSubIEs && u2TxFrameLen >= (prSubIEs->rSubIE.ucLength + 2)) { |
| kalMemCopy(pucPayload, &prSubIEs->rSubIE, |
| prSubIEs->rSubIE.ucLength + 2); |
| pucPayload += prSubIEs->rSubIE.ucLength + 2; |
| u2FrameLen += prSubIEs->rSubIE.ucLength + 2; |
| prSubIEs = prSubIEs->prNext; |
| if (prSubIEs) |
| prTxFrame->u2Repetitions++; |
| } |
| nicTxSetMngPacket(prAdapter, prMsduInfo, prStaRec->ucBssIndex, |
| prStaRec->ucIndex, WLAN_MAC_MGMT_HEADER_LEN, |
| u2FrameLen, NULL, MSDU_RATE_MODE_AUTO); |
| |
| /* 5 Enqueue the frame to send this action frame. */ |
| nicTxEnqueueMsdu(prAdapter, prMsduInfo); |
| } |
| |
| static u_int8_t rlmRmReportFrameIsValid(struct SW_RFB *prSwRfb) |
| { |
| uint16_t u2ElemLen = 0; |
| uint16_t u2Offset = |
| (uint16_t)OFFSET_OF(struct ACTION_RM_REPORT_FRAME, aucInfoElem); |
| uint8_t *pucIE = (uint8_t *)prSwRfb->pvHeader; |
| struct IE_MEASUREMENT_REPORT *prCurrMeasElem = NULL; |
| uint16_t u2CalcIELen = 0; |
| uint16_t u2IELen = 0; |
| |
| if (prSwRfb->u2PacketLen <= u2Offset) { |
| DBGLOG(RLM, ERROR, "RRM: Rm Packet length %d is too short\n", |
| prSwRfb->u2PacketLen); |
| return FALSE; |
| } |
| pucIE += u2Offset; |
| u2ElemLen = prSwRfb->u2PacketLen - u2Offset; |
| IE_FOR_EACH(pucIE, u2ElemLen, u2Offset) |
| { |
| u2IELen = IE_LEN(pucIE); |
| |
| /* The minimum value of the Length field is 3 (based on a |
| ** minimum length for the Measurement Request field |
| ** of 0 octets |
| */ |
| if (u2IELen <= 3) { |
| DBGLOG(RLM, ERROR, "RRM: Abnormal RM IE length is %d\n", |
| u2IELen); |
| return FALSE; |
| } |
| |
| /* Check whether the length of each measurment request element |
| ** is reasonable |
| */ |
| prCurrMeasElem = (struct IE_MEASUREMENT_REPORT *)pucIE; |
| switch (prCurrMeasElem->ucMeasurementType) { |
| case ELEM_RM_TYPE_BEACON_REPORT: |
| if (u2IELen < (3 + OFFSET_OF(struct RM_BCN_REPORT, |
| aucOptElem))) { |
| DBGLOG(RLM, ERROR, |
| "RRM: Abnormal Becaon Req IE length is %d\n", |
| u2IELen); |
| return FALSE; |
| } |
| break; |
| default: |
| DBGLOG(RLM, ERROR, |
| "RRM: Not support: MeasurementType is %d, IE length is %d\n", |
| prCurrMeasElem->ucMeasurementType, u2IELen); |
| return FALSE; |
| } |
| |
| u2CalcIELen += IE_SIZE(pucIE); |
| } |
| if (u2CalcIELen != u2ElemLen) { |
| DBGLOG(RLM, ERROR, |
| "RRM: Calculated Total IE len is not equal to received length\n"); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| void rlmProcessRadioMeasurementResponse(struct ADAPTER *prAdapter, |
| struct SW_RFB *prSwRfb) |
| { |
| struct ACTION_RM_REPORT_FRAME *prRxFrame = NULL; |
| uint8_t *pucOptInfo = NULL; |
| uint8_t *pucReportElem = NULL; |
| uint16_t u2TmpLen = 0; |
| struct IE_MEASUREMENT_REPORT *prMeasureReportIE = NULL; |
| struct RM_BCN_REPORT *prBeaconReportIE = NULL; |
| struct T_MULTI_AP_BEACON_METRICS_RESP *prBcnMeasureReport = NULL; |
| int32_t i4Ret = 0; |
| |
| ASSERT(prAdapter); |
| ASSERT(prSwRfb); |
| |
| /*TODO, check it's soft ap mode or not ?*/ |
| |
| prRxFrame = (struct ACTION_RM_REPORT_FRAME *)prSwRfb->pvHeader; |
| if (!rlmRmReportFrameIsValid(prSwRfb)) |
| return; |
| |
| prBcnMeasureReport = (struct T_MULTI_AP_BEACON_METRICS_RESP *) |
| kalMemAlloc(sizeof( |
| struct T_MULTI_AP_BEACON_METRICS_RESP), |
| VIR_MEM_TYPE); |
| if (prBcnMeasureReport == NULL) { |
| DBGLOG(INIT, ERROR, "alloc memory fail\n"); |
| return; |
| } |
| |
| kalMemZero(prBcnMeasureReport, |
| sizeof(struct T_MULTI_AP_BEACON_METRICS_RESP)); |
| COPY_MAC_ADDR(prBcnMeasureReport->mStaMac, prRxFrame->aucSrcAddr); |
| |
| pucOptInfo = &prRxFrame->aucInfoElem[0]; |
| u2TmpLen = OFFSET_OF(struct ACTION_RM_REPORT_FRAME, aucInfoElem); |
| pucReportElem = prBcnMeasureReport->uElem; |
| |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] u2FrameCtrl = 0x%x\n", prRxFrame->u2FrameCtrl); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] u2Duration = %u\n", prRxFrame->u2Duration); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] aucDestAddr = " MACSTR "\n", |
| prRxFrame->aucDestAddr); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] aucSrcAddr = " MACSTR "\n", prRxFrame->aucSrcAddr); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] aucBSSID = " MACSTR "\n", prRxFrame->aucBSSID); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] u2SeqCtrl = %u\n", prRxFrame->u2SeqCtrl); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucCategory = %u\n", prRxFrame->ucCategory); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucAction = %u\n", prRxFrame->ucAction); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucDialogToken = %u\n", prRxFrame->ucDialogToken); |
| /* Measurement Report Elements */ |
| while (prSwRfb->u2PacketLen > u2TmpLen) { |
| switch (pucOptInfo[0]) { |
| case ELEM_ID_MEASUREMENT_REPORT: |
| prMeasureReportIE = |
| (struct IE_MEASUREMENT_REPORT *) &pucOptInfo[0]; |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucId = %u\n", |
| prMeasureReportIE->ucId); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucLength = %u\n", |
| prMeasureReportIE->ucLength); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucToken = %u\n", |
| prMeasureReportIE->ucToken); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucReportMode = 0x%x\n", |
| prMeasureReportIE->ucReportMode); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucMeasurementType = 0x%x\n", |
| prMeasureReportIE->ucMeasurementType); |
| prBeaconReportIE = |
| (struct RM_BCN_REPORT *) |
| &prMeasureReportIE->aucReportFields[0]; |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucRegulatoryClass = %d\n", |
| prBeaconReportIE->ucRegulatoryClass); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucChannel = %d\n", |
| prBeaconReportIE->ucChannel); |
| DBGLOG_MEM8(RLM, WARN, |
| prBeaconReportIE->aucStartTime, 8); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] u2Duration = %d\n", |
| prBeaconReportIE->u2Duration); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucReportInfo = 0x%x\n", |
| prBeaconReportIE->ucReportInfo); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucRCPI = 0x%x\n", |
| prBeaconReportIE->ucRCPI); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucRSNI = 0x%x\n", |
| prBeaconReportIE->ucRSNI); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] aucBSSID = " MACSTR "\n", |
| prBeaconReportIE->aucBSSID); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] ucAntennaID = %d\n", |
| prBeaconReportIE->ucAntennaID); |
| DBGLOG_MEM8(RLM, WARN, |
| prBeaconReportIE->aucParentTSF, 4); |
| DBGLOG_MEM8(RLM, WARN, |
| prBeaconReportIE->aucOptElem, |
| prMeasureReportIE->ucLength - 3 - 26); |
| |
| prBcnMeasureReport->u8ElemNum++; |
| prBcnMeasureReport->uElemLen += |
| (prMeasureReportIE->ucLength + 2); |
| kalMemCopy(pucReportElem, |
| &prMeasureReportIE->ucId, |
| prMeasureReportIE->ucLength + 2); |
| break; |
| default: |
| DBGLOG(RLM, WARN, "[SAP_Test] not know\n"); |
| DBGLOG_MEM8(RLM, WARN, |
| pucOptInfo, pucOptInfo[1] + 2); |
| break; |
| } |
| pucOptInfo += (pucOptInfo[1] + 2); |
| u2TmpLen += (pucOptInfo[1] + 2); |
| pucReportElem += prMeasureReportIE->ucLength; |
| } |
| |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] mStaMac = " MACSTR "\n", |
| prRxFrame->aucSrcAddr); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] u8ElemNum = %u\n", |
| prBcnMeasureReport->u8ElemNum); |
| DBGLOG(RLM, WARN, |
| "[SAP_Test] uElemLen = %u\n", |
| prBcnMeasureReport->uElemLen); |
| DBGLOG_MEM8(RLM, WARN, |
| prBcnMeasureReport->uElem, |
| prBcnMeasureReport->uElemLen); |
| |
| i4Ret = MontorSendMsg(EV_WLAN_MULTIAP_BEACON_METRICS_RESPONSE, |
| prBcnMeasureReport, sizeof(*prBcnMeasureReport)); |
| if (i4Ret < 0) |
| DBGLOG(AAA, ERROR, |
| "EV_WLAN_MULTIAP_BEACON_METRICS_RESPONSE nl send msg failed!\n"); |
| |
| kalMemFree(prBcnMeasureReport, VIR_MEM_TYPE, |
| sizeof(struct T_MULTI_AP_BEACON_METRICS_RESP)); |
| } |
| #endif /* CFG_AP_80211K_SUPPORT */ |
| |