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