Pārlūkot izejas kodu

qcacld-3.0: Dynamic antenna switch in disconnected/station mode feature

Functionality for switching antenna mode (TX/RX chains)
from 1x1 to 2x2 and vice versa dynamically without reloading
the driver. This feature is currently supported in the
disconnected state where only scan operation can happen and
standalone station mode.

The changes include the following:
1. Driver command IOCTL to set/get antenna mode as follows
DRIVER SETANTENNAMODE <# of streams>
DRIVER GETANTENNAMODE

2. Processing SETANTENNAMODE and GETANTENNAMODE to dynamically
change the number of TX/RX chains as follows

Dynamic switch request is allowed only when the supported
number of chains in the system is greater than 1.

Dynamic antenna switch is achieved by sending
WMI_PDEV_SET_ANTENNA_MODE command to the FW chain mask
manager with parameters to change the number of TX/RX chains.
This request will be maintained by the chain mask
manager and applied whenever only one MAC is active.

If there is a station association followed by dynamic switch
to 1x1 mode then SM power save IE is included in the HT caps
of the association/reassociation request management frame.

Dynamic switch in the connected standalone station mode is
achieved by sending SMPS force mode command with appropriate
values for smps mode by the chain mask manager.

Change-Id: I65d51d173a627dc2751ef10cc2172a5215e7b8db
CRs-Fixed: 931250
Archana Ramachandran 9 gadi atpakaļ
vecāks
revīzija
393f3796b6
2 mainītis faili ar 267 papildinājumiem un 0 dzēšanām
  1. 260 0
      core/hdd/src/wlan_hdd_ioctl.c
  2. 7 0
      core/hdd/src/wlan_hdd_main.c

+ 260 - 0
core/hdd/src/wlan_hdd_ioctl.c

@@ -7011,6 +7011,264 @@ static int drv_cmd_rx_filter_add(hdd_adapter_t *adapter,
 	return hdd_driver_rxfilter_comand_handler(command, adapter, true);
 }
 
+/**
+ * hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE
+ * command
+ * @value: Pointer to SETANTENNAMODE command
+ * @mode: Pointer to antenna mode
+ * @reason: Pointer to reason for set antenna mode
+ *
+ * This function parses the SETANTENNAMODE command passed in the format
+ * SETANTENNAMODE<space>mode
+ *
+ * Return: 0 for success non-zero for failure
+ */
+static int hdd_parse_setantennamode_command(const uint8_t *value)
+{
+	const uint8_t *in_ptr = value;
+	int tmp, v;
+	char arg1[32];
+
+	in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
+
+	/* no argument after the command */
+	if (NULL == in_ptr) {
+		hddLog(LOGE, FL("No argument after the command"));
+		return -EINVAL;
+	}
+
+	/* no space after the command */
+	if (SPACE_ASCII_VALUE != *in_ptr) {
+		hddLog(LOGE, FL("No space after the command"));
+		return -EINVAL;
+	}
+
+	/* remove empty spaces */
+	while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
+		in_ptr++;
+
+	/* no argument followed by spaces */
+	if ('\0' == *in_ptr) {
+		hddLog(LOGE, FL("No argument followed by spaces"));
+		return -EINVAL;
+	}
+
+	/* get the argument i.e. antenna mode */
+	v = sscanf(in_ptr, "%31s ", arg1);
+	if (1 != v) {
+		hddLog(LOGE, FL("argument retrieval from cmd string failed"));
+		return -EINVAL;
+	}
+
+	v = kstrtos32(arg1, 10, &tmp);
+	if (v < 0) {
+		hddLog(LOGE, FL("argument string to int conversion failed"));
+		return -EINVAL;
+	}
+
+	return tmp;
+}
+
+/**
+ * hdd_is_supported_chain_mask_2x2() - Verify if supported chain
+ * mask is 2x2 mode
+ * @hdd_ctx: Pointer to hdd contex
+ *
+ * Return: true if supported chain mask 2x2 else false
+ */
+static bool hdd_is_supported_chain_mask_2x2(hdd_context_t *hdd_ctx)
+{
+	/*
+	 * Revisit and the update logic to determine the number
+	 * of TX/RX chains supported in the system when
+	 * antenna sharing per band chain mask support is
+	 * brought in
+	 */
+	return (hdd_ctx->config->enable2x2 == 0x01) ? true : false;
+}
+
+/**
+ * hdd_is_supported_chain_mask_1x1() - Verify if the supported
+ * chain mask is 1x1
+ * @hdd_ctx: Pointer to hdd contex
+ *
+ * Return: true if supported chain mask 1x1 else false
+ */
+static bool hdd_is_supported_chain_mask_1x1(hdd_context_t *hdd_ctx)
+{
+	/*
+	 * Revisit and update the logic to determine the number
+	 * of TX/RX chains supported in the system when
+	 * antenna sharing per band chain mask support is
+	 * brought in
+	 */
+	return (!hdd_ctx->config->enable2x2) ? true : false;
+}
+
+/**
+ * drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command
+ * handler
+ * @adapter: Pointer to network adapter
+ * @hdd_ctx: Pointer to hdd context
+ * @command: Pointer to input command
+ * @command_len: Command length
+ * @priv_data: Pointer to private data in command
+ */
+static int drv_cmd_set_antenna_mode(hdd_adapter_t *adapter,
+				hdd_context_t *hdd_ctx,
+				uint8_t *command,
+				uint8_t command_len,
+				hdd_priv_data_t *priv_data)
+{
+	struct sir_antenna_mode_param params;
+	QDF_STATUS status;
+	int ret = 0;
+	int mode;
+	uint8_t *value = command;
+	uint8_t smps_mode;
+	uint8_t smps_enable;
+
+	if (((1 << QDF_STA_MODE) != hdd_ctx->concurrency_mode) ||
+	    (hdd_ctx->no_of_active_sessions[QDF_STA_MODE] > 1)) {
+		hdd_err("Operation invalid in non sta or concurrent mode");
+		ret = -EPERM;
+		goto exit;
+	}
+
+	mode = hdd_parse_setantennamode_command(value);
+	if (mode < 0) {
+		hdd_err("Invalid SETANTENNA command");
+		ret = mode;
+		goto exit;
+	}
+
+	hdd_info("Processing antenna mode switch to: %d", mode);
+
+	if (hdd_ctx->current_antenna_mode == mode) {
+		hdd_err("System already in the requested mode");
+		ret = 0;
+		goto exit;
+	}
+
+	if ((HDD_ANTENNA_MODE_2X2 == mode) &&
+	    (!hdd_is_supported_chain_mask_2x2(hdd_ctx))) {
+		hdd_err("System does not support 2x2 mode");
+		ret = -EPERM;
+		goto exit;
+	}
+
+	if ((HDD_ANTENNA_MODE_1X1 == mode) &&
+	    hdd_is_supported_chain_mask_1x1(hdd_ctx)) {
+		hdd_err("System only supports 1x1 mode");
+		ret = 0;
+		goto exit;
+	}
+
+	switch (mode) {
+	case HDD_ANTENNA_MODE_1X1:
+		params.num_rx_chains = 1;
+		params.num_tx_chains = 1;
+		break;
+	case HDD_ANTENNA_MODE_2X2:
+		params.num_rx_chains = 2;
+		params.num_tx_chains = 2;
+		break;
+	default:
+		hdd_err("unsupported antenna mode");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	params.set_antenna_mode_resp =
+	    (void *)wlan_hdd_soc_set_antenna_mode_cb;
+	hdd_info("Set antenna mode rx chains: %d tx chains: %d",
+		 params.num_rx_chains,
+		 params.num_tx_chains);
+
+
+	INIT_COMPLETION(hdd_ctx->set_antenna_mode_cmpl);
+	status = sme_soc_set_antenna_mode(hdd_ctx->hHal, &params);
+	if (QDF_STATUS_SUCCESS != status) {
+		hdd_err("set antenna mode failed status : %d", status);
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	ret = wait_for_completion_timeout(
+		&hdd_ctx->set_antenna_mode_cmpl,
+		msecs_to_jiffies(WLAN_WAIT_TIME_ANTENNA_MODE_REQ));
+	if (!ret) {
+		ret = -EFAULT;
+		hdd_err("send set antenna mode timed out");
+		goto exit;
+	}
+
+	/* Update SME SMPS config */
+	if (HDD_ANTENNA_MODE_1X1 == mode) {
+		smps_enable = true;
+		smps_mode = HDD_SMPS_MODE_STATIC;
+	} else {
+		smps_enable = false;
+		smps_mode = HDD_SMPS_MODE_DISABLED;
+	}
+
+	hdd_info("Update SME SMPS enable: %d mode: %d",
+		 smps_enable, smps_mode);
+	status = sme_update_mimo_power_save(
+		hdd_ctx->hHal, smps_enable, smps_mode, false);
+	if (QDF_STATUS_SUCCESS != status) {
+		hdd_err("Update SMPS config failed enable: %d mode: %d status: %d",
+			smps_enable, smps_mode, status);
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	hdd_info("Successfully switched to mode: %d x %d", mode, mode);
+	ret = 0;
+	hdd_ctx->current_antenna_mode = mode;
+
+exit:
+	hdd_info("Set antenna status: %d current mode: %d",
+		 ret, hdd_ctx->current_antenna_mode);
+	return ret;
+
+}
+
+/**
+ * drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command
+ * handler
+ * @adapter: Pointer to hdd adapter
+ * @hdd_ctx: Pointer to hdd context
+ * @command: Pointer to input command
+ * @command_len: length of the command
+ * @priv_data: private data coming with the driver command
+ *
+ * Return: 0 for success non-zero for failure
+ */
+static inline int drv_cmd_get_antenna_mode(hdd_adapter_t *adapter,
+					   hdd_context_t *hdd_ctx,
+					   uint8_t *command,
+					   uint8_t command_len,
+					   hdd_priv_data_t *priv_data)
+{
+	uint32_t antenna_mode = 0;
+	char extra[32];
+	uint8_t len = 0;
+
+	antenna_mode = hdd_ctx->current_antenna_mode;
+	len = scnprintf(extra, sizeof(extra), "%s %d", command,
+			antenna_mode);
+	len = QDF_MIN(priv_data->total_len, len + 1);
+	if (copy_to_user(priv_data->buf, &extra, len)) {
+		hdd_err("Failed to copy data to user buffer");
+		return -EFAULT;
+	}
+
+	hdd_info("Get antenna mode: %d", antenna_mode);
+
+	return 0;
+}
+
 /*
  * dummy (no-op) hdd driver command handler
  */
@@ -7323,6 +7581,8 @@ static const hdd_drv_cmd_t hdd_drv_cmds[] = {
 	{"RXFILTER-ADD",              drv_cmd_rx_filter_add},
 	{"SET_FCC_CHANNEL",           drv_cmd_set_fcc_channel},
 	{"CHANNEL_SWITCH",            drv_cmd_set_channel_switch},
+	{"SETANTENNAMODE",            drv_cmd_set_antenna_mode},
+	{"GETANTENNAMODE",            drv_cmd_get_antenna_mode},
 };
 
 /**

+ 7 - 0
core/hdd/src/wlan_hdd_main.c

@@ -1328,6 +1328,13 @@ void hdd_update_tgt_cfg(void *context, void *param)
 	hdd_ctx->fine_time_meas_cap_target = cfg->fine_time_measurement_cap;
 	hdd_info(FL("fine_time_meas_cap: 0x%x"),
 		hdd_ctx->config->fine_time_meas_cap);
+
+	hdd_ctx->current_antenna_mode =
+		(hdd_ctx->config->enable2x2 == 0x01) ?
+		HDD_ANTENNA_MODE_2X2 : HDD_ANTENNA_MODE_1X1;
+	hdd_info("Init current antenna mode: %d",
+		 hdd_ctx->current_antenna_mode);
+
 }
 
 /**