wlan: Add support for PER based roaming #2

In congested environments, it could be possible that in station
mode client gets good RSSI from AP however it's not able to
transfer data at good rates. DUT should roam to a better AP if
available, if it faces congestion in home channel.

Following changes are added for this functionality:
1. Add changes to monitor Rx and Tx rates to trigger roam scan
in case data rates are low below a preconfigured value.
2. Implement a new AP selection algorithm which will consider AP
capabilty, channel conditions and RSSI to select new bss.

Add required protocol changes for same.

Change-Id: I394c15b54ea95aa08f648f33383cb0a364278585
CRs-Fixed: 1031345
diff --git a/CORE/MAC/inc/aniGlobal.h b/CORE/MAC/inc/aniGlobal.h
index d4e503e..cb3dbb4 100644
--- a/CORE/MAC/inc/aniGlobal.h
+++ b/CORE/MAC/inc/aniGlobal.h
@@ -1073,6 +1073,13 @@
     v_BOOL_t fActiveScanOnDFSChannels;
     tAuthAckStatus  authAckStatus;
     sir_mgmt_frame_ind_callback mgmt_frame_ind_cb;
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+    v_U8_t PERroamCandidatesCnt;
+    tSirCandidateChanInfo candidateChannelInfo[SIR_PER_ROAM_MAX_CANDIDATE_CNT];
+    tSirRoamAPInfo previousRoamApInfo[SIR_PER_ROAM_MAX_CANDIDATE_CNT];
+    v_U32_t PERroamTimeout;
+    v_U32_t currentBssScore;
+#endif
 } tAniSirGlobal;
 
 #ifdef FEATURE_WLAN_TDLS
diff --git a/CORE/MAC/inc/sirApi.h b/CORE/MAC/inc/sirApi.h
index f60b71e..8994dc3 100644
--- a/CORE/MAC/inc/sirApi.h
+++ b/CORE/MAC/inc/sirApi.h
@@ -719,11 +719,11 @@
     tANI_U8              mdiePresent;
     tANI_U8              mdie[SIR_MDIE_SIZE];                // MDIE for 11r, picked from the beacons
 #endif
-#ifdef FEATURE_WLAN_ESE
-    tANI_U16             QBSSLoad_present;
-    tANI_U16             QBSSLoad_avail; 
+#if defined(FEATURE_WLAN_ESE) || defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
+    tANI_U8              QBSSLoad_present;
+    tANI_U8              QBSS_ChanLoad;
+    tANI_U16             QBSSLoad_avail;
 #endif
-    // Please keep the structure 4 bytes aligned above the ieFields
 
     tANI_U8              fProbeRsp; //whether it is from a probe rsp
     tANI_U8              reservedPadding1;
@@ -731,9 +731,14 @@
     tANI_U8              reservedPadding3;
     tANI_U32             WscIeLen;
     tANI_U8              WscIeProbeRsp[WSCIE_PROBE_RSP_LEN];
-    tANI_U8              reservedPadding4;
-    
+    tANI_U8              HTCapsPresent;
+    tANI_U8              vhtCapsPresent;
+    tANI_U8              wmeInfoPresent;
+    tANI_U8              beacomformingCapable;
+    tANI_U8              chanWidth;
+    /* Please keep the structure 4 bytes aligned above the ieFields */
     tANI_U32             ieFields[1];
+
 } tSirBssDescription, *tpSirBssDescription;
 
 /// Definition for response message to previously
@@ -3971,6 +3976,35 @@
 #endif //FEATURE_WLAN_SCAN_PNO
 
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+#define SIR_PER_ROAM_MAX_AP_CNT 20
+#define SIR_PER_ROAM_MAX_CANDIDATE_CNT 10
+typedef struct __attribute__((packed))
+{
+    tANI_U8    channelNumber;
+    tANI_U8    channelCCA;
+    tANI_U8    otherApCount;
+    tANI_S8    otherApRssi[SIR_PER_ROAM_MAX_AP_CNT];
+} tSirCandidateChanInfo, * tpSirCandidateChanInfo;
+
+typedef struct sPERRoamScanStart
+{
+    tANI_U16                msgType;
+    tANI_U16                msgLen;
+    tANI_U8                 start;
+} tPERRoamScanStart, *tpPERRoamScanStart;
+
+typedef struct sSirRoamAPInfo
+{
+    tSirMacAddr bssAddr;
+    unsigned int timeStamp;
+} tSirRoamAPInfo, *tpSirRoamAPInfo;
+
+typedef struct __attribute__((packed))
+{
+    tANI_U32    candidateCount;
+    tSirCandidateChanInfo  channelInfo[SIR_PER_ROAM_MAX_CANDIDATE_CNT];
+} tSirPerRoamScanResult, * tpSirPerRoamScanResult;
+
 typedef struct
 {
   tSirMacSSid ssId;
@@ -4016,6 +4050,35 @@
   tSirRoamNetworkType ConnectedNetwork;
   tSirMobilityDomainInfo MDID;
 } tSirRoamOffloadScanReq, *tpSirRoamOffloadScanReq;
+
+/**
+ * struct sSirPERRoamOffloadScanReq - Offload PER config params
+ * @sessionId: session id
+ * @rateUpThreshold: rate at which to stop monitoring the rate
+ * @rateDownThreshold: rate at which to start monitoring
+ * @isPERRoamCCAEnabled: CCA sensing is enabled or disabled
+ * @waitPeriodForNextPERScan: time to wait before start monitoring again once a
+ * scan has been triggered
+ * @PERtimerThreshold: time to collect stats to trigger roam scan
+ * @PERroamTriggerPercent: minimum percentage of packets needs to be below
+ * rateDownThreshold to trigger a roam scan
+ */
+typedef struct sSirPERRoamOffloadScanReq
+{
+  tANI_U16  sessionId;
+  tANI_U16  rateUpThreshold;
+  tANI_U16  rateDownThreshold;
+  tANI_U16  isPERRoamCCAEnabled;
+  tANI_U32  waitPeriodForNextPERScan;
+  tANI_U32  PERtimerThreshold;
+  tANI_U32  PERroamTriggerPercent;
+} tSirPERRoamOffloadScanReq, *tpSirPERRoamOffloadScanReq;
+
+typedef struct sSirPERRoamTriggerScanReq
+{
+  tANI_BOOLEAN roamScanReq;
+} tSirPERRoamTriggerScanReq, *tpSirPERRoamTriggerScanReq;
+
 #endif //WLAN_FEATURE_ROAM_SCAN_OFFLOAD
 
 #define SIR_NOCHANGE_POWER_VALUE  0xFFFFFFFF
diff --git a/CORE/MAC/inc/wniApi.h b/CORE/MAC/inc/wniApi.h
index 4c22db5..ff69d7f 100644
--- a/CORE/MAC/inc/wniApi.h
+++ b/CORE/MAC/inc/wniApi.h
@@ -356,6 +356,9 @@
     eWNI_SME_CANDIDATE_FOUND_IND, /*ROAM candidate indication from FW*/
     eWNI_SME_HANDOFF_REQ,/*upper layer requested handoff to driver in STA mode*/
     eWNI_SME_ROAM_SCAN_OFFLOAD_RSP,/*Fwd the LFR scan offload rsp from FW to SME*/
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+    eWNI_SME_ROAM_SCAN_TRIGGER_RSP,
+#endif
 #ifdef FEATURE_WLAN_LPHB
     eWNI_SME_LPHB_IND,
 #endif /* FEATURE_WLAN_LPHB */
diff --git a/CORE/MAC/src/include/parserApi.h b/CORE/MAC/src/include/parserApi.h
index e36227b..39281f2 100644
--- a/CORE/MAC/src/include/parserApi.h
+++ b/CORE/MAC/src/include/parserApi.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -106,8 +106,10 @@
 #ifdef WLAN_FEATURE_VOWIFI_11R
     tANI_U8                   mdie[SIR_MDIE_SIZE];
 #endif
+#if defined(FEATURE_WLAN_ESE) || defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
 #ifdef FEATURE_WLAN_ESE
     tDot11fIEESETxmitPower    eseTxPwr;
+#endif
     tDot11fIEQBSSLoad         QBSSLoad;
 #endif
     tANI_U8                   ssidPresent;
diff --git a/CORE/MAC/src/include/sirParams.h b/CORE/MAC/src/include/sirParams.h
index c1b0644..1f64736 100644
--- a/CORE/MAC/src/include/sirParams.h
+++ b/CORE/MAC/src/include/sirParams.h
@@ -141,6 +141,9 @@
    EXT_LL_STAT              = 60,
    WIFI_CONFIG            = 61,
    ANTENNA_DIVERSITY_SELECTION  = 62,
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+   PER_BASED_ROAMING = 63,
+#endif
    //MAX_FEATURE_SUPPORTED = 128
 } placeHolderInCapBitmap;
 
@@ -755,6 +758,12 @@
 #define SIR_HAL_MODIFY_ROAM_PARAMS_IND     (SIR_HAL_ITC_MSG_TYPES_BEGIN + 283)
 #define SIR_HAL_SET_ALLOWED_ACTION_FRAMES  (SIR_HAL_ITC_MSG_TYPES_BEGIN + 284)
 #define SIR_HAL_PAUSE_TL_IND               (SIR_HAL_ITC_MSG_TYPES_BEGIN + 285)
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+#define SIR_HAL_PER_ROAM_SCAN_OFFLOAD_REQ  (SIR_HAL_ITC_MSG_TYPES_BEGIN + 286)
+#define SIR_HAL_PER_ROAM_SCAN_OFFLOAD_RSP  (SIR_HAL_ITC_MSG_TYPES_BEGIN + 287)
+#define SIR_HAL_PER_ROAM_SCAN_TRIGGER_REQ  (SIR_HAL_ITC_MSG_TYPES_BEGIN + 288)
+#define SIR_HAL_PER_ROAM_SCAN_TRIGGER_RSP  (SIR_HAL_ITC_MSG_TYPES_BEGIN + 289)
+#endif
 #define SIR_HAL_MSG_TYPES_END              (SIR_HAL_MSG_TYPES_BEGIN + 0x1FF)
 
 // CFG message types
diff --git a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
index 878984f..1930dcf 100644
--- a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
+++ b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
@@ -749,9 +749,55 @@
                                   MAC_ADDR_ARRAY(pHdr->sa));
         }
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
-    if ( WDA_GET_ROAMCANDIDATEIND(pRxPacketInfo))
+    if (WDA_GET_ROAMCANDIDATEIND(pRxPacketInfo))
     {
-        limLog( pMac, LOG2, FL("Notify SME with candidate ind"));
+        limLog( pMac, LOGW, FL("Notify SME with candidate ind"));
+
+        if (WDA_IF_PER_ROAMCANDIDATEIND(pRxPacketInfo) &&
+            IS_FEATURE_SUPPORTED_BY_FW(PER_BASED_ROAMING) &&
+            pMac->roam.configParam.isPERRoamEnabled)
+        {
+            tSirPerRoamScanResult *candidateChanInfo =
+                (tSirPerRoamScanResult *)WDA_GET_RX_MPDU_DATA(pRxPacketInfo);
+            int chanInfoLen = WDA_GET_RX_PAYLOAD_LEN(pRxPacketInfo)
+                                      - sizeof(tANI_U32);
+
+            /* Translate network buffer into system buffer */
+            vos_buff_to_hl_buff((v_U8_t *)candidateChanInfo,
+                          WDA_GET_RX_PAYLOAD_LEN(pRxPacketInfo));
+
+            /* Max candidates allowed */
+            if (candidateChanInfo->candidateCount > SIR_PER_ROAM_MAX_AP_CNT)
+            {
+                limLog(pMac, LOGE,
+                       FL("Got maximum candidates as %d setting default"),
+                       candidateChanInfo->candidateCount);
+                candidateChanInfo->candidateCount = SIR_PER_ROAM_MAX_AP_CNT;
+            }
+
+            vos_mem_set(&pMac->candidateChannelInfo,
+                        sizeof(tSirCandidateChanInfo) *
+                        SIR_PER_ROAM_MAX_CANDIDATE_CNT, 0);
+
+            vos_mem_copy(&pMac->candidateChannelInfo,
+                         candidateChanInfo->channelInfo,
+                         (sizeof(tSirCandidateChanInfo) *
+                         SIR_PER_ROAM_MAX_CANDIDATE_CNT) < chanInfoLen ?
+                         (sizeof(tSirCandidateChanInfo) *
+                         SIR_PER_ROAM_MAX_CANDIDATE_CNT):
+                         chanInfoLen);
+
+            limLog(pMac, LOG1,
+                   FL("PER based Roam candidates %d"),
+                   candidateChanInfo->candidateCount);
+
+            pMac->PERroamCandidatesCnt = candidateChanInfo->candidateCount;
+        } else
+        {
+            /* Normal RSSI based roaming */
+            pMac->PERroamCandidatesCnt = 0;
+        }
+
         //send a session 0 for now - TBD
         limSendSmeCandidateFoundInd(pMac, 0);
         goto end;
diff --git a/CORE/MAC/src/pe/lim/limScanResultUtils.c b/CORE/MAC/src/pe/lim/limScanResultUtils.c
index 3969f30..cb63812 100644
--- a/CORE/MAC/src/pe/lim/limScanResultUtils.c
+++ b/CORE/MAC/src/pe/lim/limScanResultUtils.c
@@ -176,7 +176,32 @@
     pBssDescr->beaconInterval = pBPR->beaconInterval;
     pBssDescr->capabilityInfo = limGetU16((tANI_U8 *) &pBPR->capabilityInfo);
 
-     if(!pBssDescr->beaconInterval )
+    pBssDescr->HTCapsPresent = 0;
+    pBssDescr->chanWidth = eHT_CHANNEL_WIDTH_20MHZ;
+    pBssDescr->wmeInfoPresent = 0;
+    pBssDescr->vhtCapsPresent = 0;
+    pBssDescr->beacomformingCapable = 0;
+    /* HT capability */
+    if (pBPR->HTCaps.present) {
+        pBssDescr->HTCapsPresent = 1;
+        if (pBPR->HTCaps.supportedChannelWidthSet)
+            pBssDescr->chanWidth = eHT_CHANNEL_WIDTH_40MHZ;
+    }
+    if (pBPR->wmeEdcaPresent)
+        pBssDescr->wmeInfoPresent = 1;
+
+#ifdef WLAN_FEATURE_11AC
+    /* VHT Parameters */
+    if (pBPR->VHTCaps.present) {
+        pBssDescr->vhtCapsPresent = 1;
+        if (pBPR->VHTCaps.muBeamformerCap)
+            pBssDescr->beacomformingCapable = 1;
+    }
+    if (pBPR->VHTOperation.present)
+        if (pBPR->VHTOperation.chanWidth == 1)
+            pBssDescr->chanWidth = eHT_CHANNEL_WIDTH_80MHZ;
+#endif
+    if(!pBssDescr->beaconInterval )
     {
 			        limLog(pMac, LOGW,
             FL("Beacon Interval is ZERO, making it to default 100 "
@@ -259,13 +284,14 @@
     }
 #endif
 
-#ifdef FEATURE_WLAN_ESE
+#if defined(FEATURE_WLAN_ESE) || defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
     pBssDescr->QBSSLoad_present = FALSE;
     pBssDescr->QBSSLoad_avail = 0; 
     if( pBPR->QBSSLoad.present) 
     {
         pBssDescr->QBSSLoad_present = TRUE;
         pBssDescr->QBSSLoad_avail = pBPR->QBSSLoad.avail;
+        pBssDescr->QBSS_ChanLoad = pBPR->QBSSLoad.chautil;
     }
 #endif
     // Copy IE fields
diff --git a/CORE/MAC/src/pe/lim/limSerDesUtils.c b/CORE/MAC/src/pe/lim/limSerDesUtils.c
index 71da217..b41d6dd 100644
--- a/CORE/MAC/src/pe/lim/limSerDesUtils.c
+++ b/CORE/MAC/src/pe/lim/limSerDesUtils.c
@@ -229,10 +229,16 @@
 #endif
 #endif
 
-#ifdef FEATURE_WLAN_ESE
-    pBssDescription->QBSSLoad_present = limGetU16(pBuf);
-    pBuf += sizeof(tANI_U16);
-    len  -= sizeof(tANI_U16);
+#if defined(FEATURE_WLAN_ESE) || defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
+    /* Extract QBSSLoad_present */
+    pBssDescription->QBSSLoad_present = *pBuf++;
+    len  -= sizeof(tANI_U8);
+    if (limCheckRemainingLength(pMac, len) == eSIR_FAILURE)
+        return eSIR_FAILURE;
+
+    /* Extract QBSS_ChanLoad */
+    pBssDescription->QBSS_ChanLoad = *pBuf++;
+    len  -= sizeof(tANI_U8);
     if (limCheckRemainingLength(pMac, len) == eSIR_FAILURE)
         return eSIR_FAILURE;
 
@@ -277,9 +283,25 @@
         return eSIR_FAILURE;
     }
 
-    /* 1 reserved byte padding */
-    pBuf += (WSCIE_PROBE_RSP_LEN + 1);
-    len -= (WSCIE_PROBE_RSP_LEN + 1);
+    /* Extract HTCapsPresent */
+    pBssDescription->HTCapsPresent = *pBuf++;
+    len --;
+
+    /* Extract vhtCapsPresent */
+    pBssDescription->vhtCapsPresent = *pBuf++;
+    len --;
+
+    /* Extract wmeInfoPresent */
+    pBssDescription->wmeInfoPresent = *pBuf++;
+    len --;
+
+    /* Extract beacomformingCapable */
+    pBssDescription->beacomformingCapable = *pBuf++;
+    len --;
+
+    /* Extract chanWidth */
+    pBssDescription->chanWidth = *pBuf++;
+    len --;
 
     if (len > 0)
     {
diff --git a/CORE/SME/inc/csrApi.h b/CORE/SME/inc/csrApi.h
index 7f7820a..93e9963 100644
--- a/CORE/SME/inc/csrApi.h
+++ b/CORE/SME/inc/csrApi.h
@@ -402,6 +402,9 @@
     tANI_U8 MFPRequired;
     tANI_U8 MFPCapable;
 #endif
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+    tANI_BOOLEAN isPERRoamScan;
+#endif
 }tCsrScanResultFilter;
 
 
@@ -1181,6 +1184,13 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
     tANI_BOOLEAN isRoamOffloadScanEnabled;
     tANI_BOOLEAN bFastRoamInConIniFeatureEnabled;
+    v_BOOL_t isPERRoamEnabled;
+    v_BOOL_t isPERRoamCCAEnabled;
+    v_U32_t rateUpThreshold;
+    v_U32_t rateDownThreshold;
+    v_U32_t waitPeriodForNextPERScan;
+    v_U32_t PERtimerThreshold;
+    v_U32_t PERroamTriggerPercent;
 #endif
 #endif
 
diff --git a/CORE/SME/inc/csrInternal.h b/CORE/SME/inc/csrInternal.h
index 9729f6e..daf16c9 100644
--- a/CORE/SME/inc/csrInternal.h
+++ b/CORE/SME/inc/csrInternal.h
@@ -623,6 +623,13 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
     tANI_U8      isRoamOffloadScanEnabled;
     tANI_BOOLEAN bFastRoamInConIniFeatureEnabled;
+    v_BOOL_t     isPERRoamEnabled;
+    v_BOOL_t     isPERRoamCCAEnabled;
+    tANI_U32     rateUpThreshold;
+    tANI_U32     rateDownThreshold;
+    tANI_U32     waitPeriodForNextPERScan;
+    tANI_U32     PERtimerThreshold;
+    tANI_U32     PERroamTriggerPercent;
 #endif
 #endif
 
diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h
index 264c37b..06c738c 100644
--- a/CORE/SME/inc/sme_Api.h
+++ b/CORE/SME/inc/sme_Api.h
@@ -1906,6 +1906,16 @@
                                          void * txFailIndCallback);
 #endif /* WLAN_FEATURE_RMC */
 
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+VOS_STATUS sme_set_per_roam_rxconfig (tHalHandle hHal, v_U8_t sessionId,
+                  v_U16_t minRate, v_U16_t maxRate, v_U8_t minPercentage,
+                  v_U16_t minPktRequired, v_U64_t waitPeriodForNextPERScan);
+
+VOS_STATUS sme_unset_per_roam_rxconfig (tHalHandle hHal);
+
+void sme_PERRoamScanStartStop(void *hHal, tANI_U8 start);
+#endif
+
 /* ---------------------------------------------------------------------------
 
     \fn sme_DHCPStartInd
diff --git a/CORE/SME/src/csr/csrApiRoam.c b/CORE/SME/src/csr/csrApiRoam.c
index ec93e5e..ced64e7 100644
--- a/CORE/SME/src/csr/csrApiRoam.c
+++ b/CORE/SME/src/csr/csrApiRoam.c
@@ -1932,8 +1932,21 @@
         pMac->roam.configParam.nRoamScanHomeAwayTime = pParam->nRoamScanHomeAwayTime;
 #endif
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
-        pMac->roam.configParam.isRoamOffloadScanEnabled = pParam->isRoamOffloadScanEnabled;
-        pMac->roam.configParam.bFastRoamInConIniFeatureEnabled = pParam->bFastRoamInConIniFeatureEnabled;
+        pMac->roam.configParam.isRoamOffloadScanEnabled =
+                pParam->isRoamOffloadScanEnabled;
+        pMac->roam.configParam.bFastRoamInConIniFeatureEnabled =
+                pParam->bFastRoamInConIniFeatureEnabled;
+        pMac->roam.configParam.isPERRoamEnabled =
+                pParam->isPERRoamEnabled;
+        pMac->roam.configParam.rateUpThreshold = pParam->rateUpThreshold;
+        pMac->roam.configParam.rateDownThreshold = pParam->rateDownThreshold;
+        pMac->roam.configParam.waitPeriodForNextPERScan =
+                pParam->waitPeriodForNextPERScan;
+        pMac->roam.configParam.PERtimerThreshold = pParam->PERtimerThreshold;
+        pMac->roam.configParam.isPERRoamCCAEnabled =
+                pParam->isPERRoamCCAEnabled;
+        pMac->roam.configParam.PERroamTriggerPercent =
+                pParam->PERroamTriggerPercent;
 #endif
 #ifdef FEATURE_WLAN_LFR
         pMac->roam.configParam.isFastRoamIniFeatureEnabled = pParam->isFastRoamIniFeatureEnabled;
@@ -2141,6 +2154,17 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
         pParam->isRoamOffloadScanEnabled = pMac->roam.configParam.isRoamOffloadScanEnabled;
         pParam->bFastRoamInConIniFeatureEnabled = pMac->roam.configParam.bFastRoamInConIniFeatureEnabled;
+        pParam->isPERRoamEnabled =
+                pMac->roam.configParam.isPERRoamEnabled;
+        pParam->rateUpThreshold = pMac->roam.configParam.rateUpThreshold;
+        pParam->rateDownThreshold = pMac->roam.configParam.rateDownThreshold;
+        pParam->waitPeriodForNextPERScan =
+                pMac->roam.configParam.waitPeriodForNextPERScan;
+        pParam->PERtimerThreshold = pMac->roam.configParam.PERtimerThreshold;
+        pParam->isPERRoamCCAEnabled =
+                pMac->roam.configParam.isPERRoamCCAEnabled;
+        pParam->PERroamTriggerPercent =
+                pMac->roam.configParam.PERroamTriggerPercent;
 #endif
 #ifdef FEATURE_WLAN_LFR
         pParam->isFastRoamIniFeatureEnabled = pMac->roam.configParam.isFastRoamIniFeatureEnabled;
@@ -8287,8 +8311,11 @@
     tpCsrNeighborRoamControlInfo    pNeighborRoamInfo = &pMac->roam.neighborRoamInfo;
     tCsrRoamInfo roamInfo;
     tANI_U32 roamId = 0;
-    
-    if ( eSIR_SME_SUCCESS == pSmeJoinRsp->statusCode ) 
+    tANI_U32 current_timestamp, max_time = -1;
+    tANI_U32 candidateApCnt, oldestIndex;
+    tANI_U8 nilMac[6] = {0};
+
+    if (eSIR_SME_SUCCESS == pSmeJoinRsp->statusCode)
     {
         smsLog( pMac, LOGW, "CSR SmeReassocReq Successful" );
         result = eCsrReassocSuccess;
@@ -8301,6 +8328,73 @@
             /* Need to dig more on indicating events to SME QoS module */
             sme_QosCsrEventInd(pMac, pSmeJoinRsp->sessionId, SME_QOS_CSR_HANDOFF_COMPLETE, NULL);
             csrRoamComplete( pMac, result, pSmeJoinRsp);
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+            /* Add previous BSSID to blacklist ; this will be in blacklised for
+             * some period and score of this AP will be reduced if black listed
+             * to avoid ping pong */
+            if (pMac->PERroamCandidatesCnt)
+            {
+              current_timestamp = jiffies_to_msecs(jiffies);
+              for (candidateApCnt = 0; candidateApCnt <
+                   SIR_PER_ROAM_MAX_CANDIDATE_CNT; candidateApCnt++)
+              {
+                  /* Find one blank entry */
+                  if (sirCompareMacAddr(nilMac,
+                          pMac->previousRoamApInfo[candidateApCnt].bssAddr))
+                  {
+                      vos_mem_copy(pMac->previousRoamApInfo[candidateApCnt].
+                                   bssAddr,
+                                   pNeighborRoamInfo->prevConnProfile.bssid,
+                                   sizeof(tSirMacAddr));
+                      pMac->previousRoamApInfo[candidateApCnt].timeStamp =
+                          current_timestamp;
+                      smsLog(pMac, LOG1, FL("added bssid=" MAC_ADDRESS_STR " at index %d"),
+                             MAC_ADDR_ARRAY(
+                                 pNeighborRoamInfo->prevConnProfile.bssid),
+                             candidateApCnt);
+                      break;
+                  }
+                  /* if already in the list */
+                  if (sirCompareMacAddr(pMac->previousRoamApInfo
+                      [candidateApCnt].bssAddr,
+                      pNeighborRoamInfo->prevConnProfile.bssid) &&
+                      ((current_timestamp -
+                          pMac->previousRoamApInfo[candidateApCnt].timeStamp) >
+                          pMac->PERroamTimeout))
+                  {
+                      vos_mem_copy(pMac->previousRoamApInfo[candidateApCnt].
+                                   bssAddr,
+                                   pNeighborRoamInfo->prevConnProfile.bssid,
+                                   sizeof(tSirMacAddr));
+                      pMac->previousRoamApInfo[candidateApCnt].timeStamp =
+                          current_timestamp;
+                      break;
+                  } else
+                  {
+                      /* find oldest BSSID entry in the blacklist */
+                      if (max_time <
+                             pMac->previousRoamApInfo[candidateApCnt].timeStamp)
+                      {
+                          max_time =
+                             pMac->previousRoamApInfo[candidateApCnt].timeStamp;
+                          oldestIndex = candidateApCnt;
+                      }
+                  }
+              }
+              if (candidateApCnt == SIR_PER_ROAM_MAX_CANDIDATE_CNT)
+              {
+                 smsLog(pMac, LOGW,
+                        "%s: Clearing out oldest roam results bssid="
+                        MAC_ADDRESS_STR,
+                        __func__,
+                        MAC_ADDR_ARRAY(pMac->previousRoamApInfo[oldestIndex].bssAddr));
+                  pMac->previousRoamApInfo[oldestIndex].timeStamp = current_timestamp;
+                  vos_mem_copy(pMac->previousRoamApInfo[oldestIndex].bssAddr,
+                       pNeighborRoamInfo->prevConnProfile.bssid,
+                       sizeof(tSirMacAddr));
+              }
+            }
+#endif
         }
         else
 #endif
@@ -16594,7 +16688,9 @@
 eHalStatus csrRoamOffloadScan(tpAniSirGlobal pMac, tANI_U8 command, tANI_U8 reason)
 {
    vos_msg_t msg;
+   vos_msg_t PERroamScanConfigMsg = {0};
    tSirRoamOffloadScanReq *pRequestBuf;
+   tSirPERRoamOffloadScanReq *PERRoamReqBuf;
    tpCsrNeighborRoamControlInfo    pNeighborRoamInfo = &pMac->roam.neighborRoamInfo;
    tCsrRoamSession *pSession = NULL;
    tANI_U8 i,j,num_channels = 0, ucDot11Mode;
@@ -16880,6 +16976,7 @@
     /* MAWC feature */
     pRequestBuf->MAWCEnabled =
             pMac->roam.configParam.MAWCEnabled;
+
 #ifdef FEATURE_WLAN_ESE
     pRequestBuf->IsESEEnabled = pMac->roam.configParam.isEseIniFeatureEnabled;
 #endif
@@ -17078,6 +17175,52 @@
     }
 
    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_DEBUG, "Roam Scan Offload Command %d, Reason %d", command, reason);
+
+   if (sme_IsFeatureSupportedByFW(PER_BASED_ROAMING) &&
+      (command != ROAM_SCAN_OFFLOAD_STOP))
+   {
+
+      /* PER ROAM SCAN */
+      PERRoamReqBuf = vos_mem_malloc(sizeof(*PERRoamReqBuf));
+      if (!PERRoamReqBuf)
+      {
+          VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+                 "%s: Not able to allocate mem for PERRoamReqBuf", __func__);
+          return eHAL_STATUS_FAILURE;
+      }
+      /* PER Roam Config */
+      PERRoamReqBuf->rateUpThreshold =
+              pMac->roam.configParam.rateUpThreshold;
+      PERRoamReqBuf->rateDownThreshold =
+              pMac->roam.configParam.rateDownThreshold;
+      PERRoamReqBuf->waitPeriodForNextPERScan =
+              pMac->roam.configParam.waitPeriodForNextPERScan;
+      PERRoamReqBuf->PERtimerThreshold =
+              pMac->roam.configParam.PERtimerThreshold;
+      PERRoamReqBuf->isPERRoamCCAEnabled =
+              pMac->roam.configParam.isPERRoamCCAEnabled;
+      PERRoamReqBuf->PERroamTriggerPercent =
+              pMac->roam.configParam.PERroamTriggerPercent;
+      PERRoamReqBuf->sessionId = sessionId;
+
+      PERroamScanConfigMsg.type = WDA_PER_ROAM_SCAN_OFFLOAD_REQ;
+      PERroamScanConfigMsg.reserved = 0;
+      PERroamScanConfigMsg.bodyptr = PERRoamReqBuf;
+      if (!VOS_IS_STATUS_SUCCESS(vos_mq_post_message(VOS_MODULE_ID_WDA,
+                                     &PERroamScanConfigMsg))) {
+          VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+               FL("Not able to post WDA_PER_ROAM_SCAN_OFFLOAD_REQ msg to WDA"));
+          vos_mem_free(PERRoamReqBuf);
+          return eHAL_STATUS_FAILURE;
+      }
+      VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_DEBUG,
+                FL("rateUpThreshold =%x rateDownThreshold =%x waitPeriodForNextPERScan=%u PERtimerThreshold=%u"),
+                PERRoamReqBuf->rateUpThreshold,
+                PERRoamReqBuf->rateDownThreshold,
+                PERRoamReqBuf->waitPeriodForNextPERScan,
+                PERRoamReqBuf->PERtimerThreshold);
+   }
+
    return status;
 }
 
diff --git a/CORE/SME/src/csr/csrApiScan.c b/CORE/SME/src/csr/csrApiScan.c
index 79fe04a..3c5aa4b 100644
--- a/CORE/SME/src/csr/csrApiScan.c
+++ b/CORE/SME/src/csr/csrApiScan.c
@@ -2043,6 +2043,259 @@
     return (ret);
 }
 
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+
+/* Calculate channel weight based on other APs RSSI and count for
+ * PER based roaming */
+static tANI_U32 GetPERRoamRssiCountWeight(tANI_S32 rssi, tANI_S32 count)
+{
+    tANI_S32 rssiWeight=0;
+    tANI_S32 countWeight=0;
+    tANI_S32 rssicountWeight=0;
+
+    rssiWeight = ROAMING_RSSI_WEIGHT * (rssi - MIN_RSSI)
+                 /(MAX_RSSI - MIN_RSSI);
+
+    if(rssiWeight > ROAMING_RSSI_WEIGHT)
+        rssiWeight = ROAMING_RSSI_WEIGHT;
+    else if (rssiWeight < 0)
+        rssiWeight = 0;
+
+    countWeight = ROAM_AP_COUNT_WEIGHT * (count + ROAM_MIN_COUNT)
+                  /(ROAM_MAX_COUNT + ROAM_MIN_COUNT);
+
+    if(countWeight > ROAM_AP_COUNT_WEIGHT)
+        countWeight = ROAM_AP_COUNT_WEIGHT;
+
+    rssicountWeight =  ROAM_MAX_WEIGHT - (rssiWeight + countWeight);
+
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
+       FL("rssiWeight=%d, countWeight=%d, rssicountWeight=%d rssi=%d count=%d"),
+       rssiWeight, countWeight, rssicountWeight, rssi, count);
+
+    return rssicountWeight;
+}
+
+/* Calculate BSS score based on AP capabilty and channel condition
+ * for PER based roaming */
+static tANI_U32 calculateBssScore(tSirBssDescription *bssInfo,
+                             tANI_S32 best_rssi, tANI_S32 ap_cnt, tANI_S32 cca)
+{
+    tANI_S32 score = 0;
+    tANI_S32 ap_load = 0;
+    tANI_S32 normalised_width = PER_ROAM_20MHZ;
+    tANI_S32 normalised_rssi;
+    tANI_S32 channel_weight;
+    if (bssInfo->rssi) {
+        /* Calculate % of rssi we are getting
+         * max = 100
+         * min = 0
+         * less than -40 = 100%
+         * -40 - -55 = 80%
+         * -55 - -65 = 60%
+         * below that = 100 - value
+         * TODO: a linear decrement function after PER_ROAM_GOOD_RSSI_WEIGHT
+         * since throughput decrements linearly after PER_ROAM_GOOD_RSSI_WEIGHT
+         **/
+        if (bssInfo->rssi >= PER_EXCELENT_RSSI)
+            normalised_rssi = PER_ROAM_EXCELLENT_RSSI_WEIGHT;
+        else if (bssInfo->rssi >= PER_GOOD_RSSI)
+            normalised_rssi = PER_ROAM_GOOD_RSSI_WEIGHT;
+        else if (bssInfo->rssi >= PER_POOR_RSSI)
+            normalised_rssi = PER_ROAM_BAD_RSSI_WEIGHT;
+        else
+            normalised_rssi = bssInfo->rssi - MIN_RSSI;
+
+        /* Calculate score part for rssi */
+        score += (normalised_rssi * RSSI_WEIGHTAGE);
+    }
+
+    if (bssInfo->HTCapsPresent) {
+        score += PER_ROAM_MAX_WEIGHT * HT_CAPABILITY_WEIGHTAGE;
+    }
+    /* VHT caps are available */
+    if (bssInfo->vhtCapsPresent) {
+        score += PER_ROAM_MAX_WEIGHT * VHT_CAP_WEIGHTAGE;
+    }
+
+    if (bssInfo->beacomformingCapable)
+        score += PER_ROAM_MAX_WEIGHT * BEAMFORMING_CAP_WEIGHTAGE;
+
+    /* Channel width  20Mhz=30, 40Mhz=70, 80Mhz=100 */
+    if (bssInfo->chanWidth == eHT_CHANNEL_WIDTH_80MHZ)
+        normalised_width = PER_ROAM_80MHZ;
+    else if (bssInfo->chanWidth == eHT_CHANNEL_WIDTH_40MHZ)
+        normalised_width = PER_ROAM_40MHZ;
+    else
+        normalised_width = PER_ROAM_20MHZ;
+    score += normalised_width * CHAN_WIDTH_WEIGHTAGE;
+
+    /* Channel Band, Channel Number */
+    if (GetRFBand(bssInfo->channelId) == SIR_BAND_5_GHZ)
+        score += PER_ROAM_MAX_WEIGHT * CHAN_BAND_WEIGHTAGE;
+
+    /* WMM emabled */
+    if (bssInfo->wmeInfoPresent)
+        score += PER_ROAM_MAX_WEIGHT * WMM_WEIGHTAGE;
+
+#if defined(FEATURE_WLAN_ESE) || defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
+    /* AP load Ie */
+    if (bssInfo->QBSSLoad_present) {
+        /* calculate value in % */
+        ap_load = (bssInfo->QBSS_ChanLoad * PER_ROAM_MAX_WEIGHT) / MAX_AP_LOAD;
+    }
+#endif
+    //TODO we don't have this info for current AP, need to check
+    /* if CCA consideration is off in configuration, FW will send 50% for
+       every channel which should be considered as it is */
+    if (ap_load)
+        score += (100 - ap_load) * CCA_WEIGHTAGE;
+    else
+        score +=  (100 - cca) * CCA_WEIGHTAGE;
+
+    channel_weight = GetPERRoamRssiCountWeight(best_rssi, ap_cnt);
+
+    score += channel_weight * OTHER_AP_WEIGHT;
+
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_LOW,
+        FL("rssi=%d normalized_rssi=%d htcaps=%d vht=%d bw=%d channel=%d wmm=%d beamforming=%d ap_load=%d channel_weight=%d"),
+                 bssInfo->rssi, normalised_rssi, bssInfo->HTCapsPresent,
+                 bssInfo->vhtCapsPresent, bssInfo->chanWidth,
+                 bssInfo->channelId, bssInfo->wmeInfoPresent,
+                 bssInfo->beacomformingCapable, ap_load, channel_weight);
+    return score;
+}
+
+/* Calculate candidate AP score for PER based roaming */
+static tANI_S32 csrFindCongestionScore (tpAniSirGlobal pMac, tCsrScanResult *pBss)
+{
+    tANI_S32 score = 0;
+    tANI_S32 i;
+    tANI_S32 candidateApCnt, best_rssi, other_ap_cnt;
+    tANI_U32 current_timestamp;
+    tpCsrNeighborRoamControlInfo pNeighborRoamInfo =
+        &pMac->roam.neighborRoamInfo;
+
+    tSirBssDescription *bssInfo = &(pBss->Result.BssDescriptor);
+    pBss->congestionScore = 0;
+    for (i = 0; i < pMac->PERroamCandidatesCnt; i++)
+        if (pMac->candidateChannelInfo[i].channelNumber ==
+            pBss->Result.BssDescriptor.channelId)
+            break;
+
+    if (i == SIR_PER_ROAM_MAX_CANDIDATE_CNT) {
+        smsLog(pMac, LOGE,
+               FL("candidate chan info not found for channel %d bssid "
+               MAC_ADDRESS_STR), pBss->Result.BssDescriptor.channelId,
+               MAC_ADDR_ARRAY(pBss->Result.BssDescriptor.bssId));
+        return -1;
+    }
+
+    /* find best RSSI of other AP in this channel */
+    best_rssi = MIN_RSSI;
+    for (other_ap_cnt = 0; other_ap_cnt <
+             pMac->candidateChannelInfo[i].otherApCount; other_ap_cnt++) {
+        if (pMac->candidateChannelInfo[i].otherApRssi[other_ap_cnt] > best_rssi)
+            best_rssi = pMac->candidateChannelInfo[i].otherApRssi[other_ap_cnt];
+    }
+
+    score = calculateBssScore(bssInfo, best_rssi,
+                              pMac->candidateChannelInfo[i].otherApCount,
+                              pMac->candidateChannelInfo[i].channelCCA);
+    current_timestamp = jiffies_to_msecs(jiffies);
+
+    /* penalty logic */
+
+    /* In the previous list */
+    for (candidateApCnt = 0; candidateApCnt <
+             SIR_PER_ROAM_MAX_CANDIDATE_CNT; candidateApCnt++) {
+        if (sirCompareMacAddr(pMac->previousRoamApInfo[candidateApCnt].bssAddr,
+                pBss->Result.BssDescriptor.bssId) &&
+           ((current_timestamp - pMac->previousRoamApInfo[candidateApCnt].timeStamp) <
+                PENALTY_TIMEOUT)) {
+            score = (score * PENALTY_REMAINING_SCORE)/PENALTY_TOTAL_SCORE;
+            VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+                 FL("AP BSSID " MAC_ADDRESS_STR "adding penalty(in previous list)new score %d"),
+                 MAC_ADDR_ARRAY(pBss->Result.BssDescriptor.bssId),
+                 score);
+            break;
+        }
+    }
+    /* preauth failed last time */
+    for (candidateApCnt = 0; candidateApCnt <
+             MAX_NUM_PREAUTH_FAIL_LIST_ADDRESS; candidateApCnt++) {
+        if (sirCompareMacAddr(pNeighborRoamInfo->FTRoamInfo.
+                preAuthFailList.macAddress[candidateApCnt],
+                pBss->Result.BssDescriptor.bssId)) {
+            score = (score * PENALTY_REMAINING_SCORE)/PENALTY_TOTAL_SCORE;
+            VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+                 FL("AP BSSID " MAC_ADDRESS_STR "adding penalty(previously auth failed)new score %d"),
+                 MAC_ADDR_ARRAY(pBss->Result.BssDescriptor.bssId),
+                 score);
+            break;
+        }
+    }
+    pBss->congestionScore = score;
+
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+                 FL("AP BSSID " MAC_ADDRESS_STR " score  %d channel %d"),
+                 MAC_ADDR_ARRAY(pBss->Result.BssDescriptor.bssId),
+                 score, pBss->Result.BssDescriptor.channelId);
+    return 0;
+}
+
+/* Calculate current AP score for PER based roaming */
+static tANI_S32 csrFindSelfCongestionScore(tpAniSirGlobal pMac,
+                                      tSirBssDescription *bssInfo)
+{
+    tANI_S32 i, best_rssi, other_ap_cnt;
+    tANI_S32 score = 0;
+
+    for (i = 0; i <= pMac->PERroamCandidatesCnt; i++)
+        if (pMac->candidateChannelInfo[i].channelNumber == bssInfo->channelId)
+            break;
+    if (i > pMac->PERroamCandidatesCnt) {
+        /* home channel info is not present, no need to roam */
+        VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+                  FL("home channel %d congestion info not present"),
+                  bssInfo->channelId);
+        pMac->currentBssScore = PER_ROAM_MAX_BSS_SCORE;
+        return -1;
+    }
+
+    /* find best RSSI of other AP in this channel */
+    best_rssi = MIN_RSSI;
+    for (other_ap_cnt = 0; other_ap_cnt <
+             pMac->candidateChannelInfo[i].otherApCount; other_ap_cnt++) {
+        if (pMac->candidateChannelInfo[i].otherApRssi[other_ap_cnt] > best_rssi)
+            best_rssi = pMac->candidateChannelInfo[i].otherApRssi[other_ap_cnt];
+    }
+
+    score = calculateBssScore(bssInfo, best_rssi,
+                              pMac->candidateChannelInfo[i].otherApCount,
+                              pMac->candidateChannelInfo[i].channelCCA);
+    pMac->currentBssScore = score;
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+                 FL("PER Roam Current AP score  %d channel %d"),
+                 score, bssInfo->channelId);
+    return 0;
+}
+
+
+static tANI_BOOLEAN csrIsBetterBssInCongestion(tCsrScanResult *pBss1,
+                                               tCsrScanResult *pBss2)
+{
+    tANI_BOOLEAN ret;
+
+    if(CSR_IS_BETTER_PREFER_VALUE(pBss1->congestionScore,
+                                  pBss2->congestionScore))
+        ret = eANI_BOOLEAN_TRUE;
+    else
+        ret = eANI_BOOLEAN_FALSE;
+
+    return (ret);
+}
+#endif
 
 //To check whther pBss1 is better than pBss2
 static tANI_BOOLEAN csrIsBetterBss(tCsrScanResult *pBss1, tCsrScanResult *pBss2)
@@ -2168,7 +2421,9 @@
     tDot11fBeaconIEs *pIes, *pNewIes;
     tANI_BOOLEAN fMatch;
     tANI_U16 i = 0;
-    
+    tCsrRoamSession *pSession = CSR_GET_SESSION(pMac,
+                                    pMac->roam.roamSession->sessionId);
+
     if(phResult)
     {
         *phResult = CSR_INVALID_SCANRESULT_HANDLE;
@@ -2285,7 +2540,13 @@
         vos_mem_set(pRetList, sizeof(tScanResultList), 0);
         csrLLOpen(pMac->hHdd, &pRetList->List);
         pRetList->pCurEntry = NULL;
-        
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+        if (pFilter && pFilter->isPERRoamScan)
+            if (pSession && pSession->pConnectBssDesc)
+               csrFindSelfCongestionScore(pMac,
+                                          pSession->pConnectBssDesc);
+#endif
+
         csrLLLock(&pMac->scan.scanResultList);
         pEntry = csrLLPeekHead( &pMac->scan.scanResultList, LL_ACCESS_NOLOCK );
         while( pEntry ) 
@@ -2377,7 +2638,22 @@
                 //No need to lock pRetList because it is locally allocated and no outside can access it at this time
                 if(csrLLIsListEmpty(&pRetList->List, LL_ACCESS_NOLOCK))
                 {
-                    csrLLInsertTail(&pRetList->List, &pResult->Link, LL_ACCESS_NOLOCK);
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+                    if (pFilter && pFilter->isPERRoamScan) {
+                         csrFindCongestionScore(pMac, pResult);
+                         if (pResult->congestionScore > pMac->currentBssScore) {
+                             csrLLInsertTail(&pRetList->List, &pResult->Link,
+                                             LL_ACCESS_NOLOCK);
+                             smsLog(pMac, LOGW,
+                                  FL("added one entry in LL in PER Roam list"));
+                         }
+                    }
+                    else
+#endif
+                    {
+                        csrLLInsertTail(&pRetList->List, &pResult->Link,
+                                        LL_ACCESS_NOLOCK);
+                    }
                 }
                 else
                 {
@@ -2389,20 +2665,48 @@
                     while(pTmpEntry)
                     {
                         pTmpResult = GET_BASE_ADDR( pTmpEntry, tCsrScanResult, Link );
-                        if(csrIsBetterBss(pResult, pTmpResult))
-                        {
-                            csrLLInsertEntry(&pRetList->List, pTmpEntry, &pResult->Link, LL_ACCESS_NOLOCK);
-                            //To indicate we are done
-                            pResult = NULL;
-                            break;
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+                        if (pFilter && pFilter->isPERRoamScan) {
+                            csrFindCongestionScore(pMac, pResult);
+                            if(csrIsBetterBssInCongestion(pResult, pTmpResult)&&
+                             (pResult->congestionScore > pMac->currentBssScore))
+                            {
+                                csrLLInsertEntry(&pRetList->List, pTmpEntry,
+                                              &pResult->Link, LL_ACCESS_NOLOCK);
+                                smsLog(pMac, LOGW,
+                                    FL("added another entry in LL in PER Roam list"));
+                                pResult = NULL;
+                                break;
+                            }
+                            pTmpEntry = csrLLNext(&pRetList->List,
+                                                  pTmpEntry, LL_ACCESS_NOLOCK);
                         }
-                        pTmpEntry = csrLLNext(&pRetList->List, pTmpEntry, LL_ACCESS_NOLOCK);
+                        else
+#endif
+                        {
+                            if(csrIsBetterBss(pResult, pTmpResult))
+                            {
+                                csrLLInsertEntry(&pRetList->List, pTmpEntry,
+                                              &pResult->Link, LL_ACCESS_NOLOCK);
+                                //To indicate we are done
+                                pResult = NULL;
+                                break;
+                            }
+                            pTmpEntry = csrLLNext(&pRetList->List,
+                                                  pTmpEntry, LL_ACCESS_NOLOCK);
+                       }
                     }
                     if(pResult != NULL)
-                    {
-                        //This one is not better than any one
-                        csrLLInsertTail(&pRetList->List, &pResult->Link, LL_ACCESS_NOLOCK);
-                    }
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+                        if ((pFilter && !pFilter->isPERRoamScan) ||
+                            (pFilter == NULL) ||
+                            (pResult->congestionScore > pMac->currentBssScore))
+#endif
+                        {
+                            //This one is not better than any one
+                            csrLLInsertTail(&pRetList->List,
+                                            &pResult->Link, LL_ACCESS_NOLOCK);
+                        }
                 }
                 count++;
             }
@@ -2425,6 +2729,8 @@
                 csrLLClose(&pRetList->List);
                 vos_mem_free(pRetList);
                 status = eHAL_STATUS_E_NULL_VALUE;
+                smsLog(pMac, LOGW,
+                       FL("Nil scan results or no matching AP found"));
             }
             else if(phResult)
             {
diff --git a/CORE/SME/src/csr/csrInsideApi.h b/CORE/SME/src/csr/csrInsideApi.h
index 8ee20e7..c11d5e0 100644
--- a/CORE/SME/src/csr/csrInsideApi.h
+++ b/CORE/SME/src/csr/csrInsideApi.h
@@ -112,6 +112,43 @@
 #define CSR_JOIN_RETRY_TIMEOUT_PERIOD        ( 1 *  PAL_TIMER_TO_SEC_UNIT )  // 1 second
 #endif
 
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+#define ROAMING_RSSI_WEIGHT 50
+#define MIN_RSSI (-100)
+#define MAX_RSSI 0
+#define ROAM_AP_COUNT_WEIGHT 50
+#define ROAM_MAX_COUNT 30
+#define ROAM_MIN_COUNT 0
+#define ROAM_MAX_WEIGHT 100
+
+#define RSSI_WEIGHTAGE 25
+#define HT_CAPABILITY_WEIGHTAGE 10
+#define VHT_CAP_WEIGHTAGE 6
+#define BEAMFORMING_CAP_WEIGHTAGE 2
+#define CHAN_WIDTH_WEIGHTAGE 10
+#define CHAN_BAND_WEIGHTAGE 5
+#define WMM_WEIGHTAGE 2
+#define CCA_WEIGHTAGE 10
+#define OTHER_AP_WEIGHT 30
+
+#define MAX_AP_LOAD 255
+#define PENALTY_TIMEOUT (30 * 60 * 1000)
+#define PENALTY_REMAINING_SCORE (7)
+#define PENALTY_TOTAL_SCORE (10)
+#define PER_EXCELENT_RSSI -40
+#define PER_GOOD_RSSI -55
+#define PER_POOR_RSSI -65
+#define PER_ROAM_EXCELLENT_RSSI_WEIGHT 100
+#define PER_ROAM_GOOD_RSSI_WEIGHT 80
+#define PER_ROAM_BAD_RSSI_WEIGHT 60
+#define PER_ROAM_MAX_WEIGHT 100
+#define PER_ROAM_80MHZ 100
+#define PER_ROAM_40MHZ 70
+#define PER_ROAM_20MHZ 30
+#define PER_ROAM_PENALTY (3/10)
+#define PER_ROAM_MAX_BSS_SCORE 10000
+#endif
+
 typedef enum 
 {
     eCsrNextScanNothing,
@@ -170,7 +207,9 @@
     eCsrEncryptionType ucEncryptionType; //Preferred Encryption type that matched with profile.
     eCsrEncryptionType mcEncryptionType; 
     eCsrAuthType authType; //Preferred auth type that matched with the profile.
-
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+    int congestionScore;
+#endif
     tCsrScanResultInfo Result;
 }tCsrScanResult;
 
diff --git a/CORE/SME/src/csr/csrNeighborRoam.c b/CORE/SME/src/csr/csrNeighborRoam.c
index b889aa6..14f6756 100644
--- a/CORE/SME/src/csr/csrNeighborRoam.c
+++ b/CORE/SME/src/csr/csrNeighborRoam.c
@@ -1381,6 +1381,10 @@
        pScanFilter->ChannelInfo.numOfChannels = 0;
        pScanFilter->ChannelInfo.ChannelList = NULL;
     }
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+    if (pMac->PERroamCandidatesCnt)
+       pScanFilter->isPERRoamScan = true;
+#endif
 #ifdef WLAN_FEATURE_VOWIFI_11R
     if (pNeighborRoamInfo->is11rAssoc)
     {
@@ -2212,10 +2216,15 @@
 
                     else
                     {
-                        /* There is no candidate or We are not roaming Now.
-                         * Inform the FW to restart Roam Offload Scan  */
-                        csrRoamOffloadScan(pMac, ROAM_SCAN_OFFLOAD_RESTART,
-                            REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW);
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+                        if (pMac->PERroamCandidatesCnt == 0)
+#endif
+                        {
+                            /* There is no candidate or We are not roaming Now.
+                             * Inform the FW to restart Roam Offload Scan  */
+                            csrRoamOffloadScan(pMac, ROAM_SCAN_OFFLOAD_RESTART,
+                                REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW);
+                        }
                     }
                 }
                 CSR_NEIGHBOR_ROAM_STATE_TRANSITION(eCSR_NEIGHBOR_ROAM_STATE_CONNECTED);
diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c
index 75a4c37..3dc10cd 100644
--- a/CORE/SME/src/sme_common/sme_Api.c
+++ b/CORE/SME/src/sme_common/sme_Api.c
@@ -6194,6 +6194,90 @@
 }
 #endif /* WLAN_FEATURE_RMC */
 
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+void sme_PERRoamScanStartStop(void *hHal, tANI_U8 start)
+{
+    eHalStatus            status;
+    VOS_STATUS            vosStatus;
+    tpAniSirGlobal        pMac = PMAC_STRUCT(hHal);
+    vos_msg_t             vosMessage;
+    tPERRoamScanStart     *pMsg;
+
+    status = sme_AcquireGlobalLock(&pMac->sme);
+    if ( eHAL_STATUS_SUCCESS == status)
+    {
+        pMsg = (tPERRoamScanStart *)
+                   vos_mem_malloc(sizeof(tPERRoamScanStart));
+        if (NULL == pMsg)
+        {
+            VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+                   "%s: Failed to allocate memory", __func__);
+            sme_ReleaseGlobalLock( &pMac->sme );
+            return;
+        }
+
+        pMsg->msgType = WDA_PER_ROAM_SCAN_TRIGGER_REQ;
+        pMsg->msgLen = (tANI_U16)sizeof(*pMsg);
+
+        pMsg->start = start;
+
+        vosMessage.type = WDA_PER_ROAM_SCAN_TRIGGER_REQ;
+        vosMessage.bodyptr = pMsg;
+        vosMessage.reserved = 0;
+
+        MTRACE(vos_trace(VOS_MODULE_ID_SME,
+                 TRACE_CODE_SME_TX_WDA_MSG, NO_SESSION, vosMessage.type));
+        vosStatus = vos_mq_post_message(VOS_MQ_ID_WDA, &vosMessage );
+        if ( !VOS_IS_STATUS_SUCCESS(vosStatus) )
+        {
+           VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+                         "%s: Post TX Fail monitor Start MSG fail", __func__);
+           vos_mem_free(pMsg);
+           status = eHAL_STATUS_FAILURE;
+        }
+        sme_ReleaseGlobalLock(&pMac->sme);
+    }
+}
+
+/* sme_set_per_roam_rxconfig : set PER config for Rx monitoring
+ * @hHal: hal pointer
+ * @staId: station id
+ * @minRate : rate at which to start monitoring
+ * @maxRate : Rate at which to stop monitoring
+ * @minPercentage: minimum % of packets required in minRate to trigger a scan
+ * @minPktRequired: minimum number of packets required to trigger a scan
+ * @waitPeriodForNextPERScan: time to wait before start monitoring again once
+ * roam scan is triggered
+ * */
+
+VOS_STATUS sme_set_per_roam_rxconfig (tHalHandle hHal, v_U8_t staId,
+                             v_U16_t minRate, v_U16_t maxRate,
+                             v_U8_t minPercentage, v_U16_t minPktRequired,
+                             v_U64_t waitPeriodForNextPERScan)
+{
+    tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
+    v_VOID_t * pVosContext = vos_get_global_context(VOS_MODULE_ID_SME, NULL);
+    WLANTL_StartRxRateMonitor(pVosContext, staId, minRate, maxRate,
+                    minPercentage, minPktRequired,
+                    (void *) hHal, waitPeriodForNextPERScan,
+                    sme_PERRoamScanStartStop);
+    pMac->PERroamCandidatesCnt = 0;
+    return eHAL_STATUS_SUCCESS;
+}
+
+/* sme_unset_per_roam_rxconfig : unset PER config for Rx monitoring
+ * */
+VOS_STATUS sme_unset_per_roam_rxconfig (tHalHandle hHal)
+{
+    tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
+    v_VOID_t * pVosContext = vos_get_global_context(VOS_MODULE_ID_SME, NULL);
+    vos_mem_set(pMac->previousRoamApInfo,
+                sizeof(tSirRoamAPInfo) * SIR_PER_ROAM_MAX_CANDIDATE_CNT, 0);
+    WLANTL_StopRxRateMonitor(pVosContext);
+    return eHAL_STATUS_SUCCESS;
+}
+#endif
+
 /* ---------------------------------------------------------------------------
     \fn sme_BtcSignalBtEvent
     \brief  API to signal Bluetooth (BT) event to the WLAN driver. Based on the
diff --git a/CORE/SYS/legacy/src/utils/src/macTrace.c b/CORE/SYS/legacy/src/utils/src/macTrace.c
index 8c38da2..12a8a6b 100644
--- a/CORE/SYS/legacy/src/utils/src/macTrace.c
+++ b/CORE/SYS/legacy/src/utils/src/macTrace.c
@@ -809,6 +809,10 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
         CASE_RETURN_STRING(WDA_ROAM_SCAN_OFFLOAD_REQ);
         CASE_RETURN_STRING(WDA_ROAM_SCAN_OFFLOAD_RSP);
+        CASE_RETURN_STRING(WDA_PER_ROAM_SCAN_OFFLOAD_REQ);
+        CASE_RETURN_STRING(WDA_PER_ROAM_SCAN_OFFLOAD_RSP);
+        CASE_RETURN_STRING(WDA_PER_ROAM_SCAN_TRIGGER_REQ);
+        CASE_RETURN_STRING(WDA_PER_ROAM_SCAN_TRIGGER_RSP);
 #endif
 #ifdef WLAN_WAKEUP_EVENTS
         CASE_RETURN_STRING(WDA_WAKE_REASON_IND);
diff --git a/CORE/SYS/legacy/src/utils/src/parserApi.c b/CORE/SYS/legacy/src/utils/src/parserApi.c
index fd22112..3ae3f50 100644
--- a/CORE/SYS/legacy/src/utils/src/parserApi.c
+++ b/CORE/SYS/legacy/src/utils/src/parserApi.c
@@ -2354,7 +2354,7 @@
     }
 #endif
 
-#if defined FEATURE_WLAN_ESE
+#if defined(FEATURE_WLAN_ESE) || defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
     if (pr->QBSSLoad.present)
     {
         vos_mem_copy(&pProbeResp->QBSSLoad, &pr->QBSSLoad, sizeof(tDot11fIEQBSSLoad));
diff --git a/CORE/VOSS/inc/vos_memory.h b/CORE/VOSS/inc/vos_memory.h
index c6de77e..fd064c6 100644
--- a/CORE/VOSS/inc/vos_memory.h
+++ b/CORE/VOSS/inc/vos_memory.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -144,7 +144,15 @@
   --------------------------------------------------------------------------*/
 v_VOID_t vos_mem_zero( v_VOID_t *ptr, v_SIZE_t numBytes );
 
+static __inline__ unsigned long vos_htonl(unsigned long ul)
+{
+  return( ( ( ul & 0x000000ff ) << 24 ) |
+          ( ( ul & 0x0000ff00 ) <<  8 ) |
+          ( ( ul & 0x00ff0000 ) >>  8 ) |
+          ( ( ul & 0xff000000 ) >> 24 )   );
+}
 
+void vos_buff_to_hl_buff (v_U8_t *buffer, int size);
 /*----------------------------------------------------------------------------
   
   \brief vos_mem_copy() - Copy memory
diff --git a/CORE/VOSS/src/vos_memory.c b/CORE/VOSS/src/vos_memory.c
index 76db5f9..e87cfd3 100644
--- a/CORE/VOSS/src/vos_memory.c
+++ b/CORE/VOSS/src/vos_memory.c
@@ -450,6 +450,20 @@
    memset(ptr, value, numBytes);
 }
 
+void vos_buff_to_hl_buff (tANI_U8 *buffer, int size)
+{
+    int *val, i;
+    if (size % 4 != 0)
+        VOS_TRACE(VOS_MODULE_ID_PE,VOS_TRACE_LEVEL_ERROR,
+                  "%s: size should be multiple of 4, size %d",
+                  __func__, size);
+
+    val = (int *)buffer;
+
+    for (i=0; i<(size/4); i++)
+        *(val+i) = vos_htonl ((unsigned long)(*(val+i)));
+}
+
 v_VOID_t vos_mem_zero( v_VOID_t *ptr, v_SIZE_t numBytes )
 {
    if (0 == numBytes)
diff --git a/CORE/WDA/inc/wlan_qct_wda.h b/CORE/WDA/inc/wlan_qct_wda.h
index ee47989..ef48cd8 100644
--- a/CORE/WDA/inc/wlan_qct_wda.h
+++ b/CORE/WDA/inc/wlan_qct_wda.h
@@ -810,6 +810,7 @@
 #  define WDA_GET_OFFLOADSCANLEARN(pRxMeta) (((WDI_DS_RxMetaInfoType*)(pRxMeta))->offloadScanLearn)
 /* WDA_GET_ROAMCANDIDATEIND **************************************************/
 #  define WDA_GET_ROAMCANDIDATEIND(pRxMeta) (((WDI_DS_RxMetaInfoType*)(pRxMeta))->roamCandidateInd)
+#  define WDA_IF_PER_ROAMCANDIDATEIND(pRxMeta) (((WDI_DS_RxMetaInfoType*)(pRxMeta))->perRoamCndInd)
 #endif
 #ifdef WLAN_FEATURE_EXTSCAN
 #define WDA_GET_EXTSCANFULLSCANRESIND(pRxMeta) (((WDI_DS_RxMetaInfoType*)(pRxMeta))->extscanBuffer)
@@ -1181,6 +1182,10 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
 #define WDA_ROAM_SCAN_OFFLOAD_REQ   SIR_HAL_ROAM_SCAN_OFFLOAD_REQ
 #define WDA_ROAM_SCAN_OFFLOAD_RSP   SIR_HAL_ROAM_SCAN_OFFLOAD_RSP
+#define WDA_PER_ROAM_SCAN_OFFLOAD_REQ   SIR_HAL_PER_ROAM_SCAN_OFFLOAD_REQ
+#define WDA_PER_ROAM_SCAN_OFFLOAD_RSP   SIR_HAL_PER_ROAM_SCAN_OFFLOAD_RSP
+#define WDA_PER_ROAM_SCAN_TRIGGER_REQ   SIR_HAL_PER_ROAM_SCAN_TRIGGER_REQ
+#define WDA_PER_ROAM_SCAN_TRIGGER_RSP   SIR_HAL_PER_ROAM_SCAN_TRIGGER_RSP
 #endif
 
 #ifdef WLAN_WAKEUP_EVENTS
diff --git a/CORE/WDA/src/wlan_qct_wda.c b/CORE/WDA/src/wlan_qct_wda.c
index fedb845..706940d 100644
--- a/CORE/WDA/src/wlan_qct_wda.c
+++ b/CORE/WDA/src/wlan_qct_wda.c
@@ -177,7 +177,11 @@
 #endif // FEATURE_WLAN_SCAN_PNO
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
 VOS_STATUS WDA_ProcessRoamScanOffloadReq(tWDA_CbContext *pWDA,tSirRoamOffloadScanReq *pRoamOffloadScanReqParams);
+VOS_STATUS WDA_ProcessPERRoamScanOffloadReq(tWDA_CbContext *pWDA,
+                    tSirPERRoamOffloadScanReq *pPERRoamOffloadScanReqParams);
 void WDA_RoamOffloadScanReqCallback(WDI_Status status, void* pUserData);
+void WDA_PERRoamOffloadScanReqCallback(WDI_Status status, void* pUserData);
+void WDA_PERRoamTriggerScanReqCallback(WDI_Status status, void* pUserData);
 void WDA_ConvertSirAuthToWDIAuth(WDI_AuthType *AuthType, v_U8_t csrAuthType);
 void WDA_ConvertSirEncToWDIEnc(WDI_EdType *EncrType, v_U8_t csrEncrType);
 #endif
@@ -14758,6 +14762,63 @@
 }
 #endif /* WLAN_FEATURE_RMC */
 
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+VOS_STATUS WDA_ProcessPERRoamScanTriggerReq(tWDA_CbContext *pWDA,
+                    tPERRoamScanStart *pPERRoamTriggerScanReqParams)
+{
+   WDI_Status status;
+   tWDA_ReqParams *pWdaParams ;
+   WDI_PERRoamTriggerScanInfo *pwdiPERRoamTriggerScanInfo =
+       (WDI_PERRoamTriggerScanInfo *)vos_mem_malloc(
+                                         sizeof(WDI_PERRoamTriggerScanInfo));
+
+   VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+              "------> %s " ,__func__);
+
+   if (NULL == pwdiPERRoamTriggerScanInfo)
+   {
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+                           "%s: VOS MEM Alloc Failure", __func__);
+      VOS_ASSERT(0);
+      return VOS_STATUS_E_NOMEM;
+   }
+
+   pWdaParams = (tWDA_ReqParams *)vos_mem_malloc(sizeof(tWDA_ReqParams)) ;
+   if (NULL == pWdaParams)
+   {
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+                           "%s: VOS MEM Alloc Failure", __func__);
+      VOS_ASSERT(0);
+      vos_mem_free(pwdiPERRoamTriggerScanInfo);
+      return VOS_STATUS_E_NOMEM;
+   }
+
+   pwdiPERRoamTriggerScanInfo->roamScanReq =
+                                    pPERRoamTriggerScanReqParams->start;
+
+   /* Store Params pass it to WDI */
+   pWdaParams->wdaWdiApiMsgParam = (void *)pwdiPERRoamTriggerScanInfo;
+   pWdaParams->pWdaContext = pWDA;
+
+   /* Store param pointer as passed in by caller */
+   pWdaParams->wdaMsgParam = pPERRoamTriggerScanReqParams;
+   status = WDI_PERRoamScanTriggerReq(pwdiPERRoamTriggerScanInfo,
+                    (WDI_PERRoamTriggerScanCb)WDA_PERRoamTriggerScanReqCallback,
+                    pWdaParams);
+
+   if(IS_WDI_STATUS_FAILURE(status))
+   {
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+              "Failure in Start Roam Candidate trigger Req WDI API" );
+      vos_mem_free(pWdaParams->wdaWdiApiMsgParam) ;
+      vos_mem_free(pWdaParams->wdaMsgParam);
+      pWdaParams->wdaWdiApiMsgParam = NULL;
+      pWdaParams->wdaMsgParam = NULL;
+   }
+   return CONVERT_WDI2VOS_STATUS(status) ;
+}
+#endif
+
 /*
  * FUNCTION: WDA_ProcessSetSpoofMacAddrReq
  *
@@ -15693,6 +15754,11 @@
          WDA_ProcessRoamScanOffloadReq(pWDA, (tSirRoamOffloadScanReq *)pMsg->bodyptr);
          break;
       }
+      case WDA_PER_ROAM_SCAN_OFFLOAD_REQ:
+      {
+         WDA_ProcessPERRoamScanOffloadReq(pWDA, (tSirPERRoamOffloadScanReq *)pMsg->bodyptr);
+         break;
+      }
 #endif
       case WDA_SET_TX_PER_TRACKING_REQ:
       {
@@ -15851,7 +15917,14 @@
          break;
       }
 #endif /* WLAN_FEATURE_RMC */
-
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+      case WDA_PER_ROAM_SCAN_TRIGGER_REQ:
+      {
+         WDA_ProcessPERRoamScanTriggerReq(pWDA,
+                                          (tPERRoamScanStart *)pMsg->bodyptr);
+         break;
+      }
+#endif
 #ifdef FEATURE_WLAN_BATCH_SCAN
       case WDA_SET_BATCH_SCAN_REQ:
       {
@@ -18000,6 +18073,69 @@
    }
 }
 
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+VOS_STATUS WDA_ProcessPERRoamScanOffloadReq(tWDA_CbContext *pWDA,
+                    tSirPERRoamOffloadScanReq *pPERRoamOffloadScanReqParams)
+{
+   WDI_Status status;
+   tWDA_ReqParams *pWdaParams ;
+   WDI_PERRoamOffloadScanInfo *pwdiPERRoamOffloadScanInfo;
+
+   VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+              "------> %s " ,__func__);
+
+   pwdiPERRoamOffloadScanInfo = (WDI_PERRoamOffloadScanInfo *)
+                             vos_mem_malloc(sizeof(WDI_PERRoamOffloadScanInfo));
+   if (NULL == pwdiPERRoamOffloadScanInfo) {
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+                           "%s: VOS MEM Alloc Failure", __func__);
+      VOS_ASSERT(0);
+      return VOS_STATUS_E_NOMEM;
+   }
+
+   pWdaParams = (tWDA_ReqParams *)vos_mem_malloc(sizeof(tWDA_ReqParams)) ;
+   if (NULL == pWdaParams) {
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+                           "%s: VOS MEM Alloc Failure", __func__);
+      VOS_ASSERT(0);
+      vos_mem_free(pwdiPERRoamOffloadScanInfo);
+      return VOS_STATUS_E_NOMEM;
+   }
+
+   pwdiPERRoamOffloadScanInfo->rateUpThreshold =
+          pPERRoamOffloadScanReqParams->rateUpThreshold;
+   pwdiPERRoamOffloadScanInfo->rateDownThreshold =
+          pPERRoamOffloadScanReqParams->rateDownThreshold;
+   pwdiPERRoamOffloadScanInfo->waitPeriodForNextPERScan =
+          pPERRoamOffloadScanReqParams->waitPeriodForNextPERScan;
+   pwdiPERRoamOffloadScanInfo->PERtimerThreshold =
+          pPERRoamOffloadScanReqParams->PERtimerThreshold;
+   pwdiPERRoamOffloadScanInfo->isPERRoamCCAEnabled =
+          pPERRoamOffloadScanReqParams->isPERRoamCCAEnabled;
+   pwdiPERRoamOffloadScanInfo->PERroamTriggerPercent =
+          pPERRoamOffloadScanReqParams->PERroamTriggerPercent;
+
+   /* Store Params pass it to WDI */
+   pWdaParams->wdaWdiApiMsgParam = (void *)pwdiPERRoamOffloadScanInfo;
+   pWdaParams->pWdaContext = pWDA;
+
+   /* Store param pointer as passed in by caller */
+   pWdaParams->wdaMsgParam = pPERRoamOffloadScanReqParams;
+   status = WDI_PERRoamScanOffloadReq(pwdiPERRoamOffloadScanInfo,
+                    (WDI_PERRoamOffloadScanCb)WDA_PERRoamOffloadScanReqCallback,
+                    pWdaParams);
+
+   if(IS_WDI_STATUS_FAILURE(status)) {
+      VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+              "Failure in Send config PER roam params");
+      vos_mem_free(pWdaParams->wdaWdiApiMsgParam) ;
+      vos_mem_free(pWdaParams->wdaMsgParam);
+      vos_mem_free(pWdaParams);
+   }
+   return CONVERT_WDI2VOS_STATUS(status) ;
+}
+#endif
+
 /*
  * FUNCTION: WDA_ProcessRoamScanOffloadReq
  * Request to WDI to set Roam Offload Scan
@@ -18395,6 +18531,75 @@
 
    return ;
 }
+
+void WDA_PERRoamTriggerScanReqCallback(WDI_Status status, void* pUserData)
+{
+   tWDA_ReqParams *pWdaParams = (tWDA_ReqParams *)pUserData;
+   vos_msg_t vosMsg;
+   wpt_uint8 reason = 0;
+
+   VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+                                          "<------ %s " ,__func__);
+   if (NULL == pWdaParams) {
+     VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+             "%s: pWdaParams received NULL", __func__);
+     VOS_ASSERT(0) ;
+     return ;
+   }
+   if ( pWdaParams->wdaMsgParam != NULL)
+      vos_mem_free(pWdaParams->wdaMsgParam);
+
+   vos_mem_free(pWdaParams) ;
+   vosMsg.type = eWNI_SME_ROAM_SCAN_TRIGGER_RSP;
+   vosMsg.bodyptr = NULL;
+   if (WDI_STATUS_SUCCESS != status)
+      reason = 0;
+
+   vosMsg.bodyval = reason;
+   if (VOS_STATUS_SUCCESS !=
+       vos_mq_post_message(VOS_MQ_ID_SME, (vos_msg_t*)&vosMsg)) {
+      /* free the mem and return */
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+                 "%s: Failed to post the rsp to UMAC", __func__);
+   }
+
+   return ;
+}
+
+
+void WDA_PERRoamOffloadScanReqCallback(WDI_Status status, void* pUserData)
+{
+   tWDA_ReqParams *pWdaParams = (tWDA_ReqParams *)pUserData;
+   vos_msg_t vosMsg;
+   wpt_uint8 reason = 0;
+
+   VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+                                          "<------ %s " ,__func__);
+   if (NULL == pWdaParams) {
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+              "%s: pWdaParams received NULL", __func__);
+      VOS_ASSERT(0) ;
+      return ;
+   }
+   if ( pWdaParams->wdaMsgParam != NULL)
+      vos_mem_free(pWdaParams->wdaMsgParam);
+
+   vos_mem_free(pWdaParams) ;
+   vosMsg.type = eWNI_SME_ROAM_SCAN_OFFLOAD_RSP;
+   vosMsg.bodyptr = NULL;
+   if (WDI_STATUS_SUCCESS != status)
+      reason = 0;
+
+   vosMsg.bodyval = reason;
+   if (VOS_STATUS_SUCCESS !=
+       vos_mq_post_message(VOS_MQ_ID_SME, (vos_msg_t*)&vosMsg)) {
+      /* free the mem and return */
+      VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+                 "%s: Failed to post the rsp to UMAC", __func__);
+   }
+
+   return ;
+}
 #endif
 
 /*
diff --git a/CORE/WDI/CP/inc/wlan_qct_wdi.h b/CORE/WDI/CP/inc/wlan_qct_wdi.h
index 36afdc7..b44e3f7 100644
--- a/CORE/WDI/CP/inc/wlan_qct_wdi.h
+++ b/CORE/WDI/CP/inc/wlan_qct_wdi.h
@@ -5582,6 +5582,22 @@
    function pointer will be called */
    void*                      pUserData;
 } WDI_RoamScanOffloadReqParamsType;
+
+typedef struct
+{
+  wpt_uint32  requestId;
+  wpt_uint32  rateUpThreshold;
+  wpt_uint32  rateDownThreshold;
+  wpt_uint32  isPERRoamCCAEnabled;
+  wpt_uint32  waitPeriodForNextPERScan;
+  wpt_uint32  PERtimerThreshold;
+  wpt_uint32  PERroamTriggerPercent;
+} WDI_PERRoamOffloadScanInfo;
+
+typedef struct
+{
+  wpt_boolean roamScanReq; // Request info for all peers
+} WDI_PERRoamTriggerScanInfo;
 #endif //WLAN_FEATURE_ROAM_SCAN_OFFLOAD
 
 /*---------------------------------------------------------------------------
@@ -8061,6 +8077,8 @@
 typedef void  (*WDI_RoamOffloadScanCb)(WDI_Status  wdiStatus,
                                        void*       pUserData);
 
+typedef void (*WDI_PERRoamOffloadScanCb)(WDI_Status wdiStatus, void *pUserData);
+typedef void (*WDI_PERRoamTriggerScanCb)(WDI_Status wdiStatus, void *pUserData);
 #endif
 /*---------------------------------------------------------------------------
    WDI_SetTxPerTrackingRspCb
@@ -11195,6 +11213,22 @@
   WDI_RoamOffloadScanCb                 wdiRoamOffloadScancb,
   void*                                 pUserData
 );
+
+WDI_Status
+WDI_PERRoamScanOffloadReq
+(
+  WDI_PERRoamOffloadScanInfo              *pwdiRoamScanOffloadReqParams,
+  WDI_PERRoamOffloadScanCb                 wdiRoamOffloadScancb,
+  void*                                    pUserData
+);
+
+WDI_Status
+WDI_PERRoamScanTriggerReq
+(
+  WDI_PERRoamTriggerScanInfo              *pwdiRoamScanTriggerReqParams,
+  WDI_PERRoamTriggerScanCb                 wdiRoamTriggerScancb,
+  void*                                    pUserData
+);
 #endif
 
 /**
diff --git a/CORE/WDI/CP/inc/wlan_qct_wdi_bd.h b/CORE/WDI/CP/inc/wlan_qct_wdi_bd.h
index 59de6aa..e42c183 100644
--- a/CORE/WDI/CP/inc/wlan_qct_wdi_bd.h
+++ b/CORE/WDI/CP/inc/wlan_qct_wdi_bd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 2016 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -451,7 +451,8 @@
         wpt_uint32 reorderSlotIdx:6;
         
 #ifdef WCN_PRONTO
-        wpt_uint32 reserved7: 2;
+        wpt_uint32 reserved7: 1;
+        wpt_uint32 perRoamCndInd:1;
         wpt_uint32 outOfOrderForward: 1;
         wpt_uint32 reorderEnable: 1;
 #else
@@ -463,7 +464,8 @@
 #ifdef WCN_PRONTO
         wpt_uint32 reorderEnable: 1;
         wpt_uint32 outOfOrderForward: 1;
-        wpt_uint32 reserved7: 2;
+        wpt_uint32 perRoamCndInd:1;
+        wpt_uint32 reserved7: 1;
 #else
         wpt_uint32 reserved7:4;
 #endif //WCN_PRONTO
diff --git a/CORE/WDI/CP/inc/wlan_qct_wdi_dp.h b/CORE/WDI/CP/inc/wlan_qct_wdi_dp.h
index 77bd475..465946e 100644
--- a/CORE/WDI/CP/inc/wlan_qct_wdi_dp.h
+++ b/CORE/WDI/CP/inc/wlan_qct_wdi_dp.h
@@ -306,6 +306,7 @@
 //LFR scan related
 #define WDI_RX_BD_GET_OFFLOADSCANLEARN( _pvBDHeader )         (((WDI_RxBdType*)_pvBDHeader)->offloadScanLearn)
 #define WDI_RX_BD_GET_ROAMCANDIDATEIND( _pvBDHeader )         (((WDI_RxBdType*)_pvBDHeader)->roamCandidateInd)
+#define WDI_RX_BD_GET_PER_ROAMCANDIDATEIND( _pvBDHeader )     (((WDI_RxBdType*)_pvBDHeader)->perRoamCndInd)
 #endif
 #ifdef WLAN_FEATURE_EXTSCAN
 #define WDI_RX_BD_GET_EXTSCANFULLSCANRESIND( _pvBDHeader ) (((WDI_RxBdType*)_pvBDHeader)->extscanBuffer)
diff --git a/CORE/WDI/CP/inc/wlan_qct_wdi_i.h b/CORE/WDI/CP/inc/wlan_qct_wdi_i.h
index 094ece3..7debb41 100644
--- a/CORE/WDI/CP/inc/wlan_qct_wdi_i.h
+++ b/CORE/WDI/CP/inc/wlan_qct_wdi_i.h
@@ -489,6 +489,11 @@
   WDI_STOP_RSSI_MONITOR_REQ                      = 114,
   WDI_WIFI_CONFIG_SET_REQ                        = 115,
 
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+  WDI_PER_ROAM_SCAN_OFFLOAD_REQ                  = 116,
+  WDI_PER_ROAM_SCAN_TRIGGER_REQ                  = 117,
+#endif
+
   WDI_MAX_REQ,
 
   /*Send a suspend Indication down to HAL*/
@@ -861,6 +866,10 @@
   WDI_STOP_RSSI_MONITOR_RSP                      = 114,
 
   WDI_WIFI_CONFIG_SET_RSP                        = 115,
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+  WDI_PER_ROAM_SCAN_OFFLOAD_RSP                  = 116,
+  WDI_PER_ROAM_SCAN_TRIGGER_RSP                  = 117,
+#endif
 
   /*-------------------------------------------------------------------------
     Indications
@@ -5576,6 +5585,14 @@
   WDI_ControlBlockType*  pWDICtx,
   WDI_EventInfoType*     pEventData
 );
+
+WDI_Status
+WDI_ProcessPERRoamScanOffloadReq(WDI_ControlBlockType *pWDICtx,
+                                 WDI_EventInfoType *pEventData);
+
+WDI_Status
+WDI_ProcessPERRoamScanTriggerReq(WDI_ControlBlockType *pWDICtx,
+                                 WDI_EventInfoType *pEventData);
 /**
  @brief Process Start Roam Candidate Lookup Response function (called when a
         response is being received over the bus from HAL)
@@ -5592,6 +5609,20 @@
   WDI_ControlBlockType*  pWDICtx,
   WDI_EventInfoType*     pEventData
 );
+
+WDI_Status
+WDI_ProcessPERRoamScanOffloadRsp
+(
+  WDI_ControlBlockType*  pWDICtx,
+  WDI_EventInfoType*     pEventData
+);
+
+WDI_Status
+WDI_ProcessPERRoamScanTriggerRsp
+(
+  WDI_ControlBlockType*  pWDICtx,
+  WDI_EventInfoType*     pEventData
+);
 #endif
 
 #ifdef WLAN_FEATURE_PACKET_FILTERING
diff --git a/CORE/WDI/CP/src/wlan_qct_wdi.c b/CORE/WDI/CP/src/wlan_qct_wdi.c
index e0ce18f..381006f 100644
--- a/CORE/WDI/CP/src/wlan_qct_wdi.c
+++ b/CORE/WDI/CP/src/wlan_qct_wdi.c
@@ -219,6 +219,7 @@
 #endif
    ,WIFI_CONFIG                    //61
    ,ANTENNA_DIVERSITY_SELECTION    //62
+   ,PER_BASED_ROAMING              //63
 };
 
 /*-------------------------------------------------------------------------- 
@@ -508,6 +509,13 @@
   WDI_ProcessRssiMonitorStopReq,           /* WDI_STOP_RSSI_MONITOR_REQ */
 
   WDI_ProcessWifiConfigReq,                /*WDI_WIFI_CONFIG_SET_REQ*/
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+  WDI_ProcessPERRoamScanOffloadReq,  /* WDI_PER_ROAM_SCAN_OFFLOAD_REQ */
+  WDI_ProcessPERRoamScanTriggerReq,  /* WDI_PER_ROAM_SCAN_TRIGGER_REQ */
+#else
+  NULL,
+  NULL,
+#endif /* WLAN_FEATURE_ROAM_SCAN_OFFLOAD */
   /*-------------------------------------------------------------------------
     Indications
   -------------------------------------------------------------------------*/
@@ -822,6 +830,13 @@
     WDI_ProcessRssiMonitorStopRsp,             /* WDI_STOP_RSSI_MONITOR_RSP*/
 
     WDI_ProcessWificonfigSetRsp,                   /* /WDI_WIFI_CONFIG_SET_RSP*/
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+    WDI_ProcessPERRoamScanOffloadRsp, /* WDI_PER_ROAM_SCAN_OFFLOAD_RSP */
+    WDI_ProcessPERRoamScanTriggerRsp, /* WDI_PER_ROAM_SCAN_TRIGGER_RSP */
+#else
+    NULL,
+    NULL,
+#endif
   /*---------------------------------------------------------------------
     Indications
   ---------------------------------------------------------------------*/
@@ -1431,6 +1446,10 @@
     CASE_RETURN_STRING (WDI_HAL_START_OEM_DATA_RSP_IND_NEW);
 #endif
     CASE_RETURN_STRING (WDI_ANTENNA_DIVERSITY_SELECTION_RSP);
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+    CASE_RETURN_STRING (WDI_PER_ROAM_SCAN_OFFLOAD_RSP);
+    CASE_RETURN_STRING (WDI_PER_ROAM_SCAN_TRIGGER_RSP);
+#endif
     default:
         return "Unknown WDI MessageId";
   }
@@ -1617,6 +1636,11 @@
                                  "ANTENNA_DIVERSITY_SELECTION");
                           pCapStr += strlen("ANTENNA_DIVERSITY_SELECTION");
                           break;
+                     case PER_BASED_ROAMING:
+                          snprintf(pCapStr, sizeof("PER_BASED_ROAMING"),
+                                         "%s", "PER_BASED_ROAMING");
+                          pCapStr += strlen("PER_BASED_ROAMING");
+                          break;
                  }
                  *pCapStr++ = ',';
                  *pCapStr++ = ' ';
@@ -22801,6 +22825,7 @@
 
   halMsgHeader.msgLen  = sizeof(halMsgHeader) + usBufferLen;
   *pusDataOffset       = sizeof(halMsgHeader);
+
   wpalMemoryCopy(*pMsgBuffer, &halMsgHeader, sizeof(halMsgHeader));
 
   return WDI_STATUS_SUCCESS;
@@ -24708,6 +24733,12 @@
        return WLAN_HAL_MODIFY_ROAM_PARAMS_IND;
   case WDI_SET_ALLOWED_ACTION_FRAMES_IND:
        return WLAN_HAL_SET_ALLOWED_ACTION_FRAMES_IND;
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+  case WDI_PER_ROAM_SCAN_OFFLOAD_REQ:
+      return WLAN_HAL_SET_PER_ROAM_CONFIG_REQ;
+  case WDI_PER_ROAM_SCAN_TRIGGER_REQ:
+      return WLAN_HAL_PER_ROAM_SCAN_TRIGGER_REQ;
+#endif
   default:
     return WLAN_HAL_MSG_MAX;
   }
@@ -25071,6 +25102,12 @@
 #endif /* FEATURE_OEM_DATA_SUPPORT */
   case WLAN_HAL_ANTENNA_DIVERSITY_SELECTION_RSP:
        return WDI_ANTENNA_DIVERSITY_SELECTION_RSP;
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+  case WLAN_HAL_SET_PER_ROAM_CONFIG_RSP:
+       return WDI_PER_ROAM_SCAN_OFFLOAD_RSP;
+  case WLAN_HAL_PER_ROAM_SCAN_TRIGGER_RSP:
+       return WDI_PER_ROAM_SCAN_TRIGGER_RSP;
+#endif
   default:
     return eDRIVER_TYPE_MAX;
   }
@@ -26831,6 +26868,53 @@
    return WDI_PostMainEvent(&gWDICb, WDI_REQUEST_EVENT, &wdiEventData);
 }
 
+WDI_Status
+WDI_PERRoamScanOffloadReq(WDI_PERRoamOffloadScanInfo
+                          *pwdiPERRoamScanOffloadReqParams,
+                          WDI_PERRoamOffloadScanCb wdiPERRoamOffloadScancb,
+                          void *pUserData)
+{
+   WDI_EventInfoType wdiEventData = {{0}};
+
+   if (eWLAN_PAL_FALSE == gWDIInitialized) {
+     WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR,
+               "WDI API call before module is initialized - Fail request");
+
+     return WDI_STATUS_E_NOT_ALLOWED;
+   }
+
+   wdiEventData.wdiRequest      = WDI_PER_ROAM_SCAN_OFFLOAD_REQ;
+   wdiEventData.pEventData      = pwdiPERRoamScanOffloadReqParams;
+   wdiEventData.uEventDataSize  = sizeof(*pwdiPERRoamScanOffloadReqParams);
+   wdiEventData.pCBfnc          = wdiPERRoamOffloadScancb;
+   wdiEventData.pUserData       = pUserData;
+
+   return WDI_PostMainEvent(&gWDICb, WDI_REQUEST_EVENT, &wdiEventData);
+}
+
+WDI_Status
+WDI_PERRoamScanTriggerReq(WDI_PERRoamTriggerScanInfo
+                          *pwdiPERRoamScanTriggerReqParams,
+                          WDI_PERRoamTriggerScanCb wdiPERRoamTriggerScancb,
+                          void *pUserData)
+{
+   WDI_EventInfoType      wdiEventData = {{0}};
+   if (eWLAN_PAL_FALSE == gWDIInitialized) {
+     WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR,
+               "WDI API call before module is initialized - Fail request");
+
+     return WDI_STATUS_E_NOT_ALLOWED;
+   }
+
+   wdiEventData.wdiRequest      = WDI_PER_ROAM_SCAN_TRIGGER_REQ;
+   wdiEventData.pEventData      = pwdiPERRoamScanTriggerReqParams;
+   wdiEventData.uEventDataSize  = sizeof(*pwdiPERRoamScanTriggerReqParams);
+   wdiEventData.pCBfnc          = wdiPERRoamTriggerScancb;
+   wdiEventData.pUserData       = pUserData;
+
+   return WDI_PostMainEvent(&gWDICb, WDI_REQUEST_EVENT, &wdiEventData);
+}
+
 void
 WDI_wdiEdTypeEncToEdTypeEnc(tEdType *EdType, WDI_EdType wdiEdType)
 {
@@ -27067,6 +27151,123 @@
                wdiRoamOffloadScancb, pEventData->pUserData, WDI_ROAM_SCAN_OFFLOAD_RESP);
 }
 
+
+WDI_Status
+WDI_ProcessPERRoamScanOffloadReq(WDI_ControlBlockType *pWDICtx,
+                                 WDI_EventInfoType *pEventData)
+{
+   wpt_uint16 usSendSize = 0;
+   wpt_uint16 usDataOffset = 0;
+   wpt_uint8 *pSendBuffer = NULL;
+   WDI_PERRoamOffloadScanInfo *wdiPERRoamOffloadReq = NULL;
+   WDI_PERRoamOffloadScanCb wdiPERRoamOffloadScancb = NULL;
+   tSetPerRoamConfigReq halPERRoamConfigReq;
+
+   wdiPERRoamOffloadReq = (WDI_PERRoamOffloadScanInfo *)pEventData->pEventData;
+   wdiPERRoamOffloadScancb   = (WDI_PERRoamOffloadScanCb)pEventData->pCBfnc;
+
+   if ((!pEventData) || (!wdiPERRoamOffloadReq)|| (!wdiPERRoamOffloadScancb)) {
+      WPAL_TRACE( eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_WARN,
+                  "%s: Invalid parameters", __func__);
+      WDI_ASSERT(0);
+      return WDI_STATUS_E_FAILURE;
+   }
+
+    if ((WDI_STATUS_SUCCESS != WDI_GetMessageBuffer(pWDICtx,
+                    WDI_PER_ROAM_SCAN_OFFLOAD_REQ,
+                    sizeof(halPERRoamConfigReq.perRoamConfigParams),
+                    &pSendBuffer, &usDataOffset, &usSendSize))||
+                    (usSendSize < (usDataOffset +
+                    sizeof(halPERRoamConfigReq.perRoamConfigParams)))) {
+        WPAL_TRACE( eWLAN_MODULE_DAL_CTRL,  eWLAN_PAL_TRACE_LEVEL_FATAL,
+                "Unable to get send buffer halPERRoamConfigReq Req");
+        WDI_ASSERT(0);
+        return WDI_STATUS_E_FAILURE;
+   }
+
+   halPERRoamConfigReq.perRoamConfigParams.request_id =
+                     wdiPERRoamOffloadReq->requestId;
+   halPERRoamConfigReq.perRoamConfigParams.waitPeriodForNextPERScan =
+                     wdiPERRoamOffloadReq->waitPeriodForNextPERScan;
+   halPERRoamConfigReq.perRoamConfigParams.rateUpThreshold =
+                     wdiPERRoamOffloadReq->rateUpThreshold;
+   halPERRoamConfigReq.perRoamConfigParams.rateDownThreshold =
+                     wdiPERRoamOffloadReq->rateDownThreshold;
+   halPERRoamConfigReq.perRoamConfigParams.isPERRoamCCAEnabled =
+                     wdiPERRoamOffloadReq->isPERRoamCCAEnabled;
+   halPERRoamConfigReq.perRoamConfigParams.PERroamTriggerPercent =
+                     wdiPERRoamOffloadReq->PERroamTriggerPercent;
+   halPERRoamConfigReq.perRoamConfigParams.PERtimerThreshold =
+                     wdiPERRoamOffloadReq->PERtimerThreshold;
+
+   halPERRoamConfigReq.perRoamConfigParams.reserved = 0;
+
+   wpalMemoryCopy(pSendBuffer+usDataOffset,
+                   &halPERRoamConfigReq.perRoamConfigParams,
+                   sizeof(halPERRoamConfigReq.perRoamConfigParams));
+
+   WPAL_TRACE( eWLAN_MODULE_DAL_CTRL,  eWLAN_PAL_TRACE_LEVEL_INFO,
+          "request_id %d waitPeriodForNextPERScan=%d rateUpThreshold=%d rateDownThreshold=%d isPERRoamCCAEnabled=%d PERtimerThreshold=%d PERroamTriggerPercent =%d",
+          halPERRoamConfigReq.perRoamConfigParams.request_id,
+          halPERRoamConfigReq.perRoamConfigParams.waitPeriodForNextPERScan,
+          halPERRoamConfigReq.perRoamConfigParams.rateUpThreshold,
+          halPERRoamConfigReq.perRoamConfigParams.rateDownThreshold,
+          halPERRoamConfigReq.perRoamConfigParams.isPERRoamCCAEnabled,
+          halPERRoamConfigReq.perRoamConfigParams.PERtimerThreshold,
+          halPERRoamConfigReq.perRoamConfigParams.PERroamTriggerPercent);
+   return  WDI_SendMsg(pWDICtx, pSendBuffer, usSendSize,
+               wdiPERRoamOffloadScancb, pEventData->pUserData,
+               WDI_PER_ROAM_SCAN_OFFLOAD_RSP);
+}
+
+WDI_Status
+WDI_ProcessPERRoamScanTriggerReq(WDI_ControlBlockType *pWDICtx,
+                                 WDI_EventInfoType *pEventData)
+{
+   wpt_uint16 usSendSize = 0;
+   wpt_uint16 usDataOffset = 0;
+   wpt_uint8 *pSendBuffer = NULL;
+   WDI_PERRoamTriggerScanCb wdiPERRoamTriggerScancb  = NULL;
+   WDI_PERRoamTriggerScanInfo *wdiPERRoamTriggerReq;
+   tStartRoamScanReq halPERRoamTriggerReq;
+
+   wdiPERRoamTriggerReq = (WDI_PERRoamTriggerScanInfo *) pEventData->pEventData;
+   wdiPERRoamTriggerScancb   = (WDI_PERRoamTriggerScanCb)pEventData->pCBfnc;
+
+   if ((!pEventData) || (!wdiPERRoamTriggerReq) || (!wdiPERRoamTriggerScancb)) {
+      WPAL_TRACE( eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_WARN,
+                  "%s: Invalid parameters", __func__);
+      WDI_ASSERT(0);
+      return WDI_STATUS_E_FAILURE;
+   }
+
+   if (( WDI_STATUS_SUCCESS != WDI_GetMessageBuffer(pWDICtx,
+                    WDI_PER_ROAM_SCAN_TRIGGER_REQ,
+                    sizeof(halPERRoamTriggerReq.startRoamScanTriggerParams),
+                    &pSendBuffer, &usDataOffset, &usSendSize))||
+                    (usSendSize < (usDataOffset +
+                    sizeof(halPERRoamTriggerReq.startRoamScanTriggerParams)))) {
+        WPAL_TRACE( eWLAN_MODULE_DAL_CTRL,  eWLAN_PAL_TRACE_LEVEL_FATAL,
+                "Unable to get send buffer in GetFrameLog Req");
+        WDI_ASSERT(0);
+        return WDI_STATUS_E_FAILURE;
+   }
+
+   halPERRoamTriggerReq.startRoamScanTriggerParams.roamScanReq =
+                     wdiPERRoamTriggerReq->roamScanReq;
+
+   wpalMemoryCopy(pSendBuffer+usDataOffset,
+                   &halPERRoamTriggerReq.startRoamScanTriggerParams,
+                   sizeof(halPERRoamTriggerReq.startRoamScanTriggerParams));
+
+   return  WDI_SendMsg(pWDICtx, pSendBuffer, usSendSize,
+               wdiPERRoamTriggerScancb, pEventData->pUserData,
+               WDI_PER_ROAM_SCAN_TRIGGER_RSP);
+}
+
+
+
+
 /**
  @brief Process Start Roam Candidate Lookup Rsp function (called when a
         response is being received over the bus from HAL)
@@ -27115,6 +27316,63 @@
 
    return WDI_STATUS_SUCCESS;
 }/* WDI_ProcessRoamScanOffloadRsp  */
+
+WDI_Status
+WDI_ProcessPERRoamScanOffloadRsp(WDI_ControlBlockType *pWDICtx,
+                                 WDI_EventInfoType *pEventData)
+{
+   WDI_Status                   wdiStatus;
+   eHalStatus                   halStatus;
+   WDI_PERRoamOffloadScanCb     wdiPERRoamOffloadScancb = NULL;
+
+   if ((NULL == pWDICtx) || (NULL == pEventData) ||
+       (NULL == pEventData->pEventData)) {
+      WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_WARN,
+                  "%s: Invalid parameters", __func__);
+      WDI_ASSERT(0);
+      return WDI_STATUS_E_FAILURE;
+   }
+
+   wdiPERRoamOffloadScancb = (WDI_PERRoamOffloadScanCb)pWDICtx->pfncRspCB;
+
+   halStatus = *((eHalStatus*)pEventData->pEventData);
+   wdiStatus   =   WDI_HAL_2_WDI_STATUS(halStatus);
+
+   /*Notify UMAC*/
+   wdiPERRoamOffloadScancb(wdiStatus, pWDICtx->pRspCBUserData);
+
+   return WDI_STATUS_SUCCESS;
+}/* WDI_ProcessPERRoamScanOffloadRsp  */
+
+WDI_Status
+WDI_ProcessPERRoamScanTriggerRsp
+(
+  WDI_ControlBlockType*  pWDICtx,
+  WDI_EventInfoType*     pEventData
+)
+{
+   WDI_Status                   wdiStatus;
+   eHalStatus                   halStatus;
+   WDI_PERRoamOffloadScanCb     wdiPERRoamTriggerScancb = NULL;
+
+   if ((NULL == pWDICtx) || (NULL == pEventData) ||
+       (NULL == pEventData->pEventData)) {
+      WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_WARN,
+                  "%s: Invalid parameters", __func__);
+      WDI_ASSERT(0);
+      return WDI_STATUS_E_FAILURE;
+   }
+
+   wdiPERRoamTriggerScancb = (WDI_PERRoamTriggerScanCb)pWDICtx->pfncRspCB;
+
+   halStatus = *((eHalStatus*)pEventData->pEventData);
+   wdiStatus   =   WDI_HAL_2_WDI_STATUS(halStatus);
+
+   /* Notify UMAC */
+   wdiPERRoamTriggerScancb(wdiStatus, pWDICtx->pRspCBUserData);
+
+   return WDI_STATUS_SUCCESS;
+}/* WDI_ProcessPERRoamScanTriggerRsp  */
 #endif
 
 /**
diff --git a/CORE/WDI/DP/inc/wlan_qct_wdi_ds.h b/CORE/WDI/DP/inc/wlan_qct_wdi_ds.h
index a7c7a02..483ad65 100644
--- a/CORE/WDI/DP/inc/wlan_qct_wdi_ds.h
+++ b/CORE/WDI/DP/inc/wlan_qct_wdi_ds.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 2016 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -168,6 +168,7 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
    wpt_uint32 offloadScanLearn;
    wpt_uint32 roamCandidateInd;
+   wpt_uint32 perRoamCndInd;
 #endif
 #ifdef WLAN_FEATURE_EXTSCAN
    wpt_uint32 extscanBuffer;
diff --git a/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c b/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
index c18bced..22c9a01 100644
--- a/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
+++ b/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
@@ -804,6 +804,7 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
       pRxMetadata->offloadScanLearn = WDI_RX_BD_GET_OFFLOADSCANLEARN(pBDHeader);
       pRxMetadata->roamCandidateInd = WDI_RX_BD_GET_ROAMCANDIDATEIND(pBDHeader);
+      pRxMetadata->perRoamCndInd = WDI_RX_BD_GET_PER_ROAMCANDIDATEIND(pBDHeader);
 #endif
 #ifdef WLAN_FEATURE_EXTSCAN
       pRxMetadata->extscanBuffer = WDI_RX_BD_GET_EXTSCANFULLSCANRESIND(pBDHeader);