/*
 * Copyright (c) 2012-2017 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.
 */

/*
 *
 * This file limPropExtsUtils.cc contains the utility functions
 * to populate, parse proprietary extensions required to
 * support ANI feature set.
 *
 * Author:        Chandra Modumudi
 * Date:          11/27/02
 * History:-
 * Date           Modified by    Modification Information
 * --------------------------------------------------------------------
 *
 */
#include "aniGlobal.h"
#include "wniCfg.h"
#include "sirCommon.h"
#include "sirDebug.h"
#include "utilsApi.h"
#include "cfgApi.h"
#include "limApi.h"
#include "limTypes.h"
#include "limUtils.h"
#include "limAssocUtils.h"
#include "limPropExtsUtils.h"
#include "limSerDesUtils.h"
#include "limTrace.h"
#include "limSession.h"
#define LIM_GET_NOISE_MAX_TRY 5
#define LIM_OPERATING_EXT_IDENTIFIER 201

/**
 * limCheckOUI() - Check if the given OUI match in IE buffer
 * @pMac: Pointer to Global MAC structure
 * @pIE: Pointer to starting IE
 * @ieLen: Length of all IEs
 *
 * Return: None
 */
static void limCheckOUI(tpAniSirGlobal pMac, tANI_U8 *pIE, tANI_U16 ieLen)
{
    tANI_U16 left = ieLen;
    tANI_U8 *ptr = pIE;
    tANI_U8 elem_id, elem_len, oui_len, i=0;

    pMac->roam.configParam.agg_btc_sco_enabled = eANI_BOOLEAN_FALSE;

    if (!ptr || ieLen == 0) {
        limLog(pMac, LOGE, FL("Invalid IEs"));
        return;
    }
    if (!pMac->roam.configParam.num_ba_buff_btc_sco)
        return;

    oui_len = 3;
    while(i < oui_len && pMac->roam.configParam.agg_btc_sco_oui[i] == 0)
          i+=1;
    if (i == oui_len) {
        /*
         * If gEnableAggBTCScoOUI ini is not set, oui is set to all
         * zeros and aggregation during SCO should be enabled for
         * all APs.
         */
        pMac->roam.configParam.agg_btc_sco_enabled = eANI_BOOLEAN_TRUE;
        return;
    }

    while (left >= 2) {
        elem_id  = ptr[0];
        elem_len = ptr[1];
        left -= 2;
        if (elem_len > left) {
            limLog(pMac, LOGE, FL("Invalid IEs eid: %d elem_len: %d left: %d"),
                   elem_id, elem_len, left);
            return;
        }
        if (SIR_MAC_EID_VENDOR == elem_id) {
            if (memcmp(&ptr[2], &pMac->roam.configParam.agg_btc_sco_oui,
                oui_len) == 0) {
                pMac->roam.configParam.agg_btc_sco_enabled = eANI_BOOLEAN_TRUE;
                return;
            }
        }
        left -= elem_len;
        ptr += (elem_len + 2);
    }
}

/**
 * limExtractApCapability()
 *
 *FUNCTION:
 * This function is called to extract AP's HCF/WME/WSM capability
 * from the IEs received from it in Beacon/Probe Response frames
 *
 *LOGIC:
 *
 *ASSUMPTIONS:
 * NA
 *
 *NOTE:
 *
 * @param   pMac      Pointer to Global MAC structure
 * @param   pIE       Pointer to starting IE in Beacon/Probe Response
 * @param   ieLen     Length of all IEs combined
 * @param   qosCap    Bits are set according to capabilities
 * @return  0 - If AP does not assert HCF capability & 1 - otherwise
 */
void
limExtractApCapability(tpAniSirGlobal pMac, tANI_U8 *pIE, tANI_U16 ieLen,
                       tANI_U8 *qosCap, tANI_U16 *propCap, tANI_U8 *uapsd, 
                       tPowerdBm *localConstraint,
                       tpPESession psessionEntry
                       )
{
    tSirProbeRespBeacon *pBeaconStruct;
#if !defined WLAN_FEATURE_VOWIFI
    tANI_U32            localPowerConstraints = 0;
#endif
    
    pBeaconStruct = vos_mem_malloc(sizeof(tSirProbeRespBeacon));

    if ( NULL == pBeaconStruct )
    {
        limLog(pMac, LOGE, FL("Unable to allocate memory in limExtractApCapability") );
        return;
    }

    vos_mem_set( (tANI_U8 *) pBeaconStruct, sizeof(tSirProbeRespBeacon), 0);
    *qosCap = 0;
    *propCap = 0;
    *uapsd = 0;
    PELOG3(limLog( pMac, LOG3,
        FL("In limExtractApCapability: The IE's being received are:"));
    sirDumpBuf( pMac, SIR_LIM_MODULE_ID, LOG3, pIE, ieLen );)
    if (sirParseBeaconIE(pMac, pBeaconStruct, pIE, (tANI_U32)ieLen) == eSIR_SUCCESS)
    {
        if (pBeaconStruct->wmeInfoPresent || pBeaconStruct->wmeEdcaPresent
            || pBeaconStruct->HTCaps.present)
            LIM_BSS_CAPS_SET(WME, *qosCap);
        if (LIM_BSS_CAPS_GET(WME, *qosCap) && pBeaconStruct->wsmCapablePresent)
            LIM_BSS_CAPS_SET(WSM, *qosCap);
        if (pBeaconStruct->propIEinfo.aniIndicator &&
            pBeaconStruct->propIEinfo.capabilityPresent)
            *propCap = pBeaconStruct->propIEinfo.capability;
        if (pBeaconStruct->HTCaps.present)
            pMac->lim.htCapabilityPresentInBeacon = 1;
        else
            pMac->lim.htCapabilityPresentInBeacon = 0;

#ifdef WLAN_FEATURE_11AC
        VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO_MED,
            "***beacon.VHTCaps.present*****=%d BSS_VHT_CAPABLE:%d",
            pBeaconStruct->VHTCaps.present,
            IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps));
        VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO_MED,
           "***beacon.SU Beamformer Capable*****=%d",pBeaconStruct->VHTCaps.suBeamFormerCap);

        if (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps)
                          && pBeaconStruct->VHTOperation.present)
        {
            psessionEntry->vhtCapabilityPresentInBeacon = 1;
            psessionEntry->apCenterChan = pBeaconStruct->VHTOperation.chanCenterFreqSeg1;
            psessionEntry->apChanWidth = pBeaconStruct->VHTOperation.chanWidth;
        }
        else
        {
            psessionEntry->vhtCapabilityPresentInBeacon = 0;
        }
#endif
        // Extract the UAPSD flag from WMM Parameter element
        if (pBeaconStruct->wmeEdcaPresent)
            *uapsd = pBeaconStruct->edcaParams.qosInfo.uapsd;

        /* Get MaxTxPwr from country IE if present.
           If the channel number field has a positive  integer value less
           than 201, then it contains a positive integer value that indicates
           the lowest channel number in the subband */

        if (pBeaconStruct->countryInfoPresent &&
            pBeaconStruct->countryInfoParam.channelTransmitPower[0].channelNumber < LIM_OPERATING_EXT_IDENTIFIER )
        {
            int i;
            tANI_U8 firstChannel =0, numChannels =0;
            tANI_U8 channel = psessionEntry->currentOperChannel;

            for (i=0; i < pBeaconStruct->countryInfoParam.numIntervals; ++i)
            {
                if (i >= COUNTRY_INFO_MAX_CHANNEL)
                    break;

                firstChannel = pBeaconStruct->countryInfoParam.channelTransmitPower[i].channelNumber;
                numChannels = pBeaconStruct->countryInfoParam.channelTransmitPower[i].numChannel;

                if ((channel >= firstChannel) &&
                    (channel < (firstChannel + numChannels)))
                    break;
            }

            if (i < pBeaconStruct->countryInfoParam.numIntervals && i < COUNTRY_INFO_MAX_CHANNEL)
            {
                *localConstraint = pBeaconStruct->countryInfoParam.channelTransmitPower[i].maxTransmitPower;
            }
        }
#if defined FEATURE_WLAN_ESE
        /* If there is Power Constraint Element specifically,
         * adapt to it. Hence there is else condition check
         * for this if statement.
         */
        if ( pBeaconStruct->eseTxPwr.present)
        {
            *localConstraint = pBeaconStruct->eseTxPwr.power_limit;
        }
#endif
        if (pBeaconStruct->powerConstraintPresent)
        {
            *localConstraint -= pBeaconStruct->localPowerConstraint.localPowerConstraints;
        }
#if !defined WLAN_FEATURE_VOWIFI
        localPowerConstraints = (tANI_U32)pBeaconStruct->localPowerConstraint.localPowerConstraints;
        if (cfgSetInt(pMac, WNI_CFG_LOCAL_POWER_CONSTRAINT, localPowerConstraints) != eSIR_SUCCESS)
        {
            limLog(pMac, LOGP, FL("Could not update local power constraint to cfg."));
        }
#endif
        psessionEntry->countryInfoPresent = FALSE; /* Initializing before first use */
        if (pBeaconStruct->countryInfoPresent)
        {
            psessionEntry->countryInfoPresent = TRUE;
        }
        /* Save the Extended caps from AP in probe resp or beacon */
        if (pBeaconStruct->ExtCap.present)
        {
            vos_mem_copy(&psessionEntry->ExtCap, &pBeaconStruct->ExtCap, sizeof(tDot11fIEExtCap));
        }

    }

    limCheckOUI(pMac, pIE, ieLen);
    /* Update HS 2.0 Information Element */
    sir_copy_hs20_ie(&psessionEntry->hs20vendor_ie,
                     &pBeaconStruct->hs20vendor_ie);
    vos_mem_free(pBeaconStruct);
    return;
} /****** end limExtractApCapability() ******/

/**
 * limGetHTCBState
 *
 *FUNCTION:
 * This routing provides the translation of Airgo Enum to HT enum for determining 
 * secondary channel offset.
 * Airgo Enum is required for backward compatibility purposes.
 *
 *
 *NOTE:
 *
 * @param  pMac - Pointer to Global MAC structure
 * @return The corresponding HT enumeration
 */
ePhyChanBondState  limGetHTCBState(ePhyChanBondState aniCBMode) 
{
    switch ( aniCBMode )
    {
#ifdef WLAN_FEATURE_11AC
        case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW:
        case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED:
        case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH:
#endif
        case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY:
        return PHY_DOUBLE_CHANNEL_HIGH_PRIMARY;
#ifdef WLAN_FEATURE_11AC
        case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW:
        case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED:
        case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH:
#endif
        case PHY_DOUBLE_CHANNEL_LOW_PRIMARY:
        return PHY_DOUBLE_CHANNEL_LOW_PRIMARY;
#ifdef WLAN_FEATURE_11AC
        case PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED:
           return PHY_SINGLE_CHANNEL_CENTERED;
#endif
        default :
           return PHY_SINGLE_CHANNEL_CENTERED;
     }
}

 /*
 * limGetStaPeerType
 *
 *FUNCTION:
 * Based on a combination of the following -
 * 1) tDphHashNode.aniPeer
 * 2) tDphHashNode.propCapability
 * this API determines if a given STA is an ANI peer or not
 *
 *LOGIC:
 *
 *ASSUMPTIONS:
 *
 *NOTE:
 *
 * @param  pMac - Pointer to Global MAC structure
 * @param  pStaDs - Pointer to the tpDphHashNode of the STA
 *         under consideration
 * @return tStaRateMode
 */
tStaRateMode limGetStaPeerType( tpAniSirGlobal pMac,
    tpDphHashNode pStaDs,
    tpPESession   psessionEntry)
{
tStaRateMode staPeerType = eSTA_11b;
  // Determine the peer-STA type
  if( pStaDs->aniPeer )
  {
    if(PROP_CAPABILITY_GET( TAURUS, pStaDs->propCapability ))
        staPeerType = eSTA_TAURUS;
    else if( PROP_CAPABILITY_GET( TITAN, pStaDs->propCapability ))
        staPeerType = eSTA_TITAN;
    else
        staPeerType = eSTA_POLARIS;
  }
#ifdef WLAN_FEATURE_11AC
  else if(pStaDs->mlmStaContext.vhtCapability)
      staPeerType = eSTA_11ac;
#endif
  else if(pStaDs->mlmStaContext.htCapability)
        staPeerType = eSTA_11n;
  else if(pStaDs->erpEnabled)
        staPeerType = eSTA_11bg;
  else if(psessionEntry->limRFBand == SIR_BAND_5_GHZ)
        staPeerType = eSTA_11a;
  return staPeerType;
}

