wlan: Add Dhcp server offload feature

Add support to set the dhcp server offload.
dhcp info such as DHCP enable, server ip address,
number of clients, and start address of dhcp pool
will be sent to firmware.

Change-Id: Id24c355820287d77b0e4200b86b42fe80e6cd4f8
CRs-Fixed: 1091422
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index 7d06c2e..8bcbee0 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -47,6 +47,12 @@
 #include <vos_types.h>
 #include <csrApi.h>
 
+#ifdef DHCP_SERVER_OFFLOAD
+#define IPADDR_NUM_ENTRIES     (4)
+#define IPADDR_STRING_LENGTH   (16)
+#define DHCP_START_POOL_ADDRESS 100
+#endif /* DHCP_SERVER_OFFLOAD */
+
 //Number of items that can be configured
 #define MAX_CFG_INI_ITEMS   512
 
@@ -2743,6 +2749,32 @@
 #define CFG_DISABLE_BAR_WAKEUP_HOST_MAX         1
 #define CFG_DISABLE_BAR_WAKEUP_HOST_DEFAULT     0
 
+#ifdef DHCP_SERVER_OFFLOAD
+/*
+ * Enable/Disable DHCP Server Offload
+ * Default: Disable
+ */
+#define CFG_DHCP_SERVER_OFFLOAD_SUPPORT_NAME      "gDHCPServerOffloadEnable"
+#define CFG_DHCP_SERVER_OFFLOAD_SUPPORT_MIN       ( 0 )
+#define CFG_DHCP_SERVER_OFFLOAD_SUPPORT_MAX       ( 1 )
+#define CFG_DHCP_SERVER_OFFLOAD_SUPPORT_DEFAULT   ( 1 )
+
+/* Max number of DHCP clients to be supported */
+#define CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_NAME     "gDHCPMaxNumClients"
+#define CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_MIN      ( 1 )
+#define CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_MAX      ( 8 )
+#define CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_DEFAULT  ( 8 )
+
+/* Start address of the pool */
+#define CFG_DHCP_SERVER_OFFLOAD_START_LSB_NAME     "gDHCPStartLsb"
+#define CFG_DHCP_SERVER_OFFLOAD_START_LSB_MIN      ( 100 )
+#define CFG_DHCP_SERVER_OFFLOAD_START_LSB_MAX      ( 255 )
+#define CFG_DHCP_SERVER_OFFLOAD_START_LSB_DEFAULT  ( 100 )
+
+/* DHCP Server IP*/
+#define CFG_DHCP_SERVER_IP_NAME     "gDHCPServerIP"
+#define CFG_DHCP_SERVER_IP_DEFAULT  "192,168,1,2"
+#endif /* DHCP_SERVER_OFFLOAD */
 
 /*
  * gExtScanConcMode is used to manage EXT Scan during concurrency
@@ -3465,6 +3497,12 @@
    v_U8_t                      max_chan_for_dwell_time_cfg;
    v_U16_t                     tdls_enable_defer_time;
    v_U8_t                      boffset_correction_enable;
+#ifdef DHCP_SERVER_OFFLOAD
+   v_BOOL_t                    enable_dhcp_srv_offload;
+   v_U32_t                     dhcp_max_num_clients;
+   v_U8_t                      dhcp_srv_ip[IPADDR_STRING_LENGTH];
+   v_U8_t                      dhcp_start_lsb;
+#endif  /* DHCP_SERVER_OFFLOAD */
    uint32_t                    enable_edca_params;
    uint32_t                    edca_vo_cwmin;
    uint32_t                    edca_vi_cwmin;
@@ -3499,6 +3537,9 @@
 VOS_STATUS hdd_execute_config_command(hdd_context_t *pHddCtx, char *command);
 tANI_BOOLEAN hdd_is_okc_mode_enabled(hdd_context_t *pHddCtx);
 
+VOS_STATUS hdd_string_to_u8_array(char *str, tANI_U8 *intArray, tANI_U8 *len,
+				  tANI_U8 intArrayMaxLen);
+
 #define VAR_OFFSET( _Struct, _Var ) (offsetof(_Struct, _Var))
 #define VAR_SIZE( _Struct, _Var ) (sizeof(((_Struct *)0)->_Var))
 
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 966f6f2..5b9f38d 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -774,6 +774,14 @@
 
 } hdd_hostapd_state_t;
 
+#ifdef DHCP_SERVER_OFFLOAD
+typedef struct hdd_dhcp_state_s
+{
+    VOS_STATUS dhcp_offload_status;
+    vos_event_t vos_event;
+} hdd_dhcp_state_t;
+#endif /* DHCP_SERVER_OFFLOAD */
+
 
 /*
  * Per station structure kept in HDD for multiple station support for SoftAP
@@ -1210,6 +1218,9 @@
 
    /* Currently used antenna Index*/
    int antennaIndex;
+#ifdef DHCP_SERVER_OFFLOAD
+   hdd_dhcp_state_t dhcp_status;
+#endif /* DHCP_SERVER_OFFLOAD */
 };
 
 #define WLAN_HDD_GET_STATION_CTX_PTR(pAdapter) (&(pAdapter)->sessionCtx.station)
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index f2f7cf9..aedea63 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -3631,6 +3631,7 @@
                CFG_SAR_BOFFSET_SET_CORRECTION_MIN,
                CFG_SAR_BOFFSET_SET_CORRECTION_MAX),
 
+
   REG_VARIABLE(CFG_ENABLE_EDCA_INI_NAME, WLAN_PARAM_Integer,
                hdd_config_t, enable_edca_params,
                VAR_FLAGS_OPTIONAL |
@@ -3775,6 +3776,33 @@
                       VAR_FLAGS_OPTIONAL,
                       (void *) CFG_SAP_AUTH_OFL_KEY_DEFAULT ),
 #endif
+#ifdef DHCP_SERVER_OFFLOAD
+  REG_VARIABLE(CFG_DHCP_SERVER_OFFLOAD_SUPPORT_NAME, WLAN_PARAM_Integer,
+               hdd_config_t, enable_dhcp_srv_offload,
+               VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+               CFG_DHCP_SERVER_OFFLOAD_SUPPORT_DEFAULT,
+               CFG_DHCP_SERVER_OFFLOAD_SUPPORT_MIN,
+               CFG_DHCP_SERVER_OFFLOAD_SUPPORT_MAX),
+
+  REG_VARIABLE(CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_NAME, WLAN_PARAM_Integer,
+               hdd_config_t, dhcp_max_num_clients,
+               VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+               CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_DEFAULT,
+               CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_MIN,
+               CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT_MAX),
+
+  REG_VARIABLE_STRING(CFG_DHCP_SERVER_IP_NAME, WLAN_PARAM_String,
+               hdd_config_t, dhcp_srv_ip,
+               VAR_FLAGS_OPTIONAL,
+               (void *) CFG_DHCP_SERVER_IP_DEFAULT),
+
+  REG_VARIABLE(CFG_DHCP_SERVER_OFFLOAD_START_LSB_NAME, WLAN_PARAM_Integer,
+               hdd_config_t, dhcp_start_lsb,
+               VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+               CFG_DHCP_SERVER_OFFLOAD_START_LSB_DEFAULT,
+               CFG_DHCP_SERVER_OFFLOAD_START_LSB_MIN,
+               CFG_DHCP_SERVER_OFFLOAD_START_LSB_MAX),
+#endif /* DHCP_SERVER_OFFLOAD */
 };
 
 /*
@@ -4312,6 +4340,21 @@
           "Name = [gPERRoamCCAEnabled] Value = [%u] ",
           pHddCtx->cfg_ini->isPERRoamCCAEnabled);
 
+#ifdef DHCP_SERVER_OFFLOAD
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+           "Name = [gDHCPServerOffloadEnable] Value = [%u]",
+                   pHddCtx->cfg_ini->enable_dhcp_srv_offload);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+           "Name = [gDHCPMaxNumClients] Value = [%u]",
+                   pHddCtx->cfg_ini->dhcp_max_num_clients);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+           "Name = [gDHCPServerIP] Value = [%s]",
+                   pHddCtx->cfg_ini->dhcp_srv_ip);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+           "Name = [gDHCPStartLsb] Value = [%u]",
+                   pHddCtx->cfg_ini->dhcp_start_lsb);
+#endif /* DHCP_SERVER_OFFLOAD */
+
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
           "Name = [gPERRoamFullScanThreshold] Value = [%u] ",
           pHddCtx->cfg_ini->PERRoamFullScanThreshold);
@@ -4865,8 +4908,8 @@
 
 }
 
-#ifdef WLAN_FEATURE_NEIGHBOR_ROAMING
-static VOS_STATUS hdd_string_to_u8_array( char *str, tANI_U8 *intArray, tANI_U8 *len, tANI_U8 intArrayMaxLen )
+VOS_STATUS hdd_string_to_u8_array(char *str, tANI_U8 *intArray,
+				   tANI_U8 *len, tANI_U8 intArrayMaxLen)
 {
    char *s = str;
 
@@ -4894,7 +4937,6 @@
    return VOS_STATUS_SUCCESS;
 
 }
-#endif
 
 
 v_BOOL_t hdd_update_config_dat( hdd_context_t *pHddCtx )
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 39e1b2c..39c3303 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -9173,6 +9173,114 @@
     return ret;
 }
 
+#ifdef DHCP_SERVER_OFFLOAD
+void hdd_dhcp_server_offload_done(void *fw_dhcp_srv_offload_cb_context,
+				  VOS_STATUS status)
+{
+	hdd_adapter_t* adapter = (hdd_adapter_t*)fw_dhcp_srv_offload_cb_context;
+
+	ENTER();
+
+	if (NULL == adapter)
+	{
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: adapter is NULL",__func__);
+		return;
+	}
+
+	adapter->dhcp_status.dhcp_offload_status = status;
+	vos_event_set(&adapter->dhcp_status.vos_event);
+	return;
+}
+
+/**
+ * wlan_hdd_set_dhcp_server_offload() - set dhcp server offload
+ * @hostapd_adapter: pointer to hostapd adapter.
+ *
+ * Return: None
+ */
+static VOS_STATUS wlan_hdd_set_dhcp_server_offload(hdd_adapter_t *hostapd_adapter)
+{
+	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(hostapd_adapter);
+	sir_dhcp_srv_offload_info dhcp_srv_info;
+	tANI_U8 num_entries = 0;
+	tANI_U8 srv_ip[IPADDR_NUM_ENTRIES];
+	tANI_U8 num;
+	tANI_U32 temp;
+	VOS_STATUS ret;
+
+	ENTER();
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (0 != ret)
+		return VOS_STATUS_E_INVAL;
+
+	/* Prepare the request to send to SME */
+	dhcp_srv_info = vos_mem_malloc(sizeof(*dhcp_srv_info));
+	if (NULL == dhcp_srv_info) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: could not allocate tDhcpSrvOffloadInfo!", __func__);
+		return VOS_STATUS_E_NOMEM;
+	}
+
+	vos_mem_zero(dhcp_srv_info, sizeof(*dhcp_srv_info));
+
+	dhcp_srv_info->bssidx = hostapd_adapter->sessionId;
+	dhcp_srv_info->dhcp_srv_offload_enabled = TRUE;
+	dhcp_srv_info->dhcp_client_num = hdd_ctx->cfg_ini->dhcp_max_num_clients;
+	dhcp_srv_info->start_lsb = hdd_ctx->cfg_ini->dhcp_start_lsb;
+	dhcp_srv_info->dhcp_offload_callback = hdd_dhcp_server_offload_done;
+	dhcp_srv_info->dhcp_server_offload_cb_context = hostapd_adapter;
+
+	hdd_string_to_u8_array(hdd_ctx->cfg_ini->dhcp_srv_ip,
+			       srv_ip,
+			       &num_entries,
+			       IPADDR_NUM_ENTRIES);
+	if (num_entries != IPADDR_NUM_ENTRIES) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: incorrect IP address (%s) assigned for DHCP server!",
+		       __func__, hdd_ctx->cfg_ini->dhcp_srv_ip);
+		vos_mem_free(dhcp_srv_info);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	if ((srv_ip[0] >= 224) && (srv_ip[0] <= 239)) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: invalid IP address (%s)! It could NOT be multicast IP address!",
+		       __func__, hdd_ctx->cfg_ini->dhcp_srv_ip);
+		vos_mem_free(dhcp_srv_info);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	if (srv_ip[IPADDR_NUM_ENTRIES-1] >= DHCP_START_POOL_ADDRESS) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: invalid IP address (%s)! The last field must be less than 100!",
+		       __func__, hdd_ctx->cfg_ini->dhcp_srv_ip);
+		vos_mem_free(dhcp_srv_info);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	for (num = 0; num < num_entries; num++) {
+		temp = srv_ip[num];
+		dhcp_srv_info->dhcp_srv_ip |= (temp << (8 * num));
+	}
+
+	if (eHAL_STATUS_SUCCESS !=
+	    sme_set_dhcp_srv_offload(hdd_ctx->hHal, dhcp_srv_info)) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: sme_set_dhcp_srv_offload fail!", __func__);
+		vos_mem_free(dhcp_srv_info);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
+	       "%s: enable DHCP Server offload successfully!", __func__);
+
+	vos_mem_free(dhcp_srv_info);
+	return 0;
+}
+#endif /* DHCP_SERVER_OFFLOAD */
+
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
 static int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
                             struct beacon_parameters *params)
@@ -9659,6 +9767,29 @@
     hdd_wmm_init(pHostapdAdapter);
     wlan_hdd_incr_active_session(pHddCtx, pHostapdAdapter->device_mode);
 
+#ifdef DHCP_SERVER_OFFLOAD
+    /* set dhcp server offload */
+    if (iniConfig->enable_dhcp_srv_offload &&
+        sme_IsFeatureSupportedByFW(SAP_OFFLOADS)) {
+        status = wlan_hdd_set_dhcp_server_offload(pHostapdAdapter);
+        if (!VOS_IS_STATUS_SUCCESS(status))
+        {
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                      ("HDD DHCP Server Offload Failed!!"));
+            return -EINVAL;
+        }
+        vos_event_reset(&pHostapdAdapter->dhcp_status.vos_event);
+        status = vos_wait_single_event(&pHostapdAdapter->dhcp_status.vos_event, 2000);
+        if (!VOS_IS_STATUS_SUCCESS(status) || pHostapdAdapter->dhcp_status.dhcp_offload_status)
+        {
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                     ("ERROR: DHCP HDD vos wait for single_event failed!! %d"),
+                     pHostapdAdapter->dhcp_status.dhcp_offload_status);
+            return -EINVAL;
+        }
+    }
+#endif /* DHCP_SERVER_OFFLOAD */
+
 #ifdef WLAN_FEATURE_P2P_DEBUG
     if (pHostapdAdapter->device_mode == WLAN_HDD_P2P_GO)
     {
diff --git a/CORE/HDD/src/wlan_hdd_hostapd.c b/CORE/HDD/src/wlan_hdd_hostapd.c
index 5913fec..16d9058 100644
--- a/CORE/HDD/src/wlan_hdd_hostapd.c
+++ b/CORE/HDD/src/wlan_hdd_hostapd.c
@@ -4818,6 +4818,9 @@
 VOS_STATUS hdd_init_ap_mode( hdd_adapter_t *pAdapter )
 {
     hdd_hostapd_state_t * phostapdBuf;
+#ifdef DHCP_SERVER_OFFLOAD
+    hdd_dhcp_state_t *dhcp_status;
+#endif /* DHCP_SERVER_OFFLOAD */
     struct net_device *dev = pAdapter->dev;
     hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
     VOS_STATUS status;
@@ -4841,6 +4844,9 @@
 
     // Allocate the Wireless Extensions state structure
     phostapdBuf = WLAN_HDD_GET_HOSTAP_STATE_PTR( pAdapter );
+#ifdef DHCP_SERVER_OFFLOAD
+    dhcp_status = &pAdapter->dhcp_status;
+#endif /* DHCP_SERVER_OFFLOAD */
 
     spin_lock_init(&pAdapter->sta_hash_lock);
     pAdapter->is_sta_id_hash_initialized = VOS_FALSE;
@@ -4862,7 +4868,10 @@
 
     // Zero the memory.  This zeros the profile structure.
     memset(phostapdBuf, 0,sizeof(hdd_hostapd_state_t));
-    
+#ifdef DHCP_SERVER_OFFLOAD
+    memset(dhcp_status, 0,sizeof(*dhcp_status));
+#endif /* DHCP_SERVER_OFFLOAD */
+
     // Set up the pointer to the Wireless Extensions state structure
     // NOP
     status = hdd_set_hostapd(pAdapter);
@@ -4877,8 +4886,13 @@
          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: Hostapd HDD vos event init failed!!"));
          return status;
     }
-    
-
+#ifdef DHCP_SERVER_OFFLOAD
+    status = vos_event_init(&dhcp_status->vos_event);
+    if (!VOS_IS_STATUS_SUCCESS(status)) {
+         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: Hostapd HDD vos event init failed!!"));
+         return status;
+    }
+#endif /* DHCP_SERVER_OFFLOAD */
     sema_init(&(WLAN_HDD_GET_AP_CTX_PTR(pAdapter))->semWpsPBCOverlapInd, 1);
  
      // Register as a wireless device
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index f356ded..237daca 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -8964,6 +8964,15 @@
     }
 }
 
+VOS_STATUS hdd_cleanup_ap_events(hdd_adapter_t *adapter)
+{
+#ifdef DHCP_SERVER_OFFLOAD
+    vos_event_destroy(&adapter->dhcp_status.vos_event);
+#endif
+    return VOS_STATUS_SUCCESS;
+}
+
+
 VOS_STATUS hdd_stop_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter,
                              const v_BOOL_t bCloseSession )
 {
@@ -9134,6 +9143,7 @@
           }
 
          //Any softap specific cleanup here...
+         hdd_cleanup_ap_events(pAdapter);
          if (pAdapter->device_mode == WLAN_HDD_P2P_GO) {
             while (pAdapter->is_roc_inprogress) {
                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,