xref: /openbmc/linux/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1*828c91f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2277b024eSKalle Valo /*
3932183aaSGanapathi Bhat  * NXP Wireless LAN device driver: functions for station ioctl
4277b024eSKalle Valo  *
5932183aaSGanapathi Bhat  * Copyright 2011-2020 NXP
6277b024eSKalle Valo  */
7277b024eSKalle Valo 
8277b024eSKalle Valo #include "decl.h"
9277b024eSKalle Valo #include "ioctl.h"
10277b024eSKalle Valo #include "util.h"
11277b024eSKalle Valo #include "fw.h"
12277b024eSKalle Valo #include "main.h"
13277b024eSKalle Valo #include "wmm.h"
14277b024eSKalle Valo #include "11n.h"
15277b024eSKalle Valo #include "cfg80211.h"
16277b024eSKalle Valo 
17277b024eSKalle Valo static int disconnect_on_suspend;
18277b024eSKalle Valo module_param(disconnect_on_suspend, int, 0644);
19277b024eSKalle Valo 
20277b024eSKalle Valo /*
21277b024eSKalle Valo  * Copies the multicast address list from device to driver.
22277b024eSKalle Valo  *
23277b024eSKalle Valo  * This function does not validate the destination memory for
24277b024eSKalle Valo  * size, and the calling function must ensure enough memory is
25277b024eSKalle Valo  * available.
26277b024eSKalle Valo  */
mwifiex_copy_mcast_addr(struct mwifiex_multicast_list * mlist,struct net_device * dev)27277b024eSKalle Valo int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
28277b024eSKalle Valo 			    struct net_device *dev)
29277b024eSKalle Valo {
30277b024eSKalle Valo 	int i = 0;
31277b024eSKalle Valo 	struct netdev_hw_addr *ha;
32277b024eSKalle Valo 
33277b024eSKalle Valo 	netdev_for_each_mc_addr(ha, dev)
34277b024eSKalle Valo 		memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
35277b024eSKalle Valo 
36277b024eSKalle Valo 	return i;
37277b024eSKalle Valo }
38277b024eSKalle Valo 
39277b024eSKalle Valo /*
40277b024eSKalle Valo  * Wait queue completion handler.
41277b024eSKalle Valo  *
42277b024eSKalle Valo  * This function waits on a cmd wait queue. It also cancels the pending
43277b024eSKalle Valo  * request after waking up, in case of errors.
44277b024eSKalle Valo  */
mwifiex_wait_queue_complete(struct mwifiex_adapter * adapter,struct cmd_ctrl_node * cmd_queued)45277b024eSKalle Valo int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
46277b024eSKalle Valo 				struct cmd_ctrl_node *cmd_queued)
47277b024eSKalle Valo {
48277b024eSKalle Valo 	int status;
49277b024eSKalle Valo 
50277b024eSKalle Valo 	/* Wait for completion */
51277b024eSKalle Valo 	status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
52277b024eSKalle Valo 						  *(cmd_queued->condition),
53277b024eSKalle Valo 						  (12 * HZ));
54277b024eSKalle Valo 	if (status <= 0) {
55277b024eSKalle Valo 		if (status == 0)
56277b024eSKalle Valo 			status = -ETIMEDOUT;
57277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
58277b024eSKalle Valo 			    status);
59277b024eSKalle Valo 		mwifiex_cancel_all_pending_cmd(adapter);
60277b024eSKalle Valo 		return status;
61277b024eSKalle Valo 	}
62277b024eSKalle Valo 
63277b024eSKalle Valo 	status = adapter->cmd_wait_q.status;
64277b024eSKalle Valo 	adapter->cmd_wait_q.status = 0;
65277b024eSKalle Valo 
66277b024eSKalle Valo 	return status;
67277b024eSKalle Valo }
68277b024eSKalle Valo 
69277b024eSKalle Valo /*
70277b024eSKalle Valo  * This function prepares the correct firmware command and
71277b024eSKalle Valo  * issues it to set the multicast list.
72277b024eSKalle Valo  *
73277b024eSKalle Valo  * This function can be used to enable promiscuous mode, or enable all
74277b024eSKalle Valo  * multicast packets, or to enable selective multicast.
75277b024eSKalle Valo  */
mwifiex_request_set_multicast_list(struct mwifiex_private * priv,struct mwifiex_multicast_list * mcast_list)76277b024eSKalle Valo int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
77277b024eSKalle Valo 				struct mwifiex_multicast_list *mcast_list)
78277b024eSKalle Valo {
79277b024eSKalle Valo 	int ret = 0;
80277b024eSKalle Valo 	u16 old_pkt_filter;
81277b024eSKalle Valo 
82277b024eSKalle Valo 	old_pkt_filter = priv->curr_pkt_filter;
83277b024eSKalle Valo 
84277b024eSKalle Valo 	if (mcast_list->mode == MWIFIEX_PROMISC_MODE) {
85277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
86277b024eSKalle Valo 			    "info: Enable Promiscuous mode\n");
87277b024eSKalle Valo 		priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
88277b024eSKalle Valo 		priv->curr_pkt_filter &=
89277b024eSKalle Valo 			~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
90277b024eSKalle Valo 	} else {
91277b024eSKalle Valo 		/* Multicast */
92277b024eSKalle Valo 		priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
93277b024eSKalle Valo 		if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) {
94277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, INFO,
95277b024eSKalle Valo 				    "info: Enabling All Multicast!\n");
96277b024eSKalle Valo 			priv->curr_pkt_filter |=
97277b024eSKalle Valo 				HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
98277b024eSKalle Valo 		} else {
99277b024eSKalle Valo 			priv->curr_pkt_filter &=
100277b024eSKalle Valo 				~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
101277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, INFO,
102277b024eSKalle Valo 				    "info: Set multicast list=%d\n",
103277b024eSKalle Valo 				    mcast_list->num_multicast_addr);
104277b024eSKalle Valo 			/* Send multicast addresses to firmware */
105277b024eSKalle Valo 			ret = mwifiex_send_cmd(priv,
106277b024eSKalle Valo 					       HostCmd_CMD_MAC_MULTICAST_ADR,
107277b024eSKalle Valo 					       HostCmd_ACT_GEN_SET, 0,
108277b024eSKalle Valo 					       mcast_list, false);
109277b024eSKalle Valo 		}
110277b024eSKalle Valo 	}
111277b024eSKalle Valo 	mwifiex_dbg(priv->adapter, INFO,
112277b024eSKalle Valo 		    "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
113277b024eSKalle Valo 		    old_pkt_filter, priv->curr_pkt_filter);
114277b024eSKalle Valo 	if (old_pkt_filter != priv->curr_pkt_filter) {
115277b024eSKalle Valo 		ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
116277b024eSKalle Valo 				       HostCmd_ACT_GEN_SET,
117277b024eSKalle Valo 				       0, &priv->curr_pkt_filter, false);
118277b024eSKalle Valo 	}
119277b024eSKalle Valo 
120277b024eSKalle Valo 	return ret;
121277b024eSKalle Valo }
122277b024eSKalle Valo 
123277b024eSKalle Valo /*
124277b024eSKalle Valo  * This function fills bss descriptor structure using provided
125277b024eSKalle Valo  * information.
126277b024eSKalle Valo  * beacon_ie buffer is allocated in this function. It is caller's
127277b024eSKalle Valo  * responsibility to free the memory.
128277b024eSKalle Valo  */
mwifiex_fill_new_bss_desc(struct mwifiex_private * priv,struct cfg80211_bss * bss,struct mwifiex_bssdescriptor * bss_desc)129277b024eSKalle Valo int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
130277b024eSKalle Valo 			      struct cfg80211_bss *bss,
131277b024eSKalle Valo 			      struct mwifiex_bssdescriptor *bss_desc)
132277b024eSKalle Valo {
133277b024eSKalle Valo 	u8 *beacon_ie;
134277b024eSKalle Valo 	size_t beacon_ie_len;
135277b024eSKalle Valo 	struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
136277b024eSKalle Valo 	const struct cfg80211_bss_ies *ies;
137277b024eSKalle Valo 
138277b024eSKalle Valo 	rcu_read_lock();
139277b024eSKalle Valo 	ies = rcu_dereference(bss->ies);
140277b024eSKalle Valo 	beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
141277b024eSKalle Valo 	beacon_ie_len = ies->len;
142277b024eSKalle Valo 	bss_desc->timestamp = ies->tsf;
143277b024eSKalle Valo 	rcu_read_unlock();
144277b024eSKalle Valo 
145277b024eSKalle Valo 	if (!beacon_ie) {
146277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
147277b024eSKalle Valo 			    " failed to alloc beacon_ie\n");
148277b024eSKalle Valo 		return -ENOMEM;
149277b024eSKalle Valo 	}
150277b024eSKalle Valo 
151277b024eSKalle Valo 	memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
152277b024eSKalle Valo 	bss_desc->rssi = bss->signal;
153277b024eSKalle Valo 	/* The caller of this function will free beacon_ie */
154277b024eSKalle Valo 	bss_desc->beacon_buf = beacon_ie;
155277b024eSKalle Valo 	bss_desc->beacon_buf_size = beacon_ie_len;
156277b024eSKalle Valo 	bss_desc->beacon_period = bss->beacon_interval;
157277b024eSKalle Valo 	bss_desc->cap_info_bitmap = bss->capability;
158277b024eSKalle Valo 	bss_desc->bss_band = bss_priv->band;
159277b024eSKalle Valo 	bss_desc->fw_tsf = bss_priv->fw_tsf;
160277b024eSKalle Valo 	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
161277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
162277b024eSKalle Valo 			    "info: InterpretIE: AP WEP enabled\n");
163277b024eSKalle Valo 		bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
164277b024eSKalle Valo 	} else {
165277b024eSKalle Valo 		bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
166277b024eSKalle Valo 	}
167277b024eSKalle Valo 	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS)
168277b024eSKalle Valo 		bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
169277b024eSKalle Valo 	else
170277b024eSKalle Valo 		bss_desc->bss_mode = NL80211_IFTYPE_STATION;
171277b024eSKalle Valo 
172277b024eSKalle Valo 	/* Disable 11ac by default. Enable it only where there
173277b024eSKalle Valo 	 * exist VHT_CAP IE in AP beacon
174277b024eSKalle Valo 	 */
175277b024eSKalle Valo 	bss_desc->disable_11ac = true;
176277b024eSKalle Valo 
177277b024eSKalle Valo 	if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
178277b024eSKalle Valo 		bss_desc->sensed_11h = true;
179277b024eSKalle Valo 
18053a70942SGanapathi Bhat 	return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
181277b024eSKalle Valo }
182277b024eSKalle Valo 
mwifiex_dnld_txpwr_table(struct mwifiex_private * priv)183277b024eSKalle Valo void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
184277b024eSKalle Valo {
185277b024eSKalle Valo 	if (priv->adapter->dt_node) {
186277b024eSKalle Valo 		char txpwr[] = {"marvell,00_txpwrlimit"};
187277b024eSKalle Valo 
188277b024eSKalle Valo 		memcpy(&txpwr[8], priv->adapter->country_code, 2);
189277b024eSKalle Valo 		mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr);
190277b024eSKalle Valo 	}
191277b024eSKalle Valo }
192277b024eSKalle Valo 
mwifiex_process_country_ie(struct mwifiex_private * priv,struct cfg80211_bss * bss)193277b024eSKalle Valo static int mwifiex_process_country_ie(struct mwifiex_private *priv,
194277b024eSKalle Valo 				      struct cfg80211_bss *bss)
195277b024eSKalle Valo {
196277b024eSKalle Valo 	const u8 *country_ie;
197277b024eSKalle Valo 	u8 country_ie_len;
198277b024eSKalle Valo 	struct mwifiex_802_11d_domain_reg *domain_info =
199277b024eSKalle Valo 					&priv->adapter->domain_reg;
200277b024eSKalle Valo 
201277b024eSKalle Valo 	rcu_read_lock();
202277b024eSKalle Valo 	country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
203277b024eSKalle Valo 	if (!country_ie) {
204277b024eSKalle Valo 		rcu_read_unlock();
205277b024eSKalle Valo 		return 0;
206277b024eSKalle Valo 	}
207277b024eSKalle Valo 
208277b024eSKalle Valo 	country_ie_len = country_ie[1];
209277b024eSKalle Valo 	if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
210277b024eSKalle Valo 		rcu_read_unlock();
211277b024eSKalle Valo 		return 0;
212277b024eSKalle Valo 	}
213277b024eSKalle Valo 
214277b024eSKalle Valo 	if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
215277b024eSKalle Valo 		rcu_read_unlock();
216277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
217277b024eSKalle Valo 			    "11D: skip setting domain info in FW\n");
218277b024eSKalle Valo 		return 0;
219277b024eSKalle Valo 	}
2203d94a4a8SGanapathi Bhat 
2213d94a4a8SGanapathi Bhat 	if (country_ie_len >
2223d94a4a8SGanapathi Bhat 	    (IEEE80211_COUNTRY_STRING_LEN + MWIFIEX_MAX_TRIPLET_802_11D)) {
22365b1aae0SBrian Norris 		rcu_read_unlock();
2243d94a4a8SGanapathi Bhat 		mwifiex_dbg(priv->adapter, ERROR,
2253d94a4a8SGanapathi Bhat 			    "11D: country_ie_len overflow!, deauth AP\n");
2263d94a4a8SGanapathi Bhat 		return -EINVAL;
2273d94a4a8SGanapathi Bhat 	}
2283d94a4a8SGanapathi Bhat 
229277b024eSKalle Valo 	memcpy(priv->adapter->country_code, &country_ie[2], 2);
230277b024eSKalle Valo 
231277b024eSKalle Valo 	domain_info->country_code[0] = country_ie[2];
232277b024eSKalle Valo 	domain_info->country_code[1] = country_ie[3];
233277b024eSKalle Valo 	domain_info->country_code[2] = ' ';
234277b024eSKalle Valo 
235277b024eSKalle Valo 	country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
236277b024eSKalle Valo 
237277b024eSKalle Valo 	domain_info->no_of_triplet =
238277b024eSKalle Valo 		country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
239277b024eSKalle Valo 
240277b024eSKalle Valo 	memcpy((u8 *)domain_info->triplet,
241277b024eSKalle Valo 	       &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
242277b024eSKalle Valo 
243277b024eSKalle Valo 	rcu_read_unlock();
244277b024eSKalle Valo 
245277b024eSKalle Valo 	if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
246277b024eSKalle Valo 			     HostCmd_ACT_GEN_SET, 0, NULL, false)) {
247277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
248277b024eSKalle Valo 			    "11D: setting domain info in FW fail\n");
249277b024eSKalle Valo 		return -1;
250277b024eSKalle Valo 	}
251277b024eSKalle Valo 
252277b024eSKalle Valo 	mwifiex_dnld_txpwr_table(priv);
253277b024eSKalle Valo 
254277b024eSKalle Valo 	return 0;
255277b024eSKalle Valo }
256277b024eSKalle Valo 
257277b024eSKalle Valo /*
258277b024eSKalle Valo  * In Ad-Hoc mode, the IBSS is created if not found in scan list.
259277b024eSKalle Valo  * In both Ad-Hoc and infra mode, an deauthentication is performed
260277b024eSKalle Valo  * first.
261277b024eSKalle Valo  */
mwifiex_bss_start(struct mwifiex_private * priv,struct cfg80211_bss * bss,struct cfg80211_ssid * req_ssid)262277b024eSKalle Valo int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
263277b024eSKalle Valo 		      struct cfg80211_ssid *req_ssid)
264277b024eSKalle Valo {
265277b024eSKalle Valo 	int ret;
266277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
267277b024eSKalle Valo 	struct mwifiex_bssdescriptor *bss_desc = NULL;
268277b024eSKalle Valo 
269277b024eSKalle Valo 	priv->scan_block = false;
270277b024eSKalle Valo 
271277b024eSKalle Valo 	if (bss) {
2723d94a4a8SGanapathi Bhat 		if (adapter->region_code == 0x00 &&
2733d94a4a8SGanapathi Bhat 		    mwifiex_process_country_ie(priv, bss))
2743d94a4a8SGanapathi Bhat 			return -EINVAL;
275277b024eSKalle Valo 
276277b024eSKalle Valo 		/* Allocate and fill new bss descriptor */
277277b024eSKalle Valo 		bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
278277b024eSKalle Valo 				   GFP_KERNEL);
279277b024eSKalle Valo 		if (!bss_desc)
280277b024eSKalle Valo 			return -ENOMEM;
281277b024eSKalle Valo 
282277b024eSKalle Valo 		ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
283277b024eSKalle Valo 		if (ret)
284277b024eSKalle Valo 			goto done;
285277b024eSKalle Valo 	}
286277b024eSKalle Valo 
287277b024eSKalle Valo 	if (priv->bss_mode == NL80211_IFTYPE_STATION ||
288277b024eSKalle Valo 	    priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
289277b024eSKalle Valo 		u8 config_bands;
290277b024eSKalle Valo 
291277b024eSKalle Valo 		if (!bss_desc)
292277b024eSKalle Valo 			return -1;
293277b024eSKalle Valo 
294277b024eSKalle Valo 		if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
295277b024eSKalle Valo 						HostCmd_SCAN_RADIO_TYPE_BG) {
296277b024eSKalle Valo 			config_bands = BAND_B | BAND_G | BAND_GN;
297277b024eSKalle Valo 		} else {
298277b024eSKalle Valo 			config_bands = BAND_A | BAND_AN;
299277b024eSKalle Valo 			if (adapter->fw_bands & BAND_AAC)
300277b024eSKalle Valo 				config_bands |= BAND_AAC;
301277b024eSKalle Valo 		}
302277b024eSKalle Valo 
303277b024eSKalle Valo 		if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
304277b024eSKalle Valo 			adapter->config_bands = config_bands;
305277b024eSKalle Valo 
306277b024eSKalle Valo 		ret = mwifiex_check_network_compatibility(priv, bss_desc);
307277b024eSKalle Valo 		if (ret)
308277b024eSKalle Valo 			goto done;
309277b024eSKalle Valo 
310277b024eSKalle Valo 		if (mwifiex_11h_get_csa_closed_channel(priv) ==
311277b024eSKalle Valo 							(u8)bss_desc->channel) {
312277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
313277b024eSKalle Valo 				    "Attempt to reconnect on csa closed chan(%d)\n",
314277b024eSKalle Valo 				    bss_desc->channel);
315a6139b62SAmitkumar Karwar 			ret = -1;
316277b024eSKalle Valo 			goto done;
317277b024eSKalle Valo 		}
318277b024eSKalle Valo 
319277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
320277b024eSKalle Valo 			    "info: SSID found in scan list ...\t"
321277b024eSKalle Valo 			    "associating...\n");
322277b024eSKalle Valo 
323277b024eSKalle Valo 		mwifiex_stop_net_dev_queue(priv->netdev, adapter);
324277b024eSKalle Valo 		if (netif_carrier_ok(priv->netdev))
325277b024eSKalle Valo 			netif_carrier_off(priv->netdev);
326277b024eSKalle Valo 
327277b024eSKalle Valo 		/* Clear any past association response stored for
328277b024eSKalle Valo 		 * application retrieval */
329277b024eSKalle Valo 		priv->assoc_rsp_size = 0;
330277b024eSKalle Valo 		ret = mwifiex_associate(priv, bss_desc);
331277b024eSKalle Valo 
332277b024eSKalle Valo 		/* If auth type is auto and association fails using open mode,
333277b024eSKalle Valo 		 * try to connect using shared mode */
334277b024eSKalle Valo 		if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
335277b024eSKalle Valo 		    priv->sec_info.is_authtype_auto &&
336277b024eSKalle Valo 		    priv->sec_info.wep_enabled) {
337277b024eSKalle Valo 			priv->sec_info.authentication_mode =
338277b024eSKalle Valo 						NL80211_AUTHTYPE_SHARED_KEY;
339277b024eSKalle Valo 			ret = mwifiex_associate(priv, bss_desc);
340277b024eSKalle Valo 		}
341277b024eSKalle Valo 
342277b024eSKalle Valo 		if (bss)
343277b024eSKalle Valo 			cfg80211_put_bss(priv->adapter->wiphy, bss);
344277b024eSKalle Valo 	} else {
345277b024eSKalle Valo 		/* Adhoc mode */
346277b024eSKalle Valo 		/* If the requested SSID matches current SSID, return */
347277b024eSKalle Valo 		if (bss_desc && bss_desc->ssid.ssid_len &&
348277b024eSKalle Valo 		    (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor.
349277b024eSKalle Valo 				       ssid, &bss_desc->ssid))) {
350277b024eSKalle Valo 			ret = 0;
351277b024eSKalle Valo 			goto done;
352277b024eSKalle Valo 		}
353277b024eSKalle Valo 
354277b024eSKalle Valo 		priv->adhoc_is_link_sensed = false;
355277b024eSKalle Valo 
356277b024eSKalle Valo 		ret = mwifiex_check_network_compatibility(priv, bss_desc);
357277b024eSKalle Valo 
358277b024eSKalle Valo 		mwifiex_stop_net_dev_queue(priv->netdev, adapter);
359277b024eSKalle Valo 		if (netif_carrier_ok(priv->netdev))
360277b024eSKalle Valo 			netif_carrier_off(priv->netdev);
361277b024eSKalle Valo 
362277b024eSKalle Valo 		if (!ret) {
363277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
364277b024eSKalle Valo 				    "info: network found in scan\t"
365277b024eSKalle Valo 				    " list. Joining...\n");
366277b024eSKalle Valo 			ret = mwifiex_adhoc_join(priv, bss_desc);
367277b024eSKalle Valo 			if (bss)
368277b024eSKalle Valo 				cfg80211_put_bss(priv->adapter->wiphy, bss);
369277b024eSKalle Valo 		} else {
370277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
371277b024eSKalle Valo 				    "info: Network not found in\t"
372277b024eSKalle Valo 				    "the list, creating adhoc with ssid = %s\n",
373277b024eSKalle Valo 				    req_ssid->ssid);
374277b024eSKalle Valo 			ret = mwifiex_adhoc_start(priv, req_ssid);
375277b024eSKalle Valo 		}
376277b024eSKalle Valo 	}
377277b024eSKalle Valo 
378277b024eSKalle Valo done:
379277b024eSKalle Valo 	/* beacon_ie buffer was allocated in function
380277b024eSKalle Valo 	 * mwifiex_fill_new_bss_desc(). Free it now.
381277b024eSKalle Valo 	 */
382277b024eSKalle Valo 	if (bss_desc)
383277b024eSKalle Valo 		kfree(bss_desc->beacon_buf);
384277b024eSKalle Valo 	kfree(bss_desc);
3854699fc3fSGanapathi Bhat 
3864699fc3fSGanapathi Bhat 	if (ret < 0)
3874699fc3fSGanapathi Bhat 		priv->attempted_bss_desc = NULL;
3884699fc3fSGanapathi Bhat 
389277b024eSKalle Valo 	return ret;
390277b024eSKalle Valo }
391277b024eSKalle Valo 
392277b024eSKalle Valo /*
393277b024eSKalle Valo  * IOCTL request handler to set host sleep configuration.
394277b024eSKalle Valo  *
395277b024eSKalle Valo  * This function prepares the correct firmware command and
396277b024eSKalle Valo  * issues it.
397277b024eSKalle Valo  */
mwifiex_set_hs_params(struct mwifiex_private * priv,u16 action,int cmd_type,struct mwifiex_ds_hs_cfg * hs_cfg)398277b024eSKalle Valo int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
399277b024eSKalle Valo 			  int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
400277b024eSKalle Valo 
401277b024eSKalle Valo {
402277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
403277b024eSKalle Valo 	int status = 0;
404277b024eSKalle Valo 	u32 prev_cond = 0;
405277b024eSKalle Valo 
406277b024eSKalle Valo 	if (!hs_cfg)
407277b024eSKalle Valo 		return -ENOMEM;
408277b024eSKalle Valo 
409277b024eSKalle Valo 	switch (action) {
410277b024eSKalle Valo 	case HostCmd_ACT_GEN_SET:
411277b024eSKalle Valo 		if (adapter->pps_uapsd_mode) {
412277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
413277b024eSKalle Valo 				    "info: Host Sleep IOCTL\t"
414277b024eSKalle Valo 				    "is blocked in UAPSD/PPS mode\n");
415277b024eSKalle Valo 			status = -1;
416277b024eSKalle Valo 			break;
417277b024eSKalle Valo 		}
418277b024eSKalle Valo 		if (hs_cfg->is_invoke_hostcmd) {
419277b024eSKalle Valo 			if (hs_cfg->conditions == HS_CFG_CANCEL) {
420fc3a2fcaSGanapathi Bhat 				if (!test_bit(MWIFIEX_IS_HS_CONFIGURED,
421fc3a2fcaSGanapathi Bhat 					      &adapter->work_flags))
422277b024eSKalle Valo 					/* Already cancelled */
423277b024eSKalle Valo 					break;
424277b024eSKalle Valo 				/* Save previous condition */
425277b024eSKalle Valo 				prev_cond = le32_to_cpu(adapter->hs_cfg
426277b024eSKalle Valo 							.conditions);
427277b024eSKalle Valo 				adapter->hs_cfg.conditions =
428277b024eSKalle Valo 						cpu_to_le32(hs_cfg->conditions);
429277b024eSKalle Valo 			} else if (hs_cfg->conditions) {
430277b024eSKalle Valo 				adapter->hs_cfg.conditions =
431277b024eSKalle Valo 						cpu_to_le32(hs_cfg->conditions);
432277b024eSKalle Valo 				adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
433277b024eSKalle Valo 				if (hs_cfg->gap)
434277b024eSKalle Valo 					adapter->hs_cfg.gap = (u8)hs_cfg->gap;
435277b024eSKalle Valo 			} else if (adapter->hs_cfg.conditions ==
436277b024eSKalle Valo 				   cpu_to_le32(HS_CFG_CANCEL)) {
437277b024eSKalle Valo 				/* Return failure if no parameters for HS
438277b024eSKalle Valo 				   enable */
439277b024eSKalle Valo 				status = -1;
440277b024eSKalle Valo 				break;
441277b024eSKalle Valo 			}
442277b024eSKalle Valo 
443277b024eSKalle Valo 			status = mwifiex_send_cmd(priv,
444277b024eSKalle Valo 						  HostCmd_CMD_802_11_HS_CFG_ENH,
445277b024eSKalle Valo 						  HostCmd_ACT_GEN_SET, 0,
446277b024eSKalle Valo 						  &adapter->hs_cfg,
447277b024eSKalle Valo 						  cmd_type == MWIFIEX_SYNC_CMD);
448277b024eSKalle Valo 
449277b024eSKalle Valo 			if (hs_cfg->conditions == HS_CFG_CANCEL)
450277b024eSKalle Valo 				/* Restore previous condition */
451277b024eSKalle Valo 				adapter->hs_cfg.conditions =
452277b024eSKalle Valo 						cpu_to_le32(prev_cond);
453277b024eSKalle Valo 		} else {
454277b024eSKalle Valo 			adapter->hs_cfg.conditions =
455277b024eSKalle Valo 						cpu_to_le32(hs_cfg->conditions);
456277b024eSKalle Valo 			adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
457277b024eSKalle Valo 			adapter->hs_cfg.gap = (u8)hs_cfg->gap;
458277b024eSKalle Valo 		}
459277b024eSKalle Valo 		break;
460277b024eSKalle Valo 	case HostCmd_ACT_GEN_GET:
461277b024eSKalle Valo 		hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
462277b024eSKalle Valo 		hs_cfg->gpio = adapter->hs_cfg.gpio;
463277b024eSKalle Valo 		hs_cfg->gap = adapter->hs_cfg.gap;
464277b024eSKalle Valo 		break;
465277b024eSKalle Valo 	default:
466277b024eSKalle Valo 		status = -1;
467277b024eSKalle Valo 		break;
468277b024eSKalle Valo 	}
469277b024eSKalle Valo 
470277b024eSKalle Valo 	return status;
471277b024eSKalle Valo }
472277b024eSKalle Valo 
473277b024eSKalle Valo /*
474277b024eSKalle Valo  * Sends IOCTL request to cancel the existing Host Sleep configuration.
475277b024eSKalle Valo  *
476277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
477277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
478277b024eSKalle Valo  */
mwifiex_cancel_hs(struct mwifiex_private * priv,int cmd_type)479277b024eSKalle Valo int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type)
480277b024eSKalle Valo {
481277b024eSKalle Valo 	struct mwifiex_ds_hs_cfg hscfg;
482277b024eSKalle Valo 
483277b024eSKalle Valo 	hscfg.conditions = HS_CFG_CANCEL;
484277b024eSKalle Valo 	hscfg.is_invoke_hostcmd = true;
485277b024eSKalle Valo 
486277b024eSKalle Valo 	return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
487277b024eSKalle Valo 				    cmd_type, &hscfg);
488277b024eSKalle Valo }
489277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_cancel_hs);
490277b024eSKalle Valo 
491277b024eSKalle Valo /*
492277b024eSKalle Valo  * Sends IOCTL request to cancel the existing Host Sleep configuration.
493277b024eSKalle Valo  *
494277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
495277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
496277b024eSKalle Valo  */
mwifiex_enable_hs(struct mwifiex_adapter * adapter)497277b024eSKalle Valo int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
498277b024eSKalle Valo {
499277b024eSKalle Valo 	struct mwifiex_ds_hs_cfg hscfg;
500277b024eSKalle Valo 	struct mwifiex_private *priv;
501277b024eSKalle Valo 	int i;
502277b024eSKalle Valo 
503277b024eSKalle Valo 	if (disconnect_on_suspend) {
504277b024eSKalle Valo 		for (i = 0; i < adapter->priv_num; i++) {
505277b024eSKalle Valo 			priv = adapter->priv[i];
506277b024eSKalle Valo 			if (priv)
507277b024eSKalle Valo 				mwifiex_deauthenticate(priv, NULL);
508277b024eSKalle Valo 		}
509277b024eSKalle Valo 	}
510277b024eSKalle Valo 
5110c9b7f22SXinming Hu 	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
5127d7f07d8Schunfan chen 
5130c9b7f22SXinming Hu 	if (priv && priv->sched_scanning) {
5147d7f07d8Schunfan chen #ifdef CONFIG_PM
515a5c92f0bSWei-Ning Huang 		if (priv->wdev.wiphy->wowlan_config &&
516a5c92f0bSWei-Ning Huang 		    !priv->wdev.wiphy->wowlan_config->nd_config) {
5177d7f07d8Schunfan chen #endif
5187d7f07d8Schunfan chen 			mwifiex_dbg(adapter, CMD, "aborting bgscan!\n");
5190c9b7f22SXinming Hu 			mwifiex_stop_bg_scan(priv);
520b34939b9SArend Van Spriel 			cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
5217d7f07d8Schunfan chen #ifdef CONFIG_PM
5227d7f07d8Schunfan chen 		}
5237d7f07d8Schunfan chen #endif
5240c9b7f22SXinming Hu 	}
5250c9b7f22SXinming Hu 
526277b024eSKalle Valo 	if (adapter->hs_activated) {
527277b024eSKalle Valo 		mwifiex_dbg(adapter, CMD,
528277b024eSKalle Valo 			    "cmd: HS Already activated\n");
529277b024eSKalle Valo 		return true;
530277b024eSKalle Valo 	}
531277b024eSKalle Valo 
532277b024eSKalle Valo 	adapter->hs_activate_wait_q_woken = false;
533277b024eSKalle Valo 
5346a162200SChristophe Jaillet 	memset(&hscfg, 0, sizeof(hscfg));
535277b024eSKalle Valo 	hscfg.is_invoke_hostcmd = true;
536277b024eSKalle Valo 
537fc3a2fcaSGanapathi Bhat 	set_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags);
538277b024eSKalle Valo 	mwifiex_cancel_all_pending_cmd(adapter);
539277b024eSKalle Valo 
540277b024eSKalle Valo 	if (mwifiex_set_hs_params(mwifiex_get_priv(adapter,
541277b024eSKalle Valo 						   MWIFIEX_BSS_ROLE_STA),
542277b024eSKalle Valo 				  HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD,
543277b024eSKalle Valo 				  &hscfg)) {
544277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
545277b024eSKalle Valo 			    "IOCTL request HS enable failed\n");
546277b024eSKalle Valo 		return false;
547277b024eSKalle Valo 	}
548277b024eSKalle Valo 
549277b024eSKalle Valo 	if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
550277b024eSKalle Valo 					     adapter->hs_activate_wait_q_woken,
551277b024eSKalle Valo 					     (10 * HZ)) <= 0) {
552277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
553277b024eSKalle Valo 			    "hs_activate_wait_q terminated\n");
554277b024eSKalle Valo 		return false;
555277b024eSKalle Valo 	}
556277b024eSKalle Valo 
557277b024eSKalle Valo 	return true;
558277b024eSKalle Valo }
559277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_enable_hs);
560277b024eSKalle Valo 
561277b024eSKalle Valo /*
562277b024eSKalle Valo  * IOCTL request handler to get BSS information.
563277b024eSKalle Valo  *
564277b024eSKalle Valo  * This function collates the information from different driver structures
565277b024eSKalle Valo  * to send to the user.
566277b024eSKalle Valo  */
mwifiex_get_bss_info(struct mwifiex_private * priv,struct mwifiex_bss_info * info)567277b024eSKalle Valo int mwifiex_get_bss_info(struct mwifiex_private *priv,
568277b024eSKalle Valo 			 struct mwifiex_bss_info *info)
569277b024eSKalle Valo {
570277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
571277b024eSKalle Valo 	struct mwifiex_bssdescriptor *bss_desc;
572277b024eSKalle Valo 
573277b024eSKalle Valo 	if (!info)
574277b024eSKalle Valo 		return -1;
575277b024eSKalle Valo 
576277b024eSKalle Valo 	bss_desc = &priv->curr_bss_params.bss_descriptor;
577277b024eSKalle Valo 
578277b024eSKalle Valo 	info->bss_mode = priv->bss_mode;
579277b024eSKalle Valo 
580277b024eSKalle Valo 	memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
581277b024eSKalle Valo 
582277b024eSKalle Valo 	memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
583277b024eSKalle Valo 
584277b024eSKalle Valo 	info->bss_chan = bss_desc->channel;
585277b024eSKalle Valo 
586277b024eSKalle Valo 	memcpy(info->country_code, adapter->country_code,
587277b024eSKalle Valo 	       IEEE80211_COUNTRY_STRING_LEN);
588277b024eSKalle Valo 
589277b024eSKalle Valo 	info->media_connected = priv->media_connected;
590277b024eSKalle Valo 
591277b024eSKalle Valo 	info->max_power_level = priv->max_tx_power_level;
592277b024eSKalle Valo 	info->min_power_level = priv->min_tx_power_level;
593277b024eSKalle Valo 
594277b024eSKalle Valo 	info->adhoc_state = priv->adhoc_state;
595277b024eSKalle Valo 
596277b024eSKalle Valo 	info->bcn_nf_last = priv->bcn_nf_last;
597277b024eSKalle Valo 
598277b024eSKalle Valo 	if (priv->sec_info.wep_enabled)
599277b024eSKalle Valo 		info->wep_status = true;
600277b024eSKalle Valo 	else
601277b024eSKalle Valo 		info->wep_status = false;
602277b024eSKalle Valo 
603fc3a2fcaSGanapathi Bhat 	info->is_hs_configured = test_bit(MWIFIEX_IS_HS_CONFIGURED,
604fc3a2fcaSGanapathi Bhat 					  &adapter->work_flags);
605277b024eSKalle Valo 	info->is_deep_sleep = adapter->is_deep_sleep;
606277b024eSKalle Valo 
607277b024eSKalle Valo 	return 0;
608277b024eSKalle Valo }
609277b024eSKalle Valo 
610277b024eSKalle Valo /*
611277b024eSKalle Valo  * The function disables auto deep sleep mode.
612277b024eSKalle Valo  */
mwifiex_disable_auto_ds(struct mwifiex_private * priv)613277b024eSKalle Valo int mwifiex_disable_auto_ds(struct mwifiex_private *priv)
614277b024eSKalle Valo {
6159557d9f2SBrian Norris 	struct mwifiex_ds_auto_ds auto_ds = {
6169557d9f2SBrian Norris 		.auto_ds = DEEP_SLEEP_OFF,
6179557d9f2SBrian Norris 	};
618277b024eSKalle Valo 
619277b024eSKalle Valo 	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
620277b024eSKalle Valo 				DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
621277b024eSKalle Valo }
622277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds);
623277b024eSKalle Valo 
624277b024eSKalle Valo /*
625277b024eSKalle Valo  * Sends IOCTL request to get the data rate.
626277b024eSKalle Valo  *
627277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
628277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
629277b024eSKalle Valo  */
mwifiex_drv_get_data_rate(struct mwifiex_private * priv,u32 * rate)630277b024eSKalle Valo int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate)
631277b024eSKalle Valo {
632277b024eSKalle Valo 	int ret;
633277b024eSKalle Valo 
634277b024eSKalle Valo 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
635277b024eSKalle Valo 			       HostCmd_ACT_GEN_GET, 0, NULL, true);
636277b024eSKalle Valo 
637277b024eSKalle Valo 	if (!ret) {
638277b024eSKalle Valo 		if (priv->is_data_rate_auto)
639277b024eSKalle Valo 			*rate = mwifiex_index_to_data_rate(priv, priv->tx_rate,
640277b024eSKalle Valo 							   priv->tx_htinfo);
641277b024eSKalle Valo 		else
642277b024eSKalle Valo 			*rate = priv->data_rate;
643277b024eSKalle Valo 	}
644277b024eSKalle Valo 
645277b024eSKalle Valo 	return ret;
646277b024eSKalle Valo }
647277b024eSKalle Valo 
648277b024eSKalle Valo /*
649277b024eSKalle Valo  * IOCTL request handler to set tx power configuration.
650277b024eSKalle Valo  *
651277b024eSKalle Valo  * This function prepares the correct firmware command and
652277b024eSKalle Valo  * issues it.
653277b024eSKalle Valo  *
654277b024eSKalle Valo  * For non-auto power mode, all the following power groups are set -
655277b024eSKalle Valo  *      - Modulation class HR/DSSS
656277b024eSKalle Valo  *      - Modulation class OFDM
657277b024eSKalle Valo  *      - Modulation class HTBW20
658277b024eSKalle Valo  *      - Modulation class HTBW40
659277b024eSKalle Valo  */
mwifiex_set_tx_power(struct mwifiex_private * priv,struct mwifiex_power_cfg * power_cfg)660277b024eSKalle Valo int mwifiex_set_tx_power(struct mwifiex_private *priv,
661277b024eSKalle Valo 			 struct mwifiex_power_cfg *power_cfg)
662277b024eSKalle Valo {
663277b024eSKalle Valo 	int ret;
664277b024eSKalle Valo 	struct host_cmd_ds_txpwr_cfg *txp_cfg;
665277b024eSKalle Valo 	struct mwifiex_types_power_group *pg_tlv;
666277b024eSKalle Valo 	struct mwifiex_power_group *pg;
667277b024eSKalle Valo 	u8 *buf;
668277b024eSKalle Valo 	u16 dbm = 0;
669277b024eSKalle Valo 
670277b024eSKalle Valo 	if (!power_cfg->is_power_auto) {
671277b024eSKalle Valo 		dbm = (u16) power_cfg->power_level;
672277b024eSKalle Valo 		if ((dbm < priv->min_tx_power_level) ||
673277b024eSKalle Valo 		    (dbm > priv->max_tx_power_level)) {
674277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, ERROR,
675277b024eSKalle Valo 				    "txpower value %d dBm\t"
676277b024eSKalle Valo 				    "is out of range (%d dBm-%d dBm)\n",
677277b024eSKalle Valo 				    dbm, priv->min_tx_power_level,
678277b024eSKalle Valo 				    priv->max_tx_power_level);
679277b024eSKalle Valo 			return -1;
680277b024eSKalle Valo 		}
681277b024eSKalle Valo 	}
682277b024eSKalle Valo 	buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
683277b024eSKalle Valo 	if (!buf)
684277b024eSKalle Valo 		return -ENOMEM;
685277b024eSKalle Valo 
686277b024eSKalle Valo 	txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf;
687277b024eSKalle Valo 	txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
688277b024eSKalle Valo 	if (!power_cfg->is_power_auto) {
68965a576e2SAdrian Bunk 		u16 dbm_min = power_cfg->is_power_fixed ?
69065a576e2SAdrian Bunk 			      dbm : priv->min_tx_power_level;
69165a576e2SAdrian Bunk 
692277b024eSKalle Valo 		txp_cfg->mode = cpu_to_le32(1);
693277b024eSKalle Valo 		pg_tlv = (struct mwifiex_types_power_group *)
694277b024eSKalle Valo 			 (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
695277b024eSKalle Valo 		pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
696277b024eSKalle Valo 		pg_tlv->length =
697277b024eSKalle Valo 			cpu_to_le16(4 * sizeof(struct mwifiex_power_group));
698277b024eSKalle Valo 		pg = (struct mwifiex_power_group *)
699277b024eSKalle Valo 		     (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
700277b024eSKalle Valo 		      + sizeof(struct mwifiex_types_power_group));
701277b024eSKalle Valo 		/* Power group for modulation class HR/DSSS */
702277b024eSKalle Valo 		pg->first_rate_code = 0x00;
703277b024eSKalle Valo 		pg->last_rate_code = 0x03;
704277b024eSKalle Valo 		pg->modulation_class = MOD_CLASS_HR_DSSS;
705277b024eSKalle Valo 		pg->power_step = 0;
70665a576e2SAdrian Bunk 		pg->power_min = (s8) dbm_min;
707277b024eSKalle Valo 		pg->power_max = (s8) dbm;
708277b024eSKalle Valo 		pg++;
709277b024eSKalle Valo 		/* Power group for modulation class OFDM */
710277b024eSKalle Valo 		pg->first_rate_code = 0x00;
711277b024eSKalle Valo 		pg->last_rate_code = 0x07;
712277b024eSKalle Valo 		pg->modulation_class = MOD_CLASS_OFDM;
713277b024eSKalle Valo 		pg->power_step = 0;
71465a576e2SAdrian Bunk 		pg->power_min = (s8) dbm_min;
715277b024eSKalle Valo 		pg->power_max = (s8) dbm;
716277b024eSKalle Valo 		pg++;
717277b024eSKalle Valo 		/* Power group for modulation class HTBW20 */
718277b024eSKalle Valo 		pg->first_rate_code = 0x00;
719277b024eSKalle Valo 		pg->last_rate_code = 0x20;
720277b024eSKalle Valo 		pg->modulation_class = MOD_CLASS_HT;
721277b024eSKalle Valo 		pg->power_step = 0;
72265a576e2SAdrian Bunk 		pg->power_min = (s8) dbm_min;
723277b024eSKalle Valo 		pg->power_max = (s8) dbm;
724277b024eSKalle Valo 		pg->ht_bandwidth = HT_BW_20;
725277b024eSKalle Valo 		pg++;
726277b024eSKalle Valo 		/* Power group for modulation class HTBW40 */
727277b024eSKalle Valo 		pg->first_rate_code = 0x00;
728277b024eSKalle Valo 		pg->last_rate_code = 0x20;
729277b024eSKalle Valo 		pg->modulation_class = MOD_CLASS_HT;
730277b024eSKalle Valo 		pg->power_step = 0;
73165a576e2SAdrian Bunk 		pg->power_min = (s8) dbm_min;
732277b024eSKalle Valo 		pg->power_max = (s8) dbm;
733277b024eSKalle Valo 		pg->ht_bandwidth = HT_BW_40;
734277b024eSKalle Valo 	}
735277b024eSKalle Valo 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG,
736277b024eSKalle Valo 			       HostCmd_ACT_GEN_SET, 0, buf, true);
737277b024eSKalle Valo 
738277b024eSKalle Valo 	kfree(buf);
739277b024eSKalle Valo 	return ret;
740277b024eSKalle Valo }
741277b024eSKalle Valo 
742277b024eSKalle Valo /*
743277b024eSKalle Valo  * IOCTL request handler to get power save mode.
744277b024eSKalle Valo  *
745277b024eSKalle Valo  * This function prepares the correct firmware command and
746277b024eSKalle Valo  * issues it.
747277b024eSKalle Valo  */
mwifiex_drv_set_power(struct mwifiex_private * priv,u32 * ps_mode)748277b024eSKalle Valo int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode)
749277b024eSKalle Valo {
750277b024eSKalle Valo 	int ret;
751277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
752277b024eSKalle Valo 	u16 sub_cmd;
753277b024eSKalle Valo 
754277b024eSKalle Valo 	if (*ps_mode)
755277b024eSKalle Valo 		adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
756277b024eSKalle Valo 	else
757277b024eSKalle Valo 		adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
758277b024eSKalle Valo 	sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
759277b024eSKalle Valo 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
760277b024eSKalle Valo 			       sub_cmd, BITMAP_STA_PS, NULL, true);
761277b024eSKalle Valo 	if ((!ret) && (sub_cmd == DIS_AUTO_PS))
762277b024eSKalle Valo 		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
763277b024eSKalle Valo 				       GET_PS, 0, NULL, false);
764277b024eSKalle Valo 
765277b024eSKalle Valo 	return ret;
766277b024eSKalle Valo }
767277b024eSKalle Valo 
768277b024eSKalle Valo /*
769277b024eSKalle Valo  * IOCTL request handler to set/reset WPA IE.
770277b024eSKalle Valo  *
771277b024eSKalle Valo  * The supplied WPA IE is treated as a opaque buffer. Only the first field
772277b024eSKalle Valo  * is checked to determine WPA version. If buffer length is zero, the existing
773277b024eSKalle Valo  * WPA IE is reset.
774277b024eSKalle Valo  */
mwifiex_set_wpa_ie(struct mwifiex_private * priv,u8 * ie_data_ptr,u16 ie_len)7759ddb378bSXinming Hu static int mwifiex_set_wpa_ie(struct mwifiex_private *priv,
776277b024eSKalle Valo 			      u8 *ie_data_ptr, u16 ie_len)
777277b024eSKalle Valo {
778277b024eSKalle Valo 	if (ie_len) {
779277b024eSKalle Valo 		if (ie_len > sizeof(priv->wpa_ie)) {
780277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, ERROR,
781277b024eSKalle Valo 				    "failed to copy WPA IE, too big\n");
782277b024eSKalle Valo 			return -1;
783277b024eSKalle Valo 		}
784277b024eSKalle Valo 		memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
7851d8f5c13Schunfan chen 		priv->wpa_ie_len = ie_len;
786277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, CMD,
787277b024eSKalle Valo 			    "cmd: Set Wpa_ie_len=%d IE=%#x\n",
788277b024eSKalle Valo 			    priv->wpa_ie_len, priv->wpa_ie[0]);
789277b024eSKalle Valo 
790277b024eSKalle Valo 		if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
791277b024eSKalle Valo 			priv->sec_info.wpa_enabled = true;
792277b024eSKalle Valo 		} else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
793277b024eSKalle Valo 			priv->sec_info.wpa2_enabled = true;
794277b024eSKalle Valo 		} else {
795277b024eSKalle Valo 			priv->sec_info.wpa_enabled = false;
796277b024eSKalle Valo 			priv->sec_info.wpa2_enabled = false;
797277b024eSKalle Valo 		}
798277b024eSKalle Valo 	} else {
799277b024eSKalle Valo 		memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
800277b024eSKalle Valo 		priv->wpa_ie_len = 0;
801277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
802277b024eSKalle Valo 			    "info: reset wpa_ie_len=%d IE=%#x\n",
803277b024eSKalle Valo 			    priv->wpa_ie_len, priv->wpa_ie[0]);
804277b024eSKalle Valo 		priv->sec_info.wpa_enabled = false;
805277b024eSKalle Valo 		priv->sec_info.wpa2_enabled = false;
806277b024eSKalle Valo 	}
807277b024eSKalle Valo 
808277b024eSKalle Valo 	return 0;
809277b024eSKalle Valo }
810277b024eSKalle Valo 
811277b024eSKalle Valo /*
812277b024eSKalle Valo  * IOCTL request handler to set/reset WAPI IE.
813277b024eSKalle Valo  *
814277b024eSKalle Valo  * The supplied WAPI IE is treated as a opaque buffer. Only the first field
815277b024eSKalle Valo  * is checked to internally enable WAPI. If buffer length is zero, the existing
816277b024eSKalle Valo  * WAPI IE is reset.
817277b024eSKalle Valo  */
mwifiex_set_wapi_ie(struct mwifiex_private * priv,u8 * ie_data_ptr,u16 ie_len)818277b024eSKalle Valo static int mwifiex_set_wapi_ie(struct mwifiex_private *priv,
819277b024eSKalle Valo 			       u8 *ie_data_ptr, u16 ie_len)
820277b024eSKalle Valo {
821277b024eSKalle Valo 	if (ie_len) {
822277b024eSKalle Valo 		if (ie_len > sizeof(priv->wapi_ie)) {
823277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, ERROR,
824277b024eSKalle Valo 				    "info: failed to copy WAPI IE, too big\n");
825277b024eSKalle Valo 			return -1;
826277b024eSKalle Valo 		}
827277b024eSKalle Valo 		memcpy(priv->wapi_ie, ie_data_ptr, ie_len);
828277b024eSKalle Valo 		priv->wapi_ie_len = ie_len;
829277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, CMD,
830277b024eSKalle Valo 			    "cmd: Set wapi_ie_len=%d IE=%#x\n",
831277b024eSKalle Valo 			    priv->wapi_ie_len, priv->wapi_ie[0]);
832277b024eSKalle Valo 
833277b024eSKalle Valo 		if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY)
834277b024eSKalle Valo 			priv->sec_info.wapi_enabled = true;
835277b024eSKalle Valo 	} else {
836277b024eSKalle Valo 		memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie));
837277b024eSKalle Valo 		priv->wapi_ie_len = ie_len;
838277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
839277b024eSKalle Valo 			    "info: Reset wapi_ie_len=%d IE=%#x\n",
840277b024eSKalle Valo 			    priv->wapi_ie_len, priv->wapi_ie[0]);
841277b024eSKalle Valo 		priv->sec_info.wapi_enabled = false;
842277b024eSKalle Valo 	}
843277b024eSKalle Valo 	return 0;
844277b024eSKalle Valo }
845277b024eSKalle Valo 
846277b024eSKalle Valo /*
847277b024eSKalle Valo  * IOCTL request handler to set/reset WPS IE.
848277b024eSKalle Valo  *
849277b024eSKalle Valo  * The supplied WPS IE is treated as a opaque buffer. Only the first field
850277b024eSKalle Valo  * is checked to internally enable WPS. If buffer length is zero, the existing
851277b024eSKalle Valo  * WPS IE is reset.
852277b024eSKalle Valo  */
mwifiex_set_wps_ie(struct mwifiex_private * priv,u8 * ie_data_ptr,u16 ie_len)853277b024eSKalle Valo static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
854277b024eSKalle Valo 			       u8 *ie_data_ptr, u16 ie_len)
855277b024eSKalle Valo {
856277b024eSKalle Valo 	if (ie_len) {
857277b024eSKalle Valo 		if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
858277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, ERROR,
859277b024eSKalle Valo 				    "info: failed to copy WPS IE, too big\n");
860277b024eSKalle Valo 			return -1;
861277b024eSKalle Valo 		}
862277b024eSKalle Valo 
863277b024eSKalle Valo 		priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
864277b024eSKalle Valo 		if (!priv->wps_ie)
865277b024eSKalle Valo 			return -ENOMEM;
866277b024eSKalle Valo 
867277b024eSKalle Valo 		memcpy(priv->wps_ie, ie_data_ptr, ie_len);
868277b024eSKalle Valo 		priv->wps_ie_len = ie_len;
869277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, CMD,
870277b024eSKalle Valo 			    "cmd: Set wps_ie_len=%d IE=%#x\n",
871277b024eSKalle Valo 			    priv->wps_ie_len, priv->wps_ie[0]);
872277b024eSKalle Valo 	} else {
873277b024eSKalle Valo 		kfree(priv->wps_ie);
874277b024eSKalle Valo 		priv->wps_ie_len = ie_len;
875277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
876277b024eSKalle Valo 			    "info: Reset wps_ie_len=%d\n", priv->wps_ie_len);
877277b024eSKalle Valo 	}
878277b024eSKalle Valo 	return 0;
879277b024eSKalle Valo }
880277b024eSKalle Valo 
881277b024eSKalle Valo /*
882277b024eSKalle Valo  * IOCTL request handler to set WAPI key.
883277b024eSKalle Valo  *
884277b024eSKalle Valo  * This function prepares the correct firmware command and
885277b024eSKalle Valo  * issues it.
886277b024eSKalle Valo  */
mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private * priv,struct mwifiex_ds_encrypt_key * encrypt_key)887277b024eSKalle Valo static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
888277b024eSKalle Valo 			       struct mwifiex_ds_encrypt_key *encrypt_key)
889277b024eSKalle Valo {
890277b024eSKalle Valo 
891277b024eSKalle Valo 	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
892277b024eSKalle Valo 				HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED,
893277b024eSKalle Valo 				encrypt_key, true);
894277b024eSKalle Valo }
895277b024eSKalle Valo 
896277b024eSKalle Valo /*
897277b024eSKalle Valo  * IOCTL request handler to set WEP network key.
898277b024eSKalle Valo  *
899277b024eSKalle Valo  * This function prepares the correct firmware command and
900277b024eSKalle Valo  * issues it, after validation checks.
901277b024eSKalle Valo  */
mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private * priv,struct mwifiex_ds_encrypt_key * encrypt_key)902277b024eSKalle Valo static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
903277b024eSKalle Valo 			      struct mwifiex_ds_encrypt_key *encrypt_key)
904277b024eSKalle Valo {
905277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
906277b024eSKalle Valo 	int ret;
907277b024eSKalle Valo 	struct mwifiex_wep_key *wep_key;
908277b024eSKalle Valo 	int index;
909277b024eSKalle Valo 
910277b024eSKalle Valo 	if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
911277b024eSKalle Valo 		priv->wep_key_curr_index = 0;
912277b024eSKalle Valo 	wep_key = &priv->wep_key[priv->wep_key_curr_index];
913277b024eSKalle Valo 	index = encrypt_key->key_index;
914277b024eSKalle Valo 	if (encrypt_key->key_disable) {
915277b024eSKalle Valo 		priv->sec_info.wep_enabled = 0;
916277b024eSKalle Valo 	} else if (!encrypt_key->key_len) {
917277b024eSKalle Valo 		/* Copy the required key as the current key */
918277b024eSKalle Valo 		wep_key = &priv->wep_key[index];
919277b024eSKalle Valo 		if (!wep_key->key_length) {
920277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
921277b024eSKalle Valo 				    "key not set, so cannot enable it\n");
922277b024eSKalle Valo 			return -1;
923277b024eSKalle Valo 		}
924277b024eSKalle Valo 
925277b024eSKalle Valo 		if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) {
926277b024eSKalle Valo 			memcpy(encrypt_key->key_material,
927277b024eSKalle Valo 			       wep_key->key_material, wep_key->key_length);
928277b024eSKalle Valo 			encrypt_key->key_len = wep_key->key_length;
929277b024eSKalle Valo 		}
930277b024eSKalle Valo 
931277b024eSKalle Valo 		priv->wep_key_curr_index = (u16) index;
932277b024eSKalle Valo 		priv->sec_info.wep_enabled = 1;
933277b024eSKalle Valo 	} else {
934277b024eSKalle Valo 		wep_key = &priv->wep_key[index];
935277b024eSKalle Valo 		memset(wep_key, 0, sizeof(struct mwifiex_wep_key));
936277b024eSKalle Valo 		/* Copy the key in the driver */
937277b024eSKalle Valo 		memcpy(wep_key->key_material,
938277b024eSKalle Valo 		       encrypt_key->key_material,
939277b024eSKalle Valo 		       encrypt_key->key_len);
940277b024eSKalle Valo 		wep_key->key_index = index;
941277b024eSKalle Valo 		wep_key->key_length = encrypt_key->key_len;
942277b024eSKalle Valo 		priv->sec_info.wep_enabled = 1;
943277b024eSKalle Valo 	}
944277b024eSKalle Valo 	if (wep_key->key_length) {
945277b024eSKalle Valo 		void *enc_key;
946277b024eSKalle Valo 
947277b024eSKalle Valo 		if (encrypt_key->key_disable) {
948277b024eSKalle Valo 			memset(&priv->wep_key[index], 0,
949277b024eSKalle Valo 			       sizeof(struct mwifiex_wep_key));
950277b024eSKalle Valo 			goto done;
951277b024eSKalle Valo 		}
952277b024eSKalle Valo 
953277b024eSKalle Valo 		if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
954277b024eSKalle Valo 			enc_key = encrypt_key;
955277b024eSKalle Valo 		else
956277b024eSKalle Valo 			enc_key = NULL;
957277b024eSKalle Valo 
958277b024eSKalle Valo 		/* Send request to firmware */
959277b024eSKalle Valo 		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
960277b024eSKalle Valo 				       HostCmd_ACT_GEN_SET, 0, enc_key, false);
961277b024eSKalle Valo 		if (ret)
962277b024eSKalle Valo 			return ret;
963277b024eSKalle Valo 	}
964277b024eSKalle Valo 
965277b024eSKalle Valo done:
966277b024eSKalle Valo 	if (priv->sec_info.wep_enabled)
967277b024eSKalle Valo 		priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
968277b024eSKalle Valo 	else
969277b024eSKalle Valo 		priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
970277b024eSKalle Valo 
971277b024eSKalle Valo 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
972277b024eSKalle Valo 			       HostCmd_ACT_GEN_SET, 0,
973277b024eSKalle Valo 			       &priv->curr_pkt_filter, true);
974277b024eSKalle Valo 
975277b024eSKalle Valo 	return ret;
976277b024eSKalle Valo }
977277b024eSKalle Valo 
978277b024eSKalle Valo /*
979277b024eSKalle Valo  * IOCTL request handler to set WPA key.
980277b024eSKalle Valo  *
981277b024eSKalle Valo  * This function prepares the correct firmware command and
982277b024eSKalle Valo  * issues it, after validation checks.
983277b024eSKalle Valo  *
984277b024eSKalle Valo  * Current driver only supports key length of up to 32 bytes.
985277b024eSKalle Valo  *
986277b024eSKalle Valo  * This function can also be used to disable a currently set key.
987277b024eSKalle Valo  */
mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private * priv,struct mwifiex_ds_encrypt_key * encrypt_key)988277b024eSKalle Valo static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv,
989277b024eSKalle Valo 			      struct mwifiex_ds_encrypt_key *encrypt_key)
990277b024eSKalle Valo {
991277b024eSKalle Valo 	int ret;
992277b024eSKalle Valo 	u8 remove_key = false;
993277b024eSKalle Valo 	struct host_cmd_ds_802_11_key_material *ibss_key;
994277b024eSKalle Valo 
995277b024eSKalle Valo 	/* Current driver only supports key length of up to 32 bytes */
996277b024eSKalle Valo 	if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
997277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
998277b024eSKalle Valo 			    "key length too long\n");
999277b024eSKalle Valo 		return -1;
1000277b024eSKalle Valo 	}
1001277b024eSKalle Valo 
1002277b024eSKalle Valo 	if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
1003277b024eSKalle Valo 		/*
1004277b024eSKalle Valo 		 * IBSS/WPA-None uses only one key (Group) for both receiving
1005277b024eSKalle Valo 		 * and sending unicast and multicast packets.
1006277b024eSKalle Valo 		 */
1007277b024eSKalle Valo 		/* Send the key as PTK to firmware */
1008277b024eSKalle Valo 		encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
1009277b024eSKalle Valo 		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1010277b024eSKalle Valo 				       HostCmd_ACT_GEN_SET,
1011277b024eSKalle Valo 				       KEY_INFO_ENABLED, encrypt_key, false);
1012277b024eSKalle Valo 		if (ret)
1013277b024eSKalle Valo 			return ret;
1014277b024eSKalle Valo 
1015277b024eSKalle Valo 		ibss_key = &priv->aes_key;
1016277b024eSKalle Valo 		memset(ibss_key, 0,
1017277b024eSKalle Valo 		       sizeof(struct host_cmd_ds_802_11_key_material));
1018277b024eSKalle Valo 		/* Copy the key in the driver */
1019277b024eSKalle Valo 		memcpy(ibss_key->key_param_set.key, encrypt_key->key_material,
1020277b024eSKalle Valo 		       encrypt_key->key_len);
1021277b024eSKalle Valo 		memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len,
1022277b024eSKalle Valo 		       sizeof(ibss_key->key_param_set.key_len));
1023277b024eSKalle Valo 		ibss_key->key_param_set.key_type_id
1024277b024eSKalle Valo 			= cpu_to_le16(KEY_TYPE_ID_TKIP);
1025277b024eSKalle Valo 		ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED);
1026277b024eSKalle Valo 
1027277b024eSKalle Valo 		/* Send the key as GTK to firmware */
1028277b024eSKalle Valo 		encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST;
1029277b024eSKalle Valo 	}
1030277b024eSKalle Valo 
1031277b024eSKalle Valo 	if (!encrypt_key->key_index)
1032277b024eSKalle Valo 		encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
1033277b024eSKalle Valo 
1034277b024eSKalle Valo 	if (remove_key)
1035277b024eSKalle Valo 		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1036277b024eSKalle Valo 				       HostCmd_ACT_GEN_SET,
1037277b024eSKalle Valo 				       !KEY_INFO_ENABLED, encrypt_key, true);
1038277b024eSKalle Valo 	else
1039277b024eSKalle Valo 		ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1040277b024eSKalle Valo 				       HostCmd_ACT_GEN_SET,
1041277b024eSKalle Valo 				       KEY_INFO_ENABLED, encrypt_key, true);
1042277b024eSKalle Valo 
1043277b024eSKalle Valo 	return ret;
1044277b024eSKalle Valo }
1045277b024eSKalle Valo 
1046277b024eSKalle Valo /*
1047277b024eSKalle Valo  * IOCTL request handler to set/get network keys.
1048277b024eSKalle Valo  *
1049277b024eSKalle Valo  * This is a generic key handling function which supports WEP, WPA
1050277b024eSKalle Valo  * and WAPI.
1051277b024eSKalle Valo  */
1052277b024eSKalle Valo static int
mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private * priv,struct mwifiex_ds_encrypt_key * encrypt_key)1053277b024eSKalle Valo mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv,
1054277b024eSKalle Valo 			      struct mwifiex_ds_encrypt_key *encrypt_key)
1055277b024eSKalle Valo {
1056277b024eSKalle Valo 	int status;
1057277b024eSKalle Valo 
1058277b024eSKalle Valo 	if (encrypt_key->is_wapi_key)
1059277b024eSKalle Valo 		status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key);
1060277b024eSKalle Valo 	else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
1061277b024eSKalle Valo 		status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key);
1062277b024eSKalle Valo 	else
1063277b024eSKalle Valo 		status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key);
1064277b024eSKalle Valo 	return status;
1065277b024eSKalle Valo }
1066277b024eSKalle Valo 
1067277b024eSKalle Valo /*
1068277b024eSKalle Valo  * This function returns the driver version.
1069277b024eSKalle Valo  */
1070277b024eSKalle Valo int
mwifiex_drv_get_driver_version(struct mwifiex_adapter * adapter,char * version,int max_len)1071277b024eSKalle Valo mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
1072277b024eSKalle Valo 			       int max_len)
1073277b024eSKalle Valo {
1074277b024eSKalle Valo 	union {
1075277b024eSKalle Valo 		__le32 l;
1076277b024eSKalle Valo 		u8 c[4];
1077277b024eSKalle Valo 	} ver;
1078277b024eSKalle Valo 	char fw_ver[32];
1079277b024eSKalle Valo 
1080277b024eSKalle Valo 	ver.l = cpu_to_le32(adapter->fw_release_number);
1081277b024eSKalle Valo 	sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
1082277b024eSKalle Valo 
1083277b024eSKalle Valo 	snprintf(version, max_len, driver_version, fw_ver);
1084277b024eSKalle Valo 
1085277b024eSKalle Valo 	mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version);
1086277b024eSKalle Valo 
1087277b024eSKalle Valo 	return 0;
1088277b024eSKalle Valo }
1089277b024eSKalle Valo 
1090277b024eSKalle Valo /*
1091277b024eSKalle Valo  * Sends IOCTL request to set encoding parameters.
1092277b024eSKalle Valo  *
1093277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
1094277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
1095277b024eSKalle Valo  */
mwifiex_set_encode(struct mwifiex_private * priv,struct key_params * kp,const u8 * key,int key_len,u8 key_index,const u8 * mac_addr,int disable)1096277b024eSKalle Valo int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
1097277b024eSKalle Valo 		       const u8 *key, int key_len, u8 key_index,
1098277b024eSKalle Valo 		       const u8 *mac_addr, int disable)
1099277b024eSKalle Valo {
1100277b024eSKalle Valo 	struct mwifiex_ds_encrypt_key encrypt_key;
1101277b024eSKalle Valo 
11026a162200SChristophe Jaillet 	memset(&encrypt_key, 0, sizeof(encrypt_key));
1103277b024eSKalle Valo 	encrypt_key.key_len = key_len;
1104277b024eSKalle Valo 	encrypt_key.key_index = key_index;
1105277b024eSKalle Valo 
1106277b024eSKalle Valo 	if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
1107277b024eSKalle Valo 		encrypt_key.is_igtk_key = true;
1108277b024eSKalle Valo 
1109277b024eSKalle Valo 	if (!disable) {
1110277b024eSKalle Valo 		if (key_len)
1111277b024eSKalle Valo 			memcpy(encrypt_key.key_material, key, key_len);
1112277b024eSKalle Valo 		else
1113277b024eSKalle Valo 			encrypt_key.is_current_wep_key = true;
1114277b024eSKalle Valo 
1115277b024eSKalle Valo 		if (mac_addr)
1116277b024eSKalle Valo 			memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
1117277b024eSKalle Valo 		if (kp && kp->seq && kp->seq_len) {
1118277b024eSKalle Valo 			memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
1119277b024eSKalle Valo 			encrypt_key.pn_len = kp->seq_len;
1120277b024eSKalle Valo 			encrypt_key.is_rx_seq_valid = true;
1121277b024eSKalle Valo 		}
1122277b024eSKalle Valo 	} else {
1123277b024eSKalle Valo 		encrypt_key.key_disable = true;
1124277b024eSKalle Valo 		if (mac_addr)
1125277b024eSKalle Valo 			memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
1126277b024eSKalle Valo 	}
1127277b024eSKalle Valo 
1128277b024eSKalle Valo 	return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key);
1129277b024eSKalle Valo }
1130277b024eSKalle Valo 
1131277b024eSKalle Valo /*
1132277b024eSKalle Valo  * Sends IOCTL request to get extended version.
1133277b024eSKalle Valo  *
1134277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
1135277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
1136277b024eSKalle Valo  */
1137277b024eSKalle Valo int
mwifiex_get_ver_ext(struct mwifiex_private * priv,u32 version_str_sel)113817934b6aSXinming Hu mwifiex_get_ver_ext(struct mwifiex_private *priv, u32 version_str_sel)
1139277b024eSKalle Valo {
1140277b024eSKalle Valo 	struct mwifiex_ver_ext ver_ext;
1141277b024eSKalle Valo 
1142ba852018SChristophe Jaillet 	memset(&ver_ext, 0, sizeof(ver_ext));
114317934b6aSXinming Hu 	ver_ext.version_str_sel = version_str_sel;
1144277b024eSKalle Valo 	if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
1145277b024eSKalle Valo 			     HostCmd_ACT_GEN_GET, 0, &ver_ext, true))
1146277b024eSKalle Valo 		return -1;
1147277b024eSKalle Valo 
1148277b024eSKalle Valo 	return 0;
1149277b024eSKalle Valo }
1150277b024eSKalle Valo 
1151277b024eSKalle Valo int
mwifiex_remain_on_chan_cfg(struct mwifiex_private * priv,u16 action,struct ieee80211_channel * chan,unsigned int duration)1152277b024eSKalle Valo mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
1153277b024eSKalle Valo 			   struct ieee80211_channel *chan,
1154277b024eSKalle Valo 			   unsigned int duration)
1155277b024eSKalle Valo {
1156277b024eSKalle Valo 	struct host_cmd_ds_remain_on_chan roc_cfg;
1157277b024eSKalle Valo 	u8 sc;
1158277b024eSKalle Valo 
1159277b024eSKalle Valo 	memset(&roc_cfg, 0, sizeof(roc_cfg));
1160277b024eSKalle Valo 	roc_cfg.action = cpu_to_le16(action);
1161277b024eSKalle Valo 	if (action == HostCmd_ACT_GEN_SET) {
1162277b024eSKalle Valo 		roc_cfg.band_cfg = chan->band;
1163277b024eSKalle Valo 		sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
1164277b024eSKalle Valo 		roc_cfg.band_cfg |= (sc << 2);
1165277b024eSKalle Valo 
1166277b024eSKalle Valo 		roc_cfg.channel =
1167277b024eSKalle Valo 			ieee80211_frequency_to_channel(chan->center_freq);
1168277b024eSKalle Valo 		roc_cfg.duration = cpu_to_le32(duration);
1169277b024eSKalle Valo 	}
1170277b024eSKalle Valo 	if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN,
1171277b024eSKalle Valo 			     action, 0, &roc_cfg, true)) {
1172277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
1173277b024eSKalle Valo 			    "failed to remain on channel\n");
1174277b024eSKalle Valo 		return -1;
1175277b024eSKalle Valo 	}
1176277b024eSKalle Valo 
1177277b024eSKalle Valo 	return roc_cfg.status;
1178277b024eSKalle Valo }
1179277b024eSKalle Valo 
1180277b024eSKalle Valo /*
1181277b024eSKalle Valo  * Sends IOCTL request to get statistics information.
1182277b024eSKalle Valo  *
1183277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
1184277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
1185277b024eSKalle Valo  */
1186277b024eSKalle Valo int
mwifiex_get_stats_info(struct mwifiex_private * priv,struct mwifiex_ds_get_stats * log)1187277b024eSKalle Valo mwifiex_get_stats_info(struct mwifiex_private *priv,
1188277b024eSKalle Valo 		       struct mwifiex_ds_get_stats *log)
1189277b024eSKalle Valo {
1190277b024eSKalle Valo 	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG,
1191277b024eSKalle Valo 				HostCmd_ACT_GEN_GET, 0, log, true);
1192277b024eSKalle Valo }
1193277b024eSKalle Valo 
1194277b024eSKalle Valo /*
1195277b024eSKalle Valo  * IOCTL request handler to read/write register.
1196277b024eSKalle Valo  *
1197277b024eSKalle Valo  * This function prepares the correct firmware command and
1198277b024eSKalle Valo  * issues it.
1199277b024eSKalle Valo  *
1200277b024eSKalle Valo  * Access to the following registers are supported -
1201277b024eSKalle Valo  *      - MAC
1202277b024eSKalle Valo  *      - BBP
1203277b024eSKalle Valo  *      - RF
1204277b024eSKalle Valo  *      - PMIC
1205277b024eSKalle Valo  *      - CAU
1206277b024eSKalle Valo  */
mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private * priv,struct mwifiex_ds_reg_rw * reg_rw,u16 action)1207277b024eSKalle Valo static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv,
1208277b024eSKalle Valo 					struct mwifiex_ds_reg_rw *reg_rw,
1209277b024eSKalle Valo 					u16 action)
1210277b024eSKalle Valo {
1211277b024eSKalle Valo 	u16 cmd_no;
1212277b024eSKalle Valo 
12138cfb8600SPrasun Maiti 	switch (reg_rw->type) {
1214277b024eSKalle Valo 	case MWIFIEX_REG_MAC:
1215277b024eSKalle Valo 		cmd_no = HostCmd_CMD_MAC_REG_ACCESS;
1216277b024eSKalle Valo 		break;
1217277b024eSKalle Valo 	case MWIFIEX_REG_BBP:
1218277b024eSKalle Valo 		cmd_no = HostCmd_CMD_BBP_REG_ACCESS;
1219277b024eSKalle Valo 		break;
1220277b024eSKalle Valo 	case MWIFIEX_REG_RF:
1221277b024eSKalle Valo 		cmd_no = HostCmd_CMD_RF_REG_ACCESS;
1222277b024eSKalle Valo 		break;
1223277b024eSKalle Valo 	case MWIFIEX_REG_PMIC:
1224277b024eSKalle Valo 		cmd_no = HostCmd_CMD_PMIC_REG_ACCESS;
1225277b024eSKalle Valo 		break;
1226277b024eSKalle Valo 	case MWIFIEX_REG_CAU:
1227277b024eSKalle Valo 		cmd_no = HostCmd_CMD_CAU_REG_ACCESS;
1228277b024eSKalle Valo 		break;
1229277b024eSKalle Valo 	default:
1230277b024eSKalle Valo 		return -1;
1231277b024eSKalle Valo 	}
1232277b024eSKalle Valo 
1233277b024eSKalle Valo 	return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
1234277b024eSKalle Valo }
1235277b024eSKalle Valo 
1236277b024eSKalle Valo /*
1237277b024eSKalle Valo  * Sends IOCTL request to write to a register.
1238277b024eSKalle Valo  *
1239277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
1240277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
1241277b024eSKalle Valo  */
1242277b024eSKalle Valo int
mwifiex_reg_write(struct mwifiex_private * priv,u32 reg_type,u32 reg_offset,u32 reg_value)1243277b024eSKalle Valo mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type,
1244277b024eSKalle Valo 		  u32 reg_offset, u32 reg_value)
1245277b024eSKalle Valo {
1246277b024eSKalle Valo 	struct mwifiex_ds_reg_rw reg_rw;
1247277b024eSKalle Valo 
12488cfb8600SPrasun Maiti 	reg_rw.type = reg_type;
12498cfb8600SPrasun Maiti 	reg_rw.offset = reg_offset;
12508cfb8600SPrasun Maiti 	reg_rw.value = reg_value;
1251277b024eSKalle Valo 
1252277b024eSKalle Valo 	return mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_SET);
1253277b024eSKalle Valo }
1254277b024eSKalle Valo 
1255277b024eSKalle Valo /*
1256277b024eSKalle Valo  * Sends IOCTL request to read from a register.
1257277b024eSKalle Valo  *
1258277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
1259277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
1260277b024eSKalle Valo  */
1261277b024eSKalle Valo int
mwifiex_reg_read(struct mwifiex_private * priv,u32 reg_type,u32 reg_offset,u32 * value)1262277b024eSKalle Valo mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type,
1263277b024eSKalle Valo 		 u32 reg_offset, u32 *value)
1264277b024eSKalle Valo {
1265277b024eSKalle Valo 	int ret;
1266277b024eSKalle Valo 	struct mwifiex_ds_reg_rw reg_rw;
1267277b024eSKalle Valo 
12688cfb8600SPrasun Maiti 	reg_rw.type = reg_type;
12698cfb8600SPrasun Maiti 	reg_rw.offset = reg_offset;
1270277b024eSKalle Valo 	ret = mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_GET);
1271277b024eSKalle Valo 
1272277b024eSKalle Valo 	if (ret)
1273277b024eSKalle Valo 		goto done;
1274277b024eSKalle Valo 
12758cfb8600SPrasun Maiti 	*value = reg_rw.value;
1276277b024eSKalle Valo 
1277277b024eSKalle Valo done:
1278277b024eSKalle Valo 	return ret;
1279277b024eSKalle Valo }
1280277b024eSKalle Valo 
1281277b024eSKalle Valo /*
1282277b024eSKalle Valo  * Sends IOCTL request to read from EEPROM.
1283277b024eSKalle Valo  *
1284277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
1285277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
1286277b024eSKalle Valo  */
1287277b024eSKalle Valo int
mwifiex_eeprom_read(struct mwifiex_private * priv,u16 offset,u16 bytes,u8 * value)1288277b024eSKalle Valo mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
1289277b024eSKalle Valo 		    u8 *value)
1290277b024eSKalle Valo {
1291277b024eSKalle Valo 	int ret;
1292277b024eSKalle Valo 	struct mwifiex_ds_read_eeprom rd_eeprom;
1293277b024eSKalle Valo 
12948cfb8600SPrasun Maiti 	rd_eeprom.offset =  offset;
12958cfb8600SPrasun Maiti 	rd_eeprom.byte_count = bytes;
1296277b024eSKalle Valo 
1297277b024eSKalle Valo 	/* Send request to firmware */
1298277b024eSKalle Valo 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS,
1299277b024eSKalle Valo 			       HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true);
1300277b024eSKalle Valo 
1301277b024eSKalle Valo 	if (!ret)
13028cfb8600SPrasun Maiti 		memcpy(value, rd_eeprom.value, min((u16)MAX_EEPROM_DATA,
13038cfb8600SPrasun Maiti 		       rd_eeprom.byte_count));
1304277b024eSKalle Valo 	return ret;
1305277b024eSKalle Valo }
1306277b024eSKalle Valo 
1307277b024eSKalle Valo /*
1308277b024eSKalle Valo  * This function sets a generic IE. In addition to generic IE, it can
1309277b024eSKalle Valo  * also handle WPA, WPA2 and WAPI IEs.
1310277b024eSKalle Valo  */
1311277b024eSKalle Valo static int
mwifiex_set_gen_ie_helper(struct mwifiex_private * priv,u8 * ie_data_ptr,u16 ie_len)1312277b024eSKalle Valo mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
1313277b024eSKalle Valo 			  u16 ie_len)
1314277b024eSKalle Valo {
1315277b024eSKalle Valo 	struct ieee_types_vendor_header *pvendor_ie;
1316da2c9cedSColin Ian King 	static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
1317da2c9cedSColin Ian King 	static const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
13189ddb378bSXinming Hu 	u16 unparsed_len = ie_len, cur_ie_len;
1319277b024eSKalle Valo 
1320277b024eSKalle Valo 	/* If the passed length is zero, reset the buffer */
1321277b024eSKalle Valo 	if (!ie_len) {
1322277b024eSKalle Valo 		priv->gen_ie_buf_len = 0;
1323277b024eSKalle Valo 		priv->wps.session_enable = false;
1324277b024eSKalle Valo 		return 0;
13259ddb378bSXinming Hu 	} else if (!ie_data_ptr ||
13269ddb378bSXinming Hu 		   ie_len <= sizeof(struct ieee_types_header)) {
1327277b024eSKalle Valo 		return -1;
1328277b024eSKalle Valo 	}
1329277b024eSKalle Valo 	pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
1330277b024eSKalle Valo 
133184a38fb3Schunfan chen 	while (pvendor_ie) {
13329ddb378bSXinming Hu 		cur_ie_len = pvendor_ie->len + sizeof(struct ieee_types_header);
133384a38fb3Schunfan chen 
133484a38fb3Schunfan chen 		if (pvendor_ie->element_id == WLAN_EID_RSN) {
13359ddb378bSXinming Hu 			/* IE is a WPA/WPA2 IE so call set_wpa function */
13369ddb378bSXinming Hu 			mwifiex_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len);
13379ddb378bSXinming Hu 			priv->wps.session_enable = false;
13389ddb378bSXinming Hu 			goto next_ie;
133984a38fb3Schunfan chen 		}
134084a38fb3Schunfan chen 
134184a38fb3Schunfan chen 		if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) {
1342277b024eSKalle Valo 			/* IE is a WAPI IE so call set_wapi function */
13439ddb378bSXinming Hu 			mwifiex_set_wapi_ie(priv, (u8 *)pvendor_ie,
13449ddb378bSXinming Hu 					    cur_ie_len);
13459ddb378bSXinming Hu 			goto next_ie;
1346277b024eSKalle Valo 		}
134784a38fb3Schunfan chen 
13489ddb378bSXinming Hu 		if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
13499ddb378bSXinming Hu 			/* Test to see if it is a WPA IE, if not, then
13509ddb378bSXinming Hu 			 * it is a gen IE
13519ddb378bSXinming Hu 			 */
135263d7ef36SBrian Norris 			if (!memcmp(&pvendor_ie->oui, wpa_oui,
13539ddb378bSXinming Hu 				    sizeof(wpa_oui))) {
13549ddb378bSXinming Hu 				/* IE is a WPA/WPA2 IE so call set_wpa function
13559ddb378bSXinming Hu 				 */
13569ddb378bSXinming Hu 				mwifiex_set_wpa_ie(priv, (u8 *)pvendor_ie,
13579ddb378bSXinming Hu 						   cur_ie_len);
13589ddb378bSXinming Hu 				priv->wps.session_enable = false;
13599ddb378bSXinming Hu 				goto next_ie;
13609ddb378bSXinming Hu 			}
13619ddb378bSXinming Hu 
136263d7ef36SBrian Norris 			if (!memcmp(&pvendor_ie->oui, wps_oui,
13639ddb378bSXinming Hu 				    sizeof(wps_oui))) {
13649ddb378bSXinming Hu 				/* Test to see if it is a WPS IE,
13659ddb378bSXinming Hu 				 * if so, enable wps session flag
13669ddb378bSXinming Hu 				 */
13679ddb378bSXinming Hu 				priv->wps.session_enable = true;
13689ddb378bSXinming Hu 				mwifiex_dbg(priv->adapter, MSG,
13699ddb378bSXinming Hu 					    "WPS Session Enabled.\n");
13709ddb378bSXinming Hu 				mwifiex_set_wps_ie(priv, (u8 *)pvendor_ie,
13719ddb378bSXinming Hu 						   cur_ie_len);
13729ddb378bSXinming Hu 				goto next_ie;
13739ddb378bSXinming Hu 			}
13749ddb378bSXinming Hu 		}
13759ddb378bSXinming Hu 
13769ddb378bSXinming Hu 		/* Saved in gen_ie, such as P2P IE.etc.*/
13779ddb378bSXinming Hu 
13789ddb378bSXinming Hu 		/* Verify that the passed length is not larger than the
13799ddb378bSXinming Hu 		 * available space remaining in the buffer
13809ddb378bSXinming Hu 		 */
13819ddb378bSXinming Hu 		if (cur_ie_len <
13829ddb378bSXinming Hu 		    (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
13839ddb378bSXinming Hu 			/* Append the passed data to the end
13849ddb378bSXinming Hu 			 * of the genIeBuffer
13859ddb378bSXinming Hu 			 */
13869ddb378bSXinming Hu 			memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len,
13879ddb378bSXinming Hu 			       (u8 *)pvendor_ie, cur_ie_len);
13889ddb378bSXinming Hu 			/* Increment the stored buffer length by the
13899ddb378bSXinming Hu 			 * size passed
13909ddb378bSXinming Hu 			 */
13919ddb378bSXinming Hu 			priv->gen_ie_buf_len += cur_ie_len;
13929ddb378bSXinming Hu 		}
13939ddb378bSXinming Hu 
13949ddb378bSXinming Hu next_ie:
13959ddb378bSXinming Hu 		unparsed_len -= cur_ie_len;
139684a38fb3Schunfan chen 
139784a38fb3Schunfan chen 		if (unparsed_len <= sizeof(struct ieee_types_header))
139884a38fb3Schunfan chen 			pvendor_ie = NULL;
139984a38fb3Schunfan chen 		else
140084a38fb3Schunfan chen 			pvendor_ie = (struct ieee_types_vendor_header *)
14019ddb378bSXinming Hu 				(((u8 *)pvendor_ie) + cur_ie_len);
140284a38fb3Schunfan chen 	}
140384a38fb3Schunfan chen 
14049ddb378bSXinming Hu 	return 0;
1405277b024eSKalle Valo }
1406277b024eSKalle Valo 
1407277b024eSKalle Valo /*
1408277b024eSKalle Valo  * IOCTL request handler to set/get generic IE.
1409277b024eSKalle Valo  *
1410277b024eSKalle Valo  * In addition to various generic IEs, this function can also be
1411277b024eSKalle Valo  * used to set the ARP filter.
1412277b024eSKalle Valo  */
mwifiex_misc_ioctl_gen_ie(struct mwifiex_private * priv,struct mwifiex_ds_misc_gen_ie * gen_ie,u16 action)1413277b024eSKalle Valo static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv,
1414277b024eSKalle Valo 				     struct mwifiex_ds_misc_gen_ie *gen_ie,
1415277b024eSKalle Valo 				     u16 action)
1416277b024eSKalle Valo {
1417277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
1418277b024eSKalle Valo 
1419277b024eSKalle Valo 	switch (gen_ie->type) {
1420277b024eSKalle Valo 	case MWIFIEX_IE_TYPE_GEN_IE:
1421277b024eSKalle Valo 		if (action == HostCmd_ACT_GEN_GET) {
1422277b024eSKalle Valo 			gen_ie->len = priv->wpa_ie_len;
1423277b024eSKalle Valo 			memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
1424277b024eSKalle Valo 		} else {
1425277b024eSKalle Valo 			mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data,
1426277b024eSKalle Valo 						  (u16) gen_ie->len);
1427277b024eSKalle Valo 		}
1428277b024eSKalle Valo 		break;
1429277b024eSKalle Valo 	case MWIFIEX_IE_TYPE_ARP_FILTER:
1430277b024eSKalle Valo 		memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
1431277b024eSKalle Valo 		if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
1432277b024eSKalle Valo 			adapter->arp_filter_size = 0;
1433277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
1434277b024eSKalle Valo 				    "invalid ARP filter size\n");
1435277b024eSKalle Valo 			return -1;
1436277b024eSKalle Valo 		} else {
1437277b024eSKalle Valo 			memcpy(adapter->arp_filter, gen_ie->ie_data,
1438277b024eSKalle Valo 			       gen_ie->len);
1439277b024eSKalle Valo 			adapter->arp_filter_size = gen_ie->len;
1440277b024eSKalle Valo 		}
1441277b024eSKalle Valo 		break;
1442277b024eSKalle Valo 	default:
1443277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR, "invalid IE type\n");
1444277b024eSKalle Valo 		return -1;
1445277b024eSKalle Valo 	}
1446277b024eSKalle Valo 	return 0;
1447277b024eSKalle Valo }
1448277b024eSKalle Valo 
1449277b024eSKalle Valo /*
1450277b024eSKalle Valo  * Sends IOCTL request to set a generic IE.
1451277b024eSKalle Valo  *
1452277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
1453277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
1454277b024eSKalle Valo  */
1455277b024eSKalle Valo int
mwifiex_set_gen_ie(struct mwifiex_private * priv,const u8 * ie,int ie_len)1456277b024eSKalle Valo mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len)
1457277b024eSKalle Valo {
1458277b024eSKalle Valo 	struct mwifiex_ds_misc_gen_ie gen_ie;
1459277b024eSKalle Valo 
1460277b024eSKalle Valo 	if (ie_len > IEEE_MAX_IE_SIZE)
1461277b024eSKalle Valo 		return -EFAULT;
1462277b024eSKalle Valo 
1463277b024eSKalle Valo 	gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE;
1464277b024eSKalle Valo 	gen_ie.len = ie_len;
1465277b024eSKalle Valo 	memcpy(gen_ie.ie_data, ie, ie_len);
1466277b024eSKalle Valo 	if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET))
1467277b024eSKalle Valo 		return -EFAULT;
1468277b024eSKalle Valo 
1469277b024eSKalle Valo 	return 0;
1470277b024eSKalle Valo }
14718de00f1bSchunfan chen 
14728de00f1bSchunfan chen /* This function get Host Sleep wake up reason.
14738de00f1bSchunfan chen  *
14748de00f1bSchunfan chen  */
mwifiex_get_wakeup_reason(struct mwifiex_private * priv,u16 action,int cmd_type,struct mwifiex_ds_wakeup_reason * wakeup_reason)14758de00f1bSchunfan chen int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action,
14768de00f1bSchunfan chen 			      int cmd_type,
14778de00f1bSchunfan chen 			      struct mwifiex_ds_wakeup_reason *wakeup_reason)
14788de00f1bSchunfan chen {
14798de00f1bSchunfan chen 	int status = 0;
14808de00f1bSchunfan chen 
14818de00f1bSchunfan chen 	status = mwifiex_send_cmd(priv, HostCmd_CMD_HS_WAKEUP_REASON,
14828de00f1bSchunfan chen 				  HostCmd_ACT_GEN_GET, 0, wakeup_reason,
14838de00f1bSchunfan chen 				  cmd_type == MWIFIEX_SYNC_CMD);
14848de00f1bSchunfan chen 
14858de00f1bSchunfan chen 	return status;
14868de00f1bSchunfan chen }
148728bf8312SGanapathi Bhat 
mwifiex_get_chan_info(struct mwifiex_private * priv,struct mwifiex_channel_band * channel_band)148828bf8312SGanapathi Bhat int mwifiex_get_chan_info(struct mwifiex_private *priv,
148928bf8312SGanapathi Bhat 			  struct mwifiex_channel_band *channel_band)
149028bf8312SGanapathi Bhat {
149128bf8312SGanapathi Bhat 	int status = 0;
149228bf8312SGanapathi Bhat 
149328bf8312SGanapathi Bhat 	status = mwifiex_send_cmd(priv, HostCmd_CMD_STA_CONFIGURE,
149428bf8312SGanapathi Bhat 				  HostCmd_ACT_GEN_GET, 0, channel_band,
149528bf8312SGanapathi Bhat 				  MWIFIEX_SYNC_CMD);
149628bf8312SGanapathi Bhat 
149728bf8312SGanapathi Bhat 	return status;
149828bf8312SGanapathi Bhat }
1499