wlan: Add support to send ECSA IE in beacons in SAP/GO

Adds support for ECSA and CSA IE in beacons in SAP/GO.
Channel change will be initiated by IOCTL.

Change-Id: I8d4b9161015c03ed58648ca203be7d29e6c0975e
CRs-Fixed: 2143138
diff --git a/CORE/HDD/inc/qc_sap_ioctl.h b/CORE/HDD/inc/qc_sap_ioctl.h
index 764384e..aaccdd9 100644
--- a/CORE/HDD/inc/qc_sap_ioctl.h
+++ b/CORE/HDD/inc/qc_sap_ioctl.h
@@ -195,7 +195,8 @@
     QCSAP_PARAM_GET_FRAME_LOGS = 12,
     QCSAP_PARAM_SET_PROXIMITY = 13,
     QCSAP_PARAM_SET_WOWL = 14,
-    QCSAP_PARAM_CAP_TSF = 15
+    QCSAP_PARAM_CAP_TSF = 15,
+    QCSAP_PARAM_SET_CHANNEL_CHANGE = 16,
 };
 
 int iw_softap_get_channel_list(struct net_device *dev, 
diff --git a/CORE/HDD/src/wlan_hdd_hostapd.c b/CORE/HDD/src/wlan_hdd_hostapd.c
index c6a2412..235e0a6 100644
--- a/CORE/HDD/src/wlan_hdd_hostapd.c
+++ b/CORE/HDD/src/wlan_hdd_hostapd.c
@@ -2148,6 +2148,18 @@
                                      (uint32_t *)&set_value, 1);
                 break;
             }
+        case QCSAP_PARAM_SET_CHANNEL_CHANGE:
+            if ((WLAN_HDD_SOFTAP == pHostapdAdapter->device_mode) ||
+                (WLAN_HDD_P2P_GO == pHostapdAdapter->device_mode)) {
+                hddLog(LOG1, FL("ET Channel Change to new channel= %d"),
+                       set_value);
+                ret = wlansap_set_channel_change(pVosContext, set_value);
+            } else {
+                hddLog(LOGE, FL("Channel %d Change Failed, Device in not in SAP/GO mode"),
+                       set_value);
+                ret = -EINVAL;
+            }
+            break;
         default:
             hddLog(LOGE, FL("Invalid setparam command %d value %d"),
                     sub_cmd, set_value);
@@ -4776,6 +4788,8 @@
       IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,  "setProximity" },
   { QCSAP_PARAM_CAP_TSF,
      IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,  "cap_tsf" },
+  {QCSAP_PARAM_SET_CHANNEL_CHANGE,
+   IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setChanChange"},
   { QCSAP_PARAM_SET_WOWL,
       IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,  "wowl" },
   { QCSAP_IOCTL_GETPARAM,
diff --git a/CORE/MAC/inc/sirApi.h b/CORE/MAC/inc/sirApi.h
index 2bb9f04..6693298 100644
--- a/CORE/MAC/inc/sirApi.h
+++ b/CORE/MAC/inc/sirApi.h
@@ -155,6 +155,9 @@
 
 #define WLAN_DISA_MAX_PAYLOAD_SIZE                1600
 
+#define CHANNEL_SWITCH_BEACON_COUNT 5
+#define SAP_CHANNEL_SWITCH_MODE 1
+
 enum eSirHostMsgTypes
 {
     SIR_HAL_APP_SETUP_NTF = SIR_HAL_HOST_MSG_START,
@@ -6331,4 +6334,21 @@
 typedef struct {
     tANI_U8 session_id;
 }tDelBaParams,*ptDelBaParams;
+
+/**
+ * struct sir_ecsa_ie_req - structure to send req to include ECSA IE in beacon
+ * @type: type of msg
+ * @len: length of msg
+ * @new_chan: new channel to which switch is requested
+ * @cb_mode:cbmode of the new channel
+ * @bssid: bssid of the AP
+ */
+struct sir_ecsa_ie_req {
+   uint16_t type;
+   uint16_t len;
+   uint8_t new_chan;
+   uint8_t cb_mode;
+   uint8_t bssid[VOS_MAC_ADDR_SIZE];
+};
+
 #endif /* __SIR_API_H */
diff --git a/CORE/MAC/inc/sirMacProtDef.h b/CORE/MAC/inc/sirMacProtDef.h
index 52c172a..13b795c 100644
--- a/CORE/MAC/inc/sirMacProtDef.h
+++ b/CORE/MAC/inc/sirMacProtDef.h
@@ -392,6 +392,10 @@
 #define SIR_MAC_RSN_EID_MIN                4
 #define SIR_MAC_RSN_EID_MAX                254
 
+#define SIR_MAC_EXT_CHNL_SWITCH_ANN_EID    60
+#define SIR_MAC_WIDER_BW_CHANNEL_SWITCH_ANN 194
+
+
 //using reserved EID for Qos Action IE for now,
 //need to check 11e spec for the actual EID
 #define SIR_MAC_QOS_ACTION_EID         49
diff --git a/CORE/MAC/inc/wniApi.h b/CORE/MAC/inc/wniApi.h
index 6445286..855c7ee 100644
--- a/CORE/MAC/inc/wniApi.h
+++ b/CORE/MAC/inc/wniApi.h
@@ -396,6 +396,7 @@
 #endif
 
     eWNI_SME_DEL_BA_SES_REQ,
+    eWNI_SME_SET_CHAN_SW_IE_REQ,
     eWNI_SME_MSG_TYPES_END
 };
 
diff --git a/CORE/MAC/src/include/parserApi.h b/CORE/MAC/src/include/parserApi.h
index d891dc6..33f1fcd 100644
--- a/CORE/MAC/src/include/parserApi.h
+++ b/CORE/MAC/src/include/parserApi.h
@@ -574,6 +574,19 @@
                             struct sDphHashNode   *pSta,
                             tpPESession            psessionEntry);
 
+/**
+ * populate_dot11f_ext_chann_switch_ann() - Function to populate ECS
+ * @mac_ptr:            Pointer to PMAC structure
+ * @dot_11_ptr:         ECS element
+ * @session_entry:      PE session entry
+ *
+ * This function is used to populate the extended channel switch element
+ *
+ * Return: None
+ */
+void populate_dot11f_ext_chann_switch_ann(tpAniSirGlobal mac_ctx,
+        tDot11fIEext_chan_switch_ann *dot_11_ptr, tpPESession session_entry);
+
 /// Populate a tDot11fIEChanSwitchAnn
 void
 PopulateDot11fChanSwitchAnn(tpAniSirGlobal          pMac,
@@ -582,7 +595,7 @@
 
 /// Populate a tDot11fIEChanSwitchAnn
 void
-PopulateDot11fExtChanSwitchAnn(tpAniSirGlobal          pMac,
+PopulateDot11fsecChanOffset(tpAniSirGlobal          pMac,
                              tDot11fIEsec_chan_offset *pDot11f,
                              tpPESession psessionEntry);
 
diff --git a/CORE/MAC/src/pe/include/limSession.h b/CORE/MAC/src/pe/include/limSession.h
index 21b75bf..5a2a6e0 100644
--- a/CORE/MAC/src/pe/include/limSession.h
+++ b/CORE/MAC/src/pe/include/limSession.h
@@ -320,6 +320,8 @@
     /* *********************11H related*****************************/
     //tANI_U32           gLim11hEnable;
     tLimSpecMgmtInfo   gLimSpecMgmt;
+    bool include_ecsa_ie;
+    bool include_wide_ch_bw_ie;
     // CB Primary/Secondary Channel Switch Info
     tLimChannelSwitchInfo  gLimChannelSwitch;
     /* *********************End 11H related*****************************/
diff --git a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
index ddb9f5f..e037756 100644
--- a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
+++ b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
@@ -1721,6 +1721,7 @@
 #endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
         case eWNI_SME_MAC_SPOOF_ADDR_IND:
         case eWNI_SME_REGISTER_MGMT_FRAME_CB:
+        case eWNI_SME_SET_CHAN_SW_IE_REQ:
             // These messages are from HDD
             limProcessNormalHddMsg(pMac, limMsg, false);   //no need to response to hdd
             break;
diff --git a/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c b/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c
index 346585c..e9d5187 100644
--- a/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c
+++ b/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c
@@ -5627,6 +5627,84 @@
   else
       limLog(pMac, LOGE, FL("sme_req->callback is null"));
 }
+/**
+ * lim_process_sme_set_csa_ie_request() - process sme dfs csa ie req
+ *
+ * @mac_ctx: Pointer to Global MAC structure
+ * @msg_buf: pointer to the SME message buffer
+ *
+ * This function processes SME request messages from HDD or upper layer
+ * application.
+ *
+ * Return: None
+ */
+static void lim_process_sme_set_csa_ie_request(tpAniSirGlobal mac_ctx,
+                                               uint32_t *msg_buf)
+{
+   struct sir_ecsa_ie_req *csa_ie_req = (struct sir_ecsa_ie_req *)msg_buf;
+   uint8_t session_id;
+   tpPESession session_entry = NULL;
+   tLimWiderBWChannelSwitchInfo *wider_bw_ch_switch;
+
+   if (!csa_ie_req) {
+       limLog(mac_ctx, LOGE, FL("Buffer is Pointing to NULL"));
+       return;
+   }
+
+   session_entry = peFindSessionByBssid(mac_ctx,
+                                        csa_ie_req->bssid, &session_id);
+   if (!session_entry) {
+       limLog(mac_ctx, LOGE,
+              FL("Session not found for given BSSID" MAC_ADDRESS_STR),
+              MAC_ADDR_ARRAY(csa_ie_req->bssid));
+       return;
+   }
+
+   if (session_entry->valid && !LIM_IS_AP_ROLE(session_entry)) {
+       limLog(mac_ctx, LOGE, FL("Invalid SystemRole %d"),
+              GET_LIM_SYSTEM_ROLE(session_entry));
+       return;
+   }
+
+   /* target channel */
+   session_entry->gLimChannelSwitch.primaryChannel = csa_ie_req->new_chan;
+
+   /* Channel switch announcement needs to be included in beacon */
+   session_entry->include_ecsa_ie = true;
+   session_entry->gLimChannelSwitch.switchCount =
+                             CHANNEL_SWITCH_BEACON_COUNT;
+   session_entry->gLimChannelSwitch.secondarySubBand =
+                             csa_ie_req->cb_mode;
+   session_entry->gLimChannelSwitch.switchMode = SAP_CHANNEL_SWITCH_MODE;
+
+   /* Now encode the Wider Ch BW element depending on the ch width */
+   if (session_entry->vhtCapability &&
+       (csa_ie_req->cb_mode >=
+        PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED)) {
+       wider_bw_ch_switch = &session_entry->gLimWiderBWChannelSwitch;
+       session_entry->include_wide_ch_bw_ie = true;
+       wider_bw_ch_switch->newChanWidth = WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ;
+       /* Fetch the center channel based on the channel width */
+       wider_bw_ch_switch->newCenterChanFreq0 =
+                     limGetCenterChannel(mac_ctx, csa_ie_req->new_chan,
+                                         csa_ie_req->cb_mode,
+                                         WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ);
+   }
+   /* Send ECSA/CSA IE request from here */
+   if (schSetFixedBeaconFields(mac_ctx, session_entry) != eSIR_SUCCESS) {
+       limLog(mac_ctx, LOGE, FL("Unable to set CSA IE in beacon"));
+       return;
+   }
+
+    limSendBeaconInd(mac_ctx, session_entry);
+    session_entry->include_ecsa_ie = false;
+    session_entry->include_wide_ch_bw_ie = false;
+
+    limLog(mac_ctx, LOG1, FL("IE count:%d chan:%d secondarySubBand:%d"),
+           session_entry->gLimChannelSwitch.switchCount,
+           session_entry->gLimChannelSwitch.primaryChannel,
+           session_entry->gLimChannelSwitch.secondarySubBand);
+}
 
 /**
  * limProcessSmeReqMessages()
@@ -5988,6 +6066,8 @@
         case eWNI_SME_REGISTER_MGMT_FRAME_CB:
             lim_register_mgmt_frame_ind_cb(pMac, pMsgBuf);
             break;
+        case eWNI_SME_SET_CHAN_SW_IE_REQ:
+            lim_process_sme_set_csa_ie_request(pMac, pMsgBuf);
         default:
             vos_mem_free((v_VOID_t*)pMsg->bodyptr);
             pMsg->bodyptr = NULL;
diff --git a/CORE/MAC/src/pe/lim/limSendManagementFrames.c b/CORE/MAC/src/pe/lim/limSendManagementFrames.c
index 692e8dc..920da90 100644
--- a/CORE/MAC/src/pe/lim/limSendManagementFrames.c
+++ b/CORE/MAC/src/pe/lim/limSendManagementFrames.c
@@ -798,6 +798,18 @@
     }
 #endif
 
+    if (LIM_IS_AP_ROLE(psessionEntry) && psessionEntry->include_ecsa_ie) {
+       populate_dot11f_ext_chann_switch_ann(pMac, &pFrm->ext_chan_switch_ann,
+                                              psessionEntry);
+       if (psessionEntry->lim11hEnable) {
+           PopulateDot11fChanSwitchAnn(pMac,
+                                       &pFrm->ChanSwitchAnn, psessionEntry);
+           if (psessionEntry->include_wide_ch_bw_ie)
+               PopulateDot11fWiderBWChanSwitchAnn(pMac,
+                                &pFrm->WiderBWChanSwitchAnn, psessionEntry);
+       }
+    }
+
 
     if ( psessionEntry->pLimStartBssReq )
     {
diff --git a/CORE/MAC/src/pe/sch/schBeaconGen.c b/CORE/MAC/src/pe/sch/schBeaconGen.c
index 15438a7..19553ad 100644
--- a/CORE/MAC/src/pe/sch/schBeaconGen.c
+++ b/CORE/MAC/src/pe/sch/schBeaconGen.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -377,6 +377,18 @@
     }
 #endif
 
+    if (LIM_IS_AP_ROLE(psessionEntry) && psessionEntry->include_ecsa_ie) {
+       populate_dot11f_ext_chann_switch_ann(pMac, &pBcn2->ext_chan_switch_ann,
+                                              psessionEntry);
+       if (psessionEntry->lim11hEnable) {
+           PopulateDot11fChanSwitchAnn(pMac,
+                                       &pBcn2->ChanSwitchAnn, psessionEntry);
+           if (psessionEntry->include_wide_ch_bw_ie)
+               PopulateDot11fWiderBWChanSwitchAnn(pMac,
+                                &pBcn2->WiderBWChanSwitchAnn, psessionEntry);
+       }
+    }
+
     PopulateDot11fExtSuppRates( pMac, POPULATE_DOT11F_RATES_OPERATIONAL,
                                 &pBcn2->ExtSuppRates, psessionEntry );
  
@@ -463,7 +475,6 @@
         }
 
     }
-
     nStatus = dot11fPackBeacon2( pMac, pBcn2,
                                  pMac->sch.schObject.gSchBeaconFrameEnd,
                                  SCH_MAX_BEACON_SIZE, &nBytes );
@@ -591,13 +602,28 @@
                      sizeof(beacon2->PowerConstraints));
 
     }
+
+    if (beacon2->ext_chan_switch_ann.present) {
+        SetProbeRspIeBitmap(DefProbeRspIeBitmap,
+                            SIR_MAC_EXT_CHNL_SWITCH_ANN_EID);
+        vos_mem_copy((void *)&prb_rsp->ext_chan_switch_ann,
+                     (void *)&beacon2->ext_chan_switch_ann,
+                     sizeof(beacon2->ext_chan_switch_ann));
+    }
     /* Channel Switch Annoouncement SIR_MAC_CHNL_SWITCH_ANN_EID */
     if(beacon2->ChanSwitchAnn.present)
     {
         SetProbeRspIeBitmap(DefProbeRspIeBitmap,SIR_MAC_CHNL_SWITCH_ANN_EID);
         vos_mem_copy((void *)&prb_rsp->ChanSwitchAnn, (void *)&beacon2->ChanSwitchAnn,
                      sizeof(beacon2->ChanSwitchAnn));
-
+       if (beacon2->WiderBWChanSwitchAnn.present)
+       {
+          SetProbeRspIeBitmap(DefProbeRspIeBitmap,
+                              SIR_MAC_WIDER_BW_CHANNEL_SWITCH_ANN);
+          vos_mem_copy((void *)&prb_rsp->WiderBWChanSwitchAnn,
+                     (void *)&beacon2->WiderBWChanSwitchAnn,
+                     sizeof(beacon2->WiderBWChanSwitchAnn));
+       }
     }
     /* ERP information */
     if(beacon2->ERPInfo.present)
diff --git a/CORE/SAP/inc/sapApi.h b/CORE/SAP/inc/sapApi.h
index 390e10b..3da71fa 100644
--- a/CORE/SAP/inc/sapApi.h
+++ b/CORE/SAP/inc/sapApi.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 2016-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1617,6 +1617,17 @@
                                  v_U16_t reason_code,
                                  v_U8_t subtype,
                                  struct tagCsrDelStaParams *pDelStaParams);
+/**
+ * wlansap_set_channel_change() -
+ * This function to support SAP channel change with CSA/ECSA IE
+ * set in the beacons.
+ *
+ * @vos_ctx: vos context.
+ * @new_channel: target channel number.
+ *
+ * Return: 0 for success, non zero for failure
+ */
+int wlansap_set_channel_change(v_PVOID_t vos_ctx, uint32_t new_channel);
 
 #ifdef __cplusplus
  }
diff --git a/CORE/SAP/src/sapFsm.c b/CORE/SAP/src/sapFsm.c
index 9e48d8c..42f4f47 100644
--- a/CORE/SAP/src/sapFsm.c
+++ b/CORE/SAP/src/sapFsm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 2016-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1172,6 +1172,17 @@
                 sapContext->sapsMachine = eSAP_DISCONNECTING;
                 vosStatus = sapGotoDisconnecting(sapContext);
             }
+            else if (msg == eSAP_CHANNEL_SWITCH_ANNOUNCEMENT_START ) {
+                tHalHandle hHal = VOS_GET_HAL_CB(sapContext->pvosGCtx);
+                if (!hHal) {
+                     VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
+                                 "In %s, NULL hHal in state %s, msg %d",
+                                  __func__, "eSAP_STARTING", msg);
+                }
+                vosStatus = sme_roam_csa_ie_request(hHal, sapContext->bssid,
+                                        sapContext->ecsa_info.new_channel,
+                                        sapContext->csrRoamProfile.phyMode);
+            }
             else
             {
                 VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, "In %s, in state %s, invalid event msg %d",
diff --git a/CORE/SAP/src/sapFsm_ext.h b/CORE/SAP/src/sapFsm_ext.h
index bc5ee88..ebaa824 100644
--- a/CORE/SAP/src/sapFsm_ext.h
+++ b/CORE/SAP/src/sapFsm_ext.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -50,6 +50,7 @@
   eSAP_MAC_START_FAILS,
   eSAP_HDD_STOP_INFRA_BSS,
   eSAP_WRITE_REMOTE_AMP_ASSOC,
+  eSAP_CHANNEL_SWITCH_ANNOUNCEMENT_START,
 
   eSAP_NO_MSG
 }eSapMsg_t;
diff --git a/CORE/SAP/src/sapInternal.h b/CORE/SAP/src/sapInternal.h
index dd94138..ed7cd1e 100644
--- a/CORE/SAP/src/sapInternal.h
+++ b/CORE/SAP/src/sapInternal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 2015, 2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -189,6 +189,16 @@
 
 } hdd_station_info_t;
 
+/**
+ * struct ecsa_info - structure to store ecsa info
+ * @new_channel: new channel to which switch is requested
+ * @channel_switch_in_progress: check if channel switch is in progress
+ */
+struct ecsa_info {
+    uint8_t new_channel;
+    bool channel_switch_in_progress;
+};
+
 typedef struct sSapContext {
 
     vos_lock_t          SapGlobalLock;
@@ -275,6 +285,7 @@
     v_U8_t            ObssScanInterval;
     v_U8_t            ObssTransitionDelayFactor;
 #endif
+    struct ecsa_info ecsa_info;
 } *ptSapContext;
 
 
diff --git a/CORE/SAP/src/sapModule.c b/CORE/SAP/src/sapModule.c
index 3b1749c..de981ca 100644
--- a/CORE/SAP/src/sapModule.c
+++ b/CORE/SAP/src/sapModule.c
@@ -2459,3 +2459,89 @@
                    pDelStaParams->reason_code, pDelStaParams->subtype,
                    MAC_ADDR_ARRAY(pDelStaParams->peerMacAddr));
 }
+
+/**
+ * wlansap_validate_phy_mode() -
+ * validate if the phymode allow the channel to set.
+ *
+ * @phy_mode: current phymode
+ * @channel: target channel number.
+ *
+ * Return: true if channel is allowed else false
+ */
+static bool wlansap_validate_phy_mode(uint32_t phy_mode, uint32_t channel)
+{
+  switch (phy_mode) {
+  case eSAP_DOT11_MODE_11a:
+     if (channel <= SIR_11B_CHANNEL_END)
+         return false;
+  case eSAP_DOT11_MODE_11b:
+  case eSAP_DOT11_MODE_11g:
+  case eSAP_DOT11_MODE_11g_ONLY:
+  case eSAP_DOT11_MODE_11b_ONLY:
+    if (channel > SIR_11B_CHANNEL_END)
+        return false;
+  default:
+    return true;
+  }
+
+  return true;
+}
+
+int wlansap_set_channel_change(v_PVOID_t vos_ctx, uint32_t new_channel)
+{
+   ptSapContext sap_ctx;
+   tWLAN_SAPEvent sap_event = {0};
+   v_PVOID_t hal;
+   tpAniSirGlobal mac_ctx;
+
+   sap_ctx = VOS_GET_SAP_CB(vos_ctx);
+
+   if (!sap_ctx) {
+        hddLog(LOGE, FL("sap_ctx is NULL"));
+        return -EINVAL;
+   }
+
+   hal = VOS_GET_HAL_CB(sap_ctx->pvosGCtx);
+   if (!hal) {
+        hddLog(LOGE, FL("hal is NULL"));
+        return -EINVAL;
+   }
+   mac_ctx = PMAC_STRUCT(hal);
+   if (sap_ctx->channel == new_channel) {
+        hddLog(LOGE, FL("channel %d already set"), new_channel);
+        return -EALREADY;
+   }
+   if (sap_ctx->ecsa_info.channel_switch_in_progress) {
+        hddLog(LOGE, FL("channel switch already in progress ignore"));
+        return -EALREADY;
+   }
+   if (NV_CHANNEL_ENABLE != vos_nv_getChannelEnabledState(new_channel)) {
+        hddLog(LOGE, FL("Invalid channel Ignore channel switch "));
+        return -EINVAL;
+   }
+
+   if (eSAP_STARTED != sap_ctx->sapsMachine) {
+        hddLog(LOGE, FL("SAP is not in eSAP_STARTED state "));
+        return -EINVAL;
+   }
+   if (!wlansap_validate_phy_mode(sap_ctx->csrRoamProfile.phyMode,
+       new_channel)) {
+        hddLog(LOGE, FL("Channel %d not valid for phyMode %d"), new_channel,
+               sap_ctx->csrRoamProfile.phyMode);
+        return -EINVAL;
+   }
+
+   sap_ctx->ecsa_info.new_channel = new_channel;
+   sap_ctx->ecsa_info.channel_switch_in_progress = true;
+   /*
+    * Post the eSAP_CHANNEL_SWITCH_ANNOUNCEMENT_START
+    * to SAP state machine to process the channel
+    * request with CSA IE set in the beacons.
+    */
+   sap_event.event = eSAP_CHANNEL_SWITCH_ANNOUNCEMENT_START;
+   sapFsm(sap_ctx, &sap_event);
+
+   return 0;
+}
+
diff --git a/CORE/SME/inc/smeInside.h b/CORE/SME/inc/smeInside.h
index 902e0a0..7e5ec7f 100644
--- a/CORE/SME/inc/smeInside.h
+++ b/CORE/SME/inc/smeInside.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -362,5 +362,20 @@
 void activeListCmdTimeoutHandle(void *userData);
 
 void csrGetStaticUapsdMask(tpAniSirGlobal pMac, tANI_U8 *staticUapsdMask);
+/**
+ * csr_roam_send_chan_sw_ie_request() - Request to transmit CSA IE
+ * @mac_ctx:        Global MAC context
+ * @bssid:          BSSID
+ * @new_chan: Channel on which to send the IE
+ * @cb_mode: cb mode
+ *
+ * This function sends request to transmit channel switch announcement
+ * IE to lower layers
+ *
+ * Return: success or failure
+ **/
+VOS_STATUS csr_roam_send_chan_sw_ie_request(tpAniSirGlobal mac_ctx,
+                          tCsrBssid bssid, uint8_t new_chan, uint8_t cb_mode);
+
 
 #endif //#if !defined( __SMEINSIDE_H )
diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h
index 5d4a807..431f568 100644
--- a/CORE/SME/inc/sme_Api.h
+++ b/CORE/SME/inc/sme_Api.h
@@ -4009,4 +4009,16 @@
                                pgetArpStatsParams pGetStatsParam);
 eHalStatus sme_del_sta_ba_session_req(tHalHandle hHal,
                                       tDelBaParams sta_del_params);
+/**
+ * sme_roam_csa_ie_request() - request CSA IE transmission from PE
+ * @hal: handle returned by mac_open
+ * @bssid: SAP bssid
+ * @new_chan: target channel information
+ * @phy_mode: SAP phymode
+ *
+ * Return: VOS_STATUS
+ */
+VOS_STATUS sme_roam_csa_ie_request(tHalHandle hal, tCsrBssid bssid,
+                                   uint8_t new_chan, uint32_t phy_mode);
+
 #endif //#if !defined( __SME_API_H )
diff --git a/CORE/SME/src/csr/csrApiRoam.c b/CORE/SME/src/csr/csrApiRoam.c
index 0418201..e897126 100644
--- a/CORE/SME/src/csr/csrApiRoam.c
+++ b/CORE/SME/src/csr/csrApiRoam.c
@@ -19648,3 +19648,29 @@
        *staticUapsdMask = pSession->pCurRoamProfile->uapsd_mask;
 }
 
+VOS_STATUS csr_roam_send_chan_sw_ie_request(tpAniSirGlobal mac_ctx,
+               tCsrBssid bssid, uint8_t new_chan, uint8_t cb_mode)
+{
+   VOS_STATUS status = VOS_STATUS_SUCCESS;
+   struct sir_ecsa_ie_req *msg;
+
+   msg = vos_mem_malloc(sizeof(*msg));
+   if (!msg) {
+        smsLog(mac_ctx, LOGE, FL(" Memory alloc failed "));
+        return VOS_STATUS_E_NOMEM;
+   }
+
+   msg->type = eWNI_SME_SET_CHAN_SW_IE_REQ;
+   msg->len = sizeof(*msg);
+
+   msg->new_chan = new_chan;
+   msg->cb_mode = cb_mode;
+   vos_mem_copy(msg->bssid, bssid, VOS_MAC_ADDR_SIZE);
+
+   status = palSendMBMessage(mac_ctx->hHdd, msg);
+   if (!VOS_IS_STATUS_SUCCESS(status))
+         smsLog(mac_ctx, LOGE,
+                FL(" channel switch req fauiled status %d "), status);
+   return status;
+}
+
diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c
index a6bd8b3..76d943b 100644
--- a/CORE/SME/src/sme_common/sme_Api.c
+++ b/CORE/SME/src/sme_common/sme_Api.c
@@ -15090,3 +15090,24 @@
     }
     return(status);
 }
+
+VOS_STATUS sme_roam_csa_ie_request(tHalHandle hal, tCsrBssid bssid,
+                                   uint8_t new_chan, uint32_t phy_mode)
+{
+   VOS_STATUS status = VOS_STATUS_E_FAILURE;
+   tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal);
+   uint8_t cb_mode = 0;
+
+   status = sme_AcquireGlobalLock(&mac_ctx->sme);
+   if (VOS_IS_STATUS_SUCCESS(status)) {
+       if (CSR_IS_CHANNEL_5GHZ(new_chan)) {
+           sme_SelectCBMode(hal, phy_mode, new_chan);
+           cb_mode = mac_ctx->roam.configParam.channelBondingMode5GHz;
+       }
+       status = csr_roam_send_chan_sw_ie_request(mac_ctx, bssid,
+                                                 new_chan, cb_mode);
+       sme_ReleaseGlobalLock(&mac_ctx->sme);
+   }
+   return status;
+}
+
diff --git a/CORE/SYS/legacy/src/utils/src/macTrace.c b/CORE/SYS/legacy/src/utils/src/macTrace.c
index ba7c97a..70c2f7a 100644
--- a/CORE/SYS/legacy/src/utils/src/macTrace.c
+++ b/CORE/SYS/legacy/src/utils/src/macTrace.c
@@ -582,6 +582,7 @@
         CASE_RETURN_STRING(eWNI_SME_MSG_TYPES_END);
         CASE_RETURN_STRING(eWNI_SME_CAP_TSF_REQ);
         CASE_RETURN_STRING(eWNI_SME_GET_TSF_REQ);
+        CASE_RETURN_STRING(eWNI_SME_SET_CHAN_SW_IE_REQ);
         default:
             return( (tANI_U8*)"UNKNOWN" );
             break;
diff --git a/CORE/SYS/legacy/src/utils/src/parserApi.c b/CORE/SYS/legacy/src/utils/src/parserApi.c
index e5e1f75..b761a40 100644
--- a/CORE/SYS/legacy/src/utils/src/parserApi.c
+++ b/CORE/SYS/legacy/src/utils/src/parserApi.c
@@ -295,6 +295,32 @@
 
 } // End PopulateDot11fCapabilities2.
 
+void populate_dot11f_ext_chann_switch_ann(tpAniSirGlobal mac_ctx,
+            tDot11fIEext_chan_switch_ann *dot_11_ptr, tpPESession session_entry)
+{
+   offset_t ch_offset;
+
+   if (session_entry->gLimChannelSwitch.secondarySubBand >=
+       PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED)
+         ch_offset = BW80;
+   else
+         ch_offset = session_entry->gLimChannelSwitch.secondarySubBand;
+
+   dot_11_ptr->switch_mode = session_entry->gLimChannelSwitch.switchMode;
+   dot_11_ptr->new_reg_class = limGetOPClassFromChannel(
+         mac_ctx->scan.countryCodeCurrent,
+         session_entry->gLimChannelSwitch.primaryChannel, ch_offset);
+   dot_11_ptr->new_channel = session_entry->gLimChannelSwitch.primaryChannel;
+   dot_11_ptr->switch_count = session_entry->gLimChannelSwitch.switchCount;
+   dot_11_ptr->present = 1;
+
+   limLog(mac_ctx, LOG1, FL("country:%s cb mode:%d width:%d reg:%d off:%d"),
+          mac_ctx->scan.countryCodeCurrent,
+          session_entry->gLimChannelSwitch.primaryChannel,
+          session_entry->gLimChannelSwitch.secondarySubBand,
+          dot_11_ptr->new_reg_class, ch_offset);
+}
+
 void
 PopulateDot11fChanSwitchAnn(tpAniSirGlobal          pMac,
                             tDot11fIEChanSwitchAnn *pDot11f,
@@ -308,7 +334,7 @@
 } // End PopulateDot11fChanSwitchAnn.
 
 void
-PopulateDot11fExtChanSwitchAnn(tpAniSirGlobal pMac,
+PopulateDot11fsecChanOffset(tpAniSirGlobal pMac,
                                tDot11fIEsec_chan_offset *pDot11f,
                                tpPESession psessionEntry)
 {
@@ -329,6 +355,9 @@
     pDot11f->newChanWidth = psessionEntry->gLimWiderBWChannelSwitch.newChanWidth;
     pDot11f->newCenterChanFreq0 = psessionEntry->gLimWiderBWChannelSwitch.newCenterChanFreq0;
     pDot11f->newCenterChanFreq1 = psessionEntry->gLimWiderBWChannelSwitch.newCenterChanFreq1;
+    limLog(pMac, LOG1, FL("wrapper: width:%d f0:%d f1:%d"),
+           pDot11f->newChanWidth, pDot11f->newCenterChanFreq0,
+           pDot11f->newCenterChanFreq1);
 }
 #endif