|
@@ -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,
|