From c4ca02df28e3ee2d43c852b5cb1a36fc493c6441 Mon Sep 17 00:00:00 2001 From: Aditya Kodukula Date: Tue, 5 Apr 2022 16:08:12 -0700 Subject: [PATCH] qcacld-3.0: Add infra to disable/enable wifi As part of protected dynamic interface control feature, add infra to disable/enable wifi, when invoked from user space. Change-Id: I3f6c2abcef1ef89cbd0a735820de9b54d37bfa29 CRs-Fixed: 3166467 --- Kbuild | 3 + core/hdd/inc/wlan_hdd_main.h | 18 ++++ core/hdd/src/wlan_hdd_main.c | 159 ++++++++++++++++++++++++++++++----- 3 files changed, 161 insertions(+), 19 deletions(-) diff --git a/Kbuild b/Kbuild index 0dd208508a..892dc32398 100644 --- a/Kbuild +++ b/Kbuild @@ -4379,6 +4379,9 @@ ifeq ($(CONFIG_DP_HW_TX_DELAY_STATS_ENABLE), y) cppflags-y += -DHW_TX_DELAY_STATS_ENABLE endif +#Flags to enable/disable Dynamic WLAN interface control feature +cppflags-$(CONFIG_WLAN_DYNAMIC_IFACE_CTRL) += -DFEATURE_WLAN_DYNAMIC_IFACE_CTRL + KBUILD_CPPFLAGS += $(cppflags-y) # Currently, for versions of gcc which support it, the kernel Makefile diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 00435ca921..c47dd22fe5 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1968,6 +1968,22 @@ struct hdd_rtpm_tput_policy_context { }; #endif +/** + * enum wlan_state_ctrl_str_id - state contrl param string id + * @WLAN_OFF_STR: Turn OFF WiFi + * @WLAN_ON_STR: Turn ON WiFi + * @WLAN_ENABLE_STR: Enable WiFi + * @WLAN_DISABLE_STR: Disable Wifi + * @WLAN_WAIT_FOR_READY_STR: Driver should wait for ongoing recovery + */ +enum wlan_state_ctrl_str_id { + WLAN_OFF_STR = 0, + WLAN_ON_STR, + WLAN_ENABLE_STR, + WLAN_DISABLE_STR, + WLAN_WAIT_FOR_READY_STR +}; + /** * struct hdd_context - hdd shared driver and psoc/device context * @psoc: object manager psoc context @@ -1997,6 +2013,7 @@ struct hdd_rtpm_tput_policy_context { * @dump_in_progress: Stores value of dump in progress * @hdd_dual_sta_policy: Concurrent STA policy configuration * @rx_skip_qdisc_chk_conc: flag to skip ingress qdisc check in concurrency + * @is_wlan_disabled: if wlan is disabled by userspace */ struct hdd_context { struct wlan_objmgr_psoc *psoc; @@ -2379,6 +2396,7 @@ struct hdd_context { #ifdef CONFIG_WLAN_FREQ_LIST uint8_t power_type; #endif + bool is_wlan_disabled; }; /** diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 11e7b25bb9..51dbc3cd64 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -1460,6 +1460,11 @@ int __wlan_hdd_validate_context(struct hdd_context *hdd_ctx, const char *func) return -EAGAIN; } + if (hdd_ctx->is_wlan_disabled) { + hdd_debug("WLAN is disabled by user space"); + return -EAGAIN; + } + return 0; } @@ -4989,6 +4994,12 @@ static int __hdd_open(struct net_device *dev) return -EIO; } + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) { + hdd_err("Can't start WLAN module, WiFi Disabled"); + return ret; + } + ret = hdd_trigger_psoc_idle_restart(hdd_ctx); if (ret) { hdd_err("Failed to start WLAN modules return"); @@ -17486,41 +17497,132 @@ static void hdd_inform_wifi_off(void) osif_psoc_sync_op_stop(psoc_sync); } +static int hdd_validate_wlan_string(const char __user *user_buf) +{ + char buf[15]; + int i; + static const char * const wlan_str[] = { + [WLAN_OFF_STR] = "OFF", + [WLAN_ON_STR] = "ON", + [WLAN_ENABLE_STR] = "ENABLE", + [WLAN_DISABLE_STR] = "DISABLE", + [WLAN_WAIT_FOR_READY_STR] = "WAIT_FOR_READY" + }; + + if (copy_from_user(buf, user_buf, sizeof(buf))) { + pr_err("Failed to read buffer\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(wlan_str); i++) { + if (qdf_str_ncmp(buf, wlan_str[i], strlen(wlan_str[i])) == 0) + return i; + } + + return -EINVAL; +} + +#ifdef FEATURE_WLAN_DYNAMIC_IFACE_CTRL +#define WIFI_DISABLE_SLEEP (10) +#define WIFI_DISABLE_MAX_RETRY_ATTEMPTS (10) + +static int hdd_disable_wifi(struct hdd_context *hdd_ctx) +{ + int ret; + int retries = 0; + void *hif_ctx; + + if (!hdd_ctx) { + hdd_err_rl("hdd_ctx is Null"); + return -EINVAL; + } + + if (hdd_ctx->is_wlan_disabled) { + hdd_err_rl("Wifi already disabled"); + return -EINVAL; + } + + hdd_debug("Initiating WLAN idle shutdown"); + if (hdd_is_any_interface_open(hdd_ctx)) { + hdd_err("Interfaces still open, cannot process wifi disable"); + return -EAGAIN; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + + hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_IDLE_SHUTDOWN); + + while (retries < WIFI_DISABLE_MAX_RETRY_ATTEMPTS) { + if (hif_ctx) { + /* + * Trigger runtime sync resume before psoc_idle_shutdown + * such that resume can happen successfully + */ + hif_pm_runtime_sync_resume(hif_ctx, + RTPM_ID_SOC_IDLE_SHUTDOWN); + } + ret = pld_idle_shutdown(hdd_ctx->parent_dev, + hdd_psoc_idle_shutdown); + + if (-EAGAIN == ret || hdd_ctx->is_wiphy_suspended) { + hdd_debug("System suspend in progress.Retries done:%d", + retries); + msleep(WIFI_DISABLE_SLEEP); + retries++; + } + break; + } + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_IDLE_SHUTDOWN); + + if (retries > WIFI_DISABLE_MAX_RETRY_ATTEMPTS) { + hdd_debug("Max retries reached"); + return -EINVAL; + } + hdd_ctx->is_wlan_disabled = true; + + return 0; +} +#else +static int hdd_disable_wifi(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif /* FEATURE_WLAN_DYNAMIC_IFACE_CTRL */ + static ssize_t wlan_hdd_state_ctrl_param_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *f_pos) { - char buf[3]; - static const char wlan_off_str[] = "OFF"; - static const char wlan_on_str[] = "ON"; - static const char wlan_wait_for_ready_str[] = "WAIT_FOR_READY"; - int ret; + int id, ret; unsigned long rc; struct hdd_context *hdd_ctx; bool is_wait_for_ready = false; - if (copy_from_user(buf, user_buf, 3)) { - pr_err("Failed to read buffer\n"); - return -EINVAL; - } - if (strncmp(buf, wlan_off_str, strlen(wlan_off_str)) == 0) { + id = hdd_validate_wlan_string(user_buf); + + switch (id) { + case WLAN_OFF_STR: pr_info("Wifi turning off from UI\n"); hdd_inform_wifi_off(); goto exit; - } - - if (strncmp(buf, wlan_on_str, strlen(wlan_on_str)) == 0) + case WLAN_ON_STR: pr_info("Wifi Turning On from UI\n"); - - if (strncmp(buf, wlan_wait_for_ready_str, - strlen(wlan_wait_for_ready_str)) == 0) { + break; + case WLAN_WAIT_FOR_READY_STR: is_wait_for_ready = true; pr_info("Wifi wait for ready from UI\n"); - } else if (strncmp(buf, wlan_on_str, strlen(wlan_on_str)) != 0) { - pr_err("Invalid value received from framework"); - goto exit; + break; + case WLAN_ENABLE_STR: + pr_info("Enabling WiFi\n"); + break; + case WLAN_DISABLE_STR: + pr_info("Disabling WiFi\n"); + break; + default: + hdd_err_rl("Invalid value received from framework"); + return -EINVAL; } hdd_info("is_driver_loaded %d is_driver_recovering %d", @@ -17547,6 +17649,25 @@ static ssize_t wlan_hdd_state_ctrl_param_write(struct file *filp, if (hdd_ctx) hdd_psoc_idle_timer_stop(hdd_ctx); + if (id == WLAN_DISABLE_STR) { + ret = hdd_disable_wifi(hdd_ctx); + if (ret) + return ret; + } + + if (id == WLAN_ENABLE_STR) { + if (!hdd_ctx) { + hdd_err_rl("hdd_ctx is Null"); + return -EINVAL; + } + + if (!hdd_ctx->is_wlan_disabled) { + hdd_err_rl("WiFi is already enabled"); + return -EINVAL; + } + hdd_ctx->is_wlan_disabled = false; + } + exit: return count; }