xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/marvell/mwifiex/ie.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * NXP Wireless LAN device driver: management IE handling- setting and
3*4882a593Smuzhiyun  * deleting IE.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright 2011-2020 NXP
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * This software file (the "File") is distributed by NXP
8*4882a593Smuzhiyun  * under the terms of the GNU General Public License Version 2, June 1991
9*4882a593Smuzhiyun  * (the "License").  You may use, redistribute and/or modify this File in
10*4882a593Smuzhiyun  * accordance with the terms and conditions of the License, a copy of which
11*4882a593Smuzhiyun  * is available by writing to the Free Software Foundation, Inc.,
12*4882a593Smuzhiyun  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
13*4882a593Smuzhiyun  * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
16*4882a593Smuzhiyun  * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
17*4882a593Smuzhiyun  * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
18*4882a593Smuzhiyun  * this warranty disclaimer.
19*4882a593Smuzhiyun  */
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "main.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /* This function checks if current IE index is used by any on other interface.
24*4882a593Smuzhiyun  * Return: -1: yes, current IE index is used by someone else.
25*4882a593Smuzhiyun  *          0: no, current IE index is NOT used by other interface.
26*4882a593Smuzhiyun  */
27*4882a593Smuzhiyun static int
mwifiex_ie_index_used_by_other_intf(struct mwifiex_private * priv,u16 idx)28*4882a593Smuzhiyun mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun 	int i;
31*4882a593Smuzhiyun 	struct mwifiex_adapter *adapter = priv->adapter;
32*4882a593Smuzhiyun 	struct mwifiex_ie *ie;
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun 	for (i = 0; i < adapter->priv_num; i++) {
35*4882a593Smuzhiyun 		if (adapter->priv[i] != priv) {
36*4882a593Smuzhiyun 			ie = &adapter->priv[i]->mgmt_ie[idx];
37*4882a593Smuzhiyun 			if (ie->mgmt_subtype_mask && ie->ie_length)
38*4882a593Smuzhiyun 				return -1;
39*4882a593Smuzhiyun 		}
40*4882a593Smuzhiyun 	}
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	return 0;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun /* Get unused IE index. This index will be used for setting new IE */
46*4882a593Smuzhiyun static int
mwifiex_ie_get_autoidx(struct mwifiex_private * priv,u16 subtype_mask,struct mwifiex_ie * ie,u16 * index)47*4882a593Smuzhiyun mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask,
48*4882a593Smuzhiyun 		       struct mwifiex_ie *ie, u16 *index)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	u16 mask, len, i;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
53*4882a593Smuzhiyun 		mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
54*4882a593Smuzhiyun 		len = le16_to_cpu(ie->ie_length);
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 		if (mask == MWIFIEX_AUTO_IDX_MASK)
57*4882a593Smuzhiyun 			continue;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 		if (mask == subtype_mask) {
60*4882a593Smuzhiyun 			if (len > IEEE_MAX_IE_SIZE)
61*4882a593Smuzhiyun 				continue;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 			*index = i;
64*4882a593Smuzhiyun 			return 0;
65*4882a593Smuzhiyun 		}
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 		if (!priv->mgmt_ie[i].ie_length) {
68*4882a593Smuzhiyun 			if (mwifiex_ie_index_used_by_other_intf(priv, i))
69*4882a593Smuzhiyun 				continue;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 			*index = i;
72*4882a593Smuzhiyun 			return 0;
73*4882a593Smuzhiyun 		}
74*4882a593Smuzhiyun 	}
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	return -1;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /* This function prepares IE data buffer for command to be sent to FW */
80*4882a593Smuzhiyun static int
mwifiex_update_autoindex_ies(struct mwifiex_private * priv,struct mwifiex_ie_list * ie_list)81*4882a593Smuzhiyun mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
82*4882a593Smuzhiyun 			     struct mwifiex_ie_list *ie_list)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun 	u16 travel_len, index, mask;
85*4882a593Smuzhiyun 	s16 input_len, tlv_len;
86*4882a593Smuzhiyun 	struct mwifiex_ie *ie;
87*4882a593Smuzhiyun 	u8 *tmp;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	input_len = le16_to_cpu(ie_list->len);
90*4882a593Smuzhiyun 	travel_len = sizeof(struct mwifiex_ie_types_header);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	ie_list->len = 0;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
95*4882a593Smuzhiyun 		ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
96*4882a593Smuzhiyun 		tlv_len = le16_to_cpu(ie->ie_length);
97*4882a593Smuzhiyun 		travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 		if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
100*4882a593Smuzhiyun 			return -1;
101*4882a593Smuzhiyun 		index = le16_to_cpu(ie->ie_index);
102*4882a593Smuzhiyun 		mask = le16_to_cpu(ie->mgmt_subtype_mask);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 		if (index == MWIFIEX_AUTO_IDX_MASK) {
105*4882a593Smuzhiyun 			/* automatic addition */
106*4882a593Smuzhiyun 			if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
107*4882a593Smuzhiyun 				return -1;
108*4882a593Smuzhiyun 			if (index == MWIFIEX_AUTO_IDX_MASK)
109*4882a593Smuzhiyun 				return -1;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 			tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
112*4882a593Smuzhiyun 			memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
113*4882a593Smuzhiyun 			priv->mgmt_ie[index].ie_length = ie->ie_length;
114*4882a593Smuzhiyun 			priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
115*4882a593Smuzhiyun 			priv->mgmt_ie[index].mgmt_subtype_mask =
116*4882a593Smuzhiyun 							cpu_to_le16(mask);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 			ie->ie_index = cpu_to_le16(index);
119*4882a593Smuzhiyun 		} else {
120*4882a593Smuzhiyun 			if (mask != MWIFIEX_DELETE_MASK)
121*4882a593Smuzhiyun 				return -1;
122*4882a593Smuzhiyun 			/*
123*4882a593Smuzhiyun 			 * Check if this index is being used on any
124*4882a593Smuzhiyun 			 * other interface.
125*4882a593Smuzhiyun 			 */
126*4882a593Smuzhiyun 			if (mwifiex_ie_index_used_by_other_intf(priv, index))
127*4882a593Smuzhiyun 				return -1;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 			ie->ie_length = 0;
130*4882a593Smuzhiyun 			memcpy(&priv->mgmt_ie[index], ie,
131*4882a593Smuzhiyun 			       sizeof(struct mwifiex_ie));
132*4882a593Smuzhiyun 		}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 		le16_unaligned_add_cpu(&ie_list->len,
135*4882a593Smuzhiyun 				       le16_to_cpu(
136*4882a593Smuzhiyun 					    priv->mgmt_ie[index].ie_length) +
137*4882a593Smuzhiyun 				       MWIFIEX_IE_HDR_SIZE);
138*4882a593Smuzhiyun 		input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
139*4882a593Smuzhiyun 	}
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
142*4882a593Smuzhiyun 		return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
143*4882a593Smuzhiyun 					HostCmd_ACT_GEN_SET,
144*4882a593Smuzhiyun 					UAP_CUSTOM_IE_I, ie_list, true);
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	return 0;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun /* Copy individual custom IEs for beacon, probe response and assoc response
150*4882a593Smuzhiyun  * and prepare single structure for IE setting.
151*4882a593Smuzhiyun  * This function also updates allocated IE indices from driver.
152*4882a593Smuzhiyun  */
153*4882a593Smuzhiyun static int
mwifiex_update_uap_custom_ie(struct mwifiex_private * priv,struct mwifiex_ie * beacon_ie,u16 * beacon_idx,struct mwifiex_ie * pr_ie,u16 * probe_idx,struct mwifiex_ie * ar_ie,u16 * assoc_idx)154*4882a593Smuzhiyun mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
155*4882a593Smuzhiyun 			     struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
156*4882a593Smuzhiyun 			     struct mwifiex_ie *pr_ie, u16 *probe_idx,
157*4882a593Smuzhiyun 			     struct mwifiex_ie *ar_ie, u16 *assoc_idx)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun 	struct mwifiex_ie_list *ap_custom_ie;
160*4882a593Smuzhiyun 	u8 *pos;
161*4882a593Smuzhiyun 	u16 len;
162*4882a593Smuzhiyun 	int ret;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
165*4882a593Smuzhiyun 	if (!ap_custom_ie)
166*4882a593Smuzhiyun 		return -ENOMEM;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
169*4882a593Smuzhiyun 	pos = (u8 *)ap_custom_ie->ie_list;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	if (beacon_ie) {
172*4882a593Smuzhiyun 		len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
173*4882a593Smuzhiyun 		      le16_to_cpu(beacon_ie->ie_length);
174*4882a593Smuzhiyun 		memcpy(pos, beacon_ie, len);
175*4882a593Smuzhiyun 		pos += len;
176*4882a593Smuzhiyun 		le16_unaligned_add_cpu(&ap_custom_ie->len, len);
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun 	if (pr_ie) {
179*4882a593Smuzhiyun 		len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
180*4882a593Smuzhiyun 		      le16_to_cpu(pr_ie->ie_length);
181*4882a593Smuzhiyun 		memcpy(pos, pr_ie, len);
182*4882a593Smuzhiyun 		pos += len;
183*4882a593Smuzhiyun 		le16_unaligned_add_cpu(&ap_custom_ie->len, len);
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 	if (ar_ie) {
186*4882a593Smuzhiyun 		len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
187*4882a593Smuzhiyun 		      le16_to_cpu(ar_ie->ie_length);
188*4882a593Smuzhiyun 		memcpy(pos, ar_ie, len);
189*4882a593Smuzhiyun 		pos += len;
190*4882a593Smuzhiyun 		le16_unaligned_add_cpu(&ap_custom_ie->len, len);
191*4882a593Smuzhiyun 	}
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
196*4882a593Smuzhiyun 	if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
197*4882a593Smuzhiyun 		/* save beacon ie index after auto-indexing */
198*4882a593Smuzhiyun 		*beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
199*4882a593Smuzhiyun 		len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
200*4882a593Smuzhiyun 		      le16_to_cpu(beacon_ie->ie_length);
201*4882a593Smuzhiyun 		pos += len;
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 	if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
204*4882a593Smuzhiyun 		/* save probe resp ie index after auto-indexing */
205*4882a593Smuzhiyun 		*probe_idx = *((u16 *)pos);
206*4882a593Smuzhiyun 		len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
207*4882a593Smuzhiyun 		      le16_to_cpu(pr_ie->ie_length);
208*4882a593Smuzhiyun 		pos += len;
209*4882a593Smuzhiyun 	}
210*4882a593Smuzhiyun 	if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
211*4882a593Smuzhiyun 		/* save assoc resp ie index after auto-indexing */
212*4882a593Smuzhiyun 		*assoc_idx = *((u16 *)pos);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	kfree(ap_custom_ie);
215*4882a593Smuzhiyun 	return ret;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun /* This function checks if the vendor specified IE is present in passed buffer
219*4882a593Smuzhiyun  * and copies it to mwifiex_ie structure.
220*4882a593Smuzhiyun  * Function takes pointer to struct mwifiex_ie pointer as argument.
221*4882a593Smuzhiyun  * If the vendor specified IE is present then memory is allocated for
222*4882a593Smuzhiyun  * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
223*4882a593Smuzhiyun  * this memory.
224*4882a593Smuzhiyun  */
mwifiex_update_vs_ie(const u8 * ies,int ies_len,struct mwifiex_ie ** ie_ptr,u16 mask,unsigned int oui,u8 oui_type)225*4882a593Smuzhiyun static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
226*4882a593Smuzhiyun 				struct mwifiex_ie **ie_ptr, u16 mask,
227*4882a593Smuzhiyun 				unsigned int oui, u8 oui_type)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	struct ieee_types_header *vs_ie;
230*4882a593Smuzhiyun 	struct mwifiex_ie *ie = *ie_ptr;
231*4882a593Smuzhiyun 	const u8 *vendor_ie;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
234*4882a593Smuzhiyun 	if (vendor_ie) {
235*4882a593Smuzhiyun 		if (!*ie_ptr) {
236*4882a593Smuzhiyun 			*ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
237*4882a593Smuzhiyun 					  GFP_KERNEL);
238*4882a593Smuzhiyun 			if (!*ie_ptr)
239*4882a593Smuzhiyun 				return -ENOMEM;
240*4882a593Smuzhiyun 			ie = *ie_ptr;
241*4882a593Smuzhiyun 		}
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 		vs_ie = (struct ieee_types_header *)vendor_ie;
244*4882a593Smuzhiyun 		if (le16_to_cpu(ie->ie_length) + vs_ie->len + 2 >
245*4882a593Smuzhiyun 			IEEE_MAX_IE_SIZE)
246*4882a593Smuzhiyun 			return -EINVAL;
247*4882a593Smuzhiyun 		memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
248*4882a593Smuzhiyun 		       vs_ie, vs_ie->len + 2);
249*4882a593Smuzhiyun 		le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 2);
250*4882a593Smuzhiyun 		ie->mgmt_subtype_mask = cpu_to_le16(mask);
251*4882a593Smuzhiyun 		ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
252*4882a593Smuzhiyun 	}
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	*ie_ptr = ie;
255*4882a593Smuzhiyun 	return 0;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun /* This function parses beacon IEs, probe response IEs, association response IEs
259*4882a593Smuzhiyun  * from cfg80211_ap_settings->beacon and sets these IE to FW.
260*4882a593Smuzhiyun  */
mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private * priv,struct cfg80211_beacon_data * data)261*4882a593Smuzhiyun static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
262*4882a593Smuzhiyun 					    struct cfg80211_beacon_data *data)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
265*4882a593Smuzhiyun 	u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
266*4882a593Smuzhiyun 	u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
267*4882a593Smuzhiyun 	int ret = 0;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	if (data->beacon_ies && data->beacon_ies_len) {
270*4882a593Smuzhiyun 		mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
271*4882a593Smuzhiyun 				     &beacon_ie, MGMT_MASK_BEACON,
272*4882a593Smuzhiyun 				     WLAN_OUI_MICROSOFT,
273*4882a593Smuzhiyun 				     WLAN_OUI_TYPE_MICROSOFT_WPS);
274*4882a593Smuzhiyun 		mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
275*4882a593Smuzhiyun 				     &beacon_ie, MGMT_MASK_BEACON,
276*4882a593Smuzhiyun 				     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
277*4882a593Smuzhiyun 	}
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	if (data->proberesp_ies && data->proberesp_ies_len) {
280*4882a593Smuzhiyun 		mwifiex_update_vs_ie(data->proberesp_ies,
281*4882a593Smuzhiyun 				     data->proberesp_ies_len, &pr_ie,
282*4882a593Smuzhiyun 				     MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
283*4882a593Smuzhiyun 				     WLAN_OUI_TYPE_MICROSOFT_WPS);
284*4882a593Smuzhiyun 		mwifiex_update_vs_ie(data->proberesp_ies,
285*4882a593Smuzhiyun 				     data->proberesp_ies_len, &pr_ie,
286*4882a593Smuzhiyun 				     MGMT_MASK_PROBE_RESP,
287*4882a593Smuzhiyun 				     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	if (data->assocresp_ies && data->assocresp_ies_len) {
291*4882a593Smuzhiyun 		mwifiex_update_vs_ie(data->assocresp_ies,
292*4882a593Smuzhiyun 				     data->assocresp_ies_len, &ar_ie,
293*4882a593Smuzhiyun 				     MGMT_MASK_ASSOC_RESP |
294*4882a593Smuzhiyun 				     MGMT_MASK_REASSOC_RESP,
295*4882a593Smuzhiyun 				     WLAN_OUI_MICROSOFT,
296*4882a593Smuzhiyun 				     WLAN_OUI_TYPE_MICROSOFT_WPS);
297*4882a593Smuzhiyun 		mwifiex_update_vs_ie(data->assocresp_ies,
298*4882a593Smuzhiyun 				     data->assocresp_ies_len, &ar_ie,
299*4882a593Smuzhiyun 				     MGMT_MASK_ASSOC_RESP |
300*4882a593Smuzhiyun 				     MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
301*4882a593Smuzhiyun 				     WLAN_OUI_TYPE_WFA_P2P);
302*4882a593Smuzhiyun 	}
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	if (beacon_ie || pr_ie || ar_ie) {
305*4882a593Smuzhiyun 		ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
306*4882a593Smuzhiyun 						   &beacon_idx, pr_ie,
307*4882a593Smuzhiyun 						   &pr_idx, ar_ie, &ar_idx);
308*4882a593Smuzhiyun 		if (ret)
309*4882a593Smuzhiyun 			goto done;
310*4882a593Smuzhiyun 	}
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	priv->beacon_idx = beacon_idx;
313*4882a593Smuzhiyun 	priv->proberesp_idx = pr_idx;
314*4882a593Smuzhiyun 	priv->assocresp_idx = ar_idx;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun done:
317*4882a593Smuzhiyun 	kfree(beacon_ie);
318*4882a593Smuzhiyun 	kfree(pr_ie);
319*4882a593Smuzhiyun 	kfree(ar_ie);
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	return ret;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun /* This function parses  head and tail IEs, from cfg80211_beacon_data and sets
325*4882a593Smuzhiyun  * these IE to FW.
326*4882a593Smuzhiyun  */
mwifiex_uap_parse_tail_ies(struct mwifiex_private * priv,struct cfg80211_beacon_data * info)327*4882a593Smuzhiyun static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
328*4882a593Smuzhiyun 				      struct cfg80211_beacon_data *info)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun 	struct mwifiex_ie *gen_ie;
331*4882a593Smuzhiyun 	struct ieee_types_header *hdr;
332*4882a593Smuzhiyun 	struct ieee80211_vendor_ie *vendorhdr;
333*4882a593Smuzhiyun 	u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
334*4882a593Smuzhiyun 	int left_len, parsed_len = 0;
335*4882a593Smuzhiyun 	unsigned int token_len;
336*4882a593Smuzhiyun 	int err = 0;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	if (!info->tail || !info->tail_len)
339*4882a593Smuzhiyun 		return 0;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
342*4882a593Smuzhiyun 	if (!gen_ie)
343*4882a593Smuzhiyun 		return -ENOMEM;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	left_len = info->tail_len;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	/* Many IEs are generated in FW by parsing bss configuration.
348*4882a593Smuzhiyun 	 * Let's not add them here; else we may end up duplicating these IEs
349*4882a593Smuzhiyun 	 */
350*4882a593Smuzhiyun 	while (left_len > sizeof(struct ieee_types_header)) {
351*4882a593Smuzhiyun 		hdr = (void *)(info->tail + parsed_len);
352*4882a593Smuzhiyun 		token_len = hdr->len + sizeof(struct ieee_types_header);
353*4882a593Smuzhiyun 		if (token_len > left_len) {
354*4882a593Smuzhiyun 			err = -EINVAL;
355*4882a593Smuzhiyun 			goto out;
356*4882a593Smuzhiyun 		}
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 		switch (hdr->element_id) {
359*4882a593Smuzhiyun 		case WLAN_EID_SSID:
360*4882a593Smuzhiyun 		case WLAN_EID_SUPP_RATES:
361*4882a593Smuzhiyun 		case WLAN_EID_COUNTRY:
362*4882a593Smuzhiyun 		case WLAN_EID_PWR_CONSTRAINT:
363*4882a593Smuzhiyun 		case WLAN_EID_ERP_INFO:
364*4882a593Smuzhiyun 		case WLAN_EID_EXT_SUPP_RATES:
365*4882a593Smuzhiyun 		case WLAN_EID_HT_CAPABILITY:
366*4882a593Smuzhiyun 		case WLAN_EID_HT_OPERATION:
367*4882a593Smuzhiyun 		case WLAN_EID_VHT_CAPABILITY:
368*4882a593Smuzhiyun 		case WLAN_EID_VHT_OPERATION:
369*4882a593Smuzhiyun 			break;
370*4882a593Smuzhiyun 		case WLAN_EID_VENDOR_SPECIFIC:
371*4882a593Smuzhiyun 			/* Skip only Microsoft WMM IE */
372*4882a593Smuzhiyun 			if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
373*4882a593Smuzhiyun 						    WLAN_OUI_TYPE_MICROSOFT_WMM,
374*4882a593Smuzhiyun 						    (const u8 *)hdr,
375*4882a593Smuzhiyun 						    token_len))
376*4882a593Smuzhiyun 				break;
377*4882a593Smuzhiyun 			fallthrough;
378*4882a593Smuzhiyun 		default:
379*4882a593Smuzhiyun 			if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
380*4882a593Smuzhiyun 				err = -EINVAL;
381*4882a593Smuzhiyun 				goto out;
382*4882a593Smuzhiyun 			}
383*4882a593Smuzhiyun 			memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len);
384*4882a593Smuzhiyun 			ie_len += token_len;
385*4882a593Smuzhiyun 			break;
386*4882a593Smuzhiyun 		}
387*4882a593Smuzhiyun 		left_len -= token_len;
388*4882a593Smuzhiyun 		parsed_len += token_len;
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	/* parse only WPA vendor IE from tail, WMM IE is configured by
392*4882a593Smuzhiyun 	 * bss_config command
393*4882a593Smuzhiyun 	 */
394*4882a593Smuzhiyun 	vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
395*4882a593Smuzhiyun 						    WLAN_OUI_TYPE_MICROSOFT_WPA,
396*4882a593Smuzhiyun 						    info->tail, info->tail_len);
397*4882a593Smuzhiyun 	if (vendorhdr) {
398*4882a593Smuzhiyun 		token_len = vendorhdr->len + sizeof(struct ieee_types_header);
399*4882a593Smuzhiyun 		if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
400*4882a593Smuzhiyun 			err = -EINVAL;
401*4882a593Smuzhiyun 			goto out;
402*4882a593Smuzhiyun 		}
403*4882a593Smuzhiyun 		memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
404*4882a593Smuzhiyun 		ie_len += token_len;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	if (!ie_len)
408*4882a593Smuzhiyun 		goto out;
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	gen_ie->ie_index = cpu_to_le16(gen_idx);
411*4882a593Smuzhiyun 	gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
412*4882a593Smuzhiyun 						MGMT_MASK_PROBE_RESP |
413*4882a593Smuzhiyun 						MGMT_MASK_ASSOC_RESP);
414*4882a593Smuzhiyun 	gen_ie->ie_length = cpu_to_le16(ie_len);
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
417*4882a593Smuzhiyun 					 NULL, NULL)) {
418*4882a593Smuzhiyun 		err = -EINVAL;
419*4882a593Smuzhiyun 		goto out;
420*4882a593Smuzhiyun 	}
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	priv->gen_idx = gen_idx;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun  out:
425*4882a593Smuzhiyun 	kfree(gen_ie);
426*4882a593Smuzhiyun 	return err;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun /* This function parses different IEs-head & tail IEs, beacon IEs,
430*4882a593Smuzhiyun  * probe response IEs, association response IEs from cfg80211_ap_settings
431*4882a593Smuzhiyun  * function and sets these IE to FW.
432*4882a593Smuzhiyun  */
mwifiex_set_mgmt_ies(struct mwifiex_private * priv,struct cfg80211_beacon_data * info)433*4882a593Smuzhiyun int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
434*4882a593Smuzhiyun 			 struct cfg80211_beacon_data *info)
435*4882a593Smuzhiyun {
436*4882a593Smuzhiyun 	int ret;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	ret = mwifiex_uap_parse_tail_ies(priv, info);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	if (ret)
441*4882a593Smuzhiyun 		return ret;
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	return mwifiex_set_mgmt_beacon_data_ies(priv, info);
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun /* This function removes management IE set */
mwifiex_del_mgmt_ies(struct mwifiex_private * priv)447*4882a593Smuzhiyun int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
448*4882a593Smuzhiyun {
449*4882a593Smuzhiyun 	struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
450*4882a593Smuzhiyun 	struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL;
451*4882a593Smuzhiyun 	int ret = 0;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) {
454*4882a593Smuzhiyun 		gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
455*4882a593Smuzhiyun 		if (!gen_ie)
456*4882a593Smuzhiyun 			return -ENOMEM;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 		gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
459*4882a593Smuzhiyun 		gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
460*4882a593Smuzhiyun 		gen_ie->ie_length = 0;
461*4882a593Smuzhiyun 		if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
462*4882a593Smuzhiyun 						 NULL, &priv->proberesp_idx,
463*4882a593Smuzhiyun 						 NULL, &priv->assocresp_idx)) {
464*4882a593Smuzhiyun 			ret = -1;
465*4882a593Smuzhiyun 			goto done;
466*4882a593Smuzhiyun 		}
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 		priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
469*4882a593Smuzhiyun 	}
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
472*4882a593Smuzhiyun 		beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
473*4882a593Smuzhiyun 		if (!beacon_ie) {
474*4882a593Smuzhiyun 			ret = -ENOMEM;
475*4882a593Smuzhiyun 			goto done;
476*4882a593Smuzhiyun 		}
477*4882a593Smuzhiyun 		beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
478*4882a593Smuzhiyun 		beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
479*4882a593Smuzhiyun 		beacon_ie->ie_length = 0;
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun 	if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
482*4882a593Smuzhiyun 		pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
483*4882a593Smuzhiyun 		if (!pr_ie) {
484*4882a593Smuzhiyun 			ret = -ENOMEM;
485*4882a593Smuzhiyun 			goto done;
486*4882a593Smuzhiyun 		}
487*4882a593Smuzhiyun 		pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
488*4882a593Smuzhiyun 		pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
489*4882a593Smuzhiyun 		pr_ie->ie_length = 0;
490*4882a593Smuzhiyun 	}
491*4882a593Smuzhiyun 	if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
492*4882a593Smuzhiyun 		ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
493*4882a593Smuzhiyun 		if (!ar_ie) {
494*4882a593Smuzhiyun 			ret = -ENOMEM;
495*4882a593Smuzhiyun 			goto done;
496*4882a593Smuzhiyun 		}
497*4882a593Smuzhiyun 		ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
498*4882a593Smuzhiyun 		ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
499*4882a593Smuzhiyun 		ar_ie->ie_length = 0;
500*4882a593Smuzhiyun 	}
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	if (beacon_ie || pr_ie || ar_ie)
503*4882a593Smuzhiyun 		ret = mwifiex_update_uap_custom_ie(priv,
504*4882a593Smuzhiyun 						   beacon_ie, &priv->beacon_idx,
505*4882a593Smuzhiyun 						   pr_ie, &priv->proberesp_idx,
506*4882a593Smuzhiyun 						   ar_ie, &priv->assocresp_idx);
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun done:
509*4882a593Smuzhiyun 	kfree(gen_ie);
510*4882a593Smuzhiyun 	kfree(beacon_ie);
511*4882a593Smuzhiyun 	kfree(pr_ie);
512*4882a593Smuzhiyun 	kfree(ar_ie);
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	return ret;
515*4882a593Smuzhiyun }
516