xref: /linux/drivers/net/wireless/nxp/nxpwifi/ie.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1*73b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
2*73b01e57SJeff Chen /*
3*73b01e57SJeff Chen  * nxpwifi: management element handling - set/delete elements.
4*73b01e57SJeff Chen  *
5*73b01e57SJeff Chen  * Copyright 2011-2024 NXP
6*73b01e57SJeff Chen  */
7*73b01e57SJeff Chen 
8*73b01e57SJeff Chen #include "main.h"
9*73b01e57SJeff Chen #include "cmdevt.h"
10*73b01e57SJeff Chen 
11*73b01e57SJeff Chen /* Return true if the IE index is used by another interface. */
12*73b01e57SJeff Chen static bool
nxpwifi_ie_index_used_by_other_intf(struct nxpwifi_private * priv,u16 idx)13*73b01e57SJeff Chen nxpwifi_ie_index_used_by_other_intf(struct nxpwifi_private *priv, u16 idx)
14*73b01e57SJeff Chen {
15*73b01e57SJeff Chen 	int i;
16*73b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
17*73b01e57SJeff Chen 	struct nxpwifi_ie *ie;
18*73b01e57SJeff Chen 
19*73b01e57SJeff Chen 	for (i = 0; i < adapter->priv_num; i++) {
20*73b01e57SJeff Chen 		if (adapter->priv[i] != priv) {
21*73b01e57SJeff Chen 			ie = &adapter->priv[i]->mgmt_ie[idx];
22*73b01e57SJeff Chen 			if (ie->mgmt_subtype_mask && ie->ie_length)
23*73b01e57SJeff Chen 				return true;
24*73b01e57SJeff Chen 		}
25*73b01e57SJeff Chen 	}
26*73b01e57SJeff Chen 
27*73b01e57SJeff Chen 	return false;
28*73b01e57SJeff Chen }
29*73b01e57SJeff Chen 
30*73b01e57SJeff Chen /* Pick an unused IE index for a new element. */
31*73b01e57SJeff Chen static int
nxpwifi_ie_get_autoidx(struct nxpwifi_private * priv,u16 subtype_mask,struct nxpwifi_ie * ie,u16 * index)32*73b01e57SJeff Chen nxpwifi_ie_get_autoidx(struct nxpwifi_private *priv, u16 subtype_mask,
33*73b01e57SJeff Chen 		       struct nxpwifi_ie *ie, u16 *index)
34*73b01e57SJeff Chen {
35*73b01e57SJeff Chen 	u16 mask, len, i;
36*73b01e57SJeff Chen 
37*73b01e57SJeff Chen 	for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
38*73b01e57SJeff Chen 		mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
39*73b01e57SJeff Chen 		len = le16_to_cpu(ie->ie_length);
40*73b01e57SJeff Chen 
41*73b01e57SJeff Chen 		if (mask == NXPWIFI_AUTO_IDX_MASK)
42*73b01e57SJeff Chen 			continue;
43*73b01e57SJeff Chen 
44*73b01e57SJeff Chen 		if (mask == subtype_mask) {
45*73b01e57SJeff Chen 			if (len > IEEE_MAX_IE_SIZE)
46*73b01e57SJeff Chen 				continue;
47*73b01e57SJeff Chen 
48*73b01e57SJeff Chen 			*index = i;
49*73b01e57SJeff Chen 			return 0;
50*73b01e57SJeff Chen 		}
51*73b01e57SJeff Chen 
52*73b01e57SJeff Chen 		if (!priv->mgmt_ie[i].ie_length) {
53*73b01e57SJeff Chen 			if (nxpwifi_ie_index_used_by_other_intf(priv, i))
54*73b01e57SJeff Chen 				continue;
55*73b01e57SJeff Chen 
56*73b01e57SJeff Chen 			*index = i;
57*73b01e57SJeff Chen 			return 0;
58*73b01e57SJeff Chen 		}
59*73b01e57SJeff Chen 	}
60*73b01e57SJeff Chen 
61*73b01e57SJeff Chen 	return -ENOENT;
62*73b01e57SJeff Chen }
63*73b01e57SJeff Chen 
64*73b01e57SJeff Chen /* Build IE list and resolve AUTO index before sending to FW. */
65*73b01e57SJeff Chen static int
nxpwifi_update_autoindex_ies(struct nxpwifi_private * priv,struct nxpwifi_ie_list * ie_list)66*73b01e57SJeff Chen nxpwifi_update_autoindex_ies(struct nxpwifi_private *priv,
67*73b01e57SJeff Chen 			     struct nxpwifi_ie_list *ie_list)
68*73b01e57SJeff Chen {
69*73b01e57SJeff Chen 	u16 travel_len, index, mask;
70*73b01e57SJeff Chen 	s16 input_len, tlv_len;
71*73b01e57SJeff Chen 	struct nxpwifi_ie *ie;
72*73b01e57SJeff Chen 	u8 *tmp;
73*73b01e57SJeff Chen 
74*73b01e57SJeff Chen 	input_len = le16_to_cpu(ie_list->len);
75*73b01e57SJeff Chen 	travel_len = sizeof(struct nxpwifi_ie_types_header);
76*73b01e57SJeff Chen 
77*73b01e57SJeff Chen 	ie_list->len = 0;
78*73b01e57SJeff Chen 
79*73b01e57SJeff Chen 	while (input_len >= sizeof(struct nxpwifi_ie_types_header)) {
80*73b01e57SJeff Chen 		ie = (struct nxpwifi_ie *)(((u8 *)ie_list) + travel_len);
81*73b01e57SJeff Chen 		tlv_len = le16_to_cpu(ie->ie_length);
82*73b01e57SJeff Chen 		travel_len += tlv_len + NXPWIFI_IE_HDR_SIZE;
83*73b01e57SJeff Chen 
84*73b01e57SJeff Chen 		if (input_len < tlv_len + NXPWIFI_IE_HDR_SIZE)
85*73b01e57SJeff Chen 			return -EINVAL;
86*73b01e57SJeff Chen 		index = le16_to_cpu(ie->ie_index);
87*73b01e57SJeff Chen 		mask = le16_to_cpu(ie->mgmt_subtype_mask);
88*73b01e57SJeff Chen 
89*73b01e57SJeff Chen 		if (index == NXPWIFI_AUTO_IDX_MASK) {
90*73b01e57SJeff Chen 			/* automatic addition */
91*73b01e57SJeff Chen 			if (nxpwifi_ie_get_autoidx(priv, mask, ie, &index))
92*73b01e57SJeff Chen 				return -ENOENT;
93*73b01e57SJeff Chen 			if (index == NXPWIFI_AUTO_IDX_MASK)
94*73b01e57SJeff Chen 				return -EINVAL;
95*73b01e57SJeff Chen 
96*73b01e57SJeff Chen 			tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
97*73b01e57SJeff Chen 			memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
98*73b01e57SJeff Chen 			priv->mgmt_ie[index].ie_length = ie->ie_length;
99*73b01e57SJeff Chen 			priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
100*73b01e57SJeff Chen 			priv->mgmt_ie[index].mgmt_subtype_mask =
101*73b01e57SJeff Chen 							cpu_to_le16(mask);
102*73b01e57SJeff Chen 
103*73b01e57SJeff Chen 			ie->ie_index = cpu_to_le16(index);
104*73b01e57SJeff Chen 		} else {
105*73b01e57SJeff Chen 			if (mask != NXPWIFI_DELETE_MASK)
106*73b01e57SJeff Chen 				return -EINVAL;
107*73b01e57SJeff Chen 			/*
108*73b01e57SJeff Chen 			 * Check if this index is being used on any
109*73b01e57SJeff Chen 			 * other interface.
110*73b01e57SJeff Chen 			 */
111*73b01e57SJeff Chen 			if (nxpwifi_ie_index_used_by_other_intf(priv, index))
112*73b01e57SJeff Chen 				return -EPERM;
113*73b01e57SJeff Chen 
114*73b01e57SJeff Chen 			ie->ie_length = 0;
115*73b01e57SJeff Chen 			memcpy(&priv->mgmt_ie[index], ie,
116*73b01e57SJeff Chen 			       sizeof(struct nxpwifi_ie));
117*73b01e57SJeff Chen 		}
118*73b01e57SJeff Chen 
119*73b01e57SJeff Chen 		le16_unaligned_add_cpu
120*73b01e57SJeff Chen 		(&ie_list->len,
121*73b01e57SJeff Chen 		 le16_to_cpu(priv->mgmt_ie[index].ie_length) +
122*73b01e57SJeff Chen 		 NXPWIFI_IE_HDR_SIZE);
123*73b01e57SJeff Chen 		input_len -= tlv_len + NXPWIFI_IE_HDR_SIZE;
124*73b01e57SJeff Chen 	}
125*73b01e57SJeff Chen 
126*73b01e57SJeff Chen 	if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP)
127*73b01e57SJeff Chen 		return nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG,
128*73b01e57SJeff Chen 					HOST_ACT_GEN_SET,
129*73b01e57SJeff Chen 					UAP_CUSTOM_IE_I, ie_list, true);
130*73b01e57SJeff Chen 
131*73b01e57SJeff Chen 	return 0;
132*73b01e57SJeff Chen }
133*73b01e57SJeff Chen 
134*73b01e57SJeff Chen /* Pack beacon/probe/assoc IEs into one list and update auto-assigned indices. */
135*73b01e57SJeff Chen static int
nxpwifi_update_uap_custom_ie(struct nxpwifi_private * priv,struct nxpwifi_ie * beacon_ie,u16 * beacon_idx,struct nxpwifi_ie * pr_ie,u16 * probe_idx,struct nxpwifi_ie * ar_ie,u16 * assoc_idx)136*73b01e57SJeff Chen nxpwifi_update_uap_custom_ie(struct nxpwifi_private *priv,
137*73b01e57SJeff Chen 			     struct nxpwifi_ie *beacon_ie, u16 *beacon_idx,
138*73b01e57SJeff Chen 			     struct nxpwifi_ie *pr_ie, u16 *probe_idx,
139*73b01e57SJeff Chen 			     struct nxpwifi_ie *ar_ie, u16 *assoc_idx)
140*73b01e57SJeff Chen {
141*73b01e57SJeff Chen 	struct nxpwifi_ie_list *ap_custom_ie;
142*73b01e57SJeff Chen 	u8 *pos;
143*73b01e57SJeff Chen 	u16 len;
144*73b01e57SJeff Chen 	int ret;
145*73b01e57SJeff Chen 
146*73b01e57SJeff Chen 	ap_custom_ie = kzalloc_obj(*ap_custom_ie, GFP_KERNEL);
147*73b01e57SJeff Chen 	if (!ap_custom_ie)
148*73b01e57SJeff Chen 		return -ENOMEM;
149*73b01e57SJeff Chen 
150*73b01e57SJeff Chen 	ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
151*73b01e57SJeff Chen 	pos = (u8 *)ap_custom_ie->ie_list;
152*73b01e57SJeff Chen 
153*73b01e57SJeff Chen 	if (beacon_ie) {
154*73b01e57SJeff Chen 		len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
155*73b01e57SJeff Chen 		      le16_to_cpu(beacon_ie->ie_length);
156*73b01e57SJeff Chen 		memcpy(pos, beacon_ie, len);
157*73b01e57SJeff Chen 		pos += len;
158*73b01e57SJeff Chen 		le16_unaligned_add_cpu(&ap_custom_ie->len, len);
159*73b01e57SJeff Chen 	}
160*73b01e57SJeff Chen 	if (pr_ie) {
161*73b01e57SJeff Chen 		len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
162*73b01e57SJeff Chen 		      le16_to_cpu(pr_ie->ie_length);
163*73b01e57SJeff Chen 		memcpy(pos, pr_ie, len);
164*73b01e57SJeff Chen 		pos += len;
165*73b01e57SJeff Chen 		le16_unaligned_add_cpu(&ap_custom_ie->len, len);
166*73b01e57SJeff Chen 	}
167*73b01e57SJeff Chen 	if (ar_ie) {
168*73b01e57SJeff Chen 		len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
169*73b01e57SJeff Chen 		      le16_to_cpu(ar_ie->ie_length);
170*73b01e57SJeff Chen 		memcpy(pos, ar_ie, len);
171*73b01e57SJeff Chen 		pos += len;
172*73b01e57SJeff Chen 		le16_unaligned_add_cpu(&ap_custom_ie->len, len);
173*73b01e57SJeff Chen 	}
174*73b01e57SJeff Chen 
175*73b01e57SJeff Chen 	ret = nxpwifi_update_autoindex_ies(priv, ap_custom_ie);
176*73b01e57SJeff Chen 
177*73b01e57SJeff Chen 	pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
178*73b01e57SJeff Chen 	if (beacon_ie && *beacon_idx == NXPWIFI_AUTO_IDX_MASK) {
179*73b01e57SJeff Chen 		/* save beacon element index after auto-indexing */
180*73b01e57SJeff Chen 		*beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
181*73b01e57SJeff Chen 		len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
182*73b01e57SJeff Chen 		      le16_to_cpu(beacon_ie->ie_length);
183*73b01e57SJeff Chen 		pos += len;
184*73b01e57SJeff Chen 	}
185*73b01e57SJeff Chen 	if (pr_ie && le16_to_cpu(pr_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK) {
186*73b01e57SJeff Chen 		/* save probe resp element index after auto-indexing */
187*73b01e57SJeff Chen 		*probe_idx = *((u16 *)pos);
188*73b01e57SJeff Chen 		len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
189*73b01e57SJeff Chen 		      le16_to_cpu(pr_ie->ie_length);
190*73b01e57SJeff Chen 		pos += len;
191*73b01e57SJeff Chen 	}
192*73b01e57SJeff Chen 	if (ar_ie && le16_to_cpu(ar_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK)
193*73b01e57SJeff Chen 		/* save assoc resp element index after auto-indexing */
194*73b01e57SJeff Chen 		*assoc_idx = *((u16 *)pos);
195*73b01e57SJeff Chen 
196*73b01e57SJeff Chen 	kfree(ap_custom_ie);
197*73b01e57SJeff Chen 	return ret;
198*73b01e57SJeff Chen }
199*73b01e57SJeff Chen 
200*73b01e57SJeff Chen /* Append vendor IE (if present) into nxpwifi_ie, allocating as needed. */
nxpwifi_update_vs_ie(const u8 * ies,int ies_len,struct nxpwifi_ie ** ie_ptr,u16 mask,unsigned int oui,u8 oui_type)201*73b01e57SJeff Chen static int nxpwifi_update_vs_ie(const u8 *ies, int ies_len,
202*73b01e57SJeff Chen 				struct nxpwifi_ie **ie_ptr, u16 mask,
203*73b01e57SJeff Chen 				unsigned int oui, u8 oui_type)
204*73b01e57SJeff Chen {
205*73b01e57SJeff Chen 	struct element *vs_ie;
206*73b01e57SJeff Chen 	struct nxpwifi_ie *ie = *ie_ptr;
207*73b01e57SJeff Chen 	const u8 *vendor_ie;
208*73b01e57SJeff Chen 
209*73b01e57SJeff Chen 	vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
210*73b01e57SJeff Chen 	if (vendor_ie) {
211*73b01e57SJeff Chen 		if (!*ie_ptr) {
212*73b01e57SJeff Chen 			*ie_ptr = kzalloc_obj(struct nxpwifi_ie, GFP_KERNEL);
213*73b01e57SJeff Chen 			if (!*ie_ptr)
214*73b01e57SJeff Chen 				return -ENOMEM;
215*73b01e57SJeff Chen 			ie = *ie_ptr;
216*73b01e57SJeff Chen 		}
217*73b01e57SJeff Chen 
218*73b01e57SJeff Chen 		vs_ie = (struct element *)vendor_ie;
219*73b01e57SJeff Chen 		if (le16_to_cpu(ie->ie_length) + vs_ie->datalen + 2 >
220*73b01e57SJeff Chen 			IEEE_MAX_IE_SIZE)
221*73b01e57SJeff Chen 			return -EINVAL;
222*73b01e57SJeff Chen 		memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
223*73b01e57SJeff Chen 		       vs_ie, vs_ie->datalen + 2);
224*73b01e57SJeff Chen 		le16_unaligned_add_cpu(&ie->ie_length, vs_ie->datalen + 2);
225*73b01e57SJeff Chen 		ie->mgmt_subtype_mask = cpu_to_le16(mask);
226*73b01e57SJeff Chen 		ie->ie_index = cpu_to_le16(NXPWIFI_AUTO_IDX_MASK);
227*73b01e57SJeff Chen 	}
228*73b01e57SJeff Chen 
229*73b01e57SJeff Chen 	*ie_ptr = ie;
230*73b01e57SJeff Chen 	return 0;
231*73b01e57SJeff Chen }
232*73b01e57SJeff Chen 
233*73b01e57SJeff Chen /* Parse beacon/probe/assoc IEs from cfg80211 and push them to FW. */
nxpwifi_set_mgmt_beacon_data_ies(struct nxpwifi_private * priv,struct cfg80211_beacon_data * data)234*73b01e57SJeff Chen static int nxpwifi_set_mgmt_beacon_data_ies(struct nxpwifi_private *priv,
235*73b01e57SJeff Chen 					    struct cfg80211_beacon_data *data)
236*73b01e57SJeff Chen {
237*73b01e57SJeff Chen 	struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
238*73b01e57SJeff Chen 	u16 beacon_idx = NXPWIFI_AUTO_IDX_MASK, pr_idx = NXPWIFI_AUTO_IDX_MASK;
239*73b01e57SJeff Chen 	u16 ar_idx = NXPWIFI_AUTO_IDX_MASK;
240*73b01e57SJeff Chen 	int ret = 0;
241*73b01e57SJeff Chen 
242*73b01e57SJeff Chen 	if (data->beacon_ies && data->beacon_ies_len) {
243*73b01e57SJeff Chen 		nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
244*73b01e57SJeff Chen 				     &beacon_ie, MGMT_MASK_BEACON,
245*73b01e57SJeff Chen 				     WLAN_OUI_MICROSOFT,
246*73b01e57SJeff Chen 				     WLAN_OUI_TYPE_MICROSOFT_WPS);
247*73b01e57SJeff Chen 		nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
248*73b01e57SJeff Chen 				     &beacon_ie, MGMT_MASK_BEACON,
249*73b01e57SJeff Chen 				     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
250*73b01e57SJeff Chen 	}
251*73b01e57SJeff Chen 
252*73b01e57SJeff Chen 	if (data->proberesp_ies && data->proberesp_ies_len) {
253*73b01e57SJeff Chen 		nxpwifi_update_vs_ie(data->proberesp_ies,
254*73b01e57SJeff Chen 				     data->proberesp_ies_len, &pr_ie,
255*73b01e57SJeff Chen 				     MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
256*73b01e57SJeff Chen 				     WLAN_OUI_TYPE_MICROSOFT_WPS);
257*73b01e57SJeff Chen 		nxpwifi_update_vs_ie(data->proberesp_ies,
258*73b01e57SJeff Chen 				     data->proberesp_ies_len, &pr_ie,
259*73b01e57SJeff Chen 				     MGMT_MASK_PROBE_RESP,
260*73b01e57SJeff Chen 				     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
261*73b01e57SJeff Chen 	}
262*73b01e57SJeff Chen 
263*73b01e57SJeff Chen 	if (data->assocresp_ies && data->assocresp_ies_len) {
264*73b01e57SJeff Chen 		nxpwifi_update_vs_ie(data->assocresp_ies,
265*73b01e57SJeff Chen 				     data->assocresp_ies_len, &ar_ie,
266*73b01e57SJeff Chen 				     MGMT_MASK_ASSOC_RESP |
267*73b01e57SJeff Chen 				     MGMT_MASK_REASSOC_RESP,
268*73b01e57SJeff Chen 				     WLAN_OUI_MICROSOFT,
269*73b01e57SJeff Chen 				     WLAN_OUI_TYPE_MICROSOFT_WPS);
270*73b01e57SJeff Chen 		nxpwifi_update_vs_ie(data->assocresp_ies,
271*73b01e57SJeff Chen 				     data->assocresp_ies_len, &ar_ie,
272*73b01e57SJeff Chen 				     MGMT_MASK_ASSOC_RESP |
273*73b01e57SJeff Chen 				     MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
274*73b01e57SJeff Chen 				     WLAN_OUI_TYPE_WFA_P2P);
275*73b01e57SJeff Chen 	}
276*73b01e57SJeff Chen 
277*73b01e57SJeff Chen 	if (beacon_ie || pr_ie || ar_ie) {
278*73b01e57SJeff Chen 		ret = nxpwifi_update_uap_custom_ie(priv, beacon_ie,
279*73b01e57SJeff Chen 						   &beacon_idx, pr_ie,
280*73b01e57SJeff Chen 						   &pr_idx, ar_ie, &ar_idx);
281*73b01e57SJeff Chen 		if (ret)
282*73b01e57SJeff Chen 			goto done;
283*73b01e57SJeff Chen 	}
284*73b01e57SJeff Chen 
285*73b01e57SJeff Chen 	priv->beacon_idx = beacon_idx;
286*73b01e57SJeff Chen 	priv->proberesp_idx = pr_idx;
287*73b01e57SJeff Chen 	priv->assocresp_idx = ar_idx;
288*73b01e57SJeff Chen 
289*73b01e57SJeff Chen done:
290*73b01e57SJeff Chen 	kfree(beacon_ie);
291*73b01e57SJeff Chen 	kfree(pr_ie);
292*73b01e57SJeff Chen 	kfree(ar_ie);
293*73b01e57SJeff Chen 
294*73b01e57SJeff Chen 	return ret;
295*73b01e57SJeff Chen }
296*73b01e57SJeff Chen 
297*73b01e57SJeff Chen /* Parse head/tail IEs from cfg80211_beacon_data and send them to FW. */
nxpwifi_uap_parse_tail_ies(struct nxpwifi_private * priv,struct cfg80211_beacon_data * info)298*73b01e57SJeff Chen static int nxpwifi_uap_parse_tail_ies(struct nxpwifi_private *priv,
299*73b01e57SJeff Chen 				      struct cfg80211_beacon_data *info)
300*73b01e57SJeff Chen {
301*73b01e57SJeff Chen 	struct nxpwifi_ie *gen_ie;
302*73b01e57SJeff Chen 	struct element *hdr;
303*73b01e57SJeff Chen 	struct ieee80211_vendor_ie *vendorhdr;
304*73b01e57SJeff Chen 	u16 gen_idx = NXPWIFI_AUTO_IDX_MASK, ie_len = 0;
305*73b01e57SJeff Chen 	int left_len, parsed_len = 0;
306*73b01e57SJeff Chen 	unsigned int token_len;
307*73b01e57SJeff Chen 	int ret = 0;
308*73b01e57SJeff Chen 
309*73b01e57SJeff Chen 	if (!info->tail || !info->tail_len)
310*73b01e57SJeff Chen 		return 0;
311*73b01e57SJeff Chen 
312*73b01e57SJeff Chen 	gen_ie = kzalloc_obj(*gen_ie, GFP_KERNEL);
313*73b01e57SJeff Chen 	if (!gen_ie)
314*73b01e57SJeff Chen 		return -ENOMEM;
315*73b01e57SJeff Chen 
316*73b01e57SJeff Chen 	left_len = info->tail_len;
317*73b01e57SJeff Chen 
318*73b01e57SJeff Chen 	/* Skip IEs generated by FW from bss configuration to avoid duplicates. */
319*73b01e57SJeff Chen 	while (left_len > sizeof(struct element)) {
320*73b01e57SJeff Chen 		hdr = (void *)(info->tail + parsed_len);
321*73b01e57SJeff Chen 		token_len = hdr->datalen + sizeof(struct element);
322*73b01e57SJeff Chen 		if (token_len > left_len) {
323*73b01e57SJeff Chen 			ret = -EINVAL;
324*73b01e57SJeff Chen 			goto done;
325*73b01e57SJeff Chen 		}
326*73b01e57SJeff Chen 
327*73b01e57SJeff Chen 		switch (hdr->id) {
328*73b01e57SJeff Chen 		case WLAN_EID_SSID:
329*73b01e57SJeff Chen 		case WLAN_EID_SUPP_RATES:
330*73b01e57SJeff Chen 		case WLAN_EID_COUNTRY:
331*73b01e57SJeff Chen 		case WLAN_EID_PWR_CONSTRAINT:
332*73b01e57SJeff Chen 		case WLAN_EID_ERP_INFO:
333*73b01e57SJeff Chen 		case WLAN_EID_EXT_SUPP_RATES:
334*73b01e57SJeff Chen 		case WLAN_EID_HT_CAPABILITY:
335*73b01e57SJeff Chen 		case WLAN_EID_HT_OPERATION:
336*73b01e57SJeff Chen 		case WLAN_EID_VHT_CAPABILITY:
337*73b01e57SJeff Chen 			break;
338*73b01e57SJeff Chen 		case WLAN_EID_VENDOR_SPECIFIC:
339*73b01e57SJeff Chen 			/* Skip only Microsoft WMM element */
340*73b01e57SJeff Chen 			if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
341*73b01e57SJeff Chen 						    WLAN_OUI_TYPE_MICROSOFT_WMM,
342*73b01e57SJeff Chen 						    (const u8 *)hdr,
343*73b01e57SJeff Chen 						    token_len))
344*73b01e57SJeff Chen 				break;
345*73b01e57SJeff Chen 			fallthrough;
346*73b01e57SJeff Chen 		default:
347*73b01e57SJeff Chen 			if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
348*73b01e57SJeff Chen 				ret = -EINVAL;
349*73b01e57SJeff Chen 				goto done;
350*73b01e57SJeff Chen 			}
351*73b01e57SJeff Chen 			memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len);
352*73b01e57SJeff Chen 			ie_len += token_len;
353*73b01e57SJeff Chen 			break;
354*73b01e57SJeff Chen 		}
355*73b01e57SJeff Chen 		left_len -= token_len;
356*73b01e57SJeff Chen 		parsed_len += token_len;
357*73b01e57SJeff Chen 	}
358*73b01e57SJeff Chen 
359*73b01e57SJeff Chen 	/*
360*73b01e57SJeff Chen 	 * parse only WPA vendor element from tail, WMM element is configured by
361*73b01e57SJeff Chen 	 * bss_config command
362*73b01e57SJeff Chen 	 */
363*73b01e57SJeff Chen 	vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
364*73b01e57SJeff Chen 						    WLAN_OUI_TYPE_MICROSOFT_WPA,
365*73b01e57SJeff Chen 						    info->tail, info->tail_len);
366*73b01e57SJeff Chen 	if (vendorhdr) {
367*73b01e57SJeff Chen 		token_len = vendorhdr->len + sizeof(struct element);
368*73b01e57SJeff Chen 		if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
369*73b01e57SJeff Chen 			ret = -EINVAL;
370*73b01e57SJeff Chen 			goto done;
371*73b01e57SJeff Chen 		}
372*73b01e57SJeff Chen 		memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
373*73b01e57SJeff Chen 		ie_len += token_len;
374*73b01e57SJeff Chen 	}
375*73b01e57SJeff Chen 
376*73b01e57SJeff Chen 	if (!ie_len)
377*73b01e57SJeff Chen 		goto done;
378*73b01e57SJeff Chen 
379*73b01e57SJeff Chen 	gen_ie->ie_index = cpu_to_le16(gen_idx);
380*73b01e57SJeff Chen 	gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
381*73b01e57SJeff Chen 						MGMT_MASK_PROBE_RESP |
382*73b01e57SJeff Chen 						MGMT_MASK_ASSOC_RESP);
383*73b01e57SJeff Chen 	gen_ie->ie_length = cpu_to_le16(ie_len);
384*73b01e57SJeff Chen 
385*73b01e57SJeff Chen 	ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL,
386*73b01e57SJeff Chen 					   NULL, NULL, NULL);
387*73b01e57SJeff Chen 
388*73b01e57SJeff Chen 	if (ret)
389*73b01e57SJeff Chen 		goto done;
390*73b01e57SJeff Chen 
391*73b01e57SJeff Chen 	priv->gen_idx = gen_idx;
392*73b01e57SJeff Chen 
393*73b01e57SJeff Chen  done:
394*73b01e57SJeff Chen 	kfree(gen_ie);
395*73b01e57SJeff Chen 	return ret;
396*73b01e57SJeff Chen }
397*73b01e57SJeff Chen 
398*73b01e57SJeff Chen /* Parse head/tail/beacon/probe/assoc IEs and program the FW. */
nxpwifi_set_mgmt_ies(struct nxpwifi_private * priv,struct cfg80211_beacon_data * info)399*73b01e57SJeff Chen int nxpwifi_set_mgmt_ies(struct nxpwifi_private *priv,
400*73b01e57SJeff Chen 			 struct cfg80211_beacon_data *info)
401*73b01e57SJeff Chen {
402*73b01e57SJeff Chen 	int ret;
403*73b01e57SJeff Chen 
404*73b01e57SJeff Chen 	ret = nxpwifi_uap_parse_tail_ies(priv, info);
405*73b01e57SJeff Chen 
406*73b01e57SJeff Chen 	if (ret)
407*73b01e57SJeff Chen 		return ret;
408*73b01e57SJeff Chen 
409*73b01e57SJeff Chen 	return nxpwifi_set_mgmt_beacon_data_ies(priv, info);
410*73b01e57SJeff Chen }
411*73b01e57SJeff Chen 
412*73b01e57SJeff Chen /* Remove previously set management IEs. */
nxpwifi_del_mgmt_ies(struct nxpwifi_private * priv)413*73b01e57SJeff Chen int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv)
414*73b01e57SJeff Chen {
415*73b01e57SJeff Chen 	struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL;
416*73b01e57SJeff Chen 	struct nxpwifi_ie *ar_ie = NULL, *gen_ie = NULL;
417*73b01e57SJeff Chen 	int ret = 0;
418*73b01e57SJeff Chen 
419*73b01e57SJeff Chen 	if (priv->gen_idx != NXPWIFI_AUTO_IDX_MASK) {
420*73b01e57SJeff Chen 		gen_ie = kmalloc_obj(*gen_ie, GFP_KERNEL);
421*73b01e57SJeff Chen 		if (!gen_ie)
422*73b01e57SJeff Chen 			return -ENOMEM;
423*73b01e57SJeff Chen 
424*73b01e57SJeff Chen 		gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
425*73b01e57SJeff Chen 		gen_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
426*73b01e57SJeff Chen 		gen_ie->ie_length = 0;
427*73b01e57SJeff Chen 		ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
428*73b01e57SJeff Chen 						   NULL, &priv->proberesp_idx,
429*73b01e57SJeff Chen 						   NULL, &priv->assocresp_idx);
430*73b01e57SJeff Chen 		if (ret)
431*73b01e57SJeff Chen 			goto done;
432*73b01e57SJeff Chen 
433*73b01e57SJeff Chen 		priv->gen_idx = NXPWIFI_AUTO_IDX_MASK;
434*73b01e57SJeff Chen 	}
435*73b01e57SJeff Chen 
436*73b01e57SJeff Chen 	if (priv->beacon_idx != NXPWIFI_AUTO_IDX_MASK) {
437*73b01e57SJeff Chen 		beacon_ie = kmalloc_obj(*beacon_ie, GFP_KERNEL);
438*73b01e57SJeff Chen 		if (!beacon_ie) {
439*73b01e57SJeff Chen 			ret = -ENOMEM;
440*73b01e57SJeff Chen 			goto done;
441*73b01e57SJeff Chen 		}
442*73b01e57SJeff Chen 		beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
443*73b01e57SJeff Chen 		beacon_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
444*73b01e57SJeff Chen 		beacon_ie->ie_length = 0;
445*73b01e57SJeff Chen 	}
446*73b01e57SJeff Chen 	if (priv->proberesp_idx != NXPWIFI_AUTO_IDX_MASK) {
447*73b01e57SJeff Chen 		pr_ie = kmalloc_obj(*pr_ie, GFP_KERNEL);
448*73b01e57SJeff Chen 		if (!pr_ie) {
449*73b01e57SJeff Chen 			ret = -ENOMEM;
450*73b01e57SJeff Chen 			goto done;
451*73b01e57SJeff Chen 		}
452*73b01e57SJeff Chen 		pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
453*73b01e57SJeff Chen 		pr_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
454*73b01e57SJeff Chen 		pr_ie->ie_length = 0;
455*73b01e57SJeff Chen 	}
456*73b01e57SJeff Chen 	if (priv->assocresp_idx != NXPWIFI_AUTO_IDX_MASK) {
457*73b01e57SJeff Chen 		ar_ie = kmalloc_obj(*ar_ie, GFP_KERNEL);
458*73b01e57SJeff Chen 		if (!ar_ie) {
459*73b01e57SJeff Chen 			ret = -ENOMEM;
460*73b01e57SJeff Chen 			goto done;
461*73b01e57SJeff Chen 		}
462*73b01e57SJeff Chen 		ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
463*73b01e57SJeff Chen 		ar_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
464*73b01e57SJeff Chen 		ar_ie->ie_length = 0;
465*73b01e57SJeff Chen 	}
466*73b01e57SJeff Chen 
467*73b01e57SJeff Chen 	if (beacon_ie || pr_ie || ar_ie)
468*73b01e57SJeff Chen 		ret = nxpwifi_update_uap_custom_ie(priv,
469*73b01e57SJeff Chen 						   beacon_ie, &priv->beacon_idx,
470*73b01e57SJeff Chen 						   pr_ie, &priv->proberesp_idx,
471*73b01e57SJeff Chen 						   ar_ie, &priv->assocresp_idx);
472*73b01e57SJeff Chen 
473*73b01e57SJeff Chen done:
474*73b01e57SJeff Chen 	kfree(gen_ie);
475*73b01e57SJeff Chen 	kfree(beacon_ie);
476*73b01e57SJeff Chen 	kfree(pr_ie);
477*73b01e57SJeff Chen 	kfree(ar_ie);
478*73b01e57SJeff Chen 
479*73b01e57SJeff Chen 	return ret;
480*73b01e57SJeff Chen }
481