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