Browse Source

qcacld-3.0: Move con_mode_handler

A previous change (Ibe982d1b52647b78dbcdbc2f9dcef609a6b2c02d) introduced
forward declarations in hdd_main.c. Move the relevant logic so as to
avoid needing these forward declarations.

Change-Id: I2bbd95a7eb51e8288eeb121923e9cecc7e238886
CRs-Fixed: 2408209
Dustin Brown 6 years ago
parent
commit
26afe8f73b
1 changed files with 503 additions and 506 deletions
  1. 503 506
      core/hdd/src/wlan_hdd_main.c

+ 503 - 506
core/hdd/src/wlan_hdd_main.c

@@ -13477,661 +13477,658 @@ static void hdd_qdf_deinit(void)
 	hdd_qdf_print_deinit();
 }
 
-static void hdd_driver_mode_change_register(void);
-static void hdd_driver_mode_change_unregister(void);
+#ifdef FEATURE_MONITOR_MODE_SUPPORT
+static bool is_monitor_mode_supported(void)
+{
+	return true;
+}
+#else
+static bool is_monitor_mode_supported(void)
+{
+	pr_err("Monitor mode not supported!");
+	return false;
+}
+#endif
+
+#ifdef WLAN_FEATURE_EPPING
+static bool is_epping_mode_supported(void)
+{
+	return true;
+}
+#else
+static bool is_epping_mode_supported(void)
+{
+	pr_err("Epping mode not supported!");
+	return false;
+}
+#endif
+
+#ifdef QCA_WIFI_FTM
+static bool is_ftm_mode_supported(void)
+{
+	return true;
+}
+#else
+static bool is_ftm_mode_supported(void)
+{
+	pr_err("FTM mode not supported!");
+	return false;
+}
+#endif
 
 /**
- * hdd_driver_load() - Perform the driver-level load operation
- *
- * Note: this is used in both static and DLKM driver builds
+ * is_con_mode_valid() check con mode is valid or not
+ * @mode: global con mode
  *
- * Return: Errno
+ * Return: TRUE on success FALSE on failure
  */
-static int hdd_driver_load(void)
+static bool is_con_mode_valid(enum QDF_GLOBAL_MODE mode)
 {
-	struct hdd_driver *hdd_driver = hdd_driver_get();
-	QDF_STATUS status;
-	int errno;
-
-	pr_err("%s: Loading driver v%s\n",
-	       WLAN_MODULE_NAME,
-	       g_wlan_driver_version);
-
-	status = hdd_qdf_init();
-	if (QDF_IS_STATUS_ERROR(status)) {
-		errno = qdf_status_to_os_return(status);
-		goto exit;
-	}
-
-	status = hdd_driver_ctx_init(hdd_driver);
-	if (QDF_IS_STATUS_ERROR(status)) {
-		hdd_err("Failed to init driver context; status:%u", status);
-		errno = qdf_status_to_os_return(status);
-		goto qdf_deinit;
-	}
-
-	status = dsc_driver_trans_start(hdd_driver->dsc_driver, "load");
-	QDF_BUG(QDF_IS_STATUS_SUCCESS(status));
-	if (QDF_IS_STATUS_ERROR(status)) {
-		errno = qdf_status_to_os_return(status);
-		goto hdd_driver_deinit;
-	}
-
-	errno = hdd_init();
-	if (errno) {
-		hdd_err("Failed to init HDD; errno:%d", errno);
-		goto trans_stop;
-	}
-
-	status = hdd_component_init();
-	if (QDF_IS_STATUS_ERROR(status)) {
-		hdd_err("Failed to init components; status:%u", status);
-		errno = qdf_status_to_os_return(status);
-		goto hdd_deinit;
+	switch (mode) {
+	case QDF_GLOBAL_MONITOR_MODE:
+		return is_monitor_mode_supported();
+	case QDF_GLOBAL_EPPING_MODE:
+		return is_epping_mode_supported();
+	case QDF_GLOBAL_FTM_MODE:
+		return is_ftm_mode_supported();
+	case QDF_GLOBAL_MISSION_MODE:
+		return true;
+	default:
+		return false;
 	}
+}
 
-	status = qdf_wake_lock_create(&wlan_wake_lock, "wlan");
-	if (QDF_IS_STATUS_ERROR(status)) {
-		hdd_err("Failed to create wake lock; status:%u", status);
-		errno = qdf_status_to_os_return(status);
-		goto comp_deinit;
-	}
+static void hdd_stop_present_mode(struct hdd_context *hdd_ctx,
+				  enum QDF_GLOBAL_MODE curr_mode)
+{
+	if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED)
+		return;
 
-	hdd_set_conparam(con_mode);
+	switch (curr_mode) {
+	case QDF_GLOBAL_MONITOR_MODE:
+		hdd_info("Release wakelock for monitor mode!");
+		qdf_wake_lock_release(&hdd_ctx->monitor_mode_wakelock,
+				      WIFI_POWER_EVENT_WAKELOCK_MONITOR_MODE);
+		/* fallthrough */
+	case QDF_GLOBAL_MISSION_MODE:
+	case QDF_GLOBAL_FTM_MODE:
+		hdd_abort_mac_scan_all_adapters(hdd_ctx);
+		wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, NULL);
+		hdd_stop_all_adapters(hdd_ctx);
+		hdd_deinit_all_adapters(hdd_ctx, false);
 
-	errno = wlan_hdd_state_ctrl_param_create();
-	if (errno) {
-		hdd_err("Failed to create ctrl param; errno:%d", errno);
-		goto wakelock_destroy;
+		break;
+	default:
+		break;
 	}
+}
 
-	errno = pld_init();
-	if (errno) {
-		hdd_err("Failed to init PLD; errno:%d", errno);
-		goto param_destroy;
-	}
+static void hdd_cleanup_present_mode(struct hdd_context *hdd_ctx,
+				    enum QDF_GLOBAL_MODE curr_mode)
+{
+	int driver_status;
 
-	hdd_driver->state = driver_state_loaded;
-	hdd_driver_mode_change_register();
-	dsc_driver_trans_stop(hdd_driver->dsc_driver);
+	driver_status = hdd_ctx->driver_status;
 
-	/* psoc probe can happen in registration; do after 'load' transition */
-	errno = wlan_hdd_register_driver();
-	if (errno) {
-		hdd_err("Failed to register driver; errno:%d", errno);
-		goto pld_deinit;
+	switch (curr_mode) {
+	case QDF_GLOBAL_MISSION_MODE:
+	case QDF_GLOBAL_MONITOR_MODE:
+	case QDF_GLOBAL_FTM_MODE:
+		hdd_close_all_adapters(hdd_ctx, false);
+		break;
+	case QDF_GLOBAL_EPPING_MODE:
+		epping_disable();
+		epping_close();
+		break;
+	default:
+		return;
 	}
+}
 
-	hdd_debug("%s: driver loaded", WLAN_MODULE_NAME);
+static int
+hdd_parse_driver_mode(const char *mode_str, enum QDF_GLOBAL_MODE *out_mode)
+{
+	QDF_STATUS status;
+	uint32_t mode;
 
-	return 0;
+	*out_mode = QDF_GLOBAL_MAX_MODE;
 
-pld_deinit:
-	status = dsc_driver_trans_start(hdd_driver->dsc_driver, "unload");
-	QDF_BUG(QDF_IS_STATUS_SUCCESS(status));
+	status = qdf_uint32_parse(mode_str, &mode);
+	if (QDF_IS_STATUS_ERROR(status))
+		return qdf_status_to_os_return(status);
 
-	hdd_driver_mode_change_unregister();
-	pld_deinit();
+	if (mode >= QDF_GLOBAL_MAX_MODE)
+		return -ERANGE;
 
-param_destroy:
-	wlan_hdd_state_ctrl_param_destroy();
-wakelock_destroy:
-	qdf_wake_lock_destroy(&wlan_wake_lock);
-comp_deinit:
-	hdd_component_deinit();
-hdd_deinit:
-	hdd_deinit();
-trans_stop:
-	hdd_driver->state = driver_state_deinit;
-	dsc_driver_trans_stop(hdd_driver->dsc_driver);
-hdd_driver_deinit:
-	hdd_driver_ctx_deinit(hdd_driver);
-qdf_deinit:
-	hdd_qdf_deinit();
+	*out_mode = (enum QDF_GLOBAL_MODE)mode;
 
-exit:
-	return errno;
+	return 0;
 }
 
 /**
- * hdd_driver_unload() - Performs the driver-level unload operation
+ * __hdd_driver_mode_change() - Handles a driver mode change
+ * @hdd_ctx: Pointer to the global HDD context
+ * @next_mode: the driver mode to transition to
  *
- * Note: this is used in both static and DLKM driver builds
+ * This function is invoked when user updates con_mode using sys entry,
+ * to initialize and bring-up driver in that specific mode.
  *
- * Return: None
+ * Return: Errno
  */
-static void hdd_driver_unload(void)
+static int __hdd_driver_mode_change(struct hdd_context *hdd_ctx,
+				    enum QDF_GLOBAL_MODE next_mode)
 {
-	struct hdd_driver *hdd_driver = hdd_driver_get();
-	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
-	QDF_STATUS status;
+	enum QDF_GLOBAL_MODE curr_mode;
+	int errno;
 
-	pr_info("%s: Unloading driver v%s\n", WLAN_MODULE_NAME,
-		QWLAN_VERSIONSTR);
+	hdd_info("Driver mode changing to %d", next_mode);
 
-	if (g_is_system_reboot_triggered) {
-		hdd_info("System rebooting; Skipping unload");
-		return;
-	}
+	errno = wlan_hdd_validate_context(hdd_ctx);
+	if (errno)
+		return errno;
 
-	if (hdd_ctx)
-		hdd_psoc_idle_timer_stop(hdd_ctx);
+	if (!is_con_mode_valid(next_mode)) {
+		hdd_err_rl("Requested driver mode is invalid");
+		return -EINVAL;
+	}
 
-	/* trigger SoC remove */
-	wlan_hdd_unregister_driver();
+	qdf_atomic_set(&hdd_ctx->con_mode_flag, 1);
+	mutex_lock(&hdd_init_deinit_lock);
 
-	status = dsc_driver_trans_start_wait(hdd_driver->dsc_driver, "unload");
-	QDF_BUG(QDF_IS_STATUS_SUCCESS(status));
-	if (QDF_IS_STATUS_ERROR(status)) {
-		hdd_err("Unable to unload wlan; status:%u", status);
-		return;
+	curr_mode = hdd_get_conparam();
+	if (curr_mode == next_mode) {
+		hdd_err_rl("Driver is already in the requested mode");
+		errno = 0;
+		goto unlock;
 	}
 
-	dsc_driver_wait_for_ops(hdd_driver->dsc_driver);
+	/* ensure adapters are stopped */
+	hdd_stop_present_mode(hdd_ctx, curr_mode);
 
-	cds_set_driver_loaded(false);
-	cds_set_unload_in_progress(true);
+	errno = hdd_wlan_stop_modules(hdd_ctx, true);
+	if (errno) {
+		hdd_err("Stop wlan modules failed");
+		goto unlock;
+	}
 
-	if (!cds_wait_for_external_threads_completion(__func__))
-		hdd_warn("External threads are still active attempting "
-			 "driver unload anyway");
+	/* Cleanup present mode before switching to new mode */
+	hdd_cleanup_present_mode(hdd_ctx, curr_mode);
 
-	hdd_driver_mode_change_unregister();
-	pld_deinit();
-	wlan_hdd_state_ctrl_param_destroy();
-	hdd_set_conparam(0);
-	qdf_wake_lock_destroy(&wlan_wake_lock);
-	hdd_component_deinit();
-	hdd_deinit();
+	hdd_set_conparam(next_mode);
 
-	hdd_driver->state = driver_state_deinit;
-	dsc_driver_trans_stop(hdd_driver->dsc_driver);
+	errno = hdd_wlan_start_modules(hdd_ctx, false);
+	if (errno) {
+		hdd_err("Start wlan modules failed: %d", errno);
+		goto unlock;
+	}
 
-	hdd_driver_ctx_deinit(hdd_driver);
+	errno = hdd_open_adapters_for_mode(hdd_ctx, next_mode);
+	if (errno) {
+		hdd_err("Failed to open adapters");
+		goto unlock;
+	}
 
-	hdd_qdf_deinit();
+	if (next_mode == QDF_GLOBAL_MONITOR_MODE) {
+		struct hdd_adapter *adapter =
+			hdd_get_adapter(hdd_ctx, QDF_MONITOR_MODE);
+
+		QDF_BUG(adapter);
+		if (!adapter) {
+			hdd_err("Failed to get monitor adapter");
+			goto unlock;
+		}
+
+		errno = hdd_start_adapter(adapter);
+		if (errno) {
+			hdd_err("Failed to start monitor adapter");
+			goto unlock;
+		}
+
+		hdd_info("Acquire wakelock for monitor mode");
+		qdf_wake_lock_acquire(&hdd_ctx->monitor_mode_wakelock,
+				      WIFI_POWER_EVENT_WAKELOCK_MONITOR_MODE);
+	}
+
+	/* con_mode is a global module parameter */
+	con_mode = next_mode;
+	hdd_info("Driver mode successfully changed to %d", next_mode);
+
+	errno = 0;
+
+unlock:
+	mutex_unlock(&hdd_init_deinit_lock);
+	qdf_atomic_set(&hdd_ctx->con_mode_flag, 0);
+
+	return errno;
 }
 
-#ifndef MODULE
-/**
- * wlan_boot_cb() - Wlan boot callback
- * @kobj:      object whose directory we're creating the link in.
- * @attr:      attribute the user is interacting with
- * @buff:      the buffer containing the user data
- * @count:     number of bytes in the buffer
- *
- * This callback is invoked when the fs is ready to start the
- * wlan driver initialization.
- *
- * Return: 'count' on success or a negative error code in case of failure
- */
-static ssize_t wlan_boot_cb(struct kobject *kobj,
-			    struct kobj_attribute *attr,
-			    const char *buf,
-			    size_t count)
+static int hdd_driver_mode_change(enum QDF_GLOBAL_MODE mode)
 {
+	struct hdd_driver *hdd_driver = hdd_driver_get();
+	struct hdd_context *hdd_ctx;
+	QDF_STATUS status;
+	int errno;
 
-	if (wlan_loader->loaded_state) {
-		hdd_err("wlan driver already initialized");
-		return -EALREADY;
+	hdd_enter();
+
+	status = dsc_driver_trans_start_wait(hdd_driver->dsc_driver,
+					     "mode change");
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Failed to start 'mode change'; status:%u", status);
+		errno = qdf_status_to_os_return(status);
+		goto exit;
 	}
 
-	if (hdd_driver_load())
-		return -EIO;
+	dsc_driver_wait_for_ops(hdd_driver->dsc_driver);
 
-	wlan_loader->loaded_state = MODULE_INITIALIZED;
+	hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+	errno = wlan_hdd_validate_context(hdd_ctx);
+	if (errno)
+		goto trans_stop;
 
-	return count;
+	if (!cds_wait_for_external_threads_completion(__func__)) {
+		hdd_warn("External threads still active, cannot change mode");
+		errno = -EAGAIN;
+		goto trans_stop;
+	}
+
+	cds_ssr_protect(__func__);
+	errno = __hdd_driver_mode_change(hdd_ctx, mode);
+	cds_ssr_unprotect(__func__);
+
+trans_stop:
+	dsc_driver_trans_stop(hdd_driver->dsc_driver);
+
+exit:
+	hdd_exit();
+
+	return errno;
 }
 
-/**
- * hdd_sysfs_cleanup() - cleanup sysfs
- *
- * Return: None
- *
- */
-static void hdd_sysfs_cleanup(void)
+static int hdd_set_con_mode(enum QDF_GLOBAL_MODE mode)
 {
-	/* remove from group */
-	if (wlan_loader->boot_wlan_obj && wlan_loader->attr_group)
-		sysfs_remove_group(wlan_loader->boot_wlan_obj,
-				   wlan_loader->attr_group);
+	con_mode = mode;
 
-	/* unlink the object from parent */
-	kobject_del(wlan_loader->boot_wlan_obj);
+	return 0;
+}
 
-	/* free the object */
-	kobject_put(wlan_loader->boot_wlan_obj);
+static int (*hdd_set_con_mode_cb)(enum QDF_GLOBAL_MODE mode) = hdd_set_con_mode;
 
-	kfree(wlan_loader->attr_group);
-	kfree(wlan_loader);
+static void hdd_driver_mode_change_register(void)
+{
+	hdd_set_con_mode_cb = hdd_driver_mode_change;
+}
 
-	wlan_loader = NULL;
+static void hdd_driver_mode_change_unregister(void)
+{
+	hdd_set_con_mode_cb = hdd_set_con_mode;
+}
+
+static int con_mode_handler(const char *kmessage, const struct kernel_param *kp)
+{
+	enum QDF_GLOBAL_MODE mode;
+	int errno;
+
+	errno = hdd_parse_driver_mode(kmessage, &mode);
+	if (errno) {
+		hdd_err_rl("Failed to parse driver mode '%s'", kmessage);
+		return errno;
+	}
+
+	return hdd_set_con_mode_cb(mode);
 }
 
 /**
- * wlan_init_sysfs() - Creates the sysfs to be invoked when the fs is
- * ready
- *
- * This is creates the syfs entry boot_wlan. Which shall be invoked
- * when the filesystem is ready.
+ * hdd_driver_load() - Perform the driver-level load operation
  *
- * QDF API cannot be used here since this function is called even before
- * initializing WLAN driver.
+ * Note: this is used in both static and DLKM driver builds
  *
- * Return: 0 for success, errno on failure
+ * Return: Errno
  */
-static int wlan_init_sysfs(void)
+static int hdd_driver_load(void)
 {
-	int ret = -ENOMEM;
+	struct hdd_driver *hdd_driver = hdd_driver_get();
+	QDF_STATUS status;
+	int errno;
 
-	wlan_loader = kzalloc(sizeof(*wlan_loader), GFP_KERNEL);
-	if (!wlan_loader)
-		return -ENOMEM;
+	pr_err("%s: Loading driver v%s\n",
+	       WLAN_MODULE_NAME,
+	       g_wlan_driver_version);
 
-	wlan_loader->boot_wlan_obj = NULL;
-	wlan_loader->attr_group = kzalloc(sizeof(*(wlan_loader->attr_group)),
-					  GFP_KERNEL);
-	if (!wlan_loader->attr_group)
-		goto error_return;
+	status = hdd_qdf_init();
+	if (QDF_IS_STATUS_ERROR(status)) {
+		errno = qdf_status_to_os_return(status);
+		goto exit;
+	}
 
-	wlan_loader->loaded_state = 0;
-	wlan_loader->attr_group->attrs = attrs;
+	status = hdd_driver_ctx_init(hdd_driver);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Failed to init driver context; status:%u", status);
+		errno = qdf_status_to_os_return(status);
+		goto qdf_deinit;
+	}
 
-	wlan_loader->boot_wlan_obj = kobject_create_and_add(WLAN_LOADER_NAME,
-							    kernel_kobj);
-	if (!wlan_loader->boot_wlan_obj) {
-		hdd_err("sysfs create and add failed");
-		goto error_return;
+	status = dsc_driver_trans_start(hdd_driver->dsc_driver, "load");
+	QDF_BUG(QDF_IS_STATUS_SUCCESS(status));
+	if (QDF_IS_STATUS_ERROR(status)) {
+		errno = qdf_status_to_os_return(status);
+		goto hdd_driver_deinit;
 	}
 
-	ret = sysfs_create_group(wlan_loader->boot_wlan_obj,
-				 wlan_loader->attr_group);
-	if (ret) {
-		hdd_err("sysfs create group failed; errno:%d", ret);
-		goto error_return;
+	errno = hdd_init();
+	if (errno) {
+		hdd_err("Failed to init HDD; errno:%d", errno);
+		goto trans_stop;
 	}
 
-	return 0;
+	status = hdd_component_init();
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Failed to init components; status:%u", status);
+		errno = qdf_status_to_os_return(status);
+		goto hdd_deinit;
+	}
 
-error_return:
-	hdd_sysfs_cleanup();
+	status = qdf_wake_lock_create(&wlan_wake_lock, "wlan");
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Failed to create wake lock; status:%u", status);
+		errno = qdf_status_to_os_return(status);
+		goto comp_deinit;
+	}
 
-	return ret;
-}
+	hdd_set_conparam(con_mode);
 
-/**
- * wlan_deinit_sysfs() - Removes the sysfs created to initialize the wlan
- *
- * Return: 0 on success or errno on failure
- */
-static int wlan_deinit_sysfs(void)
-{
-	if (!wlan_loader) {
-		hdd_err("wlan_loader is null");
-		return -EINVAL;
+	errno = wlan_hdd_state_ctrl_param_create();
+	if (errno) {
+		hdd_err("Failed to create ctrl param; errno:%d", errno);
+		goto wakelock_destroy;
 	}
 
-	hdd_sysfs_cleanup();
-	return 0;
-}
+	errno = pld_init();
+	if (errno) {
+		hdd_err("Failed to init PLD; errno:%d", errno);
+		goto param_destroy;
+	}
 
-#endif /* MODULE */
+	hdd_driver->state = driver_state_loaded;
+	hdd_driver_mode_change_register();
+	dsc_driver_trans_stop(hdd_driver->dsc_driver);
 
-#ifdef MODULE
-/**
- * hdd_module_init() - Module init helper
- *
- * Module init helper function used by both module and static driver.
- *
- * Return: 0 for success, errno on failure
- */
-static int hdd_module_init(void)
-{
-	if (hdd_driver_load())
-		return -EINVAL;
+	/* psoc probe can happen in registration; do after 'load' transition */
+	errno = wlan_hdd_register_driver();
+	if (errno) {
+		hdd_err("Failed to register driver; errno:%d", errno);
+		goto pld_deinit;
+	}
+
+	hdd_debug("%s: driver loaded", WLAN_MODULE_NAME);
 
 	return 0;
-}
-#else
-static int __init hdd_module_init(void)
-{
-	int ret = -EINVAL;
 
-	ret = wlan_init_sysfs();
-	if (ret)
-		hdd_err("Failed to create sysfs entry");
+pld_deinit:
+	status = dsc_driver_trans_start(hdd_driver->dsc_driver, "unload");
+	QDF_BUG(QDF_IS_STATUS_SUCCESS(status));
 
-	return ret;
-}
-#endif
+	hdd_driver_mode_change_unregister();
+	pld_deinit();
+
+param_destroy:
+	wlan_hdd_state_ctrl_param_destroy();
+wakelock_destroy:
+	qdf_wake_lock_destroy(&wlan_wake_lock);
+comp_deinit:
+	hdd_component_deinit();
+hdd_deinit:
+	hdd_deinit();
+trans_stop:
+	hdd_driver->state = driver_state_deinit;
+	dsc_driver_trans_stop(hdd_driver->dsc_driver);
+hdd_driver_deinit:
+	hdd_driver_ctx_deinit(hdd_driver);
+qdf_deinit:
+	hdd_qdf_deinit();
 
+exit:
+	return errno;
+}
 
-#ifdef MODULE
 /**
- * hdd_module_exit() - Exit function
+ * hdd_driver_unload() - Performs the driver-level unload operation
  *
- * This is the driver exit point (invoked when module is unloaded using rmmod)
+ * Note: this is used in both static and DLKM driver builds
  *
  * Return: None
  */
-static void __exit hdd_module_exit(void)
-{
-	hdd_driver_unload();
-}
-#else
-static void __exit hdd_module_exit(void)
+static void hdd_driver_unload(void)
 {
-	hdd_driver_unload();
-	wlan_deinit_sysfs();
-}
-#endif
+	struct hdd_driver *hdd_driver = hdd_driver_get();
+	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+	QDF_STATUS status;
 
-static int fwpath_changed_handler(const char *kmessage,
-				  const struct kernel_param *kp)
-{
-	return param_set_copystring(kmessage, kp);
-}
+	pr_info("%s: Unloading driver v%s\n", WLAN_MODULE_NAME,
+		QWLAN_VERSIONSTR);
 
-#ifdef FEATURE_MONITOR_MODE_SUPPORT
-static bool is_monitor_mode_supported(void)
-{
-	return true;
-}
-#else
-static bool is_monitor_mode_supported(void)
-{
-	pr_err("Monitor mode not supported!");
-	return false;
-}
-#endif
+	if (g_is_system_reboot_triggered) {
+		hdd_info("System rebooting; Skipping unload");
+		return;
+	}
 
-#ifdef WLAN_FEATURE_EPPING
-static bool is_epping_mode_supported(void)
-{
-	return true;
-}
-#else
-static bool is_epping_mode_supported(void)
-{
-	pr_err("Epping mode not supported!");
-	return false;
-}
-#endif
+	if (hdd_ctx)
+		hdd_psoc_idle_timer_stop(hdd_ctx);
 
-#ifdef QCA_WIFI_FTM
-static bool is_ftm_mode_supported(void)
-{
-	return true;
-}
-#else
-static bool is_ftm_mode_supported(void)
-{
-	pr_err("FTM mode not supported!");
-	return false;
-}
-#endif
+	/* trigger SoC remove */
+	wlan_hdd_unregister_driver();
 
-/**
- * is_con_mode_valid() check con mode is valid or not
- * @mode: global con mode
- *
- * Return: TRUE on success FALSE on failure
- */
-static bool is_con_mode_valid(enum QDF_GLOBAL_MODE mode)
-{
-	switch (mode) {
-	case QDF_GLOBAL_MONITOR_MODE:
-		return is_monitor_mode_supported();
-	case QDF_GLOBAL_EPPING_MODE:
-		return is_epping_mode_supported();
-	case QDF_GLOBAL_FTM_MODE:
-		return is_ftm_mode_supported();
-	case QDF_GLOBAL_MISSION_MODE:
-		return true;
-	default:
-		return false;
+	status = dsc_driver_trans_start_wait(hdd_driver->dsc_driver, "unload");
+	QDF_BUG(QDF_IS_STATUS_SUCCESS(status));
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Unable to unload wlan; status:%u", status);
+		return;
 	}
-}
 
-static void hdd_stop_present_mode(struct hdd_context *hdd_ctx,
-				  enum QDF_GLOBAL_MODE curr_mode)
-{
-	if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED)
-		return;
+	dsc_driver_wait_for_ops(hdd_driver->dsc_driver);
 
-	switch (curr_mode) {
-	case QDF_GLOBAL_MONITOR_MODE:
-		hdd_info("Release wakelock for monitor mode!");
-		qdf_wake_lock_release(&hdd_ctx->monitor_mode_wakelock,
-				      WIFI_POWER_EVENT_WAKELOCK_MONITOR_MODE);
-		/* fallthrough */
-	case QDF_GLOBAL_MISSION_MODE:
-	case QDF_GLOBAL_FTM_MODE:
-		hdd_abort_mac_scan_all_adapters(hdd_ctx);
-		wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, NULL);
-		hdd_stop_all_adapters(hdd_ctx);
-		hdd_deinit_all_adapters(hdd_ctx, false);
+	cds_set_driver_loaded(false);
+	cds_set_unload_in_progress(true);
 
-		break;
-	default:
-		break;
-	}
-}
+	if (!cds_wait_for_external_threads_completion(__func__))
+		hdd_warn("External threads are still active attempting "
+			 "driver unload anyway");
 
-static void hdd_cleanup_present_mode(struct hdd_context *hdd_ctx,
-				    enum QDF_GLOBAL_MODE curr_mode)
-{
-	int driver_status;
+	hdd_driver_mode_change_unregister();
+	pld_deinit();
+	wlan_hdd_state_ctrl_param_destroy();
+	hdd_set_conparam(0);
+	qdf_wake_lock_destroy(&wlan_wake_lock);
+	hdd_component_deinit();
+	hdd_deinit();
 
-	driver_status = hdd_ctx->driver_status;
+	hdd_driver->state = driver_state_deinit;
+	dsc_driver_trans_stop(hdd_driver->dsc_driver);
 
-	switch (curr_mode) {
-	case QDF_GLOBAL_MISSION_MODE:
-	case QDF_GLOBAL_MONITOR_MODE:
-	case QDF_GLOBAL_FTM_MODE:
-		hdd_close_all_adapters(hdd_ctx, false);
-		break;
-	case QDF_GLOBAL_EPPING_MODE:
-		epping_disable();
-		epping_close();
-		break;
-	default:
-		return;
-	}
-}
+	hdd_driver_ctx_deinit(hdd_driver);
 
-static int
-hdd_parse_driver_mode(const char *mode_str, enum QDF_GLOBAL_MODE *out_mode)
-{
-	QDF_STATUS status;
-	uint32_t mode;
+	hdd_qdf_deinit();
+}
 
-	*out_mode = QDF_GLOBAL_MAX_MODE;
+#ifndef MODULE
+/**
+ * wlan_boot_cb() - Wlan boot callback
+ * @kobj:      object whose directory we're creating the link in.
+ * @attr:      attribute the user is interacting with
+ * @buff:      the buffer containing the user data
+ * @count:     number of bytes in the buffer
+ *
+ * This callback is invoked when the fs is ready to start the
+ * wlan driver initialization.
+ *
+ * Return: 'count' on success or a negative error code in case of failure
+ */
+static ssize_t wlan_boot_cb(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf,
+			    size_t count)
+{
 
-	status = qdf_uint32_parse(mode_str, &mode);
-	if (QDF_IS_STATUS_ERROR(status))
-		return qdf_status_to_os_return(status);
+	if (wlan_loader->loaded_state) {
+		hdd_err("wlan driver already initialized");
+		return -EALREADY;
+	}
 
-	if (mode >= QDF_GLOBAL_MAX_MODE)
-		return -ERANGE;
+	if (hdd_driver_load())
+		return -EIO;
 
-	*out_mode = (enum QDF_GLOBAL_MODE)mode;
+	wlan_loader->loaded_state = MODULE_INITIALIZED;
 
-	return 0;
+	return count;
 }
 
 /**
- * __hdd_driver_mode_change() - Handles a driver mode change
- * @hdd_ctx: Pointer to the global HDD context
- * @next_mode: the driver mode to transition to
+ * hdd_sysfs_cleanup() - cleanup sysfs
  *
- * This function is invoked when user updates con_mode using sys entry,
- * to initialize and bring-up driver in that specific mode.
+ * Return: None
  *
- * Return: Errno
  */
-static int __hdd_driver_mode_change(struct hdd_context *hdd_ctx,
-				    enum QDF_GLOBAL_MODE next_mode)
+static void hdd_sysfs_cleanup(void)
 {
-	enum QDF_GLOBAL_MODE curr_mode;
-	int errno;
-
-	hdd_info("Driver mode changing to %d", next_mode);
-
-	errno = wlan_hdd_validate_context(hdd_ctx);
-	if (errno)
-		return errno;
+	/* remove from group */
+	if (wlan_loader->boot_wlan_obj && wlan_loader->attr_group)
+		sysfs_remove_group(wlan_loader->boot_wlan_obj,
+				   wlan_loader->attr_group);
 
-	if (!is_con_mode_valid(next_mode)) {
-		hdd_err_rl("Requested driver mode is invalid");
-		return -EINVAL;
-	}
+	/* unlink the object from parent */
+	kobject_del(wlan_loader->boot_wlan_obj);
 
-	qdf_atomic_set(&hdd_ctx->con_mode_flag, 1);
-	mutex_lock(&hdd_init_deinit_lock);
+	/* free the object */
+	kobject_put(wlan_loader->boot_wlan_obj);
 
-	curr_mode = hdd_get_conparam();
-	if (curr_mode == next_mode) {
-		hdd_err_rl("Driver is already in the requested mode");
-		errno = 0;
-		goto unlock;
-	}
+	kfree(wlan_loader->attr_group);
+	kfree(wlan_loader);
 
-	/* ensure adapters are stopped */
-	hdd_stop_present_mode(hdd_ctx, curr_mode);
+	wlan_loader = NULL;
+}
 
-	errno = hdd_wlan_stop_modules(hdd_ctx, true);
-	if (errno) {
-		hdd_err("Stop wlan modules failed");
-		goto unlock;
-	}
+/**
+ * wlan_init_sysfs() - Creates the sysfs to be invoked when the fs is
+ * ready
+ *
+ * This is creates the syfs entry boot_wlan. Which shall be invoked
+ * when the filesystem is ready.
+ *
+ * QDF API cannot be used here since this function is called even before
+ * initializing WLAN driver.
+ *
+ * Return: 0 for success, errno on failure
+ */
+static int wlan_init_sysfs(void)
+{
+	int ret = -ENOMEM;
 
-	/* Cleanup present mode before switching to new mode */
-	hdd_cleanup_present_mode(hdd_ctx, curr_mode);
+	wlan_loader = kzalloc(sizeof(*wlan_loader), GFP_KERNEL);
+	if (!wlan_loader)
+		return -ENOMEM;
 
-	hdd_set_conparam(next_mode);
+	wlan_loader->boot_wlan_obj = NULL;
+	wlan_loader->attr_group = kzalloc(sizeof(*(wlan_loader->attr_group)),
+					  GFP_KERNEL);
+	if (!wlan_loader->attr_group)
+		goto error_return;
 
-	errno = hdd_wlan_start_modules(hdd_ctx, false);
-	if (errno) {
-		hdd_err("Start wlan modules failed: %d", errno);
-		goto unlock;
-	}
+	wlan_loader->loaded_state = 0;
+	wlan_loader->attr_group->attrs = attrs;
 
-	errno = hdd_open_adapters_for_mode(hdd_ctx, next_mode);
-	if (errno) {
-		hdd_err("Failed to open adapters");
-		goto unlock;
+	wlan_loader->boot_wlan_obj = kobject_create_and_add(WLAN_LOADER_NAME,
+							    kernel_kobj);
+	if (!wlan_loader->boot_wlan_obj) {
+		hdd_err("sysfs create and add failed");
+		goto error_return;
 	}
 
-	if (next_mode == QDF_GLOBAL_MONITOR_MODE) {
-		struct hdd_adapter *adapter =
-			hdd_get_adapter(hdd_ctx, QDF_MONITOR_MODE);
-
-		QDF_BUG(adapter);
-		if (!adapter) {
-			hdd_err("Failed to get monitor adapter");
-			goto unlock;
-		}
-
-		errno = hdd_start_adapter(adapter);
-		if (errno) {
-			hdd_err("Failed to start monitor adapter");
-			goto unlock;
-		}
-
-		hdd_info("Acquire wakelock for monitor mode");
-		qdf_wake_lock_acquire(&hdd_ctx->monitor_mode_wakelock,
-				      WIFI_POWER_EVENT_WAKELOCK_MONITOR_MODE);
+	ret = sysfs_create_group(wlan_loader->boot_wlan_obj,
+				 wlan_loader->attr_group);
+	if (ret) {
+		hdd_err("sysfs create group failed; errno:%d", ret);
+		goto error_return;
 	}
 
-	/* con_mode is a global module parameter */
-	con_mode = next_mode;
-	hdd_info("Driver mode successfully changed to %d", next_mode);
-
-	errno = 0;
+	return 0;
 
-unlock:
-	mutex_unlock(&hdd_init_deinit_lock);
-	qdf_atomic_set(&hdd_ctx->con_mode_flag, 0);
+error_return:
+	hdd_sysfs_cleanup();
 
-	return errno;
+	return ret;
 }
 
-static int hdd_driver_mode_change(enum QDF_GLOBAL_MODE mode)
+/**
+ * wlan_deinit_sysfs() - Removes the sysfs created to initialize the wlan
+ *
+ * Return: 0 on success or errno on failure
+ */
+static int wlan_deinit_sysfs(void)
 {
-	struct hdd_driver *hdd_driver = hdd_driver_get();
-	struct hdd_context *hdd_ctx;
-	QDF_STATUS status;
-	int errno;
-
-	hdd_enter();
-
-	status = dsc_driver_trans_start_wait(hdd_driver->dsc_driver,
-					     "mode change");
-	if (QDF_IS_STATUS_ERROR(status)) {
-		hdd_err("Failed to start 'mode change'; status:%u", status);
-		errno = qdf_status_to_os_return(status);
-		goto exit;
-	}
-
-	dsc_driver_wait_for_ops(hdd_driver->dsc_driver);
-
-	hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
-	errno = wlan_hdd_validate_context(hdd_ctx);
-	if (errno)
-		goto trans_stop;
-
-	if (!cds_wait_for_external_threads_completion(__func__)) {
-		hdd_warn("External threads still active, cannot change mode");
-		errno = -EAGAIN;
-		goto trans_stop;
+	if (!wlan_loader) {
+		hdd_err("wlan_loader is null");
+		return -EINVAL;
 	}
 
-	cds_ssr_protect(__func__);
-	errno = __hdd_driver_mode_change(hdd_ctx, mode);
-	cds_ssr_unprotect(__func__);
+	hdd_sysfs_cleanup();
+	return 0;
+}
 
-trans_stop:
-	dsc_driver_trans_stop(hdd_driver->dsc_driver);
+#endif /* MODULE */
 
-exit:
-	hdd_exit();
+#ifdef MODULE
+/**
+ * hdd_module_init() - Module init helper
+ *
+ * Module init helper function used by both module and static driver.
+ *
+ * Return: 0 for success, errno on failure
+ */
+static int hdd_module_init(void)
+{
+	if (hdd_driver_load())
+		return -EINVAL;
 
-	return errno;
+	return 0;
 }
-
-static int hdd_set_con_mode(enum QDF_GLOBAL_MODE mode)
+#else
+static int __init hdd_module_init(void)
 {
-	con_mode = mode;
+	int ret = -EINVAL;
 
-	return 0;
+	ret = wlan_init_sysfs();
+	if (ret)
+		hdd_err("Failed to create sysfs entry");
+
+	return ret;
 }
+#endif
 
-static int (*hdd_set_con_mode_cb)(enum QDF_GLOBAL_MODE mode) = hdd_set_con_mode;
 
-static void hdd_driver_mode_change_register(void)
+#ifdef MODULE
+/**
+ * hdd_module_exit() - Exit function
+ *
+ * This is the driver exit point (invoked when module is unloaded using rmmod)
+ *
+ * Return: None
+ */
+static void __exit hdd_module_exit(void)
 {
-	hdd_set_con_mode_cb = hdd_driver_mode_change;
+	hdd_driver_unload();
 }
-
-static void hdd_driver_mode_change_unregister(void)
+#else
+static void __exit hdd_module_exit(void)
 {
-	hdd_set_con_mode_cb = hdd_set_con_mode;
+	hdd_driver_unload();
+	wlan_deinit_sysfs();
 }
+#endif
 
-static int con_mode_handler(const char *kmessage, const struct kernel_param *kp)
+static int fwpath_changed_handler(const char *kmessage,
+				  const struct kernel_param *kp)
 {
-	enum QDF_GLOBAL_MODE mode;
-	int errno;
-
-	errno = hdd_parse_driver_mode(kmessage, &mode);
-	if (errno) {
-		hdd_err_rl("Failed to parse driver mode '%s'", kmessage);
-		return errno;
-	}
-
-	return hdd_set_con_mode_cb(mode);
+	return param_set_copystring(kmessage, kp);
 }
 
 static int con_mode_handler_ftm(const char *kmessage,