/*
 * Copyright (c) 2011-2014, 2016, 2018 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.
 */


/**=========================================================================

  \file  sme_Rrm.c

  \brief implementation for SME RRM APIs

  ========================================================================*/

/* $Header$ */

#if defined WLAN_FEATURE_VOWIFI
/*--------------------------------------------------------------------------
  Include Files
  ------------------------------------------------------------------------*/
#include "aniGlobal.h"
#include "smeInside.h"
#include "sme_Api.h"
#include "smsDebug.h"
#include "cfgApi.h"
#include "regdomain_common.h"

#ifdef FEATURE_WLAN_DIAG_SUPPORT
#include "vos_diag_core_event.h"
#include "vos_diag_core_log.h"
#endif /* FEATURE_WLAN_DIAG_SUPPORT */

#include "csrInsideApi.h"

#include "rrmGlobal.h"

#if defined(FEATURE_WLAN_ESE) && !defined(FEATURE_WLAN_ESE_UPLOAD)
#include "csrEse.h"
#endif

/* Roam score for a neighbor AP will be calculated based on the below definitions.
    The calculated roam score will be used to select the roam able
    candidate from neighbor AP list */
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_REACHABILITY             0   /* When we support 11r over the DS, this should have a non-zero value */
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_SECURITY                 10
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_KEY_SCOPE                20
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_SPECTRUM_MGMT 0   /* Not used */
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_QOS           5
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_APSD          3
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_RRM           8
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_DELAYED_BA    0   /* We dont support delayed BA */
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_IMMEDIATE_BA  3
#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_MOBILITY_DOMAIN          30

#ifdef FEATURE_WLAN_ESE
#define RRM_ROAM_SCORE_NEIGHBOR_IAPP_LIST                       30
#endif

v_TIME_t RRM_scan_timer;

/**---------------------------------------------------------------------------

  \brief rrmLLPurgeNeighborCache() -
    This function purges all the entries in the neighbor cache and frees up all the internal nodes

  \param  - pMac  - Pointer to the Hal Handle.
          - pList - Pointer the List that should be purged.
  \return - void

  --------------------------------------------------------------------------*/
static void rrmLLPurgeNeighborCache(tpAniSirGlobal pMac, tDblLinkList *pList)
{
    tListElem *pEntry;
    tRrmNeighborReportDesc *pNeighborReportDesc;

    csrLLLock(pList);

    while((pEntry = csrLLRemoveHead(pList, LL_ACCESS_NOLOCK)) != NULL)
    {
        pNeighborReportDesc = GET_BASE_ADDR( pEntry, tRrmNeighborReportDesc, List );
        vos_mem_free(pNeighborReportDesc->pNeighborBssDescription);
        vos_mem_free(pNeighborReportDesc);
    }

    csrLLUnlock(pList);

    return;
}

/**---------------------------------------------------------------------------

  \brief rrmIndicateNeighborReportResult() -
        This function calls the callback register by the caller while requesting for
        neighbor report. This function gets invoked if a neighbor report is received from an AP
        or neighbor response wait timer expires.

  \param  - pMac - Pointer to the Hal Handle.
          - vosStatus - VOS_STATUS_SUCCESS/VOS_STATUS_FAILURE based on whether a valid report is
            received or neighbor timer expired
  \return - void

  --------------------------------------------------------------------------*/
void rrmIndicateNeighborReportResult(tpAniSirGlobal pMac, VOS_STATUS vosStatus)
{
    NeighborReportRspCallback callback;
    void                      *callbackContext;

    /* Reset the neighbor response pending status */
    pMac->rrm.rrmSmeContext.neighborReqControlInfo.isNeighborRspPending = eANI_BOOLEAN_FALSE;

    /* Stop the timer if it is already running. The timer should be running only in the SUCCESS case. */
    if (VOS_TIMER_STATE_RUNNING == vos_timer_getCurrentState(&pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspWaitTimer))
    {
        smsLog( pMac, LOG1, FL("No entry in neighbor report cache"));
        vos_timer_stop(&pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspWaitTimer);
    }
    callback = pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspCallbackInfo.neighborRspCallback;
    callbackContext = pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspCallbackInfo.neighborRspCallbackContext;

    /* Reset the callback and the callback context before calling the callback. It is very likely that there may be a registration in
            callback itself. */
    pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspCallbackInfo.neighborRspCallback = NULL;
    pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspCallbackInfo.neighborRspCallbackContext = NULL;

    /* Call the callback with the status received from caller */
    if (callback)
        callback(callbackContext, vosStatus);
#if defined(FEATURE_WLAN_ESE) && !defined(FEATURE_WLAN_ESE_UPLOAD)
    // We came here with IAPP AP List
    // Make sure we inform CSR of the neighbor list
    // for ESE Associations. First clear the cache.
    else
    if (csrNeighborRoamIsESEAssoc(pMac))
    {
        ProcessIAPPNeighborAPList(pMac);
    }
#endif

    return;

}

/**---------------------------------------------------------------------------

  \brief sme_RrmBeaconReportXmitInd() -

   Create and send the beacon report Xmit ind message to PE.

  \param  - pMac - Pointer to the Hal Handle.
              - pResult - scan result.
              - measurementDone - flag to indicate that the measurement is done.
  \return - 0 for success, non zero for failure

  --------------------------------------------------------------------------*/
static eHalStatus sme_RrmSendBeaconReportXmitInd( tpAniSirGlobal pMac,
                                                  tCsrScanResultInfo **pResultArr,
                                                  tANI_U8 measurementDone,
                                                  tANI_U8 bss_count )
{
   tpSirBssDescription pBssDesc = NULL;
   tpSirBeaconReportXmitInd pBeaconRep;
   tANI_U16 length, ie_len, tot_len;
   tANI_U8 bssCounter=0, msgCounter=0;
   tCsrScanResultInfo *pCurResult=NULL;
   eHalStatus status = eHAL_STATUS_FAILURE;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;


#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, "Beacon report xmit Ind to PE");
#endif

   if( NULL == pResultArr && !measurementDone )
   {
      smsLog( pMac, LOGE, "Beacon report xmit Ind to PE Failed");
      return eHAL_STATUS_FAILURE;
   }

   if (pResultArr)
       pCurResult=pResultArr[bssCounter];

   do
   {
       length = sizeof(tSirBeaconReportXmitInd);
       pBeaconRep = vos_mem_malloc ( length );
       if ( NULL == pBeaconRep )
       {
          smsLog( pMac, LOGP, "Unable to allocate memory for beacon report");
          return eHAL_STATUS_FAILED_ALLOC;
       }
       vos_mem_zero( pBeaconRep, length );
#if defined WLAN_VOWIFI_DEBUG
       smsLog( pMac, LOGE, FL("Allocated memory for pBeaconRep"));
#endif
       pBeaconRep->messageType = eWNI_SME_BEACON_REPORT_RESP_XMIT_IND;
       pBeaconRep->length = length;
       pBeaconRep->uDialogToken = pSmeRrmContext->token;
       pBeaconRep->duration = pSmeRrmContext->duration[0];
       pBeaconRep->regClass = pSmeRrmContext->regClass;
       vos_mem_copy( pBeaconRep->bssId, pSmeRrmContext->sessionBssId, sizeof(tSirMacAddr) );

       msgCounter=0;
       while (pCurResult)
       {
           pBssDesc = &pCurResult->BssDescriptor;
           if(pBssDesc != NULL)
           {
               ie_len = GET_IE_LEN_IN_BSS( pBssDesc->length );
               tot_len = ie_len+sizeof(tSirBssDescription);
               pBeaconRep->pBssDescription[msgCounter] =
                                      vos_mem_malloc(tot_len);
               if (NULL == pBeaconRep->pBssDescription[msgCounter])
                   break;
               vos_mem_zero(pBeaconRep->pBssDescription[msgCounter],
                                                             tot_len);
               vos_mem_copy( pBeaconRep->pBssDescription[msgCounter],
                             pBssDesc,
                             sizeof(tSirBssDescription) );
               vos_mem_copy( &pBeaconRep->pBssDescription[msgCounter]->ieFields[0],
                             pBssDesc->ieFields, ie_len  );
               smsLog( pMac, LOG1,
                   "...RRM Result Bssid = "MAC_ADDRESS_STR" chan= %d, rssi = -%d",
                   MAC_ADDR_ARRAY(pBeaconRep->pBssDescription[msgCounter]->bssId),
                   pBeaconRep->pBssDescription[msgCounter]->channelId,
                   pBeaconRep->pBssDescription[msgCounter]->rssi * (-1));

               pBeaconRep->numBssDesc++;

               if (++msgCounter >= SIR_BCN_REPORT_MAX_BSS_DESC)
                   break;

               pCurResult = pResultArr[bssCounter + msgCounter];
           }
           else
           {
               pCurResult = NULL;
               break;
           }
       }

       bssCounter+=msgCounter;
       if (!pResultArr || (pCurResult == NULL) || (bssCounter >= bss_count))
       {
           pCurResult = NULL;
           smsLog(pMac, LOG1,
                  "Reached to the max/last BSS in pCurResult list");
       }
       else
       {
           pCurResult = pResultArr[bssCounter];
           smsLog(pMac, LOG1,
                  "Move to the next BSS set in pCurResult list");
       }

       pBeaconRep->fMeasureDone = (pCurResult)?false:measurementDone;

       smsLog(pMac, LOG1,
              "SME Sending BcnRepXmit to PE numBss %d msgCounter %d bssCounter %d",
              pBeaconRep->numBssDesc, msgCounter, bssCounter);

       status = palSendMBMessage(pMac->hHdd, pBeaconRep);

   } while (pCurResult);

   return status;
}

#if defined(FEATURE_WLAN_ESE_UPLOAD)
/**---------------------------------------------------------------------------

  \brief sme_EseSendBeaconReqScanResults()

   This function sends up the scan results received as a part of
   beacon request scanning.
   This function is called after receiving the scan results per channel
   Due to the limitation on the size of the IWEVCUSTOM buffer, we send 3 BSSIDs of
   beacon report information in one custom event;

  \param  - pMac -      Pointer to the Hal Handle.
              - sessionId  - Session id
              - channel     - scan results belongs to this channel
              - pResultArr - scan result.
              - measurementDone - flag to indicate that the measurement is done.
              - bss_count - number of bss found
  \return - 0 for success, non zero for failure

  --------------------------------------------------------------------------*/
static eHalStatus sme_EseSendBeaconReqScanResults(tpAniSirGlobal pMac,
                                                  tANI_U32       sessionId,
                                                  tANI_U8        channel,
                                                  tCsrScanResultInfo **pResultArr,
                                                  tANI_U8        measurementDone,
                                                  tANI_U8        bss_count)
{
   eHalStatus              status         = eHAL_STATUS_FAILURE;
   tSirRetStatus           fillIeStatus;
   tpSirBssDescription     pBssDesc       = NULL;
   tANI_U32                ie_len         = 0;
   tANI_U32                outIeLen       = 0;
   tANI_U8                 bssCounter     = 0;
   tCsrScanResultInfo     *pCurResult     = NULL;
   tANI_U8                 msgCounter     = 0;
   tpRrmSMEContext         pSmeRrmContext = &pMac->rrm.rrmSmeContext;
   tCsrRoamInfo            *roam_info;
   tSirEseBcnReportRsp     bcnReport;
   tpSirEseBcnReportRsp    pBcnReport     = &bcnReport;
   tpCsrEseBeaconReqParams pCurMeasReqIe  = NULL;
   tANI_U8                 i              = 0;

   if (NULL == pSmeRrmContext)
   {
       smsLog( pMac, LOGE, "pSmeRrmContext is NULL");
       return eHAL_STATUS_FAILURE;
   }

   if (NULL == pResultArr && !measurementDone)
   {
      smsLog( pMac, LOGE, "Beacon report xmit Ind to HDD Failed");
      return eHAL_STATUS_FAILURE;
   }

   roam_info = vos_mem_malloc(sizeof(*roam_info));
   if (!roam_info)
      return eHAL_STATUS_FAILED_ALLOC;
   if (pResultArr)
       pCurResult=pResultArr[bssCounter];

   vos_mem_zero(&bcnReport, sizeof(tSirEseBcnReportRsp));
   do
   {
       pCurMeasReqIe = NULL;
       for (i = 0; i < pSmeRrmContext->eseBcnReqInfo.numBcnReqIe; i++)
       {
           if(pSmeRrmContext->eseBcnReqInfo.bcnReq[i].channel == channel)
           {
               pCurMeasReqIe = &pSmeRrmContext->eseBcnReqInfo.bcnReq[i];
               break;
           }
       }
       if(NULL != pCurMeasReqIe)
           pBcnReport->measurementToken = pCurMeasReqIe->measurementToken;
       smsLog( pMac, LOG1, "Channel(%d) MeasToken(%d)", channel, pBcnReport->measurementToken);

       msgCounter=0;
       while (pCurResult)
       {
           pBssDesc = &pCurResult->BssDescriptor;
           if (NULL != pBssDesc)
           {
               ie_len = GET_IE_LEN_IN_BSS( pBssDesc->length );
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.ChanNum = pBssDesc->channelId;
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.Spare = 0;
               if(NULL != pCurMeasReqIe)
                   pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.MeasDuration = pCurMeasReqIe->measurementDuration;
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.PhyType = pBssDesc->nwType;
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.RecvSigPower = pBssDesc->rssi;
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.ParentTsf = pBssDesc->parentTSF;
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.TargetTsf[0] = pBssDesc->timeStamp[0];
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.TargetTsf[1] = pBssDesc->timeStamp[1];
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.BcnInterval = pBssDesc->beaconInterval;
               pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.CapabilityInfo = pBssDesc->capabilityInfo;
               vos_mem_copy(pBcnReport->bcnRepBssInfo[msgCounter].bcnReportFields.Bssid,
                                      pBssDesc->bssId, sizeof(tSirMacAddr));

               fillIeStatus = sirFillBeaconMandatoryIEforEseBcnReport(pMac,
                              (tANI_U8 *)pBssDesc->ieFields,
                              ie_len,
                              &(pBcnReport->bcnRepBssInfo[msgCounter].pBuf),
                              &outIeLen);
               if (eSIR_FAILURE == fillIeStatus)
               {
                       continue;
               }
               pBcnReport->bcnRepBssInfo[msgCounter].ieLen = outIeLen;

               smsLog( pMac, LOG1,"Bssid("MAC_ADDRESS_STR") Channel=%d Rssi=%d",
                       MAC_ADDR_ARRAY(pBssDesc->bssId),
                       pBssDesc->channelId, (-1) * pBssDesc->rssi);

               pBcnReport->numBss++;

               if (++msgCounter >= SIR_BCN_REPORT_MAX_BSS_DESC)
                   break;

               pCurResult = pResultArr[msgCounter];
           }
           else
           {
               pCurResult = NULL;
               break;
           }
       }

       bssCounter += msgCounter;
       if (!pResultArr || !pCurResult || (bssCounter >= SIR_BCN_REPORT_MAX_BSS_DESC))
       {
           pCurResult = NULL;
           smsLog(pMac, LOGE,
                  "Reached to the max/last BSS in pCurResult list");
       }
       else
       {
           pCurResult = pResultArr[bssCounter];
           smsLog(pMac, LOGE,
                  "Move to the next BSS set in pCurResult list");
       }

       pBcnReport->flag = (measurementDone << 1)|((pCurResult)?true:false);

       smsLog(pMac, LOG1, "SME Sending BcnRep to HDD numBss(%d)"
               " msgCounter(%d) bssCounter(%d) flag(%d)",
                pBcnReport->numBss, msgCounter, bssCounter, pBcnReport->flag);

       roam_info->pEseBcnReportRsp = pBcnReport;
       status = csrRoamCallCallback(pMac, sessionId, roam_info,
                           0, eCSR_ROAM_ESE_BCN_REPORT_IND, 0);

       /* Free the memory allocated to IE */
       for (i = 0; i < msgCounter; i++)
       {
           if (pBcnReport->bcnRepBssInfo[i].pBuf)
               vos_mem_free(pBcnReport->bcnRepBssInfo[i].pBuf);
       }
   } while (pCurResult);
   vos_mem_free(roam_info);

   return status;
}

#endif /* FEATURE_WLAN_ESE_UPLOAD */

/**---------------------------------------------------------------------------

  \brief sme_RrmSendScanRequest() -

   This function is called to get the scan result from CSR and send the beacon report
   xmit ind message to PE.

  \param  - pMac - Pointer to the Hal Handle.
              - num_chan - number of channels.
              - channel list - list of channels to fetch the result from.
              - measurementDone - flag to indicate that the measurement is done.
  \return - 0 for success, non zero for failure

  --------------------------------------------------------------------------*/
static eHalStatus sme_RrmSendScanResult( tpAniSirGlobal pMac,
                                         tANI_U8 num_chan,
                                         tANI_U8* chanList,
                                         tANI_U8 measurementDone )
{
   tCsrScanResultFilter filter;
   tScanResultHandle pResult;
   tCsrScanResultInfo *pScanResult, *pNextResult;
   tCsrScanResultInfo *pScanResultsArr[SIR_BCN_REPORT_MAX_BSS_DESC];
   eHalStatus status;
   tANI_U8 counter=0;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;
   tANI_U32 sessionId;
   tCsrRoamInfo *roam_info;
   tSirScanType scan_type;

#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, "Send scan result to PE ");
#endif

   vos_mem_zero( &filter, sizeof(filter) );
   vos_mem_zero( pScanResultsArr, sizeof(pNextResult)*SIR_BCN_REPORT_MAX_BSS_DESC );

   filter.BSSIDs.numOfBSSIDs = 1;
   filter.BSSIDs.bssid = &pSmeRrmContext->bssId;

   if( pSmeRrmContext->ssId.length )
   {
      filter.SSIDs.SSIDList =( tCsrSSIDInfo *)vos_mem_malloc(sizeof(tCsrSSIDInfo));
      if( filter.SSIDs.SSIDList == NULL )
      {
         smsLog( pMac, LOGP, FL("vos_mem_malloc failed:") );
         return eHAL_STATUS_FAILURE;
      }
#if defined WLAN_VOWIFI_DEBUG
      smsLog( pMac, LOGE, FL("Allocated memory for SSIDList"));
#endif
      vos_mem_zero( filter.SSIDs.SSIDList, sizeof(tCsrSSIDInfo) );

      filter.SSIDs.SSIDList->SSID.length = pSmeRrmContext->ssId.length;
      vos_mem_copy(filter.SSIDs.SSIDList->SSID.ssId, pSmeRrmContext->ssId.ssId, pSmeRrmContext->ssId.length);
      filter.SSIDs.numOfSSIDs = 1;
   }
   else
   {
      filter.SSIDs.numOfSSIDs = 0;
   }

   filter.ChannelInfo.numOfChannels = num_chan;
   filter.ChannelInfo.ChannelList = chanList;

   filter.fMeasurement = TRUE;

   /*
    * In case this is beacon report request from last AP (before roaming)
    * following call to csrRoamGetSessionIdFromBSSID will fail, hence use
    * current session ID instead of one stored in SME rrm context
    */
   if (eHAL_STATUS_FAILURE ==
        csrRoamGetSessionIdFromBSSID(pMac,
                                     (tCsrBssid*)pSmeRrmContext->sessionBssId,
                                      &sessionId )) {
       smsLog( pMac, LOG1, FL("BSSID mismatch, using current sessionID"));
       sessionId = pMac->roam.roamSession->sessionId;
   }

   status = sme_ScanGetResult(pMac, (tANI_U8)sessionId, &filter, &pResult);

   if( filter.SSIDs.SSIDList )
   {
      //Free the memory allocated for SSIDList.
      vos_mem_free( filter.SSIDs.SSIDList );
#if defined WLAN_VOWIFI_DEBUG
      smsLog( pMac, LOGE, FL("Free memory for SSIDList") );
#endif
   }

   smsLog(pMac, LOG1, FL("RRM Measurement Done %d"), measurementDone);
   if (NULL == pResult)
   {
      /*
       * no scan results
       *
       * Spec. doesnt say anything about such condition.
       * Since section 7.4.6.2 (IEEE802.11k-2008) says-rrm report frame should
       * contain one or more report IEs. It probably means dont send any
       * response if no matching BSS found. Moreover, there is no flag or
       * field in measurement report IE(7.3.2.22) OR beacon report
       * IE(7.3.2.22.6) that can be set to indicate no BSS found on a
       * given channel.
       *
       * If we finished measurement on all the channels, we still need to
       * send a xmit indication with moreToFollow set to MEASURMENT_DONE
       * so that PE can clean any context allocated.
       */
      if( measurementDone )
      {
#if defined(FEATURE_WLAN_ESE_UPLOAD)
         if (eRRM_MSG_SOURCE_ESE_UPLOAD == pSmeRrmContext->msgSource)
         {
             status = sme_EseSendBeaconReqScanResults(pMac,
                                                  sessionId,
                                                  chanList[0],
                                                  NULL,
                                                  measurementDone,
                                                  0);
         }
         else
#endif /*FEATURE_WLAN_ESE_UPLOAD*/
             status = sme_RrmSendBeaconReportXmitInd( pMac, NULL, measurementDone, 0);
      }
      return status;
   }

   pScanResult = sme_ScanResultGetFirst(pMac, pResult);

   if( NULL == pScanResult && measurementDone )
   {
#if defined(FEATURE_WLAN_ESE_UPLOAD)
       if (eRRM_MSG_SOURCE_ESE_UPLOAD == pSmeRrmContext->msgSource)
       {
           status = sme_EseSendBeaconReqScanResults(pMac,
                                               sessionId,
                                               chanList[0],
                                               NULL,
                                               measurementDone,
                                               0);
       }
       else
#endif /*FEATURE_WLAN_ESE_UPLOAD*/
           status = sme_RrmSendBeaconReportXmitInd( pMac, NULL, measurementDone, 0 );
   }
   if (eRRM_MSG_SOURCE_ESE_UPLOAD == pSmeRrmContext->msgSource ||
       eRRM_MSG_SOURCE_LEGACY_ESE == pSmeRrmContext->msgSource)
       scan_type = pSmeRrmContext->measMode[pSmeRrmContext->currentIndex];
   else
       scan_type = pSmeRrmContext->measMode[0];

   counter=0;
   while (pScanResult)
   {
      /*
       * In passive scan, sta listens beacon. Connected AP beacon
       * is offloaded to firmware. Firmware will discard
       * connected AP beacon except that special IE exists.
       * Connected AP beacon will not be sent to host. Hence, timer
       * of connected AP in scan results is not updated and can
       * not meet "pScanResult->timer >= RRM_scan_timer".
       */
      tCsrRoamSession *session;
      uint8_t is_conn_bss_found = false;

      if (scan_type == eSIR_PASSIVE_SCAN) {
          session = CSR_GET_SESSION(pMac, sessionId);
         if (csrIsConnStateConnectedInfra(pMac, sessionId) &&
             (NULL != session->pConnectBssDesc) &&
             (csrIsDuplicateBssDescription(pMac, &pScanResult->BssDescriptor,
             session->pConnectBssDesc, NULL, FALSE))) {
             is_conn_bss_found = true;
             smsLog(pMac, LOG1, "Connected BSS in scan results");
         }
      }
      pNextResult = sme_ScanResultGetNext(pMac, pResult);
      smsLog(pMac, LOG1, "Scan res timer:%lu, rrm scan timer:%lu",
             pScanResult->timer, RRM_scan_timer);
      if ((pScanResult->timer >= RRM_scan_timer) || (is_conn_bss_found == true))
      {
          roam_info = vos_mem_malloc(sizeof(*roam_info));
          if (NULL == roam_info) {
              smsLog( pMac, LOGP, FL("vos_mem_malloc failed:") );
              status =  eHAL_STATUS_FAILED_ALLOC;
              goto rrm_send_scan_results_done;
          }
          vos_mem_zero(roam_info, sizeof(*roam_info));
          roam_info->pBssDesc = &pScanResult->BssDescriptor;
          csrRoamCallCallback(pMac, sessionId, roam_info, 0,
                           eCSR_ROAM_UPDATE_SCAN_RESULT, eCSR_ROAM_RESULT_NONE);
          vos_mem_free(roam_info);
          pScanResultsArr[counter++] = pScanResult;
      }
      pScanResult = pNextResult; //sme_ScanResultGetNext(hHal, pResult);
      if (counter >= SIR_BCN_REPORT_MAX_BSS_DESC)
         break;
   }

   smsLog(pMac, LOG1, " Number of BSS Desc with RRM Scan %d ", counter);
   /*
    * The beacon report should be sent whether the counter is zero or non-zero.
    * There might be a few scan results in the cache but not actually are a
    * result of this scan. During that scenario, the counter will be zero.
    * The report should be sent and LIM will further cleanup the RRM to
    * accept the further incoming requests
    * In case the counter is Zero, the pScanResultsArr will be NULL.
    * The next level routine does a check for the measurementDone to determine
    * whether to send a report or not.
    */
   if (counter || measurementDone) {
#if defined(FEATURE_WLAN_ESE_UPLOAD)
        if (eRRM_MSG_SOURCE_ESE_UPLOAD == pSmeRrmContext->msgSource)
            status = sme_EseSendBeaconReqScanResults(pMac, sessionId,
                                    chanList[0], pScanResultsArr,
                                    measurementDone, counter);
   else
#endif
            status = sme_RrmSendBeaconReportXmitInd(pMac,
                                    pScanResultsArr,
                                    measurementDone, counter);
   }

rrm_send_scan_results_done:
   sme_ScanResultPurge(pMac, pResult);

   return status;
}
/**---------------------------------------------------------------------------

  \brief sme_RrmScanRequestCallback() -

   The sme module calls this callback function once it finish the scan request
   and this function send the beacon report xmit to PE and starts a timer of
   random interval to issue next request.

  \param  - halHandle - Pointer to the Hal Handle.
              - pContext - Pointer to the data context.
              - scanId - Scan ID.
              - status - CSR Status.
  \return - 0 for success, non zero for failure

  --------------------------------------------------------------------------*/

static eHalStatus sme_RrmScanRequestCallback(tHalHandle halHandle,
                                             void *pContext,
                                             tANI_U8 sessionId,
                                             tANI_U32 scanId,
                                             eCsrScanStatus status)
{

   tANI_U16 interval;
   tpAniSirGlobal pMac = (tpAniSirGlobal) halHandle;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;
   tANI_U32 time_tick;



#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, "Scan Request callback ");
#endif
   //if any more channels are pending, start a timer of a random value within randomization interval.
   //
   //
   if( (pSmeRrmContext->currentIndex + 1) < pSmeRrmContext->channelList.numOfChannels )
   {
      sme_RrmSendScanResult( pMac, 1, &pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->currentIndex], false );

      pSmeRrmContext->currentIndex++; //Advance the current index.

      /* Start the timer to issue next request. From timer tick get a random
       number within 10ms and max randomization interval. */
      time_tick = vos_timer_get_system_ticks();
      interval = time_tick % (pSmeRrmContext->randnIntvl - 10 + 1) + 10;

#if defined WLAN_VOWIFI_DEBUG
      smsLog( pMac, LOGE, "Set timer for interval %d ", interval);
#endif
      vos_timer_start( &pSmeRrmContext->IterMeasTimer, interval );

   }
   else
   {
      //Done with the measurement. Clean up all context and send a message to PE with measurement done flag set.
      sme_RrmSendScanResult( pMac, 1, &pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->currentIndex], true );
      vos_mem_free( pSmeRrmContext->channelList.ChannelList );
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
      pSmeRrmContext->eseBcnReqInProgress = FALSE;
#endif
#if defined WLAN_VOWIFI_DEBUG
      smsLog( pMac, LOGE, FL("Free memory for ChannelList") );
#endif
   }

   return eHAL_STATUS_SUCCESS;
}

/*--------------------------------------------------------------------------
  \brief sme_RrmIssueScanReq() - This is called to send a scan request as part
         of beacon report request .

  \param  pMac  - pMac global pointer

  \return eHAL_STATUS_SUCCESS - Validation is successful.

  \sa

  --------------------------------------------------------------------------*/
eHalStatus sme_RrmIssueScanReq( tpAniSirGlobal pMac )
{
   eHalStatus status = eHAL_STATUS_SUCCESS;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;
   tANI_U32 sessionId;
   tSirScanType scanType;

   status = csrRoamGetSessionIdFromBSSID( pMac, (tCsrBssid*)pSmeRrmContext->sessionBssId, &sessionId );
   if( status != eHAL_STATUS_SUCCESS )
   {
       smsLog( pMac, LOGE, "%s : Invalid sme Session ID", __func__);
       return eHAL_STATUS_FAILURE;
   }

   if ((pSmeRrmContext->currentIndex) >= pSmeRrmContext->channelList.numOfChannels)
       return status;

   if( eRRM_MSG_SOURCE_ESE_UPLOAD == pSmeRrmContext->msgSource ||
       eRRM_MSG_SOURCE_LEGACY_ESE == pSmeRrmContext->msgSource )
       scanType = pSmeRrmContext->measMode[pSmeRrmContext->currentIndex];
   else
       scanType = pSmeRrmContext->measMode[0];

   if ((eSIR_ACTIVE_SCAN == scanType) || (eSIR_PASSIVE_SCAN == scanType))
   {
       tCsrScanRequest scanRequest;
       v_U32_t scanId = 0;
       v_TIME_t current_time;

#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, "Issue scan request " );
#endif

       vos_mem_zero( &scanRequest, sizeof(scanRequest));

       /* set scanType, active or passive */
       scanRequest.scanType = scanType;

       vos_mem_copy(scanRequest.bssid,
             pSmeRrmContext->bssId, sizeof(scanRequest.bssid) );

       if (pSmeRrmContext->ssId.length)
       {
          scanRequest.SSIDs.numOfSSIDs = 1;
          scanRequest.SSIDs.SSIDList =( tCsrSSIDInfo *)vos_mem_malloc(sizeof(tCsrSSIDInfo));
          if (NULL == scanRequest.SSIDs.SSIDList)
          {
              smsLog( pMac, LOGP, FL("vos_mem_malloc failed:") );
              return eHAL_STATUS_FAILURE;
          }
#if defined WLAN_VOWIFI_DEBUG
          smsLog( pMac, LOGE, FL("Allocated memory for pSSIDList"));
#endif
          vos_mem_zero( scanRequest.SSIDs.SSIDList, sizeof(tCsrSSIDInfo) );
          scanRequest.SSIDs.SSIDList->SSID.length = pSmeRrmContext->ssId.length;
          vos_mem_copy(scanRequest.SSIDs.SSIDList->SSID.ssId, pSmeRrmContext->ssId.ssId, pSmeRrmContext->ssId.length);
       }

       /* set min and max channel time */
       scanRequest.minChnTime = 0; //pSmeRrmContext->duration; Dont use min timeout.
       if( eRRM_MSG_SOURCE_ESE_UPLOAD == pSmeRrmContext->msgSource ||
           eRRM_MSG_SOURCE_LEGACY_ESE == pSmeRrmContext->msgSource )
           scanRequest.maxChnTime = pSmeRrmContext->duration[pSmeRrmContext->currentIndex];
       else
           scanRequest.maxChnTime = pSmeRrmContext->duration[0];

       smsLog( pMac, LOG1, "Scan Type(%d) Max Dwell Time(%d)", scanRequest.scanType,
                  scanRequest.maxChnTime );
      /**
       * For RRM scans timing is very important especially when the request
       * is for limited channels. There is no need for firmware to rest for
       * about 100-200 ms on the home channel. Instead, it can start the
       * scan right away which will make the host to respond with the beacon
       * report as quickly as possible. Ensure that the scan requests are
       * not back to back and hence there is a check to see if the requests
       * are atleast 1 second apart.
       */
       current_time = vos_timer_get_system_time();
       smsLog(pMac, LOG1, "prev scan triggered before %ld ms, totalchannels %d",
              current_time - RRM_scan_timer,
              pSmeRrmContext->channelList.numOfChannels);
       if ((abs(current_time - RRM_scan_timer) > 1000) &&
               (pSmeRrmContext->channelList.numOfChannels == 1)) {
            scanRequest.restTime = 1;
            scanRequest.min_rest_time = 1;
            scanRequest.idle_time = 1;
       }

       RRM_scan_timer = vos_timer_get_system_time();

#if defined WLAN_VOWIFI_DEBUG
       smsLog( pMac, LOGE, "For Duration %d ", scanRequest.maxChnTime );
#endif

       /* set BSSType to default type */
       scanRequest.BSSType = eCSR_BSS_TYPE_ANY;

       /*Scan all the channels */
       scanRequest.ChannelInfo.numOfChannels = 1;

       scanRequest.ChannelInfo.ChannelList = &pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->currentIndex];
#if defined WLAN_VOWIFI_DEBUG
       smsLog( pMac, LOGE, "On channel %d ", pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->currentIndex] );
#endif

       /* set requestType to full scan */
       scanRequest.requestType = eCSR_SCAN_REQUEST_FULL_SCAN;

       status = sme_ScanRequest( pMac, (tANI_U8)sessionId, &scanRequest, &scanId, &sme_RrmScanRequestCallback, NULL );

       if ( pSmeRrmContext->ssId.length )
       {
           vos_mem_free(scanRequest.SSIDs.SSIDList);
#if defined WLAN_VOWIFI_DEBUG
           smsLog( pMac, LOGE, FL("Free memory for SSIDList"));
#endif
       }
   }
   else if (eSIR_BEACON_TABLE == scanType)  /* beacon table */
   {
       /*In beacon table mode, scan results are taken directly from scan cache
         without issuing any scan request. So, it is not proper to update
         RRM_scan_timer with latest time and hence made it to zero to satisfy
         pScanResult->timer >= RRM_scan_timer  */
       RRM_scan_timer = 0;

       if ((pSmeRrmContext->currentIndex + 1) < pSmeRrmContext->channelList.numOfChannels)
       {
           sme_RrmSendScanResult( pMac, 1, &pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->currentIndex], false );
           pSmeRrmContext->currentIndex++; //Advance the current index.
           sme_RrmIssueScanReq(pMac);
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
           pSmeRrmContext->eseBcnReqInProgress = FALSE;
#endif
       }
       else
       {
           //Done with the measurement. Clean up all context and send a message to PE with measurement done flag set.
           sme_RrmSendScanResult( pMac, 1, &pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->currentIndex], true );
           vos_mem_free( pSmeRrmContext->channelList.ChannelList );
       }
   }
   else
   {
       smsLog( pMac, LOGE, "Unknown beacon report request mode(%d)", scanType);
                /* Indicate measurement completion to PE */
                /* If this is not done, pCurrentReq pointer will not be freed and
                   PE will not handle subsequent Beacon requests */
        sme_RrmSendBeaconReportXmitInd(pMac, NULL, true, 0);
   }

   return status;
}

/*--------------------------------------------------------------------------
  \brief sme_RrmProcessBeaconReportReqInd() - This is called to process the Beacon
         report request from peer AP forwarded through PE .

  \param pMsgBuf - a pointer to a buffer that maps to various structures base
                   on the message type.
                   The beginning of the buffer can always map to tSirSmeRsp.

  \return eHAL_STATUS_SUCCESS - Validation is successful.

  \sa

  --------------------------------------------------------------------------*/
eHalStatus sme_RrmProcessBeaconReportReqInd(tpAniSirGlobal pMac, void *pMsgBuf)
{
   tpSirBeaconReportReqInd pBeaconReq = (tpSirBeaconReportReqInd) pMsgBuf;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;
   tANI_U32 len = 0, i = 0, temp = 0;
   eHalStatus status = eHAL_STATUS_SUCCESS;

#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, "Received Beacon report request ind Channel = %d", pBeaconReq->channelInfo.channelNum );
#endif

   if (pBeaconReq->channelList.numChannels > SIR_ESE_MAX_MEAS_IE_REQS) {
         smsLog( pMac, LOGP, "Beacon report request numChannels: %u exceeds "
                "max num channels", pBeaconReq->channelList.numChannels);
         return eHAL_STATUS_FAILURE;
   }
   //section 11.10.8.1 (IEEE Std 802.11k-2008)
   //channel 0 and 255 has special meaning.
   if( (pBeaconReq->channelInfo.channelNum == 0)  ||
       ((pBeaconReq->channelInfo.channelNum == 255) && (pBeaconReq->channelList.numChannels == 0) ) )
   {
      //Add all the channel in the regulatory domain.
      wlan_cfgGetStrLen( pMac, WNI_CFG_VALID_CHANNEL_LIST, &len );
      pSmeRrmContext->channelList.ChannelList = vos_mem_malloc( len );
      if( pSmeRrmContext->channelList.ChannelList == NULL )
      {
         smsLog( pMac, LOGP, FL("vos_mem_malloc failed:") );
         return eHAL_STATUS_FAILURE;
      }
#if defined WLAN_VOWIFI_DEBUG
      smsLog( pMac, LOGE, FL("Allocated memory for ChannelList") );
#endif
      csrGetCfgValidChannels( pMac, pSmeRrmContext->channelList.ChannelList, &len );

      for (i = 0 ; i <len; i++)
      {
          if (regdm_get_opclass_from_channel(pMac->scan.countryCodeCurrent,
              pSmeRrmContext->channelList.ChannelList[i], BWALL) ==
              pBeaconReq->channelInfo.regulatoryClass)
          {
              pSmeRrmContext->channelList.ChannelList[temp] =
                                                 pSmeRrmContext->channelList.ChannelList[i];
              temp++;
          }
      }
      len = temp;
      pSmeRrmContext->channelList.numOfChannels = (tANI_U8)len;
#if defined WLAN_VOWIFI_DEBUG
      smsLog( pMac, LOGE, "channel == 0 performing on all channels");
#endif
   }
   else
   {
      len = 0;
      pSmeRrmContext->channelList.numOfChannels = 0;

      //If valid channel is present. We first Measure on the given channel. and
      //if there are additional channels present in APchannelreport, measure on these also.
      if ( pBeaconReq->channelInfo.channelNum != 255 )
         len = 1;
#if defined WLAN_VOWIFI_DEBUG
      else
         smsLog( pMac, LOGE, "channel == 255");
#endif

      len += pBeaconReq->channelList.numChannels;

      pSmeRrmContext->channelList.ChannelList = vos_mem_malloc( len );
      if( pSmeRrmContext->channelList.ChannelList == NULL )
      {
         smsLog( pMac, LOGP, FL("vos_mem_malloc failed") );
         return eHAL_STATUS_FAILURE;
      }
#if defined WLAN_VOWIFI_DEBUG
      smsLog( pMac, LOGE, FL("Allocated memory for ChannelList") );
#endif

      if ( pBeaconReq->channelInfo.channelNum != 255 )
      {
#if defined WLAN_VOWIFI_DEBUG
         smsLog( pMac, LOGE, "channel == %d  ", pBeaconReq->channelInfo.channelNum );
#endif
         if(csrRoamIsChannelValid( pMac, pBeaconReq->channelInfo.channelNum ))
            pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->channelList.numOfChannels++] = pBeaconReq->channelInfo.channelNum;
#if defined WLAN_VOWIFI_DEBUG
         else
            smsLog( pMac, LOGE, "is Invalid channel, Ignoring this channel" );
#endif
      }

      for ( i = 0 ; i < pBeaconReq->channelList.numChannels; i++ )
      {
         if(csrRoamIsChannelValid( pMac, pBeaconReq->channelList.channelNumber[i] ))
         {
            pSmeRrmContext->channelList.ChannelList[pSmeRrmContext->channelList.numOfChannels] = pBeaconReq->channelList.channelNumber[i];
            pSmeRrmContext->channelList.numOfChannels++;
         }
      }
   }

   //Copy session bssid
   vos_mem_copy( pSmeRrmContext->sessionBssId, pBeaconReq->bssId, sizeof(tSirMacAddr) );

   //copy measurement bssid
   vos_mem_copy( pSmeRrmContext->bssId, pBeaconReq->macaddrBssid, sizeof(tSirMacAddr) );

   //Copy ssid
   vos_mem_copy( &pSmeRrmContext->ssId, &pBeaconReq->ssId, sizeof(tAniSSID) );

   pSmeRrmContext->token = pBeaconReq->uDialogToken;
   pSmeRrmContext->regClass = pBeaconReq->channelInfo.regulatoryClass;
   pSmeRrmContext->randnIntvl = VOS_MAX(pBeaconReq->randomizationInterval, pSmeRrmContext->rrmConfig.max_randn_interval);
   pSmeRrmContext->currentIndex = 0;
   pSmeRrmContext->msgSource = pBeaconReq->msgSource;
   vos_mem_copy((tANI_U8*)&pSmeRrmContext->measMode, (tANI_U8*)&pBeaconReq->fMeasurementtype, SIR_ESE_MAX_MEAS_IE_REQS);
   vos_mem_copy((tANI_U8*)&pSmeRrmContext->duration, (tANI_U8*)&pBeaconReq->measurementDuration, SIR_ESE_MAX_MEAS_IE_REQS);

   status = sme_RrmIssueScanReq( pMac );

   return status;
}

/*--------------------------------------------------------------------------
  \brief sme_RrmNeighborReportRequest() - This is API can be used to trigger a
         Neighbor report from the peer.

  \param sessionId - session identifier on which the request should be made.
  \param pNeighborReq - a pointer to a neighbor report request.

  \return eHAL_STATUS_SUCCESS - Validation is successful.

  \sa

  --------------------------------------------------------------------------*/
VOS_STATUS sme_RrmNeighborReportRequest(tpAniSirGlobal pMac, tANI_U8 sessionId,
                                    tpRrmNeighborReq pNeighborReq, tpRrmNeighborRspCallbackInfo callbackInfo)
{
   eHalStatus status = eHAL_STATUS_SUCCESS;
   tpSirNeighborReportReqInd pMsg;
   tCsrRoamSession *pSession;

#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, FL("Request to send Neighbor report request received "));
#endif
   if( !CSR_IS_SESSION_VALID( pMac, sessionId ) )
   {
      smsLog( pMac, LOGE, FL("Invalid session %d"), sessionId );
      return VOS_STATUS_E_INVAL;
   }
   pSession = CSR_GET_SESSION( pMac, sessionId );

   /* If already a report is pending, return failure */
   if (eANI_BOOLEAN_TRUE == pMac->rrm.rrmSmeContext.neighborReqControlInfo.isNeighborRspPending)
   {
       smsLog( pMac, LOGE, FL("Neighbor request already pending.. Not allowed"));
       return VOS_STATUS_E_AGAIN;
   }

   pMsg = vos_mem_malloc ( sizeof(tSirNeighborReportReqInd) );
   if ( NULL == pMsg )
   {
      smsLog( pMac, LOGE, "Unable to allocate memory for Neighbor request");
      return VOS_STATUS_E_NOMEM;
   }


   vos_mem_zero( pMsg, sizeof(tSirNeighborReportReqInd) );
#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, FL(" Allocated memory for Neighbor request") );
#endif

   rrmLLPurgeNeighborCache(pMac, &pMac->rrm.rrmSmeContext.neighborReportCache);

#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, FL("Purged the neighbor cache before sending Neighbor request: Status = %d"), status );
#endif

   pMsg->messageType = eWNI_SME_NEIGHBOR_REPORT_REQ_IND;
   pMsg->length = sizeof( tSirNeighborReportReqInd );
   vos_mem_copy( &pMsg->bssId, &pSession->connectedProfile.bssid, sizeof(tSirMacAddr) );
   pMsg->noSSID = pNeighborReq->no_ssid;
   vos_mem_copy( &pMsg->ucSSID, &pNeighborReq->ssid, sizeof(tSirMacSSid));

   status = palSendMBMessage(pMac->hHdd, pMsg);
   if( status != eHAL_STATUS_SUCCESS )
      return VOS_STATUS_E_FAILURE;

   /* Neighbor report request message sent successfully to PE. Now register the callbacks */
   pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspCallbackInfo.neighborRspCallback =
                                                            callbackInfo->neighborRspCallback;
   pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspCallbackInfo.neighborRspCallbackContext =
                                                            callbackInfo->neighborRspCallbackContext;
   pMac->rrm.rrmSmeContext.neighborReqControlInfo.isNeighborRspPending = eANI_BOOLEAN_TRUE;

   /* Start neighbor response wait timer now */
   vos_timer_start(&pMac->rrm.rrmSmeContext.neighborReqControlInfo.neighborRspWaitTimer, callbackInfo->timeout);

   return VOS_STATUS_SUCCESS;
}

/*--------------------------------------------------------------------------
  \brief rrmCalculateNeighborAPRoamScore() - This API is called while handling
                individual neighbor reports from the APs neighbor AP report to
                calculate the cumulative roam score before storing it in neighbor
                cache.

  \param pNeighborReportDesc - Neighbor BSS Descriptor node for which roam score
                                should be calculated

  \return void.
--------------------------------------------------------------------------*/
static void rrmCalculateNeighborAPRoamScore(tpAniSirGlobal pMac, tpRrmNeighborReportDesc pNeighborReportDesc)
{
    tpSirNeighborBssDescripton  pNeighborBssDesc;
    tANI_U32    roamScore = 0;
#ifdef FEATURE_WLAN_ESE
    tANI_U8     sessionId;
#endif

    if (NULL == pNeighborReportDesc)
    {
        VOS_ASSERT(0);
        return;
    }
    if (NULL == pNeighborReportDesc->pNeighborBssDescription)
    {
        VOS_ASSERT(0);
        return;
    }

    pNeighborBssDesc = pNeighborReportDesc->pNeighborBssDescription;

    if (pNeighborBssDesc->bssidInfo.rrmInfo.fMobilityDomain)
    {
        roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_MOBILITY_DOMAIN;
        if (pNeighborBssDesc->bssidInfo.rrmInfo.fSameSecurityMode)
        {
            roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_SECURITY;
            if (pNeighborBssDesc->bssidInfo.rrmInfo.fSameAuthenticator)
            {
                roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_KEY_SCOPE;
                if (pNeighborBssDesc->bssidInfo.rrmInfo.fCapRadioMeasurement)
                {
                    roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_RRM;
                    if (pNeighborBssDesc->bssidInfo.rrmInfo.fCapSpectrumMeasurement)
                        roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_SPECTRUM_MGMT;
                    if (pNeighborBssDesc->bssidInfo.rrmInfo.fCapQos)
                        roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_QOS;
                    if (pNeighborBssDesc->bssidInfo.rrmInfo.fCapApsd)
                        roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_APSD;
                    if (pNeighborBssDesc->bssidInfo.rrmInfo.fCapDelayedBlockAck)
                        roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_DELAYED_BA;
                    if (pNeighborBssDesc->bssidInfo.rrmInfo.fCapImmediateBlockAck)
                        roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_IMMEDIATE_BA;
                    if (pNeighborBssDesc->bssidInfo.rrmInfo.fApPreauthReachable)
                        roamScore += RRM_ROAM_SCORE_NEIGHBOR_REPORT_REACHABILITY;
                }
            }
        }
    }
#ifdef FEATURE_WLAN_ESE
    sessionId = pNeighborReportDesc->sessionId;
    /* It has come in the report so its the best score */
    if (csrNeighborRoamIs11rAssoc(pMac, sessionId) == FALSE) {
        /* IAPP Route so lets make use of this info
         * save all AP, as the list does not come all the time
         * Save and reuse till the next AP List comes to us.
         * Even save our own MAC address. Will be useful next time around.
         */
        roamScore += RRM_ROAM_SCORE_NEIGHBOR_IAPP_LIST;
    }
#endif
    pNeighborReportDesc->roamScore = roamScore;

    return;
}

/*--------------------------------------------------------------------------
  \brief rrmStoreNeighborRptByRoamScore() - This API is called to store a given
                        Neighbor BSS descriptor to the neighbor cache. This function
                        stores the neighbor BSS descriptors in such a way that descriptors
                        are sorted by roamScore in descending order

  \param pNeighborReportDesc - Neighbor BSS Descriptor node to be stored in cache

  \return void.
--------------------------------------------------------------------------*/
void rrmStoreNeighborRptByRoamScore(tpAniSirGlobal pMac, tpRrmNeighborReportDesc pNeighborReportDesc)
{
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;
   tListElem       *pEntry;
   tRrmNeighborReportDesc  *pTempNeighborReportDesc;

   if (NULL == pNeighborReportDesc)
   {
       VOS_ASSERT(0);
       return;
   }
   if (NULL == pNeighborReportDesc->pNeighborBssDescription)
   {
       VOS_ASSERT(0);
       return;
   }

   if (csrLLIsListEmpty(&pSmeRrmContext->neighborReportCache, LL_ACCESS_LOCK))
   {
       smsLog(pMac, LOGE, FL("Neighbor report cache is empty.. Adding a entry now"));
        /* Neighbor list cache is empty. Insert this entry in the tail */
       csrLLInsertTail(&pSmeRrmContext->neighborReportCache, &pNeighborReportDesc->List, LL_ACCESS_LOCK);
       return;
   }
   else
   {
       /* Should store the neighbor BSS description in the order sorted by roamScore in descending
              order. APs with highest roamScore should be the 1st entry in the list */
        pEntry = csrLLPeekHead(&pSmeRrmContext->neighborReportCache, LL_ACCESS_LOCK);
        while (pEntry != NULL)
        {
            pTempNeighborReportDesc = GET_BASE_ADDR( pEntry, tRrmNeighborReportDesc, List );
            if (pTempNeighborReportDesc->roamScore < pNeighborReportDesc->roamScore)
                break;
            pEntry = csrLLNext(&pSmeRrmContext->neighborReportCache, pEntry, LL_ACCESS_LOCK);
        }

        if (pEntry)
            /* This BSS roamscore is better than something in the list. Insert this before that one */
            csrLLInsertEntry(&pSmeRrmContext->neighborReportCache, pEntry, &pNeighborReportDesc->List, LL_ACCESS_LOCK);
        else
            /* All the entries in the list has a better roam Score than this one. Insert this at the last */
            csrLLInsertTail(&pSmeRrmContext->neighborReportCache, &pNeighborReportDesc->List, LL_ACCESS_LOCK);
   }
   return;
}

/*--------------------------------------------------------------------------
  \brief sme_RrmProcessNeighborReport() - This is called to process the Neighbor
         report received from PE.

  \param pMsgBuf - a pointer to a buffer that maps to various structures base
                   on the message type.
                   The beginning of the buffer can always map to tSirSmeRsp.

  \return eHAL_STATUS_SUCCESS - Validation is successful.

  \sa

  --------------------------------------------------------------------------*/
eHalStatus sme_RrmProcessNeighborReport(tpAniSirGlobal pMac, void *pMsgBuf)
{
   eHalStatus status = eHAL_STATUS_SUCCESS;
   tpSirNeighborReportInd pNeighborRpt = (tpSirNeighborReportInd) pMsgBuf;
   tpRrmNeighborReportDesc  pNeighborReportDesc;
   tANI_U8 i = 0;
   VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
   tANI_U32 sessionId;

   /* Get the session id */
   status = csrRoamGetSessionIdFromBSSID(pMac, (tCsrBssid *)pNeighborRpt->bssId,
                                         &sessionId);
   if (HAL_STATUS_SUCCESS(status)) {
#ifdef FEATURE_WLAN_ESE
       /* Clear the cache for ESE. */
       if (csrNeighborRoamIsESEAssoc(pMac, sessionId)) {
           rrmLLPurgeNeighborCache(pMac,
           &pMac->rrm.rrmSmeContext.neighborReportCache);
       }
#endif
   }

   for (i = 0; i < pNeighborRpt->numNeighborReports; i++)
   {
       pNeighborReportDesc = vos_mem_malloc(sizeof(tRrmNeighborReportDesc));
       if (NULL == pNeighborReportDesc)
       {
           smsLog( pMac, LOGE, "Failed to allocate memory for RRM Neighbor report desc");
           status = eHAL_STATUS_FAILED_ALLOC;
           goto end;

       }

       vos_mem_zero(pNeighborReportDesc, sizeof(tRrmNeighborReportDesc));
       pNeighborReportDesc->pNeighborBssDescription = vos_mem_malloc(sizeof(tSirNeighborBssDescription));
       if (NULL == pNeighborReportDesc->pNeighborBssDescription)
       {
           smsLog( pMac, LOGE, "Failed to allocate memory for RRM Neighbor report BSS Description");
           vos_mem_free(pNeighborReportDesc);
           status = eHAL_STATUS_FAILED_ALLOC;
           goto end;
       }
       vos_mem_zero(pNeighborReportDesc->pNeighborBssDescription, sizeof(tSirNeighborBssDescription));
       vos_mem_copy(pNeighborReportDesc->pNeighborBssDescription, &pNeighborRpt->sNeighborBssDescription[i],
                                                sizeof(tSirNeighborBssDescription));

#if defined WLAN_VOWIFI_DEBUG
       smsLog( pMac, LOGE, "Received neighbor report with Neighbor BSSID: "MAC_ADDRESS_STR,
                            MAC_ADDR_ARRAY(pNeighborRpt->sNeighborBssDescription[i].bssId));
#endif

       /* Calculate the roam score based on the BSS Capability in the BSSID Information and store it in Neighbor report Desc */
       rrmCalculateNeighborAPRoamScore(pMac, pNeighborReportDesc);

       /* Store the Neighbor report Desc in the cache based on the roam score */
       if ( pNeighborReportDesc->roamScore > 0)
       {
          rrmStoreNeighborRptByRoamScore(pMac, pNeighborReportDesc);
       }
       else
       {
           smsLog(pMac, LOGE, FL("Roam score of BSSID  "MAC_ADDRESS_STR" is 0, Ignoring.."),
                        MAC_ADDR_ARRAY(pNeighborRpt->sNeighborBssDescription[i].bssId));

           vos_mem_free(pNeighborReportDesc->pNeighborBssDescription);
           vos_mem_free(pNeighborReportDesc);
       }
   }
end:

   if (!csrLLCount(&pMac->rrm.rrmSmeContext.neighborReportCache))
      vosStatus = VOS_STATUS_E_FAILURE;

   /* Received a report from AP. Indicate SUCCESS to the caller if there are some valid reports */
   rrmIndicateNeighborReportResult(pMac, vosStatus);

   return status;
}
/*--------------------------------------------------------------------------
  \brief sme_RrmMsgProcessor() - sme_ProcessMsg() calls this function for the
  messages that are handled by SME RRM module.

  \param pMac - Pointer to the global MAC parameter structure.
  \param msg_type - the type of msg passed by PE as defined in wniApi.h
  \param pMsgBuf - a pointer to a buffer that maps to various structures base
                   on the message type.
                   The beginning of the buffer can always map to tSirSmeRsp.

  \return eHAL_STATUS_SUCCESS - Validation is successful.

  \sa

  --------------------------------------------------------------------------*/
eHalStatus sme_RrmMsgProcessor( tpAniSirGlobal pMac,  v_U16_t msg_type,
                                void *pMsgBuf)
{
   VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
         FL(" Msg = %d for RRM measurement") , msg_type );

   //switch on the msg type & make the state transition accordingly
   switch(msg_type)
   {
      case eWNI_SME_NEIGHBOR_REPORT_IND:
         sme_RrmProcessNeighborReport( pMac, pMsgBuf );
         break;

      case eWNI_SME_BEACON_REPORT_REQ_IND:
         sme_RrmProcessBeaconReportReqInd( pMac, pMsgBuf );
         break;

      default:
         //err msg
         VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
               FL("sme_RrmMsgProcessor:unknown msg type = %d"), msg_type);

         break;
   }

   return eHAL_STATUS_SUCCESS;
}

/* ---------------------------------------------------------------------------

    \fn rrmIterMeasTimerHandle

    \brief  Timer handler to handle the timeout condition when a specific BT
            stop event does not come back, in which case to restore back the
            heartbeat timer.

    \param  pMac - The handle returned by macOpen.

    \return VOID

  ---------------------------------------------------------------------------*/

void rrmIterMeasTimerHandle( v_PVOID_t userData )
{
   tpAniSirGlobal pMac = (tpAniSirGlobal) userData;
#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, "Randomization timer expired...send on next channel ");
#endif
    //Issue a scan req for next channel.
    sme_RrmIssueScanReq( pMac );
}

/* ---------------------------------------------------------------------------

    \fn rrmNeighborRspTimeoutHandler

    \brief  Timer handler to handle the timeout condition when a neighbor request is sent
                    and no neighbor response is received from the AP

    \param  pMac - The handle returned by macOpen.

    \return VOID

---------------------------------------------------------------------------*/

void rrmNeighborRspTimeoutHandler
( v_PVOID_t userData )
{
   tpAniSirGlobal pMac = (tpAniSirGlobal) userData;
#if defined WLAN_VOWIFI_DEBUG
   smsLog( pMac, LOGE, "Neighbor Response timed out ");
#endif
    rrmIndicateNeighborReportResult(pMac, VOS_STATUS_E_FAILURE);
    return;
}

/* ---------------------------------------------------------------------------

    \fn rrmOpen

    \brief

    \param  pMac - The handle returned by macOpen.

    \return VOS_STATUS

            VOS_STATUS_E_FAILURE  success

            VOS_STATUS_SUCCESS  failure

  ---------------------------------------------------------------------------*/

VOS_STATUS rrmOpen (tpAniSirGlobal pMac)

{

   VOS_STATUS vosStatus;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;
   eHalStatus   halStatus = eHAL_STATUS_SUCCESS;

   pSmeRrmContext->rrmConfig.max_randn_interval = 50; //ms

   vosStatus = vos_timer_init( &pSmeRrmContext->IterMeasTimer,

                      VOS_TIMER_TYPE_SW,

                      rrmIterMeasTimerHandle,

                      (void*) pMac);

   if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {

       VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "rrmOpen: Fail to init timer");

       return VOS_STATUS_E_FAILURE;
   }

   vosStatus = vos_timer_init( &pSmeRrmContext->neighborReqControlInfo.neighborRspWaitTimer,

                      VOS_TIMER_TYPE_SW,

                      rrmNeighborRspTimeoutHandler,

                      (void*) pMac);

   if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {

       VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "rrmOpen: Fail to init timer");

       return VOS_STATUS_E_FAILURE;
   }

   pSmeRrmContext->neighborReqControlInfo.isNeighborRspPending = eANI_BOOLEAN_FALSE;

   halStatus = csrLLOpen(pMac->hHdd, &pSmeRrmContext->neighborReportCache);
   if (eHAL_STATUS_SUCCESS != halStatus)
   {
       VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "rrmOpen: Fail to open neighbor cache result");
       return VOS_STATUS_E_FAILURE;
   }

   return VOS_STATUS_SUCCESS;
}


/* ---------------------------------------------------------------------------

    \fn rrmClose

    \brief

    \param  pMac - The handle returned by macOpen.

    \return VOS_STATUS

            VOS_STATUS_E_FAILURE  success

            VOS_STATUS_SUCCESS  failure

  ---------------------------------------------------------------------------*/

VOS_STATUS rrmClose (tpAniSirGlobal pMac)

{

   VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;

   if( VOS_TIMER_STATE_RUNNING == vos_timer_getCurrentState( &pSmeRrmContext->IterMeasTimer ) )
   {
      vosStatus = vos_timer_stop( &pSmeRrmContext->IterMeasTimer );
      if(!VOS_IS_STATUS_SUCCESS(vosStatus))
      {
         VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, FL("Timer stop fail") );
      }
   }

   vosStatus = vos_timer_destroy( &pSmeRrmContext->IterMeasTimer );
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {

       VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, FL("Fail to destroy timer") );

   }

   if( VOS_TIMER_STATE_RUNNING ==
          vos_timer_getCurrentState( &pSmeRrmContext->neighborReqControlInfo.neighborRspWaitTimer ) )
   {
      vosStatus = vos_timer_stop( &pSmeRrmContext->neighborReqControlInfo.neighborRspWaitTimer );
      if(!VOS_IS_STATUS_SUCCESS(vosStatus))
      {
         VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, FL("Timer stop fail") );
      }
   }

   vosStatus = vos_timer_destroy( &pSmeRrmContext->neighborReqControlInfo.neighborRspWaitTimer );
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
       VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, FL("Fail to destroy timer") );

   }

   rrmLLPurgeNeighborCache(pMac, &pSmeRrmContext->neighborReportCache);

   csrLLClose(&pSmeRrmContext->neighborReportCache);

   return vosStatus;

}




/* ---------------------------------------------------------------------------

    \fn rrmReady

    \brief  fn

    \param  pMac - The handle returned by macOpen.

    \return VOS_STATUS

  ---------------------------------------------------------------------------*/

VOS_STATUS rrmReady (tpAniSirGlobal pMac)

{

    return VOS_STATUS_SUCCESS;
}

/* ---------------------------------------------------------------------------

    \fn rrmChangeDefaultConfigParam
    \brief  fn

    \param  pMac - The handle returned by macOpen.
    \param  pRrmConfig - pointer to new rrm configs.

    \return VOS_STATUS

  ---------------------------------------------------------------------------*/
VOS_STATUS rrmChangeDefaultConfigParam(tpAniSirGlobal pMac,
				       struct rrm_config_param *pRrmConfig)
{
	vos_mem_copy(&pMac->rrm.rrmSmeContext.rrmConfig, pRrmConfig,
		     sizeof(struct rrm_config_param));

	return VOS_STATUS_SUCCESS;
}

/* ---------------------------------------------------------------------------

    \fn smeRrmGetFirstBssEntryFromNeighborCache()

    \brief  This function returns the first entry from the neighbor cache to the caller

    \param  pMac - The handle returned by macOpen.

    \return VOID

---------------------------------------------------------------------------*/
tRrmNeighborReportDesc* smeRrmGetFirstBssEntryFromNeighborCache( tpAniSirGlobal pMac)
{
   tListElem *pEntry;
   tRrmNeighborReportDesc *pTempBssEntry = NULL;
   tpRrmSMEContext pSmeRrmContext = &pMac->rrm.rrmSmeContext;


   pEntry = csrLLPeekHead( &pSmeRrmContext->neighborReportCache, LL_ACCESS_LOCK );

   if(!pEntry || !csrLLCount(&pSmeRrmContext->neighborReportCache))
   {
      //list empty
      smsLog(pMac, LOGW, FL("List empty"));
      return NULL;
   }

   pTempBssEntry = GET_BASE_ADDR( pEntry, tRrmNeighborReportDesc, List );

   return pTempBssEntry;
}

/* ---------------------------------------------------------------------------

    \fn smeRrmGetNextBssEntryFromNeighborCache()

    \brief  This function returns the entry next to the given entry from the
                neighbor cache to the caller

    \param  pMac - The handle returned by macOpen.

    \return VOID

---------------------------------------------------------------------------*/
tRrmNeighborReportDesc* smeRrmGetNextBssEntryFromNeighborCache( tpAniSirGlobal pMac,
                                                        tpRrmNeighborReportDesc pBssEntry)
{
   tListElem *pEntry;
   tRrmNeighborReportDesc *pTempBssEntry = NULL;

   pEntry = csrLLNext(&pMac->rrm.rrmSmeContext.neighborReportCache, &pBssEntry->List, LL_ACCESS_LOCK);

   if(!pEntry)
   {
      //list empty
      smsLog(pMac, LOGW, FL("List empty"));
      return NULL;
   }

   pTempBssEntry = GET_BASE_ADDR( pEntry, tRrmNeighborReportDesc, List );

   return pTempBssEntry;
}

#if defined(FEATURE_WLAN_ESE) && !defined(FEATURE_WLAN_ESE_UPLOAD)
void csrEseSendAdjacentApRepMsg(tpAniSirGlobal pMac, tCsrRoamSession *pSession)
{
   tpSirAdjacentApRepInd pAdjRep;
   tANI_U16 length;
   tANI_U32 roamTS2;

   smsLog( pMac, LOG1, "Adjacent AP Report Msg to PE");

   length = sizeof(tSirAdjacentApRepInd );
   pAdjRep = vos_mem_malloc ( length );

   if ( NULL == pAdjRep )
   {
       smsLog( pMac, LOGP, "Unable to allocate memory for Adjacent AP report");
       return;
   }

   vos_mem_zero( pAdjRep, length );
   pAdjRep->messageType = eWNI_SME_ESE_ADJACENT_AP_REPORT;
   pAdjRep->length = length;
   pAdjRep->channelNum = pSession->prevOpChannel;
   vos_mem_copy( pAdjRep->bssid, &pSession->connectedProfile.bssid, sizeof(tSirMacAddr) );
   vos_mem_copy( pAdjRep->prevApMacAddr, &pSession->prevApBssid, sizeof(tSirMacAddr) );
   vos_mem_copy( &pAdjRep->prevApSSID, &pSession->prevApSSID, sizeof(tSirMacSSid) );
   roamTS2 = vos_timer_get_system_time();
   pAdjRep->tsmRoamdelay = roamTS2 - pSession->roamTS1;
   pAdjRep->roamReason =SIR_ESE_ASSOC_REASON_UNSPECIFIED;
   pAdjRep->clientDissSecs =(pAdjRep->tsmRoamdelay/1000);

   palSendMBMessage(pMac->hHdd, pAdjRep);

   return;
}
#endif   /* FEATURE_WLAN_ESE */
#endif
