Merge tag 'v3.5-rc7' into late/soc
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / mwifiex / ie.c
1 /*
2  * Marvell Wireless LAN device driver: management IE handling- setting and
3  * deleting IE.
4  *
5  * Copyright (C) 2012, Marvell International Ltd.
6  *
7  * This software file (the "File") is distributed by Marvell International
8  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
9  * (the "License").  You may use, redistribute and/or modify this File in
10  * accordance with the terms and conditions of the License, a copy of which
11  * is available by writing to the Free Software Foundation, Inc.,
12  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
13  * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
14  *
15  * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
17  * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
18  * this warranty disclaimer.
19  */
20
21 #include "main.h"
22
23 /* This function checks if current IE index is used by any on other interface.
24  * Return: -1: yes, current IE index is used by someone else.
25  *          0: no, current IE index is NOT used by other interface.
26  */
27 static int
28 mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx)
29 {
30         int i;
31         struct mwifiex_adapter *adapter = priv->adapter;
32         struct mwifiex_ie *ie;
33
34         for (i = 0; i < adapter->priv_num; i++) {
35                 if (adapter->priv[i] != priv) {
36                         ie = &adapter->priv[i]->mgmt_ie[idx];
37                         if (ie->mgmt_subtype_mask && ie->ie_length)
38                                 return -1;
39                 }
40         }
41
42         return 0;
43 }
44
45 /* Get unused IE index. This index will be used for setting new IE */
46 static int
47 mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask,
48                        struct mwifiex_ie *ie, u16 *index)
49 {
50         u16 mask, len, i;
51
52         for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
53                 mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
54                 len = le16_to_cpu(priv->mgmt_ie[i].ie_length) +
55                       le16_to_cpu(ie->ie_length);
56
57                 if (mask == MWIFIEX_AUTO_IDX_MASK)
58                         continue;
59
60                 if (mask == subtype_mask) {
61                         if (len > IEEE_MAX_IE_SIZE)
62                                 continue;
63
64                         *index = i;
65                         return 0;
66                 }
67
68                 if (!priv->mgmt_ie[i].ie_length) {
69                         if (mwifiex_ie_index_used_by_other_intf(priv, i))
70                                 continue;
71
72                         *index = i;
73                         return 0;
74                 }
75         }
76
77         return -1;
78 }
79
80 /* This function prepares IE data buffer for command to be sent to FW */
81 static int
82 mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
83                              struct mwifiex_ie_list *ie_list)
84 {
85         u16 travel_len, index, mask;
86         s16 input_len;
87         struct mwifiex_ie *ie;
88         u8 *tmp;
89
90         input_len = le16_to_cpu(ie_list->len);
91         travel_len = sizeof(struct host_cmd_tlv);
92
93         ie_list->len = 0;
94
95         while (input_len > 0) {
96                 ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
97                 input_len -= le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
98                 travel_len += le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
99
100                 index = le16_to_cpu(ie->ie_index);
101                 mask = le16_to_cpu(ie->mgmt_subtype_mask);
102
103                 if (index == MWIFIEX_AUTO_IDX_MASK) {
104                         /* automatic addition */
105                         if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
106                                 return -1;
107                         if (index == MWIFIEX_AUTO_IDX_MASK)
108                                 return -1;
109
110                         tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
111                         tmp += le16_to_cpu(priv->mgmt_ie[index].ie_length);
112                         memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
113                         le16_add_cpu(&priv->mgmt_ie[index].ie_length,
114                                      le16_to_cpu(ie->ie_length));
115                         priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
116                         priv->mgmt_ie[index].mgmt_subtype_mask =
117                                                         cpu_to_le16(mask);
118
119                         ie->ie_index = cpu_to_le16(index);
120                         ie->ie_length = priv->mgmt_ie[index].ie_length;
121                         memcpy(&ie->ie_buffer, &priv->mgmt_ie[index].ie_buffer,
122                                le16_to_cpu(priv->mgmt_ie[index].ie_length));
123                 } else {
124                         if (mask != MWIFIEX_DELETE_MASK)
125                                 return -1;
126                         /*
127                          * Check if this index is being used on any
128                          * other interface.
129                          */
130                         if (mwifiex_ie_index_used_by_other_intf(priv, index))
131                                 return -1;
132
133                         ie->ie_length = 0;
134                         memcpy(&priv->mgmt_ie[index], ie,
135                                sizeof(struct mwifiex_ie));
136                 }
137
138                 le16_add_cpu(&ie_list->len,
139                              le16_to_cpu(priv->mgmt_ie[index].ie_length) +
140                              MWIFIEX_IE_HDR_SIZE);
141         }
142
143         if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
144                 return mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG,
145                                               HostCmd_ACT_GEN_SET,
146                                               UAP_CUSTOM_IE_I, ie_list);
147
148         return 0;
149 }
150
151 /* Copy individual custom IEs for beacon, probe response and assoc response
152  * and prepare single structure for IE setting.
153  * This function also updates allocated IE indices from driver.
154  */
155 static int
156 mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
157                              struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
158                              struct mwifiex_ie *pr_ie, u16 *probe_idx,
159                              struct mwifiex_ie *ar_ie, u16 *assoc_idx)
160 {
161         struct mwifiex_ie_list *ap_custom_ie;
162         u8 *pos;
163         u16 len;
164         int ret;
165
166         ap_custom_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
167         if (!ap_custom_ie)
168                 return -ENOMEM;
169
170         ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
171         pos = (u8 *)ap_custom_ie->ie_list;
172
173         if (beacon_ie) {
174                 len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
175                       le16_to_cpu(beacon_ie->ie_length);
176                 memcpy(pos, beacon_ie, len);
177                 pos += len;
178                 le16_add_cpu(&ap_custom_ie->len, len);
179         }
180         if (pr_ie) {
181                 len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
182                       le16_to_cpu(pr_ie->ie_length);
183                 memcpy(pos, pr_ie, len);
184                 pos += len;
185                 le16_add_cpu(&ap_custom_ie->len, len);
186         }
187         if (ar_ie) {
188                 len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
189                       le16_to_cpu(ar_ie->ie_length);
190                 memcpy(pos, ar_ie, len);
191                 pos += len;
192                 le16_add_cpu(&ap_custom_ie->len, len);
193         }
194
195         ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
196
197         pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
198         if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
199                 /* save beacon ie index after auto-indexing */
200                 *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
201                 len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
202                       le16_to_cpu(beacon_ie->ie_length);
203                 pos += len;
204         }
205         if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
206                 /* save probe resp ie index after auto-indexing */
207                 *probe_idx = *((u16 *)pos);
208                 len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
209                       le16_to_cpu(pr_ie->ie_length);
210                 pos += len;
211         }
212         if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
213                 /* save assoc resp ie index after auto-indexing */
214                 *assoc_idx = *((u16 *)pos);
215
216         kfree(ap_custom_ie);
217         return ret;
218 }
219
220 /* This function parses different IEs- Tail IEs, beacon IEs, probe response IEs,
221  * association response IEs from cfg80211_ap_settings function and sets these IE
222  * to FW.
223  */
224 int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
225                          struct cfg80211_ap_settings *params)
226 {
227         struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
228         struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL;
229         struct ieee_types_header *ie = NULL;
230         u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
231         u16 ar_idx = MWIFIEX_AUTO_IDX_MASK, rsn_idx = MWIFIEX_AUTO_IDX_MASK;
232         u16 mask;
233         int ret = 0;
234
235         if (params->beacon.tail && params->beacon.tail_len) {
236                 ie = (void *)cfg80211_find_ie(WLAN_EID_RSN, params->beacon.tail,
237                                               params->beacon.tail_len);
238                 if (ie) {
239                         rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
240                         if (!rsn_ie)
241                                 return -ENOMEM;
242
243                         rsn_ie->ie_index = cpu_to_le16(rsn_idx);
244                         mask = MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP |
245                                MGMT_MASK_ASSOC_RESP;
246                         rsn_ie->mgmt_subtype_mask = cpu_to_le16(mask);
247                         rsn_ie->ie_length = cpu_to_le16(ie->len + 2);
248                         memcpy(rsn_ie->ie_buffer, ie, ie->len + 2);
249
250                         if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &rsn_idx,
251                                                          NULL, NULL,
252                                                          NULL, NULL)) {
253                                 ret = -1;
254                                 goto done;
255                         }
256
257                         priv->rsn_idx = rsn_idx;
258                 }
259         }
260
261         if (params->beacon.beacon_ies && params->beacon.beacon_ies_len) {
262                 beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
263                 if (!beacon_ie) {
264                         ret = -ENOMEM;
265                         goto done;
266                 }
267
268                 beacon_ie->ie_index = cpu_to_le16(beacon_idx);
269                 beacon_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON);
270                 beacon_ie->ie_length =
271                                 cpu_to_le16(params->beacon.beacon_ies_len);
272                 memcpy(beacon_ie->ie_buffer, params->beacon.beacon_ies,
273                        params->beacon.beacon_ies_len);
274         }
275
276         if (params->beacon.proberesp_ies && params->beacon.proberesp_ies_len) {
277                 pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
278                 if (!pr_ie) {
279                         ret = -ENOMEM;
280                         goto done;
281                 }
282
283                 pr_ie->ie_index = cpu_to_le16(pr_idx);
284                 pr_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_PROBE_RESP);
285                 pr_ie->ie_length =
286                                 cpu_to_le16(params->beacon.proberesp_ies_len);
287                 memcpy(pr_ie->ie_buffer, params->beacon.proberesp_ies,
288                        params->beacon.proberesp_ies_len);
289         }
290
291         if (params->beacon.assocresp_ies && params->beacon.assocresp_ies_len) {
292                 ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
293                 if (!ar_ie) {
294                         ret = -ENOMEM;
295                         goto done;
296                 }
297
298                 ar_ie->ie_index = cpu_to_le16(ar_idx);
299                 mask = MGMT_MASK_ASSOC_RESP | MGMT_MASK_REASSOC_RESP;
300                 ar_ie->mgmt_subtype_mask = cpu_to_le16(mask);
301                 ar_ie->ie_length =
302                                 cpu_to_le16(params->beacon.assocresp_ies_len);
303                 memcpy(ar_ie->ie_buffer, params->beacon.assocresp_ies,
304                        params->beacon.assocresp_ies_len);
305         }
306
307         if (beacon_ie || pr_ie || ar_ie) {
308                 ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
309                                                    &beacon_idx, pr_ie,
310                                                    &pr_idx, ar_ie, &ar_idx);
311                 if (ret)
312                         goto done;
313         }
314
315         priv->beacon_idx = beacon_idx;
316         priv->proberesp_idx = pr_idx;
317         priv->assocresp_idx = ar_idx;
318
319 done:
320         kfree(beacon_ie);
321         kfree(pr_ie);
322         kfree(ar_ie);
323         kfree(rsn_ie);
324
325         return ret;
326 }
327
328 /* This function removes management IE set */
329 int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
330 {
331         struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
332         struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL;
333         int ret = 0;
334
335         if (priv->rsn_idx != MWIFIEX_AUTO_IDX_MASK) {
336                 rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
337                 if (!rsn_ie)
338                         return -ENOMEM;
339
340                 rsn_ie->ie_index = cpu_to_le16(priv->rsn_idx);
341                 rsn_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
342                 rsn_ie->ie_length = 0;
343                 if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &priv->rsn_idx,
344                                                  NULL, &priv->proberesp_idx,
345                                                  NULL, &priv->assocresp_idx)) {
346                         ret = -1;
347                         goto done;
348                 }
349
350                 priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK;
351         }
352
353         if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
354                 beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
355                 if (!beacon_ie) {
356                         ret = -ENOMEM;
357                         goto done;
358                 }
359                 beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
360                 beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
361                 beacon_ie->ie_length = 0;
362         }
363         if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
364                 pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
365                 if (!pr_ie) {
366                         ret = -ENOMEM;
367                         goto done;
368                 }
369                 pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
370                 pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
371                 pr_ie->ie_length = 0;
372         }
373         if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
374                 ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
375                 if (!ar_ie) {
376                         ret = -ENOMEM;
377                         goto done;
378                 }
379                 ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
380                 ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
381                 ar_ie->ie_length = 0;
382         }
383
384         if (beacon_ie || pr_ie || ar_ie)
385                 ret = mwifiex_update_uap_custom_ie(priv,
386                                                    beacon_ie, &priv->beacon_idx,
387                                                    pr_ie, &priv->proberesp_idx,
388                                                    ar_ie, &priv->assocresp_idx);
389
390 done:
391         kfree(beacon_ie);
392         kfree(pr_ie);
393         kfree(ar_ie);
394         kfree(rsn_ie);
395
396         return ret;
397 }