wlan: Move the SAP to user configured channel on STA disconnect

If STA+SAP SCC allowed on DFS channel then standalone SAP is not
allowed to operate on the DFS channel. But it can operate on DFS
channel if concurrent STA is connected on the same channel.
So move the SAP from DFS channel to user configured channel if
STA gets disconnected from the reference AP.

CRs-Fixed: 2161091
Change-Id: Iec30d528ee9511de3d157869af01b2b2f07d3b23
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 5a252ae..f001f77 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.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.
  *
@@ -2283,4 +2283,15 @@
 
 int wlan_hdd_check_and_stop_mon(hdd_adapter_t *sta_adapter, bool wait);
 
+/**
+ * hdd_is_sta_sap_scc_allowed_on_dfs_chan() - check if sta+sap scc allowed on
+ * dfs chan
+ * @hdd_ctx: pointer to hdd context
+ *
+ * This function used to check if sta+sap scc allowed on DFS channel.
+ *
+ * Return: None
+ */
+bool hdd_is_sta_sap_scc_allowed_on_dfs_chan(hdd_context_t *hdd_ctx);
+
 #endif    // end #if !defined( WLAN_HDD_MAIN_H )
diff --git a/CORE/HDD/src/wlan_hdd_assoc.c b/CORE/HDD/src/wlan_hdd_assoc.c
index fe069a4..9c84d84 100644
--- a/CORE/HDD/src/wlan_hdd_assoc.c
+++ b/CORE/HDD/src/wlan_hdd_assoc.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.
  *
@@ -66,6 +66,7 @@
 #include "wlan_hdd_hostapd.h"
 #include "vos_utils.h"
 #include <wlan_hdd_wext.h>
+#include "sapInternal.h"
 
 v_BOOL_t mibIsDot11DesiredBssTypeInfrastructure( hdd_adapter_t *pAdapter );
 
@@ -1509,6 +1510,57 @@
               hdd_sta_ctx->conn_info.noise);
 }
 
+/**
+ * hdd_check_and_move_if_sap_is_on_dfs_chan() - move the sap to non dfs channel
+ * @hdd_ctx: pointer to hdd context
+ * @sta_adapter: pointer to sta adapater
+ *
+ * This function is used to check if SAP is operating on DFS channel in stand
+ * alone mode and move it to non dfs channel
+ *
+ * Return: void.
+ */
+static void hdd_check_and_move_if_sap_is_on_dfs_chan(hdd_context_t *hdd_ctx,
+        hdd_adapter_t *sta_adapter)
+{
+    hdd_adapter_t *sap_adapter;
+    ptSapContext sap_ctx;
+    v_CONTEXT_t vos_ctx;
+    eNVChannelEnabledType chan_state;
+
+    if (hdd_is_sta_sap_scc_allowed_on_dfs_chan(hdd_ctx)) {
+        sap_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_SOFTAP);
+
+        if (!sap_adapter) {
+            hddLog(LOG1, FL("SAP not exists, nothing to do"));
+            return;
+        }
+
+        vos_ctx = hdd_ctx->pvosContext;
+        if (!vos_ctx) {
+            hddLog(LOGE, FL("vos_ctx is NULL"));
+            return;
+        }
+        sap_ctx = VOS_GET_SAP_CB(vos_ctx);
+        if (!sap_ctx) {
+            hddLog(LOG1, FL("sap_ctx not exists"));
+            return;
+        }
+
+        chan_state = vos_nv_getChannelEnabledState(sap_ctx->channel);
+
+        hddLog(LOG1, "SAP is operating on channel (%hu), chan_state %d",
+                sap_ctx->channel, chan_state);
+
+        if (vos_nv_getChannelEnabledState(sap_ctx->channel) !=
+                NV_CHANNEL_DFS) {
+            hddLog(LOG1, "SAP is on non DFS channel. nothing to do");
+            return;
+        }
+        hddLog(LOG1, "Schedule workqueue to move the SAP to non DFS channel");
+        schedule_delayed_work(&hdd_ctx->ecsa_chan_change_work, 0);
+    }
+}
 
 static eHalStatus hdd_DisConnectHandler( hdd_adapter_t *pAdapter, tCsrRoamInfo *pRoamInfo,
                                             tANI_U32 roamId, eRoamCmdStatus roamStatus,
@@ -1815,6 +1867,8 @@
     //Unblock anyone waiting for disconnect to complete
     complete(&pAdapter->disconnect_comp_var);
     hdd_print_bss_info(pHddStaCtx);
+
+    hdd_check_and_move_if_sap_is_on_dfs_chan(pHddCtx, pAdapter);
     return( status );
 }
 
@@ -2115,7 +2169,7 @@
  */
 static void
 hdd_schedule_ecsa_chan_change_work(hdd_context_t *hdd_ctx,
-                                                  uint8_t sta_session_id)
+                                   uint8_t sta_session_id)
 {
    v_TIME_t conn_start_time;
    int32_t time_diff;
@@ -4224,8 +4278,7 @@
                          (pHostapdAdapter->sessionCtx.ap.operatingChannel !=
                           pRoamInfo->chan_info.chan_id))
                  {
-                     hdd_schedule_ecsa_chan_change_work(pHddCtx,
-                             pAdapter->sessionId);
+                     schedule_delayed_work(&pHddCtx->ecsa_chan_change_work, 0);
                  }
                  else
                      hddLog(LOG1, FL("SAP restart not required"));
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index d6e3b7d..f4269e4 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -10455,6 +10455,7 @@
                          "%s: Invalid Channel [%d]", __func__, pConfig->channel);
                  return -EINVAL;
             }
+            pConfig->user_config_channel = pConfig->channel;
         }
         else
         {
@@ -10464,7 +10465,8 @@
                   WLANSAP_SetChannelRange(hHal, hdd_pConfig->apStartChannelNum,
                        hdd_pConfig->apEndChannelNum,hdd_pConfig->apOperatingBand);
              }
-                   pHddCtx->is_dynamic_channel_range_set = 0;
+             pHddCtx->is_dynamic_channel_range_set = 0;
+             pConfig->user_config_channel = SAP_DEFAULT_24GHZ_CHANNEL;
         }
     }
     else
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 6c4a1b7..7739afa 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -9899,6 +9899,12 @@
     hdd_adapter_t *sap_adapter;
     hdd_station_ctx_t *sta_ctx;
     hdd_adapter_t *sta_adapter;
+    ptSapContext sap_ctx = NULL;
+    v_CONTEXT_t vos_ctx;
+    tANI_U8 target_channel;
+    tsap_Config_t *sap_config;
+    bool sta_sap_scc_on_dfs_chan;
+    eNVChannelEnabledType chan_state;
     hdd_context_t *hdd_ctx = container_of(to_delayed_work(work),
                                           hdd_context_t,
                                           ecsa_chan_change_work);
@@ -9913,6 +9919,22 @@
         return;
     }
 
+    vos_ctx = hdd_ctx->pvosContext;
+    if (!vos_ctx) {
+        hddLog(LOGE, FL("vos_ctx is NULL"));
+        return;
+    }
+
+    sap_ctx = VOS_GET_SAP_CB(vos_ctx);
+    if (!sap_ctx) {
+        hddLog(LOGE, FL("sap_ctx is NULL"));
+        return;
+    }
+
+    sap_config = &sap_adapter->sessionCtx.ap.sapConfig;
+
+    sta_sap_scc_on_dfs_chan = hdd_is_sta_sap_scc_allowed_on_dfs_chan(hdd_ctx);
+
     sta_adapter = hdd_get_adapter(hdd_ctx,
                                   WLAN_HDD_INFRA_STATION);
     if (!sta_adapter) {
@@ -9922,15 +9944,25 @@
     sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter);
 
     if (sta_ctx->conn_info.connState != eConnectionState_Associated) {
-        hddLog(LOGE, FL("sta not in connected state %d"),
-               sta_ctx->conn_info.connState);
+        chan_state = vos_nv_getChannelEnabledState(sap_ctx->channel);
+        hddLog(LOG1, FL("sta not in connected state %d, sta_sap_scc_on_dfs_chan %d, chan_state %d"),
+                sta_ctx->conn_info.connState, sta_sap_scc_on_dfs_chan,
+                chan_state);
+        if (sta_sap_scc_on_dfs_chan &&
+                (chan_state == NV_CHANNEL_DFS)) {
+            hddLog(LOG1, FL("Switch SAP to user configured channel"));
+            target_channel = sap_config->user_config_channel;
+            goto switch_channel;
+
+        }
         return;
     }
 
-    hddLog(LOGE, FL("Switch SAP to SCC channel %d"),
-           sta_ctx->conn_info.operationChannel);
-    wlansap_set_channel_change((WLAN_HDD_GET_CTX(sap_adapter))->pvosContext,
-                               sta_ctx->conn_info.operationChannel, true);
+    target_channel = sta_ctx->conn_info.operationChannel;
+switch_channel:
+    hddLog(LOGE, FL("Switch SAP to %d channel"),
+           target_channel);
+    wlansap_set_channel_change(vos_ctx, target_channel, true);
 }
 
 /**
@@ -9950,6 +9982,24 @@
     vos_ssr_unprotect(__func__);
 }
 
+/**
+ * hdd_is_sta_sap_scc_allowed_on_dfs_chan() - check if sta+sap scc allowed on
+ * dfs chan
+ * @hdd_ctx: pointer to hdd context
+ *
+ * This function used to check if sta+sap scc allowed on DFS channel.
+ *
+ * Return: None
+ */
+bool hdd_is_sta_sap_scc_allowed_on_dfs_chan(hdd_context_t *hdd_ctx)
+{
+    if (hdd_ctx->cfg_ini->force_scc_with_ecsa &&
+            hdd_ctx->cfg_ini->sta_sap_scc_on_dfs_chan)
+        return true;
+    else
+        return false;
+}
+
 VOS_STATUS hdd_stop_all_adapters( hdd_context_t *pHddCtx )
 {
    hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;