xref: /linux/drivers/net/wireless/nxp/nxpwifi/join.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1*73b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
2*73b01e57SJeff Chen /*
3*73b01e57SJeff Chen  * nxpwifi: association and ad-hoc start/join
4*73b01e57SJeff Chen  *
5*73b01e57SJeff Chen  * Copyright 2011-2024 NXP
6*73b01e57SJeff Chen  */
7*73b01e57SJeff Chen 
8*73b01e57SJeff Chen #include "cfg.h"
9*73b01e57SJeff Chen #include "util.h"
10*73b01e57SJeff Chen #include "fw.h"
11*73b01e57SJeff Chen #include "main.h"
12*73b01e57SJeff Chen #include "cmdevt.h"
13*73b01e57SJeff Chen #include "wmm.h"
14*73b01e57SJeff Chen #include "11n.h"
15*73b01e57SJeff Chen #include "11ac.h"
16*73b01e57SJeff Chen #include "11ax.h"
17*73b01e57SJeff Chen 
18*73b01e57SJeff Chen #define CAPINFO_MASK    (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9)))
19*73b01e57SJeff Chen 
20*73b01e57SJeff Chen /* Append generic IE as pass-through TLV for join */
21*73b01e57SJeff Chen static int
nxpwifi_cmd_append_generic_ie(struct nxpwifi_private * priv,u8 ** buffer)22*73b01e57SJeff Chen nxpwifi_cmd_append_generic_ie(struct nxpwifi_private *priv, u8 **buffer)
23*73b01e57SJeff Chen {
24*73b01e57SJeff Chen 	int ret_len = 0;
25*73b01e57SJeff Chen 	struct nxpwifi_ie_types_header ie_header;
26*73b01e57SJeff Chen 
27*73b01e57SJeff Chen 	/* Null Checks */
28*73b01e57SJeff Chen 	if (!buffer)
29*73b01e57SJeff Chen 		return 0;
30*73b01e57SJeff Chen 	if (!(*buffer))
31*73b01e57SJeff Chen 		return 0;
32*73b01e57SJeff Chen 
33*73b01e57SJeff Chen 	/*
34*73b01e57SJeff Chen 	 * If there is a generic element buffer setup, append it to the return
35*73b01e57SJeff Chen 	 * parameter buffer pointer.
36*73b01e57SJeff Chen 	 */
37*73b01e57SJeff Chen 	if (priv->gen_ie_buf_len) {
38*73b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
39*73b01e57SJeff Chen 			    "info: %s: append generic element len %d to %p\n",
40*73b01e57SJeff Chen 			    __func__, priv->gen_ie_buf_len, *buffer);
41*73b01e57SJeff Chen 
42*73b01e57SJeff Chen 		/* Wrap the generic element buffer with a pass through TLV type */
43*73b01e57SJeff Chen 		ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
44*73b01e57SJeff Chen 		ie_header.len = cpu_to_le16(priv->gen_ie_buf_len);
45*73b01e57SJeff Chen 		memcpy(*buffer, &ie_header, sizeof(ie_header));
46*73b01e57SJeff Chen 
47*73b01e57SJeff Chen 		/*
48*73b01e57SJeff Chen 		 * Increment the return size and the return buffer pointer
49*73b01e57SJeff Chen 		 * param
50*73b01e57SJeff Chen 		 */
51*73b01e57SJeff Chen 		*buffer += sizeof(ie_header);
52*73b01e57SJeff Chen 		ret_len += sizeof(ie_header);
53*73b01e57SJeff Chen 
54*73b01e57SJeff Chen 		/*
55*73b01e57SJeff Chen 		 * Copy the generic element buffer to the output buffer, advance
56*73b01e57SJeff Chen 		 * pointer
57*73b01e57SJeff Chen 		 */
58*73b01e57SJeff Chen 		memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len);
59*73b01e57SJeff Chen 
60*73b01e57SJeff Chen 		/*
61*73b01e57SJeff Chen 		 * Increment the return size and the return buffer pointer
62*73b01e57SJeff Chen 		 * param
63*73b01e57SJeff Chen 		 */
64*73b01e57SJeff Chen 		*buffer += priv->gen_ie_buf_len;
65*73b01e57SJeff Chen 		ret_len += priv->gen_ie_buf_len;
66*73b01e57SJeff Chen 
67*73b01e57SJeff Chen 		/* Reset the generic element buffer */
68*73b01e57SJeff Chen 		priv->gen_ie_buf_len = 0;
69*73b01e57SJeff Chen 	}
70*73b01e57SJeff Chen 
71*73b01e57SJeff Chen 	/* return the length appended to the buffer */
72*73b01e57SJeff Chen 	return ret_len;
73*73b01e57SJeff Chen }
74*73b01e57SJeff Chen 
75*73b01e57SJeff Chen /* Append TSF timestamp (AP TSF and local RX TSF) for reassoc */
76*73b01e57SJeff Chen static int
nxpwifi_cmd_append_tsf_tlv(struct nxpwifi_private * priv,u8 ** buffer,struct nxpwifi_bssdescriptor * bss_desc)77*73b01e57SJeff Chen nxpwifi_cmd_append_tsf_tlv(struct nxpwifi_private *priv, u8 **buffer,
78*73b01e57SJeff Chen 			   struct nxpwifi_bssdescriptor *bss_desc)
79*73b01e57SJeff Chen {
80*73b01e57SJeff Chen 	struct nxpwifi_ie_types_tsf_timestamp tsf_tlv;
81*73b01e57SJeff Chen 	__le64 tsf_val;
82*73b01e57SJeff Chen 
83*73b01e57SJeff Chen 	/* Null Checks */
84*73b01e57SJeff Chen 	if (!buffer)
85*73b01e57SJeff Chen 		return 0;
86*73b01e57SJeff Chen 	if (!*buffer)
87*73b01e57SJeff Chen 		return 0;
88*73b01e57SJeff Chen 
89*73b01e57SJeff Chen 	memset(&tsf_tlv, 0x00, sizeof(struct nxpwifi_ie_types_tsf_timestamp));
90*73b01e57SJeff Chen 
91*73b01e57SJeff Chen 	tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP);
92*73b01e57SJeff Chen 	tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val));
93*73b01e57SJeff Chen 
94*73b01e57SJeff Chen 	memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header));
95*73b01e57SJeff Chen 	*buffer += sizeof(tsf_tlv.header);
96*73b01e57SJeff Chen 
97*73b01e57SJeff Chen 	/* TSF at the time when beacon/probe_response was received */
98*73b01e57SJeff Chen 	tsf_val = cpu_to_le64(bss_desc->fw_tsf);
99*73b01e57SJeff Chen 	memcpy(*buffer, &tsf_val, sizeof(tsf_val));
100*73b01e57SJeff Chen 	*buffer += sizeof(tsf_val);
101*73b01e57SJeff Chen 
102*73b01e57SJeff Chen 	tsf_val = cpu_to_le64(bss_desc->timestamp);
103*73b01e57SJeff Chen 
104*73b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, INFO,
105*73b01e57SJeff Chen 		    "info: %s: TSF offset calc: %016llx - %016llx\n",
106*73b01e57SJeff Chen 		    __func__, bss_desc->timestamp, bss_desc->fw_tsf);
107*73b01e57SJeff Chen 
108*73b01e57SJeff Chen 	memcpy(*buffer, &tsf_val, sizeof(tsf_val));
109*73b01e57SJeff Chen 	*buffer += sizeof(tsf_val);
110*73b01e57SJeff Chen 
111*73b01e57SJeff Chen 	return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val));
112*73b01e57SJeff Chen }
113*73b01e57SJeff Chen 
114*73b01e57SJeff Chen /* Compute intersection of two rate sets; rate1 updated in-place */
nxpwifi_get_common_rates(struct nxpwifi_private * priv,u8 * rate1,u32 rate1_size,u8 * rate2,u32 rate2_size)115*73b01e57SJeff Chen static int nxpwifi_get_common_rates(struct nxpwifi_private *priv, u8 *rate1,
116*73b01e57SJeff Chen 				    u32 rate1_size, u8 *rate2, u32 rate2_size)
117*73b01e57SJeff Chen {
118*73b01e57SJeff Chen 	int ret;
119*73b01e57SJeff Chen 	u8 *ptr = rate1, *tmp;
120*73b01e57SJeff Chen 	u32 i, j;
121*73b01e57SJeff Chen 
122*73b01e57SJeff Chen 	tmp = kmemdup(rate1, rate1_size, GFP_KERNEL);
123*73b01e57SJeff Chen 	if (!tmp)
124*73b01e57SJeff Chen 		return -ENOMEM;
125*73b01e57SJeff Chen 
126*73b01e57SJeff Chen 	memset(rate1, 0, rate1_size);
127*73b01e57SJeff Chen 
128*73b01e57SJeff Chen 	for (i = 0; i < rate2_size && rate2[i]; i++) {
129*73b01e57SJeff Chen 		for (j = 0; j < rate1_size && tmp[j]; j++) {
130*73b01e57SJeff Chen 			/*
131*73b01e57SJeff Chen 			 * Check common rate, excluding the bit for
132*73b01e57SJeff Chen 			 * basic rate
133*73b01e57SJeff Chen 			 */
134*73b01e57SJeff Chen 			if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) {
135*73b01e57SJeff Chen 				*rate1++ = tmp[j];
136*73b01e57SJeff Chen 				break;
137*73b01e57SJeff Chen 			}
138*73b01e57SJeff Chen 		}
139*73b01e57SJeff Chen 	}
140*73b01e57SJeff Chen 
141*73b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n",
142*73b01e57SJeff Chen 		    priv->data_rate);
143*73b01e57SJeff Chen 
144*73b01e57SJeff Chen 	if (!priv->is_data_rate_auto) {
145*73b01e57SJeff Chen 		while (*ptr) {
146*73b01e57SJeff Chen 			if ((*ptr & 0x7f) == priv->data_rate) {
147*73b01e57SJeff Chen 				ret = 0;
148*73b01e57SJeff Chen 				goto done;
149*73b01e57SJeff Chen 			}
150*73b01e57SJeff Chen 			ptr++;
151*73b01e57SJeff Chen 		}
152*73b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
153*73b01e57SJeff Chen 			    "previously set fixed data rate %#x\t"
154*73b01e57SJeff Chen 			    "is not compatible with the network\n",
155*73b01e57SJeff Chen 			    priv->data_rate);
156*73b01e57SJeff Chen 
157*73b01e57SJeff Chen 		ret = -EPERM;
158*73b01e57SJeff Chen 		goto done;
159*73b01e57SJeff Chen 	}
160*73b01e57SJeff Chen 
161*73b01e57SJeff Chen 	ret = 0;
162*73b01e57SJeff Chen done:
163*73b01e57SJeff Chen 	kfree(tmp);
164*73b01e57SJeff Chen 	return ret;
165*73b01e57SJeff Chen }
166*73b01e57SJeff Chen 
167*73b01e57SJeff Chen /* Build common rates from BSS descriptor into out_rates */
168*73b01e57SJeff Chen static int
nxpwifi_setup_rates_from_bssdesc(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc,u8 * out_rates,u32 * out_rates_size)169*73b01e57SJeff Chen nxpwifi_setup_rates_from_bssdesc(struct nxpwifi_private *priv,
170*73b01e57SJeff Chen 				 struct nxpwifi_bssdescriptor *bss_desc,
171*73b01e57SJeff Chen 				 u8 *out_rates, u32 *out_rates_size)
172*73b01e57SJeff Chen {
173*73b01e57SJeff Chen 	u8 card_rates[NXPWIFI_SUPPORTED_RATES];
174*73b01e57SJeff Chen 	u32 card_rates_size;
175*73b01e57SJeff Chen 	int ret;
176*73b01e57SJeff Chen 
177*73b01e57SJeff Chen 	/* Copy AP supported rates */
178*73b01e57SJeff Chen 	memcpy(out_rates, bss_desc->supported_rates, NXPWIFI_SUPPORTED_RATES);
179*73b01e57SJeff Chen 	/* Get the STA supported rates */
180*73b01e57SJeff Chen 	card_rates_size = nxpwifi_get_active_data_rates(priv, card_rates);
181*73b01e57SJeff Chen 	/* Get the common rates between AP and STA supported rates */
182*73b01e57SJeff Chen 	ret = nxpwifi_get_common_rates(priv, out_rates, NXPWIFI_SUPPORTED_RATES,
183*73b01e57SJeff Chen 				       card_rates, card_rates_size);
184*73b01e57SJeff Chen 	if (ret) {
185*73b01e57SJeff Chen 		*out_rates_size = 0;
186*73b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
187*73b01e57SJeff Chen 			    "%s: cannot get common rates\n",
188*73b01e57SJeff Chen 			    __func__);
189*73b01e57SJeff Chen 	} else {
190*73b01e57SJeff Chen 		*out_rates_size =
191*73b01e57SJeff Chen 			min_t(size_t, strlen(out_rates), NXPWIFI_SUPPORTED_RATES);
192*73b01e57SJeff Chen 	}
193*73b01e57SJeff Chen 
194*73b01e57SJeff Chen 	return ret;
195*73b01e57SJeff Chen }
196*73b01e57SJeff Chen 
197*73b01e57SJeff Chen /* Append WPS IE as pass-through TLV for join */
198*73b01e57SJeff Chen static int
nxpwifi_cmd_append_wps_ie(struct nxpwifi_private * priv,u8 ** buffer)199*73b01e57SJeff Chen nxpwifi_cmd_append_wps_ie(struct nxpwifi_private *priv, u8 **buffer)
200*73b01e57SJeff Chen {
201*73b01e57SJeff Chen 	int ret_len = 0;
202*73b01e57SJeff Chen 	struct nxpwifi_ie_types_header ie_header;
203*73b01e57SJeff Chen 
204*73b01e57SJeff Chen 	if (!buffer || !*buffer)
205*73b01e57SJeff Chen 		return 0;
206*73b01e57SJeff Chen 
207*73b01e57SJeff Chen 	/*
208*73b01e57SJeff Chen 	 * If there is a wps element buffer setup, append it to the return
209*73b01e57SJeff Chen 	 * parameter buffer pointer.
210*73b01e57SJeff Chen 	 */
211*73b01e57SJeff Chen 	if (priv->wps_ie_len) {
212*73b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, CMD,
213*73b01e57SJeff Chen 			    "cmd: append wps element %d to %p\n",
214*73b01e57SJeff Chen 			    priv->wps_ie_len, *buffer);
215*73b01e57SJeff Chen 
216*73b01e57SJeff Chen 		/* Wrap the generic element buffer with a pass through TLV type */
217*73b01e57SJeff Chen 		ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
218*73b01e57SJeff Chen 		ie_header.len = cpu_to_le16(priv->wps_ie_len);
219*73b01e57SJeff Chen 		memcpy(*buffer, &ie_header, sizeof(ie_header));
220*73b01e57SJeff Chen 		*buffer += sizeof(ie_header);
221*73b01e57SJeff Chen 		ret_len += sizeof(ie_header);
222*73b01e57SJeff Chen 
223*73b01e57SJeff Chen 		memcpy(*buffer, priv->wps_ie, priv->wps_ie_len);
224*73b01e57SJeff Chen 		*buffer += priv->wps_ie_len;
225*73b01e57SJeff Chen 		ret_len += priv->wps_ie_len;
226*73b01e57SJeff Chen 	}
227*73b01e57SJeff Chen 
228*73b01e57SJeff Chen 	kfree(priv->wps_ie);
229*73b01e57SJeff Chen 	priv->wps_ie_len = 0;
230*73b01e57SJeff Chen 	return ret_len;
231*73b01e57SJeff Chen }
232*73b01e57SJeff Chen 
233*73b01e57SJeff Chen /* Append WPA/WPA2 RSN IE TLV */
nxpwifi_append_rsn_ie_wpa_wpa2(struct nxpwifi_private * priv,u8 ** buffer)234*73b01e57SJeff Chen static int nxpwifi_append_rsn_ie_wpa_wpa2(struct nxpwifi_private *priv,
235*73b01e57SJeff Chen 					  u8 **buffer)
236*73b01e57SJeff Chen {
237*73b01e57SJeff Chen 	struct nxpwifi_ie_types_rsn_param_set *rsn_ie_tlv;
238*73b01e57SJeff Chen 	int rsn_ie_len;
239*73b01e57SJeff Chen 
240*73b01e57SJeff Chen 	if (!buffer || !(*buffer))
241*73b01e57SJeff Chen 		return 0;
242*73b01e57SJeff Chen 
243*73b01e57SJeff Chen 	rsn_ie_tlv = (struct nxpwifi_ie_types_rsn_param_set *)(*buffer);
244*73b01e57SJeff Chen 	rsn_ie_tlv->header.type = cpu_to_le16((u16)priv->wpa_ie[0]);
245*73b01e57SJeff Chen 	rsn_ie_tlv->header.type =
246*73b01e57SJeff Chen 		cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF);
247*73b01e57SJeff Chen 	rsn_ie_tlv->header.len = cpu_to_le16((u16)priv->wpa_ie[1]);
248*73b01e57SJeff Chen 	rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len)
249*73b01e57SJeff Chen 							 & 0x00FF);
250*73b01e57SJeff Chen 	if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2))
251*73b01e57SJeff Chen 		memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2],
252*73b01e57SJeff Chen 		       le16_to_cpu(rsn_ie_tlv->header.len));
253*73b01e57SJeff Chen 	else
254*73b01e57SJeff Chen 		return -ENOMEM;
255*73b01e57SJeff Chen 
256*73b01e57SJeff Chen 	rsn_ie_len = sizeof(rsn_ie_tlv->header) +
257*73b01e57SJeff Chen 					le16_to_cpu(rsn_ie_tlv->header.len);
258*73b01e57SJeff Chen 	*buffer += rsn_ie_len;
259*73b01e57SJeff Chen 
260*73b01e57SJeff Chen 	return rsn_ie_len;
261*73b01e57SJeff Chen }
262*73b01e57SJeff Chen 
263*73b01e57SJeff Chen /* Build 802.11 association command and required TLVs */
nxpwifi_cmd_802_11_associate(struct nxpwifi_private * priv,struct host_cmd_ds_command * cmd,struct nxpwifi_bssdescriptor * bss_desc)264*73b01e57SJeff Chen int nxpwifi_cmd_802_11_associate(struct nxpwifi_private *priv,
265*73b01e57SJeff Chen 				 struct host_cmd_ds_command *cmd,
266*73b01e57SJeff Chen 				 struct nxpwifi_bssdescriptor *bss_desc)
267*73b01e57SJeff Chen {
268*73b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
269*73b01e57SJeff Chen 	struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate;
270*73b01e57SJeff Chen 	struct nxpwifi_ie_types_host_mlme *host_mlme_tlv;
271*73b01e57SJeff Chen 	struct nxpwifi_ie_types_ssid_param_set *ssid_tlv;
272*73b01e57SJeff Chen 	struct nxpwifi_ie_types_phy_param_set *phy_tlv;
273*73b01e57SJeff Chen 	struct nxpwifi_ie_types_ss_param_set *ss_tlv;
274*73b01e57SJeff Chen 	struct nxpwifi_ie_types_rates_param_set *rates_tlv;
275*73b01e57SJeff Chen 	struct nxpwifi_ie_types_auth_type *auth_tlv;
276*73b01e57SJeff Chen 	struct nxpwifi_ie_types_sae_pwe_mode *sae_pwe_tlv;
277*73b01e57SJeff Chen 	struct nxpwifi_ie_types_chan_list_param_set *chan_tlv;
278*73b01e57SJeff Chen 	u8 rates[NXPWIFI_SUPPORTED_RATES];
279*73b01e57SJeff Chen 	u32 rates_size;
280*73b01e57SJeff Chen 	u16 tmp_cap;
281*73b01e57SJeff Chen 	u8 *pos;
282*73b01e57SJeff Chen 	int rsn_ie_len = 0;
283*73b01e57SJeff Chen 	int ret;
284*73b01e57SJeff Chen 
285*73b01e57SJeff Chen 	pos = (u8 *)assoc;
286*73b01e57SJeff Chen 
287*73b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_802_11_ASSOCIATE);
288*73b01e57SJeff Chen 
289*73b01e57SJeff Chen 	/* Save so we know which BSS Desc to use in the response handler */
290*73b01e57SJeff Chen 	priv->attempted_bss_desc = bss_desc;
291*73b01e57SJeff Chen 
292*73b01e57SJeff Chen 	memcpy(assoc->peer_sta_addr,
293*73b01e57SJeff Chen 	       bss_desc->mac_address, sizeof(assoc->peer_sta_addr));
294*73b01e57SJeff Chen 	pos += sizeof(assoc->peer_sta_addr);
295*73b01e57SJeff Chen 
296*73b01e57SJeff Chen 	/* Set the listen interval */
297*73b01e57SJeff Chen 	assoc->listen_interval = cpu_to_le16(priv->listen_interval);
298*73b01e57SJeff Chen 	/* Set the beacon period */
299*73b01e57SJeff Chen 	assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period);
300*73b01e57SJeff Chen 
301*73b01e57SJeff Chen 	pos += sizeof(assoc->cap_info_bitmap);
302*73b01e57SJeff Chen 	pos += sizeof(assoc->listen_interval);
303*73b01e57SJeff Chen 	pos += sizeof(assoc->beacon_period);
304*73b01e57SJeff Chen 	pos += sizeof(assoc->dtim_period);
305*73b01e57SJeff Chen 
306*73b01e57SJeff Chen 	host_mlme_tlv = (struct nxpwifi_ie_types_host_mlme *)pos;
307*73b01e57SJeff Chen 	host_mlme_tlv->header.type = cpu_to_le16(TLV_TYPE_HOST_MLME);
308*73b01e57SJeff Chen 	host_mlme_tlv->header.len = cpu_to_le16(sizeof(host_mlme_tlv->host_mlme));
309*73b01e57SJeff Chen 	host_mlme_tlv->host_mlme = 1;
310*73b01e57SJeff Chen 	pos += sizeof(host_mlme_tlv->header) + sizeof(host_mlme_tlv->host_mlme);
311*73b01e57SJeff Chen 
312*73b01e57SJeff Chen 	ssid_tlv = (struct nxpwifi_ie_types_ssid_param_set *)pos;
313*73b01e57SJeff Chen 	ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID);
314*73b01e57SJeff Chen 	ssid_tlv->header.len = cpu_to_le16((u16)bss_desc->ssid.ssid_len);
315*73b01e57SJeff Chen 	memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid,
316*73b01e57SJeff Chen 	       le16_to_cpu(ssid_tlv->header.len));
317*73b01e57SJeff Chen 	pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len);
318*73b01e57SJeff Chen 
319*73b01e57SJeff Chen 	phy_tlv = (struct nxpwifi_ie_types_phy_param_set *)pos;
320*73b01e57SJeff Chen 	phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS);
321*73b01e57SJeff Chen 	phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set));
322*73b01e57SJeff Chen 	memcpy(&phy_tlv->fh_ds.ds_param_set,
323*73b01e57SJeff Chen 	       &bss_desc->phy_param_set.ds_param_set.current_chan,
324*73b01e57SJeff Chen 	       sizeof(phy_tlv->fh_ds.ds_param_set));
325*73b01e57SJeff Chen 	pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len);
326*73b01e57SJeff Chen 
327*73b01e57SJeff Chen 	ss_tlv = (struct nxpwifi_ie_types_ss_param_set *)pos;
328*73b01e57SJeff Chen 	ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS);
329*73b01e57SJeff Chen 	ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set));
330*73b01e57SJeff Chen 	pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len);
331*73b01e57SJeff Chen 
332*73b01e57SJeff Chen 	/* Get the common rates supported between the driver and the BSS Desc */
333*73b01e57SJeff Chen 	ret = nxpwifi_setup_rates_from_bssdesc(priv, bss_desc,
334*73b01e57SJeff Chen 					       rates, &rates_size);
335*73b01e57SJeff Chen 	if (ret)
336*73b01e57SJeff Chen 		return ret;
337*73b01e57SJeff Chen 
338*73b01e57SJeff Chen 	/* Save the data rates into Current BSS state structure */
339*73b01e57SJeff Chen 	priv->curr_bss_params.num_of_rates = rates_size;
340*73b01e57SJeff Chen 	memcpy(&priv->curr_bss_params.data_rates, rates, rates_size);
341*73b01e57SJeff Chen 
342*73b01e57SJeff Chen 	/* Setup the Rates TLV in the association command */
343*73b01e57SJeff Chen 	rates_tlv = (struct nxpwifi_ie_types_rates_param_set *)pos;
344*73b01e57SJeff Chen 	rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
345*73b01e57SJeff Chen 	rates_tlv->header.len = cpu_to_le16((u16)rates_size);
346*73b01e57SJeff Chen 	memcpy(rates_tlv->rates, rates, rates_size);
347*73b01e57SJeff Chen 	pos += sizeof(rates_tlv->header) + rates_size;
348*73b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO, "info: ASSOC_CMD: rates size = %d\n",
349*73b01e57SJeff Chen 		    rates_size);
350*73b01e57SJeff Chen 
351*73b01e57SJeff Chen 	/* Add the Authentication type */
352*73b01e57SJeff Chen 	auth_tlv = (struct nxpwifi_ie_types_auth_type *)pos;
353*73b01e57SJeff Chen 	auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
354*73b01e57SJeff Chen 	auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type));
355*73b01e57SJeff Chen 	if (priv->sec_info.wep_enabled)
356*73b01e57SJeff Chen 		auth_tlv->auth_type =
357*73b01e57SJeff Chen 			cpu_to_le16((u16)priv->sec_info.authentication_mode);
358*73b01e57SJeff Chen 	else
359*73b01e57SJeff Chen 		auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM);
360*73b01e57SJeff Chen 
361*73b01e57SJeff Chen 	pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len);
362*73b01e57SJeff Chen 
363*73b01e57SJeff Chen 	if (priv->sec_info.authentication_mode == WLAN_AUTH_SAE) {
364*73b01e57SJeff Chen 		auth_tlv->auth_type = cpu_to_le16(NXPWIFI_AUTHTYPE_SAE);
365*73b01e57SJeff Chen 		if (bss_desc->bcn_rsnx_ie &&
366*73b01e57SJeff Chen 		    bss_desc->bcn_rsnx_ie->datalen &&
367*73b01e57SJeff Chen 		    (bss_desc->bcn_rsnx_ie->data[0] &
368*73b01e57SJeff Chen 		     WLAN_RSNX_CAPA_SAE_H2E)) {
369*73b01e57SJeff Chen 			sae_pwe_tlv =
370*73b01e57SJeff Chen 				(struct nxpwifi_ie_types_sae_pwe_mode *)pos;
371*73b01e57SJeff Chen 			sae_pwe_tlv->header.type =
372*73b01e57SJeff Chen 				cpu_to_le16(TLV_TYPE_SAE_PWE_MODE);
373*73b01e57SJeff Chen 			sae_pwe_tlv->header.len =
374*73b01e57SJeff Chen 				cpu_to_le16(sizeof(sae_pwe_tlv->pwe[0]));
375*73b01e57SJeff Chen 			sae_pwe_tlv->pwe[0] = bss_desc->bcn_rsnx_ie->data[0];
376*73b01e57SJeff Chen 			pos += sizeof(sae_pwe_tlv->header) +
377*73b01e57SJeff Chen 				sizeof(sae_pwe_tlv->pwe[0]);
378*73b01e57SJeff Chen 		}
379*73b01e57SJeff Chen 	}
380*73b01e57SJeff Chen 
381*73b01e57SJeff Chen 	if (IS_SUPPORT_MULTI_BANDS(adapter) &&
382*73b01e57SJeff Chen 	    !(ISSUPP_11NENABLED(adapter->fw_cap_info) &&
383*73b01e57SJeff Chen 	    !bss_desc->disable_11n &&
384*73b01e57SJeff Chen 	    (priv->config_bands & BAND_GN ||
385*73b01e57SJeff Chen 	     priv->config_bands & BAND_AN) &&
386*73b01e57SJeff Chen 	    bss_desc->bcn_ht_cap)) {
387*73b01e57SJeff Chen 		/*
388*73b01e57SJeff Chen 		 * Append a channel TLV for the channel the attempted AP was
389*73b01e57SJeff Chen 		 * found on
390*73b01e57SJeff Chen 		 */
391*73b01e57SJeff Chen 		chan_tlv = (struct nxpwifi_ie_types_chan_list_param_set *)pos;
392*73b01e57SJeff Chen 		chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
393*73b01e57SJeff Chen 		chan_tlv->header.len =
394*73b01e57SJeff Chen 			cpu_to_le16(sizeof(struct nxpwifi_chan_scan_param_set));
395*73b01e57SJeff Chen 
396*73b01e57SJeff Chen 		memset(chan_tlv->chan_scan_param, 0x00,
397*73b01e57SJeff Chen 		       sizeof(struct nxpwifi_chan_scan_param_set));
398*73b01e57SJeff Chen 		chan_tlv->chan_scan_param[0].chan_number =
399*73b01e57SJeff Chen 			(bss_desc->phy_param_set.ds_param_set.current_chan);
400*73b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Chan = %d\n",
401*73b01e57SJeff Chen 			    chan_tlv->chan_scan_param[0].chan_number);
402*73b01e57SJeff Chen 
403*73b01e57SJeff Chen 		chan_tlv->chan_scan_param[0].band_cfg =
404*73b01e57SJeff Chen 			nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
405*73b01e57SJeff Chen 
406*73b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Band = %d\n",
407*73b01e57SJeff Chen 			    chan_tlv->chan_scan_param[0].band_cfg);
408*73b01e57SJeff Chen 		pos += sizeof(chan_tlv->header) +
409*73b01e57SJeff Chen 			sizeof(struct nxpwifi_chan_scan_param_set);
410*73b01e57SJeff Chen 	}
411*73b01e57SJeff Chen 
412*73b01e57SJeff Chen 	if (!priv->wps.session_enable) {
413*73b01e57SJeff Chen 		if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
414*73b01e57SJeff Chen 			rsn_ie_len = nxpwifi_append_rsn_ie_wpa_wpa2(priv, &pos);
415*73b01e57SJeff Chen 
416*73b01e57SJeff Chen 		if (rsn_ie_len == -ENOMEM)
417*73b01e57SJeff Chen 			return -ENOMEM;
418*73b01e57SJeff Chen 	}
419*73b01e57SJeff Chen 
420*73b01e57SJeff Chen 	if (ISSUPP_11NENABLED(adapter->fw_cap_info) &&
421*73b01e57SJeff Chen 	    !bss_desc->disable_11n &&
422*73b01e57SJeff Chen 	    (priv->config_bands & BAND_GN ||
423*73b01e57SJeff Chen 	     priv->config_bands & BAND_AN))
424*73b01e57SJeff Chen 		nxpwifi_cmd_append_11n_tlv(priv, bss_desc, &pos);
425*73b01e57SJeff Chen 
426*73b01e57SJeff Chen 	if (ISSUPP_11ACENABLED(adapter->fw_cap_info) &&
427*73b01e57SJeff Chen 	    !bss_desc->disable_11n && !bss_desc->disable_11ac &&
428*73b01e57SJeff Chen 	    (priv->config_bands & BAND_GAC ||
429*73b01e57SJeff Chen 	     priv->config_bands & BAND_AAC))
430*73b01e57SJeff Chen 		nxpwifi_cmd_append_11ac_tlv(priv, bss_desc, &pos);
431*73b01e57SJeff Chen 
432*73b01e57SJeff Chen 	if (ISSUPP_11AXENABLED(adapter->fw_cap_ext) &&
433*73b01e57SJeff Chen 	    nxpwifi_11ax_bandconfig_allowed(priv, bss_desc))
434*73b01e57SJeff Chen 		nxpwifi_cmd_append_11ax_tlv(priv, bss_desc, &pos);
435*73b01e57SJeff Chen 
436*73b01e57SJeff Chen 	/* Append vendor specific element TLV */
437*73b01e57SJeff Chen 	nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_ASSOC, &pos);
438*73b01e57SJeff Chen 
439*73b01e57SJeff Chen 	nxpwifi_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie,
440*73b01e57SJeff Chen 					    bss_desc->bcn_ht_cap);
441*73b01e57SJeff Chen 
442*73b01e57SJeff Chen 	if (priv->wps.session_enable && priv->wps_ie_len)
443*73b01e57SJeff Chen 		nxpwifi_cmd_append_wps_ie(priv, &pos);
444*73b01e57SJeff Chen 
445*73b01e57SJeff Chen 	nxpwifi_cmd_append_generic_ie(priv, &pos);
446*73b01e57SJeff Chen 
447*73b01e57SJeff Chen 	nxpwifi_cmd_append_tsf_tlv(priv, &pos, bss_desc);
448*73b01e57SJeff Chen 
449*73b01e57SJeff Chen 	nxpwifi_11h_process_join(priv, &pos, bss_desc);
450*73b01e57SJeff Chen 
451*73b01e57SJeff Chen 	cmd->size = cpu_to_le16((u16)(pos - (u8 *)assoc) + S_DS_GEN);
452*73b01e57SJeff Chen 
453*73b01e57SJeff Chen 	/* Set the Capability info at last */
454*73b01e57SJeff Chen 	tmp_cap = bss_desc->cap_info_bitmap;
455*73b01e57SJeff Chen 
456*73b01e57SJeff Chen 	if (priv->config_bands == BAND_B)
457*73b01e57SJeff Chen 		tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
458*73b01e57SJeff Chen 
459*73b01e57SJeff Chen 	tmp_cap &= CAPINFO_MASK;
460*73b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO,
461*73b01e57SJeff Chen 		    "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n",
462*73b01e57SJeff Chen 		    tmp_cap, CAPINFO_MASK);
463*73b01e57SJeff Chen 	assoc->cap_info_bitmap = cpu_to_le16(tmp_cap);
464*73b01e57SJeff Chen 
465*73b01e57SJeff Chen 	return ret;
466*73b01e57SJeff Chen }
467*73b01e57SJeff Chen 
assoc_failure_reason_to_str(u16 cap_info)468*73b01e57SJeff Chen static const char *assoc_failure_reason_to_str(u16 cap_info)
469*73b01e57SJeff Chen {
470*73b01e57SJeff Chen 	switch (cap_info) {
471*73b01e57SJeff Chen 	case CONNECT_ERR_AUTH_ERR_STA_FAILURE:
472*73b01e57SJeff Chen 		return "CONNECT_ERR_AUTH_ERR_STA_FAILURE";
473*73b01e57SJeff Chen 	case CONNECT_ERR_AUTH_MSG_UNHANDLED:
474*73b01e57SJeff Chen 		return "CONNECT_ERR_AUTH_MSG_UNHANDLED";
475*73b01e57SJeff Chen 	case CONNECT_ERR_ASSOC_ERR_TIMEOUT:
476*73b01e57SJeff Chen 		return "CONNECT_ERR_ASSOC_ERR_TIMEOUT";
477*73b01e57SJeff Chen 	case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED:
478*73b01e57SJeff Chen 		return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED";
479*73b01e57SJeff Chen 	case CONNECT_ERR_STA_FAILURE:
480*73b01e57SJeff Chen 		return "CONNECT_ERR_STA_FAILURE";
481*73b01e57SJeff Chen 	}
482*73b01e57SJeff Chen 
483*73b01e57SJeff Chen 	return "Unknown connect failure";
484*73b01e57SJeff Chen }
485*73b01e57SJeff Chen 
486*73b01e57SJeff Chen /*
487*73b01e57SJeff Chen  * Handle association command response.
488*73b01e57SJeff Chen  * Parse cap_info/status_code/AID and copy IEs, update connection state,
489*73b01e57SJeff Chen  * WMM and HT/HE parameters, queues and filters. On failure, return the
490*73b01e57SJeff Chen  * IEEE status code or a mapped timeout error.
491*73b01e57SJeff Chen  */
nxpwifi_ret_802_11_associate(struct nxpwifi_private * priv,struct host_cmd_ds_command * resp)492*73b01e57SJeff Chen int nxpwifi_ret_802_11_associate(struct nxpwifi_private *priv,
493*73b01e57SJeff Chen 				 struct host_cmd_ds_command *resp)
494*73b01e57SJeff Chen {
495*73b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
496*73b01e57SJeff Chen 	int ret = 0;
497*73b01e57SJeff Chen 	struct ieee_types_assoc_rsp *assoc_rsp;
498*73b01e57SJeff Chen 	struct nxpwifi_bssdescriptor *bss_desc;
499*73b01e57SJeff Chen 	bool enable_data = true;
500*73b01e57SJeff Chen 	u16 cap_info, status_code, aid;
501*73b01e57SJeff Chen 	const u8 *ie_ptr;
502*73b01e57SJeff Chen 	struct ieee80211_ht_operation *assoc_resp_ht_oper;
503*73b01e57SJeff Chen 	struct ieee80211_mgmt *hdr;
504*73b01e57SJeff Chen 
505*73b01e57SJeff Chen 	if (!priv->attempted_bss_desc) {
506*73b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
507*73b01e57SJeff Chen 			    "%s: failed, association terminated by host\n",
508*73b01e57SJeff Chen 			    __func__);
509*73b01e57SJeff Chen 		goto done;
510*73b01e57SJeff Chen 	}
511*73b01e57SJeff Chen 
512*73b01e57SJeff Chen 	hdr = (struct ieee80211_mgmt *)&resp->params;
513*73b01e57SJeff Chen 	if (!memcmp(hdr->bssid, priv->attempted_bss_desc->mac_address,
514*73b01e57SJeff Chen 		    ETH_ALEN))
515*73b01e57SJeff Chen 		assoc_rsp = (struct ieee_types_assoc_rsp *)&hdr->u.assoc_resp;
516*73b01e57SJeff Chen 	else
517*73b01e57SJeff Chen 		assoc_rsp = (struct ieee_types_assoc_rsp *)&resp->params;
518*73b01e57SJeff Chen 
519*73b01e57SJeff Chen 	cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap);
520*73b01e57SJeff Chen 	status_code = le16_to_cpu(assoc_rsp->status_code);
521*73b01e57SJeff Chen 	aid = le16_to_cpu(assoc_rsp->a_id);
522*73b01e57SJeff Chen 
523*73b01e57SJeff Chen 	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
524*73b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
525*73b01e57SJeff Chen 			    "invalid AID value 0x%x; bits 15:14 not set\n", aid);
526*73b01e57SJeff Chen 
527*73b01e57SJeff Chen 	aid &= ~(BIT(15) | BIT(14));
528*73b01e57SJeff Chen 
529*73b01e57SJeff Chen 	priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
530*73b01e57SJeff Chen 				   sizeof(priv->assoc_rsp_buf));
531*73b01e57SJeff Chen 
532*73b01e57SJeff Chen 	assoc_rsp->a_id = cpu_to_le16(aid);
533*73b01e57SJeff Chen 	memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
534*73b01e57SJeff Chen 
535*73b01e57SJeff Chen 	if (status_code) {
536*73b01e57SJeff Chen 		adapter->dbg.num_cmd_assoc_failure++;
537*73b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
538*73b01e57SJeff Chen 			    "ASSOC_RESP: failed,\t"
539*73b01e57SJeff Chen 			    "status code=%d err=%#x a_id=%#x\n",
540*73b01e57SJeff Chen 			    status_code, cap_info,
541*73b01e57SJeff Chen 			    le16_to_cpu(assoc_rsp->a_id));
542*73b01e57SJeff Chen 
543*73b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR, "assoc failure: reason %s\n",
544*73b01e57SJeff Chen 			    assoc_failure_reason_to_str(cap_info));
545*73b01e57SJeff Chen 		if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) {
546*73b01e57SJeff Chen 			if (status_code == NXPWIFI_ASSOC_CMD_FAILURE_AUTH) {
547*73b01e57SJeff Chen 				ret = WLAN_STATUS_AUTH_TIMEOUT;
548*73b01e57SJeff Chen 				nxpwifi_dbg(adapter, ERROR,
549*73b01e57SJeff Chen 					    "ASSOC_RESP: AUTH timeout\n");
550*73b01e57SJeff Chen 			} else {
551*73b01e57SJeff Chen 				ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
552*73b01e57SJeff Chen 				nxpwifi_dbg(adapter, ERROR,
553*73b01e57SJeff Chen 					    "ASSOC_RESP: UNSPECIFIED failure\n");
554*73b01e57SJeff Chen 			}
555*73b01e57SJeff Chen 
556*73b01e57SJeff Chen 			priv->assoc_rsp_size = 0;
557*73b01e57SJeff Chen 		} else {
558*73b01e57SJeff Chen 			ret = status_code;
559*73b01e57SJeff Chen 		}
560*73b01e57SJeff Chen 
561*73b01e57SJeff Chen 		goto done;
562*73b01e57SJeff Chen 	}
563*73b01e57SJeff Chen 
564*73b01e57SJeff Chen 	/* Send a Media Connected event, according to the Spec */
565*73b01e57SJeff Chen 	priv->media_connected = true;
566*73b01e57SJeff Chen 
567*73b01e57SJeff Chen 	adapter->ps_state = PS_STATE_AWAKE;
568*73b01e57SJeff Chen 	adapter->pps_uapsd_mode = false;
569*73b01e57SJeff Chen 	adapter->tx_lock_flag = false;
570*73b01e57SJeff Chen 
571*73b01e57SJeff Chen 	/* Set the attempted BSSID Index to current */
572*73b01e57SJeff Chen 	bss_desc = priv->attempted_bss_desc;
573*73b01e57SJeff Chen 
574*73b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO, "info: ASSOC_RESP: %s\n",
575*73b01e57SJeff Chen 		    bss_desc->ssid.ssid);
576*73b01e57SJeff Chen 
577*73b01e57SJeff Chen 	/* Make a copy of current BSSID descriptor */
578*73b01e57SJeff Chen 	memcpy(&priv->curr_bss_params.bss_descriptor,
579*73b01e57SJeff Chen 	       bss_desc, sizeof(struct nxpwifi_bssdescriptor));
580*73b01e57SJeff Chen 
581*73b01e57SJeff Chen 	/* Update curr_bss_params */
582*73b01e57SJeff Chen 	priv->curr_bss_params.bss_descriptor.channel =
583*73b01e57SJeff Chen 		bss_desc->phy_param_set.ds_param_set.current_chan;
584*73b01e57SJeff Chen 
585*73b01e57SJeff Chen 	priv->curr_bss_params.band = (u8)bss_desc->bss_band;
586*73b01e57SJeff Chen 
587*73b01e57SJeff Chen 	if (bss_desc->wmm_ie.element_id == WLAN_EID_VENDOR_SPECIFIC)
588*73b01e57SJeff Chen 		priv->curr_bss_params.wmm_enabled = true;
589*73b01e57SJeff Chen 	else
590*73b01e57SJeff Chen 		priv->curr_bss_params.wmm_enabled = false;
591*73b01e57SJeff Chen 
592*73b01e57SJeff Chen 	if ((priv->wmm_required || bss_desc->bcn_ht_cap) &&
593*73b01e57SJeff Chen 	    priv->curr_bss_params.wmm_enabled)
594*73b01e57SJeff Chen 		priv->wmm_enabled = true;
595*73b01e57SJeff Chen 	else
596*73b01e57SJeff Chen 		priv->wmm_enabled = false;
597*73b01e57SJeff Chen 
598*73b01e57SJeff Chen 	priv->curr_bss_params.wmm_uapsd_enabled = false;
599*73b01e57SJeff Chen 
600*73b01e57SJeff Chen 	priv->curr_bss_params.wmm_uapsd_enabled = priv->wmm_enabled &&
601*73b01e57SJeff Chen 		(bss_desc->wmm_ie.qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD);
602*73b01e57SJeff Chen 
603*73b01e57SJeff Chen 	/* Store the bandwidth information from assoc response */
604*73b01e57SJeff Chen 	ie_ptr = cfg80211_find_ie(WLAN_EID_HT_OPERATION, assoc_rsp->ie_buffer,
605*73b01e57SJeff Chen 				  priv->assoc_rsp_size
606*73b01e57SJeff Chen 				  - sizeof(struct ieee_types_assoc_rsp));
607*73b01e57SJeff Chen 	if (ie_ptr) {
608*73b01e57SJeff Chen 		assoc_resp_ht_oper = (struct ieee80211_ht_operation *)(ie_ptr
609*73b01e57SJeff Chen 					+ sizeof(struct element));
610*73b01e57SJeff Chen 		priv->assoc_resp_ht_param = assoc_resp_ht_oper->ht_param;
611*73b01e57SJeff Chen 		priv->ht_param_present = true;
612*73b01e57SJeff Chen 	} else {
613*73b01e57SJeff Chen 		priv->ht_param_present = false;
614*73b01e57SJeff Chen 	}
615*73b01e57SJeff Chen 
616*73b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO,
617*73b01e57SJeff Chen 		    "info: ASSOC_RESP: curr_pkt_filter is %#x\n",
618*73b01e57SJeff Chen 		    priv->curr_pkt_filter);
619*73b01e57SJeff Chen 	if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
620*73b01e57SJeff Chen 		priv->wpa_is_gtk_set = false;
621*73b01e57SJeff Chen 
622*73b01e57SJeff Chen 	if (priv->wmm_enabled) {
623*73b01e57SJeff Chen 		/* Don't re-enable carrier until we get the WMM_GET_STATUS event */
624*73b01e57SJeff Chen 		enable_data = false;
625*73b01e57SJeff Chen 	} else {
626*73b01e57SJeff Chen 		/* Since WMM is not enabled, setup the queues with the defaults */
627*73b01e57SJeff Chen 		nxpwifi_wmm_setup_queue_priorities(priv, NULL);
628*73b01e57SJeff Chen 		nxpwifi_wmm_setup_ac_downgrade(priv);
629*73b01e57SJeff Chen 	}
630*73b01e57SJeff Chen 
631*73b01e57SJeff Chen 	if (enable_data)
632*73b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO,
633*73b01e57SJeff Chen 			    "info: post association, re-enabling data flow\n");
634*73b01e57SJeff Chen 
635*73b01e57SJeff Chen 	/* Reset SNR/NF/RSSI values */
636*73b01e57SJeff Chen 	priv->data_rssi_last = 0;
637*73b01e57SJeff Chen 	priv->data_nf_last = 0;
638*73b01e57SJeff Chen 	priv->data_rssi_avg = 0;
639*73b01e57SJeff Chen 	priv->data_nf_avg = 0;
640*73b01e57SJeff Chen 	priv->bcn_rssi_last = 0;
641*73b01e57SJeff Chen 	priv->bcn_nf_last = 0;
642*73b01e57SJeff Chen 	priv->bcn_rssi_avg = 0;
643*73b01e57SJeff Chen 	priv->bcn_nf_avg = 0;
644*73b01e57SJeff Chen 	priv->rxpd_rate = 0;
645*73b01e57SJeff Chen 	priv->rxpd_htinfo = 0;
646*73b01e57SJeff Chen 
647*73b01e57SJeff Chen 	nxpwifi_save_curr_bcn(priv);
648*73b01e57SJeff Chen 
649*73b01e57SJeff Chen 	adapter->dbg.num_cmd_assoc_success++;
650*73b01e57SJeff Chen 
651*73b01e57SJeff Chen 	nxpwifi_dbg(adapter, MSG, "assoc: associated with %pM\n",
652*73b01e57SJeff Chen 		    priv->attempted_bss_desc->mac_address);
653*73b01e57SJeff Chen 
654*73b01e57SJeff Chen 	/* Add the ra_list here for infra mode as there will be only 1 ra always */
655*73b01e57SJeff Chen 	nxpwifi_ralist_add(priv,
656*73b01e57SJeff Chen 			   priv->curr_bss_params.bss_descriptor.mac_address);
657*73b01e57SJeff Chen 
658*73b01e57SJeff Chen 	netif_carrier_on(priv->netdev);
659*73b01e57SJeff Chen 	nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
660*73b01e57SJeff Chen 
661*73b01e57SJeff Chen 	if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
662*73b01e57SJeff Chen 		priv->scan_block = true;
663*73b01e57SJeff Chen 	else
664*73b01e57SJeff Chen 		priv->port_open = true;
665*73b01e57SJeff Chen 
666*73b01e57SJeff Chen done:
667*73b01e57SJeff Chen 	/* Need to indicate IOCTL complete */
668*73b01e57SJeff Chen 	if (adapter->curr_cmd->wait_q_enabled) {
669*73b01e57SJeff Chen 		if (ret)
670*73b01e57SJeff Chen 			adapter->cmd_wait_q.status = -1;
671*73b01e57SJeff Chen 		else
672*73b01e57SJeff Chen 			adapter->cmd_wait_q.status = 0;
673*73b01e57SJeff Chen 	}
674*73b01e57SJeff Chen 
675*73b01e57SJeff Chen 	return ret;
676*73b01e57SJeff Chen }
677*73b01e57SJeff Chen 
678*73b01e57SJeff Chen /* Associate to the specified BSS (STA only) */
nxpwifi_associate(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)679*73b01e57SJeff Chen int nxpwifi_associate(struct nxpwifi_private *priv,
680*73b01e57SJeff Chen 		      struct nxpwifi_bssdescriptor *bss_desc)
681*73b01e57SJeff Chen {
682*73b01e57SJeff Chen 	/*
683*73b01e57SJeff Chen 	 * Return error if the adapter is not STA role or table entry
684*73b01e57SJeff Chen 	 * is not marked as infra.
685*73b01e57SJeff Chen 	 */
686*73b01e57SJeff Chen 	if ((GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA) ||
687*73b01e57SJeff Chen 	    bss_desc->bss_mode != NL80211_IFTYPE_STATION)
688*73b01e57SJeff Chen 		return -EINVAL;
689*73b01e57SJeff Chen 
690*73b01e57SJeff Chen 	if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
691*73b01e57SJeff Chen 	    !bss_desc->disable_11n && !bss_desc->disable_11ac &&
692*73b01e57SJeff Chen 	    priv->config_bands & BAND_AAC)
693*73b01e57SJeff Chen 		nxpwifi_set_11ac_ba_params(priv);
694*73b01e57SJeff Chen 	else
695*73b01e57SJeff Chen 		nxpwifi_set_ba_params(priv);
696*73b01e57SJeff Chen 
697*73b01e57SJeff Chen 	/*
698*73b01e57SJeff Chen 	 * Clear any past association response stored for application
699*73b01e57SJeff Chen 	 * retrieval
700*73b01e57SJeff Chen 	 */
701*73b01e57SJeff Chen 	priv->assoc_rsp_size = 0;
702*73b01e57SJeff Chen 
703*73b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_802_11_ASSOCIATE,
704*73b01e57SJeff Chen 				HOST_ACT_GEN_SET, 0, bss_desc, true);
705*73b01e57SJeff Chen }
706*73b01e57SJeff Chen 
707*73b01e57SJeff Chen /* Send deauth to disconnect from an infrastructure BSS */
nxpwifi_deauthenticate_infra(struct nxpwifi_private * priv,u8 * mac)708*73b01e57SJeff Chen static int nxpwifi_deauthenticate_infra(struct nxpwifi_private *priv, u8 *mac)
709*73b01e57SJeff Chen {
710*73b01e57SJeff Chen 	u8 mac_address[ETH_ALEN];
711*73b01e57SJeff Chen 	int ret;
712*73b01e57SJeff Chen 
713*73b01e57SJeff Chen 	if (!mac || is_zero_ether_addr(mac))
714*73b01e57SJeff Chen 		memcpy(mac_address,
715*73b01e57SJeff Chen 		       priv->curr_bss_params.bss_descriptor.mac_address,
716*73b01e57SJeff Chen 		       ETH_ALEN);
717*73b01e57SJeff Chen 	else
718*73b01e57SJeff Chen 		memcpy(mac_address, mac, ETH_ALEN);
719*73b01e57SJeff Chen 
720*73b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE,
721*73b01e57SJeff Chen 			       HOST_ACT_GEN_SET, 0, mac_address, true);
722*73b01e57SJeff Chen 
723*73b01e57SJeff Chen 	return ret;
724*73b01e57SJeff Chen }
725*73b01e57SJeff Chen 
726*73b01e57SJeff Chen /* Disconnect from the current BSS (STA/P2P/AP) */
nxpwifi_deauthenticate(struct nxpwifi_private * priv,u8 * mac)727*73b01e57SJeff Chen int nxpwifi_deauthenticate(struct nxpwifi_private *priv, u8 *mac)
728*73b01e57SJeff Chen {
729*73b01e57SJeff Chen 	int ret = 0;
730*73b01e57SJeff Chen 
731*73b01e57SJeff Chen 	if (!priv->media_connected)
732*73b01e57SJeff Chen 		return 0;
733*73b01e57SJeff Chen 
734*73b01e57SJeff Chen 	priv->auth_flag = 0;
735*73b01e57SJeff Chen 	priv->auth_alg = WLAN_AUTH_NONE;
736*73b01e57SJeff Chen 	priv->host_mlme_reg = false;
737*73b01e57SJeff Chen 	priv->mgmt_frame_mask = 0;
738*73b01e57SJeff Chen 
739*73b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG,
740*73b01e57SJeff Chen 			       HOST_ACT_GEN_SET, 0,
741*73b01e57SJeff Chen 			       &priv->mgmt_frame_mask, false);
742*73b01e57SJeff Chen 	if (ret) {
743*73b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
744*73b01e57SJeff Chen 			    "could not unregister mgmt frame rx\n");
745*73b01e57SJeff Chen 		return ret;
746*73b01e57SJeff Chen 	}
747*73b01e57SJeff Chen 
748*73b01e57SJeff Chen 	switch (priv->bss_mode) {
749*73b01e57SJeff Chen 	case NL80211_IFTYPE_STATION:
750*73b01e57SJeff Chen 		ret = nxpwifi_deauthenticate_infra(priv, mac);
751*73b01e57SJeff Chen 		if (ret)
752*73b01e57SJeff Chen 			cfg80211_disconnected(priv->netdev, 0, NULL, 0,
753*73b01e57SJeff Chen 					      true, GFP_KERNEL);
754*73b01e57SJeff Chen 		break;
755*73b01e57SJeff Chen 	case NL80211_IFTYPE_AP:
756*73b01e57SJeff Chen 		ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
757*73b01e57SJeff Chen 				       HOST_ACT_GEN_SET, 0, NULL, true);
758*73b01e57SJeff Chen 		break;
759*73b01e57SJeff Chen 	default:
760*73b01e57SJeff Chen 		break;
761*73b01e57SJeff Chen 	}
762*73b01e57SJeff Chen 
763*73b01e57SJeff Chen 	return ret;
764*73b01e57SJeff Chen }
765*73b01e57SJeff Chen 
766*73b01e57SJeff Chen /* Deauthenticate/disconnect from all BSS. */
nxpwifi_deauthenticate_all(struct nxpwifi_adapter * adapter)767*73b01e57SJeff Chen void nxpwifi_deauthenticate_all(struct nxpwifi_adapter *adapter)
768*73b01e57SJeff Chen {
769*73b01e57SJeff Chen 	struct nxpwifi_private *priv;
770*73b01e57SJeff Chen 	int i;
771*73b01e57SJeff Chen 
772*73b01e57SJeff Chen 	for (i = 0; i < adapter->priv_num; i++) {
773*73b01e57SJeff Chen 		priv = adapter->priv[i];
774*73b01e57SJeff Chen 		nxpwifi_deauthenticate(priv, NULL);
775*73b01e57SJeff Chen 	}
776*73b01e57SJeff Chen }
777*73b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_deauthenticate_all);
778*73b01e57SJeff Chen 
779*73b01e57SJeff Chen /* Convert band to radio type used in channel TLV. */
nxpwifi_band_to_radio_type(u16 config_bands)780*73b01e57SJeff Chen u8 nxpwifi_band_to_radio_type(u16 config_bands)
781*73b01e57SJeff Chen {
782*73b01e57SJeff Chen 	if (config_bands & BAND_A || config_bands & BAND_AN ||
783*73b01e57SJeff Chen 	    config_bands & BAND_AAC || config_bands & BAND_AAX)
784*73b01e57SJeff Chen 		return HOST_SCAN_RADIO_TYPE_A;
785*73b01e57SJeff Chen 
786*73b01e57SJeff Chen 	return HOST_SCAN_RADIO_TYPE_BG;
787*73b01e57SJeff Chen }
788