/*
 * Copyright (c) 2011-2014, 2016-2019 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/*
 *
 * limSendMessages.c: Provides functions to send messages or Indications to HAL.
 * Author:    Sunit Bhatia
 * Date:       09/21/2006
 * History:-
 * Date        Modified by            Modification Information
 *
 * --------------------------------------------------------------------------
 *
 */
#include "limSendMessages.h"
#include "cfgApi.h"
#include "limTrace.h"
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM //FEATURE_WLAN_DIAG_SUPPORT
#include "vos_diag_core_log.h"
#endif //FEATURE_WLAN_DIAG_SUPPORT
/* When beacon filtering is enabled, firmware will
 * analyze the selected beacons received during BMPS,
 * and monitor any changes in the IEs as listed below.
 * The format of the table is:
 *    - EID
 *    - Check for IE presence
 *    - Byte offset
 *    - Byte value
 *    - Bit Mask
 *    - Byte refrence
 */
static tBeaconFilterIe beaconFilterTable[] = {
   {SIR_MAC_DS_PARAM_SET_EID,    0, {0, 0, DS_PARAM_CHANNEL_MASK, 0}},
   {SIR_MAC_ERP_INFO_EID,        0, {0, 0, ERP_FILTER_MASK,       0}},
   {SIR_MAC_EDCA_PARAM_SET_EID,  0, {0, 0, EDCA_FILTER_MASK,      0}},
   {SIR_MAC_QOS_CAPABILITY_EID,  0, {0, 0, QOS_FILTER_MASK,       0}},
   {SIR_MAC_CHNL_SWITCH_ANN_EID, 1, {0, 0, 0,                     0}},
   {SIR_MAC_HT_INFO_EID,         0, {0, 0, HT_BYTE0_FILTER_MASK,  0}},
   {SIR_MAC_HT_INFO_EID,         0, {2, 0, HT_BYTE2_FILTER_MASK,  0}},
   {SIR_MAC_HT_INFO_EID,         0, {5, 0, HT_BYTE5_FILTER_MASK,  0}}
#if defined WLAN_FEATURE_VOWIFI
   ,{SIR_MAC_PWR_CONSTRAINT_EID,  0, {0, 0, 0, 0}}
#endif
#ifdef WLAN_FEATURE_11AC
   ,{SIR_MAC_VHT_OPMODE_EID,     0,  {0, 0, 0, 0}}
   ,{SIR_MAC_VHT_OPERATION_EID,  0,  {0, 0, VHTOP_CHWIDTH_MASK, 0}}
#endif
};

/**
 * limSendBeaconParams()
 *
 *FUNCTION:
 * This function is called to send beacnon interval, short preamble or other
 * parameters to WDA, which are changed and indication is received in beacon.
 *
 *LOGIC:
 *
 *ASSUMPTIONS:
 * NA
 *
 *NOTE:
 * NA
 *
 * @param pMac  pointer to Global Mac structure.
 * @param tpUpdateBeaconParams pointer to the structure,
                which contains the beacon parameters which are changed.
 *
 * @return success if message send is ok, else false.
 */
tSirRetStatus limSendBeaconParams(tpAniSirGlobal pMac,
                                  tpUpdateBeaconParams pUpdatedBcnParams,
                                  tpPESession  psessionEntry )
{
    tpUpdateBeaconParams pBcnParams = NULL;
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;

    pBcnParams = vos_mem_malloc(sizeof(*pBcnParams));
    if ( NULL == pBcnParams )
    {
        limLog( pMac, LOGP,
            FL( "Unable to allocate memory during Update Beacon Params" ));
        return eSIR_MEM_ALLOC_FAILED;
    }
    vos_mem_copy((tANI_U8 *) pBcnParams,  pUpdatedBcnParams, sizeof(*pBcnParams));
    msgQ.type = WDA_UPDATE_BEACON_IND;
    msgQ.reserved = 0;
    msgQ.bodyptr = pBcnParams;
    msgQ.bodyval = 0;
    PELOG3(limLog( pMac, LOG3,
                FL( "Sending WDA_UPDATE_BEACON_IND, paramChangeBitmap in hex = %x" ),
                    pUpdatedBcnParams->paramChangeBitmap);)
    if(NULL == psessionEntry)
    {
        vos_mem_free(pBcnParams);
        MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
        return eSIR_FAILURE;
    }
    else
    {
        MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    }
    pBcnParams->smeSessionId = psessionEntry->smeSessionId;
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pBcnParams);
        limLog( pMac, LOGP,
                    FL("Posting  WDA_UPDATE_BEACON_IND to WDA failed, reason=%X"),
                    retCode );
    }
    limSendBeaconInd(pMac, psessionEntry);
    return retCode;
}

/**
 * limSendSwitchChnlParams()
 *
 *FUNCTION:
 * This function is called to send Channel Switch Indication to WDA
 *
 *LOGIC:
 *
 *ASSUMPTIONS:
 * NA
 *
 *NOTE:
 * NA
 *
 * @param pMac  pointer to Global Mac structure.
 * @param chnlNumber New Channel Number to be switched to.
 * @param secondaryChnlOffset  an enum for secondary channel offset.
 * @param localPowerConstraint 11h local power constraint value
 *
 * @return success if message send is ok, else false.
 */
#if !defined WLAN_FEATURE_VOWIFI
tSirRetStatus limSendSwitchChnlParams(tpAniSirGlobal pMac,
                                      tANI_U8 chnlNumber,
                                      ePhyChanBondState secondaryChnlOffset,
                                      tANI_U8 localPwrConstraint,
                                      tANI_U8 peSessionId,
                                      uint8_t is_restart)
#else
tSirRetStatus limSendSwitchChnlParams(tpAniSirGlobal pMac,
                                      tANI_U8 chnlNumber,
                                      ePhyChanBondState secondaryChnlOffset,
                                      tPowerdBm maxTxPower, tANI_U8 peSessionId,
                                      uint8_t is_restart)
#endif
{
    tpSwitchChannelParams pChnlParams = NULL;
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;
    tpPESession pSessionEntry;

    if((pSessionEntry = peFindSessionBySessionId(pMac, peSessionId)) == NULL)
    {
       limLog( pMac, LOGP,
             FL( "Unable to get Session for session Id %d" ), peSessionId);
       return eSIR_FAILURE;
    }
    pChnlParams = vos_mem_malloc(sizeof( tSwitchChannelParams ));
    if ( NULL == pChnlParams )
    {
        limLog( pMac, LOGP,
            FL( "Unable to allocate memory during Switch Channel Params" ));
        retCode = eSIR_MEM_ALLOC_FAILED;
        goto returnFailure;
    }
    vos_mem_set((tANI_U8 *) pChnlParams, sizeof(tSwitchChannelParams), 0);
    pChnlParams->secondaryChannelOffset = secondaryChnlOffset;
    pChnlParams->channelNumber= chnlNumber;
    vos_mem_copy( pChnlParams->selfStaMacAddr, pSessionEntry->selfMacAddr, sizeof(tSirMacAddr) );
#if defined WLAN_FEATURE_VOWIFI
    pChnlParams->maxTxPower = maxTxPower;
#else
    pChnlParams->localPowerConstraint = localPwrConstraint;
#endif
    vos_mem_copy(  pChnlParams->bssId, pSessionEntry->bssId, sizeof(tSirMacAddr) );
    pChnlParams->peSessionId = peSessionId;
    pChnlParams->vhtCapable = pSessionEntry->vhtCapability;
    pChnlParams->dot11_mode = pSessionEntry->dot11mode;

    /*Set DFS flag for DFS channel*/
    if (vos_nv_getChannelEnabledState(chnlNumber) == NV_CHANNEL_DFS)
       pChnlParams->isDfsChannel= VOS_TRUE;
    else
       pChnlParams->isDfsChannel = VOS_FALSE;

    pChnlParams->restart_on_chan_switch = is_restart;
    pChnlParams->reduced_beacon_interval =
       pMac->sap.SapDfsInfo.reduced_beacon_interval;
    pChnlParams->beacon_tx_rate = pSessionEntry->beacon_tx_rate;

    if (pSessionEntry->sub20_channelwidth == SUB20_MODE_5MHZ)
            pChnlParams->channelwidth = CH_WIDTH_5MHZ;
    else if (pSessionEntry->sub20_channelwidth == SUB20_MODE_10MHZ)
            pChnlParams->channelwidth = CH_WIDTH_10MHZ;

    if (pSessionEntry->bssType == eSIR_INFRASTRUCTURE_MODE &&
        pSessionEntry->sub20_channelwidth !=
        pMac->sta_sub20_current_channelwidth)
        sme_set_sta_chanlist_with_sub20(pMac,
                                        pSessionEntry->sub20_channelwidth);

    limLog(pMac, LOG1, FL("Set sub20 channel width %d"),
           pSessionEntry->sub20_channelwidth);

    if (pSessionEntry->ssidHidden)
        pChnlParams->ssidHidden = pSessionEntry->ssidHidden;

    if (pSessionEntry->ssId.length) {
        vos_mem_copy((uint8_t*)pChnlParams->ssid.ssId,
		     (uint8_t*)pSessionEntry->ssId.ssId,
		     pSessionEntry->ssId.length);
        pChnlParams->ssid.length = pSessionEntry->ssId.length;
        limLog(pMac, LOGE, FL("ssid name: %s length %d"),
	       pChnlParams->ssid.ssId, pChnlParams->ssid.length);
    }
    //we need to defer the message until we get the response back from WDA.
    SET_LIM_PROCESS_DEFD_MESGS(pMac, false);
    msgQ.type = WDA_CHNL_SWITCH_REQ;
    msgQ.reserved = 0;
    msgQ.bodyptr = pChnlParams;
    msgQ.bodyval = 0;
#if defined WLAN_FEATURE_VOWIFI
    PELOG3(limLog( pMac, LOG3,
        FL( "Sending WDA_CHNL_SWITCH_REQ with SecondaryChnOffset - %d, ChannelNumber - %d, maxTxPower - %d"),
        pChnlParams->secondaryChannelOffset, pChnlParams->channelNumber, pChnlParams->maxTxPower);)
#else
    PELOG3(limLog( pMac, LOG3,
        FL( "Sending WDA_CHNL_SWITCH_REQ with SecondaryChnOffset - %d, ChannelNumber - %d, LocalPowerConstraint - %d"),
        pChnlParams->secondaryChannelOffset, pChnlParams->channelNumber, pChnlParams->localPowerConstraint);)
#endif
    MTRACE(macTraceMsgTx(pMac, peSessionId, msgQ.type));
    limLog(pMac,LOG1,"SessionId:%d WDA_CHNL_SWITCH_REQ for SSID:%s",peSessionId,
           pSessionEntry->ssId.ssId);
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pChnlParams);
        limLog(pMac, LOGP,
               FL("Posting  WDA_CHNL_SWITCH_REQ to WDA failed, reason=%X"),
               retCode );
    }
returnFailure:
    return retCode;
}

/**
 * limSendEdcaParams()
 *
 *FUNCTION:
 * This function is called to send dynamically changing EDCA Parameters to WDA.
 *
 *LOGIC:
 *
 *ASSUMPTIONS:
 * NA
 *
 *NOTE:
 * NA
 *
 * @param pMac  pointer to Global Mac structure.
 * @param tpUpdatedEdcaParams pointer to the structure which contains
 *                                       dynamically changing EDCA parameters.
 * @return success if message send is ok, else false.
 */
tSirRetStatus limSendEdcaParams(tpAniSirGlobal pMac,
                                tSirMacEdcaParamRecord *pUpdatedEdcaParams,
                                tANI_U16 bssIdx)
{
    tEdcaParams *pEdcaParams = NULL;
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;

    pEdcaParams = vos_mem_malloc(sizeof(tEdcaParams));
    if ( NULL == pEdcaParams )
    {
        limLog( pMac, LOGP,
            FL( "Unable to allocate memory during Update EDCA Params" ));
        retCode = eSIR_MEM_ALLOC_FAILED;
        return retCode;
    }
    pEdcaParams->bssIdx = bssIdx;
    pEdcaParams->acbe = pUpdatedEdcaParams[EDCA_AC_BE];
    pEdcaParams->acbk = pUpdatedEdcaParams[EDCA_AC_BK];
    pEdcaParams->acvi = pUpdatedEdcaParams[EDCA_AC_VI];
    pEdcaParams->acvo = pUpdatedEdcaParams[EDCA_AC_VO];
    msgQ.type = WDA_UPDATE_EDCA_PROFILE_IND;
    msgQ.reserved = 0;
    msgQ.bodyptr = pEdcaParams;
    msgQ.bodyval = 0;
    {
        tANI_U8 i;
        PELOG1(limLog( pMac, LOG1,FL("Sending WDA_UPDATE_EDCA_PROFILE_IND with EDCA Parameters:" ));)
        for(i=0; i<MAX_NUM_AC; i++)
        {
            PELOG1(limLog(pMac, LOG1, FL("AC[%d]:  AIFSN %d, ACM %d, CWmin %d, CWmax %d, TxOp %d "),
                   i, pUpdatedEdcaParams[i].aci.aifsn, pUpdatedEdcaParams[i].aci.acm,
                   pUpdatedEdcaParams[i].cw.min, pUpdatedEdcaParams[i].cw.max, pUpdatedEdcaParams[i].txoplimit);)
        }
    }
    MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pEdcaParams);
        limLog( pMac, LOGP,
                    FL("Posting  WDA_UPDATE_EDCA_PROFILE_IND to WDA failed, reason=%X"),
                    retCode );
    }
    return retCode;
}

/**
 * limSetActiveEdcaParams()
 *
 * FUNCTION:
 *  This function is called to set the most up-to-date EDCA parameters
 *  given the default local EDCA parameters.  The rules are as following:
 *  - If ACM bit is set for all ACs, then downgrade everything to Best Effort.
 *  - If ACM is not set for any AC, then PE will use the default EDCA
 *    parameters as advertised by AP.
 *  - If ACM is set in any of the ACs, PE will use the EDCA parameters
 *    from the next best AC for which ACM is not enabled.
 *
 * @param pMac  pointer to Global Mac structure.
 * @param plocalEdcaParams pointer to the local EDCA parameters
 * @ param psessionEntry point to the session entry
 * @return none
 */
 void limSetActiveEdcaParams(tpAniSirGlobal pMac, tSirMacEdcaParamRecord *plocalEdcaParams, tpPESession psessionEntry)
{
    tANI_U8   ac, newAc, i;
    tANI_U8   acAdmitted;
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM //FEATURE_WLAN_DIAG_SUPPORT
    vos_log_qos_edca_pkt_type *log_ptr = NULL;
#endif //FEATURE_WLAN_DIAG_SUPPORT
    // Initialize gLimEdcaParamsActive[] to be same as localEdcaParams
    psessionEntry->gLimEdcaParamsActive[EDCA_AC_BE] = plocalEdcaParams[EDCA_AC_BE];
    psessionEntry->gLimEdcaParamsActive[EDCA_AC_BK] = plocalEdcaParams[EDCA_AC_BK];
    psessionEntry->gLimEdcaParamsActive[EDCA_AC_VI] = plocalEdcaParams[EDCA_AC_VI];
    psessionEntry->gLimEdcaParamsActive[EDCA_AC_VO] = plocalEdcaParams[EDCA_AC_VO];
    /* An AC requires downgrade if the ACM bit is set, and the AC has not
     * yet been admitted in uplink or bi-directions.
     * If an AC requires downgrade, it will downgrade to the next beset AC
     * for which ACM is not enabled.
     *
     * - There's no need to downgrade AC_BE since it IS the lowest AC. Hence
     *   start the for loop with AC_BK.
     * - If ACM bit is set for an AC, initially downgrade it to AC_BE. Then
     *   traverse thru the AC list. If we do find the next best AC which is
     *   better than AC_BE, then use that one. For example, if ACM bits are set
     *   such that: BE_ACM=1, BK_ACM=1, VI_ACM=1, VO_ACM=0
     *   then all AC will be downgraded to AC_BE.
     */
    if(!pMac->psOffloadEnabled)
    {
        limLog(pMac, LOG1, FL("adAdmitMask[UPLINK] = 0x%x "),
                           pMac->lim.gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] );
        limLog(pMac, LOG1, FL("adAdmitMask[DOWNLINK] = 0x%x "),
                           pMac->lim.gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] );
    }
    else
    {
        limLog(pMac, LOG1, FL("adAdmitMask[UPLINK] = 0x%x "),
                           psessionEntry->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] );
        limLog(pMac, LOG1, FL("adAdmitMask[DOWNLINK] = 0x%x "),
                           psessionEntry->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] );
    }
    for (ac = EDCA_AC_BK; ac <= EDCA_AC_VO; ac++)
    {
        if(!pMac->psOffloadEnabled)
        {
            acAdmitted = ( (pMac->lim.gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] &
                           (1 << ac)) >> ac );
        }
        else
        {
            acAdmitted =
                ((psessionEntry->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] &
                 (1 << ac)) >> ac );
        }

        limLog(pMac, LOG1, FL("For AC[%d]: acm=%d,  acAdmit=%d "), ac, plocalEdcaParams[ac].aci.acm, acAdmitted);
        if ( (plocalEdcaParams[ac].aci.acm == 1) && (acAdmitted == 0) )
        {
            limLog(pMac, LOG1, FL("We need to downgrade AC %d!! "), ac);
            newAc = EDCA_AC_BE;
            for (i=ac-1; i>0; i--)
            {
                if (plocalEdcaParams[i].aci.acm == 0)
                {
                    newAc = i;
                    break;
                }
            }
            limLog(pMac, LOGW, FL("Downgrading AC %d ---> AC %d "), ac, newAc);
            psessionEntry->gLimEdcaParamsActive[ac] = plocalEdcaParams[newAc];
        }
    }
//log: LOG_WLAN_QOS_EDCA_C
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM //FEATURE_WLAN_DIAG_SUPPORT
    WLAN_VOS_DIAG_LOG_ALLOC(log_ptr, vos_log_qos_edca_pkt_type, LOG_WLAN_QOS_EDCA_C);
    if(log_ptr)
    {
       log_ptr->aci_be = psessionEntry->gLimEdcaParamsActive[EDCA_AC_BE].aci.aci;
       log_ptr->cw_be  = psessionEntry->gLimEdcaParamsActive[EDCA_AC_BE].cw.max << 4 |
          psessionEntry->gLimEdcaParamsActive[EDCA_AC_BE].cw.min;
       log_ptr->txoplimit_be = psessionEntry->gLimEdcaParamsActive[EDCA_AC_BE].txoplimit;
       log_ptr->aci_bk = psessionEntry->gLimEdcaParamsActive[EDCA_AC_BK].aci.aci;
       log_ptr->cw_bk  = psessionEntry->gLimEdcaParamsActive[EDCA_AC_BK].cw.max << 4 |
          psessionEntry->gLimEdcaParamsActive[EDCA_AC_BK].cw.min;
       log_ptr->txoplimit_bk = psessionEntry->gLimEdcaParamsActive[EDCA_AC_BK].txoplimit;
       log_ptr->aci_vi = psessionEntry->gLimEdcaParamsActive[EDCA_AC_VI].aci.aci;
       log_ptr->cw_vi  = psessionEntry->gLimEdcaParamsActive[EDCA_AC_VI].cw.max << 4 |
          psessionEntry->gLimEdcaParamsActive[EDCA_AC_VI].cw.min;
       log_ptr->txoplimit_vi = psessionEntry->gLimEdcaParamsActive[EDCA_AC_VI].txoplimit;
       log_ptr->aci_vo = psessionEntry->gLimEdcaParamsActive[EDCA_AC_VO].aci.aci;
       log_ptr->cw_vo  = psessionEntry->gLimEdcaParamsActive[EDCA_AC_VO].cw.max << 4 |
          psessionEntry->gLimEdcaParamsActive[EDCA_AC_VO].cw.min;
       log_ptr->txoplimit_vo = psessionEntry->gLimEdcaParamsActive[EDCA_AC_VO].txoplimit;
    }
    WLAN_VOS_DIAG_LOG_REPORT(log_ptr);
#endif //FEATURE_WLAN_DIAG_SUPPORT

    return;
 }

/** ---------------------------------------------------------
\fn      limSetLinkState
\brief   LIM sends a message to WDA to set the link state
\param   tpAniSirGlobal  pMac
\param   tSirLinkState      state
\return  None
  -----------------------------------------------------------*/
tSirRetStatus limSetLinkState(tpAniSirGlobal pMac, tSirLinkState state,tSirMacAddr bssId,
                              tSirMacAddr selfMacAddr, tpSetLinkStateCallback callback,
                              void *callbackArg)
{
    tSirMsgQ msgQ;
    tSirRetStatus retCode;
    tpLinkStateParams pLinkStateParams = NULL;
    // Allocate memory.
    pLinkStateParams = vos_mem_malloc(sizeof(tLinkStateParams));
    if ( NULL == pLinkStateParams )
    {
        limLog( pMac, LOGP,
        FL( "Unable to allocate memory while sending Set Link State" ));
        retCode = eSIR_SME_RESOURCES_UNAVAILABLE;
        return retCode;
    }
    vos_mem_set((tANI_U8 *) pLinkStateParams, sizeof(tLinkStateParams), 0);
    pLinkStateParams->state        = state;
    pLinkStateParams->callback     = callback;
    pLinkStateParams->callbackArg  = callbackArg;

    /* Copy Mac address */
    sirCopyMacAddr(pLinkStateParams->bssid,bssId);
    sirCopyMacAddr(pLinkStateParams->selfMacAddr, selfMacAddr);

    msgQ.type = WDA_SET_LINK_STATE;
    msgQ.reserved = 0;
    msgQ.bodyptr = pLinkStateParams;
    msgQ.bodyval = 0;

    MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));

    retCode = (tANI_U32)wdaPostCtrlMsg(pMac, &msgQ);
    if (retCode != eSIR_SUCCESS)
    {
        vos_mem_free(pLinkStateParams);
        limLog(pMac, LOGP, FL("Posting link state %d failed, reason = %x "),
               state, retCode);
    }
    return retCode;
}
#ifdef WLAN_FEATURE_VOWIFI_11R
extern tSirRetStatus limSetLinkStateFT(tpAniSirGlobal pMac, tSirLinkState
state,tSirMacAddr bssId, tSirMacAddr selfMacAddr, int ft, tpPESession psessionEntry)
{
    tSirMsgQ msgQ;
    tSirRetStatus retCode;
    tpLinkStateParams pLinkStateParams = NULL;
    // Allocate memory.
    pLinkStateParams = vos_mem_malloc(sizeof(tLinkStateParams));
    if ( NULL == pLinkStateParams )
    {
        limLog( pMac, LOGP,
        FL( "Unable to allocate memory while sending Set Link State" ));
        retCode = eSIR_SME_RESOURCES_UNAVAILABLE;
        return retCode;
    }
    vos_mem_set((tANI_U8 *) pLinkStateParams, sizeof(tLinkStateParams), 0);
    pLinkStateParams->state = state;
    /* Copy Mac address */
    sirCopyMacAddr(pLinkStateParams->bssid,bssId);
    sirCopyMacAddr(pLinkStateParams->selfMacAddr, selfMacAddr);
    pLinkStateParams->ft = 1;
    pLinkStateParams->session = psessionEntry;

    msgQ.type = WDA_SET_LINK_STATE;
    msgQ.reserved = 0;
    msgQ.bodyptr = pLinkStateParams;
    msgQ.bodyval = 0;
    if(NULL == psessionEntry)
    {
        MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    }
    else
    {
        MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    }

    retCode = (tANI_U32)wdaPostCtrlMsg(pMac, &msgQ);
    if (retCode != eSIR_SUCCESS)
    {
        vos_mem_free(pLinkStateParams);
        limLog(pMac, LOGP, FL("Posting link state %d failed, reason = %x "),
               state, retCode);
    }
    return retCode;
}
#endif

/** ---------------------------------------------------------
\fn      limSendSetTxPowerReq
\brief   LIM sends a WDA_SET_TX_POWER_REQ message to WDA
\param   tpAniSirGlobal      pMac
\param   tpSirSetTxPowerReq  request message
\return  None
  -----------------------------------------------------------*/
tSirRetStatus limSendSetTxPowerReq(tpAniSirGlobal pMac,  tANI_U32 *pMsgBuf)
{
    tSirSetTxPowerReq   *txPowerReq;
    tSirRetStatus        retCode = eSIR_SUCCESS;
    tSirMsgQ             msgQ;
    tpPESession          psessionEntry;
    tANI_U8              sessionId = 0;

    if (NULL == pMsgBuf)
        return eSIR_FAILURE;

    txPowerReq = vos_mem_malloc(sizeof(tSirSetTxPowerReq));
    if ( NULL == txPowerReq )
    {
        return eSIR_FAILURE;
    }
    vos_mem_copy(txPowerReq, (tSirSetTxPowerReq *)pMsgBuf, sizeof(tSirSetTxPowerReq));

    /* Found corresponding seesion to find BSS IDX */
    psessionEntry = peFindSessionByBssid(pMac, txPowerReq->bssId, &sessionId);
    if (NULL == psessionEntry)
    {
        vos_mem_free(txPowerReq);
        limLog(pMac, LOGE, FL("Session does not exist for given BSSID"));
        return eSIR_FAILURE;
    }

    /* FW API requests BSS IDX */
    txPowerReq->bssIdx = psessionEntry->bssIdx;

    msgQ.type = WDA_SET_TX_POWER_REQ;
    msgQ.reserved = 0;
    msgQ.bodyptr = txPowerReq;
    msgQ.bodyval = 0;
    PELOGW(limLog(pMac, LOGW, FL("Sending WDA_SET_TX_POWER_REQ to WDA"));)
    MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    retCode = wdaPostCtrlMsg(pMac, &msgQ);
    if (eSIR_SUCCESS != retCode)
    {
        limLog(pMac, LOGP, FL("Posting WDA_SET_TX_POWER_REQ to WDA failed, reason=%X"), retCode);
        vos_mem_free(txPowerReq);
        return retCode;
    }

    return retCode;
}
/** ---------------------------------------------------------
\fn      limSendGetTxPowerReq
\brief   LIM sends a WDA_GET_TX_POWER_REQ message to WDA
\param   tpAniSirGlobal      pMac
\param   tpSirGetTxPowerReq  request message
\return  None
  -----------------------------------------------------------*/
tSirRetStatus limSendGetTxPowerReq(tpAniSirGlobal pMac,  tpSirGetTxPowerReq pTxPowerReq)
{
    tSirRetStatus  retCode = eSIR_SUCCESS;
    tSirMsgQ       msgQ;
    if (NULL == pTxPowerReq)
        return retCode;
    msgQ.type = WDA_GET_TX_POWER_REQ;
    msgQ.reserved = 0;
    msgQ.bodyptr = pTxPowerReq;
    msgQ.bodyval = 0;
    PELOGW(limLog(pMac, LOGW, FL( "Sending WDA_GET_TX_POWER_REQ to WDA"));)
    MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        limLog( pMac, LOGP, FL("Posting WDA_GET_TX_POWER_REQ to WDA failed, reason=%X"), retCode );
        if (NULL != pTxPowerReq)
        {
            vos_mem_free(pTxPowerReq);
        }
        return retCode;
    }
    return retCode;
}
/** ---------------------------------------------------------
\fn      limSendBeaconFilterInfo
\brief   LIM sends beacon filtering info to WDA
\param   tpAniSirGlobal  pMac
\return  None
  -----------------------------------------------------------*/
tSirRetStatus limSendBeaconFilterInfo(tpAniSirGlobal pMac,tpPESession psessionEntry)
{
    tpBeaconFilterMsg  pBeaconFilterMsg = NULL;
    tSirRetStatus      retCode = eSIR_SUCCESS;
    tSirMsgQ           msgQ;
    tANI_U8            *ptr;
    tANI_U32           i;
    tANI_U32           msgSize;
    tpBeaconFilterIe   pIe;

    if( psessionEntry == NULL )
    {
        limLog( pMac, LOGE, FL("Fail to find the right session "));
        retCode = eSIR_FAILURE;
        return retCode;
    }
    msgSize = sizeof(tBeaconFilterMsg) + sizeof(beaconFilterTable);
    pBeaconFilterMsg = vos_mem_malloc(msgSize);
    if ( NULL == pBeaconFilterMsg )
    {
        limLog( pMac, LOGP, FL("Fail to allocate memory for beaconFiilterMsg "));
        retCode = eSIR_MEM_ALLOC_FAILED;
        return retCode;
    }
    vos_mem_set((tANI_U8 *) pBeaconFilterMsg, msgSize, 0);
    // Fill in capability Info and mask
    //Don't send this message if no active Infra session is found.
    pBeaconFilterMsg->capabilityInfo = psessionEntry->limCurrentBssCaps;
    pBeaconFilterMsg->capabilityMask = CAPABILITY_FILTER_MASK;
    pBeaconFilterMsg->beaconInterval = (tANI_U16) psessionEntry->beaconParams.beaconInterval;
    // Fill in number of IEs in beaconFilterTable
    pBeaconFilterMsg->ieNum = (tANI_U16) (sizeof(beaconFilterTable) / sizeof(tBeaconFilterIe));
    //Fill the BSSIDX
    pBeaconFilterMsg->bssIdx = psessionEntry->bssIdx;

    //Fill message with info contained in the beaconFilterTable
    ptr = (tANI_U8 *)pBeaconFilterMsg + sizeof(tBeaconFilterMsg);
    for(i=0; i < (pBeaconFilterMsg->ieNum); i++)
    {
        pIe = (tpBeaconFilterIe) ptr;
        pIe->elementId = beaconFilterTable[i].elementId;
        pIe->checkIePresence = beaconFilterTable[i].checkIePresence;
        pIe->byte.offset = beaconFilterTable[i].byte.offset;
        pIe->byte.value =  beaconFilterTable[i].byte.value;
        pIe->byte.bitMask =  beaconFilterTable[i].byte.bitMask;
        pIe->byte.ref =  beaconFilterTable[i].byte.ref;
        ptr += sizeof(tBeaconFilterIe);
    }
    msgQ.type = WDA_BEACON_FILTER_IND;
    msgQ.reserved = 0;
    msgQ.bodyptr = pBeaconFilterMsg;
    msgQ.bodyval = 0;
    limLog( pMac, LOG3, FL( "Sending WDA_BEACON_FILTER_IND..." ));
    MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pBeaconFilterMsg);
        limLog( pMac, LOGP,
            FL("Posting  WDA_BEACON_FILTER_IND to WDA failed, reason=%X"),
            retCode );
        return retCode;
    }
    return retCode;
}

#ifdef WLAN_FEATURE_11AC
tSirRetStatus limSendModeUpdate(tpAniSirGlobal pMac,
                                tUpdateVHTOpMode *pTempParam,
                                tpPESession  psessionEntry )
{
    tUpdateVHTOpMode *pVhtOpMode = NULL;
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;

    pVhtOpMode = vos_mem_malloc(sizeof(tUpdateVHTOpMode));
    if ( NULL == pVhtOpMode )
    {
        limLog( pMac, LOGP,
            FL( "Unable to allocate memory during Update Op Mode" ));
        return eSIR_MEM_ALLOC_FAILED;
    }
    vos_mem_copy((tANI_U8 *)pVhtOpMode, pTempParam, sizeof(tUpdateVHTOpMode));
    msgQ.type =  WDA_UPDATE_OP_MODE;
    msgQ.reserved = 0;
    msgQ.bodyptr = pVhtOpMode;
    msgQ.bodyval = 0;
    limLog(pMac, LOG3, FL(
                "Sending WDA_UPDATE_OP_MODE, opMode - %d, staId - %d"),
                    pVhtOpMode->opMode, pVhtOpMode->staId);
    if(NULL == psessionEntry)
    {
        MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    }
    else
    {
        MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    }
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pVhtOpMode);
        limLog( pMac, LOGP,
                    FL("Posting  WDA_UPDATE_OP_MODE to WDA failed, reason=%X"),
                    retCode );
    }

    return retCode;
}

tSirRetStatus limSendRxNssUpdate(tpAniSirGlobal pMac,
                                tUpdateRxNss *pTempParam,
                                tpPESession  psessionEntry )
{
    tUpdateRxNss *pRxNss = NULL;
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;

    pRxNss = vos_mem_malloc(sizeof(tUpdateRxNss));
    if ( NULL == pRxNss )
    {
        limLog( pMac, LOGP,
            FL( "Unable to allocate memory during Update Rx Nss" ));
        return eSIR_MEM_ALLOC_FAILED;
    }
    vos_mem_copy((tANI_U8 *)pRxNss, pTempParam, sizeof(tUpdateRxNss));
    msgQ.type =  WDA_UPDATE_RX_NSS;
    msgQ.reserved = 0;
    msgQ.bodyptr = pRxNss;
    msgQ.bodyval = 0;
    PELOG3(limLog( pMac, LOG3,
                FL( "Sending WDA_UPDATE_RX_NSS" ));)
    if(NULL == psessionEntry)
    {
        MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    }
    else
    {
        MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    }
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pRxNss);
        limLog( pMac, LOGP,
                    FL("Posting WDA_UPDATE_RX_NSS to WDA failed, reason=%X"),
                    retCode );
    }

    return retCode;
}

tSirRetStatus limSetMembership(tpAniSirGlobal pMac,
                                tUpdateMembership *pTempParam,
                                tpPESession  psessionEntry )
{
    tUpdateMembership *pMembership = NULL;
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;

    pMembership = vos_mem_malloc(sizeof(tUpdateMembership));
    if ( NULL == pMembership )
    {
        limLog( pMac, LOGP,
            FL( "Unable to allocate memory during Update Membership Mode" ));
        return eSIR_MEM_ALLOC_FAILED;
    }
    vos_mem_copy((tANI_U8 *)pMembership, pTempParam, sizeof(tUpdateMembership));

    msgQ.type =  WDA_UPDATE_MEMBERSHIP;
    msgQ.reserved = 0;
    msgQ.bodyptr = pMembership;
    msgQ.bodyval = 0;
    PELOG3(limLog( pMac, LOG3,
                FL( "Sending WDA_UPDATE_MEMBERSHIP" ));)
    if(NULL == psessionEntry)
    {
        MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    }
    else
    {
        MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    }
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pMembership);
        limLog( pMac, LOGP,
                    FL("Posting  WDA_UPDATE_MEMBERSHIP to WDA failed, reason=%X"),
                    retCode );
    }

    return retCode;
}

tSirRetStatus limSetUserPos(tpAniSirGlobal pMac,
                                tUpdateUserPos *pTempParam,
                                tpPESession  psessionEntry )
{
    tUpdateUserPos *pUserPos = NULL;
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;

    pUserPos = vos_mem_malloc(sizeof(tUpdateUserPos));
    if ( NULL == pUserPos )
    {
        limLog( pMac, LOGP,
            FL( "Unable to allocate memory during Update User Position" ));
        return eSIR_MEM_ALLOC_FAILED;
    }
    vos_mem_copy((tANI_U8 *)pUserPos, pTempParam, sizeof(tUpdateUserPos));

    msgQ.type =  WDA_UPDATE_USERPOS;
    msgQ.reserved = 0;
    msgQ.bodyptr = pUserPos;
    msgQ.bodyval = 0;
    PELOG3(limLog( pMac, LOG3,
                FL( "Sending WDA_UPDATE_USERPOS" ));)
    if(NULL == psessionEntry)
    {
        MTRACE(macTraceMsgTx(pMac, NO_SESSION, msgQ.type));
    }
    else
    {
        MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    }
    if( eSIR_SUCCESS != (retCode = wdaPostCtrlMsg( pMac, &msgQ )))
    {
        vos_mem_free(pUserPos);
        limLog( pMac, LOGP,
                    FL("Posting  WDA_UPDATE_USERPOS to WDA failed, reason=%X"),
                    retCode );
    }

    return retCode;
}

#endif


#ifdef WLAN_FEATURE_11W
/** ---------------------------------------------------------
\fn      limSendExcludeUnencryptInd
\brief   LIM sends a message to HAL to indicate whether to
         ignore or indicate the unprotected packet error
\param   tpAniSirGlobal  pMac
\param   tANI_BOOLEAN excludeUnenc - true: ignore, false:
         indicate
\param   tpPESession  psessionEntry - session context
\return  status
  -----------------------------------------------------------*/
tSirRetStatus limSendExcludeUnencryptInd(tpAniSirGlobal pMac,
                                         tANI_BOOLEAN excludeUnenc,
                                         tpPESession  psessionEntry)
{
    tSirRetStatus   retCode = eSIR_SUCCESS;
    tSirMsgQ msgQ;
    tSirWlanExcludeUnencryptParam * pExcludeUnencryptParam;

    pExcludeUnencryptParam = vos_mem_malloc(sizeof(tSirWlanExcludeUnencryptParam));
    if ( NULL == pExcludeUnencryptParam )
    {
        limLog(pMac, LOGP,
            FL( "Unable to allocate memory during limSendExcludeUnencryptInd"));
        return eSIR_MEM_ALLOC_FAILED;
    }

    pExcludeUnencryptParam->excludeUnencrypt = excludeUnenc;
    sirCopyMacAddr(pExcludeUnencryptParam->bssId, psessionEntry->bssId);

    msgQ.type =  WDA_EXCLUDE_UNENCRYPTED_IND;
    msgQ.reserved = 0;
    msgQ.bodyptr = pExcludeUnencryptParam;
    msgQ.bodyval = 0;
    PELOG3(limLog(pMac, LOG3,
                FL("Sending WDA_EXCLUDE_UNENCRYPTED_IND"));)
    MTRACE(macTraceMsgTx(pMac, psessionEntry->peSessionId, msgQ.type));
    retCode = wdaPostCtrlMsg(pMac, &msgQ);
    if (eSIR_SUCCESS != retCode)
    {
        vos_mem_free(pExcludeUnencryptParam);
        limLog(pMac, LOGP,
               FL("Posting  WDA_EXCLUDE_UNENCRYPTED_IND to WDA failed, reason=%X"),
               retCode);
    }

    return retCode;
}
#endif
