Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ie.c
Go to the documentation of this file.
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(ie->ie_length);
55 
56  if (mask == MWIFIEX_AUTO_IDX_MASK)
57  continue;
58 
59  if (mask == subtype_mask) {
60  if (len > IEEE_MAX_IE_SIZE)
61  continue;
62 
63  *index = i;
64  return 0;
65  }
66 
67  if (!priv->mgmt_ie[i].ie_length) {
68  if (mwifiex_ie_index_used_by_other_intf(priv, i))
69  continue;
70 
71  *index = i;
72  return 0;
73  }
74  }
75 
76  return -1;
77 }
78 
79 /* This function prepares IE data buffer for command to be sent to FW */
80 static int
81 mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
82  struct mwifiex_ie_list *ie_list)
83 {
84  u16 travel_len, index, mask;
85  s16 input_len;
86  struct mwifiex_ie *ie;
87  u8 *tmp;
88 
89  input_len = le16_to_cpu(ie_list->len);
90  travel_len = sizeof(struct host_cmd_tlv);
91 
92  ie_list->len = 0;
93 
94  while (input_len > 0) {
95  ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
96  input_len -= le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
97  travel_len += le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
98 
99  index = le16_to_cpu(ie->ie_index);
100  mask = le16_to_cpu(ie->mgmt_subtype_mask);
101 
102  if (index == MWIFIEX_AUTO_IDX_MASK) {
103  /* automatic addition */
104  if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
105  return -1;
106  if (index == MWIFIEX_AUTO_IDX_MASK)
107  return -1;
108 
109  tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
110  memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
111  priv->mgmt_ie[index].ie_length = ie->ie_length;
112  priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
113  priv->mgmt_ie[index].mgmt_subtype_mask =
114  cpu_to_le16(mask);
115 
116  ie->ie_index = cpu_to_le16(index);
117  } else {
118  if (mask != MWIFIEX_DELETE_MASK)
119  return -1;
120  /*
121  * Check if this index is being used on any
122  * other interface.
123  */
124  if (mwifiex_ie_index_used_by_other_intf(priv, index))
125  return -1;
126 
127  ie->ie_length = 0;
128  memcpy(&priv->mgmt_ie[index], ie,
129  sizeof(struct mwifiex_ie));
130  }
131 
132  le16_add_cpu(&ie_list->len,
133  le16_to_cpu(priv->mgmt_ie[index].ie_length) +
135  }
136 
137  if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
140  UAP_CUSTOM_IE_I, ie_list);
141 
142  return 0;
143 }
144 
145 /* Copy individual custom IEs for beacon, probe response and assoc response
146  * and prepare single structure for IE setting.
147  * This function also updates allocated IE indices from driver.
148  */
149 static int
150 mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
151  struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
152  struct mwifiex_ie *pr_ie, u16 *probe_idx,
153  struct mwifiex_ie *ar_ie, u16 *assoc_idx)
154 {
155  struct mwifiex_ie_list *ap_custom_ie;
156  u8 *pos;
157  u16 len;
158  int ret;
159 
160  ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
161  if (!ap_custom_ie)
162  return -ENOMEM;
163 
164  ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
165  pos = (u8 *)ap_custom_ie->ie_list;
166 
167  if (beacon_ie) {
168  len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
169  le16_to_cpu(beacon_ie->ie_length);
170  memcpy(pos, beacon_ie, len);
171  pos += len;
172  le16_add_cpu(&ap_custom_ie->len, len);
173  }
174  if (pr_ie) {
175  len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
176  le16_to_cpu(pr_ie->ie_length);
177  memcpy(pos, pr_ie, len);
178  pos += len;
179  le16_add_cpu(&ap_custom_ie->len, len);
180  }
181  if (ar_ie) {
182  len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
183  le16_to_cpu(ar_ie->ie_length);
184  memcpy(pos, ar_ie, len);
185  pos += len;
186  le16_add_cpu(&ap_custom_ie->len, len);
187  }
188 
189  ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
190 
191  pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
192  if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
193  /* save beacon ie index after auto-indexing */
194  *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
195  len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
196  le16_to_cpu(beacon_ie->ie_length);
197  pos += len;
198  }
199  if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
200  /* save probe resp ie index after auto-indexing */
201  *probe_idx = *((u16 *)pos);
202  len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
203  le16_to_cpu(pr_ie->ie_length);
204  pos += len;
205  }
206  if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
207  /* save assoc resp ie index after auto-indexing */
208  *assoc_idx = *((u16 *)pos);
209 
210  kfree(ap_custom_ie);
211  return ret;
212 }
213 
214 /* This function checks if the vendor specified IE is present in passed buffer
215  * and copies it to mwifiex_ie structure.
216  * Function takes pointer to struct mwifiex_ie pointer as argument.
217  * If the vendor specified IE is present then memory is allocated for
218  * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
219  * this memory.
220  */
221 static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
222  struct mwifiex_ie **ie_ptr, u16 mask,
223  unsigned int oui, u8 oui_type)
224 {
225  struct ieee_types_header *vs_ie;
226  struct mwifiex_ie *ie = *ie_ptr;
227  const u8 *vendor_ie;
228 
229  vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
230  if (vendor_ie) {
231  if (!*ie_ptr) {
232  *ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
233  GFP_KERNEL);
234  if (!*ie_ptr)
235  return -ENOMEM;
236  ie = *ie_ptr;
237  }
238 
239  vs_ie = (struct ieee_types_header *)vendor_ie;
241  vs_ie, vs_ie->len + 2);
242  le16_add_cpu(&ie->ie_length, vs_ie->len + 2);
243  ie->mgmt_subtype_mask = cpu_to_le16(mask);
245  }
246 
247  *ie_ptr = ie;
248  return 0;
249 }
250 
251 /* This function parses beacon IEs, probe response IEs, association response IEs
252  * from cfg80211_ap_settings->beacon and sets these IE to FW.
253  */
254 static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
255  struct cfg80211_beacon_data *data)
256 {
257  struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
258  u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
259  u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
260  int ret = 0;
261 
262  if (data->beacon_ies && data->beacon_ies_len) {
263  mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
264  &beacon_ie, MGMT_MASK_BEACON,
267  mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
268  &beacon_ie, MGMT_MASK_BEACON,
270  }
271 
272  if (data->proberesp_ies && data->proberesp_ies_len) {
273  mwifiex_update_vs_ie(data->proberesp_ies,
274  data->proberesp_ies_len, &pr_ie,
277  mwifiex_update_vs_ie(data->proberesp_ies,
278  data->proberesp_ies_len, &pr_ie,
281  }
282 
283  if (data->assocresp_ies && data->assocresp_ies_len) {
284  mwifiex_update_vs_ie(data->assocresp_ies,
285  data->assocresp_ies_len, &ar_ie,
290  mwifiex_update_vs_ie(data->assocresp_ies,
291  data->assocresp_ies_len, &ar_ie,
295  }
296 
297  if (beacon_ie || pr_ie || ar_ie) {
298  ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
299  &beacon_idx, pr_ie,
300  &pr_idx, ar_ie, &ar_idx);
301  if (ret)
302  goto done;
303  }
304 
305  priv->beacon_idx = beacon_idx;
306  priv->proberesp_idx = pr_idx;
307  priv->assocresp_idx = ar_idx;
308 
309 done:
310  kfree(beacon_ie);
311  kfree(pr_ie);
312  kfree(ar_ie);
313 
314  return ret;
315 }
316 
317 /* This function parses different IEs-tail IEs, beacon IEs, probe response IEs,
318  * association response IEs from cfg80211_ap_settings function and sets these IE
319  * to FW.
320  */
322  struct cfg80211_beacon_data *info)
323 {
324  struct mwifiex_ie *gen_ie;
325  struct ieee_types_header *rsn_ie, *wpa_ie = NULL;
326  u16 rsn_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
327  const u8 *vendor_ie;
328 
329  if (info->tail && info->tail_len) {
330  gen_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
331  if (!gen_ie)
332  return -ENOMEM;
333  gen_ie->ie_index = cpu_to_le16(rsn_idx);
337 
338  rsn_ie = (void *)cfg80211_find_ie(WLAN_EID_RSN,
339  info->tail, info->tail_len);
340  if (rsn_ie) {
341  memcpy(gen_ie->ie_buffer, rsn_ie, rsn_ie->len + 2);
342  ie_len = rsn_ie->len + 2;
343  gen_ie->ie_length = cpu_to_le16(ie_len);
344  }
345 
348  info->tail,
349  info->tail_len);
350  if (vendor_ie) {
351  wpa_ie = (struct ieee_types_header *)vendor_ie;
352  memcpy(gen_ie->ie_buffer + ie_len,
353  wpa_ie, wpa_ie->len + 2);
354  ie_len += wpa_ie->len + 2;
355  gen_ie->ie_length = cpu_to_le16(ie_len);
356  }
357 
358  if (rsn_ie || wpa_ie) {
359  if (mwifiex_update_uap_custom_ie(priv, gen_ie, &rsn_idx,
360  NULL, NULL,
361  NULL, NULL)) {
362  kfree(gen_ie);
363  return -1;
364  }
365  priv->rsn_idx = rsn_idx;
366  }
367 
368  kfree(gen_ie);
369  }
370 
371  return mwifiex_set_mgmt_beacon_data_ies(priv, info);
372 }
373 
374 /* This function removes management IE set */
376 {
377  struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
378  struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL;
379  int ret = 0;
380 
381  if (priv->rsn_idx != MWIFIEX_AUTO_IDX_MASK) {
382  rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
383  if (!rsn_ie)
384  return -ENOMEM;
385 
386  rsn_ie->ie_index = cpu_to_le16(priv->rsn_idx);
387  rsn_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
388  rsn_ie->ie_length = 0;
389  if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &priv->rsn_idx,
390  NULL, &priv->proberesp_idx,
391  NULL, &priv->assocresp_idx)) {
392  ret = -1;
393  goto done;
394  }
395 
397  }
398 
399  if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
400  beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
401  if (!beacon_ie) {
402  ret = -ENOMEM;
403  goto done;
404  }
405  beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
407  beacon_ie->ie_length = 0;
408  }
409  if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
410  pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
411  if (!pr_ie) {
412  ret = -ENOMEM;
413  goto done;
414  }
415  pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
416  pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
417  pr_ie->ie_length = 0;
418  }
419  if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
420  ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
421  if (!ar_ie) {
422  ret = -ENOMEM;
423  goto done;
424  }
425  ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
427  ar_ie->ie_length = 0;
428  }
429 
430  if (beacon_ie || pr_ie || ar_ie)
431  ret = mwifiex_update_uap_custom_ie(priv,
432  beacon_ie, &priv->beacon_idx,
433  pr_ie, &priv->proberesp_idx,
434  ar_ie, &priv->assocresp_idx);
435 
436 done:
437  kfree(beacon_ie);
438  kfree(pr_ie);
439  kfree(ar_ie);
440  kfree(rsn_ie);
441 
442  return ret;
443 }