/*
 * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**=============================================================================
*     wlan_hdd_early_suspend.c
*
*     \brief      power management functions
*
*     Description

*
==============================================================================**/
/* $HEADER$ */

/**-----------------------------------------------------------------------------
*   Include files
* ----------------------------------------------------------------------------*/

#include <net/addrconf.h>
#include <linux/pm.h>
#include <linux/wait.h>
#include <linux/cpu.h>
#include <wlan_hdd_includes.h>
#include <wlan_qct_driver.h>
#include "halTypes.h"
#include "sme_Api.h"
#include <vos_api.h>
#include <vos_sched.h>
#include <macInitApi.h>
#include <wlan_qct_sys.h>
#include <wlan_nlink_common.h>
#include <wlan_hdd_main.h>
#include <wlan_hdd_assoc.h>
#include <wlan_hdd_dev_pwr.h>
#include <wlan_nlink_srv.h>
#include <wlan_hdd_misc.h>
#include <dbglog_host.h>

#include <linux/semaphore.h>
#include <wlan_hdd_hostapd.h>
#include "cfgApi.h"


#include <wcnss_api.h>
#include <linux/inetdevice.h>
#include <wlan_hdd_cfg.h>
#include <wlan_hdd_cfg80211.h>
#ifdef IPA_OFFLOAD
#include <wlan_hdd_ipa.h>
#endif
#include <wlan_logging_sock_svc.h>
#include <wlan_hdd_p2p.h>

/**-----------------------------------------------------------------------------
*   Preprocessor definitions and constants
* ----------------------------------------------------------------------------*/

/**-----------------------------------------------------------------------------
*   Type declarations
* ----------------------------------------------------------------------------*/

/**-----------------------------------------------------------------------------
*   Function and variables declarations
* ----------------------------------------------------------------------------*/
#include "wlan_hdd_power.h"
#include "wlan_hdd_packet_filtering.h"

#include <wlan_qct_wda.h>
#if defined(HIF_PCI)
#include "if_pci.h"
#elif defined(HIF_USB)
#include "if_usb.h"
#elif defined(HIF_SDIO)
#include "if_ath_sdio.h"
#endif

#include "ol_fw.h"
#include "wlan_hdd_host_offload.h"

/* Time in msec.
 * Time includes 60sec timeout of request_firmware for various binaries
 * (OTP, BDWLAN, QWLAN) and other cleanup and re-init sequence
 */
#ifdef CONFIG_SLUB_DEBUG_ON
#define HDD_SSR_BRING_UP_TIME 250000
#else
#define HDD_SSR_BRING_UP_TIME 240000
#endif

static eHalStatus g_full_pwr_status;
static eHalStatus g_standby_status;

extern VOS_STATUS hdd_post_voss_start_config(hdd_context_t* pHddCtx);
extern void hdd_wlan_initial_scan(hdd_adapter_t *pAdapter);

extern struct notifier_block hdd_netdev_notifier;
extern tVOS_CON_MODE hdd_get_conparam ( void );

static struct timer_list ssr_timer;
static bool ssr_timer_started;

#ifdef FEATURE_WLAN_DIAG_SUPPORT
/**
 * hdd_wlan_offload_event()- send offloads event
 * @type: offload type
 * @state: enabled or disabled
 *
 * This Function send offloads enable/disable diag event
 *
 * Return: void.
 */

void hdd_wlan_offload_event(uint8_t type, uint8_t state)
{
	WLAN_VOS_DIAG_EVENT_DEF(host_offload, struct vos_event_offload_req);
	vos_mem_zero(&host_offload, sizeof(host_offload));

	host_offload.offload_type = type;
	host_offload.state = state;

	WLAN_VOS_DIAG_EVENT_REPORT(&host_offload, EVENT_WLAN_OFFLOAD_REQ);
}

#endif

//Callback invoked by PMC to report status of standby request
void hdd_suspend_standby_cbk (void *callbackContext, eHalStatus status)
{
   hdd_context_t *pHddCtx = (hdd_context_t*)callbackContext;
   hddLog(VOS_TRACE_LEVEL_INFO, "%s: Standby status = %d", __func__, status);
   g_standby_status = status;

   if(eHAL_STATUS_SUCCESS == status)
   {
      pHddCtx->hdd_ps_state = eHDD_SUSPEND_STANDBY;
   }
   else
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestStandby failed",__func__);
   }

   complete(&pHddCtx->standby_comp_var);
}

//Callback invoked by PMC to report status of full power request
void hdd_suspend_full_pwr_callback(void *callbackContext, eHalStatus status)
{
   hdd_context_t *pHddCtx = (hdd_context_t*)callbackContext;
   hddLog(VOS_TRACE_LEVEL_INFO, "%s: Full Power status = %d", __func__, status);
   g_full_pwr_status = status;

   if(eHAL_STATUS_SUCCESS == status)
   {
      pHddCtx->hdd_ps_state = eHDD_SUSPEND_NONE;
   }
   else
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestFullPower failed",__func__);
   }

   complete(&pHddCtx->full_pwr_comp_var);
}

eHalStatus hdd_exit_standby(hdd_context_t *pHddCtx)
{
    eHalStatus status = VOS_STATUS_SUCCESS;
    unsigned long rc;

    hddLog(VOS_TRACE_LEVEL_INFO, "%s: WLAN being resumed from standby",__func__);
    INIT_COMPLETION(pHddCtx->full_pwr_comp_var);

   g_full_pwr_status = eHAL_STATUS_FAILURE;
    status = sme_RequestFullPower(pHddCtx->hHal, hdd_suspend_full_pwr_callback, pHddCtx,
      eSME_FULL_PWR_NEEDED_BY_HDD);

   if(status == eHAL_STATUS_PMC_PENDING)
   {
      //Block on a completion variable. Can't wait forever though
      rc = wait_for_completion_timeout(
                 &pHddCtx->full_pwr_comp_var,
                 msecs_to_jiffies(WLAN_WAIT_TIME_FULL_PWR));
      if (!rc) {
          hddLog(VOS_TRACE_LEVEL_ERROR,
             FL("wait on full_pwr_comp_var failed"));
      }
      status = g_full_pwr_status;
      if(g_full_pwr_status != eHAL_STATUS_SUCCESS)
      {
         hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestFullPower failed",__func__);
         VOS_ASSERT(0);
         goto failure;
      }
    }
    else if(status != eHAL_STATUS_SUCCESS)
    {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestFullPower failed - status %d",
         __func__, status);
      VOS_ASSERT(0);
      goto failure;
    }
    else
      pHddCtx->hdd_ps_state = eHDD_SUSPEND_NONE;

failure:
    //No blocking to reduce latency. No other device should be depending on WLAN
    //to finish resume and WLAN won't be instantly on after resume
    return status;
}


//Helper routine to put the chip into standby
VOS_STATUS hdd_enter_standby(hdd_context_t *pHddCtx)
{
   eHalStatus halStatus = eHAL_STATUS_SUCCESS;
   VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
   unsigned long rc;

   //Disable IMPS/BMPS as we do not want the device to enter any power
   //save mode on its own during suspend sequence
   sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
   sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);

   //Note we do not disable queues unnecessarily. Queues should already be disabled
   //if STA is disconnected or the queue will be disabled as and when disconnect
   //happens because of standby procedure.

   //Ensure that device is in full power first. There is scope for optimization
   //here especially in scenarios where PMC is already in IMPS or REQUEST_IMPS.
   //Core s/w needs to be optimized to handle this. Until then we request full
   //power before issuing request for standby.
   INIT_COMPLETION(pHddCtx->full_pwr_comp_var);
   g_full_pwr_status = eHAL_STATUS_FAILURE;
   halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_suspend_full_pwr_callback,
       pHddCtx, eSME_FULL_PWR_NEEDED_BY_HDD);

   if(halStatus == eHAL_STATUS_PMC_PENDING)
   {
      //Block on a completion variable. Can't wait forever though
      rc = wait_for_completion_timeout(
                 &pHddCtx->full_pwr_comp_var,
                 msecs_to_jiffies(WLAN_WAIT_TIME_FULL_PWR));
      if (!rc) {
          hddLog(VOS_TRACE_LEVEL_ERROR,
                 FL("wait on full_pwr_comp_var failed"));
      }

      if(g_full_pwr_status != eHAL_STATUS_SUCCESS)
      {
         hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestFullPower Failed",__func__);
         VOS_ASSERT(0);
         vosStatus = VOS_STATUS_E_FAILURE;
         goto failure;
      }
   }
   else if(halStatus != eHAL_STATUS_SUCCESS)
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestFullPower failed - status %d",
         __func__, halStatus);
      VOS_ASSERT(0);
      vosStatus = VOS_STATUS_E_FAILURE;
      goto failure;
   }

   if(pHddCtx->hdd_mcastbcast_filter_set == TRUE) {
         hdd_conf_mcastbcast_filter(pHddCtx, FALSE);
         pHddCtx->hdd_mcastbcast_filter_set = FALSE;
   }

   //Request standby. Standby will cause the STA to disassociate first. TX queues
   //will be disabled (by HDD) when STA disconnects. You do not want to disable TX
   //queues here. Also do not assert if the failure code is eHAL_STATUS_PMC_NOT_NOW as PMC
   //will send this failure code in case of concurrent sessions. Power Save cannot be supported
   //when there are concurrent sessions.
   INIT_COMPLETION(pHddCtx->standby_comp_var);
   g_standby_status = eHAL_STATUS_FAILURE;
   halStatus = sme_RequestStandby(pHddCtx->hHal, hdd_suspend_standby_cbk, pHddCtx);

   if (halStatus == eHAL_STATUS_PMC_PENDING)
   {
      //Wait till WLAN device enters standby mode
      rc = wait_for_completion_timeout(&pHddCtx->standby_comp_var,
         msecs_to_jiffies(WLAN_WAIT_TIME_STANDBY));
      if (!rc) {
          hddLog(VOS_TRACE_LEVEL_ERROR,
                 FL("wait on standby_comp_var failed"));
      }

      if (g_standby_status != eHAL_STATUS_SUCCESS && g_standby_status != eHAL_STATUS_PMC_NOT_NOW)
      {
         hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestStandby failed",__func__);
         VOS_ASSERT(0);
         vosStatus = VOS_STATUS_E_FAILURE;
         goto failure;
      }
   }
   else if (halStatus != eHAL_STATUS_SUCCESS && halStatus != eHAL_STATUS_PMC_NOT_NOW) {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestStandby failed - status %d",
         __func__, halStatus);
      VOS_ASSERT(0);
      vosStatus = VOS_STATUS_E_FAILURE;
      goto failure;
   }
   else
      pHddCtx->hdd_ps_state = eHDD_SUSPEND_STANDBY;

failure:
   //Restore IMPS config
   if(pHddCtx->cfg_ini->fIsImpsEnabled)
      sme_EnablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);

   //Restore BMPS config
   if(pHddCtx->cfg_ini->fIsBmpsEnabled)
      sme_EnablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);

   return vosStatus;
}


//Helper routine for Deep sleep entry
VOS_STATUS hdd_enter_deep_sleep(hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter)
{
   eHalStatus halStatus;
   VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
   unsigned long rc;

   //Stop the Interface TX queue.
   hddLog(LOG1, FL("Disabling queues"));
   wlan_hdd_netif_queue_control(pAdapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
        WLAN_CONTROL_PATH);

   //Disable IMPS,BMPS as we do not want the device to enter any power
   //save mode on it own during suspend sequence
   sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
   sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);

   //Ensure that device is in full power as we will touch H/W during vos_Stop
   INIT_COMPLETION(pHddCtx->full_pwr_comp_var);
   g_full_pwr_status = eHAL_STATUS_FAILURE;
   halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_suspend_full_pwr_callback,
       pHddCtx, eSME_FULL_PWR_NEEDED_BY_HDD);

   if(halStatus == eHAL_STATUS_PMC_PENDING)
   {
      //Block on a completion variable. Can't wait forever though
      rc = wait_for_completion_timeout(
                 &pHddCtx->full_pwr_comp_var,
                 msecs_to_jiffies(WLAN_WAIT_TIME_FULL_PWR));
      if (!rc) {
          hddLog(VOS_TRACE_LEVEL_ERROR,
              FL("wait on full_pwr_comp_var failed"));
      }

      if(g_full_pwr_status != eHAL_STATUS_SUCCESS){
         hddLog(VOS_TRACE_LEVEL_FATAL,"%s: sme_RequestFullPower failed",__func__);
         VOS_ASSERT(0);
      }
   }
   else if(halStatus != eHAL_STATUS_SUCCESS)
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Request for Full Power failed",__func__);
      VOS_ASSERT(0);
   }

   //Issue a disconnect. This is required to inform the supplicant that
   //STA is getting disassociated and for GUI to be updated properly
   INIT_COMPLETION(pAdapter->disconnect_comp_var);
   halStatus = sme_RoamDisconnect(pHddCtx->hHal, pAdapter->sessionId, eCSR_DISCONNECT_REASON_UNSPECIFIED);

   //Success implies disconnect command got queued up successfully
   if(halStatus == eHAL_STATUS_SUCCESS)
   {
      //Block on a completion variable. Can't wait forever though.
      rc = wait_for_completion_timeout(
                 &pAdapter->disconnect_comp_var,
                 msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
      if (!rc) {
          hddLog(VOS_TRACE_LEVEL_ERROR,
             FL("wait on disconnect_comp_var failed"));
      }
   }
   //None of the steps should fail after this. Continue even in case of failure
   vosStatus = vos_stop( pHddCtx->pvosContext );
   if (!VOS_IS_STATUS_SUCCESS( vosStatus ))
   {
      hddLog(VOS_TRACE_LEVEL_ERROR, "%s: vos_stop return failed %d",
             __func__, vosStatus);
      VOS_ASSERT(0);
   }

   pHddCtx->hdd_ps_state = eHDD_SUSPEND_DEEP_SLEEP;

   //Restore IMPS config
   if(pHddCtx->cfg_ini->fIsImpsEnabled)
      sme_EnablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);

   //Restore BMPS config
   if(pHddCtx->cfg_ini->fIsBmpsEnabled)
      sme_EnablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);

   return vosStatus;
}

VOS_STATUS hdd_exit_deep_sleep(hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter)
{
   VOS_STATUS vosStatus;
   eHalStatus halStatus;
   tANI_U32 type, subType;

   VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
      "%s: calling hdd_set_sme_config",__func__);
   vosStatus = hdd_set_sme_config( pHddCtx );
   VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
         "%s: Failed in hdd_set_sme_config",__func__);
      goto err_deep_sleep;
   }

   VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
      "%s: calling vos_start",__func__);
   vosStatus = vos_start( pHddCtx->pvosContext );
   VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
         "%s: Failed in vos_start",__func__);
      goto err_deep_sleep;
   }

   VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
      "%s: calling hdd_post_voss_start_config",__func__);
   vosStatus = hdd_post_voss_start_config( pHddCtx );
   VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
         "%s: Failed in hdd_post_voss_start_config",__func__);
      goto err_voss_stop;
   }

   vosStatus = vos_get_vdev_types(pAdapter->device_mode, &type, &subType);
   if (VOS_STATUS_SUCCESS != vosStatus)
   {
      hddLog(VOS_TRACE_LEVEL_ERROR, "failed to get vdev type");
      goto err_voss_stop;
   }

   //Open a SME session for future operation
   halStatus = sme_OpenSession( pHddCtx->hHal, hdd_smeRoamCallback, pAdapter,
         (tANI_U8 *)&pAdapter->macAddressCurrent, &pAdapter->sessionId,
         type, subType);
   if ( !HAL_STATUS_SUCCESS( halStatus ) )
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"sme_OpenSession() failed with status code %08d [x%08x]",
                    halStatus, halStatus );
      goto err_voss_stop;

   }

   pHddCtx->hdd_ps_state = eHDD_SUSPEND_NONE;

   //Trigger the initial scan
   hdd_wlan_initial_scan(pAdapter);

   return VOS_STATUS_SUCCESS;

err_voss_stop:
   vos_stop(pHddCtx->pvosContext);
err_deep_sleep:
   return VOS_STATUS_E_FAILURE;

}

#ifdef WLAN_FEATURE_GTK_OFFLOAD
void hdd_conf_gtk_offload(hdd_adapter_t *pAdapter, v_BOOL_t fenable)
{
    eHalStatus ret;
    tSirGtkOffloadParams hddGtkOffloadReqParams;
    hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);

    if(fenable)
    {
        if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
           (GTK_OFFLOAD_ENABLE == pHddStaCtx->gtkOffloadReqParams.ulFlags ))
        {
            vos_mem_copy(&hddGtkOffloadReqParams,
                 &pHddStaCtx->gtkOffloadReqParams,
                 sizeof (tSirGtkOffloadParams));

            ret = sme_SetGTKOffload(WLAN_HDD_GET_HAL_CTX(pAdapter),
                          &hddGtkOffloadReqParams, pAdapter->sessionId);
            if (eHAL_STATUS_SUCCESS != ret)
            {
                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                       "%s: sme_SetGTKOffload failed, returned %d",
                       __func__, ret);
                return;
            }

            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                   "%s: sme_SetGTKOffload successful", __func__);
        }

    }
    else
    {
        if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
            (0 ==  memcmp(&pHddStaCtx->gtkOffloadReqParams.bssId,
                     &pHddStaCtx->conn_info.bssId, VOS_MAC_ADDR_SIZE)) &&
            (GTK_OFFLOAD_ENABLE == pHddStaCtx->gtkOffloadReqParams.ulFlags))
        {

            /* Host driver has previously  offloaded GTK rekey  */
            ret = sme_GetGTKOffload(WLAN_HDD_GET_HAL_CTX(pAdapter),
                                wlan_hdd_cfg80211_update_replayCounterCallback,
                                pAdapter, pAdapter->sessionId);
            if (eHAL_STATUS_SUCCESS != ret)

            {
                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                       "%s: sme_GetGTKOffload failed, returned %d",
                       __func__, ret);
                return;
            }
            else
            {
                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                       "%s: sme_GetGTKOffload successful",
                       __func__);

                /* Sending GTK offload disable */
                memcpy(&hddGtkOffloadReqParams, &pHddStaCtx->gtkOffloadReqParams,
                      sizeof (tSirGtkOffloadParams));
                hddGtkOffloadReqParams.ulFlags = GTK_OFFLOAD_DISABLE;
                ret = sme_SetGTKOffload(WLAN_HDD_GET_HAL_CTX(pAdapter),
                                &hddGtkOffloadReqParams, pAdapter->sessionId);
                if (eHAL_STATUS_SUCCESS != ret)
                {
                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                            "%s: failed to disable GTK offload, returned %d",
                            __func__, ret);
                    return;
                }
                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                        "%s: successfully disabled GTK offload request to HAL",
                        __func__);
            }
        }
    }
    return;
}
#endif /*WLAN_FEATURE_GTK_OFFLOAD*/

#ifdef WLAN_NS_OFFLOAD

static int __wlan_hdd_ipv6_changed(struct notifier_block *nb,
				   unsigned long data, void *arg)
{
	struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg;
	struct net_device *ndev = ifa->idev->dev;
	hdd_context_t *hdd_ctx;
	hdd_adapter_t *adapter;
	int status;

	hdd_ctx = container_of(nb, hdd_context_t, ipv6_notifier);
	status = wlan_hdd_validate_context(hdd_ctx);
	if (0 != status)
		return NOTIFY_DONE;

	adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
	if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) return NOTIFY_DONE;
	if (adapter->dev != ndev) return NOTIFY_DONE;
	if (WLAN_HDD_GET_CTX(adapter) != hdd_ctx) return NOTIFY_DONE;

	if (adapter->device_mode == WLAN_HDD_INFRA_STATION ||
	    adapter->device_mode == WLAN_HDD_P2P_CLIENT ||
	    adapter->device_mode == WLAN_HDD_NDI) {

		if (eConnectionState_Associated ==
			WLAN_HDD_GET_STATION_CTX_PTR
			(adapter)->conn_info.connState)
				sme_dhcp_done_ind(hdd_ctx->hHal,
				adapter->sessionId);

		if (hdd_ctx->cfg_ini->nEnableSuspend ==
			WLAN_MAP_SUSPEND_TO_MCAST_BCAST_FILTER &&
			hdd_ctx->ns_offload_enable)
			schedule_work(&adapter->ipv6NotifierWorkQueue);
		else
			hddLog(LOG1, FL("Not scheduling ipv6 wq nEnableSuspend: %d"),
				hdd_ctx->cfg_ini->nEnableSuspend);
	}

	return NOTIFY_DONE;
}

/**
 * wlan_hdd_ipv6_changed() - IPv6 change notifier callback
 * @nb: pointer to notifier block
 * @data: data
 * @arg: arg
 *
 * This is the IPv6 notifier callback function gets invoked
 * if any change in IP and then invoke the function @__wlan_hdd_ipv6_changed
 * to reconfigure the offload parameters.
 *
 * Return: 0 on success, error number otherwise.
 */
int wlan_hdd_ipv6_changed(struct notifier_block *nb,
				unsigned long data, void *arg)
{
	int ret;

	vos_ssr_protect(__func__);
	ret = __wlan_hdd_ipv6_changed(nb, data, arg);
	vos_ssr_unprotect(__func__);

	return ret;
}

/**
 * hdd_fill_ipv6_uc_addr() - fill IPv6 unicast addresses
 * @idev: pointer to net device
 * @ipv6addr: destination array to fill IPv6 addresses
 * @ipv6addr_type: IPv6 Address type
 * @count: number of IPv6 addresses
 *
 * This is the IPv6 utility function to populate unicast addresses.
 *
 * Return: 0 on success, error number otherwise.
 */
static int hdd_fill_ipv6_uc_addr(struct inet6_dev *idev,
				uint8_t ipv6_uc_addr[][SIR_MAC_IPV6_ADDR_LEN],
				uint8_t *ipv6addr_type, uint32_t *count)
{
	struct inet6_ifaddr *ifa;
	struct list_head *p;
	uint32_t scope;

	read_lock_bh(&idev->lock);
	list_for_each(p, &idev->addr_list) {
		if (*count >= SIR_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA) {
			read_unlock_bh(&idev->lock);
			return -EINVAL;
		}
		ifa = list_entry(p, struct inet6_ifaddr, if_list);
		if (ifa->flags & IFA_F_DADFAILED)
			continue;
		scope = ipv6_addr_src_scope(&ifa->addr);
		switch (scope) {
		case IPV6_ADDR_SCOPE_GLOBAL:
		case IPV6_ADDR_SCOPE_LINKLOCAL:
			vos_mem_copy(ipv6_uc_addr[*count], &ifa->addr.s6_addr,
				sizeof(ifa->addr.s6_addr));
			ipv6addr_type[*count] = SIR_IPV6_ADDR_UC_TYPE;
			hddLog (LOG1,
				FL("Index %d scope = %s UC-Address: %pI6"),
				*count, (scope == IPV6_ADDR_SCOPE_LINKLOCAL) ?
				"LINK LOCAL": "GLOBAL", ipv6_uc_addr[*count]);
			*count += 1;
			break;
		default:
			hddLog(LOGE, "The Scope %d is not supported", scope);
		}
	}

	read_unlock_bh(&idev->lock);
	return 0;
}

/**
 * hdd_fill_ipv6_ac_addr() - fill IPv6 anycast addresses
 * @idev: pointer to net device
 * @ipv6addr: destination array to fill IPv6 addresses
 * @ipv6addr_type: IPv6 Address type
 * @count: number of IPv6 addresses
 *
 * This is the IPv6 utility function to populate anycast addresses.
 *
 * Return: 0 on success, error number otherwise.
 */
static int hdd_fill_ipv6_ac_addr(struct inet6_dev *idev,
				uint8_t ipv6_ac_addr[][SIR_MAC_IPV6_ADDR_LEN],
				uint8_t *ipv6addr_type, uint32_t *count)
{
	struct ifacaddr6 *ifaca;
	uint32_t scope;

	read_lock_bh(&idev->lock);
	for (ifaca = idev->ac_list; ifaca; ifaca = ifaca->aca_next) {
		if (*count >= SIR_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA) {
			read_unlock_bh(&idev->lock);
			return -EINVAL;
		}
		/* For anycast addr no DAD */
		scope = ipv6_addr_src_scope(&ifaca->aca_addr);
		switch (scope) {
		case IPV6_ADDR_SCOPE_GLOBAL:
		case IPV6_ADDR_SCOPE_LINKLOCAL:
			vos_mem_copy(ipv6_ac_addr[*count], &ifaca->aca_addr,
				sizeof(ifaca->aca_addr));
			ipv6addr_type[*count] = SIR_IPV6_ADDR_AC_TYPE;
			hddLog (LOG1,
				FL("Index %d scope = %s AC-Address: %pI6"),
				*count, (scope == IPV6_ADDR_SCOPE_LINKLOCAL) ?
				"LINK LOCAL": "GLOBAL", ipv6_ac_addr[*count]);
			*count += 1;
			break;
		default:
			hddLog(LOGE, "The Scope %d is not supported", scope);
		}
	}

	read_unlock_bh(&idev->lock);
	return 0;
}

/**----------------------------------------------------------------------------

  \brief hdd_conf_ns_offload() - Configure NS offload

  Called during SUSPEND to configure the NS offload (MC BC filter) which
  reduces power consumption.

  \param  - pAdapter - Adapter context for which NS offload is to be configured
  \param  - fenable - 0 - disable.
                      1 - enable. (with IPv6 notifier registration)
                      2 - enable. (without IPv6 notifier registration)

  \return - void

  ---------------------------------------------------------------------------*/
void hdd_conf_ns_offload(hdd_adapter_t *pAdapter, int fenable)
{
    struct inet6_dev *in6_dev;
    uint8_t ipv6_addr[SIR_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA]
                                     [SIR_MAC_IPV6_ADDR_LEN] = {{0,}};
    uint8_t ipv6_addr_type[SIR_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA] = {0};
    tSirHostOffloadReq offLoadRequest;
    hdd_context_t *pHddCtx;

    int i = 0, ret;
    eHalStatus returnStatus;
    uint32_t count = 0;

    ENTER();
    hddLog(LOG1, FL(" fenable = %d"), fenable);

    pHddCtx = WLAN_HDD_GET_CTX(pAdapter);

    /* In SAP/P2PGo mode, ARP/NS offload feature capability
     * is controlled by one bit.
     */

     if ((WLAN_HDD_SOFTAP == pAdapter->device_mode ||
          WLAN_HDD_P2P_GO == pAdapter->device_mode) &&
          !pHddCtx->ap_arpns_support) {
           hddLog(LOG1, FL("NS Offload is not supported in SAP/P2PGO mode"));
           return;
    }

    if (fenable) {
        in6_dev = __in6_dev_get(pAdapter->dev);
        if (NULL != in6_dev) {
            /* Unicast Addresses */
            ret = hdd_fill_ipv6_uc_addr(in6_dev, ipv6_addr,
                                        ipv6_addr_type, &count);

            if (0 > ret) {
                if (pHddCtx->cfg_ini->active_mode_offload)
                    goto disable_ns;
                else {
                     hddLog(LOG1,
                            FL("Reached max supported addresses and not enabling NS offload"));
                     return;
                }
            }

            /* Anycast Addresses */
            ret = hdd_fill_ipv6_ac_addr(in6_dev, ipv6_addr,
                                        ipv6_addr_type, &count);

            if (0 > ret) {
                if (pHddCtx->cfg_ini->active_mode_offload)
                    goto disable_ns;
                else {
                    hddLog(LOG1,
                        FL("Reached max supported addresses and not enabling NS offload"));
                    return;
                }
            }

            vos_mem_zero(&offLoadRequest, sizeof(offLoadRequest));
            for (i = 0; i < count; i++) {
                /* Filling up the request structure
                 * Filling the selfIPv6Addr with solicited address
                 * A Solicited-Node multicast address is created by
                 * taking the last 24 bits of a unicast or anycast
                 * address and appending them to the prefix
                 *
                 * FF02:0000:0000:0000:0000:0001:FFXX:XXXX
                 *
                 * here XX is the unicast/anycast bits
                 */
                offLoadRequest.nsOffloadInfo.selfIPv6Addr[i][0] = 0xFF;
                offLoadRequest.nsOffloadInfo.selfIPv6Addr[i][1] = 0x02;
                offLoadRequest.nsOffloadInfo.selfIPv6Addr[i][11] = 0x01;
                offLoadRequest.nsOffloadInfo.selfIPv6Addr[i][12] = 0xFF;
                offLoadRequest.nsOffloadInfo.selfIPv6Addr[i][13] =
                                                       ipv6_addr[i][13];
                offLoadRequest.nsOffloadInfo.selfIPv6Addr[i][14] =
                                                       ipv6_addr[i][14];
                offLoadRequest.nsOffloadInfo.selfIPv6Addr[i][15] =
                                                       ipv6_addr[i][15];
                offLoadRequest.nsOffloadInfo.slotIdx = i;

                vos_mem_copy(&offLoadRequest.nsOffloadInfo.targetIPv6Addr[i],
                   &ipv6_addr[i][0], SIR_MAC_IPV6_ADDR_LEN);

                offLoadRequest.nsOffloadInfo.targetIPv6AddrValid[i] =
                                                    SIR_IPV6_ADDR_VALID;
                offLoadRequest.nsOffloadInfo.target_ipv6_addr_type[i] =
                                                       ipv6_addr_type[i];
                hdd_wlan_offload_event(SIR_IPV6_NS_OFFLOAD,
                                               SIR_OFFLOAD_ENABLE);
                vos_mem_copy(&offLoadRequest.params.hostIpv6Addr,
                   &offLoadRequest.nsOffloadInfo.targetIPv6Addr[i],
                   sizeof(tANI_U8)*SIR_MAC_IPV6_ADDR_LEN);

                hddLog (LOG1,
                   FL("Setting NSOffload with solicitedIp: %pI6, targetIp: %pI6, Index %d"),
                   &offLoadRequest.nsOffloadInfo.selfIPv6Addr[i],
                   &offLoadRequest.nsOffloadInfo.targetIPv6Addr[i], i);
            }

            hddLog (LOG1,
                FL("configuredMcastBcastFilter: %d"),
                pHddCtx->configuredMcastBcastFilter);

            offLoadRequest.offloadType =  SIR_IPV6_NS_OFFLOAD;
            offLoadRequest.enableOrDisable = SIR_OFFLOAD_ENABLE;
            vos_mem_copy(&offLoadRequest.nsOffloadInfo.selfMacAddr,
               &pAdapter->macAddressCurrent.bytes, SIR_MAC_ADDR_LEN);
            /* set number of ns offload address count */
            offLoadRequest.num_ns_offload_count = count;
            /* Configure the Firmware with this */
            returnStatus = sme_SetHostOffload(WLAN_HDD_GET_HAL_CTX(pAdapter),
                               pAdapter->sessionId, &offLoadRequest);
            if (eHAL_STATUS_SUCCESS != returnStatus) {
                 hddLog(LOGE,
                        FL("Failed to enable HostOffload feature with status: %d"),
                        returnStatus);
            }
        }
        else {
            hddLog(LOGE,
                    FL("IPv6 dev does not exist. Failed to request NSOffload"));
            return;
        }
    } else {
disable_ns:
        /* Disable NSOffload */
        hddLog(LOG1, FL("Disable NS Offload"));
        vos_mem_zero((void *)&offLoadRequest, sizeof(tSirHostOffloadReq));
        offLoadRequest.enableOrDisable = SIR_OFFLOAD_DISABLE;
        offLoadRequest.offloadType =  SIR_IPV6_NS_OFFLOAD;
        hdd_wlan_offload_event(SIR_IPV6_NS_OFFLOAD,
                                           SIR_OFFLOAD_DISABLE);

        if (eHAL_STATUS_SUCCESS !=
             sme_SetHostOffload(WLAN_HDD_GET_HAL_CTX(pAdapter),
                 pAdapter->sessionId, &offLoadRequest)) {
             hddLog(LOGE, FL("Failed to disable NS Offload"));
        }
    }
    EXIT();
    return;
}

/**
 * __hdd_ipv6_notifier_work_queue() - IP V6 change notifier work handler
 * @work: Pointer to work context
 *
 * Return: none
 */
static void __hdd_ipv6_notifier_work_queue(struct work_struct *work)
{
    hdd_adapter_t* pAdapter =
             container_of(work, hdd_adapter_t, ipv6NotifierWorkQueue);
    hdd_context_t *pHddCtx;
    int status;
    bool ndi_connected = false;

    ENTER();

    pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
    status = wlan_hdd_validate_context(pHddCtx);
    if (0 != status)
        return;

   if (!pHddCtx->cfg_ini->active_mode_offload) {
       hddLog(LOG1, FL("Active mode offload is disabled"));
       return;
   }

    if ( VOS_FALSE == pHddCtx->sus_res_mcastbcast_filter_valid)
    {
        pHddCtx->sus_res_mcastbcast_filter =
            pHddCtx->configuredMcastBcastFilter;
        pHddCtx->sus_res_mcastbcast_filter_valid = VOS_TRUE;
    }

    /* check if the device is in NAN data mode */
    if (WLAN_HDD_IS_NDI(pAdapter))
        ndi_connected = WLAN_HDD_IS_NDI_CONNECTED(pAdapter);

    if ((eConnectionState_Associated ==
            (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState ||
         ndi_connected)) {
        /*
         * This invocation being part of the IPv6 registration callback,
         * we are passing second parameter as 2 to avoid registration
         * of IPv6 notifier again
         */
        if (pHddCtx->cfg_ini->fhostNSOffload)
            hdd_conf_ns_offload(pAdapter, 2);
    }
    EXIT();
}

/**
 * hdd_ipv6_notifier_work_queue() - IP V6 change notifier work handler
 * @work: Pointer to work context
 *
 * Return: none
 */
void hdd_ipv6_notifier_work_queue(struct work_struct *work)
{
	vos_ssr_protect(__func__);
	__hdd_ipv6_notifier_work_queue(work);
	vos_ssr_unprotect(__func__);
}


#endif

/*
 * Function: hdd_conf_hostoffload
 *           Central function to configure the supported offloads,
 *           either enable or disable them.
 */
void hdd_conf_hostoffload(hdd_adapter_t *pAdapter, v_BOOL_t fenable)
{
    hdd_context_t *pHddCtx = NULL;
    v_CONTEXT_t *pVosContext = NULL;
    VOS_STATUS vstatus = VOS_STATUS_E_FAILURE;

    ENTER();

    hddLog(VOS_TRACE_LEVEL_INFO, FL("Configuring offloads with flag: %d"),
            fenable);

    pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

    if (NULL == pVosContext) {
        hddLog(VOS_TRACE_LEVEL_ERROR, FL(" Global VOS context is Null"));
        return;
    }

    //Get the HDD context.
    pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );

    if (NULL == pHddCtx) {
        hddLog(VOS_TRACE_LEVEL_ERROR, "%s: HDD context is Null", __func__);
        return;
    }

    if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
           (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
               (WLAN_HDD_SOFTAP == pAdapter->device_mode) ||
               (WLAN_HDD_P2P_GO == pAdapter->device_mode))
    {
        if (fenable) {
            if ((eConnectionState_Associated ==
                (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState) ||
                (WLAN_HDD_SOFTAP == pAdapter->device_mode) ||
                (WLAN_HDD_P2P_GO == pAdapter->device_mode)) {
                    if (!pHddCtx->cfg_ini->active_mode_offload) {
                         if ((pHddCtx->cfg_ini->fhostArpOffload)) {
                            /*
                             * Configure the ARP Offload.
                             * Even if it fails we have to reconfigure the MC/BC
                             * filter flag as we want RIVA not to drop BroadCast
                             * Packets
                             */
                             hddLog(VOS_TRACE_LEVEL_INFO,
                                FL("Calling ARP Offload with flag: %d"),
                                fenable);
                             vstatus = hdd_conf_arp_offload(pAdapter, fenable);
                             pHddCtx->configuredMcastBcastFilter &=
                                ~(HDD_MCASTBCASTFILTER_FILTER_ALL_BROADCAST);

                             if (!VOS_IS_STATUS_SUCCESS(vstatus)) {
                                 hddLog(VOS_TRACE_LEVEL_INFO,
                                    "Failed to enable ARPOFfloadFeature %d",
                                    vstatus);
                             }
                         }
#ifdef WLAN_NS_OFFLOAD
                         if (pHddCtx->cfg_ini->fhostNSOffload &&
                             pHddCtx->ns_offload_enable) {
                             /*
                              * Configure the NS Offload.
                              * Even if it fails we have to reconfigure the
                              * MC/BC filter flag as we want RIVA not to
                              * drop Multicast Packets
                              */

                              hddLog(VOS_TRACE_LEVEL_INFO,
                                  FL("Calling NS Offload with flag: %d"),
                                  fenable);
                              hdd_conf_ns_offload(pAdapter, fenable);
                              pHddCtx->configuredMcastBcastFilter &=
                                  ~(HDD_MCASTBCASTFILTER_FILTER_ALL_MULTICAST);
                         }
#endif
                    }

                    /* Configure GTK_OFFLOAD */
#ifdef WLAN_FEATURE_GTK_OFFLOAD
                    hdd_conf_gtk_offload(pAdapter, fenable);
#endif

                    /*
                     * This variable saves the state if offload were configured
                     * or not. helps in recovering when pcie fails to suspend
                     * because of ongoing scan and state is no longer
                     * associated.
                     */
                     pAdapter->offloads_configured = TRUE;
            }
        } else {
            /* Disable offlaod features */
            if ((eConnectionState_Associated ==
                 (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState) ||
                 (pAdapter->offloads_configured == TRUE)) {

                  pAdapter->offloads_configured = FALSE;

                  /* Disable ARPOFFLOAD */
                  if (!pHddCtx->cfg_ini->active_mode_offload) {
                      if (pHddCtx->cfg_ini->fhostArpOffload) {
                          vstatus = hdd_conf_arp_offload(pAdapter, fenable);
                          if (!VOS_IS_STATUS_SUCCESS(vstatus)) {
                              hddLog(VOS_TRACE_LEVEL_ERROR,
                                 "Failed to disable ARPOffload Feature %d",
                                 vstatus);
                          }
                      }

#ifdef WLAN_NS_OFFLOAD
                      /* Disable NSOFFLOAD */
                      if (pHddCtx->cfg_ini->fhostNSOffload &&
                          pHddCtx->ns_offload_enable) {
                             hdd_conf_ns_offload(pAdapter, fenable);
                      }
#endif
                 }
                 /* Disable GTK_OFFLOAD*/
#ifdef WLAN_FEATURE_GTK_OFFLOAD
                 hdd_conf_gtk_offload(pAdapter, fenable);
#endif
            }
        }
    }

    EXIT();
    return;
}

/**
 * __hdd_ipv4_notifier_work_queue() - IP V4 change notifier work handler
 * @work: Pointer to work context
 *
 * Return: none
 */
static void __hdd_ipv4_notifier_work_queue(struct work_struct *work)
{
    hdd_adapter_t* pAdapter =
             container_of(work, hdd_adapter_t, ipv4NotifierWorkQueue);
    hdd_context_t *pHddCtx;
    int status;
    bool ndi_connected = false;

    hddLog(LOG1, FL("Reconfiguring ARP Offload"));
    pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
    status = wlan_hdd_validate_context(pHddCtx);
    if (0 != status)
        return;

    if (!pHddCtx->cfg_ini->active_mode_offload) {
        hddLog(LOG1, FL("Active mode offload is disabled"));
        return;
    }

    if (VOS_FALSE == pHddCtx->sus_res_mcastbcast_filter_valid) {
        pHddCtx->sus_res_mcastbcast_filter =
            pHddCtx->configuredMcastBcastFilter;
        pHddCtx->sus_res_mcastbcast_filter_valid = VOS_TRUE;
    }

    /* check if the device is in NAN data mode */
    if (WLAN_HDD_IS_NDI(pAdapter))
        ndi_connected = WLAN_HDD_IS_NDI_CONNECTED(pAdapter);

    if ((eConnectionState_Associated ==
            (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState ||
         ndi_connected)) {
        /*
         * This invocation being part of the IPv4 registration callback,
         * we are passing second parameter as 2 to avoid registration
         * of IPv4 notifier again.
         */
        hdd_conf_arp_offload(pAdapter, 2);
    }
}

/**
 * hdd_ipv4_notifier_work_queue() - IP V4 change notifier work handler
 * @work: Pointer to work context
 *
 * Return: none
 */
void hdd_ipv4_notifier_work_queue(struct work_struct *work)
{
	vos_ssr_protect(__func__);
	__hdd_ipv4_notifier_work_queue(work);
	vos_ssr_unprotect(__func__);
}

static int __wlan_hdd_ipv4_changed(struct notifier_block *nb,
				   unsigned long data, void *arg)
{
	struct in_ifaddr *ifa = (struct in_ifaddr *)arg;
	struct in_ifaddr **ifap = NULL;
	struct in_device *in_dev;
	struct net_device *ndev = ifa->ifa_dev->dev;
	hdd_context_t *hdd_ctx;
	hdd_adapter_t *adapter;
	int status;

	hdd_ctx = container_of(nb, hdd_context_t, ipv4_notifier);
	status = wlan_hdd_validate_context(hdd_ctx);
	if (0 != status)
		return NOTIFY_DONE;

	adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
	if (!adapter) return NOTIFY_DONE;
	if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) return NOTIFY_DONE;
	if (adapter->dev != ndev) return NOTIFY_DONE;
	if (WLAN_HDD_GET_CTX(adapter) != hdd_ctx) return NOTIFY_DONE;
        if (!(adapter->device_mode == WLAN_HDD_INFRA_STATION ||
	      adapter->device_mode == WLAN_HDD_P2P_CLIENT ||
	      adapter->device_mode == WLAN_HDD_NDI))
		return NOTIFY_DONE;

	if (eConnectionState_Associated ==
		WLAN_HDD_GET_STATION_CTX_PTR(
		adapter)->conn_info.connState)
			sme_dhcp_done_ind(hdd_ctx->hHal,
			adapter->sessionId);

	if ((hdd_ctx->cfg_ini->nEnableSuspend !=
				WLAN_MAP_SUSPEND_TO_MCAST_BCAST_FILTER) ||
			(!hdd_ctx->cfg_ini->fhostArpOffload)) {
		hddLog(LOG1, FL("Offload not enabled MCBC=%d, ARPOffload=%d"),
				hdd_ctx->cfg_ini->nEnableSuspend,
				hdd_ctx->cfg_ini->fhostArpOffload);

		return NOTIFY_DONE;
	}

	in_dev = __in_dev_get_rtnl(adapter->dev);
	if (in_dev != NULL) {
		for (ifap = &in_dev->ifa_list;
			(ifa = *ifap) != NULL;
			ifap = &ifa->ifa_next) {
			if (!strcmp(adapter->dev->name,
				ifa->ifa_label))
				break; /* found */
		}
	}

	if (ifa && ifa->ifa_local)
		schedule_work(&adapter->ipv4NotifierWorkQueue);

	return NOTIFY_DONE;
}

/**
 * wlan_hdd_ipv4_changed() - IPv4 change notifier callback
 * @nb: pointer to notifier block
 * @data: data
 * @arg: arg
 *
 * This is the IPv4 notifier callback function gets invoked
 * if any change in IP and then invoke the function @__wlan_hdd_ipv4_changed
 * to reconfigure the offload parameters.
 *
 * Return: 0 on success, error number otherwise.
 */
int wlan_hdd_ipv4_changed(struct notifier_block *nb,
				unsigned long data, void *arg)
{
	int ret;

	vos_ssr_protect(__func__);
	ret = __wlan_hdd_ipv4_changed(nb, data, arg);
	vos_ssr_unprotect(__func__);

	return ret;
}

/**----------------------------------------------------------------------------

  \brief hdd_conf_arp_offload() - Configure ARP offload

  Called during SUSPEND to configure the ARP offload (MC BC filter) which
  reduces power consumption.

  \param  - pAdapter -Adapter context for which ARP offload is to be configured
  \param  - fenable - 0 - disable.
                      1 - enable. (with IPv4 notifier registration)
                      2 - enable. (without IPv4 notifier registration)

  \return -
            VOS_STATUS_SUCCESS - on successful operation
            VOS_STATUS_E_FAILURE - on failure of operation
-----------------------------------------------------------------------------*/
VOS_STATUS hdd_conf_arp_offload(hdd_adapter_t *pAdapter, int fenable)
{
   struct in_ifaddr **ifap = NULL;
   struct in_ifaddr *ifa = NULL;
   struct in_device *in_dev;
   int i = 0;
   tSirHostOffloadReq  offLoadRequest;
   hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);

   hddLog(LOG1, FL("fenable = %d"), fenable);


   /* In SAP/P2PGo mode, ARP/NS offload feature capability
    * is controlled by one bit.
    */
   if ((WLAN_HDD_SOFTAP == pAdapter->device_mode ||
       WLAN_HDD_P2P_GO == pAdapter->device_mode) &&
       !pHddCtx->ap_arpns_support) {
       hddLog(LOG1, FL("APR Offload is not supported in SAP/P2PGO mode"));
       return VOS_STATUS_SUCCESS;
   }

   if(fenable)
   {
       if ((in_dev = __in_dev_get_rtnl(pAdapter->dev)) != NULL)
       {
           for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
                   ifap = &ifa->ifa_next)
           {
               if (!strcmp(pAdapter->dev->name, ifa->ifa_label))
               {
                   break; /* found */
               }
           }
       }
       if(ifa && ifa->ifa_local)
       {
           offLoadRequest.offloadType =  SIR_IPV4_ARP_REPLY_OFFLOAD;
           offLoadRequest.enableOrDisable = SIR_OFFLOAD_ENABLE;
           hdd_wlan_offload_event(SIR_IPV4_ARP_REPLY_OFFLOAD,
                                           SIR_OFFLOAD_ENABLE);

           hddLog(VOS_TRACE_LEVEL_INFO, "%s: Enabled", __func__);

           if (((HDD_MCASTBCASTFILTER_FILTER_ALL_BROADCAST ==
                pHddCtx->sus_res_mcastbcast_filter) ||
               (HDD_MCASTBCASTFILTER_FILTER_ALL_MULTICAST_BROADCAST ==
                pHddCtx->sus_res_mcastbcast_filter)) &&
               (VOS_TRUE == pHddCtx->sus_res_mcastbcast_filter_valid))
           {
               offLoadRequest.enableOrDisable =
                   SIR_OFFLOAD_ARP_AND_BCAST_FILTER_ENABLE;
               hddLog(VOS_TRACE_LEVEL_INFO,
                      "offload: inside arp offload conditional check");
           }
           hdd_wlan_offload_event(SIR_OFFLOAD_ARP_AND_BCAST_FILTER_ENABLE,
                                           SIR_OFFLOAD_ENABLE);
           hddLog(VOS_TRACE_LEVEL_INFO, "offload: arp filter programmed = %d",
                  offLoadRequest.enableOrDisable);

           //converting u32 to IPV4 address
           for(i = 0 ; i < 4; i++)
           {
              offLoadRequest.params.hostIpv4Addr[i] =
                      (ifa->ifa_local >> (i*8) ) & 0xFF ;
           }
           hddLog(VOS_TRACE_LEVEL_INFO, " Enable SME HostOffload: %d.%d.%d.%d",
                  offLoadRequest.params.hostIpv4Addr[0],
                  offLoadRequest.params.hostIpv4Addr[1],
                  offLoadRequest.params.hostIpv4Addr[2],
                  offLoadRequest.params.hostIpv4Addr[3]);

          if (eHAL_STATUS_SUCCESS !=
                    sme_SetHostOffload(WLAN_HDD_GET_HAL_CTX(pAdapter),
                    pAdapter->sessionId, &offLoadRequest))
          {
              hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed to enable HostOffload "
                      "feature", __func__);
              return VOS_STATUS_E_FAILURE;
          }
       }
       else
       {
           hddLog(VOS_TRACE_LEVEL_INFO, FL("IP Address is not assigned\n"));
       }

       return VOS_STATUS_SUCCESS;
   }
   else
   {
       vos_mem_zero((void *)&offLoadRequest, sizeof(tSirHostOffloadReq));
       offLoadRequest.enableOrDisable = SIR_OFFLOAD_DISABLE;
       offLoadRequest.offloadType =  SIR_IPV4_ARP_REPLY_OFFLOAD;
       hdd_wlan_offload_event(SIR_IPV4_ARP_REPLY_OFFLOAD,
                                           SIR_OFFLOAD_DISABLE);

       if (eHAL_STATUS_SUCCESS !=
                 sme_SetHostOffload(WLAN_HDD_GET_HAL_CTX(pAdapter),
                 pAdapter->sessionId, &offLoadRequest))
       {
            hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failure to disable host "
                             "offload feature", __func__);
            return VOS_STATUS_E_FAILURE;
       }
       return VOS_STATUS_SUCCESS;
   }
}

/*
 * This function is called before setting mcbc filters
 * to modify filter value considering Different Offloads
 */

void hdd_mcbc_filter_modification(hdd_context_t* pHddCtx,
                                  tANI_U8 *pMcBcFilter)
{
    if (NULL == pHddCtx)
    {
        hddLog(VOS_TRACE_LEVEL_ERROR, FL("NULL HDD context passed"));
        return;
    }

    *pMcBcFilter = pHddCtx->configuredMcastBcastFilter;
    if (pHddCtx->cfg_ini->fhostArpOffload)
    {
        /* ARP offload is enabled, do not block bcast packets at RXP
         * Will be using Bitmasking to reset the filter. As we have
         * disable Broadcast filtering, Anding with the negation
         * of Broadcast BIT
         */
        hddLog(VOS_TRACE_LEVEL_INFO, FL("ARP offload is enabled"));
        *pMcBcFilter &= ~(HDD_MCASTBCASTFILTER_FILTER_ALL_BROADCAST);
    }

#ifdef WLAN_NS_OFFLOAD
    if (pHddCtx->cfg_ini->fhostNSOffload)
    {
        /* NS offload is enabled, do not block mcast packets at RXP
         * Will be using Bitmasking to reset the filter. As we have
         * disable Multicast filtering, Anding with the negation
         * of Multicast BIT
         */
        hddLog(VOS_TRACE_LEVEL_INFO, FL("NS offload is enabled"));
        *pMcBcFilter &= ~(HDD_MCASTBCASTFILTER_FILTER_ALL_MULTICAST);
    }
#endif

    pHddCtx->configuredMcastBcastFilter = *pMcBcFilter;
}

void hdd_conf_mcastbcast_filter(hdd_context_t* pHddCtx, v_BOOL_t setfilter)
{
    eHalStatus halStatus = eHAL_STATUS_FAILURE;
    tpSirWlanSetRxpFilters wlanRxpFilterParam =
                     vos_mem_malloc(sizeof(tSirWlanSetRxpFilters));
    if (NULL == wlanRxpFilterParam)
    {
        hddLog(VOS_TRACE_LEVEL_FATAL,
           "%s: vos_mem_alloc failed ", __func__);
        return;
    }
    hddLog(VOS_TRACE_LEVEL_INFO,
        "%s: Configuring Mcast/Bcast Filter Setting. setfilter %d", __func__, setfilter);
    if (TRUE == setfilter)
    {
            hdd_mcbc_filter_modification(pHddCtx,
                  &wlanRxpFilterParam->configuredMcstBcstFilterSetting);
    }
    else
    {
        /*Use the current configured value to clear*/
        wlanRxpFilterParam->configuredMcstBcstFilterSetting =
                              pHddCtx->configuredMcastBcastFilter;
    }

    wlanRxpFilterParam->setMcstBcstFilter = setfilter;
    halStatus = sme_ConfigureRxpFilter(pHddCtx->hHal, wlanRxpFilterParam);

    if (setfilter && (eHAL_STATUS_SUCCESS == halStatus))
       pHddCtx->hdd_mcastbcast_filter_set = TRUE;

    hddLog(VOS_TRACE_LEVEL_INFO, "%s to post set/reset filter to"
           "lower mac with status %d"
           "configuredMcstBcstFilterSetting = %d"
           "setMcstBcstFilter = %d",(eHAL_STATUS_SUCCESS != halStatus) ?
           "Failed" : "Success", halStatus,
           wlanRxpFilterParam->configuredMcstBcstFilterSetting,
           wlanRxpFilterParam->setMcstBcstFilter);

    if (eHAL_STATUS_SUCCESS != halStatus)
        vos_mem_free(wlanRxpFilterParam);
}

static void hdd_conf_suspend_ind(hdd_context_t* pHddCtx,
                                 hdd_adapter_t *pAdapter,
                                 void (*callback)(void *callbackContext,
                                                  boolean suspended),
                                 void *callbackContext)
{
    eHalStatus halStatus = eHAL_STATUS_FAILURE;
    tpSirWlanSuspendParam wlanSuspendParam =
      vos_mem_malloc(sizeof(tSirWlanSuspendParam));

    if (VOS_FALSE == pHddCtx->sus_res_mcastbcast_filter_valid) {
        pHddCtx->sus_res_mcastbcast_filter =
            pHddCtx->configuredMcastBcastFilter;
        pHddCtx->sus_res_mcastbcast_filter_valid = VOS_TRUE;
        hddLog(VOS_TRACE_LEVEL_INFO, "offload: hdd_conf_suspend_ind");
        hddLog(VOS_TRACE_LEVEL_INFO, "configuredMCastBcastFilter saved = %d",
               pHddCtx->configuredMcastBcastFilter);

    }


    if(NULL == wlanSuspendParam)
    {
        hddLog(VOS_TRACE_LEVEL_FATAL,
           "%s: vos_mem_alloc failed ", __func__);
        return;
    }

    hddLog(VOS_TRACE_LEVEL_INFO,
      "%s: send wlan suspend indication", __func__);

    if((pHddCtx->cfg_ini->nEnableSuspend == WLAN_MAP_SUSPEND_TO_MCAST_BCAST_FILTER))
    {
        //Configure supported OffLoads
        hdd_conf_hostoffload(pAdapter, TRUE);
        wlanSuspendParam->configuredMcstBcstFilterSetting = pHddCtx->configuredMcastBcastFilter;
    }

    if ((eConnectionState_Associated ==
            (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState) ||
        (eConnectionState_IbssConnected ==
            (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState))
                wlanSuspendParam->connectedState = TRUE;
    else
                wlanSuspendParam->connectedState = FALSE;

    wlanSuspendParam->sessionId = pAdapter->sessionId;
    halStatus = sme_ConfigureSuspendInd(pHddCtx->hHal, wlanSuspendParam,
                                        callback, callbackContext);
    if(eHAL_STATUS_SUCCESS == halStatus)
    {
        pHddCtx->hdd_mcastbcast_filter_set = TRUE;
    } else {
        hddLog(VOS_TRACE_LEVEL_ERROR,
               FL("sme_ConfigureSuspendInd returned failure %d"), halStatus);

        vos_mem_free(wlanSuspendParam);
    }
}

static void hdd_conf_resume_ind(hdd_adapter_t *pAdapter)
{
    hdd_context_t* pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
    eHalStatus halStatus = eHAL_STATUS_FAILURE;


    halStatus = sme_ConfigureResumeReq(pHddCtx->hHal,
                                       NULL
                                      );

    if (eHAL_STATUS_SUCCESS != halStatus)
    {
        hddLog(VOS_TRACE_LEVEL_ERROR,
               "%s: sme_ConfigureResumeReq return failure %d",
               __func__, halStatus);

    }

    hddLog(VOS_TRACE_LEVEL_INFO,
      "%s: send wlan resume indication", __func__);
    /* Disable supported OffLoads */
    hdd_conf_hostoffload(pAdapter, FALSE);
    pHddCtx->hdd_mcastbcast_filter_set = FALSE;

    if (VOS_TRUE == pHddCtx->sus_res_mcastbcast_filter_valid) {
        pHddCtx->configuredMcastBcastFilter =
            pHddCtx->sus_res_mcastbcast_filter;
        pHddCtx->sus_res_mcastbcast_filter_valid = VOS_FALSE;
    }

    hddLog(VOS_TRACE_LEVEL_INFO,
           "offload: in hdd_conf_resume_ind, restoring configuredMcastBcastFilter");
    hddLog(VOS_TRACE_LEVEL_INFO, "configuredMcastBcastFilter = %d",
                  pHddCtx->configuredMcastBcastFilter);
}

#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
static void hdd_thermal_off_carrier(hdd_adapter_t *pAdapter)
{
	if (netif_carrier_ok(pAdapter->dev)) {
		pAdapter->netif_carrier_on = TRUE;
		wlan_hdd_netif_queue_control(pAdapter,
		  WLAN_NETIF_CARRIER_OFF, WLAN_CONTROL_PATH);
	} else {
		pAdapter->netif_carrier_on = FALSE;
	}
}

static void hdd_thermal_on_carrier(hdd_adapter_t *pAdapter)
{
	if (pAdapter->netif_carrier_on) {
	/* Thermal shutdown is an urgent accident visible to user space. */
		wlan_hdd_netif_queue_control(pAdapter,
			WLAN_NETIF_CARRIER_ON, WLAN_CONTROL_PATH);
	}
}
#else
static inline void hdd_thermal_off_carrier(hdd_adapter_t *pAdapter)
{
	return;
}

static inline void hdd_thermal_on_carrier(hdd_adapter_t *pAdapter)
{
	return;
}
#endif
//Suspend routine registered with Android OS
void hdd_suspend_wlan(void (*callback)(void *callbackContext, boolean suspended),
                      void *callbackContext, bool thermal)
{
   hdd_context_t *pHddCtx = NULL;
   v_CONTEXT_t pVosContext = NULL;

   VOS_STATUS status;
   hdd_adapter_t *pAdapter = NULL;
   hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
   bool hdd_enter_bmps = FALSE;
   enum netif_action_type action;

   hddLog(VOS_TRACE_LEVEL_INFO, "%s: WLAN being suspended by Android OS",__func__);

   //Get the global VOSS context.
   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
   if(!pVosContext) {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
      return;
   }

   //Get the HDD context.
   pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );

   if(!pHddCtx) {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD context is Null",__func__);
      return;
   }

   if (pHddCtx->isLogpInProgress) {
      hddLog(VOS_TRACE_LEVEL_ERROR,
             "%s: Ignore suspend wlan, LOGP in progress!", __func__);
      return;
   }

   hdd_set_pwrparams(pHddCtx);
   status =  hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
   while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
   {
       pAdapter = pAdapterNode->pAdapter;
       if ( (WLAN_HDD_INFRA_STATION != pAdapter->device_mode)
         && (WLAN_HDD_SOFTAP != pAdapter->device_mode)
         && (WLAN_HDD_P2P_CLIENT != pAdapter->device_mode) )

       {
           goto send_suspend_ind;
       }
       /* Avoid multiple enter/exit BMPS in this while loop using
        * hdd_enter_bmps flag
        */
       if (FALSE == hdd_enter_bmps && (BMPS == pmcGetPmcState(pHddCtx->hHal)))
       {
            hdd_enter_bmps = TRUE;

           /* If device was already in BMPS, and dynamic DTIM is set,
            * exit(set the device to full power) and enter BMPS again
            * to reflect new DTIM value */
           wlan_hdd_enter_bmps(pAdapter, DRIVER_POWER_MODE_ACTIVE);

           wlan_hdd_enter_bmps(pAdapter, DRIVER_POWER_MODE_AUTO);

           pHddCtx->hdd_ignore_dtim_enabled = TRUE;
       }
#ifdef SUPPORT_EARLY_SUSPEND_STANDBY_DEEPSLEEP
       if (pHddCtx->cfg_ini->nEnableSuspend == WLAN_MAP_SUSPEND_TO_STANDBY)
       {
          //stop the interface before putting the chip to standby
          hddLog(LOG1, FL("Disabling queues"));
          wlan_hdd_netif_queue_control(pAdapter,
            WLAN_NETIF_TX_DISABLE_N_CARRIER,
            WLAN_CONTROL_PATH);
       }
       else if (pHddCtx->cfg_ini->nEnableSuspend ==
               WLAN_MAP_SUSPEND_TO_DEEP_SLEEP)
       {
          //Execute deep sleep procedure
          hdd_enter_deep_sleep(pHddCtx, pAdapter);
       }
#endif

send_suspend_ind:
       //stop all TX queues before suspend
       hddLog(LOG1, FL("Disabling queues"));

      /* Thermal shutdown is an urgent accident visible to user space. */
        if (thermal) {
            hdd_thermal_off_carrier(pAdapter);
        }

       action = pHddCtx->is_nonos_suspend ? WLAN_NETIF_TX_DISABLE_N_CARRIER :
                                            WLAN_NETIF_TX_DISABLE;
       wlan_hdd_netif_queue_control(pAdapter, action, WLAN_CONTROL_PATH);

       WLANTL_PauseUnPauseQs(pVosContext, true);

      /* Keep this suspend indication at the end (before processing next adaptor)
       * for discrete. This indication is considered as trigger point to start
       * WOW (if wow is enabled). */
       /*Suspend notification sent down to driver*/
       hdd_conf_suspend_ind(pHddCtx, pAdapter, callback, callbackContext);

       status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
       pAdapterNode = pNext;
   }

   pHddCtx->hdd_wlan_suspended = TRUE;
   hdd_wlan_suspend_resume_event(HDD_WLAN_EARLY_SUSPEND);
#ifdef SUPPORT_EARLY_SUSPEND_STANDBY_DEEPSLEEP
  if(pHddCtx->cfg_ini->nEnableSuspend == WLAN_MAP_SUSPEND_TO_STANDBY)
  {
      hdd_enter_standby(pHddCtx);
  }
#endif

   return;
}

static void hdd_PowerStateChangedCB
(
   v_PVOID_t callbackContext,
   tPmcState newState
)
{
   hdd_context_t *pHddCtx = callbackContext;
   /* if the driver was not in BMPS during early suspend,
    * the dynamic DTIM is now updated at Riva */
   if ((newState == BMPS) && pHddCtx->hdd_wlan_suspended
           && pHddCtx->cfg_ini->enableDynamicDTIM
           && (pHddCtx->hdd_ignore_dtim_enabled == FALSE))
   {
       pHddCtx->hdd_ignore_dtim_enabled = TRUE;
   }
   adf_os_spin_lock(&pHddCtx->filter_lock);
   if ((newState == BMPS) &&  pHddCtx->hdd_wlan_suspended)
   {
      adf_os_spin_unlock(&pHddCtx->filter_lock);
      if (VOS_FALSE == pHddCtx->sus_res_mcastbcast_filter_valid)
      {
          pHddCtx->sus_res_mcastbcast_filter =
              pHddCtx->configuredMcastBcastFilter;
          pHddCtx->sus_res_mcastbcast_filter_valid = VOS_TRUE;

          hddLog(VOS_TRACE_LEVEL_INFO, "offload: callback to associated");
          hddLog(VOS_TRACE_LEVEL_INFO, "saving configuredMcastBcastFilter = %d",
                 pHddCtx->configuredMcastBcastFilter);
          hddLog(VOS_TRACE_LEVEL_INFO,
                 "offload: calling hdd_conf_mcastbcast_filter");

      }

      hdd_conf_mcastbcast_filter(pHddCtx, TRUE);
      if(pHddCtx->hdd_mcastbcast_filter_set != TRUE)
         hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Not able to set mcast/bcast filter ", __func__);
   }
   else
   {
      /* Android framework can send resume request when the WCN chip is
       * in IMPS mode. When the chip exits IMPS mode the firmware will
       * restore all the registers to the state they were before the chip
       * entered IMPS and so our hardware filter settings configured by the
       * resume request will be lost. So reconfigure the filters on detecting
       * a change in the power state of the WCN chip.
       */
      adf_os_spin_unlock(&pHddCtx->filter_lock);
      if (IMPS != newState)
      {
           adf_os_spin_lock(&pHddCtx->filter_lock);
           if (FALSE == pHddCtx->hdd_wlan_suspended)
           {
                adf_os_spin_unlock(&pHddCtx->filter_lock);
                hddLog(VOS_TRACE_LEVEL_INFO,
                          "Not in IMPS/BMPS and suspended state");
                hdd_conf_mcastbcast_filter(pHddCtx, FALSE);
           }
           else
           {
                adf_os_spin_unlock(&pHddCtx->filter_lock);
           }
      }
   }
}



void hdd_register_mcast_bcast_filter(hdd_context_t *pHddCtx)
{
   v_CONTEXT_t pVosContext;
   tHalHandle smeContext;

   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
   if (NULL == pVosContext)
   {
      hddLog(LOGE, "%s: Invalid pContext", __func__);
      return;
   }
   smeContext = vos_get_context(VOS_MODULE_ID_SME, pVosContext);
   if (NULL == smeContext)
   {
      hddLog(LOGE, "%s: Invalid smeContext", __func__);
      return;
   }

   adf_os_spinlock_init(&pHddCtx->filter_lock);
   if (WLAN_MAP_SUSPEND_TO_MCAST_BCAST_FILTER ==
                                            pHddCtx->cfg_ini->nEnableSuspend)
   {
      if(!pHddCtx->cfg_ini->enablePowersaveOffload)
      {
         pmcRegisterDeviceStateUpdateInd(smeContext,
                   hdd_PowerStateChangedCB, pHddCtx);
      }
      /* TODO: For Power Save Offload case */
   }
}

void hdd_unregister_mcast_bcast_filter(hdd_context_t *pHddCtx)
{
   v_CONTEXT_t pVosContext;
   tHalHandle smeContext;

   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
   if (NULL == pVosContext)
   {
      hddLog(LOGE, "%s: Invalid pContext", __func__);
      return;
   }
   smeContext = vos_get_context(VOS_MODULE_ID_SME, pVosContext);
   if (NULL == smeContext)
   {
      hddLog(LOGE, "%s: Invalid smeContext", __func__);
      return;
   }

   if (WLAN_MAP_SUSPEND_TO_MCAST_BCAST_FILTER ==
                                            pHddCtx->cfg_ini->nEnableSuspend)
   {
      if(!pHddCtx->cfg_ini->enablePowersaveOffload)
      {
         pmcDeregisterDeviceStateUpdateInd(smeContext,
                             hdd_PowerStateChangedCB);
      }
      /* TODO: For Power Save Offload case */
   }
}

#ifdef WLAN_FEATURE_USB_RECOVERY
extern bool hif_usb_check(void);
#endif

void hdd_resume_wlan(bool thermal)
{
   hdd_context_t *pHddCtx = NULL;
   hdd_adapter_t *pAdapter = NULL;
   hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
   VOS_STATUS status;
   v_CONTEXT_t pVosContext = NULL;
   enum netif_action_type action;

   hddLog(VOS_TRACE_LEVEL_INFO, "%s: WLAN being resumed by Android OS",__func__);

   //Get the global VOSS context.
   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
   if(!pVosContext) {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
      return;
   }

   //Get the HDD context.
   pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );

   if(!pHddCtx) {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD context is Null",__func__);
      return;
   }

   if (pHddCtx->isLogpInProgress)
   {
      hddLog(VOS_TRACE_LEVEL_INFO,
             "%s: Ignore resume wlan, LOGP in progress!", __func__);
      return;
   }

   pHddCtx->hdd_wlan_suspended = FALSE;
   hdd_wlan_suspend_resume_event(HDD_WLAN_EARLY_RESUME);
   /*loop through all adapters. Concurrency */
   status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );

   while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
   {
       pAdapter = pAdapterNode->pAdapter;
       if ( (WLAN_HDD_INFRA_STATION != pAdapter->device_mode)
         && (WLAN_HDD_SOFTAP != pAdapter->device_mode)
         && (WLAN_HDD_P2P_CLIENT != pAdapter->device_mode) )
       {
            goto send_resume_ind;
       }


#ifdef SUPPORT_EARLY_SUSPEND_STANDBY_DEEPSLEEP
       if(pHddCtx->hdd_ps_state == eHDD_SUSPEND_DEEP_SLEEP)
       {
          hddLog(VOS_TRACE_LEVEL_INFO, "%s: WLAN being resumed from deep sleep",__func__);
          hdd_exit_deep_sleep(pAdapter);
       }
#endif

      if(pHddCtx->hdd_ignore_dtim_enabled == TRUE)
      {
         /*Switch back to DTIM 1*/
         tSirSetPowerParamsReq powerRequest = { 0 };

         powerRequest.uIgnoreDTIM = pHddCtx->hdd_actual_ignore_DTIM_value;
         powerRequest.uListenInterval = pHddCtx->hdd_actual_LI_value;
         powerRequest.uMaxLIModulatedDTIM = pHddCtx->cfg_ini->fMaxLIModulatedDTIM;

         /*Disabled ModulatedDTIM if enabled on suspend*/
         if(pHddCtx->cfg_ini->enableModulatedDTIM)
             powerRequest.uDTIMPeriod = 0;

         /* Update ignoreDTIM and ListedInterval in CFG with default values */
         ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_IGNORE_DTIM, powerRequest.uIgnoreDTIM,
                          NULL, eANI_BOOLEAN_FALSE);
         ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, powerRequest.uListenInterval,
                          NULL, eANI_BOOLEAN_FALSE);

         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                        "Switch to DTIM%d",powerRequest.uListenInterval);
         sme_SetPowerParams( WLAN_HDD_GET_HAL_CTX(pAdapter), &powerRequest, FALSE);

         if (BMPS == pmcGetPmcState(pHddCtx->hHal))
         {
             /* put the device into full power */
             wlan_hdd_enter_bmps(pAdapter, DRIVER_POWER_MODE_ACTIVE);

             /* put the device back into BMPS */
             wlan_hdd_enter_bmps(pAdapter, DRIVER_POWER_MODE_AUTO);

             pHddCtx->hdd_ignore_dtim_enabled = FALSE;
         }
      }

send_resume_ind:
      //wake the tx queues
      hddLog(LOG1, FL("Enabling queues"));

      WLANTL_PauseUnPauseQs(pVosContext, false);

      action = pHddCtx->is_nonos_suspend ? WLAN_START_ALL_NETIF_QUEUE_N_CARRIER :
                                           WLAN_WAKE_ALL_NETIF_QUEUE;
      wlan_hdd_netif_queue_control(pAdapter, action, WLAN_CONTROL_PATH);

      if (thermal) {
        hdd_thermal_on_carrier(pAdapter);
      }

      hdd_conf_resume_ind(pAdapter);

      status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
      pAdapterNode = pNext;
   }

#ifdef IPA_OFFLOAD
   hdd_ipa_resume(pHddCtx);
#endif

#ifdef WLAN_FEATURE_USB_RECOVERY
   schedule_delayed_work(&pHddCtx->usb_detect_work, msecs_to_jiffies(10000));
#endif

#ifdef SUPPORT_EARLY_SUSPEND_STANDBY_DEEPSLEEP
   if(pHddCtx->hdd_ps_state == eHDD_SUSPEND_STANDBY)
   {
       hdd_exit_standby(pHddCtx);
   }
#endif

   return;
}

VOS_STATUS hdd_wlan_reset_initialization(void)
{
   v_CONTEXT_t pVosContext = NULL;

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: WLAN being reset",__func__);

   //Get the global VOSS context.
   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
   if(!pVosContext)
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
      return VOS_STATUS_E_FAILURE;
   }

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Preventing the phone from going to suspend",__func__);

   // Prevent the phone from going to sleep
   hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_REINIT);

   return VOS_STATUS_SUCCESS;
}

static void hdd_ssr_timer_del(void)
{
    adf_os_timer_cancel(&ssr_timer);
    ssr_timer_started = false;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
static void hdd_ssr_timer_cb(struct timer_list *t)
#else
static void hdd_ssr_timer_cb(void *data)
#endif
{
    hddLog(VOS_TRACE_LEVEL_FATAL, "%s: HDD SSR timer expired!", __func__);
    VOS_BUG(0);
}

static void hdd_ssr_timer_init(void)
{
    adf_os_timer_init(NULL, &ssr_timer,
                      hdd_ssr_timer_cb, NULL, ADF_NON_DEFERRABLE_TIMER);
}

static void hdd_ssr_timer_start(int msec)
{
    if(ssr_timer_started)
    {
        hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Trying to start SSR timer when "
               "it's running!", __func__);
    }
    adf_os_timer_start(&ssr_timer, msec);
    ssr_timer_started = true;
}

/**
 * hdd_svc_fw_shutdown_ind() - API to send FW SHUTDOWN IND to Userspace
 *
 * @dev: Device Pointer
 *
 * Return: None
 */
void hdd_svc_fw_shutdown_ind(struct device *dev)
{
	v_CONTEXT_t g_context;
	hdd_context_t *hdd_ctx;

	g_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

	if(!g_context)
		return;

	hdd_ctx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD,
						   g_context);

	hdd_ctx ? wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
					      WLAN_SVC_FW_SHUTDOWN_IND,
					      NULL, 0) : 0;
}

#ifdef FEATURE_WLAN_DIAG_SUPPORT
/**
* hdd_wlan_ssr_shutdown_event() - Send ssr shutdown status
*
* This function sends ssr shutdown status diag event
*
* Return: - Void.
*/
static void hdd_wlan_ssr_shutdown_event(void)
{
	WLAN_VOS_DIAG_EVENT_DEF(ssr_shutdown,
					struct host_event_wlan_ssr_shutdown);
	vos_mem_zero(&ssr_shutdown, sizeof(ssr_shutdown));
	ssr_shutdown.status = SSR_SUB_SYSTEM_SHUTDOWN;
	WLAN_VOS_DIAG_EVENT_REPORT(&ssr_shutdown,
					EVENT_WLAN_SSR_SHUTDOWN_SUBSYSTEM);
}
#else
static inline void hdd_wlan_ssr_shutdown_event(void)
{

};
#endif

/* the HDD interface to WLAN driver shutdown,
 * the primary shutdown function in SSR
 */
VOS_STATUS hdd_wlan_shutdown(void)
{
   VOS_STATUS       vosStatus;
   v_CONTEXT_t      pVosContext = NULL;
   hdd_context_t    *pHddCtx = NULL;
   pVosSchedContext vosSchedContext = NULL;

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: WLAN driver shutting down! ",__func__);

   /* If SSR never completes, then do kernel panic. */
   hdd_ssr_timer_init();
   hdd_ssr_timer_start(HDD_SSR_BRING_UP_TIME);

   /* Get the global VOSS context. */
   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
   if(!pVosContext) {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
      return VOS_STATUS_E_FAILURE;
   }
   /* Get the HDD context. */
   pHddCtx = (hdd_context_t*)vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
   if(!pHddCtx) {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD context is Null",__func__);
      return VOS_STATUS_E_FAILURE;
   }

   pHddCtx->isLogpInProgress = TRUE;
   pHddCtx->isWiphySuspended = FALSE;
   pHddCtx->isSchedScanUpdatePending = FALSE;

   vos_set_logp_in_progress(VOS_MODULE_ID_VOSS, TRUE);

   vos_clear_concurrent_session_count();

   hddLog(VOS_TRACE_LEVEL_INFO,
           FL("Invoking packetdump deregistration API"));
   wlan_deregister_txrx_packetdump();

   if (VOS_TIMER_STATE_RUNNING ==
            vos_timer_getCurrentState(&pHddCtx->tdls_source_timer))
      vos_timer_stop(&pHddCtx->tdls_source_timer);

#ifdef FEATURE_BUS_BANDWIDTH
   if (VOS_TIMER_STATE_RUNNING ==
           vos_timer_getCurrentState(&pHddCtx->bus_bw_timer))
   {
      vos_timer_stop(&pHddCtx->bus_bw_timer);
      hdd_rst_tcp_delack(pHddCtx);

      if (pHddCtx->hbw_requested) {
          vos_remove_pm_qos();
          pHddCtx->hbw_requested = false;
      }
   }
#endif

#ifdef IPA_UC_OFFLOAD
   hdd_ipa_uc_ssr_deinit();
#endif

   hdd_reset_all_adapters(pHddCtx);
   vosStatus = hddDevTmUnregisterNotifyCallback(pHddCtx);
   if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmUnregisterNotifyCallback failed",__func__);
   }

   /* Disable IMPS/BMPS as we do not want the device to enter any power
    * save mode on its own during reset sequence
    */
   sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
   sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);
   sme_DisablePowerSave(pHddCtx->hHal, ePMC_UAPSD_MODE_POWER_SAVE);

   vosSchedContext = get_vos_sched_ctxt();

   /* Wakeup all driver threads */
   if(TRUE == pHddCtx->isMcThreadSuspended){
      complete(&vosSchedContext->ResumeMcEvent);
      pHddCtx->isMcThreadSuspended= FALSE;
   }
#ifdef QCA_CONFIG_SMP
   if (TRUE == pHddCtx->isTlshimRxThreadSuspended) {
      complete(&vosSchedContext->ResumeTlshimRxEvent);
      pHddCtx->isTlshimRxThreadSuspended = FALSE;
    }
#endif

   /* Reset the Suspend Variable */
   pHddCtx->isWlanSuspended = FALSE;

   tl_shim_flush_cache_rx_queue();

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Doing WDA STOP", __func__);
   vosStatus = WDA_stop(pVosContext, HAL_STOP_TYPE_RF_KILL);

   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                "%s: Failed to stop WDA", __func__);
      VOS_ASSERT(VOS_IS_STATUS_SUCCESS(vosStatus));
      WDA_setNeedShutdown(pVosContext);
   }

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Doing SME STOP",__func__);
   /* Stop SME - Cannot invoke vos_stop as vos_stop relies
    * on threads being running to process the SYS Stop
    */
   vosStatus = sme_Stop(pHddCtx->hHal, HAL_STOP_TYPE_SYS_RESET);
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
       VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "%s: Failed to stop sme %d", __func__, vosStatus);
       VOS_ASSERT(0);
   }

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Doing MAC STOP",__func__);
   /* Stop MAC (PE and HAL) */
   vosStatus = macStop(pHddCtx->hHal, HAL_STOP_TYPE_SYS_RESET);
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
       VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "%s: Failed to stop mac %d", __func__, vosStatus);
       VOS_ASSERT(0);
   }

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Doing TL STOP",__func__);
   /* Stop TL */
   vosStatus = WLANTL_Stop(pVosContext);
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
       VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "%s: Failed to stop TL %d", __func__, vosStatus);
       VOS_ASSERT(0);
   }

   hdd_unregister_mcast_bcast_filter(pHddCtx);

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Doing VOS SCHED Close", __func__);
   vos_sched_close(vosSchedContext);

   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Doing VOS Close", __func__);
   vos_close(pVosContext);

   /*mac context has already been released in mac_close call
     so setting it to NULL in hdd context*/
   pHddCtx->hHal = (tHalHandle)NULL;

   if (free_riva_power_on_lock("wlan"))
   {
      hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to free power on lock",
                                           __func__);
   }

#ifdef WLAN_FEATURE_LPSS
   wlan_hdd_send_status_pkg(NULL, NULL, 0, 0);
#endif

   hdd_wlan_ssr_shutdown_event();
   hddLog(VOS_TRACE_LEVEL_FATAL, "%s: WLAN driver shutdown complete"
                                   ,__func__);
   return VOS_STATUS_SUCCESS;
}

/**
 * hdd_ssr_restart_sap() - restart sap on SSR
 * @hdd_ctx:   hdd context
 *
 * Return:     nothing
 */
static void hdd_ssr_restart_sap(hdd_context_t *hdd_ctx)
{
	VOS_STATUS       status;
	hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
	hdd_adapter_t *adapter;

	ENTER();

	status =  hdd_get_front_adapter (hdd_ctx, &adapter_node);
	while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
		adapter = adapter_node->pAdapter;
		if (adapter && adapter->device_mode == WLAN_HDD_SOFTAP) {
			if (test_bit(SOFTAP_INIT_DONE, &adapter->event_flags)) {
				hddLog(VOS_TRACE_LEVEL_INFO, FL("Restart prev SAP session"));
				wlan_hdd_start_sap(adapter, true);
			}
		}
		status = hdd_get_next_adapter ( hdd_ctx, adapter_node, &next );
		adapter_node = next;
	}

	EXIT();
}

#ifdef FEATURE_WLAN_DIAG_SUPPORT
/**
 * hdd_wlan_ssr_reinit_event - Send ssr reinit status
 *
 * This function sends ssr reinit status diag event
 *
 * Return: void.
 */
static void hdd_wlan_ssr_reinit_event(void)
{
	WLAN_VOS_DIAG_EVENT_DEF(ssr_reinit, struct host_event_wlan_ssr_reinit);
	vos_mem_zero(&ssr_reinit, sizeof(ssr_reinit));
	ssr_reinit.status = SSR_SUB_SYSTEM_REINIT;
	WLAN_VOS_DIAG_EVENT_REPORT(&ssr_reinit,
					EVENT_WLAN_SSR_REINIT_SUBSYSTEM);
}
#else
static void hdd_wlan_ssr_reinit_event(void)
{

};
#endif

/* the HDD interface to WLAN driver re-init.
 * This is called to initialize/start WLAN driver after a shutdown.
 */
VOS_STATUS hdd_wlan_re_init(void *hif_sc)
{
   VOS_STATUS       vosStatus;
   v_CONTEXT_t      pVosContext = NULL;
   hdd_context_t    *pHddCtx = NULL;
   eHalStatus       halStatus;
   bool             bug_on_reinit_failure = 0;
   hdd_adapter_t *pAdapter;
   int i;
   hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_REINIT);

   vos_set_reinit_in_progress(VOS_MODULE_ID_VOSS, TRUE);

   /* Get the VOS context */
   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
   if(pVosContext == NULL)
   {
      hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Failed vos_get_global_context",
             __func__);
      goto err_re_init;
   }

   /* Get the HDD context */
   pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
   if(!pHddCtx)
   {
      hddLog(VOS_TRACE_LEVEL_FATAL, "%s: HDD context is Null", __func__);
      goto err_re_init;
   }
   bug_on_reinit_failure = pHddCtx->cfg_ini->bug_on_reinit_failure;

   if (!hif_sc) {
      hddLog(VOS_TRACE_LEVEL_FATAL, "%s: hif_sc is NULL", __func__);
      goto err_re_init;
   }

   ((VosContextType*)pVosContext)->pHIFContext = hif_sc;

   /* The driver should always be initialized in STA mode after SSR */
   if (VOS_STA_SAP_MODE != hdd_get_conparam())
       hdd_set_conparam(0);

   /* Re-open VOSS, it is a re-open b'se control transport was never closed. */
   vosStatus = vos_open(&pVosContext, 0);
   if (!VOS_IS_STATUS_SUCCESS(vosStatus))
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_open failed",__func__);
      goto err_re_init;
   }

   hif_init_pdev_txrx_handle(hif_sc,
         vos_get_context(VOS_MODULE_ID_TXRX, pVosContext));

   /* Save the hal context in Adapter */
   pHddCtx->hHal = (tHalHandle)vos_get_context( VOS_MODULE_ID_SME, pVosContext );
   if ( NULL == pHddCtx->hHal )
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HAL context is null",__func__);
      goto err_vosclose;
   }

   /* Set the SME configuration parameters. */
   vosStatus = hdd_set_sme_config(pHddCtx);
   if ( VOS_STATUS_SUCCESS != vosStatus )
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed hdd_set_sme_config",__func__);
      goto err_vosclose;
   }

   vosStatus = vos_preStart( pHddCtx->pvosContext );
   if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_preStart failed",__func__);
      goto err_vosclose;
   }

   hdd_set_dfs_regdomain(pHddCtx,true);

   vosStatus = hdd_set_sme_chan_list(pHddCtx);
   if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {
      hddLog(VOS_TRACE_LEVEL_FATAL,
             "%s: Failed to init channel list", __func__);
      goto err_vosclose;
   }

   /* In the integrated architecture we update the configuration from
      the INI file and from NV before vOSS has been started so that
      the final contents are available to send down to the cCPU   */
   /* Apply the cfg.ini to cfg.dat */
   if (FALSE == hdd_update_config_dat(pHddCtx))
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: config update failed",__func__ );
      goto err_vosclose;
   }

   /* Set the MAC Address, currently this is used by HAL to add self sta.
    * Remove this once self sta is added as part of session open. */
   halStatus = cfgSetStr(pHddCtx->hHal, WNI_CFG_STA_ID,
         (v_U8_t *)&pHddCtx->cfg_ini->intfMacAddr[0],
           sizeof(pHddCtx->cfg_ini->intfMacAddr[0]));
   if (!HAL_STATUS_SUCCESS(halStatus))
   {
      hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed to set MAC Address. "
            "HALStatus is %08d [x%08x]",__func__, halStatus, halStatus);
      goto err_vosclose;
   }

   /*
    * Invoke ipa reinit before vos_start so that doorbell registers are
    * updated
    */
#ifdef IPA_UC_OFFLOAD
   if (hdd_ipa_uc_ssr_reinit(pHddCtx))
      hddLog(LOGE, "%s: HDD IPA UC reinit failed", __func__);
#endif

   /* Start VOSS which starts up the SME/MAC/HAL modules and everything else
      Note: Firmware image will be read and downloaded inside vos_start API */
   vosStatus = vos_start( pVosContext );
   if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_start failed",__func__);
      goto err_vosclose;
   }

   vosStatus = hdd_post_voss_start_config( pHddCtx );
   if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_post_voss_start_config failed",
         __func__);
      goto err_vosstop;
   }

   /* Try to get an adapter from mode ID */
   pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
   if (!pAdapter) {
      pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_SOFTAP);
      if (!pAdapter) {
        pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_IBSS);
        if (!pAdapter) {
           hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Failed to get Adapter!",
                  __func__);
        }
     }
   }

   /* Get WLAN Host/FW/HW version */
   if (pAdapter)
      hdd_wlan_get_version(pAdapter, NULL, NULL);

   /* Pass FW version to HIF layer */
   hif_set_fw_info(hif_sc, pHddCtx->target_fw_version);

   wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index,
                               WLAN_SVC_FW_CRASHED_IND, NULL, 0);

   /* Restart all adapters */
   hdd_start_all_adapters(pHddCtx);

   /* Reconfigure FW logs after SSR */
   if (pAdapter) {
      if (pHddCtx->fw_log_settings.enable != 0) {
         process_wma_set_command(pAdapter->sessionId,
                                 WMI_DBGLOG_MODULE_ENABLE,
                                 pHddCtx->fw_log_settings.enable , DBG_CMD);
      } else {
         process_wma_set_command(pAdapter->sessionId,
                                 WMI_DBGLOG_MODULE_DISABLE,
                                 pHddCtx->fw_log_settings.enable, DBG_CMD);
      }

      if (pHddCtx->fw_log_settings.dl_report != 0) {
         process_wma_set_command(pAdapter->sessionId,
                                 WMI_DBGLOG_REPORT_ENABLE,
                                 pHddCtx->fw_log_settings.dl_report, DBG_CMD);

         process_wma_set_command(pAdapter->sessionId,
                                 WMI_DBGLOG_TYPE,
                                 pHddCtx->fw_log_settings.dl_type, DBG_CMD);

         process_wma_set_command(pAdapter->sessionId,
                                 WMI_DBGLOG_LOG_LEVEL,
                                 pHddCtx->fw_log_settings.dl_loglevel, DBG_CMD);

         for (i = 0; i < MAX_MOD_LOGLEVEL; i++) {
            if (pHddCtx->fw_log_settings.dl_mod_loglevel[i] != 0) {
               process_wma_set_command(pAdapter->sessionId,
                                 WMI_DBGLOG_MOD_LOG_LEVEL,
                                 pHddCtx->fw_log_settings.dl_mod_loglevel[i],
                                 DBG_CMD);
            }
         }
      }
   }

   /* Register TM level change handler function to the platform */
   hddDevTmRegisterNotifyCallback(pHddCtx);

   pHddCtx->last_scan_reject_session_id = 0xFF;
   pHddCtx->last_scan_reject_reason = 0;
   pHddCtx->last_scan_reject_timestamp = 0;
   pHddCtx->scan_reject_cnt = 0;

   pHddCtx->hdd_mcastbcast_filter_set = FALSE;
   pHddCtx->btCoexModeSet = false;
   hdd_register_mcast_bcast_filter(pHddCtx);

   wlan_hdd_process_tdcc_ps(NULL, PS_TDCC_RESET, NULL, NULL);

   /* Allow the phone to go to sleep */
   hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_REINIT);
   /* register for riva power on lock */
   if (req_riva_power_on_lock("wlan"))
   {
      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: req riva power on lock failed",
                                        __func__);
      goto err_unregister_pmops;
   }
   vos_set_reinit_in_progress(VOS_MODULE_ID_VOSS, FALSE);

   sme_register_mgmt_frame_ind_callback(pHddCtx->hHal, hdd_indicate_mgmt_frame);

   /* Register for p2p ack indication */
   sme_register_p2p_ack_ind_callback(pHddCtx->hHal, hdd_send_action_cnf_cb);

#ifdef FEATURE_WLAN_EXTSCAN
   sme_ExtScanRegisterCallback(pHddCtx->hHal,
                               wlan_hdd_cfg80211_extscan_callback);
#endif /* FEATURE_WLAN_EXTSCAN */
   sme_set_rssi_threshold_breached_cb(pHddCtx->hHal, hdd_rssi_threshold_breached);
   wlan_hdd_cfg80211_link_layer_stats_init(pHddCtx);
   sme_bpf_offload_register_callback(pHddCtx->hHal, hdd_get_bpf_offload_cb);

#ifdef WLAN_FEATURE_LPSS
   wlan_hdd_send_all_scan_intf_info(pHddCtx);
   wlan_hdd_send_version_pkg(pHddCtx->target_fw_version,
                             pHddCtx->target_hw_version,
                             pHddCtx->target_hw_name);
#endif
   /* set chip power save failure detected callback */
   sme_set_chip_pwr_save_fail_cb(pHddCtx->hHal,
                                 hdd_chip_pwr_save_fail_detected_cb);

   ol_pktlog_init(hif_sc);
   goto success;

err_unregister_pmops:
#ifdef CONFIG_HAS_EARLYSUSPEND
   hdd_unregister_mcast_bcast_filter(pHddCtx);
#endif

err_vosstop:
   vos_stop(pVosContext);

err_vosclose:
   vos_close(pVosContext);
   vos_sched_close(pVosContext);

#ifdef MEMORY_DEBUG
   adf_net_buf_debug_exit();
   adf_nbuf_map_check_for_leaks();
   vos_mem_exit();
#endif

err_re_init:
   if (bug_on_reinit_failure)
      VOS_BUG(0);
   else {
      pr_err("SSR fails during reinit hence doing cleanup");
      /* Stop SSR timer */
      hdd_ssr_timer_del();
      if (pHddCtx) {
         /* Unregister all Net Device Notifiers */
         wlan_hdd_netdev_notifiers_cleanup(pHddCtx);
         /* Clean up HDD Nlink Service */
         nl_srv_exit();
         hdd_runtime_suspend_deinit(pHddCtx);
         hdd_close_all_adapters(pHddCtx);
         /* Free up dynamically allocated members
          * inside HDD Adapter
          */
         vos_mem_free(pHddCtx->cfg_ini);
         pHddCtx->cfg_ini= NULL;
         /* Destroy all wakelocks */
         wlan_hdd_wakelocks_destroy(pHddCtx);
         wlan_hdd_deinit_tx_rx_histogram(pHddCtx);
         wiphy_unregister(pHddCtx->wiphy);
         wiphy_free(pHddCtx->wiphy);
      }
   }
   vos_set_reinit_in_progress(VOS_MODULE_ID_VOSS, FALSE);
   vos_preClose(&pVosContext);
   /* Allow the phone to go to sleep */
   hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_REINIT);
   hdd_wlan_wakelock_destroy();
   return -EPERM;
success:
   hdd_wlan_ssr_reinit_event();
   if (pHddCtx->cfg_ini->sap_internal_restart)
       hdd_ssr_restart_sap(pHddCtx);
   pHddCtx->isLogpInProgress = FALSE;
   hdd_ssr_timer_del();
   return VOS_STATUS_SUCCESS;
}
