sta_cmdresp.c 26.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/*
 * Marvell Wireless LAN device driver: station command response handling
 *
 * Copyright (C) 2011, Marvell International Ltd.
 *
 * This software file (the "File") is distributed by Marvell International
 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
 * (the "License").  You may use, redistribute and/or modify this File in
 * accordance with the terms and conditions of the License, a copy of which
 * is available by writing to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
 * this warranty disclaimer.
 */

#include "decl.h"
#include "ioctl.h"
#include "util.h"
#include "fw.h"
#include "main.h"
#include "wmm.h"
#include "11n.h"


/*
 * This function handles the command response error case.
 *
 * For scan response error, the function cancels all the pending
 * scan commands and generates an event to inform the applications
 * of the scan completion.
 *
 * For Power Save command failure, we do not retry enter PS
 * command in case of Ad-hoc mode.
 *
 * For all other response errors, the current command buffer is freed
 * and returned to the free command queue.
 */
static void
mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
44
			      struct host_cmd_ds_command *resp)
45
{
46
	struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
47
	struct mwifiex_adapter *adapter = priv->adapter;
48
	struct host_cmd_ds_802_11_ps_mode_enh *pm;
49 50 51
	unsigned long flags;

	dev_err(adapter->dev, "CMD_RESP: cmd %#x error, result=%#x\n",
52
		resp->command, resp->result);
53 54 55

	if (adapter->curr_cmd->wait_q_enabled)
		adapter->cmd_wait_q.status = -1;
56 57 58

	switch (le16_to_cpu(resp->command)) {
	case HostCmd_CMD_802_11_PS_MODE_ENH:
59
		pm = &resp->params.psmode_enh;
60 61 62
		dev_err(adapter->dev,
			"PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n",
			resp->result, le16_to_cpu(pm->action));
63 64
		/* We do not re-try enter-ps command in ad-hoc mode. */
		if (le16_to_cpu(pm->action) == EN_AUTO_PS &&
65 66
		    (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) &&
		    priv->bss_mode == NL80211_IFTYPE_ADHOC)
67 68
			adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
		break;
	case HostCmd_CMD_802_11_SCAN:
		/* Cancel all pending scan command */
		spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
		list_for_each_entry_safe(cmd_node, tmp_node,
					 &adapter->scan_pending_q, list) {
			list_del(&cmd_node->list);
			spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
					       flags);
			mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
			spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
		}
		spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);

		spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
		adapter->scan_processing = false;
		spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
		if (priv->report_scan_result)
			priv->report_scan_result = false;
		if (priv->scan_pending_on_block) {
			priv->scan_pending_on_block = false;
			up(&priv->async_sem);
		}
		break;

	case HostCmd_CMD_MAC_CONTROL:
		break;

	default:
		break;
	}
	/* Handling errors here */
	mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);

	spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
	adapter->curr_cmd = NULL;
	spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
}

/*
 * This function handles the command response of get RSSI info.
 *
 * Handling includes changing the header fields into CPU format
 * and saving the following parameters in driver -
 *      - Last data and beacon RSSI value
 *      - Average data and beacon RSSI value
 *      - Last data and beacon NF value
 *      - Average data and beacon NF value
 *
 * The parameters are send to the application as well, along with
 * calculated SNR values.
 */
static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
122
					struct host_cmd_ds_command *resp)
123 124
{
	struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp =
125
						&resp->params.rssi_info_rsp;
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155

	priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last);
	priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last);

	priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg);
	priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg);

	priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last);
	priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last);

	priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg);
	priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg);

	return 0;
}

/*
 * This function handles the command response of set/get SNMP
 * MIB parameters.
 *
 * Handling includes changing the header fields into CPU format
 * and saving the parameter in driver.
 *
 * The following parameters are supported -
 *      - Fragmentation threshold
 *      - RTS threshold
 *      - Short retry limit
 */
static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv,
				       struct host_cmd_ds_command *resp,
156
				       u32 *data_buf)
157 158 159 160
{
	struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib;
	u16 oid = le16_to_cpu(smib->oid);
	u16 query_type = le16_to_cpu(smib->query_type);
161
	u32 ul_temp;
162 163

	dev_dbg(priv->adapter->dev, "info: SNMP_RESP: oid value = %#x,"
164 165
		" query_type = %#x, buf size = %#x\n",
		oid, query_type, le16_to_cpu(smib->buf_size));
166
	if (query_type == HostCmd_ACT_GEN_GET) {
167 168 169
		ul_temp = le16_to_cpu(*((__le16 *) (smib->value)));
		if (data_buf)
			*data_buf = ul_temp;
170 171 172
		switch (oid) {
		case FRAG_THRESH_I:
			dev_dbg(priv->adapter->dev,
173
				"info: SNMP_RESP: FragThsd =%u\n", ul_temp);
174 175 176
			break;
		case RTS_THRESH_I:
			dev_dbg(priv->adapter->dev,
177
				"info: SNMP_RESP: RTSThsd =%u\n", ul_temp);
178 179 180
			break;
		case SHORT_RETRY_LIM_I:
			dev_dbg(priv->adapter->dev,
181
				"info: SNMP_RESP: TxRetryCount=%u\n", ul_temp);
182
			break;
183 184 185
		case DTIM_PERIOD_I:
			dev_dbg(priv->adapter->dev,
				"info: SNMP_RESP: DTIM period=%u\n", ul_temp);
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
		default:
			break;
		}
	}

	return 0;
}

/*
 * This function handles the command response of get log request
 *
 * Handling includes changing the header fields into CPU format
 * and sending the received parameters to application.
 */
static int mwifiex_ret_get_log(struct mwifiex_private *priv,
			       struct host_cmd_ds_command *resp,
202
			       struct mwifiex_ds_get_stats *stats)
203 204 205 206
{
	struct host_cmd_ds_802_11_get_log *get_log =
		(struct host_cmd_ds_802_11_get_log *) &resp->params.get_log;

207
	if (stats) {
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
		stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame);
		stats->failed = le32_to_cpu(get_log->failed);
		stats->retry = le32_to_cpu(get_log->retry);
		stats->multi_retry = le32_to_cpu(get_log->multi_retry);
		stats->frame_dup = le32_to_cpu(get_log->frame_dup);
		stats->rts_success = le32_to_cpu(get_log->rts_success);
		stats->rts_failure = le32_to_cpu(get_log->rts_failure);
		stats->ack_failure = le32_to_cpu(get_log->ack_failure);
		stats->rx_frag = le32_to_cpu(get_log->rx_frag);
		stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame);
		stats->fcs_error = le32_to_cpu(get_log->fcs_error);
		stats->tx_frame = le32_to_cpu(get_log->tx_frame);
		stats->wep_icv_error[0] =
			le32_to_cpu(get_log->wep_icv_err_cnt[0]);
		stats->wep_icv_error[1] =
			le32_to_cpu(get_log->wep_icv_err_cnt[1]);
		stats->wep_icv_error[2] =
			le32_to_cpu(get_log->wep_icv_err_cnt[2]);
		stats->wep_icv_error[3] =
			le32_to_cpu(get_log->wep_icv_err_cnt[3]);
	}

	return 0;
}

/*
 * This function handles the command response of set/get Tx rate
 * configurations.
 *
 * Handling includes changing the header fields into CPU format
 * and saving the following parameters in driver -
 *      - DSSS rate bitmap
 *      - OFDM rate bitmap
 *      - HT MCS rate bitmaps
 *
 * Based on the new rate bitmaps, the function re-evaluates if
 * auto data rate has been activated. If not, it sends another
 * query to the firmware to get the current Tx data rate and updates
 * the driver value.
 */
static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
				   struct host_cmd_ds_command *resp,
250
				   struct mwifiex_rate_cfg *ds_rate)
251 252 253
{
	struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg;
	struct mwifiex_rate_scope *rate_scope;
254
	struct mwifiex_ie_types_header *head;
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	u16 tlv, tlv_buf_len;
	u8 *tlv_buf;
	u32 i;
	int ret = 0;

	tlv_buf = (u8 *) ((u8 *) rate_cfg) +
			sizeof(struct host_cmd_ds_tx_rate_cfg);
	tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16));

	while (tlv_buf && tlv_buf_len > 0) {
		tlv = (*tlv_buf);
		tlv = tlv | (*(tlv_buf + 1) << 8);

		switch (tlv) {
		case TLV_TYPE_RATE_SCOPE:
			rate_scope = (struct mwifiex_rate_scope *) tlv_buf;
			priv->bitmap_rates[0] =
				le16_to_cpu(rate_scope->hr_dsss_rate_bitmap);
			priv->bitmap_rates[1] =
				le16_to_cpu(rate_scope->ofdm_rate_bitmap);
			for (i = 0;
			     i <
			     sizeof(rate_scope->ht_mcs_rate_bitmap) /
			     sizeof(u16); i++)
				priv->bitmap_rates[2 + i] =
					le16_to_cpu(rate_scope->
						    ht_mcs_rate_bitmap[i]);
			break;
			/* Add RATE_DROP tlv here */
		}

		head = (struct mwifiex_ie_types_header *) tlv_buf;
		tlv_buf += le16_to_cpu(head->len) + sizeof(*head);
		tlv_buf_len -= le16_to_cpu(head->len);
	}

	priv->is_data_rate_auto = mwifiex_is_rate_auto(priv);

	if (priv->is_data_rate_auto)
		priv->data_rate = 0;
	else
296
		ret = mwifiex_send_cmd_async(priv,
297
					  HostCmd_CMD_802_11_TX_RATE_QUERY,
298
					  HostCmd_ACT_GEN_GET, 0, NULL);
299

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
	if (!ds_rate)
		return ret;

	if (le16_to_cpu(rate_cfg->action) == HostCmd_ACT_GEN_GET) {
		if (priv->is_data_rate_auto) {
			ds_rate->is_rate_auto = 1;
		return ret;
	}
	ds_rate->rate = mwifiex_get_rate_index(priv->bitmap_rates,
					       sizeof(priv->bitmap_rates));

	if (ds_rate->rate >= MWIFIEX_RATE_BITMAP_OFDM0 &&
	    ds_rate->rate <= MWIFIEX_RATE_BITMAP_OFDM7)
		ds_rate->rate -= (MWIFIEX_RATE_BITMAP_OFDM0 -
				  MWIFIEX_RATE_INDEX_OFDM0);

	if (ds_rate->rate >= MWIFIEX_RATE_BITMAP_MCS0 &&
	    ds_rate->rate <= MWIFIEX_RATE_BITMAP_MCS127)
		ds_rate->rate -= (MWIFIEX_RATE_BITMAP_MCS0 -
				  MWIFIEX_RATE_INDEX_MCS0);
320 321 322 323 324 325 326 327 328 329 330 331 332
	}

	return ret;
}

/*
 * This function handles the command response of get Tx power level.
 *
 * Handling includes saving the maximum and minimum Tx power levels
 * in driver, as well as sending the values to user.
 */
static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf)
{
333 334 335
	int length, max_power = -1, min_power = -1;
	struct mwifiex_types_power_group *pg_tlv_hdr;
	struct mwifiex_power_group *pg;
336

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	if (!data_buf)
		return -1;

	pg_tlv_hdr = (struct mwifiex_types_power_group *)
		((u8 *) data_buf + sizeof(struct host_cmd_ds_txpwr_cfg));
	pg = (struct mwifiex_power_group *)
		((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group));
	length = pg_tlv_hdr->length;
	if (length > 0) {
		max_power = pg->power_max;
		min_power = pg->power_min;
		length -= sizeof(struct mwifiex_power_group);
	}
	while (length) {
		pg++;
		if (max_power < pg->power_max)
353 354
			max_power = pg->power_max;

355 356
		if (min_power > pg->power_min)
			min_power = pg->power_min;
357

358 359 360 361 362
		length -= sizeof(struct mwifiex_power_group);
	}
	if (pg_tlv_hdr->length > 0) {
		priv->min_tx_power_level = (u8) min_power;
		priv->max_tx_power_level = (u8) max_power;
363 364 365 366 367 368 369 370 371 372 373 374 375
	}

	return 0;
}

/*
 * This function handles the command response of set/get Tx power
 * configurations.
 *
 * Handling includes changing the header fields into CPU format
 * and saving the current Tx power level in driver.
 */
static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv,
376
				    struct host_cmd_ds_command *resp)
377 378 379
{
	struct mwifiex_adapter *adapter = priv->adapter;
	struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg;
380 381
	struct mwifiex_types_power_group *pg_tlv_hdr;
	struct mwifiex_power_group *pg;
382 383 384 385
	u16 action = le16_to_cpu(txp_cfg->action);

	switch (action) {
	case HostCmd_ACT_GEN_GET:
386 387 388 389 390 391 392 393 394 395 396 397 398 399
		pg_tlv_hdr = (struct mwifiex_types_power_group *)
			((u8 *) txp_cfg +
			 sizeof(struct host_cmd_ds_txpwr_cfg));

		pg = (struct mwifiex_power_group *)
			((u8 *) pg_tlv_hdr +
			 sizeof(struct mwifiex_types_power_group));

		if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
			mwifiex_get_power_level(priv, txp_cfg);

		priv->tx_power_level = (u16) pg->power_min;
		break;

400
	case HostCmd_ACT_GEN_SET:
401 402 403 404 405 406 407 408 409 410 411 412 413
		if (!le32_to_cpu(txp_cfg->mode))
			break;

		pg_tlv_hdr = (struct mwifiex_types_power_group *)
			((u8 *) txp_cfg +
			 sizeof(struct host_cmd_ds_txpwr_cfg));

		pg = (struct mwifiex_power_group *)
			((u8 *) pg_tlv_hdr +
			 sizeof(struct mwifiex_types_power_group));

		if (pg->power_max == pg->power_min)
			priv->tx_power_level = (u16) pg->power_min;
414 415 416
		break;
	default:
		dev_err(adapter->dev, "CMD_RESP: unknown cmd action %d\n",
417
			action);
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
		return 0;
	}
	dev_dbg(adapter->dev,
		"info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n",
	       priv->tx_power_level, priv->max_tx_power_level,
	       priv->min_tx_power_level);

	return 0;
}

/*
 * This function handles the command response of set/get MAC address.
 *
 * Handling includes saving the MAC address in driver.
 */
static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv,
					  struct host_cmd_ds_command *resp)
{
	struct host_cmd_ds_802_11_mac_address *cmd_mac_addr =
437
							&resp->params.mac_addr;
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472

	memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN);

	dev_dbg(priv->adapter->dev,
		"info: set mac address: %pM\n", priv->curr_addr);

	return 0;
}

/*
 * This function handles the command response of set/get MAC multicast
 * address.
 */
static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv,
					 struct host_cmd_ds_command *resp)
{
	return 0;
}

/*
 * This function handles the command response of get Tx rate query.
 *
 * Handling includes changing the header fields into CPU format
 * and saving the Tx rate and HT information parameters in driver.
 *
 * Both rate configuration and current data rate can be retrieved
 * with this request.
 */
static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv,
					    struct host_cmd_ds_command *resp)
{
	priv->tx_rate = resp->params.tx_rate.tx_rate;
	priv->tx_htinfo = resp->params.tx_rate.ht_info;
	if (!priv->is_data_rate_auto)
		priv->data_rate =
473
			mwifiex_index_to_data_rate(priv, priv->tx_rate,
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
						   priv->tx_htinfo);

	return 0;
}

/*
 * This function handles the command response of a deauthenticate
 * command.
 *
 * If the deauthenticated MAC matches the current BSS MAC, the connection
 * state is reset.
 */
static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv,
					     struct host_cmd_ds_command *resp)
{
	struct mwifiex_adapter *adapter = priv->adapter;

	adapter->dbg.num_cmd_deauth++;
	if (!memcmp(resp->params.deauth.mac_addr,
		    &priv->curr_bss_params.bss_descriptor.mac_address,
		    sizeof(resp->params.deauth.mac_addr)))
		mwifiex_reset_connect_state(priv);

	return 0;
}

/*
 * This function handles the command response of ad-hoc stop.
 *
 * The function resets the connection state in driver.
 */
static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv,
					  struct host_cmd_ds_command *resp)
{
	mwifiex_reset_connect_state(priv);
	return 0;
}

/*
 * This function handles the command response of set/get key material.
 *
 * Handling includes updating the driver parameters to reflect the
 * changes.
 */
static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
					   struct host_cmd_ds_command *resp)
{
	struct host_cmd_ds_802_11_key_material *key =
522
						&resp->params.key_material;
523 524

	if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) {
525
		if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) {
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
			dev_dbg(priv->adapter->dev, "info: key: GTK is set\n");
			priv->wpa_is_gtk_set = true;
			priv->scan_block = false;
		}
	}

	memset(priv->aes_key.key_param_set.key, 0,
	       sizeof(key->key_param_set.key));
	priv->aes_key.key_param_set.key_len = key->key_param_set.key_len;
	memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key,
	       le16_to_cpu(priv->aes_key.key_param_set.key_len));

	return 0;
}

/*
 * This function handles the command response of get 11d domain information.
 */
static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv,
					   struct host_cmd_ds_command *resp)
{
	struct host_cmd_ds_802_11d_domain_info_rsp *domain_info =
		&resp->params.domain_info_resp;
	struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain;
	u16 action = le16_to_cpu(domain_info->action);
551
	u8 no_of_triplet;
552

553 554 555
	no_of_triplet = (u8) ((le16_to_cpu(domain->header.len)
				- IEEE80211_COUNTRY_STRING_LEN)
			      / sizeof(struct ieee80211_country_ie_triplet));
556

557 558 559
	dev_dbg(priv->adapter->dev,
		"info: 11D Domain Info Resp: no_of_triplet=%d\n",
		no_of_triplet);
560 561 562

	if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) {
		dev_warn(priv->adapter->dev,
563 564
			 "11D: invalid number of triplets %d returned\n",
			 no_of_triplet);
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
		return -1;
	}

	switch (action) {
	case HostCmd_ACT_GEN_SET:  /* Proc Set Action */
		break;
	case HostCmd_ACT_GEN_GET:
		break;
	default:
		dev_err(priv->adapter->dev,
			"11D: invalid action:%d\n", domain_info->action);
		return -1;
	}

	return 0;
}

/*
 * This function handles the command response of get RF channel.
 *
 * Handling includes changing the header fields into CPU format
 * and saving the new channel in driver.
 */
static int mwifiex_ret_802_11_rf_channel(struct mwifiex_private *priv,
					 struct host_cmd_ds_command *resp,
590
					 u16 *data_buf)
591 592 593
{
	struct host_cmd_ds_802_11_rf_channel *rf_channel =
		&resp->params.rf_channel;
594
	u16 new_channel = le16_to_cpu(rf_channel->current_channel);
595

596
	if (priv->curr_bss_params.bss_descriptor.channel != new_channel) {
597
		dev_dbg(priv->adapter->dev, "cmd: Channel Switch: %d to %d\n",
598 599
			priv->curr_bss_params.bss_descriptor.channel,
			new_channel);
600
		/* Update the channel again */
601
		priv->curr_bss_params.bss_descriptor.channel = new_channel;
602 603
	}

604 605 606
	if (data_buf)
		*data_buf = new_channel;

607 608 609 610 611 612 613 614 615 616 617
	return 0;
}

/*
 * This function handles the command response of get extended version.
 *
 * Handling includes forming the extended version string and sending it
 * to application.
 */
static int mwifiex_ret_ver_ext(struct mwifiex_private *priv,
			       struct host_cmd_ds_command *resp,
618
			       struct host_cmd_ds_version_ext *version_ext)
619 620 621
{
	struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext;

622
	if (version_ext) {
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
		version_ext->version_str_sel = ver_ext->version_str_sel;
		memcpy(version_ext->version_str, ver_ext->version_str,
		       sizeof(char) * 128);
		memcpy(priv->version_str, ver_ext->version_str, 128);
	}
	return 0;
}

/*
 * This function handles the command response of register access.
 *
 * The register value and offset are returned to the user. For EEPROM
 * access, the byte count is also returned.
 */
static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp,
				  void *data_buf)
{
640 641
	struct mwifiex_ds_reg_rw *reg_rw;
	struct mwifiex_ds_read_eeprom *eeprom;
642 643 644 645 646 647 648 649 650 651
	union reg {
		struct host_cmd_ds_mac_reg_access *mac;
		struct host_cmd_ds_bbp_reg_access *bbp;
		struct host_cmd_ds_rf_reg_access *rf;
		struct host_cmd_ds_pmic_reg_access *pmic;
		struct host_cmd_ds_802_11_eeprom_access *eeprom;
	} r;

	if (!data_buf)
		return 0;
652

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
	reg_rw = data_buf;
	eeprom = data_buf;
	switch (type) {
	case HostCmd_CMD_MAC_REG_ACCESS:
		r.mac = (struct host_cmd_ds_mac_reg_access *)
			&resp->params.mac_reg;
		reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.mac->offset));
		reg_rw->value = r.mac->value;
		break;
	case HostCmd_CMD_BBP_REG_ACCESS:
		r.bbp = (struct host_cmd_ds_bbp_reg_access *)
			&resp->params.bbp_reg;
		reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.bbp->offset));
		reg_rw->value = cpu_to_le32((u32) r.bbp->value);
		break;
668

669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
	case HostCmd_CMD_RF_REG_ACCESS:
		r.rf = (struct host_cmd_ds_rf_reg_access *)
		       &resp->params.rf_reg;
		reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset));
		reg_rw->value = cpu_to_le32((u32) r.bbp->value);
		break;
	case HostCmd_CMD_PMIC_REG_ACCESS:
		r.pmic = (struct host_cmd_ds_pmic_reg_access *)
			 &resp->params.pmic_reg;
		reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.pmic->offset));
		reg_rw->value = cpu_to_le32((u32) r.pmic->value);
		break;
	case HostCmd_CMD_CAU_REG_ACCESS:
		r.rf = (struct host_cmd_ds_rf_reg_access *)
		       &resp->params.rf_reg;
		reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset));
		reg_rw->value = cpu_to_le32((u32) r.rf->value);
		break;
	case HostCmd_CMD_802_11_EEPROM_ACCESS:
		r.eeprom = (struct host_cmd_ds_802_11_eeprom_access *)
			   &resp->params.eeprom;
		pr_debug("info: EEPROM read len=%x\n", r.eeprom->byte_count);
		if (le16_to_cpu(eeprom->byte_count) <
		    le16_to_cpu(r.eeprom->byte_count)) {
			eeprom->byte_count = cpu_to_le16(0);
			pr_debug("info: EEPROM read length is too big\n");
695 696
			return -1;
		}
697 698 699 700 701 702 703 704 705
		eeprom->offset = r.eeprom->offset;
		eeprom->byte_count = r.eeprom->byte_count;
		if (le16_to_cpu(eeprom->byte_count) > 0)
			memcpy(&eeprom->value, &r.eeprom->value,
			       le16_to_cpu(r.eeprom->byte_count));

		break;
	default:
		return -1;
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
	}
	return 0;
}

/*
 * This function handles the command response of get IBSS coalescing status.
 *
 * If the received BSSID is different than the current one, the current BSSID,
 * beacon interval, ATIM window and ERP information are updated, along with
 * changing the ad-hoc state accordingly.
 */
static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv,
					      struct host_cmd_ds_command *resp)
{
	struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp =
721
					&(resp->params.ibss_coalescing);
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
	u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };

	if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET)
		return 0;

	dev_dbg(priv->adapter->dev,
		"info: new BSSID %pM\n", ibss_coal_resp->bssid);

	/* If rsp has NULL BSSID, Just return..... No Action */
	if (!memcmp(ibss_coal_resp->bssid, zero_mac, ETH_ALEN)) {
		dev_warn(priv->adapter->dev, "new BSSID is NULL\n");
		return 0;
	}

	/* If BSSID is diff, modify current BSS parameters */
	if (memcmp(priv->curr_bss_params.bss_descriptor.mac_address,
		   ibss_coal_resp->bssid, ETH_ALEN)) {
		/* BSSID */
		memcpy(priv->curr_bss_params.bss_descriptor.mac_address,
		       ibss_coal_resp->bssid, ETH_ALEN);

		/* Beacon Interval */
		priv->curr_bss_params.bss_descriptor.beacon_period
			= le16_to_cpu(ibss_coal_resp->beacon_interval);

		/* ERP Information */
		priv->curr_bss_params.bss_descriptor.erp_flags =
			(u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect);

		priv->adhoc_state = ADHOC_COALESCED;
	}

	return 0;
}

/*
 * This function handles the command responses.
 *
 * This is a generic function, which calls command specific
 * response handlers based on the command ID.
 */
763 764
int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
				struct host_cmd_ds_command *resp)
765 766 767 768 769 770 771
{
	int ret = 0;
	struct mwifiex_adapter *adapter = priv->adapter;
	void *data_buf = adapter->curr_cmd->data_buf;

	/* If the command is not successful, cleanup and return failure */
	if (resp->result != HostCmd_RESULT_OK) {
772
		mwifiex_process_cmdresp_error(priv, resp);
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
		return -1;
	}
	/* Command successful, handle response */
	switch (cmdresp_no) {
	case HostCmd_CMD_GET_HW_SPEC:
		ret = mwifiex_ret_get_hw_spec(priv, resp);
		break;
	case HostCmd_CMD_MAC_CONTROL:
		break;
	case HostCmd_CMD_802_11_MAC_ADDRESS:
		ret = mwifiex_ret_802_11_mac_address(priv, resp);
		break;
	case HostCmd_CMD_MAC_MULTICAST_ADR:
		ret = mwifiex_ret_mac_multicast_adr(priv, resp);
		break;
	case HostCmd_CMD_TX_RATE_CFG:
		ret = mwifiex_ret_tx_rate_cfg(priv, resp, data_buf);
		break;
	case HostCmd_CMD_802_11_SCAN:
792 793
		ret = mwifiex_ret_802_11_scan(priv, resp);
		adapter->curr_cmd->wait_q_enabled = false;
794 795
		break;
	case HostCmd_CMD_802_11_BG_SCAN_QUERY:
796
		ret = mwifiex_ret_802_11_scan(priv, resp);
797 798 799 800
		dev_dbg(adapter->dev,
			"info: CMD_RESP: BG_SCAN result is ready!\n");
		break;
	case HostCmd_CMD_TXPWR_CFG:
801
		ret = mwifiex_ret_tx_power_cfg(priv, resp);
802 803 804 805 806 807 808 809
		break;
	case HostCmd_CMD_802_11_PS_MODE_ENH:
		ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf);
		break;
	case HostCmd_CMD_802_11_HS_CFG_ENH:
		ret = mwifiex_ret_802_11_hs_cfg(priv, resp);
		break;
	case HostCmd_CMD_802_11_ASSOCIATE:
810
		ret = mwifiex_ret_802_11_associate(priv, resp);
811 812 813 814 815 816
		break;
	case HostCmd_CMD_802_11_DEAUTHENTICATE:
		ret = mwifiex_ret_802_11_deauthenticate(priv, resp);
		break;
	case HostCmd_CMD_802_11_AD_HOC_START:
	case HostCmd_CMD_802_11_AD_HOC_JOIN:
817
		ret = mwifiex_ret_802_11_ad_hoc(priv, resp);
818 819 820 821 822 823 824 825
		break;
	case HostCmd_CMD_802_11_AD_HOC_STOP:
		ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp);
		break;
	case HostCmd_CMD_802_11_GET_LOG:
		ret = mwifiex_ret_get_log(priv, resp, data_buf);
		break;
	case HostCmd_CMD_RSSI_INFO:
826
		ret = mwifiex_ret_802_11_rssi_info(priv, resp);
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
		break;
	case HostCmd_CMD_802_11_SNMP_MIB:
		ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf);
		break;
	case HostCmd_CMD_802_11_TX_RATE_QUERY:
		ret = mwifiex_ret_802_11_tx_rate_query(priv, resp);
		break;
	case HostCmd_CMD_802_11_RF_CHANNEL:
		ret = mwifiex_ret_802_11_rf_channel(priv, resp, data_buf);
		break;
	case HostCmd_CMD_VERSION_EXT:
		ret = mwifiex_ret_ver_ext(priv, resp, data_buf);
		break;
	case HostCmd_CMD_FUNC_INIT:
	case HostCmd_CMD_FUNC_SHUTDOWN:
		break;
	case HostCmd_CMD_802_11_KEY_MATERIAL:
		ret = mwifiex_ret_802_11_key_material(priv, resp);
		break;
	case HostCmd_CMD_802_11D_DOMAIN_INFO:
		ret = mwifiex_ret_802_11d_domain_info(priv, resp);
		break;
	case HostCmd_CMD_11N_ADDBA_REQ:
		ret = mwifiex_ret_11n_addba_req(priv, resp);
		break;
	case HostCmd_CMD_11N_DELBA:
		ret = mwifiex_ret_11n_delba(priv, resp);
		break;
	case HostCmd_CMD_11N_ADDBA_RSP:
		ret = mwifiex_ret_11n_addba_resp(priv, resp);
		break;
	case HostCmd_CMD_RECONFIGURE_TX_BUFF:
		adapter->tx_buf_size = (u16) le16_to_cpu(resp->params.
							     tx_buf.buff_size);
861 862 863
		adapter->tx_buf_size = (adapter->tx_buf_size
					/ MWIFIEX_SDIO_BLOCK_SIZE)
				       * MWIFIEX_SDIO_BLOCK_SIZE;
864 865 866
		adapter->curr_tx_buf_size = adapter->tx_buf_size;
		dev_dbg(adapter->dev,
			"cmd: max_tx_buf_size=%d, tx_buf_size=%d\n",
867
			adapter->max_tx_buf_size, adapter->tx_buf_size);
868 869 870

		if (adapter->if_ops.update_mp_end_port)
			adapter->if_ops.update_mp_end_port(adapter,
871
				le16_to_cpu(resp->params.tx_buf.mp_end_port));
872 873
		break;
	case HostCmd_CMD_AMSDU_AGGR_CTRL:
874
		ret = mwifiex_ret_amsdu_aggr_ctrl(resp, data_buf);
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
		break;
	case HostCmd_CMD_WMM_GET_STATUS:
		ret = mwifiex_ret_wmm_get_status(priv, resp);
		break;
	case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS:
		ret = mwifiex_ret_ibss_coalescing_status(priv, resp);
		break;
	case HostCmd_CMD_MAC_REG_ACCESS:
	case HostCmd_CMD_BBP_REG_ACCESS:
	case HostCmd_CMD_RF_REG_ACCESS:
	case HostCmd_CMD_PMIC_REG_ACCESS:
	case HostCmd_CMD_CAU_REG_ACCESS:
	case HostCmd_CMD_802_11_EEPROM_ACCESS:
		ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf);
		break;
	case HostCmd_CMD_SET_BSS_MODE:
		break;
	case HostCmd_CMD_11N_CFG:
893
		ret = mwifiex_ret_11n_cfg(resp, data_buf);
894
		break;
895 896
	case HostCmd_CMD_PCIE_DESC_DETAILS:
		break;
897 898
	default:
		dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
899
			resp->command);
900 901 902 903 904
		break;
	}

	return ret;
}