ソースを参照

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
Aditya Kodukula 3 年 前
コミット
c4ca02df28
3 ファイル変更161 行追加19 行削除
  1. 3 0
      Kbuild
  2. 18 0
      core/hdd/inc/wlan_hdd_main.h
  3. 140 19
      core/hdd/src/wlan_hdd_main.c

+ 3 - 0
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

+ 18 - 0
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;
 };
 
 /**

+ 140 - 19
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;
 }