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