xref: /linux/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
173b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
273b01e57SJeff Chen /*
373b01e57SJeff Chen  * nxpwifi: functions for station ioctl
473b01e57SJeff Chen  *
573b01e57SJeff Chen  * Copyright 2011-2024 NXP
673b01e57SJeff Chen  */
773b01e57SJeff Chen 
873b01e57SJeff Chen #include "cfg.h"
973b01e57SJeff Chen #include "util.h"
1073b01e57SJeff Chen #include "fw.h"
1173b01e57SJeff Chen #include "main.h"
1273b01e57SJeff Chen #include "cmdevt.h"
1373b01e57SJeff Chen #include "wmm.h"
1473b01e57SJeff Chen #include "11n.h"
1573b01e57SJeff Chen #include "cfg80211.h"
1673b01e57SJeff Chen 
1773b01e57SJeff Chen static int disconnect_on_suspend;
1873b01e57SJeff Chen 
1973b01e57SJeff Chen /* Copies the multicast address list from device to driver */
nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list * mlist,struct net_device * dev)2073b01e57SJeff Chen int nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list *mlist,
2173b01e57SJeff Chen 			    struct net_device *dev)
2273b01e57SJeff Chen {
2373b01e57SJeff Chen 	int i = 0;
2473b01e57SJeff Chen 	struct netdev_hw_addr *ha;
2573b01e57SJeff Chen 
2673b01e57SJeff Chen 	netdev_for_each_mc_addr(ha, dev)
2773b01e57SJeff Chen 		memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
2873b01e57SJeff Chen 
2973b01e57SJeff Chen 	return i;
3073b01e57SJeff Chen }
3173b01e57SJeff Chen 
3273b01e57SJeff Chen /* Wait queue completion handler */
nxpwifi_wait_queue_complete(struct nxpwifi_adapter * adapter,struct cmd_ctrl_node * cmd_queued)3373b01e57SJeff Chen int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter,
3473b01e57SJeff Chen 				struct cmd_ctrl_node *cmd_queued)
3573b01e57SJeff Chen {
3673b01e57SJeff Chen 	int status;
3773b01e57SJeff Chen 
3873b01e57SJeff Chen 	/* Wait for completion */
3973b01e57SJeff Chen 	status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
4073b01e57SJeff Chen 						  *cmd_queued->condition,
4173b01e57SJeff Chen 						  (12 * HZ));
4273b01e57SJeff Chen 	if (status <= 0) {
4373b01e57SJeff Chen 		if (status == 0)
4473b01e57SJeff Chen 			status = -ETIMEDOUT;
4573b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
4673b01e57SJeff Chen 			    status);
4773b01e57SJeff Chen 		nxpwifi_cancel_all_pending_cmd(adapter);
48*068986fdSLinmao Li 
49*068986fdSLinmao Li 		/*
50*068986fdSLinmao Li 		 * The response path writes through cmd_node->data_buf. The
51*068986fdSLinmao Li 		 * caller can release a stack-allocated data_buf after an
52*068986fdSLinmao Li 		 * interrupted wait while a late response is still pending.
53*068986fdSLinmao Li 		 * Detach it from the current command before returning.
54*068986fdSLinmao Li 		 */
55*068986fdSLinmao Li 		spin_lock_bh(&adapter->nxpwifi_cmd_lock);
56*068986fdSLinmao Li 		if (adapter->curr_cmd == cmd_queued)
57*068986fdSLinmao Li 			adapter->curr_cmd->data_buf = NULL;
58*068986fdSLinmao Li 		spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
59*068986fdSLinmao Li 
6073b01e57SJeff Chen 		return status;
6173b01e57SJeff Chen 	}
6273b01e57SJeff Chen 
6373b01e57SJeff Chen 	status = adapter->cmd_wait_q.status;
6473b01e57SJeff Chen 	adapter->cmd_wait_q.status = 0;
6573b01e57SJeff Chen 
6673b01e57SJeff Chen 	return status;
6773b01e57SJeff Chen }
6873b01e57SJeff Chen 
6973b01e57SJeff Chen /* Set multicast list by issuing the proper firmware command */
7073b01e57SJeff Chen int
nxpwifi_request_set_multicast_list(struct nxpwifi_private * priv,struct nxpwifi_multicast_list * mcast_list)7173b01e57SJeff Chen nxpwifi_request_set_multicast_list(struct nxpwifi_private *priv,
7273b01e57SJeff Chen 				   struct nxpwifi_multicast_list *mcast_list)
7373b01e57SJeff Chen {
7473b01e57SJeff Chen 	int ret = 0;
7573b01e57SJeff Chen 	u16 old_pkt_filter;
7673b01e57SJeff Chen 
7773b01e57SJeff Chen 	old_pkt_filter = priv->curr_pkt_filter;
7873b01e57SJeff Chen 
7973b01e57SJeff Chen 	if (mcast_list->mode == NXPWIFI_PROMISC_MODE) {
8073b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
8173b01e57SJeff Chen 			    "info: Enable Promiscuous mode\n");
8273b01e57SJeff Chen 		priv->curr_pkt_filter |= HOST_ACT_MAC_PROMISCUOUS_ENABLE;
8373b01e57SJeff Chen 		priv->curr_pkt_filter &=
8473b01e57SJeff Chen 			~HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
8573b01e57SJeff Chen 	} else {
8673b01e57SJeff Chen 		/* Multicast */
8773b01e57SJeff Chen 		priv->curr_pkt_filter &= ~HOST_ACT_MAC_PROMISCUOUS_ENABLE;
8873b01e57SJeff Chen 		if (mcast_list->mode == NXPWIFI_ALL_MULTI_MODE) {
8973b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, INFO,
9073b01e57SJeff Chen 				    "info: Enabling All Multicast!\n");
9173b01e57SJeff Chen 			priv->curr_pkt_filter |=
9273b01e57SJeff Chen 				HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
9373b01e57SJeff Chen 		} else {
9473b01e57SJeff Chen 			priv->curr_pkt_filter &=
9573b01e57SJeff Chen 				~HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
9673b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, INFO,
9773b01e57SJeff Chen 				    "info: Set multicast list=%d\n",
9873b01e57SJeff Chen 				    mcast_list->num_multicast_addr);
9973b01e57SJeff Chen 			/* Send multicast addresses to firmware */
10073b01e57SJeff Chen 			ret = nxpwifi_send_cmd(priv,
10173b01e57SJeff Chen 					       HOST_CMD_MAC_MULTICAST_ADR,
10273b01e57SJeff Chen 					       HOST_ACT_GEN_SET, 0,
10373b01e57SJeff Chen 					       mcast_list, false);
10473b01e57SJeff Chen 		}
10573b01e57SJeff Chen 	}
10673b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, INFO,
10773b01e57SJeff Chen 		    "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
10873b01e57SJeff Chen 		    old_pkt_filter, priv->curr_pkt_filter);
10973b01e57SJeff Chen 	if (old_pkt_filter != priv->curr_pkt_filter) {
11073b01e57SJeff Chen 		ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
11173b01e57SJeff Chen 				       HOST_ACT_GEN_SET,
11273b01e57SJeff Chen 				       0, &priv->curr_pkt_filter, false);
11373b01e57SJeff Chen 	}
11473b01e57SJeff Chen 
11573b01e57SJeff Chen 	return ret;
11673b01e57SJeff Chen }
11773b01e57SJeff Chen 
11873b01e57SJeff Chen /* Fill BSS descriptor from cfg80211_bss */
nxpwifi_fill_new_bss_desc(struct nxpwifi_private * priv,struct cfg80211_bss * bss,struct nxpwifi_bssdescriptor * bss_desc)11973b01e57SJeff Chen int nxpwifi_fill_new_bss_desc(struct nxpwifi_private *priv,
12073b01e57SJeff Chen 			      struct cfg80211_bss *bss,
12173b01e57SJeff Chen 			      struct nxpwifi_bssdescriptor *bss_desc)
12273b01e57SJeff Chen {
12373b01e57SJeff Chen 	u8 *beacon_ie;
12473b01e57SJeff Chen 	size_t beacon_ie_len;
12573b01e57SJeff Chen 	struct nxpwifi_bss_priv *bss_priv = (void *)bss->priv;
12673b01e57SJeff Chen 	const struct cfg80211_bss_ies *ies;
12773b01e57SJeff Chen 
12873b01e57SJeff Chen 	rcu_read_lock();
12973b01e57SJeff Chen 	ies = rcu_dereference(bss->ies);
13073b01e57SJeff Chen 	beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
13173b01e57SJeff Chen 	beacon_ie_len = ies->len;
13273b01e57SJeff Chen 	bss_desc->timestamp = ies->tsf;
13373b01e57SJeff Chen 	rcu_read_unlock();
13473b01e57SJeff Chen 
13573b01e57SJeff Chen 	if (!beacon_ie) {
13673b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
13773b01e57SJeff Chen 			    " failed to alloc beacon_ie\n");
13873b01e57SJeff Chen 		return -ENOMEM;
13973b01e57SJeff Chen 	}
14073b01e57SJeff Chen 
14173b01e57SJeff Chen 	memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
14273b01e57SJeff Chen 	bss_desc->rssi = bss->signal;
14373b01e57SJeff Chen 	/* The caller of this function will free beacon_ie */
14473b01e57SJeff Chen 	bss_desc->beacon_buf = beacon_ie;
14573b01e57SJeff Chen 	bss_desc->beacon_buf_size = beacon_ie_len;
14673b01e57SJeff Chen 	bss_desc->beacon_period = bss->beacon_interval;
14773b01e57SJeff Chen 	bss_desc->cap_info_bitmap = bss->capability;
14873b01e57SJeff Chen 	bss_desc->bss_band = bss_priv->band;
14973b01e57SJeff Chen 	bss_desc->fw_tsf = bss_priv->fw_tsf;
15073b01e57SJeff Chen 	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
15173b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
15273b01e57SJeff Chen 			    "info: InterpretIE: AP WEP enabled\n");
15373b01e57SJeff Chen 		bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_8021X_WEP;
15473b01e57SJeff Chen 	} else {
15573b01e57SJeff Chen 		bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_ACCEPT_ALL;
15673b01e57SJeff Chen 	}
15773b01e57SJeff Chen 	bss_desc->bss_mode = NL80211_IFTYPE_STATION;
15873b01e57SJeff Chen 
15973b01e57SJeff Chen 	/* Disable 11ac by default */
16073b01e57SJeff Chen 	bss_desc->disable_11ac = true;
16173b01e57SJeff Chen 	/* Disable 11ax by default */
16273b01e57SJeff Chen 	bss_desc->disable_11ax = true;
16373b01e57SJeff Chen 
16473b01e57SJeff Chen 	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
16573b01e57SJeff Chen 		bss_desc->sensed_11h = true;
16673b01e57SJeff Chen 
16773b01e57SJeff Chen 	return nxpwifi_update_bss_desc_with_ie(priv->adapter, bss_desc);
16873b01e57SJeff Chen }
16973b01e57SJeff Chen 
nxpwifi_process_country_ie(struct nxpwifi_private * priv,struct cfg80211_bss * bss)17073b01e57SJeff Chen static int nxpwifi_process_country_ie(struct nxpwifi_private *priv,
17173b01e57SJeff Chen 				      struct cfg80211_bss *bss)
17273b01e57SJeff Chen {
17373b01e57SJeff Chen 	const u8 *country_ie;
17473b01e57SJeff Chen 	u8 country_ie_len;
17573b01e57SJeff Chen 	struct nxpwifi_802_11d_domain_reg *domain_info =
17673b01e57SJeff Chen 					&priv->adapter->domain_reg;
17773b01e57SJeff Chen 	int ret;
17873b01e57SJeff Chen 
17973b01e57SJeff Chen 	rcu_read_lock();
18073b01e57SJeff Chen 	country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
18173b01e57SJeff Chen 	if (!country_ie) {
18273b01e57SJeff Chen 		rcu_read_unlock();
18373b01e57SJeff Chen 		return 0;
18473b01e57SJeff Chen 	}
18573b01e57SJeff Chen 
18673b01e57SJeff Chen 	country_ie_len = country_ie[1];
18773b01e57SJeff Chen 	if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
18873b01e57SJeff Chen 		rcu_read_unlock();
18973b01e57SJeff Chen 		return 0;
19073b01e57SJeff Chen 	}
19173b01e57SJeff Chen 
19273b01e57SJeff Chen 	if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
19373b01e57SJeff Chen 		rcu_read_unlock();
19473b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
19573b01e57SJeff Chen 			    "11D: skip setting domain info in FW\n");
19673b01e57SJeff Chen 		return 0;
19773b01e57SJeff Chen 	}
19873b01e57SJeff Chen 
19973b01e57SJeff Chen 	if (country_ie_len >
20073b01e57SJeff Chen 	    (IEEE80211_COUNTRY_STRING_LEN + NXPWIFI_MAX_TRIPLET_802_11D)) {
20173b01e57SJeff Chen 		rcu_read_unlock();
20273b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
20373b01e57SJeff Chen 			    "11D: country_ie_len overflow!, deauth AP\n");
20473b01e57SJeff Chen 		return -EINVAL;
20573b01e57SJeff Chen 	}
20673b01e57SJeff Chen 
20773b01e57SJeff Chen 	memcpy(priv->adapter->country_code, &country_ie[2], 2);
20873b01e57SJeff Chen 
20973b01e57SJeff Chen 	domain_info->country_code[0] = country_ie[2];
21073b01e57SJeff Chen 	domain_info->country_code[1] = country_ie[3];
21173b01e57SJeff Chen 	domain_info->country_code[2] = ' ';
21273b01e57SJeff Chen 
21373b01e57SJeff Chen 	country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
21473b01e57SJeff Chen 
21573b01e57SJeff Chen 	domain_info->no_of_triplet =
21673b01e57SJeff Chen 		country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
21773b01e57SJeff Chen 
21873b01e57SJeff Chen 	memcpy((u8 *)domain_info->triplet,
21973b01e57SJeff Chen 	       &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
22073b01e57SJeff Chen 
22173b01e57SJeff Chen 	rcu_read_unlock();
22273b01e57SJeff Chen 
22373b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11D_DOMAIN_INFO,
22473b01e57SJeff Chen 			       HOST_ACT_GEN_SET, 0, NULL, false);
22573b01e57SJeff Chen 	if (ret)
22673b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
22773b01e57SJeff Chen 			    "11D: setting domain info in FW fail\n");
22873b01e57SJeff Chen 
22973b01e57SJeff Chen 	return ret;
23073b01e57SJeff Chen }
23173b01e57SJeff Chen 
23273b01e57SJeff Chen /* In infra mode, an deauthentication is performed first */
nxpwifi_bss_start(struct nxpwifi_private * priv,struct cfg80211_bss * bss,struct cfg80211_ssid * req_ssid)23373b01e57SJeff Chen int nxpwifi_bss_start(struct nxpwifi_private *priv, struct cfg80211_bss *bss,
23473b01e57SJeff Chen 		      struct cfg80211_ssid *req_ssid)
23573b01e57SJeff Chen {
23673b01e57SJeff Chen 	int ret;
23773b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
23873b01e57SJeff Chen 	struct nxpwifi_bssdescriptor *bss_desc = NULL;
23973b01e57SJeff Chen 	u16 config_bands;
24073b01e57SJeff Chen 
24173b01e57SJeff Chen 	priv->scan_block = false;
24273b01e57SJeff Chen 
24373b01e57SJeff Chen 	if (adapter->region_code == 0x00 &&
24473b01e57SJeff Chen 	    nxpwifi_process_country_ie(priv, bss))
24573b01e57SJeff Chen 		return -EINVAL;
24673b01e57SJeff Chen 
24773b01e57SJeff Chen 	/* Allocate and fill new bss descriptor */
24873b01e57SJeff Chen 	bss_desc = kzalloc_obj(*bss_desc, GFP_KERNEL);
24973b01e57SJeff Chen 	if (!bss_desc)
25073b01e57SJeff Chen 		return -ENOMEM;
25173b01e57SJeff Chen 
25273b01e57SJeff Chen 	ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc);
25373b01e57SJeff Chen 	if (ret)
25473b01e57SJeff Chen 		goto done;
25573b01e57SJeff Chen 
25673b01e57SJeff Chen 	if (nxpwifi_band_to_radio_type(bss_desc->bss_band) ==
25773b01e57SJeff Chen 				       HOST_SCAN_RADIO_TYPE_BG) {
25873b01e57SJeff Chen 		config_bands = BAND_B | BAND_G | BAND_GN;
25973b01e57SJeff Chen 		if (adapter->fw_bands & BAND_GAC)
26073b01e57SJeff Chen 			config_bands |= BAND_GAC;
26173b01e57SJeff Chen 		if (adapter->fw_bands & BAND_GAX)
26273b01e57SJeff Chen 			config_bands |= BAND_GAX;
26373b01e57SJeff Chen 	} else {
26473b01e57SJeff Chen 		config_bands = BAND_A | BAND_AN;
26573b01e57SJeff Chen 		if (adapter->fw_bands & BAND_AAC)
26673b01e57SJeff Chen 			config_bands |= BAND_AAC;
26773b01e57SJeff Chen 		if (adapter->fw_bands & BAND_AAX)
26873b01e57SJeff Chen 			config_bands |= BAND_AAX;
26973b01e57SJeff Chen 	}
27073b01e57SJeff Chen 
27173b01e57SJeff Chen 	if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
27273b01e57SJeff Chen 		priv->config_bands = config_bands;
27373b01e57SJeff Chen 
27473b01e57SJeff Chen 	ret = nxpwifi_check_network_compatibility(priv, bss_desc);
27573b01e57SJeff Chen 	if (ret)
27673b01e57SJeff Chen 		goto done;
27773b01e57SJeff Chen 
27873b01e57SJeff Chen 	if (nxpwifi_11h_get_csa_closed_channel(priv) == (u8)bss_desc->channel) {
27973b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
28073b01e57SJeff Chen 			    "Attempt to reconnect on csa closed chan(%d)\n",
28173b01e57SJeff Chen 			    bss_desc->channel);
28273b01e57SJeff Chen 		ret = -EINVAL;
28373b01e57SJeff Chen 		goto done;
28473b01e57SJeff Chen 	}
28573b01e57SJeff Chen 
28673b01e57SJeff Chen 	nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
28773b01e57SJeff Chen 	netif_carrier_off(priv->netdev);
28873b01e57SJeff Chen 
28973b01e57SJeff Chen 	/* Clear any past association response stored for application retrieval */
29073b01e57SJeff Chen 	priv->assoc_rsp_size = 0;
29173b01e57SJeff Chen 	ret = nxpwifi_associate(priv, bss_desc);
29273b01e57SJeff Chen 
29373b01e57SJeff Chen 	/*
29473b01e57SJeff Chen 	 * If auth type is auto and association fails using open mode, try to connect
29573b01e57SJeff Chen 	 * using shared mode
29673b01e57SJeff Chen 	 */
29773b01e57SJeff Chen 	if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
29873b01e57SJeff Chen 	    priv->sec_info.is_authtype_auto &&
29973b01e57SJeff Chen 	    priv->sec_info.wep_enabled) {
30073b01e57SJeff Chen 		priv->sec_info.authentication_mode =
30173b01e57SJeff Chen 			NL80211_AUTHTYPE_SHARED_KEY;
30273b01e57SJeff Chen 		ret = nxpwifi_associate(priv, bss_desc);
30373b01e57SJeff Chen 	}
30473b01e57SJeff Chen 
30573b01e57SJeff Chen done:
30673b01e57SJeff Chen 	/* beacon_ie buffer was allocated in function nxpwifi_fill_new_bss_desc() */
30773b01e57SJeff Chen 	if (bss_desc)
30873b01e57SJeff Chen 		kfree(bss_desc->beacon_buf);
30973b01e57SJeff Chen 	kfree(bss_desc);
31073b01e57SJeff Chen 
31173b01e57SJeff Chen 	if (ret < 0)
31273b01e57SJeff Chen 		priv->attempted_bss_desc = NULL;
31373b01e57SJeff Chen 
31473b01e57SJeff Chen 	return ret;
31573b01e57SJeff Chen }
31673b01e57SJeff Chen 
31773b01e57SJeff Chen /* IOCTL request handler to set host sleep configuration */
nxpwifi_set_hs_params(struct nxpwifi_private * priv,u16 action,int cmd_type,struct nxpwifi_ds_hs_cfg * hs_cfg)31873b01e57SJeff Chen int nxpwifi_set_hs_params(struct nxpwifi_private *priv, u16 action,
31973b01e57SJeff Chen 			  int cmd_type, struct nxpwifi_ds_hs_cfg *hs_cfg)
32073b01e57SJeff Chen 
32173b01e57SJeff Chen {
32273b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
32373b01e57SJeff Chen 	int status = 0;
32473b01e57SJeff Chen 	u32 prev_cond = 0;
32573b01e57SJeff Chen 
32673b01e57SJeff Chen 	if (!hs_cfg)
32773b01e57SJeff Chen 		return -ENOMEM;
32873b01e57SJeff Chen 
32973b01e57SJeff Chen 	switch (action) {
33073b01e57SJeff Chen 	case HOST_ACT_GEN_SET:
33173b01e57SJeff Chen 		if (adapter->pps_uapsd_mode) {
33273b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
33373b01e57SJeff Chen 				    "info: Host Sleep IOCTL\t"
33473b01e57SJeff Chen 				    "is blocked in UAPSD/PPS mode\n");
33573b01e57SJeff Chen 			status = -EPERM;
33673b01e57SJeff Chen 			break;
33773b01e57SJeff Chen 		}
33873b01e57SJeff Chen 		if (hs_cfg->is_invoke_hostcmd) {
33973b01e57SJeff Chen 			if (hs_cfg->conditions == HS_CFG_CANCEL) {
34073b01e57SJeff Chen 				if (!test_bit(NXPWIFI_IS_HS_CONFIGURED,
34173b01e57SJeff Chen 					      &adapter->work_flags))
34273b01e57SJeff Chen 					/* Already cancelled */
34373b01e57SJeff Chen 					break;
34473b01e57SJeff Chen 				/* Save previous condition */
34573b01e57SJeff Chen 				prev_cond = le32_to_cpu(adapter->hs_cfg
34673b01e57SJeff Chen 							.conditions);
34773b01e57SJeff Chen 				adapter->hs_cfg.conditions =
34873b01e57SJeff Chen 						cpu_to_le32(hs_cfg->conditions);
34973b01e57SJeff Chen 			} else if (hs_cfg->conditions) {
35073b01e57SJeff Chen 				adapter->hs_cfg.conditions =
35173b01e57SJeff Chen 						cpu_to_le32(hs_cfg->conditions);
35273b01e57SJeff Chen 				adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
35373b01e57SJeff Chen 				if (hs_cfg->gap)
35473b01e57SJeff Chen 					adapter->hs_cfg.gap = (u8)hs_cfg->gap;
35573b01e57SJeff Chen 			} else if (adapter->hs_cfg.conditions ==
35673b01e57SJeff Chen 				   cpu_to_le32(HS_CFG_CANCEL)) {
35773b01e57SJeff Chen 				status = -EINVAL;
35873b01e57SJeff Chen 				break;
35973b01e57SJeff Chen 			}
36073b01e57SJeff Chen 
36173b01e57SJeff Chen 			status = nxpwifi_send_cmd(priv,
36273b01e57SJeff Chen 						  HOST_CMD_802_11_HS_CFG_ENH,
36373b01e57SJeff Chen 						  HOST_ACT_GEN_SET, 0,
36473b01e57SJeff Chen 						  &adapter->hs_cfg,
36573b01e57SJeff Chen 						  cmd_type == NXPWIFI_SYNC_CMD);
36673b01e57SJeff Chen 
36773b01e57SJeff Chen 			if (hs_cfg->conditions == HS_CFG_CANCEL)
36873b01e57SJeff Chen 				/* Restore previous condition */
36973b01e57SJeff Chen 				adapter->hs_cfg.conditions =
37073b01e57SJeff Chen 						cpu_to_le32(prev_cond);
37173b01e57SJeff Chen 		} else {
37273b01e57SJeff Chen 			adapter->hs_cfg.conditions =
37373b01e57SJeff Chen 						cpu_to_le32(hs_cfg->conditions);
37473b01e57SJeff Chen 			adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
37573b01e57SJeff Chen 			adapter->hs_cfg.gap = (u8)hs_cfg->gap;
37673b01e57SJeff Chen 		}
37773b01e57SJeff Chen 		break;
37873b01e57SJeff Chen 	case HOST_ACT_GEN_GET:
37973b01e57SJeff Chen 		hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
38073b01e57SJeff Chen 		hs_cfg->gpio = adapter->hs_cfg.gpio;
38173b01e57SJeff Chen 		hs_cfg->gap = adapter->hs_cfg.gap;
38273b01e57SJeff Chen 		break;
38373b01e57SJeff Chen 	default:
38473b01e57SJeff Chen 		status = -EINVAL;
38573b01e57SJeff Chen 		break;
38673b01e57SJeff Chen 	}
38773b01e57SJeff Chen 
38873b01e57SJeff Chen 	return status;
38973b01e57SJeff Chen }
39073b01e57SJeff Chen 
39173b01e57SJeff Chen /* Sends IOCTL request to cancel the existing Host Sleep configuration */
nxpwifi_cancel_hs(struct nxpwifi_private * priv,int cmd_type)39273b01e57SJeff Chen int nxpwifi_cancel_hs(struct nxpwifi_private *priv, int cmd_type)
39373b01e57SJeff Chen {
39473b01e57SJeff Chen 	struct nxpwifi_ds_hs_cfg hscfg;
39573b01e57SJeff Chen 
39673b01e57SJeff Chen 	hscfg.conditions = HS_CFG_CANCEL;
39773b01e57SJeff Chen 	hscfg.is_invoke_hostcmd = true;
39873b01e57SJeff Chen 
39973b01e57SJeff Chen 	return nxpwifi_set_hs_params(priv, HOST_ACT_GEN_SET,
40073b01e57SJeff Chen 				    cmd_type, &hscfg);
40173b01e57SJeff Chen }
40273b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_cancel_hs);
40373b01e57SJeff Chen 
40473b01e57SJeff Chen /* Sends IOCTL request to cancel the existing Host Sleep configuration */
nxpwifi_enable_hs(struct nxpwifi_adapter * adapter)40573b01e57SJeff Chen bool nxpwifi_enable_hs(struct nxpwifi_adapter *adapter)
40673b01e57SJeff Chen {
40773b01e57SJeff Chen 	struct nxpwifi_ds_hs_cfg hscfg;
40873b01e57SJeff Chen 	struct nxpwifi_private *priv;
40973b01e57SJeff Chen 	int i;
41073b01e57SJeff Chen 
41173b01e57SJeff Chen 	if (disconnect_on_suspend) {
41273b01e57SJeff Chen 		for (i = 0; i < adapter->priv_num; i++) {
41373b01e57SJeff Chen 			priv = adapter->priv[i];
41473b01e57SJeff Chen 			nxpwifi_deauthenticate(priv, NULL);
41573b01e57SJeff Chen 		}
41673b01e57SJeff Chen 	}
41773b01e57SJeff Chen 
41873b01e57SJeff Chen 	priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
41973b01e57SJeff Chen 
42073b01e57SJeff Chen 	if (priv && priv->sched_scanning) {
42173b01e57SJeff Chen #ifdef CONFIG_PM
42273b01e57SJeff Chen 		if (priv->wdev.wiphy->wowlan_config &&
42373b01e57SJeff Chen 		    !priv->wdev.wiphy->wowlan_config->nd_config) {
42473b01e57SJeff Chen #endif
42573b01e57SJeff Chen 			nxpwifi_dbg(adapter, CMD, "aborting bgscan!\n");
42673b01e57SJeff Chen 			nxpwifi_stop_bg_scan(priv);
42773b01e57SJeff Chen 			cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
42873b01e57SJeff Chen #ifdef CONFIG_PM
42973b01e57SJeff Chen 		}
43073b01e57SJeff Chen #endif
43173b01e57SJeff Chen 	}
43273b01e57SJeff Chen 
43373b01e57SJeff Chen 	if (adapter->hs_activated) {
43473b01e57SJeff Chen 		nxpwifi_dbg(adapter, CMD,
43573b01e57SJeff Chen 			    "cmd: HS Already activated\n");
43673b01e57SJeff Chen 		return true;
43773b01e57SJeff Chen 	}
43873b01e57SJeff Chen 
43973b01e57SJeff Chen 	adapter->hs_activate_wait_q_woken = false;
44073b01e57SJeff Chen 
44173b01e57SJeff Chen 	memset(&hscfg, 0, sizeof(hscfg));
44273b01e57SJeff Chen 	hscfg.is_invoke_hostcmd = true;
44373b01e57SJeff Chen 
44473b01e57SJeff Chen 	set_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags);
44573b01e57SJeff Chen 	nxpwifi_cancel_all_pending_cmd(adapter);
44673b01e57SJeff Chen 
44773b01e57SJeff Chen 	if (nxpwifi_set_hs_params(nxpwifi_get_priv(adapter,
44873b01e57SJeff Chen 						   NXPWIFI_BSS_ROLE_STA),
44973b01e57SJeff Chen 				  HOST_ACT_GEN_SET, NXPWIFI_SYNC_CMD,
45073b01e57SJeff Chen 				  &hscfg)) {
45173b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
45273b01e57SJeff Chen 			    "IOCTL request HS enable failed\n");
45373b01e57SJeff Chen 		return false;
45473b01e57SJeff Chen 	}
45573b01e57SJeff Chen 
45673b01e57SJeff Chen 	if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
45773b01e57SJeff Chen 					     adapter->hs_activate_wait_q_woken,
45873b01e57SJeff Chen 					     (10 * HZ)) <= 0) {
45973b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
46073b01e57SJeff Chen 			    "hs_activate_wait_q terminated\n");
46173b01e57SJeff Chen 		return false;
46273b01e57SJeff Chen 	}
46373b01e57SJeff Chen 
46473b01e57SJeff Chen 	return true;
46573b01e57SJeff Chen }
46673b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_enable_hs);
46773b01e57SJeff Chen 
46873b01e57SJeff Chen /* IOCTL request handler to get BSS information */
nxpwifi_get_bss_info(struct nxpwifi_private * priv,struct nxpwifi_bss_info * info)46973b01e57SJeff Chen int nxpwifi_get_bss_info(struct nxpwifi_private *priv,
47073b01e57SJeff Chen 			 struct nxpwifi_bss_info *info)
47173b01e57SJeff Chen {
47273b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
47373b01e57SJeff Chen 	struct nxpwifi_bssdescriptor *bss_desc;
47473b01e57SJeff Chen 
47573b01e57SJeff Chen 	if (!info)
47673b01e57SJeff Chen 		return -EINVAL;
47773b01e57SJeff Chen 
47873b01e57SJeff Chen 	bss_desc = &priv->curr_bss_params.bss_descriptor;
47973b01e57SJeff Chen 
48073b01e57SJeff Chen 	info->bss_mode = priv->bss_mode;
48173b01e57SJeff Chen 
48273b01e57SJeff Chen 	memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
48373b01e57SJeff Chen 
48473b01e57SJeff Chen 	memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
48573b01e57SJeff Chen 
48673b01e57SJeff Chen 	info->bss_chan = bss_desc->channel;
48773b01e57SJeff Chen 
48873b01e57SJeff Chen 	memcpy(info->country_code, adapter->country_code,
48973b01e57SJeff Chen 	       IEEE80211_COUNTRY_STRING_LEN);
49073b01e57SJeff Chen 
49173b01e57SJeff Chen 	info->media_connected = priv->media_connected;
49273b01e57SJeff Chen 
49373b01e57SJeff Chen 	info->max_power_level = priv->max_tx_power_level;
49473b01e57SJeff Chen 	info->min_power_level = priv->min_tx_power_level;
49573b01e57SJeff Chen 
49673b01e57SJeff Chen 	info->bcn_nf_last = priv->bcn_nf_last;
49773b01e57SJeff Chen 
49873b01e57SJeff Chen 	if (priv->sec_info.wep_enabled)
49973b01e57SJeff Chen 		info->wep_status = true;
50073b01e57SJeff Chen 	else
50173b01e57SJeff Chen 		info->wep_status = false;
50273b01e57SJeff Chen 
50373b01e57SJeff Chen 	info->is_hs_configured = test_bit(NXPWIFI_IS_HS_CONFIGURED,
50473b01e57SJeff Chen 					  &adapter->work_flags);
50573b01e57SJeff Chen 	info->is_deep_sleep = adapter->is_deep_sleep;
50673b01e57SJeff Chen 
50773b01e57SJeff Chen 	return 0;
50873b01e57SJeff Chen }
50973b01e57SJeff Chen 
51073b01e57SJeff Chen /* The function disables auto deep sleep mode */
nxpwifi_disable_auto_ds(struct nxpwifi_private * priv)51173b01e57SJeff Chen int nxpwifi_disable_auto_ds(struct nxpwifi_private *priv)
51273b01e57SJeff Chen {
51373b01e57SJeff Chen 	struct nxpwifi_ds_auto_ds auto_ds = {
51473b01e57SJeff Chen 		.auto_ds = DEEP_SLEEP_OFF,
51573b01e57SJeff Chen 	};
51673b01e57SJeff Chen 
51773b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
51873b01e57SJeff Chen 				DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
51973b01e57SJeff Chen }
52073b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_disable_auto_ds);
52173b01e57SJeff Chen 
52273b01e57SJeff Chen /* Sends IOCTL request to get the data rate */
nxpwifi_drv_get_data_rate(struct nxpwifi_private * priv,u32 * rate)52373b01e57SJeff Chen int nxpwifi_drv_get_data_rate(struct nxpwifi_private *priv, u32 *rate)
52473b01e57SJeff Chen {
52573b01e57SJeff Chen 	int ret;
52673b01e57SJeff Chen 
52773b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_TX_RATE_QUERY,
52873b01e57SJeff Chen 			       HOST_ACT_GEN_GET, 0, NULL, true);
52973b01e57SJeff Chen 
53073b01e57SJeff Chen 	if (!ret) {
53173b01e57SJeff Chen 		if (priv->is_data_rate_auto)
53273b01e57SJeff Chen 			*rate = nxpwifi_index_to_data_rate(priv, priv->tx_rate,
53373b01e57SJeff Chen 							   priv->tx_htinfo);
53473b01e57SJeff Chen 		else
53573b01e57SJeff Chen 			*rate = priv->data_rate;
53673b01e57SJeff Chen 	}
53773b01e57SJeff Chen 
53873b01e57SJeff Chen 	return ret;
53973b01e57SJeff Chen }
54073b01e57SJeff Chen 
54173b01e57SJeff Chen /* IOCTL request handler to set tx power configuration */
nxpwifi_set_tx_power(struct nxpwifi_private * priv,struct nxpwifi_power_cfg * power_cfg)54273b01e57SJeff Chen int nxpwifi_set_tx_power(struct nxpwifi_private *priv,
54373b01e57SJeff Chen 			 struct nxpwifi_power_cfg *power_cfg)
54473b01e57SJeff Chen {
54573b01e57SJeff Chen 	int ret;
54673b01e57SJeff Chen 	struct host_cmd_ds_txpwr_cfg *txp_cfg;
54773b01e57SJeff Chen 	struct nxpwifi_types_power_group *pg_tlv;
54873b01e57SJeff Chen 	struct nxpwifi_power_group *pg;
54973b01e57SJeff Chen 	u8 *buf;
55073b01e57SJeff Chen 	u16 dbm = 0;
55173b01e57SJeff Chen 
55273b01e57SJeff Chen 	if (!power_cfg->is_power_auto) {
55373b01e57SJeff Chen 		dbm = (u16)power_cfg->power_level;
55473b01e57SJeff Chen 		if (dbm < priv->min_tx_power_level ||
55573b01e57SJeff Chen 		    dbm > priv->max_tx_power_level) {
55673b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, ERROR,
55773b01e57SJeff Chen 				    "txpower value %d dBm\t"
55873b01e57SJeff Chen 				    "is out of range (%d dBm-%d dBm)\n",
55973b01e57SJeff Chen 				    dbm, priv->min_tx_power_level,
56073b01e57SJeff Chen 				    priv->max_tx_power_level);
56173b01e57SJeff Chen 			return -EINVAL;
56273b01e57SJeff Chen 		}
56373b01e57SJeff Chen 	}
56473b01e57SJeff Chen 	buf = kzalloc(NXPWIFI_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
56573b01e57SJeff Chen 	if (!buf)
56673b01e57SJeff Chen 		return -ENOMEM;
56773b01e57SJeff Chen 
56873b01e57SJeff Chen 	txp_cfg = (struct host_cmd_ds_txpwr_cfg *)buf;
56973b01e57SJeff Chen 	txp_cfg->action = cpu_to_le16(HOST_ACT_GEN_SET);
57073b01e57SJeff Chen 	if (!power_cfg->is_power_auto) {
57173b01e57SJeff Chen 		u16 dbm_min = power_cfg->is_power_fixed ?
57273b01e57SJeff Chen 			      dbm : priv->min_tx_power_level;
57373b01e57SJeff Chen 
57473b01e57SJeff Chen 		txp_cfg->mode = cpu_to_le32(1);
57573b01e57SJeff Chen 		pg_tlv = (struct nxpwifi_types_power_group *)
57673b01e57SJeff Chen 			 (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
57773b01e57SJeff Chen 		pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
57873b01e57SJeff Chen 		pg_tlv->length =
57973b01e57SJeff Chen 			cpu_to_le16(4 * sizeof(struct nxpwifi_power_group));
58073b01e57SJeff Chen 		pg = (struct nxpwifi_power_group *)
58173b01e57SJeff Chen 		     (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
58273b01e57SJeff Chen 		      + sizeof(struct nxpwifi_types_power_group));
58373b01e57SJeff Chen 		/* Power group for modulation class HR/DSSS */
58473b01e57SJeff Chen 		pg->first_rate_code = 0x00;
58573b01e57SJeff Chen 		pg->last_rate_code = 0x03;
58673b01e57SJeff Chen 		pg->modulation_class = MOD_CLASS_HR_DSSS;
58773b01e57SJeff Chen 		pg->power_step = 0;
58873b01e57SJeff Chen 		pg->power_min = (s8)dbm_min;
58973b01e57SJeff Chen 		pg->power_max = (s8)dbm;
59073b01e57SJeff Chen 		pg++;
59173b01e57SJeff Chen 		/* Power group for modulation class OFDM */
59273b01e57SJeff Chen 		pg->first_rate_code = 0x00;
59373b01e57SJeff Chen 		pg->last_rate_code = 0x07;
59473b01e57SJeff Chen 		pg->modulation_class = MOD_CLASS_OFDM;
59573b01e57SJeff Chen 		pg->power_step = 0;
59673b01e57SJeff Chen 		pg->power_min = (s8)dbm_min;
59773b01e57SJeff Chen 		pg->power_max = (s8)dbm;
59873b01e57SJeff Chen 		pg++;
59973b01e57SJeff Chen 		/* Power group for modulation class HTBW20 */
60073b01e57SJeff Chen 		pg->first_rate_code = 0x00;
60173b01e57SJeff Chen 		pg->last_rate_code = 0x20;
60273b01e57SJeff Chen 		pg->modulation_class = MOD_CLASS_HT;
60373b01e57SJeff Chen 		pg->power_step = 0;
60473b01e57SJeff Chen 		pg->power_min = (s8)dbm_min;
60573b01e57SJeff Chen 		pg->power_max = (s8)dbm;
60673b01e57SJeff Chen 		pg->ht_bandwidth = HT_BW_20;
60773b01e57SJeff Chen 		pg++;
60873b01e57SJeff Chen 		/* Power group for modulation class HTBW40 */
60973b01e57SJeff Chen 		pg->first_rate_code = 0x00;
61073b01e57SJeff Chen 		pg->last_rate_code = 0x20;
61173b01e57SJeff Chen 		pg->modulation_class = MOD_CLASS_HT;
61273b01e57SJeff Chen 		pg->power_step = 0;
61373b01e57SJeff Chen 		pg->power_min = (s8)dbm_min;
61473b01e57SJeff Chen 		pg->power_max = (s8)dbm;
61573b01e57SJeff Chen 		pg->ht_bandwidth = HT_BW_40;
61673b01e57SJeff Chen 	}
61773b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_TXPWR_CFG,
61873b01e57SJeff Chen 			       HOST_ACT_GEN_SET, 0, buf, true);
61973b01e57SJeff Chen 
62073b01e57SJeff Chen 	kfree(buf);
62173b01e57SJeff Chen 	return ret;
62273b01e57SJeff Chen }
62373b01e57SJeff Chen 
62473b01e57SJeff Chen /* IOCTL request handler to get power save mode */
nxpwifi_drv_set_power(struct nxpwifi_private * priv,u32 * ps_mode)62573b01e57SJeff Chen int nxpwifi_drv_set_power(struct nxpwifi_private *priv, u32 *ps_mode)
62673b01e57SJeff Chen {
62773b01e57SJeff Chen 	int ret;
62873b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
62973b01e57SJeff Chen 	u16 sub_cmd;
63073b01e57SJeff Chen 
63173b01e57SJeff Chen 	if (*ps_mode)
63273b01e57SJeff Chen 		adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP;
63373b01e57SJeff Chen 	else
63473b01e57SJeff Chen 		adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM;
63573b01e57SJeff Chen 	sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
63673b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
63773b01e57SJeff Chen 			       sub_cmd, BITMAP_STA_PS, NULL, true);
63873b01e57SJeff Chen 	if (!ret && sub_cmd == DIS_AUTO_PS)
63973b01e57SJeff Chen 		ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
64073b01e57SJeff Chen 				       GET_PS, 0, NULL, false);
64173b01e57SJeff Chen 
64273b01e57SJeff Chen 	return ret;
64373b01e57SJeff Chen }
64473b01e57SJeff Chen 
64573b01e57SJeff Chen /* IOCTL request handler to set/reset WPA element */
nxpwifi_set_wpa_ie(struct nxpwifi_private * priv,u8 * ie_data_ptr,u16 ie_len)64673b01e57SJeff Chen static int nxpwifi_set_wpa_ie(struct nxpwifi_private *priv,
64773b01e57SJeff Chen 			      u8 *ie_data_ptr, u16 ie_len)
64873b01e57SJeff Chen {
64973b01e57SJeff Chen 	if (ie_len) {
65073b01e57SJeff Chen 		if (ie_len > sizeof(priv->wpa_ie)) {
65173b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, ERROR,
65273b01e57SJeff Chen 				    "failed to copy WPA element, too big\n");
65373b01e57SJeff Chen 			return -EINVAL;
65473b01e57SJeff Chen 		}
65573b01e57SJeff Chen 		memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
65673b01e57SJeff Chen 		priv->wpa_ie_len = ie_len;
65773b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, CMD,
65873b01e57SJeff Chen 			    "cmd: Set WPA element len=%d element=%#x\n",
65973b01e57SJeff Chen 			    priv->wpa_ie_len, priv->wpa_ie[0]);
66073b01e57SJeff Chen 
66173b01e57SJeff Chen 		if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
66273b01e57SJeff Chen 			priv->sec_info.wpa_enabled = true;
66373b01e57SJeff Chen 		} else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
66473b01e57SJeff Chen 			priv->sec_info.wpa2_enabled = true;
66573b01e57SJeff Chen 		} else {
66673b01e57SJeff Chen 			priv->sec_info.wpa_enabled = false;
66773b01e57SJeff Chen 			priv->sec_info.wpa2_enabled = false;
66873b01e57SJeff Chen 		}
66973b01e57SJeff Chen 	} else {
67073b01e57SJeff Chen 		memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
67173b01e57SJeff Chen 		priv->wpa_ie_len = 0;
67273b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
67373b01e57SJeff Chen 			    "info: reset WPA element len=%d element=%#x\n",
67473b01e57SJeff Chen 			    priv->wpa_ie_len, priv->wpa_ie[0]);
67573b01e57SJeff Chen 		priv->sec_info.wpa_enabled = false;
67673b01e57SJeff Chen 		priv->sec_info.wpa2_enabled = false;
67773b01e57SJeff Chen 	}
67873b01e57SJeff Chen 
67973b01e57SJeff Chen 	return 0;
68073b01e57SJeff Chen }
68173b01e57SJeff Chen 
68273b01e57SJeff Chen /* IOCTL request handler to set/reset WPS element */
nxpwifi_set_wps_ie(struct nxpwifi_private * priv,u8 * ie_data_ptr,u16 ie_len)68373b01e57SJeff Chen static int nxpwifi_set_wps_ie(struct nxpwifi_private *priv,
68473b01e57SJeff Chen 			      u8 *ie_data_ptr, u16 ie_len)
68573b01e57SJeff Chen {
68673b01e57SJeff Chen 	if (ie_len) {
68773b01e57SJeff Chen 		if (ie_len > NXPWIFI_MAX_VSIE_LEN) {
68873b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, ERROR,
68973b01e57SJeff Chen 				    "info: failed to copy WPS element, too big\n");
69073b01e57SJeff Chen 			return -EINVAL;
69173b01e57SJeff Chen 		}
69273b01e57SJeff Chen 
69373b01e57SJeff Chen 		priv->wps_ie = kzalloc(NXPWIFI_MAX_VSIE_LEN, GFP_KERNEL);
69473b01e57SJeff Chen 		if (!priv->wps_ie)
69573b01e57SJeff Chen 			return -ENOMEM;
69673b01e57SJeff Chen 
69773b01e57SJeff Chen 		memcpy(priv->wps_ie, ie_data_ptr, ie_len);
69873b01e57SJeff Chen 		priv->wps_ie_len = ie_len;
69973b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, CMD,
70073b01e57SJeff Chen 			    "cmd: Set WPS element len=%d element=%#x\n",
70173b01e57SJeff Chen 			    priv->wps_ie_len, priv->wps_ie[0]);
70273b01e57SJeff Chen 	} else {
70373b01e57SJeff Chen 		kfree(priv->wps_ie);
70473b01e57SJeff Chen 		priv->wps_ie_len = ie_len;
70573b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
70673b01e57SJeff Chen 			    "info: Reset WPS element len=%d\n", priv->wps_ie_len);
70773b01e57SJeff Chen 	}
70873b01e57SJeff Chen 	return 0;
70973b01e57SJeff Chen }
71073b01e57SJeff Chen 
71173b01e57SJeff Chen /* IOCTL request handler to set WEP network key */
71273b01e57SJeff Chen static int
nxpwifi_sec_ioctl_set_wep_key(struct nxpwifi_private * priv,struct nxpwifi_ds_encrypt_key * encrypt_key)71373b01e57SJeff Chen nxpwifi_sec_ioctl_set_wep_key(struct nxpwifi_private *priv,
71473b01e57SJeff Chen 			      struct nxpwifi_ds_encrypt_key *encrypt_key)
71573b01e57SJeff Chen {
71673b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
71773b01e57SJeff Chen 	int ret;
71873b01e57SJeff Chen 	struct nxpwifi_wep_key *wep_key;
71973b01e57SJeff Chen 	int index;
72073b01e57SJeff Chen 
72173b01e57SJeff Chen 	if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
72273b01e57SJeff Chen 		priv->wep_key_curr_index = 0;
72373b01e57SJeff Chen 	wep_key = &priv->wep_key[priv->wep_key_curr_index];
72473b01e57SJeff Chen 	index = encrypt_key->key_index;
72573b01e57SJeff Chen 	if (encrypt_key->key_disable) {
72673b01e57SJeff Chen 		priv->sec_info.wep_enabled = 0;
72773b01e57SJeff Chen 	} else if (!encrypt_key->key_len) {
72873b01e57SJeff Chen 		/* Copy the required key as the current key */
72973b01e57SJeff Chen 		wep_key = &priv->wep_key[index];
73073b01e57SJeff Chen 		if (!wep_key->key_length) {
73173b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
73273b01e57SJeff Chen 				    "key not set, so cannot enable it\n");
73373b01e57SJeff Chen 			return -EINVAL;
73473b01e57SJeff Chen 		}
73573b01e57SJeff Chen 
73673b01e57SJeff Chen 		memcpy(encrypt_key->key_material,
73773b01e57SJeff Chen 		       wep_key->key_material, wep_key->key_length);
73873b01e57SJeff Chen 		encrypt_key->key_len = wep_key->key_length;
73973b01e57SJeff Chen 
74073b01e57SJeff Chen 		priv->wep_key_curr_index = (u16)index;
74173b01e57SJeff Chen 		priv->sec_info.wep_enabled = 1;
74273b01e57SJeff Chen 	} else {
74373b01e57SJeff Chen 		wep_key = &priv->wep_key[index];
74473b01e57SJeff Chen 		memset(wep_key, 0, sizeof(struct nxpwifi_wep_key));
74573b01e57SJeff Chen 		/* Copy the key in the driver */
74673b01e57SJeff Chen 		memcpy(wep_key->key_material,
74773b01e57SJeff Chen 		       encrypt_key->key_material,
74873b01e57SJeff Chen 		       encrypt_key->key_len);
74973b01e57SJeff Chen 		wep_key->key_index = index;
75073b01e57SJeff Chen 		wep_key->key_length = encrypt_key->key_len;
75173b01e57SJeff Chen 		priv->sec_info.wep_enabled = 1;
75273b01e57SJeff Chen 	}
75373b01e57SJeff Chen 	if (wep_key->key_length) {
75473b01e57SJeff Chen 		void *enc_key;
75573b01e57SJeff Chen 
75673b01e57SJeff Chen 		if (encrypt_key->key_disable) {
75773b01e57SJeff Chen 			memset(&priv->wep_key[index], 0,
75873b01e57SJeff Chen 			       sizeof(struct nxpwifi_wep_key));
75973b01e57SJeff Chen 			goto done;
76073b01e57SJeff Chen 		}
76173b01e57SJeff Chen 
76273b01e57SJeff Chen 		enc_key = encrypt_key;
76373b01e57SJeff Chen 
76473b01e57SJeff Chen 		/* Send request to firmware */
76573b01e57SJeff Chen 		ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
76673b01e57SJeff Chen 				       HOST_ACT_GEN_SET, 0, enc_key, false);
76773b01e57SJeff Chen 		if (ret)
76873b01e57SJeff Chen 			return ret;
76973b01e57SJeff Chen 	}
77073b01e57SJeff Chen 
77173b01e57SJeff Chen done:
77273b01e57SJeff Chen 	if (priv->sec_info.wep_enabled)
77373b01e57SJeff Chen 		priv->curr_pkt_filter |= HOST_ACT_MAC_WEP_ENABLE;
77473b01e57SJeff Chen 	else
77573b01e57SJeff Chen 		priv->curr_pkt_filter &= ~HOST_ACT_MAC_WEP_ENABLE;
77673b01e57SJeff Chen 
77773b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
77873b01e57SJeff Chen 			       HOST_ACT_GEN_SET, 0,
77973b01e57SJeff Chen 			       &priv->curr_pkt_filter, true);
78073b01e57SJeff Chen 
78173b01e57SJeff Chen 	return ret;
78273b01e57SJeff Chen }
78373b01e57SJeff Chen 
78473b01e57SJeff Chen /* IOCTL request handler to set WPA key */
78573b01e57SJeff Chen static int
nxpwifi_sec_ioctl_set_wpa_key(struct nxpwifi_private * priv,struct nxpwifi_ds_encrypt_key * encrypt_key)78673b01e57SJeff Chen nxpwifi_sec_ioctl_set_wpa_key(struct nxpwifi_private *priv,
78773b01e57SJeff Chen 			      struct nxpwifi_ds_encrypt_key *encrypt_key)
78873b01e57SJeff Chen {
78973b01e57SJeff Chen 	int ret;
79073b01e57SJeff Chen 	u8 remove_key = false;
79173b01e57SJeff Chen 
79273b01e57SJeff Chen 	/* Current driver only supports key length of up to 32 bytes */
79373b01e57SJeff Chen 	if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
79473b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
79573b01e57SJeff Chen 			    "key length too long\n");
79673b01e57SJeff Chen 		return -EINVAL;
79773b01e57SJeff Chen 	}
79873b01e57SJeff Chen 
79973b01e57SJeff Chen 	if (!encrypt_key->key_index)
80073b01e57SJeff Chen 		encrypt_key->key_index = NXPWIFI_KEY_INDEX_UNICAST;
80173b01e57SJeff Chen 
80273b01e57SJeff Chen 	if (remove_key)
80373b01e57SJeff Chen 		ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
80473b01e57SJeff Chen 				       HOST_ACT_GEN_SET,
80573b01e57SJeff Chen 				       !KEY_INFO_ENABLED, encrypt_key, true);
80673b01e57SJeff Chen 	else
80773b01e57SJeff Chen 		ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
80873b01e57SJeff Chen 				       HOST_ACT_GEN_SET,
80973b01e57SJeff Chen 				       KEY_INFO_ENABLED, encrypt_key, true);
81073b01e57SJeff Chen 
81173b01e57SJeff Chen 	return ret;
81273b01e57SJeff Chen }
81373b01e57SJeff Chen 
81473b01e57SJeff Chen /* IOCTL request handler to set/get network keys */
81573b01e57SJeff Chen static int
nxpwifi_sec_ioctl_encrypt_key(struct nxpwifi_private * priv,struct nxpwifi_ds_encrypt_key * encrypt_key)81673b01e57SJeff Chen nxpwifi_sec_ioctl_encrypt_key(struct nxpwifi_private *priv,
81773b01e57SJeff Chen 			      struct nxpwifi_ds_encrypt_key *encrypt_key)
81873b01e57SJeff Chen {
81973b01e57SJeff Chen 	int status;
82073b01e57SJeff Chen 
82173b01e57SJeff Chen 	if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
82273b01e57SJeff Chen 		status = nxpwifi_sec_ioctl_set_wpa_key(priv, encrypt_key);
82373b01e57SJeff Chen 	else
82473b01e57SJeff Chen 		status = nxpwifi_sec_ioctl_set_wep_key(priv, encrypt_key);
82573b01e57SJeff Chen 
82673b01e57SJeff Chen 	return status;
82773b01e57SJeff Chen }
82873b01e57SJeff Chen 
82973b01e57SJeff Chen /* Return driver version string */
83073b01e57SJeff Chen int
nxpwifi_drv_get_driver_version(struct nxpwifi_adapter * adapter,char * version,int max_len)83173b01e57SJeff Chen nxpwifi_drv_get_driver_version(struct nxpwifi_adapter *adapter, char *version,
83273b01e57SJeff Chen 			       int max_len)
83373b01e57SJeff Chen {
83473b01e57SJeff Chen 	union {
83573b01e57SJeff Chen 		__le32 l;
83673b01e57SJeff Chen 		u8 c[4];
83773b01e57SJeff Chen 	} ver;
83873b01e57SJeff Chen 	char fw_ver[32];
83973b01e57SJeff Chen 
84073b01e57SJeff Chen 	ver.l = cpu_to_le32(adapter->fw_release_number);
84173b01e57SJeff Chen 	sprintf(fw_ver, "%u.%u.%u.p%u.%u", ver.c[2], ver.c[1],
84273b01e57SJeff Chen 		ver.c[0], ver.c[3], adapter->fw_hotfix_ver);
84373b01e57SJeff Chen 
84473b01e57SJeff Chen 	snprintf(version, max_len, nxpwifi_driver_version, fw_ver);
84573b01e57SJeff Chen 
84673b01e57SJeff Chen 	nxpwifi_dbg(adapter, MSG, "info: NXPWIFI VERSION: %s\n", version);
84773b01e57SJeff Chen 
84873b01e57SJeff Chen 	return 0;
84973b01e57SJeff Chen }
85073b01e57SJeff Chen 
85173b01e57SJeff Chen /* Sends IOCTL request to set encoding parameters */
nxpwifi_set_encode(struct nxpwifi_private * priv,struct key_params * kp,const u8 * key,int key_len,u8 key_index,const u8 * mac_addr,int disable)85273b01e57SJeff Chen int nxpwifi_set_encode(struct nxpwifi_private *priv, struct key_params *kp,
85373b01e57SJeff Chen 		       const u8 *key, int key_len, u8 key_index,
85473b01e57SJeff Chen 		       const u8 *mac_addr, int disable)
85573b01e57SJeff Chen {
85673b01e57SJeff Chen 	struct nxpwifi_ds_encrypt_key encrypt_key;
85773b01e57SJeff Chen 
85873b01e57SJeff Chen 	memset(&encrypt_key, 0, sizeof(encrypt_key));
85973b01e57SJeff Chen 	encrypt_key.key_len = key_len;
86073b01e57SJeff Chen 	encrypt_key.key_index = key_index;
86173b01e57SJeff Chen 
86273b01e57SJeff Chen 	if (kp) {
86373b01e57SJeff Chen 		encrypt_key.key_cipher = kp->cipher;
86473b01e57SJeff Chen 		if (kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
86573b01e57SJeff Chen 		    kp->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256)
86673b01e57SJeff Chen 			encrypt_key.is_igtk_key = true;
86773b01e57SJeff Chen 	}
86873b01e57SJeff Chen 
86973b01e57SJeff Chen 	if (!disable) {
87073b01e57SJeff Chen 		if (key_len)
87173b01e57SJeff Chen 			memcpy(encrypt_key.key_material, key, key_len);
87273b01e57SJeff Chen 		else
87373b01e57SJeff Chen 			encrypt_key.is_current_wep_key = true;
87473b01e57SJeff Chen 
87573b01e57SJeff Chen 		if (mac_addr)
87673b01e57SJeff Chen 			memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
87773b01e57SJeff Chen 		if (kp && kp->seq && kp->seq_len) {
87873b01e57SJeff Chen 			memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
87973b01e57SJeff Chen 			encrypt_key.pn_len = kp->seq_len;
88073b01e57SJeff Chen 			encrypt_key.is_rx_seq_valid = true;
88173b01e57SJeff Chen 		}
88273b01e57SJeff Chen 	} else {
88373b01e57SJeff Chen 		encrypt_key.key_disable = true;
88473b01e57SJeff Chen 		if (mac_addr)
88573b01e57SJeff Chen 			memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
88673b01e57SJeff Chen 	}
88773b01e57SJeff Chen 
88873b01e57SJeff Chen 	return nxpwifi_sec_ioctl_encrypt_key(priv, &encrypt_key);
88973b01e57SJeff Chen }
89073b01e57SJeff Chen 
89173b01e57SJeff Chen /* Sends IOCTL request to get extended version */
89273b01e57SJeff Chen int
nxpwifi_get_ver_ext(struct nxpwifi_private * priv,u32 version_str_sel)89373b01e57SJeff Chen nxpwifi_get_ver_ext(struct nxpwifi_private *priv, u32 version_str_sel)
89473b01e57SJeff Chen {
89573b01e57SJeff Chen 	struct nxpwifi_ver_ext ver_ext;
89673b01e57SJeff Chen 
89773b01e57SJeff Chen 	memset(&ver_ext, 0, sizeof(ver_ext));
89873b01e57SJeff Chen 	ver_ext.version_str_sel = version_str_sel;
89973b01e57SJeff Chen 
90073b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_VERSION_EXT,
90173b01e57SJeff Chen 				HOST_ACT_GEN_GET, 0, &ver_ext, true);
90273b01e57SJeff Chen }
90373b01e57SJeff Chen 
90473b01e57SJeff Chen int
nxpwifi_remain_on_chan_cfg(struct nxpwifi_private * priv,u16 action,struct ieee80211_channel * chan,unsigned int duration)90573b01e57SJeff Chen nxpwifi_remain_on_chan_cfg(struct nxpwifi_private *priv, u16 action,
90673b01e57SJeff Chen 			   struct ieee80211_channel *chan,
90773b01e57SJeff Chen 			   unsigned int duration)
90873b01e57SJeff Chen {
90973b01e57SJeff Chen 	struct host_cmd_ds_remain_on_chan roc_cfg;
91073b01e57SJeff Chen 	u8 sc;
91173b01e57SJeff Chen 	int ret;
91273b01e57SJeff Chen 
91373b01e57SJeff Chen 	memset(&roc_cfg, 0, sizeof(roc_cfg));
91473b01e57SJeff Chen 	roc_cfg.action = cpu_to_le16(action);
91573b01e57SJeff Chen 	if (action == HOST_ACT_GEN_SET) {
91673b01e57SJeff Chen 		roc_cfg.band_cfg = chan->band;
91773b01e57SJeff Chen 		sc = nxpwifi_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
91873b01e57SJeff Chen 		roc_cfg.band_cfg |= (sc << 2);
91973b01e57SJeff Chen 
92073b01e57SJeff Chen 		roc_cfg.channel =
92173b01e57SJeff Chen 			ieee80211_frequency_to_channel(chan->center_freq);
92273b01e57SJeff Chen 		roc_cfg.duration = cpu_to_le32(duration);
92373b01e57SJeff Chen 	}
92473b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_REMAIN_ON_CHAN,
92573b01e57SJeff Chen 			       action, 0, &roc_cfg, true);
92673b01e57SJeff Chen 	if (ret) {
92773b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
92873b01e57SJeff Chen 			    "failed to remain on channel\n");
92973b01e57SJeff Chen 		return ret;
93073b01e57SJeff Chen 	}
93173b01e57SJeff Chen 
93273b01e57SJeff Chen 	return roc_cfg.status;
93373b01e57SJeff Chen }
93473b01e57SJeff Chen 
93573b01e57SJeff Chen /* Sends IOCTL request to get statistics information */
93673b01e57SJeff Chen int
nxpwifi_get_stats_info(struct nxpwifi_private * priv,struct nxpwifi_ds_get_stats * log)93773b01e57SJeff Chen nxpwifi_get_stats_info(struct nxpwifi_private *priv,
93873b01e57SJeff Chen 		       struct nxpwifi_ds_get_stats *log)
93973b01e57SJeff Chen {
94073b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_802_11_GET_LOG,
94173b01e57SJeff Chen 				HOST_ACT_GEN_GET, 0, log, true);
94273b01e57SJeff Chen }
94373b01e57SJeff Chen 
94473b01e57SJeff Chen /* IOCTL request handler to read/write register */
nxpwifi_reg_mem_ioctl_reg_rw(struct nxpwifi_private * priv,struct nxpwifi_ds_reg_rw * reg_rw,u16 action)94573b01e57SJeff Chen static int nxpwifi_reg_mem_ioctl_reg_rw(struct nxpwifi_private *priv,
94673b01e57SJeff Chen 					struct nxpwifi_ds_reg_rw *reg_rw,
94773b01e57SJeff Chen 					u16 action)
94873b01e57SJeff Chen {
94973b01e57SJeff Chen 	u16 cmd_no;
95073b01e57SJeff Chen 
95173b01e57SJeff Chen 	switch (reg_rw->type) {
95273b01e57SJeff Chen 	case NXPWIFI_REG_MAC:
95373b01e57SJeff Chen 		cmd_no = HOST_CMD_MAC_REG_ACCESS;
95473b01e57SJeff Chen 		break;
95573b01e57SJeff Chen 	case NXPWIFI_REG_BBP:
95673b01e57SJeff Chen 		cmd_no = HOST_CMD_BBP_REG_ACCESS;
95773b01e57SJeff Chen 		break;
95873b01e57SJeff Chen 	case NXPWIFI_REG_RF:
95973b01e57SJeff Chen 		cmd_no = HOST_CMD_RF_REG_ACCESS;
96073b01e57SJeff Chen 		break;
96173b01e57SJeff Chen 	case NXPWIFI_REG_PMIC:
96273b01e57SJeff Chen 		cmd_no = HOST_CMD_PMIC_REG_ACCESS;
96373b01e57SJeff Chen 		break;
96473b01e57SJeff Chen 	case NXPWIFI_REG_CAU:
96573b01e57SJeff Chen 		cmd_no = HOST_CMD_CAU_REG_ACCESS;
96673b01e57SJeff Chen 		break;
96773b01e57SJeff Chen 	default:
96873b01e57SJeff Chen 		return -EINVAL;
96973b01e57SJeff Chen 	}
97073b01e57SJeff Chen 
97173b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
97273b01e57SJeff Chen }
97373b01e57SJeff Chen 
97473b01e57SJeff Chen /* Sends IOCTL request to write to a register */
97573b01e57SJeff Chen int
nxpwifi_reg_write(struct nxpwifi_private * priv,u32 reg_type,u32 reg_offset,u32 reg_value)97673b01e57SJeff Chen nxpwifi_reg_write(struct nxpwifi_private *priv, u32 reg_type,
97773b01e57SJeff Chen 		  u32 reg_offset, u32 reg_value)
97873b01e57SJeff Chen {
97973b01e57SJeff Chen 	struct nxpwifi_ds_reg_rw reg_rw;
98073b01e57SJeff Chen 
98173b01e57SJeff Chen 	reg_rw.type = reg_type;
98273b01e57SJeff Chen 	reg_rw.offset = reg_offset;
98373b01e57SJeff Chen 	reg_rw.value = reg_value;
98473b01e57SJeff Chen 
98573b01e57SJeff Chen 	return nxpwifi_reg_mem_ioctl_reg_rw(priv, &reg_rw, HOST_ACT_GEN_SET);
98673b01e57SJeff Chen }
98773b01e57SJeff Chen 
98873b01e57SJeff Chen /* Sends IOCTL request to read from a register */
98973b01e57SJeff Chen int
nxpwifi_reg_read(struct nxpwifi_private * priv,u32 reg_type,u32 reg_offset,u32 * value)99073b01e57SJeff Chen nxpwifi_reg_read(struct nxpwifi_private *priv, u32 reg_type,
99173b01e57SJeff Chen 		 u32 reg_offset, u32 *value)
99273b01e57SJeff Chen {
99373b01e57SJeff Chen 	int ret;
99473b01e57SJeff Chen 	struct nxpwifi_ds_reg_rw reg_rw;
99573b01e57SJeff Chen 
99673b01e57SJeff Chen 	reg_rw.type = reg_type;
99773b01e57SJeff Chen 	reg_rw.offset = reg_offset;
99873b01e57SJeff Chen 	ret = nxpwifi_reg_mem_ioctl_reg_rw(priv, &reg_rw, HOST_ACT_GEN_GET);
99973b01e57SJeff Chen 
100073b01e57SJeff Chen 	if (!ret)
100173b01e57SJeff Chen 		*value = reg_rw.value;
100273b01e57SJeff Chen 
100373b01e57SJeff Chen 	return ret;
100473b01e57SJeff Chen }
100573b01e57SJeff Chen 
100673b01e57SJeff Chen /* Sends IOCTL request to read from EEPROM */
100773b01e57SJeff Chen int
nxpwifi_eeprom_read(struct nxpwifi_private * priv,u16 offset,u16 bytes,u8 * value)100873b01e57SJeff Chen nxpwifi_eeprom_read(struct nxpwifi_private *priv, u16 offset, u16 bytes,
100973b01e57SJeff Chen 		    u8 *value)
101073b01e57SJeff Chen {
101173b01e57SJeff Chen 	int ret;
101273b01e57SJeff Chen 	struct nxpwifi_ds_read_eeprom rd_eeprom;
101373b01e57SJeff Chen 
101473b01e57SJeff Chen 	rd_eeprom.offset =  offset;
101573b01e57SJeff Chen 	rd_eeprom.byte_count = bytes;
101673b01e57SJeff Chen 
101773b01e57SJeff Chen 	/* Send request to firmware */
101873b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_EEPROM_ACCESS,
101973b01e57SJeff Chen 			       HOST_ACT_GEN_GET, 0, &rd_eeprom, true);
102073b01e57SJeff Chen 
102173b01e57SJeff Chen 	if (!ret)
102273b01e57SJeff Chen 		memcpy(value, rd_eeprom.value,
102373b01e57SJeff Chen 		       min((u16)MAX_EEPROM_DATA, rd_eeprom.byte_count));
102473b01e57SJeff Chen 	return ret;
102573b01e57SJeff Chen }
102673b01e57SJeff Chen 
102773b01e57SJeff Chen /* Set generic IE(s); handle WPA/WPS specially */
102873b01e57SJeff Chen static int
nxpwifi_set_gen_ie_helper(struct nxpwifi_private * priv,u8 * ie_data_ptr,u16 ie_len)102973b01e57SJeff Chen nxpwifi_set_gen_ie_helper(struct nxpwifi_private *priv, u8 *ie_data_ptr,
103073b01e57SJeff Chen 			  u16 ie_len)
103173b01e57SJeff Chen {
103273b01e57SJeff Chen 	struct ieee80211_vendor_ie *pvendor_ie;
103373b01e57SJeff Chen 	static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
103473b01e57SJeff Chen 	static const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
103573b01e57SJeff Chen 	u16 unparsed_len = ie_len, cur_ie_len;
103673b01e57SJeff Chen 
103773b01e57SJeff Chen 	/* If the passed length is zero, reset the buffer */
103873b01e57SJeff Chen 	if (!ie_len) {
103973b01e57SJeff Chen 		priv->gen_ie_buf_len = 0;
104073b01e57SJeff Chen 		priv->wps.session_enable = false;
104173b01e57SJeff Chen 		return 0;
104273b01e57SJeff Chen 	} else if (!ie_data_ptr ||
104373b01e57SJeff Chen 		   ie_len <= sizeof(struct element)) {
104473b01e57SJeff Chen 		return -EINVAL;
104573b01e57SJeff Chen 	}
104673b01e57SJeff Chen 	pvendor_ie = (struct ieee80211_vendor_ie *)ie_data_ptr;
104773b01e57SJeff Chen 
104873b01e57SJeff Chen 	while (pvendor_ie) {
104973b01e57SJeff Chen 		cur_ie_len = pvendor_ie->len + sizeof(struct element);
105073b01e57SJeff Chen 
105173b01e57SJeff Chen 		if (pvendor_ie->element_id == WLAN_EID_RSN) {
105273b01e57SJeff Chen 			/* element is a WPA/WPA2 element so call set_wpa function */
105373b01e57SJeff Chen 			nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len);
105473b01e57SJeff Chen 			priv->wps.session_enable = false;
105573b01e57SJeff Chen 			goto next_ie;
105673b01e57SJeff Chen 		}
105773b01e57SJeff Chen 
105873b01e57SJeff Chen 		if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
105973b01e57SJeff Chen 			/* Test to see if it is a WPA element, if not, then it is a gen element */
106073b01e57SJeff Chen 			if (!memcmp(&pvendor_ie->oui, wpa_oui,
106173b01e57SJeff Chen 				    sizeof(wpa_oui))) {
106273b01e57SJeff Chen 				/* element is a WPA/WPA2 element so call set_wpa function */
106373b01e57SJeff Chen 				nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie,
106473b01e57SJeff Chen 						   cur_ie_len);
106573b01e57SJeff Chen 				priv->wps.session_enable = false;
106673b01e57SJeff Chen 				goto next_ie;
106773b01e57SJeff Chen 			}
106873b01e57SJeff Chen 
106973b01e57SJeff Chen 			if (!memcmp(&pvendor_ie->oui, wps_oui,
107073b01e57SJeff Chen 				    sizeof(wps_oui))) {
107173b01e57SJeff Chen 				/*
107273b01e57SJeff Chen 				 * Test to see if it is a WPS element, if so, enable wps session
107373b01e57SJeff Chen 				 * flag
107473b01e57SJeff Chen 				 */
107573b01e57SJeff Chen 				priv->wps.session_enable = true;
107673b01e57SJeff Chen 				nxpwifi_dbg(priv->adapter, MSG,
107773b01e57SJeff Chen 					    "WPS Session Enabled.\n");
107873b01e57SJeff Chen 				nxpwifi_set_wps_ie(priv, (u8 *)pvendor_ie,
107973b01e57SJeff Chen 						   cur_ie_len);
108073b01e57SJeff Chen 				goto next_ie;
108173b01e57SJeff Chen 			}
108273b01e57SJeff Chen 		}
108373b01e57SJeff Chen 
108473b01e57SJeff Chen 		/*
108573b01e57SJeff Chen 		 * Verify that the passed length is not larger than the available space
108673b01e57SJeff Chen 		 * remaining in the buffer
108773b01e57SJeff Chen 		 */
108873b01e57SJeff Chen 		if (cur_ie_len <
108973b01e57SJeff Chen 		    (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
109073b01e57SJeff Chen 			/* Append the passed data to the end of the genIeBuffer */
109173b01e57SJeff Chen 			memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len,
109273b01e57SJeff Chen 			       (u8 *)pvendor_ie, cur_ie_len);
109373b01e57SJeff Chen 			/* Increment the stored buffer length by the size passed */
109473b01e57SJeff Chen 			priv->gen_ie_buf_len += cur_ie_len;
109573b01e57SJeff Chen 		}
109673b01e57SJeff Chen 
109773b01e57SJeff Chen next_ie:
109873b01e57SJeff Chen 		unparsed_len -= cur_ie_len;
109973b01e57SJeff Chen 
110073b01e57SJeff Chen 		if (unparsed_len <= sizeof(struct element))
110173b01e57SJeff Chen 			pvendor_ie = NULL;
110273b01e57SJeff Chen 		else
110373b01e57SJeff Chen 			pvendor_ie = (struct ieee80211_vendor_ie *)
110473b01e57SJeff Chen 				(((u8 *)pvendor_ie) + cur_ie_len);
110573b01e57SJeff Chen 	}
110673b01e57SJeff Chen 
110773b01e57SJeff Chen 	return 0;
110873b01e57SJeff Chen }
110973b01e57SJeff Chen 
111073b01e57SJeff Chen /* IOCTL request handler to set/get generic element */
nxpwifi_misc_ioctl_gen_ie(struct nxpwifi_private * priv,struct nxpwifi_ds_misc_gen_ie * gen_ie,u16 action)111173b01e57SJeff Chen static int nxpwifi_misc_ioctl_gen_ie(struct nxpwifi_private *priv,
111273b01e57SJeff Chen 				     struct nxpwifi_ds_misc_gen_ie *gen_ie,
111373b01e57SJeff Chen 				     u16 action)
111473b01e57SJeff Chen {
111573b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
111673b01e57SJeff Chen 
111773b01e57SJeff Chen 	switch (gen_ie->type) {
111873b01e57SJeff Chen 	case NXPWIFI_IE_TYPE_GEN_IE:
111973b01e57SJeff Chen 		if (action == HOST_ACT_GEN_GET) {
112073b01e57SJeff Chen 			gen_ie->len = priv->wpa_ie_len;
112173b01e57SJeff Chen 			memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
112273b01e57SJeff Chen 		} else {
112373b01e57SJeff Chen 			nxpwifi_set_gen_ie_helper(priv, gen_ie->ie_data,
112473b01e57SJeff Chen 						  (u16)gen_ie->len);
112573b01e57SJeff Chen 		}
112673b01e57SJeff Chen 		break;
112773b01e57SJeff Chen 	case NXPWIFI_IE_TYPE_ARP_FILTER:
112873b01e57SJeff Chen 		memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
112973b01e57SJeff Chen 		if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
113073b01e57SJeff Chen 			adapter->arp_filter_size = 0;
113173b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
113273b01e57SJeff Chen 				    "invalid ARP filter size\n");
113373b01e57SJeff Chen 			return -EINVAL;
113473b01e57SJeff Chen 		}
113573b01e57SJeff Chen 		memcpy(adapter->arp_filter, gen_ie->ie_data, gen_ie->len);
113673b01e57SJeff Chen 		adapter->arp_filter_size = gen_ie->len;
113773b01e57SJeff Chen 		break;
113873b01e57SJeff Chen 	default:
113973b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR, "invalid element type\n");
114073b01e57SJeff Chen 		return -EINVAL;
114173b01e57SJeff Chen 	}
114273b01e57SJeff Chen 	return 0;
114373b01e57SJeff Chen }
114473b01e57SJeff Chen 
114573b01e57SJeff Chen /* Sends IOCTL request to set a generic element */
114673b01e57SJeff Chen int
nxpwifi_set_gen_ie(struct nxpwifi_private * priv,const u8 * ie,int ie_len)114773b01e57SJeff Chen nxpwifi_set_gen_ie(struct nxpwifi_private *priv, const u8 *ie, int ie_len)
114873b01e57SJeff Chen {
114973b01e57SJeff Chen 	struct nxpwifi_ds_misc_gen_ie gen_ie;
115073b01e57SJeff Chen 
115173b01e57SJeff Chen 	if (ie_len > IEEE_MAX_IE_SIZE)
115273b01e57SJeff Chen 		return -EFAULT;
115373b01e57SJeff Chen 
115473b01e57SJeff Chen 	gen_ie.type = NXPWIFI_IE_TYPE_GEN_IE;
115573b01e57SJeff Chen 	gen_ie.len = ie_len;
115673b01e57SJeff Chen 	memcpy(gen_ie.ie_data, ie, ie_len);
115773b01e57SJeff Chen 
115873b01e57SJeff Chen 	return nxpwifi_misc_ioctl_gen_ie(priv, &gen_ie, HOST_ACT_GEN_SET);
115973b01e57SJeff Chen }
116073b01e57SJeff Chen 
116173b01e57SJeff Chen /* Get Host Sleep wakeup reason */
nxpwifi_get_wakeup_reason(struct nxpwifi_private * priv,u16 action,int cmd_type,struct nxpwifi_ds_wakeup_reason * wakeup_reason)116273b01e57SJeff Chen int nxpwifi_get_wakeup_reason(struct nxpwifi_private *priv, u16 action,
116373b01e57SJeff Chen 			      int cmd_type,
116473b01e57SJeff Chen 			      struct nxpwifi_ds_wakeup_reason *wakeup_reason)
116573b01e57SJeff Chen {
116673b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_HS_WAKEUP_REASON,
116773b01e57SJeff Chen 				HOST_ACT_GEN_GET, 0, wakeup_reason,
116873b01e57SJeff Chen 				cmd_type == NXPWIFI_SYNC_CMD);
116973b01e57SJeff Chen }
117073b01e57SJeff Chen 
nxpwifi_get_chan_info(struct nxpwifi_private * priv,struct nxpwifi_channel_band * channel_band)117173b01e57SJeff Chen int nxpwifi_get_chan_info(struct nxpwifi_private *priv,
117273b01e57SJeff Chen 			  struct nxpwifi_channel_band *channel_band)
117373b01e57SJeff Chen {
117473b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_STA_CONFIGURE,
117573b01e57SJeff Chen 				HOST_ACT_GEN_GET, 0, channel_band,
117673b01e57SJeff Chen 				NXPWIFI_SYNC_CMD);
117773b01e57SJeff Chen }
1178