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