blob: 2a00e362f5fe1f0eca4c503fa92faf6d13450deb [file] [log] [blame]
Johannes Berg2a519312009-02-10 21:25:55 +01001/*
2 * cfg80211 scan result handling
3 *
4 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
5 */
6#include <linux/kernel.h>
7#include <linux/module.h>
8#include <linux/netdevice.h>
9#include <linux/wireless.h>
10#include <linux/nl80211.h>
11#include <linux/etherdevice.h>
12#include <net/arp.h>
13#include <net/cfg80211.h>
14#include <net/iw_handler.h>
15#include "core.h"
16#include "nl80211.h"
17
18#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
19
20void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
21{
22 struct net_device *dev;
23#ifdef CONFIG_WIRELESS_EXT
24 union iwreq_data wrqu;
25#endif
26
27 dev = dev_get_by_index(&init_net, request->ifidx);
28 if (!dev)
29 goto out;
30
31 WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
32 wiphy_to_dev(request->wiphy)->scan_req = NULL;
33
34 if (aborted)
35 nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev);
36 else
37 nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev);
38
39#ifdef CONFIG_WIRELESS_EXT
40 if (!aborted) {
41 memset(&wrqu, 0, sizeof(wrqu));
42
43 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
44 }
45#endif
46
47 dev_put(dev);
48
49 out:
50 kfree(request);
51}
52EXPORT_SYMBOL(cfg80211_scan_done);
53
54static void bss_release(struct kref *ref)
55{
56 struct cfg80211_internal_bss *bss;
57
58 bss = container_of(ref, struct cfg80211_internal_bss, ref);
Johannes Berg78c1c7e2009-02-10 21:25:57 +010059 if (bss->pub.free_priv)
60 bss->pub.free_priv(&bss->pub);
Johannes Berg2a519312009-02-10 21:25:55 +010061 kfree(bss);
62}
63
64/* must hold dev->bss_lock! */
Dan Williamscb3a8ee2009-02-11 17:14:43 -050065void cfg80211_bss_age(struct cfg80211_registered_device *dev,
66 unsigned long age_secs)
67{
68 struct cfg80211_internal_bss *bss;
69 unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
70
71 list_for_each_entry(bss, &dev->bss_list, list) {
72 bss->ts -= age_jiffies;
73 }
74}
75
76/* must hold dev->bss_lock! */
Johannes Berg2a519312009-02-10 21:25:55 +010077void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
78{
79 struct cfg80211_internal_bss *bss, *tmp;
80 bool expired = false;
81
82 list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
Kalle Valoa08c1c12009-03-22 21:57:28 +020083 if (bss->hold ||
84 !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
Johannes Berg2a519312009-02-10 21:25:55 +010085 continue;
86 list_del(&bss->list);
87 rb_erase(&bss->rbn, &dev->bss_tree);
88 kref_put(&bss->ref, bss_release);
89 expired = true;
90 }
91
92 if (expired)
93 dev->bss_generation++;
94}
95
96static u8 *find_ie(u8 num, u8 *ies, size_t len)
97{
98 while (len > 2 && ies[0] != num) {
99 len -= ies[1] + 2;
100 ies += ies[1] + 2;
101 }
102 if (len < 2)
103 return NULL;
104 if (len < 2 + ies[1])
105 return NULL;
106 return ies;
107}
108
109static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
110{
111 const u8 *ie1 = find_ie(num, ies1, len1);
112 const u8 *ie2 = find_ie(num, ies2, len2);
113 int r;
114
115 if (!ie1 && !ie2)
116 return 0;
117 if (!ie1)
118 return -1;
119
120 r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1]));
121 if (r == 0 && ie1[1] != ie2[1])
122 return ie2[1] - ie1[1];
123 return r;
124}
125
126static bool is_bss(struct cfg80211_bss *a,
127 const u8 *bssid,
128 const u8 *ssid, size_t ssid_len)
129{
130 const u8 *ssidie;
131
Johannes Berg79420f02009-02-10 21:25:59 +0100132 if (bssid && compare_ether_addr(a->bssid, bssid))
Johannes Berg2a519312009-02-10 21:25:55 +0100133 return false;
134
Johannes Berg79420f02009-02-10 21:25:59 +0100135 if (!ssid)
136 return true;
137
Johannes Berg2a519312009-02-10 21:25:55 +0100138 ssidie = find_ie(WLAN_EID_SSID,
139 a->information_elements,
140 a->len_information_elements);
141 if (!ssidie)
142 return false;
143 if (ssidie[1] != ssid_len)
144 return false;
145 return memcmp(ssidie + 2, ssid, ssid_len) == 0;
146}
147
148static bool is_mesh(struct cfg80211_bss *a,
149 const u8 *meshid, size_t meshidlen,
150 const u8 *meshcfg)
151{
152 const u8 *ie;
153
154 if (!is_zero_ether_addr(a->bssid))
155 return false;
156
157 ie = find_ie(WLAN_EID_MESH_ID,
158 a->information_elements,
159 a->len_information_elements);
160 if (!ie)
161 return false;
162 if (ie[1] != meshidlen)
163 return false;
164 if (memcmp(ie + 2, meshid, meshidlen))
165 return false;
166
167 ie = find_ie(WLAN_EID_MESH_CONFIG,
168 a->information_elements,
169 a->len_information_elements);
170 if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
171 return false;
172
173 /*
174 * Ignore mesh capability (last two bytes of the IE) when
175 * comparing since that may differ between stations taking
176 * part in the same mesh.
177 */
178 return memcmp(ie + 2, meshcfg, IEEE80211_MESH_CONFIG_LEN - 2) == 0;
179}
180
181static int cmp_bss(struct cfg80211_bss *a,
182 struct cfg80211_bss *b)
183{
184 int r;
185
186 if (a->channel != b->channel)
187 return b->channel->center_freq - a->channel->center_freq;
188
189 r = memcmp(a->bssid, b->bssid, ETH_ALEN);
190 if (r)
191 return r;
192
193 if (is_zero_ether_addr(a->bssid)) {
194 r = cmp_ies(WLAN_EID_MESH_ID,
195 a->information_elements,
196 a->len_information_elements,
197 b->information_elements,
198 b->len_information_elements);
199 if (r)
200 return r;
201 return cmp_ies(WLAN_EID_MESH_CONFIG,
202 a->information_elements,
203 a->len_information_elements,
204 b->information_elements,
205 b->len_information_elements);
206 }
207
208 return cmp_ies(WLAN_EID_SSID,
209 a->information_elements,
210 a->len_information_elements,
211 b->information_elements,
212 b->len_information_elements);
213}
214
215struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
216 struct ieee80211_channel *channel,
217 const u8 *bssid,
Johannes Berg79420f02009-02-10 21:25:59 +0100218 const u8 *ssid, size_t ssid_len,
219 u16 capa_mask, u16 capa_val)
Johannes Berg2a519312009-02-10 21:25:55 +0100220{
221 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
222 struct cfg80211_internal_bss *bss, *res = NULL;
223
224 spin_lock_bh(&dev->bss_lock);
225
226 list_for_each_entry(bss, &dev->bss_list, list) {
Johannes Berg79420f02009-02-10 21:25:59 +0100227 if ((bss->pub.capability & capa_mask) != capa_val)
228 continue;
Johannes Berg2a519312009-02-10 21:25:55 +0100229 if (channel && bss->pub.channel != channel)
230 continue;
231 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
232 res = bss;
233 kref_get(&res->ref);
234 break;
235 }
236 }
237
238 spin_unlock_bh(&dev->bss_lock);
239 if (!res)
240 return NULL;
241 return &res->pub;
242}
243EXPORT_SYMBOL(cfg80211_get_bss);
244
245struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
246 struct ieee80211_channel *channel,
247 const u8 *meshid, size_t meshidlen,
248 const u8 *meshcfg)
249{
250 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
251 struct cfg80211_internal_bss *bss, *res = NULL;
252
253 spin_lock_bh(&dev->bss_lock);
254
255 list_for_each_entry(bss, &dev->bss_list, list) {
256 if (channel && bss->pub.channel != channel)
257 continue;
258 if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
259 res = bss;
260 kref_get(&res->ref);
261 break;
262 }
263 }
264
265 spin_unlock_bh(&dev->bss_lock);
266 if (!res)
267 return NULL;
268 return &res->pub;
269}
270EXPORT_SYMBOL(cfg80211_get_mesh);
271
272
273static void rb_insert_bss(struct cfg80211_registered_device *dev,
274 struct cfg80211_internal_bss *bss)
275{
276 struct rb_node **p = &dev->bss_tree.rb_node;
277 struct rb_node *parent = NULL;
278 struct cfg80211_internal_bss *tbss;
279 int cmp;
280
281 while (*p) {
282 parent = *p;
283 tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
284
285 cmp = cmp_bss(&bss->pub, &tbss->pub);
286
287 if (WARN_ON(!cmp)) {
288 /* will sort of leak this BSS */
289 return;
290 }
291
292 if (cmp < 0)
293 p = &(*p)->rb_left;
294 else
295 p = &(*p)->rb_right;
296 }
297
298 rb_link_node(&bss->rbn, parent, p);
299 rb_insert_color(&bss->rbn, &dev->bss_tree);
300}
301
302static struct cfg80211_internal_bss *
303rb_find_bss(struct cfg80211_registered_device *dev,
304 struct cfg80211_internal_bss *res)
305{
306 struct rb_node *n = dev->bss_tree.rb_node;
307 struct cfg80211_internal_bss *bss;
308 int r;
309
310 while (n) {
311 bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
312 r = cmp_bss(&res->pub, &bss->pub);
313
314 if (r == 0)
315 return bss;
316 else if (r < 0)
317 n = n->rb_left;
318 else
319 n = n->rb_right;
320 }
321
322 return NULL;
323}
324
325static struct cfg80211_internal_bss *
326cfg80211_bss_update(struct cfg80211_registered_device *dev,
327 struct cfg80211_internal_bss *res,
328 bool overwrite)
329{
330 struct cfg80211_internal_bss *found = NULL;
331 const u8 *meshid, *meshcfg;
332
333 /*
334 * The reference to "res" is donated to this function.
335 */
336
337 if (WARN_ON(!res->pub.channel)) {
338 kref_put(&res->ref, bss_release);
339 return NULL;
340 }
341
342 res->ts = jiffies;
343
344 if (is_zero_ether_addr(res->pub.bssid)) {
345 /* must be mesh, verify */
346 meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements,
347 res->pub.len_information_elements);
348 meshcfg = find_ie(WLAN_EID_MESH_CONFIG,
349 res->pub.information_elements,
350 res->pub.len_information_elements);
351 if (!meshid || !meshcfg ||
352 meshcfg[1] != IEEE80211_MESH_CONFIG_LEN) {
353 /* bogus mesh */
354 kref_put(&res->ref, bss_release);
355 return NULL;
356 }
357 }
358
359 spin_lock_bh(&dev->bss_lock);
360
361 found = rb_find_bss(dev, res);
362
363 if (found && overwrite) {
364 list_replace(&found->list, &res->list);
365 rb_replace_node(&found->rbn, &res->rbn,
366 &dev->bss_tree);
367 kref_put(&found->ref, bss_release);
368 found = res;
369 } else if (found) {
370 kref_get(&found->ref);
371 found->pub.beacon_interval = res->pub.beacon_interval;
372 found->pub.tsf = res->pub.tsf;
373 found->pub.signal = res->pub.signal;
Johannes Berg2a519312009-02-10 21:25:55 +0100374 found->pub.capability = res->pub.capability;
375 found->ts = res->ts;
376 kref_put(&res->ref, bss_release);
377 } else {
378 /* this "consumes" the reference */
379 list_add_tail(&res->list, &dev->bss_list);
380 rb_insert_bss(dev, res);
381 found = res;
382 }
383
384 dev->bss_generation++;
385 spin_unlock_bh(&dev->bss_lock);
386
387 kref_get(&found->ref);
388 return found;
389}
390
391struct cfg80211_bss *
392cfg80211_inform_bss_frame(struct wiphy *wiphy,
393 struct ieee80211_channel *channel,
394 struct ieee80211_mgmt *mgmt, size_t len,
Johannes Berg77965c92009-02-18 18:45:06 +0100395 s32 signal, gfp_t gfp)
Johannes Berg2a519312009-02-10 21:25:55 +0100396{
397 struct cfg80211_internal_bss *res;
398 size_t ielen = len - offsetof(struct ieee80211_mgmt,
399 u.probe_resp.variable);
400 bool overwrite;
401 size_t privsz = wiphy->bss_priv_size;
402
Johannes Berg77965c92009-02-18 18:45:06 +0100403 if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
Johannes Berg2a519312009-02-10 21:25:55 +0100404 (signal < 0 || signal > 100)))
405 return NULL;
406
407 if (WARN_ON(!mgmt || !wiphy ||
408 len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
409 return NULL;
410
411 res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
412 if (!res)
413 return NULL;
414
415 memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
416 res->pub.channel = channel;
Johannes Berg2a519312009-02-10 21:25:55 +0100417 res->pub.signal = signal;
418 res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
419 res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
420 res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
421 /* point to after the private area */
422 res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
423 memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen);
424 res->pub.len_information_elements = ielen;
425
426 kref_init(&res->ref);
427
428 overwrite = ieee80211_is_probe_resp(mgmt->frame_control);
429
430 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
431 if (!res)
432 return NULL;
433
Luis R. Rodrigueze38f8a72009-02-21 00:20:39 -0500434 if (res->pub.capability & WLAN_CAPABILITY_ESS)
435 regulatory_hint_found_beacon(wiphy, channel, gfp);
436
Johannes Berg2a519312009-02-10 21:25:55 +0100437 /* cfg80211_bss_update gives us a referenced result */
438 return &res->pub;
439}
440EXPORT_SYMBOL(cfg80211_inform_bss_frame);
441
442void cfg80211_put_bss(struct cfg80211_bss *pub)
443{
444 struct cfg80211_internal_bss *bss;
445
446 if (!pub)
447 return;
448
449 bss = container_of(pub, struct cfg80211_internal_bss, pub);
450 kref_put(&bss->ref, bss_release);
451}
452EXPORT_SYMBOL(cfg80211_put_bss);
453
Johannes Bergd491af12009-02-10 21:25:58 +0100454void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
455{
456 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
457 struct cfg80211_internal_bss *bss;
458
459 if (WARN_ON(!pub))
460 return;
461
462 bss = container_of(pub, struct cfg80211_internal_bss, pub);
463
464 spin_lock_bh(&dev->bss_lock);
465
466 list_del(&bss->list);
467 rb_erase(&bss->rbn, &dev->bss_tree);
468
469 spin_unlock_bh(&dev->bss_lock);
470
471 kref_put(&bss->ref, bss_release);
472}
473EXPORT_SYMBOL(cfg80211_unlink_bss);
474
Kalle Valoa08c1c12009-03-22 21:57:28 +0200475void cfg80211_hold_bss(struct cfg80211_bss *pub)
476{
477 struct cfg80211_internal_bss *bss;
478
479 if (!pub)
480 return;
481
482 bss = container_of(pub, struct cfg80211_internal_bss, pub);
483 bss->hold = true;
484}
485EXPORT_SYMBOL(cfg80211_hold_bss);
486
487void cfg80211_unhold_bss(struct cfg80211_bss *pub)
488{
489 struct cfg80211_internal_bss *bss;
490
491 if (!pub)
492 return;
493
494 bss = container_of(pub, struct cfg80211_internal_bss, pub);
495 bss->hold = false;
496}
497EXPORT_SYMBOL(cfg80211_unhold_bss);
498
Johannes Berg2a519312009-02-10 21:25:55 +0100499#ifdef CONFIG_WIRELESS_EXT
500int cfg80211_wext_siwscan(struct net_device *dev,
501 struct iw_request_info *info,
502 union iwreq_data *wrqu, char *extra)
503{
504 struct cfg80211_registered_device *rdev;
505 struct wiphy *wiphy;
506 struct iw_scan_req *wreq = NULL;
507 struct cfg80211_scan_request *creq;
508 int i, err, n_channels = 0;
509 enum ieee80211_band band;
510
511 if (!netif_running(dev))
512 return -ENETDOWN;
513
514 rdev = cfg80211_get_dev_from_ifindex(dev->ifindex);
515
516 if (IS_ERR(rdev))
517 return PTR_ERR(rdev);
518
519 if (rdev->scan_req) {
520 err = -EBUSY;
521 goto out;
522 }
523
524 wiphy = &rdev->wiphy;
525
526 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
527 if (wiphy->bands[band])
528 n_channels += wiphy->bands[band]->n_channels;
529
530 creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
531 n_channels * sizeof(void *),
532 GFP_ATOMIC);
533 if (!creq) {
534 err = -ENOMEM;
535 goto out;
536 }
537
538 creq->wiphy = wiphy;
539 creq->ifidx = dev->ifindex;
540 creq->ssids = (void *)(creq + 1);
541 creq->channels = (void *)(creq->ssids + 1);
542 creq->n_channels = n_channels;
543 creq->n_ssids = 1;
544
545 /* all channels */
546 i = 0;
547 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
548 int j;
549 if (!wiphy->bands[band])
550 continue;
551 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
552 creq->channels[i] = &wiphy->bands[band]->channels[j];
553 i++;
554 }
555 }
556
557 /* translate scan request */
558 if (wrqu->data.length == sizeof(struct iw_scan_req)) {
559 wreq = (struct iw_scan_req *)extra;
560
561 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
562 if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
563 return -EINVAL;
564 memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
565 creq->ssids[0].ssid_len = wreq->essid_len;
566 }
567 if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
568 creq->n_ssids = 0;
569 }
570
571 rdev->scan_req = creq;
572 err = rdev->ops->scan(wiphy, dev, creq);
573 if (err) {
574 rdev->scan_req = NULL;
575 kfree(creq);
576 }
577 out:
578 cfg80211_put_dev(rdev);
579 return err;
580}
581EXPORT_SYMBOL(cfg80211_wext_siwscan);
582
583static void ieee80211_scan_add_ies(struct iw_request_info *info,
584 struct cfg80211_bss *bss,
585 char **current_ev, char *end_buf)
586{
587 u8 *pos, *end, *next;
588 struct iw_event iwe;
589
590 if (!bss->information_elements ||
591 !bss->len_information_elements)
592 return;
593
594 /*
595 * If needed, fragment the IEs buffer (at IE boundaries) into short
596 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
597 */
598 pos = bss->information_elements;
599 end = pos + bss->len_information_elements;
600
601 while (end - pos > IW_GENERIC_IE_MAX) {
602 next = pos + 2 + pos[1];
603 while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
604 next = next + 2 + next[1];
605
606 memset(&iwe, 0, sizeof(iwe));
607 iwe.cmd = IWEVGENIE;
608 iwe.u.data.length = next - pos;
609 *current_ev = iwe_stream_add_point(info, *current_ev,
610 end_buf, &iwe, pos);
611
612 pos = next;
613 }
614
615 if (end > pos) {
616 memset(&iwe, 0, sizeof(iwe));
617 iwe.cmd = IWEVGENIE;
618 iwe.u.data.length = end - pos;
619 *current_ev = iwe_stream_add_point(info, *current_ev,
620 end_buf, &iwe, pos);
621 }
622}
623
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500624static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
625{
626 unsigned long end = jiffies;
627
628 if (end >= start)
629 return jiffies_to_msecs(end - start);
630
631 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
632}
Johannes Berg2a519312009-02-10 21:25:55 +0100633
634static char *
Johannes Berg77965c92009-02-18 18:45:06 +0100635ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
636 struct cfg80211_internal_bss *bss, char *current_ev,
637 char *end_buf)
Johannes Berg2a519312009-02-10 21:25:55 +0100638{
639 struct iw_event iwe;
640 u8 *buf, *cfg, *p;
641 u8 *ie = bss->pub.information_elements;
Johannes Berga77b8552009-02-18 18:27:22 +0100642 int rem = bss->pub.len_information_elements, i, sig;
Johannes Berg2a519312009-02-10 21:25:55 +0100643 bool ismesh = false;
644
645 memset(&iwe, 0, sizeof(iwe));
646 iwe.cmd = SIOCGIWAP;
647 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
648 memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
649 current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
650 IW_EV_ADDR_LEN);
651
652 memset(&iwe, 0, sizeof(iwe));
653 iwe.cmd = SIOCGIWFREQ;
654 iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
655 iwe.u.freq.e = 0;
656 current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
657 IW_EV_FREQ_LEN);
658
659 memset(&iwe, 0, sizeof(iwe));
660 iwe.cmd = SIOCGIWFREQ;
661 iwe.u.freq.m = bss->pub.channel->center_freq;
662 iwe.u.freq.e = 6;
663 current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
664 IW_EV_FREQ_LEN);
665
Johannes Berg77965c92009-02-18 18:45:06 +0100666 if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
Johannes Berg2a519312009-02-10 21:25:55 +0100667 memset(&iwe, 0, sizeof(iwe));
668 iwe.cmd = IWEVQUAL;
669 iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
670 IW_QUAL_NOISE_INVALID |
Johannes Berga77b8552009-02-18 18:27:22 +0100671 IW_QUAL_QUAL_UPDATED;
Johannes Berg77965c92009-02-18 18:45:06 +0100672 switch (wiphy->signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +0100673 case CFG80211_SIGNAL_TYPE_MBM:
Johannes Berga77b8552009-02-18 18:27:22 +0100674 sig = bss->pub.signal / 100;
675 iwe.u.qual.level = sig;
Johannes Berg2a519312009-02-10 21:25:55 +0100676 iwe.u.qual.updated |= IW_QUAL_DBM;
Johannes Berga77b8552009-02-18 18:27:22 +0100677 if (sig < -110) /* rather bad */
678 sig = -110;
679 else if (sig > -40) /* perfect */
680 sig = -40;
681 /* will give a range of 0 .. 70 */
682 iwe.u.qual.qual = sig + 110;
Johannes Berg2a519312009-02-10 21:25:55 +0100683 break;
684 case CFG80211_SIGNAL_TYPE_UNSPEC:
685 iwe.u.qual.level = bss->pub.signal;
Johannes Berga77b8552009-02-18 18:27:22 +0100686 /* will give range 0 .. 100 */
687 iwe.u.qual.qual = bss->pub.signal;
Johannes Berg2a519312009-02-10 21:25:55 +0100688 break;
689 default:
690 /* not reached */
691 break;
692 }
693 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
694 &iwe, IW_EV_QUAL_LEN);
695 }
696
697 memset(&iwe, 0, sizeof(iwe));
698 iwe.cmd = SIOCGIWENCODE;
699 if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
700 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
701 else
702 iwe.u.data.flags = IW_ENCODE_DISABLED;
703 iwe.u.data.length = 0;
704 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
705 &iwe, "");
706
707 while (rem >= 2) {
708 /* invalid data */
709 if (ie[1] > rem - 2)
710 break;
711
712 switch (ie[0]) {
713 case WLAN_EID_SSID:
714 memset(&iwe, 0, sizeof(iwe));
715 iwe.cmd = SIOCGIWESSID;
716 iwe.u.data.length = ie[1];
717 iwe.u.data.flags = 1;
718 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
719 &iwe, ie + 2);
720 break;
721 case WLAN_EID_MESH_ID:
722 memset(&iwe, 0, sizeof(iwe));
723 iwe.cmd = SIOCGIWESSID;
724 iwe.u.data.length = ie[1];
725 iwe.u.data.flags = 1;
726 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
727 &iwe, ie + 2);
728 break;
729 case WLAN_EID_MESH_CONFIG:
730 ismesh = true;
731 if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
732 break;
733 buf = kmalloc(50, GFP_ATOMIC);
734 if (!buf)
735 break;
736 cfg = ie + 2;
737 memset(&iwe, 0, sizeof(iwe));
738 iwe.cmd = IWEVCUSTOM;
739 sprintf(buf, "Mesh network (version %d)", cfg[0]);
740 iwe.u.data.length = strlen(buf);
741 current_ev = iwe_stream_add_point(info, current_ev,
742 end_buf,
743 &iwe, buf);
744 sprintf(buf, "Path Selection Protocol ID: "
745 "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
746 cfg[4]);
747 iwe.u.data.length = strlen(buf);
748 current_ev = iwe_stream_add_point(info, current_ev,
749 end_buf,
750 &iwe, buf);
751 sprintf(buf, "Path Selection Metric ID: "
752 "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
753 cfg[8]);
754 iwe.u.data.length = strlen(buf);
755 current_ev = iwe_stream_add_point(info, current_ev,
756 end_buf,
757 &iwe, buf);
758 sprintf(buf, "Congestion Control Mode ID: "
759 "0x%02X%02X%02X%02X", cfg[9], cfg[10],
760 cfg[11], cfg[12]);
761 iwe.u.data.length = strlen(buf);
762 current_ev = iwe_stream_add_point(info, current_ev,
763 end_buf,
764 &iwe, buf);
765 sprintf(buf, "Channel Precedence: "
766 "0x%02X%02X%02X%02X", cfg[13], cfg[14],
767 cfg[15], cfg[16]);
768 iwe.u.data.length = strlen(buf);
769 current_ev = iwe_stream_add_point(info, current_ev,
770 end_buf,
771 &iwe, buf);
772 kfree(buf);
773 break;
774 case WLAN_EID_SUPP_RATES:
775 case WLAN_EID_EXT_SUPP_RATES:
776 /* display all supported rates in readable format */
777 p = current_ev + iwe_stream_lcp_len(info);
778
779 memset(&iwe, 0, sizeof(iwe));
780 iwe.cmd = SIOCGIWRATE;
781 /* Those two flags are ignored... */
782 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
783
784 for (i = 0; i < ie[1]; i++) {
785 iwe.u.bitrate.value =
786 ((ie[i + 2] & 0x7f) * 500000);
787 p = iwe_stream_add_value(info, current_ev, p,
788 end_buf, &iwe, IW_EV_PARAM_LEN);
789 }
790 current_ev = p;
791 break;
792 }
793 rem -= ie[1] + 2;
794 ie += ie[1] + 2;
795 }
796
797 if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
798 || ismesh) {
799 memset(&iwe, 0, sizeof(iwe));
800 iwe.cmd = SIOCGIWMODE;
801 if (ismesh)
802 iwe.u.mode = IW_MODE_MESH;
803 else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
804 iwe.u.mode = IW_MODE_MASTER;
805 else
806 iwe.u.mode = IW_MODE_ADHOC;
807 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
808 &iwe, IW_EV_UINT_LEN);
809 }
810
811 buf = kmalloc(30, GFP_ATOMIC);
812 if (buf) {
813 memset(&iwe, 0, sizeof(iwe));
814 iwe.cmd = IWEVCUSTOM;
815 sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf));
816 iwe.u.data.length = strlen(buf);
817 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
818 &iwe, buf);
819 memset(&iwe, 0, sizeof(iwe));
820 iwe.cmd = IWEVCUSTOM;
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500821 sprintf(buf, " Last beacon: %ums ago",
822 elapsed_jiffies_msecs(bss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +0100823 iwe.u.data.length = strlen(buf);
824 current_ev = iwe_stream_add_point(info, current_ev,
825 end_buf, &iwe, buf);
826 kfree(buf);
827 }
828
829 ieee80211_scan_add_ies(info, &bss->pub, &current_ev, end_buf);
830
831 return current_ev;
832}
833
834
835static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
836 struct iw_request_info *info,
837 char *buf, size_t len)
838{
839 char *current_ev = buf;
840 char *end_buf = buf + len;
841 struct cfg80211_internal_bss *bss;
842
843 spin_lock_bh(&dev->bss_lock);
844 cfg80211_bss_expire(dev);
845
846 list_for_each_entry(bss, &dev->bss_list, list) {
847 if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
848 spin_unlock_bh(&dev->bss_lock);
849 return -E2BIG;
850 }
Johannes Berg77965c92009-02-18 18:45:06 +0100851 current_ev = ieee80211_bss(&dev->wiphy, info, bss,
852 current_ev, end_buf);
Johannes Berg2a519312009-02-10 21:25:55 +0100853 }
854 spin_unlock_bh(&dev->bss_lock);
855 return current_ev - buf;
856}
857
858
859int cfg80211_wext_giwscan(struct net_device *dev,
860 struct iw_request_info *info,
861 struct iw_point *data, char *extra)
862{
863 struct cfg80211_registered_device *rdev;
864 int res;
865
866 if (!netif_running(dev))
867 return -ENETDOWN;
868
869 rdev = cfg80211_get_dev_from_ifindex(dev->ifindex);
870
871 if (IS_ERR(rdev))
872 return PTR_ERR(rdev);
873
874 if (rdev->scan_req) {
875 res = -EAGAIN;
876 goto out;
877 }
878
879 res = ieee80211_scan_results(rdev, info, extra, data->length);
880 data->length = 0;
881 if (res >= 0) {
882 data->length = res;
883 res = 0;
884 }
885
886 out:
887 cfg80211_put_dev(rdev);
888 return res;
889}
890EXPORT_SYMBOL(cfg80211_wext_giwscan);
891#endif