sta_cmd.c 71.1 KB
Newer Older
1 2 3
/*
 * Marvell Wireless LAN device driver: station command handling
 *
X
Xinming Hu 已提交
4
 * Copyright (C) 2011-2014, Marvell International Ltd.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * 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"
27
#include "11ac.h"
28

29 30 31 32
static bool drcs;
module_param(drcs, bool, 0644);
MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0");

33 34 35 36
static bool disable_auto_ds;
module_param(disable_auto_ds, bool, 0);
MODULE_PARM_DESC(disable_auto_ds,
		 "deepsleep enabled=0(default), deepsleep disabled=1");
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
/*
 * This function prepares command to set/get RSSI information.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting data/beacon average factors
 *      - Resetting SNR/NF/RSSI values in private structure
 *      - Ensuring correct endian-ness
 */
static int
mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv,
			     struct host_cmd_ds_command *cmd, u16 cmd_action)
{
	cmd->command = cpu_to_le16(HostCmd_CMD_RSSI_INFO);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) +
				S_DS_GEN);
	cmd->params.rssi_info.action = cpu_to_le16(cmd_action);
	cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor);
	cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor);

	/* Reset SNR/NF/RSSI values in private structure */
	priv->data_rssi_last = 0;
	priv->data_nf_last = 0;
	priv->data_rssi_avg = 0;
	priv->data_nf_avg = 0;
	priv->bcn_rssi_last = 0;
	priv->bcn_nf_last = 0;
	priv->bcn_rssi_avg = 0;
	priv->bcn_nf_avg = 0;

	return 0;
}

/*
 * This function prepares command to set MAC control.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Ensuring correct endian-ness
 */
static int mwifiex_cmd_mac_control(struct mwifiex_private *priv,
				   struct host_cmd_ds_command *cmd,
79
				   u16 cmd_action, u16 *action)
80 81 82 83
{
	struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl;

	if (cmd_action != HostCmd_ACT_GEN_SET) {
84 85
		mwifiex_dbg(priv->adapter, ERROR,
			    "mac_control: only support set cmd\n");
86 87 88 89 90 91
		return -1;
	}

	cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL);
	cmd->size =
		cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN);
92
	mac_ctrl->action = cpu_to_le16(*action);
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

	return 0;
}

/*
 * This function prepares command to set/get SNMP MIB.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting SNMP MIB OID number and value
 *        (as required)
 *      - Ensuring correct endian-ness
 *
 * The following SNMP MIB OIDs are supported -
 *      - FRAG_THRESH_I     : Fragmentation threshold
 *      - RTS_THRESH_I      : RTS threshold
 *      - SHORT_RETRY_LIM_I : Short retry limit
 *      - DOT11D_I          : 11d support
 */
static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv,
				       struct host_cmd_ds_command *cmd,
				       u16 cmd_action, u32 cmd_oid,
115
				       u16 *ul_temp)
116 117 118
{
	struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib;

119 120
	mwifiex_dbg(priv->adapter, CMD,
		    "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid);
121 122
	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib)
123
				- 1 + S_DS_GEN);
124

125
	snmp_mib->oid = cpu_to_le16((u16)cmd_oid);
126 127 128
	if (cmd_action == HostCmd_ACT_GEN_GET) {
		snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET);
		snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE);
129 130 131 132 133 134
		le16_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE);
	} else if (cmd_action == HostCmd_ACT_GEN_SET) {
		snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET);
		snmp_mib->buf_size = cpu_to_le16(sizeof(u16));
		*((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp);
		le16_add_cpu(&cmd->size, sizeof(u16));
135 136
	}

137 138 139 140 141
	mwifiex_dbg(priv->adapter, CMD,
		    "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t"
		    "OIDSize=0x%x, Value=0x%x\n",
		    cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size),
		    le16_to_cpu(*(__le16 *)snmp_mib->value));
142 143 144 145 146 147 148 149 150 151 152
	return 0;
}

/*
 * This function prepares command to get log.
 *
 * Preparation includes -
 *      - Setting command ID and proper size
 *      - Ensuring correct endian-ness
 */
static int
153
mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd)
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
{
	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) +
				S_DS_GEN);
	return 0;
}

/*
 * This function prepares command to set/get Tx data rate configuration.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting configuration index, rate scope and rate drop pattern
 *        parameters (as required)
 *      - Ensuring correct endian-ness
 */
static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv,
				   struct host_cmd_ds_command *cmd,
172
				   u16 cmd_action, u16 *pbitmap_rates)
173 174 175 176 177 178 179 180 181 182 183 184 185 186
{
	struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg;
	struct mwifiex_rate_scope *rate_scope;
	struct mwifiex_rate_drop_pattern *rate_drop;
	u32 i;

	cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG);

	rate_cfg->action = cpu_to_le16(cmd_action);
	rate_cfg->cfg_index = 0;

	rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg +
		      sizeof(struct host_cmd_ds_tx_rate_cfg));
	rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE);
187 188
	rate_scope->length = cpu_to_le16
		(sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header));
189 190 191 192 193 194 195 196
	if (pbitmap_rates != NULL) {
		rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]);
		rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]);
		for (i = 0;
		     i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16);
		     i++)
			rate_scope->ht_mcs_rate_bitmap[i] =
				cpu_to_le16(pbitmap_rates[2 + i]);
197 198 199 200 201 202 203
		if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
			for (i = 0;
			     i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
			     i++)
				rate_scope->vht_mcs_rate_bitmap[i] =
					cpu_to_le16(pbitmap_rates[10 + i]);
		}
204 205 206 207 208 209 210 211 212 213
	} else {
		rate_scope->hr_dsss_rate_bitmap =
			cpu_to_le16(priv->bitmap_rates[0]);
		rate_scope->ofdm_rate_bitmap =
			cpu_to_le16(priv->bitmap_rates[1]);
		for (i = 0;
		     i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16);
		     i++)
			rate_scope->ht_mcs_rate_bitmap[i] =
				cpu_to_le16(priv->bitmap_rates[2 + i]);
214 215 216 217 218 219 220
		if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
			for (i = 0;
			     i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
			     i++)
				rate_scope->vht_mcs_rate_bitmap[i] =
					cpu_to_le16(priv->bitmap_rates[10 + i]);
		}
221 222 223
	}

	rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope +
224
					     sizeof(struct mwifiex_rate_scope));
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
	rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL);
	rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode));
	rate_drop->rate_drop_mode = 0;

	cmd->size =
		cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) +
			    sizeof(struct mwifiex_rate_scope) +
			    sizeof(struct mwifiex_rate_drop_pattern));

	return 0;
}

/*
 * This function prepares command to set/get Tx power configuration.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting Tx power mode, power group TLV
 *        (as required)
 *      - Ensuring correct endian-ness
 */
246
static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd,
247 248
				    u16 cmd_action,
				    struct host_cmd_ds_txpwr_cfg *txp)
249
{
250
	struct mwifiex_types_power_group *pg_tlv;
251 252 253 254 255 256 257 258 259
	struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg;

	cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG);
	cmd->size =
		cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg));
	switch (cmd_action) {
	case HostCmd_ACT_GEN_SET:
		if (txp->mode) {
			pg_tlv = (struct mwifiex_types_power_group
260
				  *) ((unsigned long) txp +
261
				     sizeof(struct host_cmd_ds_txpwr_cfg));
262
			memmove(cmd_txp_cfg, txp,
263 264
				sizeof(struct host_cmd_ds_txpwr_cfg) +
				sizeof(struct mwifiex_types_power_group) +
265
				le16_to_cpu(pg_tlv->length));
266 267 268 269 270 271

			pg_tlv = (struct mwifiex_types_power_group *) ((u8 *)
				  cmd_txp_cfg +
				  sizeof(struct host_cmd_ds_txpwr_cfg));
			cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) +
				  sizeof(struct mwifiex_types_power_group) +
272
				  le16_to_cpu(pg_tlv->length));
273
		} else {
274
			memmove(cmd_txp_cfg, txp, sizeof(*txp));
275 276 277 278 279 280 281 282 283 284 285
		}
		cmd_txp_cfg->action = cpu_to_le16(cmd_action);
		break;
	case HostCmd_ACT_GEN_GET:
		cmd_txp_cfg->action = cpu_to_le16(cmd_action);
		break;
	}

	return 0;
}

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
/*
 * This function prepares command to get RF Tx power.
 */
static int mwifiex_cmd_rf_tx_power(struct mwifiex_private *priv,
				   struct host_cmd_ds_command *cmd,
				   u16 cmd_action, void *data_buf)
{
	struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp;

	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr)
				+ S_DS_GEN);
	cmd->command = cpu_to_le16(HostCmd_CMD_RF_TX_PWR);
	txp->action = cpu_to_le16(cmd_action);

	return 0;
}

303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
/*
 * This function prepares command to set rf antenna.
 */
static int mwifiex_cmd_rf_antenna(struct mwifiex_private *priv,
				  struct host_cmd_ds_command *cmd,
				  u16 cmd_action,
				  struct mwifiex_ds_ant_cfg *ant_cfg)
{
	struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo;
	struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso;

	cmd->command = cpu_to_le16(HostCmd_CMD_RF_ANTENNA);

	if (cmd_action != HostCmd_ACT_GEN_SET)
		return 0;

	if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) {
		cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) +
					S_DS_GEN);
		ant_mimo->action_tx = cpu_to_le16(HostCmd_ACT_SET_TX);
		ant_mimo->tx_ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant);
		ant_mimo->action_rx = cpu_to_le16(HostCmd_ACT_SET_RX);
		ant_mimo->rx_ant_mode = cpu_to_le16((u16)ant_cfg->rx_ant);
	} else {
		cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) +
					S_DS_GEN);
		ant_siso->action = cpu_to_le16(HostCmd_ACT_SET_BOTH);
		ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant);
	}

	return 0;
}

336 337 338 339 340 341 342 343 344
/*
 * This function prepares command to set Host Sleep configuration.
 *
 * Preparation includes -
 *      - Setting command ID and proper size
 *      - Setting Host Sleep action, conditions, ARP filters
 *        (as required)
 *      - Ensuring correct endian-ness
 */
345 346 347 348 349
static int
mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
			  struct host_cmd_ds_command *cmd,
			  u16 cmd_action,
			  struct mwifiex_hs_config_param *hscfg_param)
350 351 352
{
	struct mwifiex_adapter *adapter = priv->adapter;
	struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg;
353
	bool hs_activate = false;
354

355
	if (!hscfg_param)
356 357 358 359 360
		/* New Activate command */
		hs_activate = true;
	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH);

	if (!hs_activate &&
361
	    (hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL)) &&
362 363
	    ((adapter->arp_filter_size > 0) &&
	     (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) {
364 365 366
		mwifiex_dbg(adapter, CMD,
			    "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n",
			    adapter->arp_filter_size);
367 368 369
		memcpy(((u8 *) hs_cfg) +
		       sizeof(struct host_cmd_ds_802_11_hs_cfg_enh),
		       adapter->arp_filter, adapter->arp_filter_size);
370 371 372 373
		cmd->size = cpu_to_le16
				(adapter->arp_filter_size +
				 sizeof(struct host_cmd_ds_802_11_hs_cfg_enh)
				+ S_DS_GEN);
374 375
	} else {
		cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct
376
						host_cmd_ds_802_11_hs_cfg_enh));
377 378 379
	}
	if (hs_activate) {
		hs_cfg->action = cpu_to_le16(HS_ACTIVATE);
380
		hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED);
381 382
	} else {
		hs_cfg->action = cpu_to_le16(HS_CONFIGURE);
383 384 385
		hs_cfg->params.hs_config.conditions = hscfg_param->conditions;
		hs_cfg->params.hs_config.gpio = hscfg_param->gpio;
		hs_cfg->params.hs_config.gap = hscfg_param->gap;
386 387 388 389 390
		mwifiex_dbg(adapter, CMD,
			    "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n",
			    hs_cfg->params.hs_config.conditions,
			    hs_cfg->params.hs_config.gpio,
			    hs_cfg->params.hs_config.gap);
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
	}

	return 0;
}

/*
 * This function prepares command to set/get MAC address.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting MAC address (for SET only)
 *      - Ensuring correct endian-ness
 */
static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv,
					  struct host_cmd_ds_command *cmd,
					  u16 cmd_action)
{
	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) +
				S_DS_GEN);
	cmd->result = 0;

	cmd->params.mac_addr.action = cpu_to_le16(cmd_action);

	if (cmd_action == HostCmd_ACT_GEN_SET)
		memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr,
		       ETH_ALEN);
	return 0;
}

/*
 * This function prepares command to set MAC multicast address.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting MAC multicast address
 *      - Ensuring correct endian-ness
 */
429 430 431 432
static int
mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd,
			      u16 cmd_action,
			      struct mwifiex_multicast_list *mcast_list)
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
{
	struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr;

	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) +
				S_DS_GEN);
	cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR);

	mcast_addr->action = cpu_to_le16(cmd_action);
	mcast_addr->num_of_adrs =
		cpu_to_le16((u16) mcast_list->num_multicast_addr);
	memcpy(mcast_addr->mac_list, mcast_list->mac_list,
	       mcast_list->num_multicast_addr * ETH_ALEN);

	return 0;
}

/*
 * This function prepares command to deauthenticate.
 *
 * Preparation includes -
 *      - Setting command ID and proper size
 *      - Setting AP MAC address and reason code
 *      - Ensuring correct endian-ness
 */
static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv,
					     struct host_cmd_ds_command *cmd,
459
					     u8 *mac)
460 461 462 463 464 465 466 467
{
	struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth;

	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate)
				+ S_DS_GEN);

	/* Set AP MAC address */
468
	memcpy(deauth->mac_addr, mac, ETH_ALEN);
469

470
	mwifiex_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr);
471 472 473 474 475 476 477 478 479 480 481 482 483

	deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);

	return 0;
}

/*
 * This function prepares command to stop Ad-Hoc network.
 *
 * Preparation includes -
 *      - Setting command ID and proper size
 *      - Ensuring correct endian-ness
 */
484
static int mwifiex_cmd_802_11_ad_hoc_stop(struct host_cmd_ds_command *cmd)
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
{
	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP);
	cmd->size = cpu_to_le16(S_DS_GEN);
	return 0;
}

/*
 * This function sets WEP key(s) to key parameter TLV(s).
 *
 * Multi-key parameter TLVs are supported, so we can send multiple
 * WEP keys in a single buffer.
 */
static int
mwifiex_set_keyparamset_wep(struct mwifiex_private *priv,
			    struct mwifiex_ie_type_key_param_set *key_param_set,
			    u16 *key_param_len)
{
502
	int cur_key_param_len;
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
	u8 i;

	/* Multi-key_param_set TLV is supported */
	for (i = 0; i < NUM_WEP_KEYS; i++) {
		if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) ||
		    (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) {
			key_param_set->type =
				cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
/* Key_param_set WEP fixed length */
#define KEYPARAMSET_WEP_FIXED_LEN 8
			key_param_set->length = cpu_to_le16((u16)
					(priv->wep_key[i].
					 key_length +
					 KEYPARAMSET_WEP_FIXED_LEN));
			key_param_set->key_type_id =
				cpu_to_le16(KEY_TYPE_ID_WEP);
			key_param_set->key_info =
520 521
				cpu_to_le16(KEY_ENABLED | KEY_UNICAST |
					    KEY_MCAST);
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
			key_param_set->key_len =
				cpu_to_le16(priv->wep_key[i].key_length);
			/* Set WEP key index */
			key_param_set->key[0] = i;
			/* Set default Tx key flag */
			if (i ==
			    (priv->
			     wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK))
				key_param_set->key[1] = 1;
			else
				key_param_set->key[1] = 0;
			memmove(&key_param_set->key[2],
				priv->wep_key[i].key_material,
				priv->wep_key[i].key_length);

			cur_key_param_len = priv->wep_key[i].key_length +
				KEYPARAMSET_WEP_FIXED_LEN +
				sizeof(struct mwifiex_ie_types_header);
			*key_param_len += (u16) cur_key_param_len;
			key_param_set =
				(struct mwifiex_ie_type_key_param_set *)
						((u8 *)key_param_set +
544
						 cur_key_param_len);
545 546 547
		} else if (!priv->wep_key[i].key_length) {
			continue;
		} else {
548 549 550
			mwifiex_dbg(priv->adapter, ERROR,
				    "key%d Length = %d is incorrect\n",
				    (i + 1), priv->wep_key[i].key_length);
551 552 553 554 555 556 557
			return -1;
		}
	}

	return 0;
}

558 559 560 561 562 563 564 565 566 567 568 569
/* This function populates key material v2 command
 * to set network key for AES & CMAC AES.
 */
static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv,
				  struct host_cmd_ds_command *cmd,
				  struct mwifiex_ds_encrypt_key *enc_key,
				  struct host_cmd_ds_802_11_key_material_v2 *km)
{
	struct mwifiex_adapter *adapter = priv->adapter;
	u16 size, len = KEY_PARAMS_FIXED_LEN;

	if (enc_key->is_igtk_key) {
570 571
		mwifiex_dbg(adapter, INFO,
			    "%s: Set CMAC AES Key\n", __func__);
572 573 574 575 576 577 578 579 580 581 582 583
		if (enc_key->is_rx_seq_valid)
			memcpy(km->key_param_set.key_params.cmac_aes.ipn,
			       enc_key->pn, enc_key->pn_len);
		km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST);
		km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK);
		km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC;
		km->key_param_set.key_params.cmac_aes.key_len =
					  cpu_to_le16(enc_key->key_len);
		memcpy(km->key_param_set.key_params.cmac_aes.key,
		       enc_key->key_material, enc_key->key_len);
		len += sizeof(struct mwifiex_cmac_aes_param);
	} else {
584 585
		mwifiex_dbg(adapter, INFO,
			    "%s: Set AES Key\n", __func__);
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
		if (enc_key->is_rx_seq_valid)
			memcpy(km->key_param_set.key_params.aes.pn,
			       enc_key->pn, enc_key->pn_len);
		km->key_param_set.key_type = KEY_TYPE_ID_AES;
		km->key_param_set.key_params.aes.key_len =
					  cpu_to_le16(enc_key->key_len);
		memcpy(km->key_param_set.key_params.aes.key,
		       enc_key->key_material, enc_key->key_len);
		len += sizeof(struct mwifiex_aes_param);
	}

	km->key_param_set.len = cpu_to_le16(len);
	size = len + sizeof(struct mwifiex_ie_types_header) +
	       sizeof(km->action) + S_DS_GEN;
	cmd->size = cpu_to_le16(size);

	return 0;
}

/* This function prepares command to set/get/reset network key(s).
 * This function prepares key material command for V2 format.
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting WEP keys, WAPI keys or WPA keys along with required
 *        encryption (TKIP, AES) (as required)
 *      - Ensuring correct endian-ness
 */
static int
mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv,
				   struct host_cmd_ds_command *cmd,
				   u16 cmd_action, u32 cmd_oid,
				   struct mwifiex_ds_encrypt_key *enc_key)
{
	struct mwifiex_adapter *adapter = priv->adapter;
	u8 *mac = enc_key->mac_addr;
	u16 key_info, len = KEY_PARAMS_FIXED_LEN;
	struct host_cmd_ds_802_11_key_material_v2 *km =
						&cmd->params.key_material_v2;

	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL);
	km->action = cpu_to_le16(cmd_action);

	if (cmd_action == HostCmd_ACT_GEN_GET) {
629
		mwifiex_dbg(adapter, INFO, "%s: Get key\n", __func__);
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
		km->key_param_set.key_idx =
					enc_key->key_index & KEY_INDEX_MASK;
		km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
		km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
		memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);

		if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST)
			key_info = KEY_UNICAST;
		else
			key_info = KEY_MCAST;

		if (enc_key->is_igtk_key)
			key_info |= KEY_IGTK;

		km->key_param_set.key_info = cpu_to_le16(key_info);

		cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
					S_DS_GEN + KEY_PARAMS_FIXED_LEN +
					sizeof(km->action));
		return 0;
	}

	memset(&km->key_param_set, 0,
	       sizeof(struct mwifiex_ie_type_key_param_set_v2));

	if (enc_key->key_disable) {
656
		mwifiex_dbg(adapter, INFO, "%s: Remove key\n", __func__);
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
		km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE);
		km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
		km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
		km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
		key_info = KEY_MCAST | KEY_UNICAST;
		km->key_param_set.key_info = cpu_to_le16(key_info);
		memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
		cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
					S_DS_GEN + KEY_PARAMS_FIXED_LEN +
					sizeof(km->action));
		return 0;
	}

	km->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
	km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
	km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
	key_info = KEY_ENABLED;
	memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);

	if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) {
677
		mwifiex_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__);
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
		len += sizeof(struct mwifiex_wep_param);
		km->key_param_set.len = cpu_to_le16(len);
		km->key_param_set.key_type = KEY_TYPE_ID_WEP;

		if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
				key_info |= KEY_MCAST | KEY_UNICAST;
		} else {
			if (enc_key->is_current_wep_key) {
				key_info |= KEY_MCAST | KEY_UNICAST;
				if (km->key_param_set.key_idx ==
				    (priv->wep_key_curr_index & KEY_INDEX_MASK))
					key_info |= KEY_DEFAULT;
			} else {
				if (mac) {
					if (is_broadcast_ether_addr(mac))
						key_info |= KEY_MCAST;
					else
						key_info |= KEY_UNICAST |
							    KEY_DEFAULT;
				} else {
					key_info |= KEY_MCAST;
				}
			}
		}
		km->key_param_set.key_info = cpu_to_le16(key_info);

		km->key_param_set.key_params.wep.key_len =
						  cpu_to_le16(enc_key->key_len);
		memcpy(km->key_param_set.key_params.wep.key,
		       enc_key->key_material, enc_key->key_len);

		cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
					len + sizeof(km->action) + S_DS_GEN);
		return 0;
	}

	if (is_broadcast_ether_addr(mac))
		key_info |= KEY_MCAST | KEY_RX_KEY;
	else
		key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY;

	if (enc_key->is_wapi_key) {
720
		mwifiex_dbg(adapter, INFO, "%s: Set WAPI Key\n", __func__);
721 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
		km->key_param_set.key_type = KEY_TYPE_ID_WAPI;
		memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn,
		       PN_LEN);
		km->key_param_set.key_params.wapi.key_len =
						cpu_to_le16(enc_key->key_len);
		memcpy(km->key_param_set.key_params.wapi.key,
		       enc_key->key_material, enc_key->key_len);
		if (is_broadcast_ether_addr(mac))
			priv->sec_info.wapi_key_on = true;

		if (!priv->sec_info.wapi_key_on)
			key_info |= KEY_DEFAULT;
		km->key_param_set.key_info = cpu_to_le16(key_info);

		len += sizeof(struct mwifiex_wapi_param);
		km->key_param_set.len = cpu_to_le16(len);
		cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
					len + sizeof(km->action) + S_DS_GEN);
		return 0;
	}

	if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
		key_info |= KEY_DEFAULT;
		/* Enable unicast bit for WPA-NONE/ADHOC_AES */
		if (!priv->sec_info.wpa2_enabled &&
		    !is_broadcast_ether_addr(mac))
			key_info |= KEY_UNICAST;
	} else {
		/* Enable default key for WPA/WPA2 */
		if (!priv->wpa_is_gtk_set)
			key_info |= KEY_DEFAULT;
	}

	km->key_param_set.key_info = cpu_to_le16(key_info);

	if (enc_key->key_len == WLAN_KEY_LEN_CCMP)
		return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km);

	if (enc_key->key_len == WLAN_KEY_LEN_TKIP) {
760 761
		mwifiex_dbg(adapter, INFO,
			    "%s: Set TKIP Key\n", __func__);
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
		if (enc_key->is_rx_seq_valid)
			memcpy(km->key_param_set.key_params.tkip.pn,
			       enc_key->pn, enc_key->pn_len);
		km->key_param_set.key_type = KEY_TYPE_ID_TKIP;
		km->key_param_set.key_params.tkip.key_len =
						cpu_to_le16(enc_key->key_len);
		memcpy(km->key_param_set.key_params.tkip.key,
		       enc_key->key_material, enc_key->key_len);

		len += sizeof(struct mwifiex_tkip_param);
		km->key_param_set.len = cpu_to_le16(len);
		cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
					len + sizeof(km->action) + S_DS_GEN);
	}

	return 0;
}

780 781
/*
 * This function prepares command to set/get/reset network key(s).
782
 * This function prepares key material command for V1 format.
783 784 785 786 787 788 789
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting WEP keys, WAPI keys or WPA keys along with required
 *        encryption (TKIP, AES) (as required)
 *      - Ensuring correct endian-ness
 */
790
static int
791 792 793 794
mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv,
				   struct host_cmd_ds_command *cmd,
				   u16 cmd_action, u32 cmd_oid,
				   struct mwifiex_ds_encrypt_key *enc_key)
795 796 797
{
	struct host_cmd_ds_802_11_key_material *key_material =
		&cmd->params.key_material;
798 799
	struct host_cmd_tlv_mac_addr *tlv_mac;
	u16 key_param_len = 0, cmd_size;
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
	int ret = 0;

	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL);
	key_material->action = cpu_to_le16(cmd_action);

	if (cmd_action == HostCmd_ACT_GEN_GET) {
		cmd->size =
			cpu_to_le16(sizeof(key_material->action) + S_DS_GEN);
		return ret;
	}

	if (!enc_key) {
		memset(&key_material->key_param_set, 0,
		       (NUM_WEP_KEYS *
			sizeof(struct mwifiex_ie_type_key_param_set)));
		ret = mwifiex_set_keyparamset_wep(priv,
						  &key_material->key_param_set,
						  &key_param_len);
		cmd->size = cpu_to_le16(key_param_len +
				    sizeof(key_material->action) + S_DS_GEN);
		return ret;
	} else
		memset(&key_material->key_param_set, 0,
		       sizeof(struct mwifiex_ie_type_key_param_set));
	if (enc_key->is_wapi_key) {
825
		mwifiex_dbg(priv->adapter, INFO, "info: Set WAPI Key\n");
826
		key_material->key_param_set.key_type_id =
827
						cpu_to_le16(KEY_TYPE_ID_WAPI);
828 829
		if (cmd_oid == KEY_INFO_ENABLED)
			key_material->key_param_set.key_info =
830
						cpu_to_le16(KEY_ENABLED);
831 832
		else
			key_material->key_param_set.key_info =
833
						cpu_to_le16(!KEY_ENABLED);
834 835 836 837 838 839 840 841

		key_material->key_param_set.key[0] = enc_key->key_index;
		if (!priv->sec_info.wapi_key_on)
			key_material->key_param_set.key[1] = 1;
		else
			/* set 0 when re-key */
			key_material->key_param_set.key[1] = 0;

842
		if (!is_broadcast_ether_addr(enc_key->mac_addr)) {
843 844
			/* WAPI pairwise key: unicast */
			key_material->key_param_set.key_info |=
845
				cpu_to_le16(KEY_UNICAST);
846 847
		} else {	/* WAPI group key: multicast */
			key_material->key_param_set.key_info |=
848
				cpu_to_le16(KEY_MCAST);
849 850 851 852
			priv->sec_info.wapi_key_on = true;
		}

		key_material->key_param_set.type =
853
					cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
854
		key_material->key_param_set.key_len =
855
						cpu_to_le16(WAPI_KEY_LEN);
856 857 858
		memcpy(&key_material->key_param_set.key[2],
		       enc_key->key_material, enc_key->key_len);
		memcpy(&key_material->key_param_set.key[2 + enc_key->key_len],
Y
Ying Luo 已提交
859
		       enc_key->pn, PN_LEN);
860 861 862 863 864
		key_material->key_param_set.length =
			cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN);

		key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) +
				 sizeof(struct mwifiex_ie_types_header);
865 866
		cmd->size = cpu_to_le16(sizeof(key_material->action)
					+ S_DS_GEN +  key_param_len);
867 868 869
		return ret;
	}
	if (enc_key->key_len == WLAN_KEY_LEN_CCMP) {
870
		if (enc_key->is_igtk_key) {
871
			mwifiex_dbg(priv->adapter, CMD, "cmd: CMAC_AES\n");
872 873 874 875 876 877 878 879 880 881 882 883
			key_material->key_param_set.key_type_id =
					cpu_to_le16(KEY_TYPE_ID_AES_CMAC);
			if (cmd_oid == KEY_INFO_ENABLED)
				key_material->key_param_set.key_info =
						cpu_to_le16(KEY_ENABLED);
			else
				key_material->key_param_set.key_info =
						cpu_to_le16(!KEY_ENABLED);

			key_material->key_param_set.key_info |=
							cpu_to_le16(KEY_IGTK);
		} else {
884
			mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_AES\n");
885
			key_material->key_param_set.key_type_id =
886
						cpu_to_le16(KEY_TYPE_ID_AES);
887 888
			if (cmd_oid == KEY_INFO_ENABLED)
				key_material->key_param_set.key_info =
889
						cpu_to_le16(KEY_ENABLED);
890 891
			else
				key_material->key_param_set.key_info =
892
						cpu_to_le16(!KEY_ENABLED);
893

894
			if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST)
895
				/* AES pairwise key: unicast */
896
				key_material->key_param_set.key_info |=
897
						cpu_to_le16(KEY_UNICAST);
898 899
			else	/* AES group key: multicast */
				key_material->key_param_set.key_info |=
900
							cpu_to_le16(KEY_MCAST);
901
		}
902
	} else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) {
903
		mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_TKIP\n");
904
		key_material->key_param_set.key_type_id =
905
						cpu_to_le16(KEY_TYPE_ID_TKIP);
906
		key_material->key_param_set.key_info =
907
						cpu_to_le16(KEY_ENABLED);
908 909 910 911

		if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST)
				/* TKIP pairwise key: unicast */
			key_material->key_param_set.key_info |=
912
						cpu_to_le16(KEY_UNICAST);
913 914
		else		/* TKIP group key: multicast */
			key_material->key_param_set.key_info |=
915
							cpu_to_le16(KEY_MCAST);
916 917 918 919
	}

	if (key_material->key_param_set.key_type_id) {
		key_material->key_param_set.type =
920
					cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
921
		key_material->key_param_set.key_len =
922
					cpu_to_le16((u16) enc_key->key_len);
923 924 925 926 927 928
		memcpy(key_material->key_param_set.key, enc_key->key_material,
		       enc_key->key_len);
		key_material->key_param_set.length =
			cpu_to_le16((u16) enc_key->key_len +
				    KEYPARAMSET_FIXED_LEN);

929
		key_param_len = (u16)(enc_key->key_len + KEYPARAMSET_FIXED_LEN)
930
				+ sizeof(struct mwifiex_ie_types_header);
931

932 933 934 935 936 937 938
		if (le16_to_cpu(key_material->key_param_set.key_type_id) ==
							KEY_TYPE_ID_AES_CMAC) {
			struct mwifiex_cmac_param *param =
					(void *)key_material->key_param_set.key;

			memcpy(param->ipn, enc_key->pn, IGTK_PN_LEN);
			memcpy(param->key, enc_key->key_material,
939
			       WLAN_KEY_LEN_AES_CMAC);
940 941 942 943 944 945 946 947 948 949

			key_param_len = sizeof(struct mwifiex_cmac_param);
			key_material->key_param_set.key_len =
						cpu_to_le16(key_param_len);
			key_param_len += KEYPARAMSET_FIXED_LEN;
			key_material->key_param_set.length =
						cpu_to_le16(key_param_len);
			key_param_len += sizeof(struct mwifiex_ie_types_header);
		}

950 951
		cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN
					+ key_param_len);
952

953
		if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
954 955
			tlv_mac = (void *)((u8 *)&key_material->key_param_set +
					   key_param_len);
956 957 958
			tlv_mac->header.type =
					cpu_to_le16(TLV_TYPE_STA_MAC_ADDR);
			tlv_mac->header.len = cpu_to_le16(ETH_ALEN);
959 960 961 962 963 964 965 966 967
			memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN);
			cmd_size = key_param_len + S_DS_GEN +
				   sizeof(key_material->action) +
				   sizeof(struct host_cmd_tlv_mac_addr);
		} else {
			cmd_size = key_param_len + S_DS_GEN +
				   sizeof(key_material->action);
		}
		cmd->size = cpu_to_le16(cmd_size);
968 969 970 971 972
	}

	return ret;
}

973 974 975 976 977 978 979
/* Wrapper function for setting network key depending upon FW KEY API version */
static int
mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
				struct host_cmd_ds_command *cmd,
				u16 cmd_action, u32 cmd_oid,
				struct mwifiex_ds_encrypt_key *enc_key)
{
980
	if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
981 982 983 984 985 986 987 988 989 990
		return mwifiex_cmd_802_11_key_material_v2(priv, cmd,
							  cmd_action, cmd_oid,
							  enc_key);

	else
		return mwifiex_cmd_802_11_key_material_v1(priv, cmd,
							  cmd_action, cmd_oid,
							  enc_key);
}

991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
/*
 * This function prepares command to set/get 11d domain information.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting domain information fields (for SET only)
 *      - Ensuring correct endian-ness
 */
static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv,
					   struct host_cmd_ds_command *cmd,
					   u16 cmd_action)
{
	struct mwifiex_adapter *adapter = priv->adapter;
	struct host_cmd_ds_802_11d_domain_info *domain_info =
		&cmd->params.domain_info;
	struct mwifiex_ietypes_domain_param_set *domain =
		&domain_info->domain;
	u8 no_of_triplet = adapter->domain_reg.no_of_triplet;

1010 1011
	mwifiex_dbg(adapter, INFO,
		    "info: 11D: no_of_triplet=0x%x\n", no_of_triplet);
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022

	cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO);
	domain_info->action = cpu_to_le16(cmd_action);
	if (cmd_action == HostCmd_ACT_GEN_GET) {
		cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN);
		return 0;
	}

	/* Set domain info fields */
	domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY);
	memcpy(domain->country_code, adapter->domain_reg.country_code,
1023
	       sizeof(domain->country_code));
1024

1025 1026 1027 1028
	domain->header.len =
		cpu_to_le16((no_of_triplet *
			     sizeof(struct ieee80211_country_ie_triplet))
			    + sizeof(domain->country_code));
1029 1030 1031

	if (no_of_triplet) {
		memcpy(domain->triplet, adapter->domain_reg.triplet,
1032 1033
		       no_of_triplet * sizeof(struct
					      ieee80211_country_ie_triplet));
1034 1035

		cmd->size = cpu_to_le16(sizeof(domain_info->action) +
1036 1037 1038
					le16_to_cpu(domain->header.len) +
					sizeof(struct mwifiex_ie_types_header)
					+ S_DS_GEN);
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
	} else {
		cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN);
	}

	return 0;
}

/*
 * This function prepares command to set/get IBSS coalescing status.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting status to enable or disable (for SET only)
 *      - Ensuring correct endian-ness
 */
1054
static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd,
1055
					      u16 cmd_action, u16 *enable)
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
{
	struct host_cmd_ds_802_11_ibss_status *ibss_coal =
		&(cmd->params.ibss_coalescing);

	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) +
				S_DS_GEN);
	cmd->result = 0;
	ibss_coal->action = cpu_to_le16(cmd_action);

	switch (cmd_action) {
	case HostCmd_ACT_GEN_SET:
1068 1069
		if (enable)
			ibss_coal->enable = cpu_to_le16(*enable);
1070 1071
		else
			ibss_coal->enable = 0;
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
		break;

		/* In other case.. Nothing to do */
	case HostCmd_ACT_GEN_GET:
	default:
		break;
	}

	return 0;
}

1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
/* This function prepares command buffer to get/set memory location value.
 */
static int
mwifiex_cmd_mem_access(struct host_cmd_ds_command *cmd, u16 cmd_action,
		       void *pdata_buf)
{
	struct mwifiex_ds_mem_rw *mem_rw = (void *)pdata_buf;
	struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem;

	cmd->command = cpu_to_le16(HostCmd_CMD_MEM_ACCESS);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) +
				S_DS_GEN);

	mem_access->action = cpu_to_le16(cmd_action);
	mem_access->addr = cpu_to_le32(mem_rw->addr);
	mem_access->value = cpu_to_le32(mem_rw->value);

	return 0;
}

1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
/*
 * This function prepares command to set/get register value.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting register offset (for both GET and SET) and
 *        register value (for SET only)
 *      - Ensuring correct endian-ness
 *
 * The following type of registers can be accessed with this function -
 *      - MAC register
 *      - BBP register
 *      - RF register
 *      - PMIC register
 *      - CAU register
 *      - EEPROM
 */
static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd,
				  u16 cmd_action, void *data_buf)
{
1123
	struct mwifiex_ds_reg_rw *reg_rw = data_buf;
1124 1125 1126 1127 1128 1129 1130

	switch (le16_to_cpu(cmd->command)) {
	case HostCmd_CMD_MAC_REG_ACCESS:
	{
		struct host_cmd_ds_mac_reg_access *mac_reg;

		cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN);
1131
		mac_reg = &cmd->params.mac_reg;
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
		mac_reg->action = cpu_to_le16(cmd_action);
		mac_reg->offset =
			cpu_to_le16((u16) le32_to_cpu(reg_rw->offset));
		mac_reg->value = reg_rw->value;
		break;
	}
	case HostCmd_CMD_BBP_REG_ACCESS:
	{
		struct host_cmd_ds_bbp_reg_access *bbp_reg;

		cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN);
1143
		bbp_reg = &cmd->params.bbp_reg;
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
		bbp_reg->action = cpu_to_le16(cmd_action);
		bbp_reg->offset =
			cpu_to_le16((u16) le32_to_cpu(reg_rw->offset));
		bbp_reg->value = (u8) le32_to_cpu(reg_rw->value);
		break;
	}
	case HostCmd_CMD_RF_REG_ACCESS:
	{
		struct host_cmd_ds_rf_reg_access *rf_reg;

		cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN);
1155
		rf_reg = &cmd->params.rf_reg;
1156
		rf_reg->action = cpu_to_le16(cmd_action);
1157
		rf_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset));
1158 1159 1160 1161 1162 1163 1164 1165
		rf_reg->value = (u8) le32_to_cpu(reg_rw->value);
		break;
	}
	case HostCmd_CMD_PMIC_REG_ACCESS:
	{
		struct host_cmd_ds_pmic_reg_access *pmic_reg;

		cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN);
1166
		pmic_reg = &cmd->params.pmic_reg;
1167 1168
		pmic_reg->action = cpu_to_le16(cmd_action);
		pmic_reg->offset =
1169
				cpu_to_le16((u16) le32_to_cpu(reg_rw->offset));
1170 1171 1172 1173 1174 1175 1176 1177
		pmic_reg->value = (u8) le32_to_cpu(reg_rw->value);
		break;
	}
	case HostCmd_CMD_CAU_REG_ACCESS:
	{
		struct host_cmd_ds_rf_reg_access *cau_reg;

		cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN);
1178
		cau_reg = &cmd->params.rf_reg;
1179 1180
		cau_reg->action = cpu_to_le16(cmd_action);
		cau_reg->offset =
1181
				cpu_to_le16((u16) le32_to_cpu(reg_rw->offset));
1182 1183 1184 1185 1186
		cau_reg->value = (u8) le32_to_cpu(reg_rw->value);
		break;
	}
	case HostCmd_CMD_802_11_EEPROM_ACCESS:
	{
1187
		struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf;
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
		struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom =
			&cmd->params.eeprom;

		cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN);
		cmd_eeprom->action = cpu_to_le16(cmd_action);
		cmd_eeprom->offset = rd_eeprom->offset;
		cmd_eeprom->byte_count = rd_eeprom->byte_count;
		cmd_eeprom->value = 0;
		break;
	}
	default:
		return -1;
	}

	return 0;
}

1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
/*
 * This function prepares command to set PCI-Express
 * host buffer configuration
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Setting host buffer configuration
 *      - Ensuring correct endian-ness
 */
static int
mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv,
1216
			   struct host_cmd_ds_command *cmd, u16 action)
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
{
	struct host_cmd_ds_pcie_details *host_spec =
					&cmd->params.pcie_host_spec;
	struct pcie_service_card *card = priv->adapter->card;

	cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS);
	cmd->size = cpu_to_le16(sizeof(struct
					host_cmd_ds_pcie_details) + S_DS_GEN);
	cmd->result = 0;

	memset(host_spec, 0, sizeof(struct host_cmd_ds_pcie_details));

1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
	if (action != HostCmd_ACT_GEN_SET)
		return 0;

	/* Send the ring base addresses and count to firmware */
	host_spec->txbd_addr_lo = (u32)(card->txbd_ring_pbase);
	host_spec->txbd_addr_hi = (u32)(((u64)card->txbd_ring_pbase)>>32);
	host_spec->txbd_count = MWIFIEX_MAX_TXRX_BD;
	host_spec->rxbd_addr_lo = (u32)(card->rxbd_ring_pbase);
	host_spec->rxbd_addr_hi = (u32)(((u64)card->rxbd_ring_pbase)>>32);
	host_spec->rxbd_count = MWIFIEX_MAX_TXRX_BD;
	host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase);
	host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32);
	host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD;
1242 1243 1244 1245 1246
	if (card->sleep_cookie_vbase) {
		host_spec->sleep_cookie_addr_lo =
						(u32)(card->sleep_cookie_pbase);
		host_spec->sleep_cookie_addr_hi =
				 (u32)(((u64)(card->sleep_cookie_pbase)) >> 32);
1247 1248 1249
		mwifiex_dbg(priv->adapter, INFO,
			    "sleep_cook_lo phy addr: 0x%x\n",
			    host_spec->sleep_cookie_addr_lo);
1250 1251 1252 1253 1254
	}

	return 0;
}

1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
/*
 * This function prepares command for event subscription, configuration
 * and query. Events can be subscribed or unsubscribed. Current subscribed
 * events can be queried. Also, current subscribed events are reported in
 * every FW response.
 */
static int
mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv,
			     struct host_cmd_ds_command *cmd,
			     struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg)
{
	struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt;
	struct mwifiex_ie_types_rssi_threshold *rssi_tlv;
	u16 event_bitmap;
	u8 *pos;

	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) +
				S_DS_GEN);

	subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action);
1276 1277
	mwifiex_dbg(priv->adapter, CMD,
		    "cmd: action: %d\n", subsc_evt_cfg->action);
1278 1279 1280 1281 1282 1283 1284 1285

	/*For query requests, no configuration TLV structures are to be added.*/
	if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET)
		return 0;

	subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events);

	event_bitmap = subsc_evt_cfg->events;
1286 1287
	mwifiex_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n",
		    event_bitmap);
1288 1289 1290 1291

	if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) ||
	     (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) &&
	    (event_bitmap == 0)) {
1292 1293 1294
		mwifiex_dbg(priv->adapter, ERROR,
			    "Error: No event specified\t"
			    "for bitwise action type\n");
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
		return -EINVAL;
	}

	/*
	 * Append TLV structures for each of the specified events for
	 * subscribing or re-configuring. This is not required for
	 * bitwise unsubscribing request.
	 */
	if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR)
		return 0;

	pos = ((u8 *)subsc_evt) +
			sizeof(struct host_cmd_ds_802_11_subsc_evt);

	if (event_bitmap & BITMASK_BCN_RSSI_LOW) {
		rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos;

		rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW);
		rssi_tlv->header.len =
		    cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) -
				sizeof(struct mwifiex_ie_types_header));
		rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value;
		rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq;

1319 1320 1321 1322 1323
		mwifiex_dbg(priv->adapter, EVENT,
			    "Cfg Beacon Low Rssi event,\t"
			    "RSSI:-%d dBm, Freq:%d\n",
			    subsc_evt_cfg->bcn_l_rssi_cfg.abs_value,
			    subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq);
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339

		pos += sizeof(struct mwifiex_ie_types_rssi_threshold);
		le16_add_cpu(&cmd->size,
			     sizeof(struct mwifiex_ie_types_rssi_threshold));
	}

	if (event_bitmap & BITMASK_BCN_RSSI_HIGH) {
		rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos;

		rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH);
		rssi_tlv->header.len =
		    cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) -
				sizeof(struct mwifiex_ie_types_header));
		rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value;
		rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq;

1340 1341 1342 1343 1344
		mwifiex_dbg(priv->adapter, EVENT,
			    "Cfg Beacon High Rssi event,\t"
			    "RSSI:-%d dBm, Freq:%d\n",
			    subsc_evt_cfg->bcn_h_rssi_cfg.abs_value,
			    subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq);
1345 1346 1347 1348 1349 1350 1351 1352 1353

		pos += sizeof(struct mwifiex_ie_types_rssi_threshold);
		le16_add_cpu(&cmd->size,
			     sizeof(struct mwifiex_ie_types_rssi_threshold));
	}

	return 0;
}

A
Amitkumar Karwar 已提交
1354 1355 1356 1357 1358 1359 1360 1361 1362
static int
mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv,
				  struct mwifiex_mef_entry *mef_entry,
				  u8 **buffer)
{
	struct mwifiex_mef_filter *filter = mef_entry->filter;
	int i, byte_len;
	u8 *stack_ptr = *buffer;

A
Amitkumar Karwar 已提交
1363
	for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) {
A
Amitkumar Karwar 已提交
1364 1365 1366 1367 1368 1369 1370 1371
		filter = &mef_entry->filter[i];
		if (!filter->filt_type)
			break;
		*(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat);
		stack_ptr += 4;
		*stack_ptr = TYPE_DNUM;
		stack_ptr += 1;

A
Amitkumar Karwar 已提交
1372
		byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ];
A
Amitkumar Karwar 已提交
1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
		memcpy(stack_ptr, filter->byte_seq, byte_len);
		stack_ptr += byte_len;
		*stack_ptr = byte_len;
		stack_ptr += 1;
		*stack_ptr = TYPE_BYTESEQ;
		stack_ptr += 1;

		*(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset);
		stack_ptr += 4;
		*stack_ptr = TYPE_DNUM;
		stack_ptr += 1;

		*stack_ptr = filter->filt_type;
		stack_ptr += 1;

		if (filter->filt_action) {
			*stack_ptr = filter->filt_action;
			stack_ptr += 1;
		}

		if (stack_ptr - *buffer > STACK_NBYTES)
			return -1;
	}

	*buffer = stack_ptr;
	return 0;
}

static int
mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
		    struct host_cmd_ds_command *cmd,
		    struct mwifiex_ds_mef_cfg *mef)
{
	struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg;
1407
	struct mwifiex_fw_mef_entry *mef_entry = NULL;
A
Amitkumar Karwar 已提交
1408
	u8 *pos = (u8 *)mef_cfg;
1409
	u16 i;
A
Amitkumar Karwar 已提交
1410 1411 1412 1413 1414 1415 1416

	cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG);

	mef_cfg->criteria = cpu_to_le32(mef->criteria);
	mef_cfg->num_entries = cpu_to_le16(mef->num_entries);
	pos += sizeof(*mef_cfg);

1417 1418 1419 1420 1421 1422 1423 1424 1425
	for (i = 0; i < mef->num_entries; i++) {
		mef_entry = (struct mwifiex_fw_mef_entry *)pos;
		mef_entry->mode = mef->mef_entry[i].mode;
		mef_entry->action = mef->mef_entry[i].action;
		pos += sizeof(*mef_cfg->mef_entry);

		if (mwifiex_cmd_append_rpn_expression(priv,
						      &mef->mef_entry[i], &pos))
			return -1;
A
Amitkumar Karwar 已提交
1426

1427 1428 1429
		mef_entry->exprsize =
			cpu_to_le16(pos - mef_entry->expr);
	}
A
Amitkumar Karwar 已提交
1430 1431 1432 1433 1434
	cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN);

	return 0;
}

1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455
/* This function parse cal data from ASCII to hex */
static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst)
{
	u8 *s = src, *d = dst;

	while (s - src < len) {
		if (*s && (isspace(*s) || *s == '\t')) {
			s++;
			continue;
		}
		if (isxdigit(*s)) {
			*d++ = simple_strtol(s, NULL, 16);
			s += 2;
		} else {
			s++;
		}
	}

	return d - dst;
}

1456 1457
int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
			    struct device_node *node, const char *prefix)
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
{
#ifdef CONFIG_OF
	struct property *prop;
	size_t len = strlen(prefix);
	int ret;

	/* look for all matching property names */
	for_each_property_of_node(node, prop) {
		if (len > strlen(prop->name) ||
		    strncmp(prop->name, prefix, len))
			continue;

1470 1471 1472
		/* property header is 6 bytes, data must fit in cmd buffer */
		if (prop && prop->value && prop->length > 6 &&
		    prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) {
1473 1474 1475
			ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA,
					       HostCmd_ACT_GEN_SET, 0,
					       prop, true);
1476 1477 1478 1479 1480 1481 1482 1483
			if (ret)
				return ret;
		}
	}
#endif
	return 0;
}

1484 1485
/* This function prepares command of set_cfg_data. */
static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
1486
				struct host_cmd_ds_command *cmd, void *data_buf)
1487 1488
{
	struct mwifiex_adapter *adapter = priv->adapter;
1489
	struct property *prop = data_buf;
B
Bing Zhao 已提交
1490 1491
	u32 len;
	u8 *data = (u8 *)cmd + S_DS_GEN;
1492
	int ret;
1493

1494 1495 1496 1497 1498 1499
	if (prop) {
		len = prop->length;
		ret = of_property_read_u8_array(adapter->dt_node, prop->name,
						data, len);
		if (ret)
			return ret;
1500 1501 1502
		mwifiex_dbg(adapter, INFO,
			    "download cfg_data from device tree: %s\n",
			    prop->name);
1503
	} else if (adapter->cal_data->data && adapter->cal_data->size > 0) {
1504
		len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data,
B
Bing Zhao 已提交
1505
					    adapter->cal_data->size, data);
1506 1507
		mwifiex_dbg(adapter, INFO,
			    "download cfg_data from config file\n");
1508
	} else {
1509
		return -1;
1510
	}
1511 1512

	cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
B
Bing Zhao 已提交
1513
	cmd->size = cpu_to_le16(S_DS_GEN + len);
1514 1515 1516 1517

	return 0;
}

1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533
static int
mwifiex_cmd_set_mc_policy(struct mwifiex_private *priv,
			  struct host_cmd_ds_command *cmd,
			  u16 cmd_action, void *data_buf)
{
	struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy;
	const u16 *drcs_info = data_buf;

	mc_pol->action = cpu_to_le16(cmd_action);
	mc_pol->policy = cpu_to_le16(*drcs_info);
	cmd->command = cpu_to_le16(HostCmd_CMD_MC_POLICY);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) +
				S_DS_GEN);
	return 0;
}

1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560
static int mwifiex_cmd_robust_coex(struct mwifiex_private *priv,
				   struct host_cmd_ds_command *cmd,
				   u16 cmd_action, bool *is_timeshare)
{
	struct host_cmd_ds_robust_coex *coex = &cmd->params.coex;
	struct mwifiex_ie_types_robust_coex *coex_tlv;

	cmd->command = cpu_to_le16(HostCmd_CMD_ROBUST_COEX);
	cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN);

	coex->action = cpu_to_le16(cmd_action);
	coex_tlv = (struct mwifiex_ie_types_robust_coex *)
				((u8 *)coex + sizeof(*coex));
	coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX);
	coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode));

	if (coex->action == HostCmd_ACT_GEN_GET)
		return 0;

	if (*is_timeshare)
		coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_TIMESHARE);
	else
		coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_SPATIAL);

	return 0;
}

1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584
static int mwifiex_cmd_gtk_rekey_offload(struct mwifiex_private *priv,
					 struct host_cmd_ds_command *cmd,
					 u16 cmd_action,
					 struct cfg80211_gtk_rekey_data *data)
{
	struct host_cmd_ds_gtk_rekey_params *rekey = &cmd->params.rekey;
	u64 rekey_ctr;

	cmd->command = cpu_to_le16(HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG);
	cmd->size = cpu_to_le16(sizeof(*rekey) + S_DS_GEN);

	rekey->action = cpu_to_le16(cmd_action);
	if (cmd_action == HostCmd_ACT_GEN_SET) {
		memcpy(rekey->kek, data->kek, NL80211_KEK_LEN);
		memcpy(rekey->kck, data->kck, NL80211_KCK_LEN);
		rekey_ctr = be64_to_cpup((__be64 *)data->replay_ctr);
		rekey->replay_ctr_low = cpu_to_le32((u32)rekey_ctr);
		rekey->replay_ctr_high =
			cpu_to_le32((u32)((u64)rekey_ctr >> 32));
	}

	return 0;
}

1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648
static int
mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
			 struct host_cmd_ds_command *cmd,
			 u16 cmd_action, void *data_buf)
{
	struct host_cmd_ds_coalesce_cfg *coalesce_cfg =
						&cmd->params.coalesce_cfg;
	struct mwifiex_ds_coalesce_cfg *cfg = data_buf;
	struct coalesce_filt_field_param *param;
	u16 cnt, idx, length;
	struct coalesce_receive_filt_rule *rule;

	cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG);
	cmd->size = cpu_to_le16(S_DS_GEN);

	coalesce_cfg->action = cpu_to_le16(cmd_action);
	coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules);
	rule = coalesce_cfg->rule;

	for (cnt = 0; cnt < cfg->num_of_rules; cnt++) {
		rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE);
		rule->max_coalescing_delay =
			cpu_to_le16(cfg->rule[cnt].max_coalescing_delay);
		rule->pkt_type = cfg->rule[cnt].pkt_type;
		rule->num_of_fields = cfg->rule[cnt].num_of_fields;

		length = 0;

		param = rule->params;
		for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) {
			param->operation = cfg->rule[cnt].params[idx].operation;
			param->operand_len =
					cfg->rule[cnt].params[idx].operand_len;
			param->offset =
				cpu_to_le16(cfg->rule[cnt].params[idx].offset);
			memcpy(param->operand_byte_stream,
			       cfg->rule[cnt].params[idx].operand_byte_stream,
			       param->operand_len);

			length += sizeof(struct coalesce_filt_field_param);

			param++;
		}

		/* Total rule length is sizeof max_coalescing_delay(u16),
		 * num_of_fields(u8), pkt_type(u8) and total length of the all
		 * params
		 */
		rule->header.len = cpu_to_le16(length + sizeof(u16) +
					       sizeof(u8) + sizeof(u8));

		/* Add the rule length to the command size*/
		le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) +
			     sizeof(struct mwifiex_ie_types_header));

		rule = (void *)((u8 *)rule->params + length);
	}

	/* Add sizeof action, num_of_rules to total command length */
	le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16));

	return 0;
}

X
Xinming Hu 已提交
1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
static int
mwifiex_cmd_tdls_config(struct mwifiex_private *priv,
			struct host_cmd_ds_command *cmd,
			u16 cmd_action, void *data_buf)
{
	struct host_cmd_ds_tdls_config *tdls_config = &cmd->params.tdls_config;
	struct mwifiex_tdls_init_cs_params *config;
	struct mwifiex_tdls_config *init_config;
	u16 len;

	cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_CONFIG);
	cmd->size = cpu_to_le16(S_DS_GEN);
	tdls_config->tdls_action = cpu_to_le16(cmd_action);
	le16_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action));

	switch (cmd_action) {
	case ACT_TDLS_CS_ENABLE_CONFIG:
		init_config = data_buf;
		len = sizeof(*init_config);
		memcpy(tdls_config->tdls_data, init_config, len);
		break;
	case ACT_TDLS_CS_INIT:
		config = data_buf;
		len = sizeof(*config);
		memcpy(tdls_config->tdls_data, config, len);
		break;
	case ACT_TDLS_CS_STOP:
		len = sizeof(struct mwifiex_tdls_stop_cs_params);
		memcpy(tdls_config->tdls_data, data_buf, len);
		break;
	case ACT_TDLS_CS_PARAMS:
		len = sizeof(struct mwifiex_tdls_config_cs_params);
		memcpy(tdls_config->tdls_data, data_buf, len);
		break;
	default:
		mwifiex_dbg(priv->adapter, ERROR,
			    "Unknown TDLS configuration\n");
		return -ENOTSUPP;
	}

	le16_add_cpu(&cmd->size, len);
	return 0;
}

1693 1694 1695 1696 1697 1698 1699 1700
static int
mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
		      struct host_cmd_ds_command *cmd,
		      void *data_buf)
{
	struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper;
	struct mwifiex_ds_tdls_oper *oper = data_buf;
	struct mwifiex_sta_node *sta_ptr;
1701 1702 1703 1704
	struct host_cmd_tlv_rates *tlv_rates;
	struct mwifiex_ie_types_htcap *ht_capab;
	struct mwifiex_ie_types_qos_info *wmm_qos_info;
	struct mwifiex_ie_types_extcap *extcap;
1705 1706
	struct mwifiex_ie_types_vhtcap *vht_capab;
	struct mwifiex_ie_types_aid *aid;
1707
	struct mwifiex_ie_types_tdls_idle_timeout *timeout;
1708 1709 1710
	u8 *pos, qos_info;
	u16 config_len = 0;
	struct station_parameters *params = priv->sta_params;
1711 1712 1713

	cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER);
	cmd->size = cpu_to_le16(S_DS_GEN);
1714
	le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper));
1715 1716 1717 1718 1719

	tdls_oper->reason = 0;
	memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN);
	sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac);

1720 1721
	pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper);

1722 1723 1724 1725
	switch (oper->tdls_action) {
	case MWIFIEX_TDLS_DISABLE_LINK:
		tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE);
		break;
1726 1727
	case MWIFIEX_TDLS_CREATE_LINK:
		tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE);
1728 1729 1730 1731 1732
		break;
	case MWIFIEX_TDLS_CONFIG_LINK:
		tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG);

		if (!params) {
1733 1734 1735
			mwifiex_dbg(priv->adapter, ERROR,
				    "TDLS config params not available for %pM\n",
				    oper->peer_mac);
1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
			return -ENODATA;
		}

		*(__le16 *)pos = cpu_to_le16(params->capability);
		config_len += sizeof(params->capability);

		qos_info = params->uapsd_queues | (params->max_sp << 5);
		wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos +
								    config_len);
		wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA);
		wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info));
		wmm_qos_info->qos_info = qos_info;
		config_len += sizeof(struct mwifiex_ie_types_qos_info);

		if (params->ht_capa) {
			ht_capab = (struct mwifiex_ie_types_htcap *)(pos +
								    config_len);
			ht_capab->header.type =
					    cpu_to_le16(WLAN_EID_HT_CAPABILITY);
			ht_capab->header.len =
				   cpu_to_le16(sizeof(struct ieee80211_ht_cap));
			memcpy(&ht_capab->ht_cap, params->ht_capa,
			       sizeof(struct ieee80211_ht_cap));
			config_len += sizeof(struct mwifiex_ie_types_htcap);
		}

		if (params->supported_rates && params->supported_rates_len) {
			tlv_rates = (struct host_cmd_tlv_rates *)(pos +
								  config_len);
			tlv_rates->header.type =
					       cpu_to_le16(WLAN_EID_SUPP_RATES);
			tlv_rates->header.len =
				       cpu_to_le16(params->supported_rates_len);
			memcpy(tlv_rates->rates, params->supported_rates,
			       params->supported_rates_len);
			config_len += sizeof(struct host_cmd_tlv_rates) +
				      params->supported_rates_len;
		}

		if (params->ext_capab && params->ext_capab_len) {
			extcap = (struct mwifiex_ie_types_extcap *)(pos +
								    config_len);
			extcap->header.type =
					   cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
			extcap->header.len = cpu_to_le16(params->ext_capab_len);
			memcpy(extcap->ext_capab, params->ext_capab,
			       params->ext_capab_len);
			config_len += sizeof(struct mwifiex_ie_types_extcap) +
				      params->ext_capab_len;
		}
1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
		if (params->vht_capa) {
			vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos +
								    config_len);
			vht_capab->header.type =
					   cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
			vht_capab->header.len =
				  cpu_to_le16(sizeof(struct ieee80211_vht_cap));
			memcpy(&vht_capab->vht_cap, params->vht_capa,
			       sizeof(struct ieee80211_vht_cap));
			config_len += sizeof(struct mwifiex_ie_types_vhtcap);
		}
		if (params->aid) {
			aid = (struct mwifiex_ie_types_aid *)(pos + config_len);
			aid->header.type = cpu_to_le16(WLAN_EID_AID);
			aid->header.len = cpu_to_le16(sizeof(params->aid));
			aid->aid = cpu_to_le16(params->aid);
			config_len += sizeof(struct mwifiex_ie_types_aid);
		}
1804

1805 1806 1807
		timeout = (void *)(pos + config_len);
		timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT);
		timeout->header.len = cpu_to_le16(sizeof(timeout->value));
1808
		timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC);
1809 1810
		config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout);

1811
		break;
1812
	default:
1813
		mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS operation\n");
1814 1815 1816
		return -ENOTSUPP;
	}

1817
	le16_add_cpu(&cmd->size, config_len);
1818 1819 1820

	return 0;
}
1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839

/* This function prepares command of sdio rx aggr info. */
static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd,
					u16 cmd_action, void *data_buf)
{
	struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
					&cmd->params.sdio_rx_aggr_cfg;

	cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG);
	cmd->size =
		cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) +
			    S_DS_GEN);
	cfg->action = cmd_action;
	if (cmd_action == HostCmd_ACT_GEN_SET)
		cfg->enable = *(u8 *)data_buf;

	return 0;
}

1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855
/* This function prepares command to get HS wakeup reason.
 *
 * Preparation includes -
 *      - Setting command ID, action and proper size
 *      - Ensuring correct endian-ness
 */
static int mwifiex_cmd_get_wakeup_reason(struct mwifiex_private *priv,
					 struct host_cmd_ds_command *cmd)
{
	cmd->command = cpu_to_le16(HostCmd_CMD_HS_WAKEUP_REASON);
	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wakeup_reason) +
				S_DS_GEN);

	return 0;
}

1856 1857 1858 1859 1860 1861 1862 1863 1864 1865
/*
 * This function prepares the commands before sending them to the firmware.
 *
 * This is a generic function which calls specific command preparation
 * routines based upon the command number.
 */
int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
			    u16 cmd_action, u32 cmd_oid,
			    void *data_buf, void *cmd_buf)
{
1866
	struct host_cmd_ds_command *cmd_ptr = cmd_buf;
1867 1868 1869 1870 1871 1872 1873
	int ret = 0;

	/* Prepare command */
	switch (cmd_no) {
	case HostCmd_CMD_GET_HW_SPEC:
		ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr);
		break;
1874
	case HostCmd_CMD_CFG_DATA:
1875
		ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf);
1876
		break;
1877 1878 1879 1880 1881 1882 1883 1884 1885
	case HostCmd_CMD_MAC_CONTROL:
		ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action,
					      data_buf);
		break;
	case HostCmd_CMD_802_11_MAC_ADDRESS:
		ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr,
						     cmd_action);
		break;
	case HostCmd_CMD_MAC_MULTICAST_ADR:
1886
		ret = mwifiex_cmd_mac_multicast_adr(cmd_ptr, cmd_action,
1887 1888 1889 1890 1891 1892 1893
						    data_buf);
		break;
	case HostCmd_CMD_TX_RATE_CFG:
		ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action,
					      data_buf);
		break;
	case HostCmd_CMD_TXPWR_CFG:
1894
		ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action,
1895 1896
					       data_buf);
		break;
1897 1898 1899 1900
	case HostCmd_CMD_RF_TX_PWR:
		ret = mwifiex_cmd_rf_tx_power(priv, cmd_ptr, cmd_action,
					      data_buf);
		break;
1901 1902 1903 1904
	case HostCmd_CMD_RF_ANTENNA:
		ret = mwifiex_cmd_rf_antenna(priv, cmd_ptr, cmd_action,
					     data_buf);
		break;
1905 1906 1907 1908 1909 1910 1911 1912 1913
	case HostCmd_CMD_802_11_PS_MODE_ENH:
		ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action,
						 (uint16_t)cmd_oid, data_buf);
		break;
	case HostCmd_CMD_802_11_HS_CFG_ENH:
		ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action,
				(struct mwifiex_hs_config_param *) data_buf);
		break;
	case HostCmd_CMD_802_11_SCAN:
1914
		ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf);
1915
		break;
X
Xinming Hu 已提交
1916 1917 1918 1919
	case HostCmd_CMD_802_11_BG_SCAN_CONFIG:
		ret = mwifiex_cmd_802_11_bg_scan_config(priv, cmd_ptr,
							data_buf);
		break;
1920
	case HostCmd_CMD_802_11_BG_SCAN_QUERY:
1921
		ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr);
1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934
		break;
	case HostCmd_CMD_802_11_ASSOCIATE:
		ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf);
		break;
	case HostCmd_CMD_802_11_DEAUTHENTICATE:
		ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr,
							data_buf);
		break;
	case HostCmd_CMD_802_11_AD_HOC_START:
		ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr,
						      data_buf);
		break;
	case HostCmd_CMD_802_11_GET_LOG:
1935
		ret = mwifiex_cmd_802_11_get_log(cmd_ptr);
1936 1937 1938 1939 1940 1941
		break;
	case HostCmd_CMD_802_11_AD_HOC_JOIN:
		ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr,
						     data_buf);
		break;
	case HostCmd_CMD_802_11_AD_HOC_STOP:
1942
		ret = mwifiex_cmd_802_11_ad_hoc_stop(cmd_ptr);
1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970
		break;
	case HostCmd_CMD_RSSI_INFO:
		ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action);
		break;
	case HostCmd_CMD_802_11_SNMP_MIB:
		ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action,
						  cmd_oid, data_buf);
		break;
	case HostCmd_CMD_802_11_TX_RATE_QUERY:
		cmd_ptr->command =
			cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY);
		cmd_ptr->size =
			cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) +
				    S_DS_GEN);
		priv->tx_rate = 0;
		ret = 0;
		break;
	case HostCmd_CMD_VERSION_EXT:
		cmd_ptr->command = cpu_to_le16(cmd_no);
		cmd_ptr->params.verext.version_str_sel =
			(u8) (*((u32 *) data_buf));
		memcpy(&cmd_ptr->params, data_buf,
		       sizeof(struct host_cmd_ds_version_ext));
		cmd_ptr->size =
			cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) +
				    S_DS_GEN);
		ret = 0;
		break;
1971 1972 1973 1974 1975 1976 1977 1978 1979
	case HostCmd_CMD_MGMT_FRAME_REG:
		cmd_ptr->command = cpu_to_le16(cmd_no);
		cmd_ptr->params.reg_mask.action = cpu_to_le16(cmd_action);
		cmd_ptr->params.reg_mask.mask = cpu_to_le32(*(u32 *)data_buf);
		cmd_ptr->size =
			cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) +
				    S_DS_GEN);
		ret = 0;
		break;
1980 1981 1982 1983 1984 1985 1986 1987
	case HostCmd_CMD_REMAIN_ON_CHAN:
		cmd_ptr->command = cpu_to_le16(cmd_no);
		memcpy(&cmd_ptr->params, data_buf,
		       sizeof(struct host_cmd_ds_remain_on_chan));
		cmd_ptr->size =
		      cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) +
				  S_DS_GEN);
		break;
1988 1989 1990
	case HostCmd_CMD_11AC_CFG:
		ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf);
		break;
1991 1992 1993 1994 1995 1996 1997 1998
	case HostCmd_CMD_P2P_MODE_CFG:
		cmd_ptr->command = cpu_to_le16(cmd_no);
		cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action);
		cmd_ptr->params.mode_cfg.mode = cpu_to_le16(*(u16 *)data_buf);
		cmd_ptr->size =
			cpu_to_le16(sizeof(struct host_cmd_ds_p2p_mode_cfg) +
				    S_DS_GEN);
		break;
1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010
	case HostCmd_CMD_FUNC_INIT:
		if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET)
			priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY;
		cmd_ptr->command = cpu_to_le16(cmd_no);
		cmd_ptr->size = cpu_to_le16(S_DS_GEN);
		break;
	case HostCmd_CMD_FUNC_SHUTDOWN:
		priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET;
		cmd_ptr->command = cpu_to_le16(cmd_no);
		cmd_ptr->size = cpu_to_le16(S_DS_GEN);
		break;
	case HostCmd_CMD_11N_ADDBA_REQ:
2011
		ret = mwifiex_cmd_11n_addba_req(cmd_ptr, data_buf);
2012 2013
		break;
	case HostCmd_CMD_11N_DELBA:
2014
		ret = mwifiex_cmd_11n_delba(cmd_ptr, data_buf);
2015 2016 2017 2018 2019 2020
		break;
	case HostCmd_CMD_11N_ADDBA_RSP:
		ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf);
		break;
	case HostCmd_CMD_802_11_KEY_MATERIAL:
		ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr,
2021 2022
						      cmd_action, cmd_oid,
						      data_buf);
2023 2024 2025
		break;
	case HostCmd_CMD_802_11D_DOMAIN_INFO:
		ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr,
2026
						      cmd_action);
2027 2028 2029 2030 2031 2032
		break;
	case HostCmd_CMD_RECONFIGURE_TX_BUFF:
		ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action,
					       data_buf);
		break;
	case HostCmd_CMD_AMSDU_AGGR_CTRL:
2033
		ret = mwifiex_cmd_amsdu_aggr_ctrl(cmd_ptr, cmd_action,
2034 2035 2036
						  data_buf);
		break;
	case HostCmd_CMD_11N_CFG:
2037
		ret = mwifiex_cmd_11n_cfg(priv, cmd_ptr, cmd_action, data_buf);
2038 2039
		break;
	case HostCmd_CMD_WMM_GET_STATUS:
2040 2041
		mwifiex_dbg(priv->adapter, CMD,
			    "cmd: WMM: WMM_GET_STATUS cmd sent\n");
2042 2043 2044 2045 2046 2047 2048
		cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS);
		cmd_ptr->size =
			cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) +
				    S_DS_GEN);
		ret = 0;
		break;
	case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS:
2049 2050
		ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action,
							 data_buf);
2051
		break;
2052 2053 2054
	case HostCmd_CMD_802_11_SCAN_EXT:
		ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf);
		break;
2055 2056 2057
	case HostCmd_CMD_MEM_ACCESS:
		ret = mwifiex_cmd_mem_access(cmd_ptr, cmd_action, data_buf);
		break;
2058 2059 2060 2061 2062 2063 2064 2065 2066 2067
	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_cmd_reg_access(cmd_ptr, cmd_action, data_buf);
		break;
	case HostCmd_CMD_SET_BSS_MODE:
		cmd_ptr->command = cpu_to_le16(cmd_no);
2068
		if (priv->bss_mode == NL80211_IFTYPE_ADHOC)
2069 2070
			cmd_ptr->params.bss_mode.con_type =
				CONNECTION_TYPE_ADHOC;
2071 2072
		else if (priv->bss_mode == NL80211_IFTYPE_STATION ||
			 priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT)
2073 2074
			cmd_ptr->params.bss_mode.con_type =
				CONNECTION_TYPE_INFRA;
2075 2076
		else if (priv->bss_mode == NL80211_IFTYPE_AP ||
			 priv->bss_mode == NL80211_IFTYPE_P2P_GO)
2077
			cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_AP;
2078 2079 2080 2081
		cmd_ptr->size = cpu_to_le16(sizeof(struct
				host_cmd_ds_set_bss_mode) + S_DS_GEN);
		ret = 0;
		break;
2082 2083 2084
	case HostCmd_CMD_PCIE_DESC_DETAILS:
		ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action);
		break;
2085 2086 2087
	case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
		ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf);
		break;
A
Amitkumar Karwar 已提交
2088 2089 2090
	case HostCmd_CMD_MEF_CFG:
		ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf);
		break;
2091 2092 2093 2094
	case HostCmd_CMD_COALESCE_CFG:
		ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action,
					       data_buf);
		break;
2095 2096 2097
	case HostCmd_CMD_TDLS_OPER:
		ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf);
		break;
X
Xinming Hu 已提交
2098 2099 2100 2101
	case HostCmd_CMD_TDLS_CONFIG:
		ret = mwifiex_cmd_tdls_config(priv, cmd_ptr, cmd_action,
					      data_buf);
		break;
2102 2103 2104 2105
	case HostCmd_CMD_CHAN_REPORT_REQUEST:
		ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr,
							    data_buf);
		break;
2106 2107 2108 2109
	case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
		ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action,
						   data_buf);
		break;
2110 2111 2112
	case HostCmd_CMD_HS_WAKEUP_REASON:
		ret = mwifiex_cmd_get_wakeup_reason(priv, cmd_ptr);
		break;
2113 2114 2115 2116
	case HostCmd_CMD_MC_POLICY:
		ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action,
						data_buf);
		break;
2117 2118 2119 2120
	case HostCmd_CMD_ROBUST_COEX:
		ret = mwifiex_cmd_robust_coex(priv, cmd_ptr, cmd_action,
					      data_buf);
		break;
2121 2122 2123 2124
	case HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG:
		ret = mwifiex_cmd_gtk_rekey_offload(priv, cmd_ptr, cmd_action,
						    data_buf);
		break;
2125
	default:
2126 2127
		mwifiex_dbg(priv->adapter, ERROR,
			    "PREP_CMD: unknown cmd- %#x\n", cmd_no);
2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138
		ret = -1;
		break;
	}
	return ret;
}

/*
 * This function issues commands to initialize firmware.
 *
 * This is called after firmware download to bring the card to
 * working state.
2139 2140
 * Function is also called during reinitialization of virtual
 * interfaces.
2141 2142
 *
 * The following commands are issued sequentially -
2143
 *      - Set PCI-Express host buffer configuration (PCIE only)
2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154
 *      - Function init (for first interface only)
 *      - Read MAC address (for first interface only)
 *      - Reconfigure Tx buffer size (for first interface only)
 *      - Enable auto deep sleep (for first interface only)
 *      - Get Tx rate
 *      - Get Tx power
 *      - Set IBSS coalescing status
 *      - Set AMSDU aggregation control
 *      - Set 11d control
 *      - Set MAC control (this must be the last command to initialize firmware)
 */
2155
int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
2156
{
2157
	struct mwifiex_adapter *adapter = priv->adapter;
2158
	int ret;
2159 2160 2161 2162
	u16 enable = true;
	struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl;
	struct mwifiex_ds_auto_ds auto_ds;
	enum state_11d_t state_11d;
2163
	struct mwifiex_ds_11n_tx_cfg tx_cfg;
2164
	u8 sdio_sp_rx_aggr_enable;
2165 2166

	if (first_sta) {
2167
		if (priv->adapter->iface_type == MWIFIEX_PCIE) {
2168 2169 2170 2171
			ret = mwifiex_send_cmd(priv,
					       HostCmd_CMD_PCIE_DESC_DETAILS,
					       HostCmd_ACT_GEN_SET, 0, NULL,
					       true);
2172 2173 2174
			if (ret)
				return -1;
		}
2175

2176 2177
		ret = mwifiex_send_cmd(priv, HostCmd_CMD_FUNC_INIT,
				       HostCmd_ACT_GEN_SET, 0, NULL, true);
2178 2179
		if (ret)
			return -1;
2180

2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193
		/* Download calibration data to firmware.
		 * The cal-data can be read from device tree and/or
		 * a configuration file and downloaded to firmware.
		 */
		adapter->dt_node =
				of_find_node_by_name(NULL, "marvell_cfgdata");
		if (adapter->dt_node) {
			ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node,
						      "marvell,caldata");
			if (ret)
				return -1;
		}

2194
		if (adapter->cal_data) {
2195 2196 2197
			ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA,
					       HostCmd_ACT_GEN_SET, 0, NULL,
					       true);
2198 2199 2200 2201
			if (ret)
				return -1;
		}

2202
		/* Read MAC address from HW */
2203 2204
		ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC,
				       HostCmd_ACT_GEN_GET, 0, NULL, true);
2205 2206 2207
		if (ret)
			return -1;

2208 2209
		/** Set SDIO Single Port RX Aggr Info */
		if (priv->adapter->iface_type == MWIFIEX_SDIO &&
2210 2211
		    ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) &&
		    !priv->adapter->host_disable_sdio_rx_aggr) {
2212 2213 2214 2215 2216 2217 2218
			sdio_sp_rx_aggr_enable = true;
			ret = mwifiex_send_cmd(priv,
					       HostCmd_CMD_SDIO_SP_RX_AGGR_CFG,
					       HostCmd_ACT_GEN_SET, 0,
					       &sdio_sp_rx_aggr_enable,
					       true);
			if (ret) {
2219 2220
				mwifiex_dbg(priv->adapter, ERROR,
					    "error while enabling SP aggregation..disable it");
2221 2222 2223 2224
				adapter->sdio_rx_aggr_enable = false;
			}
		}

2225
		/* Reconfigure tx buf size */
2226 2227 2228
		ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
				       HostCmd_ACT_GEN_SET, 0,
				       &priv->adapter->tx_buf_size, true);
2229 2230 2231
		if (ret)
			return -1;

2232 2233 2234
		if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) {
			/* Enable IEEE PS by default */
			priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
2235 2236 2237 2238
			ret = mwifiex_send_cmd(priv,
					       HostCmd_CMD_802_11_PS_MODE_ENH,
					       EN_AUTO_PS, BITMAP_STA_PS, NULL,
					       true);
2239 2240 2241
			if (ret)
				return -1;
		}
2242

2243 2244 2245 2246 2247 2248 2249 2250
		if (drcs) {
			adapter->drcs_enabled = true;
			if (ISSUPP_DRCS_ENABLED(adapter->fw_cap_info))
				ret = mwifiex_send_cmd(priv,
						       HostCmd_CMD_MC_POLICY,
						       HostCmd_ACT_GEN_SET, 0,
						       &adapter->drcs_enabled,
						       true);
2251 2252
			if (ret)
				return -1;
2253
		}
2254 2255 2256
	}

	/* get tx rate */
2257 2258
	ret = mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG,
			       HostCmd_ACT_GEN_GET, 0, NULL, true);
2259 2260 2261 2262 2263
	if (ret)
		return -1;
	priv->data_rate = 0;

	/* get tx power */
2264 2265
	ret = mwifiex_send_cmd(priv, HostCmd_CMD_RF_TX_PWR,
			       HostCmd_ACT_GEN_GET, 0, NULL, true);
2266 2267 2268
	if (ret)
		return -1;

2269 2270
	if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) {
		/* set ibss coalescing_status */
2271 2272 2273 2274
		ret = mwifiex_send_cmd(
				priv,
				HostCmd_CMD_802_11_IBSS_COALESCING_STATUS,
				HostCmd_ACT_GEN_SET, 0, &enable, true);
2275 2276 2277
		if (ret)
			return -1;
	}
2278 2279 2280 2281

	memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl));
	amsdu_aggr_ctrl.enable = true;
	/* Send request to firmware */
2282 2283 2284
	ret = mwifiex_send_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL,
			       HostCmd_ACT_GEN_SET, 0,
			       &amsdu_aggr_ctrl, true);
2285 2286 2287 2288
	if (ret)
		return -1;
	/* MAC Control must be the last command in init_fw */
	/* set MAC Control */
2289 2290 2291
	ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
			       HostCmd_ACT_GEN_SET, 0,
			       &priv->curr_pkt_filter, true);
2292 2293 2294
	if (ret)
		return -1;

2295 2296
	if (!disable_auto_ds &&
	    first_sta && priv->adapter->iface_type != MWIFIEX_USB &&
2297
	    priv->bss_type != MWIFIEX_BSS_TYPE_UAP) {
2298 2299 2300
		/* Enable auto deep sleep */
		auto_ds.auto_ds = DEEP_SLEEP_ON;
		auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME;
2301 2302 2303
		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
				       EN_AUTO_PS, BITMAP_AUTO_DS,
				       &auto_ds, true);
2304 2305 2306 2307
		if (ret)
			return -1;
	}

2308 2309 2310
	if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) {
		/* Send cmd to FW to enable/disable 11D function */
		state_11d = ENABLE_11D;
2311 2312 2313
		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
				       HostCmd_ACT_GEN_SET, DOT11D_I,
				       &state_11d, true);
2314
		if (ret)
2315 2316
			mwifiex_dbg(priv->adapter, ERROR,
				    "11D: failed to enable 11D\n");
2317
	}
2318

2319 2320 2321 2322
	/* Send cmd to FW to configure 11n specific configuration
	 * (Short GI, Channel BW, Green field support etc.) for transmit
	 */
	tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG;
2323 2324
	ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG,
			       HostCmd_ACT_GEN_SET, 0, &tx_cfg, true);
2325

2326 2327 2328 2329 2330
	if (init) {
		/* set last_init_cmd before sending the command */
		priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG;
		ret = -EINPROGRESS;
	}
2331 2332 2333

	return ret;
}