File Index
Files and directories
Sign in
FILE

sta_ioctl.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c C
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * NXP Wireless LAN device driver: functions for station ioctl
   4 *
   5 * Copyright 2011-2020 NXP
   6 */
   7
   8#include "decl.h"
   9#include "ioctl.h"
  10#include "util.h"
  11#include "fw.h"
  12#include "main.h"
  13#include "wmm.h"
  14#include "11n.h"
  15#include "cfg80211.h"
  16
  17static int disconnect_on_suspend;
  18module_param(disconnect_on_suspend, int, 0644);
  19
  20/*
  21 * Copies the multicast address list from device to driver.
  22 *
  23 * This function does not validate the destination memory for
  24 * size, and the calling function must ensure enough memory is
  25 * available.
  26 */
  27int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
  28			    struct net_device *dev)
  29{
  30	int i = 0;
  31	struct netdev_hw_addr *ha;
  32
  33	netdev_for_each_mc_addr(ha, dev)
  34		memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
  35
  36	return i;
  37}
  38
  39/*
  40 * Wait queue completion handler.
  41 *
  42 * This function waits on a cmd wait queue. It also cancels the pending
  43 * request after waking up, in case of errors.
  44 */
  45int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
  46				struct cmd_ctrl_node *cmd_queued)
  47{
  48	int status;
  49
  50	/* Wait for completion */
  51	status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
  52						  *(cmd_queued->condition),
  53						  (12 * HZ));
  54	if (status <= 0) {
  55		if (status == 0)
  56			status = -ETIMEDOUT;
  57		mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
  58			    status);
  59		mwifiex_cancel_all_pending_cmd(adapter);
  60		return status;
  61	}
  62
  63	status = adapter->cmd_wait_q.status;
  64	adapter->cmd_wait_q.status = 0;
  65
  66	return status;
  67}
  68
  69/*
  70 * This function prepares the correct firmware command and
  71 * issues it to set the multicast list.
  72 *
  73 * This function can be used to enable promiscuous mode, or enable all
  74 * multicast packets, or to enable selective multicast.
  75 */
  76int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
  77				struct mwifiex_multicast_list *mcast_list)
  78{
  79	int ret = 0;
  80	u16 old_pkt_filter;
  81
  82	old_pkt_filter = priv->curr_pkt_filter;
  83
  84	if (mcast_list->mode == MWIFIEX_PROMISC_MODE) {
  85		mwifiex_dbg(priv->adapter, INFO,
  86			    "info: Enable Promiscuous mode\n");
  87		priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
  88		priv->curr_pkt_filter &=
  89			~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
  90	} else {
  91		/* Multicast */
  92		priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
  93		if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) {
  94			mwifiex_dbg(priv->adapter, INFO,
  95				    "info: Enabling All Multicast!\n");
  96			priv->curr_pkt_filter |=
  97				HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
  98		} else {
  99			priv->curr_pkt_filter &=
 100				~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
 101			mwifiex_dbg(priv->adapter, INFO,
 102				    "info: Set multicast list=%d\n",
 103				    mcast_list->num_multicast_addr);
 104			/* Send multicast addresses to firmware */
 105			ret = mwifiex_send_cmd(priv,
 106					       HostCmd_CMD_MAC_MULTICAST_ADR,
 107					       HostCmd_ACT_GEN_SET, 0,
 108					       mcast_list, false);
 109		}
 110	}
 111	mwifiex_dbg(priv->adapter, INFO,
 112		    "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
 113		    old_pkt_filter, priv->curr_pkt_filter);
 114	if (old_pkt_filter != priv->curr_pkt_filter) {
 115		ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
 116				       HostCmd_ACT_GEN_SET,
 117				       0, &priv->curr_pkt_filter, false);
 118	}
 119
 120	return ret;
 121}
 122
 123/*
 124 * This function fills bss descriptor structure using provided
 125 * information.
 126 * beacon_ie buffer is allocated in this function. It is caller's
 127 * responsibility to free the memory.
 128 */
 129int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
 130			      struct cfg80211_bss *bss,
 131			      struct mwifiex_bssdescriptor *bss_desc)
 132{
 133	u8 *beacon_ie;
 134	size_t beacon_ie_len;
 135	struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
 136	const struct cfg80211_bss_ies *ies;
 137
 138	rcu_read_lock();
 139	ies = rcu_dereference(bss->ies);
 140	beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
 141	beacon_ie_len = ies->len;
 142	bss_desc->timestamp = ies->tsf;
 143	rcu_read_unlock();
 144
 145	if (!beacon_ie) {
 146		mwifiex_dbg(priv->adapter, ERROR,
 147			    " failed to alloc beacon_ie\n");
 148		return -ENOMEM;
 149	}
 150
 151	memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
 152	bss_desc->rssi = bss->signal;
 153	/* The caller of this function will free beacon_ie */
 154	bss_desc->beacon_buf = beacon_ie;
 155	bss_desc->beacon_buf_size = beacon_ie_len;
 156	bss_desc->beacon_period = bss->beacon_interval;
 157	bss_desc->cap_info_bitmap = bss->capability;
 158	bss_desc->bss_band = bss_priv->band;
 159	bss_desc->fw_tsf = bss_priv->fw_tsf;
 160	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
 161		mwifiex_dbg(priv->adapter, INFO,
 162			    "info: InterpretIE: AP WEP enabled\n");
 163		bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
 164	} else {
 165		bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
 166	}
 167	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS)
 168		bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
 169	else
 170		bss_desc->bss_mode = NL80211_IFTYPE_STATION;
 171
 172	/* Disable 11ac by default. Enable it only where there
 173	 * exist VHT_CAP IE in AP beacon
 174	 */
 175	bss_desc->disable_11ac = true;
 176
 177	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
 178		bss_desc->sensed_11h = true;
 179
 180	return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
 181}
 182
 183static void mwifiex_dnld_dt_txpwr_table(struct mwifiex_private *priv)
 184{
 185	if (priv->adapter->dt_node) {
 186		char txpwr[] = {"marvell,00_txpwrlimit"};
 187
 188		memcpy(&txpwr[8], priv->adapter->country_code, 2);
 189		mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr);
 190	}
 191}
 192
 193static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
 194{
 195	struct mwifiex_802_11d_domain_reg *domain_info = &priv->adapter->domain_reg;
 196	struct mwifiex_adapter *adapter = priv->adapter;
 197	char rgpower_table_name[30];
 198	char country_code[3];
 199
 200	strscpy(country_code, domain_info->country_code, sizeof(country_code));
 201
 202	/* World regulatory domain "00" has WW as country code */
 203	if (strncmp(country_code, "00", 2) == 0)
 204		strscpy(country_code, "WW", sizeof(country_code));
 205
 206	snprintf(rgpower_table_name, sizeof(rgpower_table_name),
 207		 "nxp/rgpower_%s.bin", country_code);
 208
 209	mwifiex_dbg(adapter, INFO, "info: %s: requesting regulatory power table %s\n",
 210		    __func__, rgpower_table_name);
 211
 212	if (adapter->rgpower_data) {
 213		release_firmware(adapter->rgpower_data);
 214		adapter->rgpower_data = NULL;
 215	}
 216
 217	if ((request_firmware(&adapter->rgpower_data, rgpower_table_name,
 218			      adapter->dev))) {
 219		mwifiex_dbg(
 220			adapter, INFO,
 221			"info: %s: failed to request regulatory power table\n",
 222			__func__);
 223		return -EIO;
 224	}
 225
 226	return 0;
 227}
 228
 229static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv)
 230{
 231	int ret;
 232
 233	ret = mwifiex_request_rgpower_table(priv);
 234	if (ret)
 235		return ret;
 236
 237	return mwifiex_send_rgpower_table(priv, priv->adapter->rgpower_data->data,
 238					  priv->adapter->rgpower_data->size);
 239}
 240
 241void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
 242{
 243	if (mwifiex_dnld_rgpower_table(priv) == 0)
 244		return;
 245
 246	mwifiex_dnld_dt_txpwr_table(priv);
 247}
 248
 249static int mwifiex_process_country_ie(struct mwifiex_private *priv,
 250				      struct cfg80211_bss *bss)
 251{
 252	const u8 *country_ie;
 253	u8 country_ie_len;
 254	struct mwifiex_802_11d_domain_reg *domain_info =
 255					&priv->adapter->domain_reg;
 256
 257	rcu_read_lock();
 258	country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
 259	if (!country_ie) {
 260		rcu_read_unlock();
 261		return 0;
 262	}
 263
 264	country_ie_len = country_ie[1];
 265	if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
 266		rcu_read_unlock();
 267		return 0;
 268	}
 269
 270	if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
 271		rcu_read_unlock();
 272		mwifiex_dbg(priv->adapter, INFO,
 273			    "11D: skip setting domain info in FW\n");
 274		return 0;
 275	}
 276
 277	if (country_ie_len >
 278	    (IEEE80211_COUNTRY_STRING_LEN + MWIFIEX_MAX_TRIPLET_802_11D)) {
 279		rcu_read_unlock();
 280		mwifiex_dbg(priv->adapter, ERROR,
 281			    "11D: country_ie_len overflow!, deauth AP\n");
 282		return -EINVAL;
 283	}
 284
 285	memcpy(priv->adapter->country_code, &country_ie[2], 2);
 286
 287	domain_info->country_code[0] = country_ie[2];
 288	domain_info->country_code[1] = country_ie[3];
 289	domain_info->country_code[2] = ' ';
 290
 291	country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
 292
 293	domain_info->no_of_triplet =
 294		country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
 295
 296	memcpy((u8 *)domain_info->triplet,
 297	       &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
 298
 299	rcu_read_unlock();
 300
 301	if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
 302			     HostCmd_ACT_GEN_SET, 0, NULL, false)) {
 303		mwifiex_dbg(priv->adapter, ERROR,
 304			    "11D: setting domain info in FW fail\n");
 305		return -1;
 306	}
 307
 308	mwifiex_dnld_txpwr_table(priv);
 309
 310	return 0;
 311}
 312
 313/*
 314 * In Ad-Hoc mode, the IBSS is created if not found in scan list.
 315 * In both Ad-Hoc and infra mode, an deauthentication is performed
 316 * first.
 317 */
 318int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
 319		      struct cfg80211_ssid *req_ssid)
 320{
 321	int ret;
 322	struct mwifiex_adapter *adapter = priv->adapter;
 323	struct mwifiex_bssdescriptor *bss_desc = NULL;
 324
 325	priv->scan_block = false;
 326
 327	if (bss) {
 328		if (adapter->region_code == 0x00 &&
 329		    mwifiex_process_country_ie(priv, bss))
 330			return -EINVAL;
 331
 332		/* Allocate and fill new bss descriptor */
 333		bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
 334				   GFP_KERNEL);
 335		if (!bss_desc)
 336			return -ENOMEM;
 337
 338		ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
 339		if (ret)
 340			goto done;
 341	}
 342
 343	if (priv->bss_mode == NL80211_IFTYPE_STATION ||
 344	    priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
 345		u8 config_bands;
 346
 347		if (!bss_desc)
 348			return -1;
 349
 350		if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
 351						HostCmd_SCAN_RADIO_TYPE_BG) {
 352			config_bands = BAND_B | BAND_G | BAND_GN;
 353		} else {
 354			config_bands = BAND_A | BAND_AN;
 355			if (adapter->fw_bands & BAND_AAC)
 356				config_bands |= BAND_AAC;
 357		}
 358
 359		if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
 360			adapter->config_bands = config_bands;
 361
 362		ret = mwifiex_check_network_compatibility(priv, bss_desc);
 363		if (ret)
 364			goto done;
 365
 366		if (mwifiex_11h_get_csa_closed_channel(priv) ==
 367							(u8)bss_desc->channel) {
 368			mwifiex_dbg(adapter, ERROR,
 369				    "Attempt to reconnect on csa closed chan(%d)\n",
 370				    bss_desc->channel);
 371			ret = -1;
 372			goto done;
 373		}
 374
 375		mwifiex_dbg(adapter, INFO,
 376			    "info: SSID found in scan list ...\t"
 377			    "associating...\n");
 378
 379		mwifiex_stop_net_dev_queue(priv->netdev, adapter);
 380		if (netif_carrier_ok(priv->netdev))
 381			netif_carrier_off(priv->netdev);
 382
 383		/* Clear any past association response stored for
 384		 * application retrieval */
 385		priv->assoc_rsp_size = 0;
 386		ret = mwifiex_associate(priv, bss_desc);
 387
 388		/* If auth type is auto and association fails using open mode,
 389		 * try to connect using shared mode */
 390		if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
 391		    priv->sec_info.is_authtype_auto &&
 392		    priv->sec_info.wep_enabled) {
 393			priv->sec_info.authentication_mode =
 394						NL80211_AUTHTYPE_SHARED_KEY;
 395			ret = mwifiex_associate(priv, bss_desc);
 396		}
 397
 398		if (bss && !priv->adapter->host_mlme_enabled)
 399			cfg80211_put_bss(priv->adapter->wiphy, bss);
 400	} else {
 401		/* Adhoc mode */
 402		/* If the requested SSID matches current SSID, return */
 403		if (bss_desc && bss_desc->ssid.ssid_len &&
 404		    cfg80211_ssid_eq(&priv->curr_bss_params.bss_descriptor.ssid,
 405				     &bss_desc->ssid)) {
 406			ret = 0;
 407			goto done;
 408		}
 409
 410		ret = mwifiex_check_network_compatibility(priv, bss_desc);
 411
 412		mwifiex_stop_net_dev_queue(priv->netdev, adapter);
 413		if (netif_carrier_ok(priv->netdev))
 414			netif_carrier_off(priv->netdev);
 415
 416		if (!ret) {
 417			mwifiex_dbg(adapter, INFO,
 418				    "info: network found in scan\t"
 419				    " list. Joining...\n");
 420			ret = mwifiex_adhoc_join(priv, bss_desc);
 421			if (bss)
 422				cfg80211_put_bss(priv->adapter->wiphy, bss);
 423		} else {
 424			mwifiex_dbg(adapter, INFO,
 425				    "info: Network not found in\t"
 426				    "the list, creating adhoc with ssid = %s\n",
 427				    req_ssid->ssid);
 428			ret = mwifiex_adhoc_start(priv, req_ssid);
 429		}
 430	}
 431
 432done:
 433	/* beacon_ie buffer was allocated in function
 434	 * mwifiex_fill_new_bss_desc(). Free it now.
 435	 */
 436	if (bss_desc)
 437		kfree(bss_desc->beacon_buf);
 438	kfree(bss_desc);
 439
 440	if (ret < 0)
 441		priv->attempted_bss_desc = NULL;
 442
 443	return ret;
 444}
 445
 446/*
 447 * IOCTL request handler to set host sleep configuration.
 448 *
 449 * This function prepares the correct firmware command and
 450 * issues it.
 451 */
 452int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
 453			  int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
 454
 455{
 456	struct mwifiex_adapter *adapter = priv->adapter;
 457	int status = 0;
 458	u32 prev_cond = 0;
 459
 460	if (!hs_cfg)
 461		return -ENOMEM;
 462
 463	switch (action) {
 464	case HostCmd_ACT_GEN_SET:
 465		if (adapter->pps_uapsd_mode) {
 466			mwifiex_dbg(adapter, INFO,
 467				    "info: Host Sleep IOCTL\t"
 468				    "is blocked in UAPSD/PPS mode\n");
 469			status = -1;
 470			break;
 471		}
 472		if (hs_cfg->is_invoke_hostcmd) {
 473			if (hs_cfg->conditions == HS_CFG_CANCEL) {
 474				if (!test_bit(MWIFIEX_IS_HS_CONFIGURED,
 475					      &adapter->work_flags))
 476					/* Already cancelled */
 477					break;
 478				/* Save previous condition */
 479				prev_cond = le32_to_cpu(adapter->hs_cfg
 480							.conditions);
 481				adapter->hs_cfg.conditions =
 482						cpu_to_le32(hs_cfg->conditions);
 483			} else if (hs_cfg->conditions) {
 484				adapter->hs_cfg.conditions =
 485						cpu_to_le32(hs_cfg->conditions);
 486				adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
 487				if (hs_cfg->gap)
 488					adapter->hs_cfg.gap = (u8)hs_cfg->gap;
 489			} else if (adapter->hs_cfg.conditions ==
 490				   cpu_to_le32(HS_CFG_CANCEL)) {
 491				/* Return failure if no parameters for HS
 492				   enable */
 493				status = -1;
 494				break;
 495			}
 496
 497			status = mwifiex_send_cmd(priv,
 498						  HostCmd_CMD_802_11_HS_CFG_ENH,
 499						  HostCmd_ACT_GEN_SET, 0,
 500						  &adapter->hs_cfg,
 501						  cmd_type == MWIFIEX_SYNC_CMD);
 502
 503			if (hs_cfg->conditions == HS_CFG_CANCEL)
 504				/* Restore previous condition */
 505				adapter->hs_cfg.conditions =
 506						cpu_to_le32(prev_cond);
 507		} else {
 508			adapter->hs_cfg.conditions =
 509						cpu_to_le32(hs_cfg->conditions);
 510			adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
 511			adapter->hs_cfg.gap = (u8)hs_cfg->gap;
 512		}
 513		break;
 514	case HostCmd_ACT_GEN_GET:
 515		hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
 516		hs_cfg->gpio = adapter->hs_cfg.gpio;
 517		hs_cfg->gap = adapter->hs_cfg.gap;
 518		break;
 519	default:
 520		status = -1;
 521		break;
 522	}
 523
 524	return status;
 525}
 526
 527/*
 528 * Sends IOCTL request to cancel the existing Host Sleep configuration.
 529 *
 530 * This function allocates the IOCTL request buffer, fills it
 531 * with requisite parameters and calls the IOCTL handler.
 532 */
 533int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type)
 534{
 535	struct mwifiex_ds_hs_cfg hscfg;
 536
 537	hscfg.conditions = HS_CFG_CANCEL;
 538	hscfg.is_invoke_hostcmd = true;
 539
 540	return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
 541				    cmd_type, &hscfg);
 542}
 543EXPORT_SYMBOL_GPL(mwifiex_cancel_hs);
 544
 545/*
 546 * Sends IOCTL request to cancel the existing Host Sleep configuration.
 547 *
 548 * This function allocates the IOCTL request buffer, fills it
 549 * with requisite parameters and calls the IOCTL handler.
 550 */
 551int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
 552{
 553	struct mwifiex_ds_hs_cfg hscfg;
 554	struct mwifiex_private *priv;
 555	int i;
 556
 557	if (disconnect_on_suspend) {
 558		for (i = 0; i < adapter->priv_num; i++) {
 559			priv = adapter->priv[i];
 560			mwifiex_deauthenticate(priv, NULL);
 561		}
 562	}
 563
 564	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
 565
 566	if (priv && priv->sched_scanning) {
 567#ifdef CONFIG_PM
 568		if (priv->wdev.wiphy->wowlan_config &&
 569		    !priv->wdev.wiphy->wowlan_config->nd_config) {
 570#endif
 571			mwifiex_dbg(adapter, CMD, "aborting bgscan!\n");
 572			mwifiex_stop_bg_scan(priv);
 573			cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
 574#ifdef CONFIG_PM
 575		}
 576#endif
 577	}
 578
 579	if (adapter->hs_activated) {
 580		mwifiex_dbg(adapter, CMD,
 581			    "cmd: HS Already activated\n");
 582		return true;
 583	}
 584
 585	adapter->hs_activate_wait_q_woken = false;
 586
 587	memset(&hscfg, 0, sizeof(hscfg));
 588	hscfg.is_invoke_hostcmd = true;
 589
 590	set_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags);
 591	mwifiex_cancel_all_pending_cmd(adapter);
 592
 593	if (mwifiex_set_hs_params(mwifiex_get_priv(adapter,
 594						   MWIFIEX_BSS_ROLE_STA),
 595				  HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD,
 596				  &hscfg)) {
 597		mwifiex_dbg(adapter, ERROR,
 598			    "IOCTL request HS enable failed\n");
 599		return false;
 600	}
 601
 602	if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
 603					     adapter->hs_activate_wait_q_woken,
 604					     (5 * HZ)) <= 0) {
 605		mwifiex_dbg(adapter, ERROR,
 606			    "hs_activate_wait_q terminated\n");
 607		return false;
 608	}
 609
 610	return true;
 611}
 612EXPORT_SYMBOL_GPL(mwifiex_enable_hs);
 613
 614/*
 615 * IOCTL request handler to get BSS information.
 616 *
 617 * This function collates the information from different driver structures
 618 * to send to the user.
 619 */
 620int mwifiex_get_bss_info(struct mwifiex_private *priv,
 621			 struct mwifiex_bss_info *info)
 622{
 623	struct mwifiex_adapter *adapter = priv->adapter;
 624	struct mwifiex_bssdescriptor *bss_desc;
 625
 626	if (!info)
 627		return -1;
 628
 629	bss_desc = &priv->curr_bss_params.bss_descriptor;
 630
 631	info->bss_mode = priv->bss_mode;
 632
 633	memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
 634
 635	memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
 636
 637	info->bss_chan = bss_desc->channel;
 638
 639	memcpy(info->country_code, adapter->country_code,
 640	       IEEE80211_COUNTRY_STRING_LEN);
 641
 642	info->media_connected = priv->media_connected;
 643
 644	info->max_power_level = priv->max_tx_power_level;
 645	info->min_power_level = priv->min_tx_power_level;
 646
 647	info->adhoc_state = priv->adhoc_state;
 648
 649	info->bcn_nf_last = priv->bcn_nf_last;
 650
 651	if (priv->sec_info.wep_enabled)
 652		info->wep_status = true;
 653	else
 654		info->wep_status = false;
 655
 656	info->is_hs_configured = test_bit(MWIFIEX_IS_HS_CONFIGURED,
 657					  &adapter->work_flags);
 658	info->is_deep_sleep = adapter->is_deep_sleep;
 659
 660	return 0;
 661}
 662
 663/*
 664 * The function disables auto deep sleep mode.
 665 */
 666int mwifiex_disable_auto_ds(struct mwifiex_private *priv)
 667{
 668	struct mwifiex_ds_auto_ds auto_ds = {
 669		.auto_ds = DEEP_SLEEP_OFF,
 670	};
 671
 672	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
 673				DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
 674}
 675EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds);
 676
 677/*
 678 * Sends IOCTL request to get the data rate.
 679 *
 680 * This function allocates the IOCTL request buffer, fills it
 681 * with requisite parameters and calls the IOCTL handler.
 682 */
 683int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate)
 684{
 685	int ret;
 686
 687	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
 688			       HostCmd_ACT_GEN_GET, 0, NULL, true);
 689
 690	if (!ret) {
 691		if (priv->is_data_rate_auto)
 692			*rate = mwifiex_index_to_data_rate(priv, priv->tx_rate,
 693							   priv->tx_htinfo);
 694		else
 695			*rate = priv->data_rate;
 696	}
 697
 698	return ret;
 699}
 700
 701/*
 702 * IOCTL request handler to set tx power configuration.
 703 *
 704 * This function prepares the correct firmware command and
 705 * issues it.
 706 *
 707 * For non-auto power mode, all the following power groups are set -
 708 *      - Modulation class HR/DSSS
 709 *      - Modulation class OFDM
 710 *      - Modulation class HTBW20
 711 *      - Modulation class HTBW40
 712 */
 713int mwifiex_set_tx_power(struct mwifiex_private *priv,
 714			 struct mwifiex_power_cfg *power_cfg)
 715{
 716	int ret;
 717	struct host_cmd_ds_txpwr_cfg *txp_cfg;
 718	struct mwifiex_types_power_group *pg_tlv;
 719	struct mwifiex_power_group *pg;
 720	u8 *buf;
 721	u16 dbm = 0;
 722
 723	if (!power_cfg->is_power_auto) {
 724		dbm = (u16) power_cfg->power_level;
 725		if ((dbm < priv->min_tx_power_level) ||
 726		    (dbm > priv->max_tx_power_level)) {
 727			mwifiex_dbg(priv->adapter, ERROR,
 728				    "txpower value %d dBm\t"
 729				    "is out of range (%d dBm-%d dBm)\n",
 730				    dbm, priv->min_tx_power_level,
 731				    priv->max_tx_power_level);
 732			return -1;
 733		}
 734	}
 735	buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
 736	if (!buf)
 737		return -ENOMEM;
 738
 739	txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf;
 740	txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
 741	if (!power_cfg->is_power_auto) {
 742		u16 dbm_min = power_cfg->is_power_fixed ?
 743			      dbm : priv->min_tx_power_level;
 744
 745		txp_cfg->mode = cpu_to_le32(1);
 746		pg_tlv = (struct mwifiex_types_power_group *)
 747			 (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
 748		pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
 749		pg_tlv->length =
 750			cpu_to_le16(4 * sizeof(struct mwifiex_power_group));
 751		pg = (struct mwifiex_power_group *)
 752		     (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
 753		      + sizeof(struct mwifiex_types_power_group));
 754		/* Power group for modulation class HR/DSSS */
 755		pg->first_rate_code = 0x00;
 756		pg->last_rate_code = 0x03;
 757		pg->modulation_class = MOD_CLASS_HR_DSSS;
 758		pg->power_step = 0;
 759		pg->power_min = (s8) dbm_min;
 760		pg->power_max = (s8) dbm;
 761		pg++;
 762		/* Power group for modulation class OFDM */
 763		pg->first_rate_code = 0x00;
 764		pg->last_rate_code = 0x07;
 765		pg->modulation_class = MOD_CLASS_OFDM;
 766		pg->power_step = 0;
 767		pg->power_min = (s8) dbm_min;
 768		pg->power_max = (s8) dbm;
 769		pg++;
 770		/* Power group for modulation class HTBW20 */
 771		pg->first_rate_code = 0x00;
 772		pg->last_rate_code = 0x20;
 773		pg->modulation_class = MOD_CLASS_HT;
 774		pg->power_step = 0;
 775		pg->power_min = (s8) dbm_min;
 776		pg->power_max = (s8) dbm;
 777		pg->ht_bandwidth = HT_BW_20;
 778		pg++;
 779		/* Power group for modulation class HTBW40 */
 780		pg->first_rate_code = 0x00;
 781		pg->last_rate_code = 0x20;
 782		pg->modulation_class = MOD_CLASS_HT;
 783		pg->power_step = 0;
 784		pg->power_min = (s8) dbm_min;
 785		pg->power_max = (s8) dbm;
 786		pg->ht_bandwidth = HT_BW_40;
 787	}
 788	ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG,
 789			       HostCmd_ACT_GEN_SET, 0, buf, true);
 790
 791	kfree(buf);
 792	return ret;
 793}
 794
 795/*
 796 * IOCTL request handler to get power save mode.
 797 *
 798 * This function prepares the correct firmware command and
 799 * issues it.
 800 */
 801int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode)
 802{
 803	int ret;
 804	struct mwifiex_adapter *adapter = priv->adapter;
 805	u16 sub_cmd;
 806
 807	if (*ps_mode)
 808		adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
 809	else
 810		adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
 811	sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
 812	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
 813			       sub_cmd, BITMAP_STA_PS, NULL, true);
 814	if ((!ret) && (sub_cmd == DIS_AUTO_PS))
 815		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
 816				       GET_PS, 0, NULL, false);
 817
 818	return ret;
 819}
 820
 821/*
 822 * IOCTL request handler to set/reset WPA IE.
 823 *
 824 * The supplied WPA IE is treated as a opaque buffer. Only the first field
 825 * is checked to determine WPA version. If buffer length is zero, the existing
 826 * WPA IE is reset.
 827 */
 828static int mwifiex_set_wpa_ie(struct mwifiex_private *priv,
 829			      u8 *ie_data_ptr, u16 ie_len)
 830{
 831	if (ie_len) {
 832		if (ie_len > sizeof(priv->wpa_ie)) {
 833			mwifiex_dbg(priv->adapter, ERROR,
 834				    "failed to copy WPA IE, too big\n");
 835			return -1;
 836		}
 837		memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
 838		priv->wpa_ie_len = ie_len;
 839		mwifiex_dbg(priv->adapter, CMD,
 840			    "cmd: Set Wpa_ie_len=%d IE=%#x\n",
 841			    priv->wpa_ie_len, priv->wpa_ie[0]);
 842
 843		if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
 844			priv->sec_info.wpa_enabled = true;
 845		} else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
 846			priv->sec_info.wpa2_enabled = true;
 847		} else {
 848			priv->sec_info.wpa_enabled = false;
 849			priv->sec_info.wpa2_enabled = false;
 850		}
 851	} else {
 852		memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
 853		priv->wpa_ie_len = 0;
 854		mwifiex_dbg(priv->adapter, INFO,
 855			    "info: reset wpa_ie_len=%d IE=%#x\n",
 856			    priv->wpa_ie_len, priv->wpa_ie[0]);
 857		priv->sec_info.wpa_enabled = false;
 858		priv->sec_info.wpa2_enabled = false;
 859	}
 860
 861	return 0;
 862}
 863
 864/*
 865 * IOCTL request handler to set/reset WAPI IE.
 866 *
 867 * The supplied WAPI IE is treated as a opaque buffer. Only the first field
 868 * is checked to internally enable WAPI. If buffer length is zero, the existing
 869 * WAPI IE is reset.
 870 */
 871static int mwifiex_set_wapi_ie(struct mwifiex_private *priv,
 872			       u8 *ie_data_ptr, u16 ie_len)
 873{
 874	if (ie_len) {
 875		if (ie_len > sizeof(priv->wapi_ie)) {
 876			mwifiex_dbg(priv->adapter, ERROR,
 877				    "info: failed to copy WAPI IE, too big\n");
 878			return -1;
 879		}
 880		memcpy(priv->wapi_ie, ie_data_ptr, ie_len);
 881		priv->wapi_ie_len = ie_len;
 882		mwifiex_dbg(priv->adapter, CMD,
 883			    "cmd: Set wapi_ie_len=%d IE=%#x\n",
 884			    priv->wapi_ie_len, priv->wapi_ie[0]);
 885
 886		if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY)
 887			priv->sec_info.wapi_enabled = true;
 888	} else {
 889		memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie));
 890		priv->wapi_ie_len = ie_len;
 891		mwifiex_dbg(priv->adapter, INFO,
 892			    "info: Reset wapi_ie_len=%d IE=%#x\n",
 893			    priv->wapi_ie_len, priv->wapi_ie[0]);
 894		priv->sec_info.wapi_enabled = false;
 895	}
 896	return 0;
 897}
 898
 899/*
 900 * IOCTL request handler to set/reset WPS IE.
 901 *
 902 * The supplied WPS IE is treated as a opaque buffer. Only the first field
 903 * is checked to internally enable WPS. If buffer length is zero, the existing
 904 * WPS IE is reset.
 905 */
 906static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
 907			       u8 *ie_data_ptr, u16 ie_len)
 908{
 909	if (ie_len) {
 910		if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
 911			mwifiex_dbg(priv->adapter, ERROR,
 912				    "info: failed to copy WPS IE, too big\n");
 913			return -1;
 914		}
 915
 916		priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
 917		if (!priv->wps_ie)
 918			return -ENOMEM;
 919
 920		memcpy(priv->wps_ie, ie_data_ptr, ie_len);
 921		priv->wps_ie_len = ie_len;
 922		mwifiex_dbg(priv->adapter, CMD,
 923			    "cmd: Set wps_ie_len=%d IE=%#x\n",
 924			    priv->wps_ie_len, priv->wps_ie[0]);
 925	} else {
 926		kfree(priv->wps_ie);
 927		priv->wps_ie_len = ie_len;
 928		mwifiex_dbg(priv->adapter, INFO,
 929			    "info: Reset wps_ie_len=%d\n", priv->wps_ie_len);
 930	}
 931	return 0;
 932}
 933
 934/*
 935 * IOCTL request handler to set WAPI key.
 936 *
 937 * This function prepares the correct firmware command and
 938 * issues it.
 939 */
 940static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
 941			       struct mwifiex_ds_encrypt_key *encrypt_key)
 942{
 943
 944	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
 945				HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED,
 946				encrypt_key, true);
 947}
 948
 949/*
 950 * IOCTL request handler to set WEP network key.
 951 *
 952 * This function prepares the correct firmware command and
 953 * issues it, after validation checks.
 954 */
 955static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
 956			      struct mwifiex_ds_encrypt_key *encrypt_key)
 957{
 958	struct mwifiex_adapter *adapter = priv->adapter;
 959	int ret;
 960	struct mwifiex_wep_key *wep_key;
 961	int index;
 962
 963	if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
 964		priv->wep_key_curr_index = 0;
 965	wep_key = &priv->wep_key[priv->wep_key_curr_index];
 966	index = encrypt_key->key_index;
 967	if (encrypt_key->key_disable) {
 968		priv->sec_info.wep_enabled = 0;
 969	} else if (!encrypt_key->key_len) {
 970		/* Copy the required key as the current key */
 971		wep_key = &priv->wep_key[index];
 972		if (!wep_key->key_length) {
 973			mwifiex_dbg(adapter, ERROR,
 974				    "key not set, so cannot enable it\n");
 975			return -1;
 976		}
 977
 978		if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) {
 979			memcpy(encrypt_key->key_material,
 980			       wep_key->key_material, wep_key->key_length);
 981			encrypt_key->key_len = wep_key->key_length;
 982		}
 983
 984		priv->wep_key_curr_index = (u16) index;
 985		priv->sec_info.wep_enabled = 1;
 986	} else {
 987		wep_key = &priv->wep_key[index];
 988		memset(wep_key, 0, sizeof(struct mwifiex_wep_key));
 989		/* Copy the key in the driver */
 990		memcpy(wep_key->key_material,
 991		       encrypt_key->key_material,
 992		       encrypt_key->key_len);
 993		wep_key->key_index = index;
 994		wep_key->key_length = encrypt_key->key_len;
 995		priv->sec_info.wep_enabled = 1;
 996	}
 997	if (wep_key->key_length) {
 998		void *enc_key;
 999
1000		if (encrypt_key->key_disable) {
1001			memset(&priv->wep_key[index], 0,
1002			       sizeof(struct mwifiex_wep_key));
1003			goto done;
1004		}
1005
1006		if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
1007			enc_key = encrypt_key;
1008		else
1009			enc_key = NULL;
1010
1011		/* Send request to firmware */
1012		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1013				       HostCmd_ACT_GEN_SET, 0, enc_key, false);
1014		if (ret)
1015			return ret;
1016	}
1017
1018done:
1019	if (priv->sec_info.wep_enabled)
1020		priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
1021	else
1022		priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
1023
1024	ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
1025			       HostCmd_ACT_GEN_SET, 0,
1026			       &priv->curr_pkt_filter, true);
1027
1028	return ret;
1029}
1030
1031/*
1032 * IOCTL request handler to set WPA key.
1033 *
1034 * This function prepares the correct firmware command and
1035 * issues it, after validation checks.
1036 *
1037 * Current driver only supports key length of up to 32 bytes.
1038 *
1039 * This function can also be used to disable a currently set key.
1040 */
1041static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv,
1042			      struct mwifiex_ds_encrypt_key *encrypt_key)
1043{
1044	int ret;
1045	u8 remove_key = false;
1046	struct host_cmd_ds_802_11_key_material *ibss_key;
1047
1048	/* Current driver only supports key length of up to 32 bytes */
1049	if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
1050		mwifiex_dbg(priv->adapter, ERROR,
1051			    "key length too long\n");
1052		return -1;
1053	}
1054
1055	if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
1056		/*
1057		 * IBSS/WPA-None uses only one key (Group) for both receiving
1058		 * and sending unicast and multicast packets.
1059		 */
1060		/* Send the key as PTK to firmware */
1061		encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
1062		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1063				       HostCmd_ACT_GEN_SET,
1064				       KEY_INFO_ENABLED, encrypt_key, false);
1065		if (ret)
1066			return ret;
1067
1068		ibss_key = &priv->aes_key;
1069		memset(ibss_key, 0,
1070		       sizeof(struct host_cmd_ds_802_11_key_material));
1071		/* Copy the key in the driver */
1072		memcpy(ibss_key->key_param_set.key, encrypt_key->key_material,
1073		       encrypt_key->key_len);
1074		memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len,
1075		       sizeof(ibss_key->key_param_set.key_len));
1076		ibss_key->key_param_set.key_type_id
1077			= cpu_to_le16(KEY_TYPE_ID_TKIP);
1078		ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED);
1079
1080		/* Send the key as GTK to firmware */
1081		encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST;
1082	}
1083
1084	if (!encrypt_key->key_index)
1085		encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
1086
1087	if (remove_key)
1088		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1089				       HostCmd_ACT_GEN_SET,
1090				       !KEY_INFO_ENABLED, encrypt_key, true);
1091	else
1092		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1093				       HostCmd_ACT_GEN_SET,
1094				       KEY_INFO_ENABLED, encrypt_key, true);
1095
1096	return ret;
1097}
1098
1099/*
1100 * IOCTL request handler to set/get network keys.
1101 *
1102 * This is a generic key handling function which supports WEP, WPA
1103 * and WAPI.
1104 */
1105static int
1106mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv,
1107			      struct mwifiex_ds_encrypt_key *encrypt_key)
1108{
1109	int status;
1110
1111	if (encrypt_key->is_wapi_key)
1112		status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key);
1113	else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
1114		status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key);
1115	else
1116		status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key);
1117	return status;
1118}
1119
1120/*
1121 * This function returns the driver version.
1122 */
1123int
1124mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
1125			       int max_len)
1126{
1127	union {
1128		__le32 l;
1129		u8 c[4];
1130	} ver;
1131	char fw_ver[32];
1132
1133	ver.l = cpu_to_le32(adapter->fw_release_number);
1134	sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
1135
1136	snprintf(version, max_len, driver_version, fw_ver);
1137
1138	mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version);
1139
1140	return 0;
1141}
1142
1143/*
1144 * Sends IOCTL request to set encoding parameters.
1145 *
1146 * This function allocates the IOCTL request buffer, fills it
1147 * with requisite parameters and calls the IOCTL handler.
1148 */
1149int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
1150		       const u8 *key, int key_len, u8 key_index,
1151		       const u8 *mac_addr, int disable)
1152{
1153	struct mwifiex_ds_encrypt_key encrypt_key;
1154
1155	memset(&encrypt_key, 0, sizeof(encrypt_key));
1156	encrypt_key.key_len = key_len;
1157	encrypt_key.key_index = key_index;
1158
1159	if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
1160		encrypt_key.is_igtk_key = true;
1161
1162	if (!disable) {
1163		if (key_len)
1164			memcpy(encrypt_key.key_material, key, key_len);
1165		else
1166			encrypt_key.is_current_wep_key = true;
1167
1168		if (mac_addr)
1169			memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
1170		if (kp && kp->seq && kp->seq_len) {
1171			memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
1172			encrypt_key.pn_len = kp->seq_len;
1173			encrypt_key.is_rx_seq_valid = true;
1174		}
1175	} else {
1176		encrypt_key.key_disable = true;
1177		if (mac_addr)
1178			memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
1179	}
1180
1181	return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key);
1182}
1183
1184/*
1185 * Sends IOCTL request to get extended version.
1186 *
1187 * This function allocates the IOCTL request buffer, fills it
1188 * with requisite parameters and calls the IOCTL handler.
1189 */
1190int
1191mwifiex_get_ver_ext(struct mwifiex_private *priv, u32 version_str_sel)
1192{
1193	struct mwifiex_ver_ext ver_ext;
1194
1195	memset(&ver_ext, 0, sizeof(ver_ext));
1196	ver_ext.version_str_sel = version_str_sel;
1197	if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
1198			     HostCmd_ACT_GEN_GET, 0, &ver_ext, true))
1199		return -1;
1200
1201	return 0;
1202}
1203
1204int
1205mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
1206			   struct ieee80211_channel *chan,
1207			   unsigned int duration)
1208{
1209	struct host_cmd_ds_remain_on_chan roc_cfg;
1210	u8 sc;
1211
1212	memset(&roc_cfg, 0, sizeof(roc_cfg));
1213	roc_cfg.action = cpu_to_le16(action);
1214	if (action == HostCmd_ACT_GEN_SET) {
1215		roc_cfg.band_cfg = chan->band;
1216		sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
1217		roc_cfg.band_cfg |= (sc << 2);
1218
1219		roc_cfg.channel =
1220			ieee80211_frequency_to_channel(chan->center_freq);
1221		roc_cfg.duration = cpu_to_le32(duration);
1222	}
1223	if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN,
1224			     action, 0, &roc_cfg, true)) {
1225		mwifiex_dbg(priv->adapter, ERROR,
1226			    "failed to remain on channel\n");
1227		return -1;
1228	}
1229
1230	return roc_cfg.status;
1231}
1232
1233/*
1234 * Sends IOCTL request to get statistics information.
1235 *
1236 * This function allocates the IOCTL request buffer, fills it
1237 * with requisite parameters and calls the IOCTL handler.
1238 */
1239int
1240mwifiex_get_stats_info(struct mwifiex_private *priv,
1241		       struct mwifiex_ds_get_stats *log)
1242{
1243	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG,
1244				HostCmd_ACT_GEN_GET, 0, log, true);
1245}
1246
1247/*
1248 * IOCTL request handler to read/write register.
1249 *
1250 * This function prepares the correct firmware command and
1251 * issues it.
1252 *
1253 * Access to the following registers are supported -
1254 *      - MAC
1255 *      - BBP
1256 *      - RF
1257 *      - PMIC
1258 *      - CAU
1259 */
1260static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv,
1261					struct mwifiex_ds_reg_rw *reg_rw,
1262					u16 action)
1263{
1264	u16 cmd_no;
1265
1266	switch (reg_rw->type) {
1267	case MWIFIEX_REG_MAC:
1268		cmd_no = HostCmd_CMD_MAC_REG_ACCESS;
1269		break;
1270	case MWIFIEX_REG_BBP:
1271		cmd_no = HostCmd_CMD_BBP_REG_ACCESS;
1272		break;
1273	case MWIFIEX_REG_RF:
1274		cmd_no = HostCmd_CMD_RF_REG_ACCESS;
1275		break;
1276	case MWIFIEX_REG_PMIC:
1277		cmd_no = HostCmd_CMD_PMIC_REG_ACCESS;
1278		break;
1279	case MWIFIEX_REG_CAU:
1280		cmd_no = HostCmd_CMD_CAU_REG_ACCESS;
1281		break;
1282	default:
1283		return -1;
1284	}
1285
1286	return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
1287}
1288
1289/*
1290 * Sends IOCTL request to write to a register.
1291 *
1292 * This function allocates the IOCTL request buffer, fills it
1293 * with requisite parameters and calls the IOCTL handler.
1294 */
1295int
1296mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type,
1297		  u32 reg_offset, u32 reg_value)
1298{
1299	struct mwifiex_ds_reg_rw reg_rw;
1300
1301	reg_rw.type = reg_type;
1302	reg_rw.offset = reg_offset;
1303	reg_rw.value = reg_value;
1304
1305	return mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_SET);
1306}
1307
1308/*
1309 * Sends IOCTL request to read from a register.
1310 *
1311 * This function allocates the IOCTL request buffer, fills it
1312 * with requisite parameters and calls the IOCTL handler.
1313 */
1314int
1315mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type,
1316		 u32 reg_offset, u32 *value)
1317{
1318	int ret;
1319	struct mwifiex_ds_reg_rw reg_rw;
1320
1321	reg_rw.type = reg_type;
1322	reg_rw.offset = reg_offset;
1323	ret = mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_GET);
1324
1325	if (ret)
1326		goto done;
1327
1328	*value = reg_rw.value;
1329
1330done:
1331	return ret;
1332}
1333
1334/*
1335 * Sends IOCTL request to read from EEPROM.
1336 *
1337 * This function allocates the IOCTL request buffer, fills it
1338 * with requisite parameters and calls the IOCTL handler.
1339 */
1340int
1341mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
1342		    u8 *value)
1343{
1344	int ret;
1345	struct mwifiex_ds_read_eeprom rd_eeprom;
1346
1347	rd_eeprom.offset =  offset;
1348	rd_eeprom.byte_count = bytes;
1349
1350	/* Send request to firmware */
1351	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS,
1352			       HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true);
1353
1354	if (!ret)
1355		memcpy(value, rd_eeprom.value, min((u16)MAX_EEPROM_DATA,
1356		       rd_eeprom.byte_count));
1357	return ret;
1358}
1359
1360/*
1361 * This function sets a generic IE. In addition to generic IE, it can
1362 * also handle WPA, WPA2 and WAPI IEs.
1363 */
1364static int
1365mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
1366			  u16 ie_len)
1367{
1368	struct ieee_types_vendor_header *pvendor_ie;
1369	static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
1370	static const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
1371	u16 unparsed_len = ie_len, cur_ie_len;
1372
1373	/* If the passed length is zero, reset the buffer */
1374	if (!ie_len) {
1375		priv->gen_ie_buf_len = 0;
1376		priv->wps.session_enable = false;
1377		return 0;
1378	} else if (!ie_data_ptr ||
1379		   ie_len <= sizeof(struct ieee_types_header)) {
1380		return -1;
1381	}
1382	pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
1383
1384	while (pvendor_ie) {
1385		cur_ie_len = pvendor_ie->len + sizeof(struct ieee_types_header);
1386
1387		if (pvendor_ie->element_id == WLAN_EID_RSN) {
1388			/* IE is a WPA/WPA2 IE so call set_wpa function */
1389			mwifiex_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len);
1390			priv->wps.session_enable = false;
1391			goto next_ie;
1392		}
1393
1394		if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) {
1395			/* IE is a WAPI IE so call set_wapi function */
1396			mwifiex_set_wapi_ie(priv, (u8 *)pvendor_ie,
1397					    cur_ie_len);
1398			goto next_ie;
1399		}
1400
1401		if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
1402			/* Test to see if it is a WPA IE, if not, then
1403			 * it is a gen IE
1404			 */
1405			if (!memcmp(&pvendor_ie->oui, wpa_oui,
1406				    sizeof(wpa_oui))) {
1407				/* IE is a WPA/WPA2 IE so call set_wpa function
1408				 */
1409				mwifiex_set_wpa_ie(priv, (u8 *)pvendor_ie,
1410						   cur_ie_len);
1411				priv->wps.session_enable = false;
1412				goto next_ie;
1413			}
1414
1415			if (!memcmp(&pvendor_ie->oui, wps_oui,
1416				    sizeof(wps_oui))) {
1417				/* Test to see if it is a WPS IE,
1418				 * if so, enable wps session flag
1419				 */
1420				priv->wps.session_enable = true;
1421				mwifiex_dbg(priv->adapter, MSG,
1422					    "WPS Session Enabled.\n");
1423				mwifiex_set_wps_ie(priv, (u8 *)pvendor_ie,
1424						   cur_ie_len);
1425				goto next_ie;
1426			}
1427		}
1428
1429		/* Saved in gen_ie, such as P2P IE.etc.*/
1430
1431		/* Verify that the passed length is not larger than the
1432		 * available space remaining in the buffer
1433		 */
1434		if (cur_ie_len <
1435		    (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
1436			/* Append the passed data to the end
1437			 * of the genIeBuffer
1438			 */
1439			memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len,
1440			       (u8 *)pvendor_ie, cur_ie_len);
1441			/* Increment the stored buffer length by the
1442			 * size passed
1443			 */
1444			priv->gen_ie_buf_len += cur_ie_len;
1445		}
1446
1447next_ie:
1448		unparsed_len -= cur_ie_len;
1449
1450		if (unparsed_len <= sizeof(struct ieee_types_header))
1451			pvendor_ie = NULL;
1452		else
1453			pvendor_ie = (struct ieee_types_vendor_header *)
1454				(((u8 *)pvendor_ie) + cur_ie_len);
1455	}
1456
1457	return 0;
1458}
1459
1460/*
1461 * IOCTL request handler to set/get generic IE.
1462 *
1463 * In addition to various generic IEs, this function can also be
1464 * used to set the ARP filter.
1465 */
1466static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv,
1467				     struct mwifiex_ds_misc_gen_ie *gen_ie,
1468				     u16 action)
1469{
1470	struct mwifiex_adapter *adapter = priv->adapter;
1471
1472	switch (gen_ie->type) {
1473	case MWIFIEX_IE_TYPE_GEN_IE:
1474		if (action == HostCmd_ACT_GEN_GET) {
1475			gen_ie->len = priv->wpa_ie_len;
1476			memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
1477		} else {
1478			mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data,
1479						  (u16) gen_ie->len);
1480		}
1481		break;
1482	case MWIFIEX_IE_TYPE_ARP_FILTER:
1483		memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
1484		if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
1485			adapter->arp_filter_size = 0;
1486			mwifiex_dbg(adapter, ERROR,
1487				    "invalid ARP filter size\n");
1488			return -1;
1489		} else {
1490			memcpy(adapter->arp_filter, gen_ie->ie_data,
1491			       gen_ie->len);
1492			adapter->arp_filter_size = gen_ie->len;
1493		}
1494		break;
1495	default:
1496		mwifiex_dbg(adapter, ERROR, "invalid IE type\n");
1497		return -1;
1498	}
1499	return 0;
1500}
1501
1502/*
1503 * Sends IOCTL request to set a generic IE.
1504 *
1505 * This function allocates the IOCTL request buffer, fills it
1506 * with requisite parameters and calls the IOCTL handler.
1507 */
1508int
1509mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len)
1510{
1511	struct mwifiex_ds_misc_gen_ie gen_ie;
1512
1513	if (ie_len > IEEE_MAX_IE_SIZE)
1514		return -EFAULT;
1515
1516	gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE;
1517	gen_ie.len = ie_len;
1518	memcpy(gen_ie.ie_data, ie, ie_len);
1519	if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET))
1520		return -EFAULT;
1521
1522	return 0;
1523}
1524
1525/* This function get Host Sleep wake up reason.
1526 *
1527 */
1528int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action,
1529			      int cmd_type,
1530			      struct mwifiex_ds_wakeup_reason *wakeup_reason)
1531{
1532	int status = 0;
1533
1534	status = mwifiex_send_cmd(priv, HostCmd_CMD_HS_WAKEUP_REASON,
1535				  HostCmd_ACT_GEN_GET, 0, wakeup_reason,
1536				  cmd_type == MWIFIEX_SYNC_CMD);
1537
1538	return status;
1539}
1540
1541int mwifiex_get_chan_info(struct mwifiex_private *priv,
1542			  struct mwifiex_channel_band *channel_band)
1543{
1544	int status = 0;
1545
1546	status = mwifiex_send_cmd(priv, HostCmd_CMD_STA_CONFIGURE,
1547				  HostCmd_ACT_GEN_GET, 0, channel_band,
1548				  MWIFIEX_SYNC_CMD);
1549
1550	return status;
1551}
~/ Read-only file preview.