wlan: Disable indoor channel on sap start

There are scenario where indoor channel operations
(like  active/passive scan/connect/roam etc)
are not desired/permitted specially in sap case.
Hence add support of disabling indoor channel
on sap start and revert it on sap stop.

Change-Id: Id90805cb4c670e1f46bae204d27bd85ea7422bc7
CRs-Fixed: 2160561
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index 72417fe..b3dd5aa 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1605,6 +1605,33 @@
 #define CFG_STA_AUTH_RETRIES_FOR_CODE17_MAX       ( 5 )
 #define CFG_STA_AUTH_RETRIES_FOR_CODE17_DEFAULT   ( 0 )
 
+/*
+ * <ini>
+ * g_mark_indoor_as_disable - Enable/Disable Indoor channel
+ * @Min: 0
+ * @Max: 1
+ * @Default: 0
+ *
+ * This ini is used to mark the Indoor channel as
+ * disable when SAP start and revert it on SAP stop,
+ * so SAP will not turn on indoor channel and
+ * sta will not scan/associate and roam on indoor
+ * channels.
+ *
+ * Related: If g_mark_indoor_as_disable set, turn the
+ * indoor channels to disable and update Wiphy & fw.
+ *
+ * Supported Feature: SAP/STA
+ *
+ * Usage: External
+ *
+ * </ini>
+ */
+#define CFG_MARK_INDOOR_AS_DISABLE_NAME     "g_mark_indoor_as_disable"
+#define CFG_MARK_INDOOR_AS_DISABLE_MIN      (0)
+#define CFG_MARK_INDOOR_AS_DISABLE_MAX      (1)
+#define CFG_MARK_INDOOR_AS_DISABLE_DEFAULT  (0)
+
 typedef enum
 {
     eHDD_LINK_SPEED_REPORT_ACTUAL = 0,
@@ -3748,6 +3775,9 @@
    uint32_t                    sta_sap_scc_on_dfs_chan;
    uint8_t                     enable_aggr_btc_sco_oui[9];
    uint8_t                     num_buff_aggr_btc_sco;
+   /* control marking indoor channel passive to disable */
+   bool                        disable_indoor_channel;
+
 } hdd_config_t;
 
 /*--------------------------------------------------------------------------- 
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h
index 002dd49..b7312b9 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1587,6 +1587,34 @@
 void* wlan_hdd_change_country_code_cb(void *pAdapter);
 void hdd_select_cbmode( hdd_adapter_t *pAdapter,v_U8_t operationChannel);
 
+/*
+ * hdd_update_indoor_channel() - enable/disable indoor channel
+ * @hdd_ctx: hdd context
+ * @disable: whether to enable / disable indoor channel
+ *
+ * enable/disable indoor channel in wiphy/cds
+ *
+ * Return: void
+ */
+void hdd_update_indoor_channel(hdd_context_t *hdd_ctx,
+    bool disable);
+
+/*
+ * hdd_modify_indoor_channel_state_flags() - modify wiphy flags and cds state
+ * @wiphy_chan: wiphy channel number
+ * @rfChannel: channel hw value
+ * @disable: Disable/enable the flags
+ *
+ * Modify wiphy flags and cds state if channel is indoor.
+ *
+ * Return: void
+ */
+void hdd_modify_indoor_channel_state_flags(
+    struct ieee80211_channel *wiphy_chan,
+    v_U32_t rfChannel,
+    bool disable);
+
+
 v_U8_t* wlan_hdd_cfg80211_get_ie_ptr(
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
                                      const v_U8_t *pIes,
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index 685f6ea..52c1a2e 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -209,6 +209,14 @@
                  CFG_RTS_THRESHOLD_MIN,
                  CFG_RTS_THRESHOLD_MAX ),
 
+   REG_VARIABLE(CFG_MARK_INDOOR_AS_DISABLE_NAME,
+                 WLAN_PARAM_Integer,
+                 hdd_config_t, disable_indoor_channel,
+                 VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                 CFG_MARK_INDOOR_AS_DISABLE_DEFAULT,
+                 CFG_MARK_INDOOR_AS_DISABLE_MIN,
+                 CFG_MARK_INDOOR_AS_DISABLE_MAX),
+
    REG_VARIABLE( CFG_FRAG_THRESHOLD_NAME, WLAN_PARAM_Integer,
                  hdd_config_t, FragmentationThreshold,
                  VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
@@ -4414,6 +4422,9 @@
           "Name = [gEnableDelAck] Value = [%u] ",
           pHddCtx->cfg_ini->enable_delack);
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+          "Name = [g_mark_indoor_as_disable] Value = [%u]",
+          pHddCtx->cfg_ini->disable_indoor_channel);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
           "Name = [disableBarWakeUp] Value = [%u] ",
           pHddCtx->cfg_ini->disableBarWakeUp);
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index aa54a44..d6e3b7d 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -10260,6 +10260,90 @@
 }
 #endif /* DHCP_SERVER_OFFLOAD */
 
+/*
+ * hdd_modify_indoor_channel_state_flags() - modify wiphy flags and cds state
+ * @wiphy_chan: wiphy channel number
+ * @rfChannel: channel hw value
+ * @disable: Disable/enable the flags
+ *
+ * Modify wiphy flags and cds state if channel is indoor.
+ *
+ * Return: void
+ */
+void hdd_modify_indoor_channel_state_flags(struct ieee80211_channel *wiphy_chan,
+    v_U32_t rfChannel, bool disable)
+{
+    v_U32_t channelLoop;
+    eRfChannels channelEnum = INVALID_RF_CHANNEL;
+
+    for (channelLoop = 0; channelLoop <= RF_CHAN_165; channelLoop++) {
+
+        if (rfChannels[channelLoop].channelNum == rfChannel) {
+            channelEnum = (eRfChannels)channelLoop;
+            break;
+        }
+    }
+
+    if (INVALID_RF_CHANNEL == channelEnum)
+        return;
+
+    if (disable) {
+        if (wiphy_chan->flags & IEEE80211_CHAN_INDOOR_ONLY) {
+            wiphy_chan->flags |=
+                IEEE80211_CHAN_DISABLED;
+            regChannels[channelEnum].enabled =
+                NV_CHANNEL_DISABLE;
+        }
+    } else {
+        if (wiphy_chan->flags & IEEE80211_CHAN_INDOOR_ONLY) {
+            wiphy_chan->flags &=
+                    ~IEEE80211_CHAN_DISABLED;
+            /*
+             * Indoor channels are marked as DFS
+             * during regulatory processing
+             */
+
+            regChannels[channelEnum].enabled =
+                    NV_CHANNEL_DFS;
+        }
+    }
+
+}
+
+void hdd_update_indoor_channel(hdd_context_t *hdd_ctx,
+                    bool disable)
+{
+    int band_num;
+    int chan_num;
+    v_U32_t rfChannel;
+    struct ieee80211_channel *wiphy_chan;
+    struct wiphy *wiphy;
+
+    ENTER();
+    hddLog(VOS_TRACE_LEVEL_INFO, "disable: %d", disable);
+
+    wiphy = hdd_ctx->wiphy;
+    for (band_num = 0; band_num < IEEE80211_NUM_BANDS; band_num++) {
+
+        if (wiphy->bands[band_num] == NULL)
+            continue;
+
+        for (chan_num = 0;
+             chan_num < wiphy->bands[band_num]->n_channels;
+             chan_num++) {
+
+            wiphy_chan =
+                &(wiphy->bands[band_num]->channels[chan_num]);
+            rfChannel = wiphy->bands[band_num]->channels[chan_num].hw_value;
+
+            hdd_modify_indoor_channel_state_flags(wiphy_chan, rfChannel,
+                                        disable);
+        }
+    }
+    EXIT();
+}
+
+
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
 static int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
                             struct beacon_parameters *params)
@@ -10300,6 +10384,19 @@
     wlan_hdd_tdls_disable_offchan_and_teardown_links(pHddCtx);
     iniConfig = pHddCtx->cfg_ini;
 
+    /* Mark the indoor channel (passive) to disable */
+    if (iniConfig->disable_indoor_channel) {
+        hdd_update_indoor_channel(pHddCtx, true);
+
+        if (!VOS_IS_STATUS_SUCCESS(
+                sme_update_channel_list((tpAniSirGlobal)pHddCtx->hHal))) {
+            hdd_update_indoor_channel(pHddCtx, false);
+            VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
+                 FL("Can't start BSS: update channel list failed"));
+            return eHAL_STATUS_FAILURE;
+        }
+    }
+
     pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pHostapdAdapter);
 
     pConfig = &pHostapdAdapter->sessionCtx.ap.sapConfig;
@@ -10890,6 +10987,12 @@
 
    return 0;
 error:
+   /* Revert the indoor to passive marking if START BSS fails */
+    if (iniConfig->disable_indoor_channel) {
+        hdd_update_indoor_channel(pHddCtx, false);
+        sme_update_channel_list((tpAniSirGlobal)pHddCtx->hHal);
+    }
+
    clear_bit(SOFTAP_INIT_DONE, &pHostapdAdapter->event_flags);
    return ret;
 }
diff --git a/CORE/HDD/src/wlan_hdd_hostapd.c b/CORE/HDD/src/wlan_hdd_hostapd.c
index db9d5ba..afc9435 100644
--- a/CORE/HDD/src/wlan_hdd_hostapd.c
+++ b/CORE/HDD/src/wlan_hdd_hostapd.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1162,7 +1162,6 @@
     pHddCtx = (hdd_context_t*)(pHostapdAdapter->pHddCtx);
     cfg_param = pHddCtx->cfg_ini;
 
-
     switch(sapEvent)
     {
         case eSAP_START_BSS_EVENT :
diff --git a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
index 7d3502d..69d4c38 100644
--- a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -2129,6 +2129,12 @@
        }
     }
 
+    /* Mark the indoor channel (passive) to enable */
+    if (pHddCtx->cfg_ini->disable_indoor_channel) {
+        hdd_update_indoor_channel(pHddCtx, false);
+        sme_update_channel_list((tpAniSirGlobal)pHddCtx->hHal);
+    }
+
     return vosStatus;
 }