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