linux/drivers/staging/brcm80211/brcmfmac/wl_iw.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Broadcom Corporation
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include <linux/kthread.h>
  18#include <linux/semaphore.h>
  19#include <bcmdefs.h>
  20#include <linux/netdevice.h>
  21#include <osl.h>
  22#include <wlioctl.h>
  23
  24#include <bcmutils.h>
  25#include <bcmendian.h>
  26#include <proto/ethernet.h>
  27
  28#include <linux/if_arp.h>
  29#include <asm/uaccess.h>
  30
  31#include <dngl_stats.h>
  32#include <dhd.h>
  33#include <dhdioctl.h>
  34
  35typedef const struct si_pub si_t;
  36#include <wlioctl.h>
  37
  38#include <proto/ethernet.h>
  39#include <dngl_stats.h>
  40#include <dhd.h>
  41
  42#define WL_ERROR(fmt, args...)  printk(fmt, ##args)
  43#define WL_TRACE(fmt, args...)  no_printk(fmt, ##args)
  44#define WL_INFORM(fmt, args...) no_printk(fmt, ##args)
  45#define WL_WSEC(fmt, args...)   no_printk(fmt, ##args)
  46#define WL_SCAN(fmt, args...)   no_printk(fmt, ##args)
  47
  48#include <wl_iw.h>
  49
  50#define IW_WSEC_ENABLED(wsec)   ((wsec) & (WEP_ENABLED |        \
  51                                         TKIP_ENABLED | AES_ENABLED))
  52
  53#include <linux/rtnetlink.h>
  54
  55#define WL_IW_USE_ISCAN  1
  56#define ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS  1
  57
  58bool g_set_essid_before_scan = true;
  59
  60#define WL_IW_IOCTL_CALL(func_call) \
  61        do {                            \
  62                func_call;              \
  63        } while (0)
  64
  65static int g_onoff = G_WLAN_SET_ON;
  66wl_iw_extra_params_t g_wl_iw_params;
  67
  68extern bool wl_iw_conn_status_str(u32 event_type, u32 status,
  69                                  u32 reason, char *stringBuf, uint buflen);
  70
  71uint wl_msg_level = WL_ERROR_VAL;
  72
  73#define MAX_WLIW_IOCTL_LEN 1024
  74
  75#if defined(IL_BIGENDIAN)
  76#include <bcmendian.h>
  77#define htod32(i) (bcmswap32(i))
  78#define htod16(i) (bcmswap16(i))
  79#define dtoh32(i) (bcmswap32(i))
  80#define dtoh16(i) (bcmswap16(i))
  81#define htodchanspec(i) htod16(i)
  82#define dtohchanspec(i) dtoh16(i)
  83#else
  84#define htod32(i) i
  85#define htod16(i) i
  86#define dtoh32(i) i
  87#define dtoh16(i) i
  88#define htodchanspec(i) i
  89#define dtohchanspec(i) i
  90#endif
  91
  92#ifdef CONFIG_WIRELESS_EXT
  93
  94extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
  95extern int dhd_wait_pend8021x(struct net_device *dev);
  96#endif
  97
  98#if WIRELESS_EXT < 19
  99#define IW_IOCTL_IDX(cmd)       ((cmd) - SIOCIWFIRST)
 100#define IW_EVENT_IDX(cmd)       ((cmd) - IWEVFIRST)
 101#endif
 102
 103static void *g_scan;
 104static volatile uint g_scan_specified_ssid;
 105static wlc_ssid_t g_specific_ssid;
 106
 107static wlc_ssid_t g_ssid;
 108
 109#if defined(WL_IW_USE_ISCAN)
 110#define ISCAN_STATE_IDLE   0
 111#define ISCAN_STATE_SCANING 1
 112
 113#define WLC_IW_ISCAN_MAXLEN   2048
 114typedef struct iscan_buf {
 115        struct iscan_buf *next;
 116        char iscan_buf[WLC_IW_ISCAN_MAXLEN];
 117} iscan_buf_t;
 118
 119typedef struct iscan_info {
 120        struct net_device *dev;
 121        struct timer_list timer;
 122        u32 timer_ms;
 123        u32 timer_on;
 124        int iscan_state;
 125        iscan_buf_t *list_hdr;
 126        iscan_buf_t *list_cur;
 127
 128        struct task_struct *sysioc_tsk;
 129        struct semaphore sysioc_sem;
 130
 131#if defined CSCAN
 132        char ioctlbuf[WLC_IOCTL_MEDLEN];
 133#else
 134        char ioctlbuf[WLC_IOCTL_SMLEN];
 135#endif
 136        wl_iscan_params_t *iscan_ex_params_p;
 137        int iscan_ex_param_size;
 138} iscan_info_t;
 139iscan_info_t *g_iscan;
 140static void wl_iw_timerfunc(unsigned long data);
 141static void wl_iw_set_event_mask(struct net_device *dev);
 142static int wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, u16 action);
 143#endif                          /* defined(WL_IW_USE_ISCAN) */
 144
 145static int
 146wl_iw_set_scan(struct net_device *dev,
 147               struct iw_request_info *info,
 148               union iwreq_data *wrqu, char *extra);
 149
 150static int
 151wl_iw_get_scan(struct net_device *dev,
 152               struct iw_request_info *info,
 153               struct iw_point *dwrq, char *extra);
 154
 155static uint
 156wl_iw_get_scan_prep(wl_scan_results_t *list,
 157                    struct iw_request_info *info, char *extra, short max_size);
 158
 159static void swap_key_from_BE(wl_wsec_key_t *key)
 160{
 161        key->index = htod32(key->index);
 162        key->len = htod32(key->len);
 163        key->algo = htod32(key->algo);
 164        key->flags = htod32(key->flags);
 165        key->rxiv.hi = htod32(key->rxiv.hi);
 166        key->rxiv.lo = htod16(key->rxiv.lo);
 167        key->iv_initialized = htod32(key->iv_initialized);
 168}
 169
 170static void swap_key_to_BE(wl_wsec_key_t *key)
 171{
 172        key->index = dtoh32(key->index);
 173        key->len = dtoh32(key->len);
 174        key->algo = dtoh32(key->algo);
 175        key->flags = dtoh32(key->flags);
 176        key->rxiv.hi = dtoh32(key->rxiv.hi);
 177        key->rxiv.lo = dtoh16(key->rxiv.lo);
 178        key->iv_initialized = dtoh32(key->iv_initialized);
 179}
 180
 181static int dev_wlc_ioctl(struct net_device *dev, int cmd, void *arg, int len)
 182{
 183        struct ifreq ifr;
 184        wl_ioctl_t ioc;
 185        mm_segment_t fs;
 186        int ret = -EINVAL;
 187
 188        if (!dev) {
 189                WL_ERROR("%s: dev is null\n", __func__);
 190                return ret;
 191        }
 192
 193        WL_INFORM("\n%s, PID:%x: send Local IOCTL -> dhd: cmd:0x%x, buf:%p, len:%d\n",
 194                  __func__, current->pid, cmd, arg, len);
 195
 196        if (g_onoff == G_WLAN_SET_ON) {
 197                memset(&ioc, 0, sizeof(ioc));
 198                ioc.cmd = cmd;
 199                ioc.buf = arg;
 200                ioc.len = len;
 201
 202                strcpy(ifr.ifr_name, dev->name);
 203                ifr.ifr_data = (caddr_t)&ioc;
 204
 205                ret = dev_open(dev);
 206                if (ret) {
 207                        WL_ERROR("%s: Error dev_open: %d\n", __func__, ret);
 208                        return ret;
 209                }
 210
 211                fs = get_fs();
 212                set_fs(get_ds());
 213                ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
 214                set_fs(fs);
 215        } else {
 216                WL_TRACE("%s: call after driver stop : ignored\n", __func__);
 217        }
 218        return ret;
 219}
 220
 221static int dev_wlc_intvar_set(struct net_device *dev, char *name, int val)
 222{
 223        char buf[WLC_IOCTL_SMLEN];
 224        uint len;
 225
 226        val = htod32(val);
 227        len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf));
 228        ASSERT(len);
 229
 230        return dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len);
 231}
 232
 233#if defined(WL_IW_USE_ISCAN)
 234static int
 235dev_iw_iovar_setbuf(struct net_device *dev,
 236                    char *iovar,
 237                    void *param, int paramlen, void *bufptr, int buflen)
 238{
 239        int iolen;
 240
 241        iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
 242        ASSERT(iolen);
 243
 244        if (iolen == 0)
 245                return 0;
 246
 247        return dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen);
 248}
 249
 250static int
 251dev_iw_iovar_getbuf(struct net_device *dev,
 252                    char *iovar,
 253                    void *param, int paramlen, void *bufptr, int buflen)
 254{
 255        int iolen;
 256
 257        iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
 258        ASSERT(iolen);
 259
 260        return dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen);
 261}
 262#endif                          /* defined(WL_IW_USE_ISCAN) */
 263
 264#if WIRELESS_EXT > 17
 265static int
 266dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len)
 267{
 268        static char ioctlbuf[MAX_WLIW_IOCTL_LEN];
 269        uint buflen;
 270
 271        buflen = bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf));
 272        ASSERT(buflen);
 273
 274        return dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen);
 275}
 276#endif                          /* WIRELESS_EXT > 17 */
 277
 278static int
 279dev_wlc_bufvar_get(struct net_device *dev, char *name, char *buf, int buflen)
 280{
 281        static char ioctlbuf[MAX_WLIW_IOCTL_LEN];
 282        int error;
 283        uint len;
 284
 285        len = bcm_mkiovar(name, NULL, 0, ioctlbuf, sizeof(ioctlbuf));
 286        ASSERT(len);
 287        error =
 288            dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf,
 289                          MAX_WLIW_IOCTL_LEN);
 290        if (!error)
 291                bcopy(ioctlbuf, buf, buflen);
 292
 293        return error;
 294}
 295
 296static int dev_wlc_intvar_get(struct net_device *dev, char *name, int *retval)
 297{
 298        union {
 299                char buf[WLC_IOCTL_SMLEN];
 300                int val;
 301        } var;
 302        int error;
 303
 304        uint len;
 305        uint data_null;
 306
 307        len =
 308            bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var),
 309                        sizeof(var.buf));
 310        ASSERT(len);
 311        error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
 312
 313        *retval = dtoh32(var.val);
 314
 315        return error;
 316}
 317
 318#if WIRELESS_EXT < 13
 319struct iw_request_info {
 320        __u16 cmd;
 321        __u16 flags;
 322};
 323
 324typedef int (*iw_handler) (struct net_device *dev,
 325                           struct iw_request_info *info,
 326                           void *wrqu, char *extra);
 327#endif
 328
 329static int
 330wl_iw_config_commit(struct net_device *dev,
 331                    struct iw_request_info *info, void *zwrq, char *extra)
 332{
 333        wlc_ssid_t ssid;
 334        int error;
 335        struct sockaddr bssid;
 336
 337        WL_TRACE("%s: SIOCSIWCOMMIT\n", dev->name);
 338
 339        error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid));
 340        if (error)
 341                return error;
 342
 343        ssid.SSID_len = dtoh32(ssid.SSID_len);
 344
 345        if (!ssid.SSID_len)
 346                return 0;
 347
 348        memset(&bssid, 0, sizeof(struct sockaddr));
 349        error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETH_ALEN);
 350        if (error) {
 351                WL_ERROR("%s: WLC_REASSOC to %s failed\n",
 352                         __func__, ssid.SSID);
 353                return error;
 354        }
 355
 356        return 0;
 357}
 358
 359static int
 360wl_iw_get_name(struct net_device *dev,
 361               struct iw_request_info *info, char *cwrq, char *extra)
 362{
 363        WL_TRACE("%s: SIOCGIWNAME\n", dev->name);
 364
 365        strcpy(cwrq, "IEEE 802.11-DS");
 366
 367        return 0;
 368}
 369
 370static int
 371wl_iw_set_freq(struct net_device *dev,
 372               struct iw_request_info *info, struct iw_freq *fwrq, char *extra)
 373{
 374        int error, chan;
 375        uint sf = 0;
 376
 377        WL_TRACE("\n %s %s: SIOCSIWFREQ\n", __func__, dev->name);
 378
 379        if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) {
 380                chan = fwrq->m;
 381        } else {
 382                if (fwrq->e >= 6) {
 383                        fwrq->e -= 6;
 384                        while (fwrq->e--)
 385                                fwrq->m *= 10;
 386                } else if (fwrq->e < 6) {
 387                        while (fwrq->e++ < 6)
 388                                fwrq->m /= 10;
 389                }
 390                if (fwrq->m > 4000 && fwrq->m < 5000)
 391                        sf = WF_CHAN_FACTOR_4_G;
 392
 393                chan = wf_mhz2channel(fwrq->m, sf);
 394        }
 395        chan = htod32(chan);
 396
 397        error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan));
 398        if (error)
 399                return error;
 400
 401        g_wl_iw_params.target_channel = chan;
 402        return -EINPROGRESS;
 403}
 404
 405static int
 406wl_iw_get_freq(struct net_device *dev,
 407               struct iw_request_info *info, struct iw_freq *fwrq, char *extra)
 408{
 409        channel_info_t ci;
 410        int error;
 411
 412        WL_TRACE("%s: SIOCGIWFREQ\n", dev->name);
 413
 414        error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci));
 415        if (error)
 416                return error;
 417
 418        fwrq->m = dtoh32(ci.hw_channel);
 419        fwrq->e = dtoh32(0);
 420        return 0;
 421}
 422
 423static int
 424wl_iw_set_mode(struct net_device *dev,
 425               struct iw_request_info *info, __u32 *uwrq, char *extra)
 426{
 427        int infra = 0, ap = 0, error = 0;
 428
 429        WL_TRACE("%s: SIOCSIWMODE\n", dev->name);
 430
 431        switch (*uwrq) {
 432        case IW_MODE_MASTER:
 433                infra = ap = 1;
 434                break;
 435        case IW_MODE_ADHOC:
 436        case IW_MODE_AUTO:
 437                break;
 438        case IW_MODE_INFRA:
 439                infra = 1;
 440                break;
 441        default:
 442                return -EINVAL;
 443        }
 444        infra = htod32(infra);
 445        ap = htod32(ap);
 446
 447        error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra));
 448        if (error)
 449                return error;
 450
 451        error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
 452        if (error)
 453                return error;
 454
 455        return -EINPROGRESS;
 456}
 457
 458static int
 459wl_iw_get_mode(struct net_device *dev,
 460               struct iw_request_info *info, __u32 *uwrq, char *extra)
 461{
 462        int error, infra = 0, ap = 0;
 463
 464        WL_TRACE("%s: SIOCGIWMODE\n", dev->name);
 465
 466        error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra));
 467        if (error)
 468                return error;
 469
 470        error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap));
 471        if (error)
 472                return error;
 473
 474        infra = dtoh32(infra);
 475        ap = dtoh32(ap);
 476        *uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC;
 477
 478        return 0;
 479}
 480
 481static int
 482wl_iw_get_range(struct net_device *dev,
 483                struct iw_request_info *info,
 484                struct iw_point *dwrq, char *extra)
 485{
 486        struct iw_range *range = (struct iw_range *)extra;
 487        wl_u32_list_t *list;
 488        wl_rateset_t rateset;
 489        s8 *channels;
 490        int error, i, k;
 491        uint sf, ch;
 492
 493        int phytype;
 494        int bw_cap = 0, sgi_tx = 0, nmode = 0;
 495        channel_info_t ci;
 496        u8 nrate_list2copy = 0;
 497        u16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130},
 498        {14, 29, 43, 58, 87, 116, 130, 144},
 499        {27, 54, 81, 108, 162, 216, 243, 270},
 500        {30, 60, 90, 120, 180, 240, 270, 300}
 501        };
 502
 503        WL_TRACE("%s: SIOCGIWRANGE\n", dev->name);
 504
 505        if (!extra)
 506                return -EINVAL;
 507
 508        channels = kmalloc((MAXCHANNEL + 1) * 4, GFP_KERNEL);
 509        if (!channels) {
 510                WL_ERROR("Could not alloc channels\n");
 511                return -ENOMEM;
 512        }
 513        list = (wl_u32_list_t *) channels;
 514
 515        dwrq->length = sizeof(struct iw_range);
 516        memset(range, 0, sizeof(range));
 517
 518        range->min_nwid = range->max_nwid = 0;
 519
 520        list->count = htod32(MAXCHANNEL);
 521        error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels,
 522                                (MAXCHANNEL + 1) * 4);
 523        if (error) {
 524                kfree(channels);
 525                return error;
 526        }
 527        for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) {
 528                range->freq[i].i = dtoh32(list->element[i]);
 529
 530                ch = dtoh32(list->element[i]);
 531                if (ch <= CH_MAX_2G_CHANNEL)
 532                        sf = WF_CHAN_FACTOR_2_4_G;
 533                else
 534                        sf = WF_CHAN_FACTOR_5_G;
 535
 536                range->freq[i].m = wf_channel2mhz(ch, sf);
 537                range->freq[i].e = 6;
 538        }
 539        range->num_frequency = range->num_channels = i;
 540
 541        range->max_qual.qual = 5;
 542        range->max_qual.level = 0x100 - 200;
 543        range->max_qual.noise = 0x100 - 200;
 544        range->sensitivity = 65535;
 545
 546#if WIRELESS_EXT > 11
 547        range->avg_qual.qual = 3;
 548        range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD;
 549        range->avg_qual.noise = 0x100 - 75;
 550#endif
 551
 552        error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset,
 553                                sizeof(rateset));
 554        if (error) {
 555                kfree(channels);
 556                return error;
 557        }
 558        rateset.count = dtoh32(rateset.count);
 559        range->num_bitrates = rateset.count;
 560        for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++)
 561                range->bitrate[i] = (rateset.rates[i] & 0x7f) * 500000;
 562        dev_wlc_intvar_get(dev, "nmode", &nmode);
 563        dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
 564
 565        if (nmode == 1 && phytype == WLC_PHY_TYPE_SSN) {
 566                dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap);
 567                dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx);
 568                dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci,
 569                              sizeof(channel_info_t));
 570                ci.hw_channel = dtoh32(ci.hw_channel);
 571
 572                if (bw_cap == 0 || (bw_cap == 2 && ci.hw_channel <= 14)) {
 573                        if (sgi_tx == 0)
 574                                nrate_list2copy = 0;
 575                        else
 576                                nrate_list2copy = 1;
 577                }
 578                if (bw_cap == 1 || (bw_cap == 2 && ci.hw_channel >= 36)) {
 579                        if (sgi_tx == 0)
 580                                nrate_list2copy = 2;
 581                        else
 582                                nrate_list2copy = 3;
 583                }
 584                range->num_bitrates += 8;
 585                for (k = 0; i < range->num_bitrates; k++, i++) {
 586                        range->bitrate[i] =
 587                            (nrate_list[nrate_list2copy][k]) * 500000;
 588                }
 589        }
 590
 591        error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i));
 592        if (error) {
 593                kfree(channels);
 594                return error;
 595        }
 596        i = dtoh32(i);
 597        if (i == WLC_PHY_TYPE_A)
 598                range->throughput = 24000000;
 599        else
 600                range->throughput = 1500000;
 601
 602        range->min_rts = 0;
 603        range->max_rts = 2347;
 604        range->min_frag = 256;
 605        range->max_frag = 2346;
 606
 607        range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS;
 608        range->num_encoding_sizes = 4;
 609        range->encoding_size[0] = WEP1_KEY_SIZE;
 610        range->encoding_size[1] = WEP128_KEY_SIZE;
 611#if WIRELESS_EXT > 17
 612        range->encoding_size[2] = TKIP_KEY_SIZE;
 613#else
 614        range->encoding_size[2] = 0;
 615#endif
 616        range->encoding_size[3] = AES_KEY_SIZE;
 617
 618        range->min_pmp = 0;
 619        range->max_pmp = 0;
 620        range->min_pmt = 0;
 621        range->max_pmt = 0;
 622        range->pmp_flags = 0;
 623        range->pm_capa = 0;
 624
 625        range->num_txpower = 2;
 626        range->txpower[0] = 1;
 627        range->txpower[1] = 255;
 628        range->txpower_capa = IW_TXPOW_MWATT;
 629
 630#if WIRELESS_EXT > 10
 631        range->we_version_compiled = WIRELESS_EXT;
 632        range->we_version_source = 19;
 633
 634        range->retry_capa = IW_RETRY_LIMIT;
 635        range->retry_flags = IW_RETRY_LIMIT;
 636        range->r_time_flags = 0;
 637        range->min_retry = 1;
 638        range->max_retry = 255;
 639        range->min_r_time = 0;
 640        range->max_r_time = 0;
 641#endif
 642
 643#if WIRELESS_EXT > 17
 644        range->enc_capa = IW_ENC_CAPA_WPA;
 645        range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
 646        range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
 647        range->enc_capa |= IW_ENC_CAPA_WPA2;
 648
 649        IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
 650        IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
 651        IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
 652        IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
 653        IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE);
 654        IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND);
 655#endif                          /* WIRELESS_EXT > 17 */
 656
 657        kfree(channels);
 658
 659        return 0;
 660}
 661
 662static int rssi_to_qual(int rssi)
 663{
 664        if (rssi <= WL_IW_RSSI_NO_SIGNAL)
 665                return 0;
 666        else if (rssi <= WL_IW_RSSI_VERY_LOW)
 667                return 1;
 668        else if (rssi <= WL_IW_RSSI_LOW)
 669                return 2;
 670        else if (rssi <= WL_IW_RSSI_GOOD)
 671                return 3;
 672        else if (rssi <= WL_IW_RSSI_VERY_GOOD)
 673                return 4;
 674        else
 675                return 5;
 676}
 677
 678static int
 679wl_iw_set_spy(struct net_device *dev,
 680              struct iw_request_info *info, struct iw_point *dwrq, char *extra)
 681{
 682        wl_iw_t *iw = *(wl_iw_t **) netdev_priv(dev);
 683        struct sockaddr *addr = (struct sockaddr *)extra;
 684        int i;
 685
 686        WL_TRACE("%s: SIOCSIWSPY\n", dev->name);
 687
 688        if (!extra)
 689                return -EINVAL;
 690
 691        iw->spy_num = min_t(int, ARRAY_SIZE(iw->spy_addr), dwrq->length);
 692        for (i = 0; i < iw->spy_num; i++)
 693                memcpy(&iw->spy_addr[i], addr[i].sa_data, ETH_ALEN);
 694        memset(iw->spy_qual, 0, sizeof(iw->spy_qual));
 695
 696        return 0;
 697}
 698
 699static int
 700wl_iw_get_spy(struct net_device *dev,
 701              struct iw_request_info *info, struct iw_point *dwrq, char *extra)
 702{
 703        wl_iw_t *iw = *(wl_iw_t **) netdev_priv(dev);
 704        struct sockaddr *addr = (struct sockaddr *)extra;
 705        struct iw_quality *qual = (struct iw_quality *)&addr[iw->spy_num];
 706        int i;
 707
 708        WL_TRACE("%s: SIOCGIWSPY\n", dev->name);
 709
 710        if (!extra)
 711                return -EINVAL;
 712
 713        dwrq->length = iw->spy_num;
 714        for (i = 0; i < iw->spy_num; i++) {
 715                memcpy(addr[i].sa_data, &iw->spy_addr[i], ETH_ALEN);
 716                addr[i].sa_family = AF_UNIX;
 717                memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality));
 718                iw->spy_qual[i].updated = 0;
 719        }
 720
 721        return 0;
 722}
 723
 724static int
 725wl_iw_ch_to_chanspec(int ch, wl_join_params_t *join_params,
 726                     int *join_params_size)
 727{
 728        chanspec_t chanspec = 0;
 729
 730        if (ch != 0) {
 731                join_params->params.chanspec_num = 1;
 732                join_params->params.chanspec_list[0] = ch;
 733
 734                if (join_params->params.chanspec_list[0])
 735                        chanspec |= WL_CHANSPEC_BAND_2G;
 736                else
 737                        chanspec |= WL_CHANSPEC_BAND_5G;
 738
 739                chanspec |= WL_CHANSPEC_BW_20;
 740                chanspec |= WL_CHANSPEC_CTL_SB_NONE;
 741
 742                *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
 743                    join_params->params.chanspec_num * sizeof(chanspec_t);
 744
 745                join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
 746                join_params->params.chanspec_list[0] |= chanspec;
 747                join_params->params.chanspec_list[0] =
 748                    htodchanspec(join_params->params.chanspec_list[0]);
 749
 750                join_params->params.chanspec_num =
 751                    htod32(join_params->params.chanspec_num);
 752
 753                WL_TRACE("%s  join_params->params.chanspec_list[0]= %X\n",
 754                         __func__, join_params->params.chanspec_list[0]);
 755        }
 756        return 1;
 757}
 758
 759static int
 760wl_iw_set_wap(struct net_device *dev,
 761              struct iw_request_info *info, struct sockaddr *awrq, char *extra)
 762{
 763        int error = -EINVAL;
 764        wl_join_params_t join_params;
 765        int join_params_size;
 766
 767        WL_TRACE("%s: SIOCSIWAP\n", dev->name);
 768
 769        if (awrq->sa_family != ARPHRD_ETHER) {
 770                WL_ERROR("Invalid Header...sa_family\n");
 771                return -EINVAL;
 772        }
 773
 774        if (is_broadcast_ether_addr(awrq->sa_data) ||
 775            is_zero_ether_addr(awrq->sa_data)) {
 776                scb_val_t scbval;
 777                memset(&scbval, 0, sizeof(scb_val_t));
 778                (void)dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval,
 779                                    sizeof(scb_val_t));
 780                return 0;
 781        }
 782
 783        memset(&join_params, 0, sizeof(join_params));
 784        join_params_size = sizeof(join_params.ssid);
 785
 786        memcpy(join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len);
 787        join_params.ssid.SSID_len = htod32(g_ssid.SSID_len);
 788        memcpy(&join_params.params.bssid, awrq->sa_data, ETH_ALEN);
 789
 790        WL_TRACE("%s  target_channel=%d\n",
 791                 __func__, g_wl_iw_params.target_channel);
 792        wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params,
 793                             &join_params_size);
 794
 795        error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params,
 796                                join_params_size);
 797        if (error) {
 798                WL_ERROR("%s Invalid ioctl data=%d\n", __func__, error);
 799        }
 800
 801        if (g_ssid.SSID_len) {
 802                WL_TRACE("%s: join SSID=%s BSSID=%pM ch=%d\n",
 803                         __func__, g_ssid.SSID, awrq->sa_data,
 804                         g_wl_iw_params.target_channel);
 805        }
 806
 807        memset(&g_ssid, 0, sizeof(g_ssid));
 808        return 0;
 809}
 810
 811static int
 812wl_iw_get_wap(struct net_device *dev,
 813              struct iw_request_info *info, struct sockaddr *awrq, char *extra)
 814{
 815        WL_TRACE("%s: SIOCGIWAP\n", dev->name);
 816
 817        awrq->sa_family = ARPHRD_ETHER;
 818        memset(awrq->sa_data, 0, ETH_ALEN);
 819
 820        (void)dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETH_ALEN);
 821
 822        return 0;
 823}
 824
 825#if WIRELESS_EXT > 17
 826static int
 827wl_iw_mlme(struct net_device *dev,
 828           struct iw_request_info *info, struct sockaddr *awrq, char *extra)
 829{
 830        struct iw_mlme *mlme;
 831        scb_val_t scbval;
 832        int error = -EINVAL;
 833
 834        WL_TRACE("%s: SIOCSIWMLME DISASSOC/DEAUTH\n", dev->name);
 835
 836        mlme = (struct iw_mlme *)extra;
 837        if (mlme == NULL) {
 838                WL_ERROR("Invalid ioctl data\n");
 839                return error;
 840        }
 841
 842        scbval.val = mlme->reason_code;
 843        bcopy(&mlme->addr.sa_data, &scbval.ea, ETH_ALEN);
 844
 845        if (mlme->cmd == IW_MLME_DISASSOC) {
 846                scbval.val = htod32(scbval.val);
 847                error =
 848                    dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval,
 849                                  sizeof(scb_val_t));
 850        } else if (mlme->cmd == IW_MLME_DEAUTH) {
 851                scbval.val = htod32(scbval.val);
 852                error =
 853                    dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON,
 854                                  &scbval, sizeof(scb_val_t));
 855        } else {
 856                WL_ERROR("Invalid ioctl data\n");
 857                return error;
 858        }
 859
 860        return error;
 861}
 862#endif                          /* WIRELESS_EXT > 17 */
 863
 864#ifndef WL_IW_USE_ISCAN
 865static int
 866wl_iw_get_aplist(struct net_device *dev,
 867                 struct iw_request_info *info,
 868                 struct iw_point *dwrq, char *extra)
 869{
 870        wl_scan_results_t *list;
 871        struct sockaddr *addr = (struct sockaddr *)extra;
 872        struct iw_quality qual[IW_MAX_AP];
 873        wl_bss_info_t *bi = NULL;
 874        int error, i;
 875        uint buflen = dwrq->length;
 876
 877        WL_TRACE("%s: SIOCGIWAPLIST\n", dev->name);
 878
 879        if (!extra)
 880                return -EINVAL;
 881
 882        list = kmalloc(buflen, GFP_KERNEL);
 883        if (!list)
 884                return -ENOMEM;
 885        memset(list, 0, buflen);
 886        list->buflen = htod32(buflen);
 887        error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen);
 888        if (error) {
 889                WL_ERROR("%d: Scan results error %d\n", __LINE__, error);
 890                kfree(list);
 891                return error;
 892        }
 893        list->buflen = dtoh32(list->buflen);
 894        list->version = dtoh32(list->version);
 895        list->count = dtoh32(list->count);
 896        if (list->version != WL_BSS_INFO_VERSION) {
 897                WL_ERROR("%s : list->version %d != WL_BSS_INFO_VERSION\n",
 898                         __func__, list->version);
 899                kfree(list);
 900                return -EINVAL;
 901        }
 902
 903        for (i = 0, dwrq->length = 0;
 904             i < list->count && dwrq->length < IW_MAX_AP; i++) {
 905                bi = bi ? (wl_bss_info_t *) ((unsigned long)bi +
 906                                             dtoh32(bi->length)) : list->
 907                    bss_info;
 908                ASSERT(((unsigned long)bi + dtoh32(bi->length)) <=
 909                       ((unsigned long)list + buflen));
 910
 911                if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
 912                        continue;
 913
 914                memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETH_ALEN);
 915                addr[dwrq->length].sa_family = ARPHRD_ETHER;
 916                qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
 917                qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
 918                qual[dwrq->length].noise = 0x100 + bi->phy_noise;
 919
 920#if WIRELESS_EXT > 18
 921                qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
 922#else
 923                qual[dwrq->length].updated = 7;
 924#endif
 925                dwrq->length++;
 926        }
 927
 928        kfree(list);
 929
 930        if (dwrq->length) {
 931                memcpy(&addr[dwrq->length], qual,
 932                       sizeof(struct iw_quality) * dwrq->length);
 933                dwrq->flags = 1;
 934        }
 935
 936        return 0;
 937}
 938#endif                          /* WL_IW_USE_ISCAN */
 939
 940#ifdef WL_IW_USE_ISCAN
 941static int
 942wl_iw_iscan_get_aplist(struct net_device *dev,
 943                       struct iw_request_info *info,
 944                       struct iw_point *dwrq, char *extra)
 945{
 946        wl_scan_results_t *list;
 947        iscan_buf_t *buf;
 948        iscan_info_t *iscan = g_iscan;
 949
 950        struct sockaddr *addr = (struct sockaddr *)extra;
 951        struct iw_quality qual[IW_MAX_AP];
 952        wl_bss_info_t *bi = NULL;
 953        int i;
 954
 955        WL_TRACE("%s: SIOCGIWAPLIST\n", dev->name);
 956
 957        if (!extra)
 958                return -EINVAL;
 959
 960        if ((!iscan) || (!iscan->sysioc_tsk)) {
 961                WL_ERROR("%s error\n", __func__);
 962                return 0;
 963        }
 964
 965        buf = iscan->list_hdr;
 966        while (buf) {
 967                list = &((wl_iscan_results_t *) buf->iscan_buf)->results;
 968                if (list->version != WL_BSS_INFO_VERSION) {
 969                        WL_ERROR("%s : list->version %d != WL_BSS_INFO_VERSION\n",
 970                                 __func__, list->version);
 971                        return -EINVAL;
 972                }
 973
 974                bi = NULL;
 975                for (i = 0, dwrq->length = 0;
 976                     i < list->count && dwrq->length < IW_MAX_AP; i++) {
 977                        bi = bi ? (wl_bss_info_t *) ((unsigned long)bi +
 978                                                     dtoh32(bi->length)) :
 979                            list->bss_info;
 980                        ASSERT(((unsigned long)bi + dtoh32(bi->length)) <=
 981                               ((unsigned long)list + WLC_IW_ISCAN_MAXLEN));
 982
 983                        if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
 984                                continue;
 985
 986                        memcpy(addr[dwrq->length].sa_data, &bi->BSSID,
 987                               ETH_ALEN);
 988                        addr[dwrq->length].sa_family = ARPHRD_ETHER;
 989                        qual[dwrq->length].qual =
 990                            rssi_to_qual(dtoh16(bi->RSSI));
 991                        qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
 992                        qual[dwrq->length].noise = 0x100 + bi->phy_noise;
 993
 994#if WIRELESS_EXT > 18
 995                        qual[dwrq->length].updated =
 996                            IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
 997#else
 998                        qual[dwrq->length].updated = 7;
 999#endif
1000
1001                        dwrq->length++;
1002                }
1003                buf = buf->next;
1004        }
1005        if (dwrq->length) {
1006                memcpy(&addr[dwrq->length], qual,
1007                       sizeof(struct iw_quality) * dwrq->length);
1008                dwrq->flags = 1;
1009        }
1010
1011        return 0;
1012}
1013
1014static int wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid)
1015{
1016        int err = 0;
1017
1018        memcpy(&params->bssid, &ether_bcast, ETH_ALEN);
1019        params->bss_type = DOT11_BSSTYPE_ANY;
1020        params->scan_type = 0;
1021        params->nprobes = -1;
1022        params->active_time = -1;
1023        params->passive_time = -1;
1024        params->home_time = -1;
1025        params->channel_num = 0;
1026
1027        params->nprobes = htod32(params->nprobes);
1028        params->active_time = htod32(params->active_time);
1029        params->passive_time = htod32(params->passive_time);
1030        params->home_time = htod32(params->home_time);
1031        if (ssid && ssid->SSID_len)
1032                memcpy(&params->ssid, ssid, sizeof(wlc_ssid_t));
1033
1034        return err;
1035}
1036
1037static int wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, u16 action)
1038{
1039        int err = 0;
1040
1041        iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION);
1042        iscan->iscan_ex_params_p->action = htod16(action);
1043        iscan->iscan_ex_params_p->scan_duration = htod16(0);
1044
1045        WL_SCAN("%s : nprobes=%d\n",
1046                __func__, iscan->iscan_ex_params_p->params.nprobes);
1047        WL_SCAN("active_time=%d\n",
1048                 iscan->iscan_ex_params_p->params.active_time);
1049        WL_SCAN("passive_time=%d\n",
1050                 iscan->iscan_ex_params_p->params.passive_time);
1051        WL_SCAN("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time);
1052        WL_SCAN("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type);
1053        WL_SCAN("bss_type=%d\n", iscan->iscan_ex_params_p->params.bss_type);
1054
1055        (void)dev_iw_iovar_setbuf(iscan->dev, "iscan", iscan->iscan_ex_params_p,
1056                                  iscan->iscan_ex_param_size, iscan->ioctlbuf,
1057                                  sizeof(iscan->ioctlbuf));
1058
1059        return err;
1060}
1061
1062static void wl_iw_timerfunc(unsigned long data)
1063{
1064        iscan_info_t *iscan = (iscan_info_t *) data;
1065        if (iscan) {
1066                iscan->timer_on = 0;
1067                if (iscan->iscan_state != ISCAN_STATE_IDLE) {
1068                        WL_TRACE("timer trigger\n");
1069                        up(&iscan->sysioc_sem);
1070                }
1071        }
1072}
1073
1074static void wl_iw_set_event_mask(struct net_device *dev)
1075{
1076        char eventmask[WL_EVENTING_MASK_LEN];
1077        char iovbuf[WL_EVENTING_MASK_LEN + 12];
1078
1079        dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf));
1080        bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN);
1081        setbit(eventmask, WLC_E_SCAN_COMPLETE);
1082        dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN,
1083                            iovbuf, sizeof(iovbuf));
1084}
1085
1086static u32 wl_iw_iscan_get(iscan_info_t *iscan)
1087{
1088        iscan_buf_t *buf;
1089        iscan_buf_t *ptr;
1090        wl_iscan_results_t *list_buf;
1091        wl_iscan_results_t list;
1092        wl_scan_results_t *results;
1093        u32 status;
1094        int res = 0;
1095
1096        MUTEX_LOCK_WL_SCAN_SET();
1097        if (iscan->list_cur) {
1098                buf = iscan->list_cur;
1099                iscan->list_cur = buf->next;
1100        } else {
1101                buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL);
1102                if (!buf) {
1103                        WL_ERROR("%s can't alloc iscan_buf_t : going to abort current iscan\n",
1104                                 __func__);
1105                        MUTEX_UNLOCK_WL_SCAN_SET();
1106                        return WL_SCAN_RESULTS_NO_MEM;
1107                }
1108                buf->next = NULL;
1109                if (!iscan->list_hdr)
1110                        iscan->list_hdr = buf;
1111                else {
1112                        ptr = iscan->list_hdr;
1113                        while (ptr->next) {
1114                                ptr = ptr->next;
1115                        }
1116                        ptr->next = buf;
1117                }
1118        }
1119        memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
1120        list_buf = (wl_iscan_results_t *) buf->iscan_buf;
1121        results = &list_buf->results;
1122        results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
1123        results->version = 0;
1124        results->count = 0;
1125
1126        memset(&list, 0, sizeof(list));
1127        list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
1128        res = dev_iw_iovar_getbuf(iscan->dev,
1129                                  "iscanresults",
1130                                  &list,
1131                                  WL_ISCAN_RESULTS_FIXED_SIZE,
1132                                  buf->iscan_buf, WLC_IW_ISCAN_MAXLEN);
1133        if (res == 0) {
1134                results->buflen = dtoh32(results->buflen);
1135                results->version = dtoh32(results->version);
1136                results->count = dtoh32(results->count);
1137                WL_TRACE("results->count = %d\n", results->count);
1138                WL_TRACE("results->buflen = %d\n", results->buflen);
1139                status = dtoh32(list_buf->status);
1140        } else {
1141                WL_ERROR("%s returns error %d\n", __func__, res);
1142                status = WL_SCAN_RESULTS_NO_MEM;
1143        }
1144        MUTEX_UNLOCK_WL_SCAN_SET();
1145        return status;
1146}
1147
1148static void wl_iw_force_specific_scan(iscan_info_t *iscan)
1149{
1150        WL_TRACE("%s force Specific SCAN for %s\n",
1151                 __func__, g_specific_ssid.SSID);
1152        rtnl_lock();
1153
1154        (void)dev_wlc_ioctl(iscan->dev, WLC_SCAN, &g_specific_ssid,
1155                            sizeof(g_specific_ssid));
1156
1157        rtnl_unlock();
1158}
1159
1160static void wl_iw_send_scan_complete(iscan_info_t *iscan)
1161{
1162#ifndef SANDGATE2G
1163        union iwreq_data wrqu;
1164
1165        memset(&wrqu, 0, sizeof(wrqu));
1166
1167        wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL);
1168        WL_TRACE("Send Event ISCAN complete\n");
1169#endif
1170}
1171
1172static int _iscan_sysioc_thread(void *data)
1173{
1174        u32 status;
1175        iscan_info_t *iscan = (iscan_info_t *) data;
1176        static bool iscan_pass_abort = false;
1177
1178        allow_signal(SIGTERM);
1179        status = WL_SCAN_RESULTS_PARTIAL;
1180        while (down_interruptible(&iscan->sysioc_sem) == 0) {
1181                if (kthread_should_stop())
1182                        break;
1183
1184                if (iscan->timer_on) {
1185                        del_timer_sync(&iscan->timer);
1186                        iscan->timer_on = 0;
1187                }
1188                rtnl_lock();
1189                status = wl_iw_iscan_get(iscan);
1190                rtnl_unlock();
1191                if (g_scan_specified_ssid && (iscan_pass_abort == true)) {
1192                        WL_TRACE("%s Get results from specific scan status = %d\n",
1193                                 __func__, status);
1194                        wl_iw_send_scan_complete(iscan);
1195                        iscan_pass_abort = false;
1196                        status = -1;
1197                }
1198
1199                switch (status) {
1200                case WL_SCAN_RESULTS_PARTIAL:
1201                        WL_TRACE("iscanresults incomplete\n");
1202                        rtnl_lock();
1203                        wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE);
1204                        rtnl_unlock();
1205                        mod_timer(&iscan->timer,
1206                                  jiffies + iscan->timer_ms * HZ / 1000);
1207                        iscan->timer_on = 1;
1208                        break;
1209                case WL_SCAN_RESULTS_SUCCESS:
1210                        WL_TRACE("iscanresults complete\n");
1211                        iscan->iscan_state = ISCAN_STATE_IDLE;
1212                        wl_iw_send_scan_complete(iscan);
1213                        break;
1214                case WL_SCAN_RESULTS_PENDING:
1215                        WL_TRACE("iscanresults pending\n");
1216                        mod_timer(&iscan->timer,
1217                                  jiffies + iscan->timer_ms * HZ / 1000);
1218                        iscan->timer_on = 1;
1219                        break;
1220                case WL_SCAN_RESULTS_ABORTED:
1221                        WL_TRACE("iscanresults aborted\n");
1222                        iscan->iscan_state = ISCAN_STATE_IDLE;
1223                        if (g_scan_specified_ssid == 0)
1224                                wl_iw_send_scan_complete(iscan);
1225                        else {
1226                                iscan_pass_abort = true;
1227                                wl_iw_force_specific_scan(iscan);
1228                        }
1229                        break;
1230                case WL_SCAN_RESULTS_NO_MEM:
1231                        WL_TRACE("iscanresults can't alloc memory: skip\n");
1232                        iscan->iscan_state = ISCAN_STATE_IDLE;
1233                        break;
1234                default:
1235                        WL_TRACE("iscanresults returned unknown status %d\n",
1236                                 status);
1237                        break;
1238                }
1239        }
1240
1241        if (iscan->timer_on) {
1242                del_timer_sync(&iscan->timer);
1243                iscan->timer_on = 0;
1244        }
1245        return 0;
1246}
1247#endif                          /* WL_IW_USE_ISCAN */
1248
1249static int
1250wl_iw_set_scan(struct net_device *dev,
1251               struct iw_request_info *info,
1252               union iwreq_data *wrqu, char *extra)
1253{
1254        int error;
1255        WL_TRACE("\n:%s dev:%s: SIOCSIWSCAN : SCAN\n", __func__, dev->name);
1256
1257        g_set_essid_before_scan = false;
1258#if defined(CSCAN)
1259        WL_ERROR("%s: Scan from SIOCGIWSCAN not supported\n", __func__);
1260        return -EINVAL;
1261#endif
1262
1263        if (g_onoff == G_WLAN_SET_OFF)
1264                return 0;
1265
1266        memset(&g_specific_ssid, 0, sizeof(g_specific_ssid));
1267#ifndef WL_IW_USE_ISCAN
1268        g_scan_specified_ssid = 0;
1269#endif
1270
1271#if WIRELESS_EXT > 17
1272        if (wrqu->data.length == sizeof(struct iw_scan_req)) {
1273                if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
1274                        struct iw_scan_req *req = (struct iw_scan_req *)extra;
1275                        if (g_scan_specified_ssid) {
1276                                WL_TRACE("%s Specific SCAN is not done ignore scan for = %s\n",
1277                                         __func__, req->essid);
1278                                return -EBUSY;
1279                        } else {
1280                                g_specific_ssid.SSID_len = min_t(size_t,
1281                                                sizeof(g_specific_ssid.SSID),
1282                                                req->essid_len);
1283                                memcpy(g_specific_ssid.SSID, req->essid,
1284                                       g_specific_ssid.SSID_len);
1285                                g_specific_ssid.SSID_len =
1286                                    htod32(g_specific_ssid.SSID_len);
1287                                g_scan_specified_ssid = 1;
1288                                WL_TRACE("### Specific scan ssid=%s len=%d\n",
1289                                         g_specific_ssid.SSID,
1290                                         g_specific_ssid.SSID_len);
1291                        }
1292                }
1293        }
1294#endif                          /* WIRELESS_EXT > 17 */
1295        error = dev_wlc_ioctl(dev, WLC_SCAN, &g_specific_ssid,
1296                                sizeof(g_specific_ssid));
1297        if (error) {
1298                WL_TRACE("#### Set SCAN for %s failed with %d\n",
1299                         g_specific_ssid.SSID, error);
1300                g_scan_specified_ssid = 0;
1301                return -EBUSY;
1302        }
1303
1304        return 0;
1305}
1306
1307#ifdef WL_IW_USE_ISCAN
1308int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag)
1309{
1310        wlc_ssid_t ssid;
1311        iscan_info_t *iscan = g_iscan;
1312
1313        if (flag)
1314                rtnl_lock();
1315
1316        wl_iw_set_event_mask(dev);
1317
1318        WL_TRACE("+++: Set Broadcast ISCAN\n");
1319        memset(&ssid, 0, sizeof(ssid));
1320
1321        iscan->list_cur = iscan->list_hdr;
1322        iscan->iscan_state = ISCAN_STATE_SCANING;
1323
1324        memset(&iscan->iscan_ex_params_p->params, 0,
1325               iscan->iscan_ex_param_size);
1326        wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, &ssid);
1327        wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START);
1328
1329        if (flag)
1330                rtnl_unlock();
1331
1332        mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
1333
1334        iscan->timer_on = 1;
1335
1336        return 0;
1337}
1338
1339static int
1340wl_iw_iscan_set_scan(struct net_device *dev,
1341                     struct iw_request_info *info,
1342                     union iwreq_data *wrqu, char *extra)
1343{
1344        wlc_ssid_t ssid;
1345        iscan_info_t *iscan = g_iscan;
1346
1347        WL_TRACE("%s: SIOCSIWSCAN : ISCAN\n", dev->name);
1348
1349#if defined(CSCAN)
1350        WL_ERROR("%s: Scan from SIOCGIWSCAN not supported\n", __func__);
1351        return -EINVAL;
1352#endif
1353
1354        if (g_onoff == G_WLAN_SET_OFF) {
1355                WL_TRACE("%s: driver is not up yet after START\n", __func__);
1356                return 0;
1357        }
1358#ifdef PNO_SUPPORT
1359        if (dhd_dev_get_pno_status(dev)) {
1360                WL_ERROR("%s: Scan called when PNO is active\n", __func__);
1361        }
1362#endif
1363
1364        if ((!iscan) || (!iscan->sysioc_tsk))
1365                return wl_iw_set_scan(dev, info, wrqu, extra);
1366
1367        if (g_scan_specified_ssid) {
1368                WL_TRACE("%s Specific SCAN already running ignoring BC scan\n",
1369                         __func__);
1370                return EBUSY;
1371        }
1372
1373        memset(&ssid, 0, sizeof(ssid));
1374
1375#if WIRELESS_EXT > 17
1376        if (wrqu->data.length == sizeof(struct iw_scan_req)) {
1377                if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
1378                        struct iw_scan_req *req = (struct iw_scan_req *)extra;
1379                        ssid.SSID_len = min_t(size_t, sizeof(ssid.SSID),
1380                                                req->essid_len);
1381                        memcpy(ssid.SSID, req->essid, ssid.SSID_len);
1382                        ssid.SSID_len = htod32(ssid.SSID_len);
1383                } else {
1384                        g_scan_specified_ssid = 0;
1385
1386                        if (iscan->iscan_state == ISCAN_STATE_SCANING) {
1387                                WL_TRACE("%s ISCAN already in progress\n",
1388                                         __func__);
1389                                return 0;
1390                        }
1391                }
1392        }
1393#endif                          /* WIRELESS_EXT > 17 */
1394        wl_iw_iscan_set_scan_broadcast_prep(dev, 0);
1395
1396        return 0;
1397}
1398#endif                          /* WL_IW_USE_ISCAN */
1399
1400#if WIRELESS_EXT > 17
1401static bool ie_is_wpa_ie(u8 **wpaie, u8 **tlvs, int *tlvs_len)
1402{
1403
1404        u8 *ie = *wpaie;
1405
1406        if ((ie[1] >= 6) &&
1407            !memcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) {
1408                return true;
1409        }
1410
1411        ie += ie[1] + 2;
1412        *tlvs_len -= (int)(ie - *tlvs);
1413        *tlvs = ie;
1414        return false;
1415}
1416
1417static bool ie_is_wps_ie(u8 **wpsie, u8 **tlvs, int *tlvs_len)
1418{
1419
1420        u8 *ie = *wpsie;
1421
1422        if ((ie[1] >= 4) &&
1423            !memcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) {
1424                return true;
1425        }
1426
1427        ie += ie[1] + 2;
1428        *tlvs_len -= (int)(ie - *tlvs);
1429        *tlvs = ie;
1430        return false;
1431}
1432#endif                          /* WIRELESS_EXT > 17 */
1433
1434static int
1435wl_iw_handle_scanresults_ies(char **event_p, char *end,
1436                             struct iw_request_info *info, wl_bss_info_t *bi)
1437{
1438#if WIRELESS_EXT > 17
1439        struct iw_event iwe;
1440        char *event;
1441
1442        event = *event_p;
1443        if (bi->ie_length) {
1444                bcm_tlv_t *ie;
1445                u8 *ptr = ((u8 *) bi) + sizeof(wl_bss_info_t);
1446                int ptr_len = bi->ie_length;
1447
1448                ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID);
1449                if (ie) {
1450                        iwe.cmd = IWEVGENIE;
1451                        iwe.u.data.length = ie->len + 2;
1452                        event =
1453                            IWE_STREAM_ADD_POINT(info, event, end, &iwe,
1454                                                 (char *)ie);
1455                }
1456                ptr = ((u8 *) bi) + sizeof(wl_bss_info_t);
1457
1458                while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
1459                        if (ie_is_wps_ie(((u8 **)&ie), &ptr, &ptr_len)) {
1460                                iwe.cmd = IWEVGENIE;
1461                                iwe.u.data.length = ie->len + 2;
1462                                event =
1463                                    IWE_STREAM_ADD_POINT(info, event, end, &iwe,
1464                                                         (char *)ie);
1465                                break;
1466                        }
1467                }
1468
1469                ptr = ((u8 *) bi) + sizeof(wl_bss_info_t);
1470                ptr_len = bi->ie_length;
1471                while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
1472                        if (ie_is_wpa_ie(((u8 **)&ie), &ptr, &ptr_len)) {
1473                                iwe.cmd = IWEVGENIE;
1474                                iwe.u.data.length = ie->len + 2;
1475                                event =
1476                                    IWE_STREAM_ADD_POINT(info, event, end, &iwe,
1477                                                         (char *)ie);
1478                                break;
1479                        }
1480                }
1481
1482                *event_p = event;
1483        }
1484#endif          /* WIRELESS_EXT > 17 */
1485        return 0;
1486}
1487
1488static uint
1489wl_iw_get_scan_prep(wl_scan_results_t *list,
1490                    struct iw_request_info *info, char *extra, short max_size)
1491{
1492        int i, j;
1493        struct iw_event iwe;
1494        wl_bss_info_t *bi = NULL;
1495        char *event = extra, *end = extra + max_size - WE_ADD_EVENT_FIX, *value;
1496        int ret = 0;
1497
1498        ASSERT(list);
1499
1500        for (i = 0; i < list->count && i < IW_MAX_AP; i++) {
1501                if (list->version != WL_BSS_INFO_VERSION) {
1502                        WL_ERROR("%s : list->version %d != WL_BSS_INFO_VERSION\n",
1503                                 __func__, list->version);
1504                        return ret;
1505                }
1506
1507                bi = bi ? (wl_bss_info_t *)((unsigned long)bi +
1508                                             dtoh32(bi->length)) : list->
1509                    bss_info;
1510
1511                WL_TRACE("%s : %s\n", __func__, bi->SSID);
1512
1513                iwe.cmd = SIOCGIWAP;
1514                iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1515                memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETH_ALEN);
1516                event =
1517                    IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1518                                         IW_EV_ADDR_LEN);
1519                iwe.u.data.length = dtoh32(bi->SSID_len);
1520                iwe.cmd = SIOCGIWESSID;
1521                iwe.u.data.flags = 1;
1522                event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
1523
1524                if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
1525                        iwe.cmd = SIOCGIWMODE;
1526                        if (dtoh16(bi->capability) & DOT11_CAP_ESS)
1527                                iwe.u.mode = IW_MODE_INFRA;
1528                        else
1529                                iwe.u.mode = IW_MODE_ADHOC;
1530                        event =
1531                            IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1532                                                 IW_EV_UINT_LEN);
1533                }
1534
1535                iwe.cmd = SIOCGIWFREQ;
1536                iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec),
1537                                              CHSPEC_CHANNEL(bi->chanspec) <=
1538                                              CH_MAX_2G_CHANNEL ?
1539                                              WF_CHAN_FACTOR_2_4_G :
1540                                              WF_CHAN_FACTOR_5_G);
1541                iwe.u.freq.e = 6;
1542                event =
1543                    IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1544                                         IW_EV_FREQ_LEN);
1545
1546                iwe.cmd = IWEVQUAL;
1547                iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
1548                iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
1549                iwe.u.qual.noise = 0x100 + bi->phy_noise;
1550                event =
1551                    IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1552                                         IW_EV_QUAL_LEN);
1553
1554                wl_iw_handle_scanresults_ies(&event, end, info, bi);
1555
1556                iwe.cmd = SIOCGIWENCODE;
1557                if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
1558                        iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1559                else
1560                        iwe.u.data.flags = IW_ENCODE_DISABLED;
1561                iwe.u.data.length = 0;
1562                event =
1563                    IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
1564
1565                if (bi->rateset.count) {
1566                        if (((event - extra) +
1567                                IW_EV_LCP_LEN) <= (unsigned long)end) {
1568                                value = event + IW_EV_LCP_LEN;
1569                                iwe.cmd = SIOCGIWRATE;
1570                                iwe.u.bitrate.fixed = iwe.u.bitrate.disabled =
1571                                    0;
1572                                for (j = 0;
1573                                     j < bi->rateset.count
1574                                     && j < IW_MAX_BITRATES; j++) {
1575                                        iwe.u.bitrate.value =
1576                                            (bi->rateset.rates[j] & 0x7f) *
1577                                            500000;
1578                                        value =
1579                                            IWE_STREAM_ADD_VALUE(info, event,
1580                                                 value, end, &iwe,
1581                                                 IW_EV_PARAM_LEN);
1582                                }
1583                                event = value;
1584                        }
1585                }
1586        }
1587
1588        ret = event - extra;
1589        if (ret < 0) {
1590                WL_ERROR("==> Wrong size\n");
1591                ret = 0;
1592        }
1593        WL_TRACE("%s: size=%d bytes prepared\n",
1594                 __func__, (unsigned int)(event - extra));
1595        return (uint)ret;
1596}
1597
1598static int
1599wl_iw_get_scan(struct net_device *dev,
1600               struct iw_request_info *info, struct iw_point *dwrq, char *extra)
1601{
1602        channel_info_t ci;
1603        wl_scan_results_t *list_merge;
1604        wl_scan_results_t *list = (wl_scan_results_t *) g_scan;
1605        int error;
1606        uint buflen_from_user = dwrq->length;
1607        uint len = G_SCAN_RESULTS;
1608        __u16 len_ret = 0;
1609#if defined(WL_IW_USE_ISCAN)
1610        iscan_info_t *iscan = g_iscan;
1611        iscan_buf_t *p_buf;
1612#endif
1613
1614        WL_TRACE("%s: buflen_from_user %d:\n", dev->name, buflen_from_user);
1615
1616        if (!extra) {
1617                WL_TRACE("%s: wl_iw_get_scan return -EINVAL\n", dev->name);
1618                return -EINVAL;
1619        }
1620
1621        error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci));
1622        if (error)
1623                return error;
1624        ci.scan_channel = dtoh32(ci.scan_channel);
1625        if (ci.scan_channel)
1626                return -EAGAIN;
1627
1628        if (g_scan_specified_ssid) {
1629                list = kmalloc(len, GFP_KERNEL);
1630                if (!list) {
1631                        WL_TRACE("%s: wl_iw_get_scan return -ENOMEM\n",
1632                                 dev->name);
1633                        g_scan_specified_ssid = 0;
1634                        return -ENOMEM;
1635                }
1636        }
1637
1638        memset(list, 0, len);
1639        list->buflen = htod32(len);
1640        error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, len);
1641        if (error) {
1642                WL_ERROR("%s: %s : Scan_results ERROR %d\n",
1643                         dev->name, __func__, error);
1644                dwrq->length = len;
1645                if (g_scan_specified_ssid) {
1646                        g_scan_specified_ssid = 0;
1647                        kfree(list);
1648                }
1649                return 0;
1650        }
1651        list->buflen = dtoh32(list->buflen);
1652        list->version = dtoh32(list->version);
1653        list->count = dtoh32(list->count);
1654
1655        if (list->version != WL_BSS_INFO_VERSION) {
1656                WL_ERROR("%s : list->version %d != WL_BSS_INFO_VERSION\n",
1657                         __func__, list->version);
1658                if (g_scan_specified_ssid) {
1659                        g_scan_specified_ssid = 0;
1660                        kfree(list);
1661                }
1662                return -EINVAL;
1663        }
1664
1665        if (g_scan_specified_ssid) {
1666                WL_TRACE("%s: Specified scan APs in the list =%d\n",
1667                         __func__, list->count);
1668                len_ret =
1669                    (__u16) wl_iw_get_scan_prep(list, info, extra,
1670                                                buflen_from_user);
1671                kfree(list);
1672
1673#if defined(WL_IW_USE_ISCAN)
1674                p_buf = iscan->list_hdr;
1675                while (p_buf != iscan->list_cur) {
1676                        list_merge =
1677                            &((wl_iscan_results_t *) p_buf->iscan_buf)->results;
1678                        WL_TRACE("%s: Bcast APs list=%d\n",
1679                                 __func__, list_merge->count);
1680                        if (list_merge->count > 0)
1681                                len_ret +=
1682                                    (__u16) wl_iw_get_scan_prep(list_merge,
1683                                        info, extra + len_ret,
1684                                        buflen_from_user - len_ret);
1685                        p_buf = p_buf->next;
1686                }
1687#else
1688                list_merge = (wl_scan_results_t *) g_scan;
1689                WL_TRACE("%s: Bcast APs list=%d\n",
1690                         __func__, list_merge->count);
1691                if (list_merge->count > 0)
1692                        len_ret +=
1693                            (__u16) wl_iw_get_scan_prep(list_merge, info,
1694                                                        extra + len_ret,
1695                                                        buflen_from_user -
1696                                                        len_ret);
1697#endif                          /* defined(WL_IW_USE_ISCAN) */
1698        } else {
1699                list = (wl_scan_results_t *) g_scan;
1700                len_ret =
1701                    (__u16) wl_iw_get_scan_prep(list, info, extra,
1702                                                buflen_from_user);
1703        }
1704
1705#if defined(WL_IW_USE_ISCAN)
1706        g_scan_specified_ssid = 0;
1707#endif
1708        if ((len_ret + WE_ADD_EVENT_FIX) < buflen_from_user)
1709                len = len_ret;
1710
1711        dwrq->length = len;
1712        dwrq->flags = 0;
1713
1714        WL_TRACE("%s return to WE %d bytes APs=%d\n",
1715                 __func__, dwrq->length, list->count);
1716        return 0;
1717}
1718
1719#if defined(WL_IW_USE_ISCAN)
1720static int
1721wl_iw_iscan_get_scan(struct net_device *dev,
1722                     struct iw_request_info *info,
1723                     struct iw_point *dwrq, char *extra)
1724{
1725        wl_scan_results_t *list;
1726        struct iw_event iwe;
1727        wl_bss_info_t *bi = NULL;
1728        int ii, j;
1729        int apcnt;
1730        char *event = extra, *end = extra + dwrq->length, *value;
1731        iscan_info_t *iscan = g_iscan;
1732        iscan_buf_t *p_buf;
1733        u32 counter = 0;
1734        u8 channel;
1735
1736        WL_TRACE("%s %s buflen_from_user %d:\n",
1737                 dev->name, __func__, dwrq->length);
1738
1739        if (!extra) {
1740                WL_TRACE("%s: INVALID SIOCGIWSCAN GET bad parameter\n",
1741                         dev->name);
1742                return -EINVAL;
1743        }
1744
1745        if ((!iscan) || (!iscan->sysioc_tsk)) {
1746                WL_ERROR("%ssysioc_tsk\n", __func__);
1747                return wl_iw_get_scan(dev, info, dwrq, extra);
1748        }
1749
1750        if (iscan->iscan_state == ISCAN_STATE_SCANING) {
1751                WL_TRACE("%s: SIOCGIWSCAN GET still scanning\n", dev->name);
1752                return -EAGAIN;
1753        }
1754
1755        WL_TRACE("%s: SIOCGIWSCAN GET broadcast results\n", dev->name);
1756        apcnt = 0;
1757        p_buf = iscan->list_hdr;
1758        while (p_buf != iscan->list_cur) {
1759                list = &((wl_iscan_results_t *) p_buf->iscan_buf)->results;
1760
1761                counter += list->count;
1762
1763                if (list->version != WL_BSS_INFO_VERSION) {
1764                        WL_ERROR("%s : list->version %d != WL_BSS_INFO_VERSION\n",
1765                                 __func__, list->version);
1766                        return -EINVAL;
1767                }
1768
1769                bi = NULL;
1770                for (ii = 0; ii < list->count && apcnt < IW_MAX_AP;
1771                     apcnt++, ii++) {
1772                        bi = bi ? (wl_bss_info_t *)((unsigned long)bi +
1773                                                     dtoh32(bi->length)) :
1774                            list->bss_info;
1775                        ASSERT(((unsigned long)bi + dtoh32(bi->length)) <=
1776                               ((unsigned long)list + WLC_IW_ISCAN_MAXLEN));
1777
1778                        if (event + ETH_ALEN + bi->SSID_len +
1779                            IW_EV_UINT_LEN + IW_EV_FREQ_LEN + IW_EV_QUAL_LEN >=
1780                            end)
1781                                return -E2BIG;
1782                        iwe.cmd = SIOCGIWAP;
1783                        iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1784                        memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID,
1785                               ETH_ALEN);
1786                        event =
1787                            IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1788                                                 IW_EV_ADDR_LEN);
1789
1790                        iwe.u.data.length = dtoh32(bi->SSID_len);
1791                        iwe.cmd = SIOCGIWESSID;
1792                        iwe.u.data.flags = 1;
1793                        event =
1794                            IWE_STREAM_ADD_POINT(info, event, end, &iwe,
1795                                                 bi->SSID);
1796
1797                        if (dtoh16(bi->capability) &
1798                            (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
1799                                iwe.cmd = SIOCGIWMODE;
1800                                if (dtoh16(bi->capability) & DOT11_CAP_ESS)
1801                                        iwe.u.mode = IW_MODE_INFRA;
1802                                else
1803                                        iwe.u.mode = IW_MODE_ADHOC;
1804                                event =
1805                                    IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1806                                                         IW_EV_UINT_LEN);
1807                        }
1808
1809                        iwe.cmd = SIOCGIWFREQ;
1810                        channel =
1811                            (bi->ctl_ch ==
1812                             0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
1813                        iwe.u.freq.m =
1814                            wf_channel2mhz(channel,
1815                                           channel <=
1816                                           CH_MAX_2G_CHANNEL ?
1817                                           WF_CHAN_FACTOR_2_4_G :
1818                                           WF_CHAN_FACTOR_5_G);
1819                        iwe.u.freq.e = 6;
1820                        event =
1821                            IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1822                                                 IW_EV_FREQ_LEN);
1823
1824                        iwe.cmd = IWEVQUAL;
1825                        iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
1826                        iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
1827                        iwe.u.qual.noise = 0x100 + bi->phy_noise;
1828                        event =
1829                            IWE_STREAM_ADD_EVENT(info, event, end, &iwe,
1830                                                 IW_EV_QUAL_LEN);
1831
1832                        wl_iw_handle_scanresults_ies(&event, end, info, bi);
1833
1834                        iwe.cmd = SIOCGIWENCODE;
1835                        if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
1836                                iwe.u.data.flags =
1837                                    IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1838                        else
1839                                iwe.u.data.flags = IW_ENCODE_DISABLED;
1840                        iwe.u.data.length = 0;
1841                        event =
1842                            IWE_STREAM_ADD_POINT(info, event, end, &iwe,
1843                                                 (char *)event);
1844
1845                        if (bi->rateset.count) {
1846                                if (event + IW_MAX_BITRATES * IW_EV_PARAM_LEN >=
1847                                    end)
1848                                        return -E2BIG;
1849
1850                                value = event + IW_EV_LCP_LEN;
1851                                iwe.cmd = SIOCGIWRATE;
1852                                iwe.u.bitrate.fixed = iwe.u.bitrate.disabled =
1853                                    0;
1854                                for (j = 0;
1855                                     j < bi->rateset.count
1856                                     && j < IW_MAX_BITRATES; j++) {
1857                                        iwe.u.bitrate.value =
1858                                            (bi->rateset.rates[j] & 0x7f) *
1859                                            500000;
1860                                        value =
1861                                            IWE_STREAM_ADD_VALUE(info, event,
1862                                                 value, end,
1863                                                 &iwe,
1864                                                 IW_EV_PARAM_LEN);
1865                                }
1866                                event = value;
1867                        }
1868                }
1869                p_buf = p_buf->next;
1870        }
1871
1872        dwrq->length = event - extra;
1873        dwrq->flags = 0;
1874
1875        WL_TRACE("%s return to WE %d bytes APs=%d\n",
1876                 __func__, dwrq->length, counter);
1877
1878        if (!dwrq->length)
1879                return -EAGAIN;
1880
1881        return 0;
1882}
1883#endif                          /* defined(WL_IW_USE_ISCAN) */
1884
1885static int
1886wl_iw_set_essid(struct net_device *dev,
1887                struct iw_request_info *info,
1888                struct iw_point *dwrq, char *extra)
1889{
1890        int error;
1891        wl_join_params_t join_params;
1892        int join_params_size;
1893
1894        WL_TRACE("%s: SIOCSIWESSID\n", dev->name);
1895
1896        if (g_set_essid_before_scan)
1897                return -EAGAIN;
1898
1899        memset(&g_ssid, 0, sizeof(g_ssid));
1900
1901        CHECK_EXTRA_FOR_NULL(extra);
1902
1903        if (dwrq->length && extra) {
1904#if WIRELESS_EXT > 20
1905                g_ssid.SSID_len = min_t(size_t, sizeof(g_ssid.SSID),
1906                                        dwrq->length);
1907#else
1908                g_ssid.SSID_len = min_t(size_t, sizeof(g_ssid.SSID),
1909                                        dwrq->length - 1);
1910#endif
1911                memcpy(g_ssid.SSID, extra, g_ssid.SSID_len);
1912        } else {
1913                g_ssid.SSID_len = 0;
1914        }
1915        g_ssid.SSID_len = htod32(g_ssid.SSID_len);
1916
1917        memset(&join_params, 0, sizeof(join_params));
1918        join_params_size = sizeof(join_params.ssid);
1919
1920        memcpy(&join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len);
1921        join_params.ssid.SSID_len = htod32(g_ssid.SSID_len);
1922        memcpy(&join_params.params.bssid, &ether_bcast, ETH_ALEN);
1923
1924        wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params,
1925                             &join_params_size);
1926
1927        error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params,
1928                                join_params_size);
1929        if (error)
1930                WL_ERROR("Invalid ioctl data=%d\n", error);
1931
1932        if (g_ssid.SSID_len) {
1933                WL_TRACE("%s: join SSID=%s ch=%d\n",
1934                         __func__, g_ssid.SSID, g_wl_iw_params.target_channel);
1935        }
1936        return 0;
1937}
1938
1939static int
1940wl_iw_get_essid(struct net_device *dev,
1941                struct iw_request_info *info,
1942                struct iw_point *dwrq, char *extra)
1943{
1944        wlc_ssid_t ssid;
1945        int error;
1946
1947        WL_TRACE("%s: SIOCGIWESSID\n", dev->name);
1948
1949        if (!extra)
1950                return -EINVAL;
1951
1952        error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid));
1953        if (error) {
1954                WL_ERROR("Error getting the SSID\n");
1955                return error;
1956        }
1957
1958        ssid.SSID_len = dtoh32(ssid.SSID_len);
1959
1960        memcpy(extra, ssid.SSID, ssid.SSID_len);
1961
1962        dwrq->length = ssid.SSID_len;
1963
1964        dwrq->flags = 1;
1965
1966        return 0;
1967}
1968
1969static int
1970wl_iw_set_nick(struct net_device *dev,
1971               struct iw_request_info *info, struct iw_point *dwrq, char *extra)
1972{
1973        wl_iw_t *iw = *(wl_iw_t **) netdev_priv(dev);
1974
1975        WL_TRACE("%s: SIOCSIWNICKN\n", dev->name);
1976
1977        if (!extra)
1978                return -EINVAL;
1979
1980        if (dwrq->length > sizeof(iw->nickname))
1981                return -E2BIG;
1982
1983        memcpy(iw->nickname, extra, dwrq->length);
1984        iw->nickname[dwrq->length - 1] = '\0';
1985
1986        return 0;
1987}
1988
1989static int
1990wl_iw_get_nick(struct net_device *dev,
1991               struct iw_request_info *info, struct iw_point *dwrq, char *extra)
1992{
1993        wl_iw_t *iw = *(wl_iw_t **) netdev_priv(dev);
1994
1995        WL_TRACE("%s: SIOCGIWNICKN\n", dev->name);
1996
1997        if (!extra)
1998                return -EINVAL;
1999
2000        strcpy(extra, iw->nickname);
2001        dwrq->length = strlen(extra) + 1;
2002
2003        return 0;
2004}
2005
2006static int
2007wl_iw_set_rate(struct net_device *dev,
2008               struct iw_request_info *info, struct iw_param *vwrq, char *extra)
2009{
2010        wl_rateset_t rateset;
2011        int error, rate, i, error_bg, error_a;
2012
2013        WL_TRACE("%s: SIOCSIWRATE\n", dev->name);
2014
2015        error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset,
2016                                sizeof(rateset));
2017        if (error)
2018                return error;
2019
2020        rateset.count = dtoh32(rateset.count);
2021
2022        if (vwrq->value < 0)
2023                rate = rateset.rates[rateset.count - 1] & 0x7f;
2024        else if (vwrq->value < rateset.count)
2025                rate = rateset.rates[vwrq->value] & 0x7f;
2026        else
2027                rate = vwrq->value / 500000;
2028
2029        if (vwrq->fixed) {
2030                error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate);
2031                error_a = dev_wlc_intvar_set(dev, "a_rate", rate);
2032
2033                if (error_bg && error_a)
2034                        return error_bg | error_a;
2035        } else {
2036                error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0);
2037                error_a = dev_wlc_intvar_set(dev, "a_rate", 0);
2038
2039                if (error_bg && error_a)
2040                        return error_bg | error_a;
2041
2042                for (i = 0; i < rateset.count; i++)
2043                        if ((rateset.rates[i] & 0x7f) > rate)
2044                                break;
2045                rateset.count = htod32(i);
2046
2047                error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset,
2048                                        sizeof(rateset));
2049                if (error)
2050                        return error;
2051        }
2052
2053        return 0;
2054}
2055
2056static int
2057wl_iw_get_rate(struct net_device *dev,
2058               struct iw_request_info *info, struct iw_param *vwrq, char *extra)
2059{
2060        int error, rate;
2061
2062        WL_TRACE("%s: SIOCGIWRATE\n", dev->name);
2063
2064        error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate));
2065        if (error)
2066                return error;
2067        rate = dtoh32(rate);
2068        vwrq->value = rate * 500000;
2069
2070        return 0;
2071}
2072
2073static int
2074wl_iw_set_rts(struct net_device *dev,
2075              struct iw_request_info *info, struct iw_param *vwrq, char *extra)
2076{
2077        int error, rts;
2078
2079        WL_TRACE("%s: SIOCSIWRTS\n", dev->name);
2080
2081        if (vwrq->disabled)
2082                rts = DOT11_DEFAULT_RTS_LEN;
2083        else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN)
2084                return -EINVAL;
2085        else
2086                rts = vwrq->value;
2087
2088        error = dev_wlc_intvar_set(dev, "rtsthresh", rts);
2089        if (error)
2090                return error;
2091
2092        return 0;
2093}
2094
2095static int
2096wl_iw_get_rts(struct net_device *dev,
2097              struct iw_request_info *info, struct iw_param *vwrq, char *extra)
2098{
2099        int error, rts;
2100
2101        WL_TRACE("%s: SIOCGIWRTS\n", dev->name);
2102
2103        error = dev_wlc_intvar_get(dev, "rtsthresh", &rts);
2104        if (error)
2105                return error;
2106
2107        vwrq->value = rts;
2108        vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN);
2109        vwrq->fixed = 1;
2110
2111        return 0;
2112}
2113
2114static int
2115wl_iw_set_frag(struct net_device *dev,
2116               struct iw_request_info *info, struct iw_param *vwrq, char *extra)
2117{
2118        int error, frag;
2119
2120        WL_TRACE("%s: SIOCSIWFRAG\n", dev->name);
2121
2122        if (vwrq->disabled)
2123                frag = DOT11_DEFAULT_FRAG_LEN;
2124        else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN)
2125                return -EINVAL;
2126        else
2127                frag = vwrq->value;
2128
2129        error = dev_wlc_intvar_set(dev, "fragthresh", frag);
2130        if (error)
2131                return error;
2132
2133        return 0;
2134}
2135
2136static int
2137wl_iw_get_frag(struct net_device *dev,
2138               struct iw_request_info *info, struct iw_param *vwrq, char *extra)
2139{
2140        int error, fragthreshold;
2141
2142        WL_TRACE("%s: SIOCGIWFRAG\n", dev->name);
2143
2144        error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold);
2145        if (error)
2146                return error;
2147
2148        vwrq->value = fragthreshold;
2149        vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN);
2150        vwrq->fixed = 1;
2151
2152        return 0;
2153}
2154
2155static int
2156wl_iw_set_txpow(struct net_device *dev,
2157                struct iw_request_info *info,
2158                struct iw_param *vwrq, char *extra)
2159{
2160        int error, disable;
2161        u16 txpwrmw;
2162        WL_TRACE("%s: SIOCSIWTXPOW\n", dev->name);
2163
2164        disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0;
2165        disable += WL_RADIO_SW_DISABLE << 16;
2166
2167        disable = htod32(disable);
2168        error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable));
2169        if (error)
2170                return error;
2171
2172        if (disable & WL_RADIO_SW_DISABLE)
2173                return 0;
2174
2175        if (!(vwrq->flags & IW_TXPOW_MWATT))
2176                return -EINVAL;
2177
2178        if (vwrq->value < 0)
2179                return 0;
2180
2181        if (vwrq->value > 0xffff)
2182                txpwrmw = 0xffff;
2183        else
2184                txpwrmw = (u16) vwrq->value;
2185
2186        error =
2187            dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw)));
2188        return error;
2189}
2190
2191static int
2192wl_iw_get_txpow(struct net_device *dev,
2193                struct iw_request_info *info,
2194                struct iw_param *vwrq, char *extra)
2195{
2196        int error, disable, txpwrdbm;
2197        u8 result;
2198
2199        WL_TRACE("%s: SIOCGIWTXPOW\n", dev->name);
2200
2201        error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable));
2202        if (error)
2203                return error;
2204
2205        error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm);
2206        if (error)
2207                return error;
2208
2209        disable = dtoh32(disable);
2210        result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
2211        vwrq->value = (s32) bcm_qdbm_to_mw(result);
2212        vwrq->fixed = 0;
2213        vwrq->disabled =
2214            (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0;
2215        vwrq->flags = IW_TXPOW_MWATT;
2216
2217        return 0;
2218}
2219
2220#if WIRELESS_EXT > 10
2221static int
2222wl_iw_set_retry(struct net_device *dev,
2223                struct iw_request_info *info,
2224                struct iw_param *vwrq, char *extra)
2225{
2226        int error, lrl, srl;
2227
2228        WL_TRACE("%s: SIOCSIWRETRY\n", dev->name);
2229
2230        if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME))
2231                return -EINVAL;
2232
2233        if (vwrq->flags & IW_RETRY_LIMIT) {
2234
2235#if WIRELESS_EXT > 20
2236                if ((vwrq->flags & IW_RETRY_LONG)
2237                    || (vwrq->flags & IW_RETRY_MAX)
2238                    || !((vwrq->flags & IW_RETRY_SHORT)
2239                         || (vwrq->flags & IW_RETRY_MIN))) {
2240#else
2241                if ((vwrq->flags & IW_RETRY_MAX)
2242                    || !(vwrq->flags & IW_RETRY_MIN)) {
2243#endif
2244                        lrl = htod32(vwrq->value);
2245                        error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl,
2246                                                sizeof(lrl));
2247                        if (error)
2248                                return error;
2249                }
2250#if WIRELESS_EXT > 20
2251                if ((vwrq->flags & IW_RETRY_SHORT)
2252                    || (vwrq->flags & IW_RETRY_MIN)
2253                    || !((vwrq->flags & IW_RETRY_LONG)
2254                         || (vwrq->flags & IW_RETRY_MAX))) {
2255#else
2256                if ((vwrq->flags & IW_RETRY_MIN)
2257                    || !(vwrq->flags & IW_RETRY_MAX)) {
2258#endif
2259                        srl = htod32(vwrq->value);
2260                        error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl,
2261                                                sizeof(srl));
2262                        if (error)
2263                                return error;
2264                }
2265        }
2266        return 0;
2267}
2268
2269static int
2270wl_iw_get_retry(struct net_device *dev,
2271                struct iw_request_info *info,
2272                struct iw_param *vwrq, char *extra)
2273{
2274        int error, lrl, srl;
2275
2276        WL_TRACE("%s: SIOCGIWRETRY\n", dev->name);
2277
2278        vwrq->disabled = 0;
2279
2280        if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
2281                return -EINVAL;
2282
2283        error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl));
2284        if (error)
2285                return error;
2286
2287        error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl));
2288        if (error)
2289                return error;
2290
2291        lrl = dtoh32(lrl);
2292        srl = dtoh32(srl);
2293
2294        if (vwrq->flags & IW_RETRY_MAX) {
2295                vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
2296                vwrq->value = lrl;
2297        } else {
2298                vwrq->flags = IW_RETRY_LIMIT;
2299                vwrq->value = srl;
2300                if (srl != lrl)
2301                        vwrq->flags |= IW_RETRY_MIN;
2302        }
2303
2304        return 0;
2305}
2306#endif                          /* WIRELESS_EXT > 10 */
2307
2308static int
2309wl_iw_set_encode(struct net_device *dev,
2310                 struct iw_request_info *info,
2311                 struct iw_point *dwrq, char *extra)
2312{
2313        wl_wsec_key_t key;
2314        int error, val, wsec;
2315
2316        WL_TRACE("%s: SIOCSIWENCODE\n", dev->name);
2317
2318        memset(&key, 0, sizeof(key));
2319
2320        if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
2321                for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS;
2322                     key.index++) {
2323                        val = htod32(key.index);
2324                        error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val,
2325                                                sizeof(val));
2326                        if (error)
2327                                return error;
2328                        val = dtoh32(val);
2329                        if (val)
2330                                break;
2331                }
2332                if (key.index == DOT11_MAX_DEFAULT_KEYS)
2333                        key.index = 0;
2334        } else {
2335                key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2336                if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2337                        return -EINVAL;
2338        }
2339
2340        if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) {
2341                val = htod32(key.index);
2342                error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val,
2343                                        sizeof(val));
2344                if (error)
2345                        return error;
2346        } else {
2347                key.len = dwrq->length;
2348
2349                if (dwrq->length > sizeof(key.data))
2350                        return -EINVAL;
2351
2352                memcpy(key.data, extra, dwrq->length);
2353
2354                key.flags = WL_PRIMARY_KEY;
2355                switch (key.len) {
2356                case WEP1_KEY_SIZE:
2357                        key.algo = CRYPTO_ALGO_WEP1;
2358                        break;
2359                case WEP128_KEY_SIZE:
2360                        key.algo = CRYPTO_ALGO_WEP128;
2361                        break;
2362                case TKIP_KEY_SIZE:
2363                        key.algo = CRYPTO_ALGO_TKIP;
2364                        break;
2365                case AES_KEY_SIZE:
2366                        key.algo = CRYPTO_ALGO_AES_CCM;
2367                        break;
2368                default:
2369                        return -EINVAL;
2370                }
2371
2372                swap_key_from_BE(&key);
2373                error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
2374                if (error)
2375                        return error;
2376        }
2377
2378        val = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED;
2379
2380        error = dev_wlc_intvar_get(dev, "wsec", &wsec);
2381        if (error)
2382                return error;
2383
2384        wsec &= ~(WEP_ENABLED);
2385        wsec |= val;
2386
2387        error = dev_wlc_intvar_set(dev, "wsec", wsec);
2388        if (error)
2389                return error;
2390
2391        val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0;
2392        val = htod32(val);
2393        error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val));
2394        if (error)
2395                return error;
2396
2397        return 0;
2398}
2399
2400static int
2401wl_iw_get_encode(struct net_device *dev,
2402                 struct iw_request_info *info,
2403                 struct iw_point *dwrq, char *extra)
2404{
2405        wl_wsec_key_t key;
2406        int error, val, wsec, auth;
2407
2408        WL_TRACE("%s: SIOCGIWENCODE\n", dev->name);
2409
2410        memset(&key, 0, sizeof(wl_wsec_key_t));
2411
2412        if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
2413                for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS;
2414                     key.index++) {
2415                        val = key.index;
2416                        error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val,
2417                                                sizeof(val));
2418                        if (error)
2419                                return error;
2420                        val = dtoh32(val);
2421                        if (val)
2422                                break;
2423                }
2424        } else
2425                key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2426
2427        if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2428                key.index = 0;
2429
2430        error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec));
2431        if (error)
2432                return error;
2433
2434        error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth));
2435        if (error)
2436                return error;
2437
2438        swap_key_to_BE(&key);
2439
2440        wsec = dtoh32(wsec);
2441        auth = dtoh32(auth);
2442        dwrq->length = min_t(u16, DOT11_MAX_KEY_SIZE, key.len);
2443
2444        dwrq->flags = key.index + 1;
2445        if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)))
2446                dwrq->flags |= IW_ENCODE_DISABLED;
2447
2448        if (auth)
2449                dwrq->flags |= IW_ENCODE_RESTRICTED;
2450
2451        if (dwrq->length && extra)
2452                memcpy(extra, key.data, dwrq->length);
2453
2454        return 0;
2455}
2456
2457static int
2458wl_iw_set_power(struct net_device *dev,
2459                struct iw_request_info *info,
2460                struct iw_param *vwrq, char *extra)
2461{
2462        int error, pm;
2463
2464        WL_TRACE("%s: SIOCSIWPOWER\n", dev->name);
2465
2466        pm = vwrq->disabled ? PM_OFF : PM_MAX;
2467
2468        pm = htod32(pm);
2469        error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
2470        if (error)
2471                return error;
2472
2473        return 0;
2474}
2475
2476static int
2477wl_iw_get_power(struct net_device *dev,
2478                struct iw_request_info *info,
2479                struct iw_param *vwrq, char *extra)
2480{
2481        int error, pm;
2482
2483        WL_TRACE("%s: SIOCGIWPOWER\n", dev->name);
2484
2485        error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm));
2486        if (error)
2487                return error;
2488
2489        pm = dtoh32(pm);
2490        vwrq->disabled = pm ? 0 : 1;
2491        vwrq->flags = IW_POWER_ALL_R;
2492
2493        return 0;
2494}
2495
2496#if WIRELESS_EXT > 17
2497static int
2498wl_iw_set_wpaie(struct net_device *dev,
2499                struct iw_request_info *info, struct iw_point *iwp, char *extra)
2500{
2501
2502        WL_TRACE("%s: SIOCSIWGENIE\n", dev->name);
2503
2504        CHECK_EXTRA_FOR_NULL(extra);
2505
2506        dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length);
2507
2508        return 0;
2509}
2510
2511static int
2512wl_iw_get_wpaie(struct net_device *dev,
2513                struct iw_request_info *info, struct iw_point *iwp, char *extra)
2514{
2515        WL_TRACE("%s: SIOCGIWGENIE\n", dev->name);
2516        iwp->length = 64;
2517        dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length);
2518        return 0;
2519}
2520
2521static int
2522wl_iw_set_encodeext(struct net_device *dev,
2523                    struct iw_request_info *info,
2524                    struct iw_point *dwrq, char *extra)
2525{
2526        wl_wsec_key_t key;
2527        int error;
2528        struct iw_encode_ext *iwe;
2529
2530        WL_TRACE("%s: SIOCSIWENCODEEXT\n", dev->name);
2531
2532        CHECK_EXTRA_FOR_NULL(extra);
2533
2534        memset(&key, 0, sizeof(key));
2535        iwe = (struct iw_encode_ext *)extra;
2536
2537        if (dwrq->flags & IW_ENCODE_DISABLED) {
2538
2539        }
2540
2541        key.index = 0;
2542        if (dwrq->flags & IW_ENCODE_INDEX)
2543                key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2544
2545        key.len = iwe->key_len;
2546
2547        if (!is_multicast_ether_addr(iwe->addr.sa_data))
2548                bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea,
2549                      ETH_ALEN);
2550
2551        if (key.len == 0) {
2552                if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
2553                        WL_WSEC("Changing the the primary Key to %d\n",
2554                                key.index);
2555                        key.index = htod32(key.index);
2556                        error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY,
2557                                              &key.index, sizeof(key.index));
2558                        if (error)
2559                                return error;
2560                } else {
2561                        swap_key_from_BE(&key);
2562                        dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
2563                }
2564        } else {
2565                if (iwe->key_len > sizeof(key.data))
2566                        return -EINVAL;
2567
2568                WL_WSEC("Setting the key index %d\n", key.index);
2569                if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
2570                        WL_WSEC("key is a Primary Key\n");
2571                        key.flags = WL_PRIMARY_KEY;
2572                }
2573
2574                bcopy((void *)iwe->key, key.data, iwe->key_len);
2575
2576                if (iwe->alg == IW_ENCODE_ALG_TKIP) {
2577                        u8 keybuf[8];
2578                        bcopy(&key.data[24], keybuf, sizeof(keybuf));
2579                        bcopy(&key.data[16], &key.data[24], sizeof(keybuf));
2580                        bcopy(keybuf, &key.data[16], sizeof(keybuf));
2581                }
2582
2583                if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
2584                        unsigned char *ivptr;
2585                        ivptr = (unsigned char *) iwe->rx_seq;
2586                        key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2587                            (ivptr[3] << 8) | ivptr[2];
2588                        key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2589                        key.iv_initialized = true;
2590                }
2591
2592                switch (iwe->alg) {
2593                case IW_ENCODE_ALG_NONE:
2594                        key.algo = CRYPTO_ALGO_OFF;
2595                        break;
2596                case IW_ENCODE_ALG_WEP:
2597                        if (iwe->key_len == WEP1_KEY_SIZE)
2598                                key.algo = CRYPTO_ALGO_WEP1;
2599                        else
2600                                key.algo = CRYPTO_ALGO_WEP128;
2601                        break;
2602                case IW_ENCODE_ALG_TKIP:
2603                        key.algo = CRYPTO_ALGO_TKIP;
2604                        break;
2605                case IW_ENCODE_ALG_CCMP:
2606                        key.algo = CRYPTO_ALGO_AES_CCM;
2607                        break;
2608                default:
2609                        break;
2610                }
2611                swap_key_from_BE(&key);
2612
2613                dhd_wait_pend8021x(dev);
2614
2615                error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
2616                if (error)
2617                        return error;
2618        }
2619        return 0;
2620}
2621
2622#if WIRELESS_EXT > 17
2623struct {
2624        pmkid_list_t pmkids;
2625        pmkid_t foo[MAXPMKID - 1];
2626} pmkid_list;
2627
2628static int
2629wl_iw_set_pmksa(struct net_device *dev,
2630                struct iw_request_info *info,
2631                struct iw_param *vwrq, char *extra)
2632{
2633        struct iw_pmksa *iwpmksa;
2634        uint i;
2635        int ret = 0;
2636
2637        WL_WSEC("%s: SIOCSIWPMKSA\n", dev->name);
2638
2639        CHECK_EXTRA_FOR_NULL(extra);
2640
2641        iwpmksa = (struct iw_pmksa *)extra;
2642
2643        if (iwpmksa->cmd == IW_PMKSA_FLUSH) {
2644                WL_WSEC("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n");
2645                memset((char *)&pmkid_list, 0, sizeof(pmkid_list));
2646        }
2647
2648        else if (iwpmksa->cmd == IW_PMKSA_REMOVE) {
2649                {
2650                        pmkid_list_t pmkid, *pmkidptr;
2651                        uint j;
2652                        pmkidptr = &pmkid;
2653
2654                        bcopy(&iwpmksa->bssid.sa_data[0],
2655                              &pmkidptr->pmkid[0].BSSID, ETH_ALEN);
2656                        bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID,
2657                              WPA2_PMKID_LEN);
2658
2659                        WL_WSEC("wl_iw_set_pmksa:IW_PMKSA_REMOVE:PMKID: %pM = ",
2660                                &pmkidptr->pmkid[0].BSSID);
2661                        for (j = 0; j < WPA2_PMKID_LEN; j++)
2662                                WL_WSEC("%02x ", pmkidptr->pmkid[0].PMKID[j]);
2663                        WL_WSEC("\n");
2664                }
2665
2666                for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
2667                        if (!memcmp
2668                            (&iwpmksa->bssid.sa_data[0],
2669                             &pmkid_list.pmkids.pmkid[i].BSSID, ETH_ALEN))
2670                                break;
2671
2672                if ((pmkid_list.pmkids.npmkid > 0)
2673                    && (i < pmkid_list.pmkids.npmkid)) {
2674                        memset(&pmkid_list.pmkids.pmkid[i], 0, sizeof(pmkid_t));
2675                        for (; i < (pmkid_list.pmkids.npmkid - 1); i++) {
2676                                bcopy(&pmkid_list.pmkids.pmkid[i + 1].BSSID,
2677                                      &pmkid_list.pmkids.pmkid[i].BSSID,
2678                                      ETH_ALEN);
2679                                bcopy(&pmkid_list.pmkids.pmkid[i + 1].PMKID,
2680                                      &pmkid_list.pmkids.pmkid[i].PMKID,
2681                                      WPA2_PMKID_LEN);
2682                        }
2683                        pmkid_list.pmkids.npmkid--;
2684                } else
2685                        ret = -EINVAL;
2686        }
2687
2688        else if (iwpmksa->cmd == IW_PMKSA_ADD) {
2689                for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
2690                        if (!memcmp
2691                            (&iwpmksa->bssid.sa_data[0],
2692                             &pmkid_list.pmkids.pmkid[i].BSSID, ETH_ALEN))
2693                                break;
2694                if (i < MAXPMKID) {
2695                        bcopy(&iwpmksa->bssid.sa_data[0],
2696                              &pmkid_list.pmkids.pmkid[i].BSSID,
2697                              ETH_ALEN);
2698                        bcopy(&iwpmksa->pmkid[0],
2699                              &pmkid_list.pmkids.pmkid[i].PMKID,
2700                              WPA2_PMKID_LEN);
2701                        if (i == pmkid_list.pmkids.npmkid)
2702                                pmkid_list.pmkids.npmkid++;
2703                } else
2704                        ret = -EINVAL;
2705                {
2706                        uint j;
2707                        uint k;
2708                        k = pmkid_list.pmkids.npmkid;
2709                        WL_WSEC("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %pM = ",
2710                                &pmkid_list.pmkids.pmkid[k].BSSID);
2711                        for (j = 0; j < WPA2_PMKID_LEN; j++)
2712                                WL_WSEC("%02x ",
2713                                        pmkid_list.pmkids.pmkid[k].PMKID[j]);
2714                        WL_WSEC("\n");
2715                }
2716        }
2717        WL_WSEC("PRINTING pmkid LIST - No of elements %d\n",
2718                pmkid_list.pmkids.npmkid);
2719        for (i = 0; i < pmkid_list.pmkids.npmkid; i++) {
2720                uint j;
2721                WL_WSEC("PMKID[%d]: %pM = ",
2722                        i, &pmkid_list.pmkids.pmkid[i].BSSID);
2723                for (j = 0; j < WPA2_PMKID_LEN; j++)
2724                        WL_WSEC("%02x ", pmkid_list.pmkids.pmkid[i].PMKID[j]);
2725                WL_WSEC("\n");
2726        }
2727        WL_WSEC("\n");
2728
2729        if (!ret)
2730                ret = dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list,
2731                                         sizeof(pmkid_list));
2732        return ret;
2733}
2734#endif                          /* WIRELESS_EXT > 17 */
2735
2736static int
2737wl_iw_get_encodeext(struct net_device *dev,
2738                    struct iw_request_info *info,
2739                    struct iw_param *vwrq, char *extra)
2740{
2741        WL_TRACE("%s: SIOCGIWENCODEEXT\n", dev->name);
2742        return 0;
2743}
2744
2745static int
2746wl_iw_set_wpaauth(struct net_device *dev,
2747                  struct iw_request_info *info,
2748                  struct iw_param *vwrq, char *extra)
2749{
2750        int error = 0;
2751        int paramid;
2752        int paramval;
2753        int val = 0;
2754        wl_iw_t *iw = *(wl_iw_t **) netdev_priv(dev);
2755
2756        WL_TRACE("%s: SIOCSIWAUTH\n", dev->name);
2757
2758        paramid = vwrq->flags & IW_AUTH_INDEX;
2759        paramval = vwrq->value;
2760
2761        WL_TRACE("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n",
2762                 dev->name, paramid, paramval);
2763
2764        switch (paramid) {
2765        case IW_AUTH_WPA_VERSION:
2766                if (paramval & IW_AUTH_WPA_VERSION_DISABLED)
2767                        val = WPA_AUTH_DISABLED;
2768                else if (paramval & (IW_AUTH_WPA_VERSION_WPA))
2769                        val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
2770                else if (paramval & IW_AUTH_WPA_VERSION_WPA2)
2771                        val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
2772                WL_INFORM("%s: %d: setting wpa_auth to 0x%0x\n",
2773                          __func__, __LINE__, val);
2774                error = dev_wlc_intvar_set(dev, "wpa_auth", val);
2775                if (error)
2776                        return error;
2777                break;
2778        case IW_AUTH_CIPHER_PAIRWISE:
2779        case IW_AUTH_CIPHER_GROUP:
2780                if (paramval & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
2781                        val = WEP_ENABLED;
2782                if (paramval & IW_AUTH_CIPHER_TKIP)
2783                        val = TKIP_ENABLED;
2784                if (paramval & IW_AUTH_CIPHER_CCMP)
2785                        val = AES_ENABLED;
2786
2787                if (paramid == IW_AUTH_CIPHER_PAIRWISE) {
2788                        iw->pwsec = val;
2789                        val |= iw->gwsec;
2790                } else {
2791                        iw->gwsec = val;
2792                        val |= iw->pwsec;
2793                }
2794
2795                if (iw->privacy_invoked && !val) {
2796                        WL_WSEC("%s: %s: 'Privacy invoked' true but clearing wsec, assuming we're a WPS enrollee\n",
2797                                dev->name, __func__);
2798                        error = dev_wlc_intvar_set(dev, "is_WPS_enrollee",
2799                                                        true);
2800                        if (error) {
2801                                WL_WSEC("Failed to set is_WPS_enrollee\n");
2802                                return error;
2803                        }
2804                } else if (val) {
2805                        error = dev_wlc_intvar_set(dev, "is_WPS_enrollee",
2806                                                        false);
2807                        if (error) {
2808                                WL_WSEC("Failed to clear is_WPS_enrollee\n");
2809                                return error;
2810                        }
2811                }
2812
2813                error = dev_wlc_intvar_set(dev, "wsec", val);
2814                if (error)
2815                        return error;
2816
2817                break;
2818
2819        case IW_AUTH_KEY_MGMT:
2820                error = dev_wlc_intvar_get(dev, "wpa_auth", &val);
2821                if (error)
2822                        return error;
2823
2824                if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
2825                        if (paramval & IW_AUTH_KEY_MGMT_PSK)
2826                                val = WPA_AUTH_PSK;
2827                        else
2828                                val = WPA_AUTH_UNSPECIFIED;
2829                } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
2830                        if (paramval & IW_AUTH_KEY_MGMT_PSK)
2831                                val = WPA2_AUTH_PSK;
2832                        else
2833                                val = WPA2_AUTH_UNSPECIFIED;
2834                }
2835                WL_INFORM("%s: %d: setting wpa_auth to %d\n",
2836                          __func__, __LINE__, val);
2837                error = dev_wlc_intvar_set(dev, "wpa_auth", val);
2838                if (error)
2839                        return error;
2840
2841                break;
2842        case IW_AUTH_TKIP_COUNTERMEASURES:
2843                dev_wlc_bufvar_set(dev, "tkip_countermeasures",
2844                                   (char *)&paramval, 1);
2845                break;
2846
2847        case IW_AUTH_80211_AUTH_ALG:
2848                WL_INFORM("Setting the D11auth %d\n", paramval);
2849                if (paramval == IW_AUTH_ALG_OPEN_SYSTEM)
2850                        val = 0;
2851                else if (paramval == IW_AUTH_ALG_SHARED_KEY)
2852                        val = 1;
2853                else if (paramval ==
2854                         (IW_AUTH_ALG_OPEN_SYSTEM | IW_AUTH_ALG_SHARED_KEY))
2855                        val = 2;
2856                else
2857                        error = 1;
2858                if (!error) {
2859                        error = dev_wlc_intvar_set(dev, "auth", val);
2860                        if (error)
2861                                return error;
2862                }
2863                break;
2864
2865        case IW_AUTH_WPA_ENABLED:
2866                if (paramval == 0) {
2867                        iw->pwsec = 0;
2868                        iw->gwsec = 0;
2869                        error = dev_wlc_intvar_get(dev, "wsec", &val);
2870                        if (error)
2871                                return error;
2872                        if (val & (TKIP_ENABLED | AES_ENABLED)) {
2873                                val &= ~(TKIP_ENABLED | AES_ENABLED);
2874                                dev_wlc_intvar_set(dev, "wsec", val);
2875                        }
2876                        val = 0;
2877                        WL_INFORM("%s: %d: setting wpa_auth to %d\n",
2878                                  __func__, __LINE__, val);
2879                        dev_wlc_intvar_set(dev, "wpa_auth", 0);
2880                        return error;
2881                }
2882                break;
2883
2884        case IW_AUTH_DROP_UNENCRYPTED:
2885                dev_wlc_bufvar_set(dev, "wsec_restrict", (char *)&paramval, 1);
2886                break;
2887
2888        case IW_AUTH_RX_UNENCRYPTED_EAPOL:
2889                dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol",
2890                                   (char *)&paramval, 1);
2891                break;
2892
2893#if WIRELESS_EXT > 17
2894        case IW_AUTH_ROAMING_CONTROL:
2895                WL_INFORM("%s: IW_AUTH_ROAMING_CONTROL\n", __func__);
2896                break;
2897        case IW_AUTH_PRIVACY_INVOKED:
2898                {
2899                        int wsec;
2900
2901                        if (paramval == 0) {
2902                                iw->privacy_invoked = false;
2903                                error = dev_wlc_intvar_set(dev,
2904                                                "is_WPS_enrollee", false);
2905                                if (error) {
2906                                        WL_WSEC("Failed to clear iovar is_WPS_enrollee\n");
2907                                        return error;
2908                                }
2909                        } else {
2910                                iw->privacy_invoked = true;
2911                                error = dev_wlc_intvar_get(dev, "wsec", &wsec);
2912                                if (error)
2913                                        return error;
2914
2915                                if (!(IW_WSEC_ENABLED(wsec))) {
2916                                        error = dev_wlc_intvar_set(dev,
2917                                                        "is_WPS_enrollee",
2918                                                        true);
2919                                        if (error) {
2920                                                WL_WSEC("Failed to set iovar is_WPS_enrollee\n");
2921                                                return error;
2922                                        }
2923                                } else {
2924                                        error = dev_wlc_intvar_set(dev,
2925                                                        "is_WPS_enrollee",
2926                                                        false);
2927                                        if (error) {
2928                                                WL_WSEC("Failed to clear is_WPS_enrollee\n");
2929                                                return error;
2930                                        }
2931                                }
2932                        }
2933                        break;
2934                }
2935#endif                          /* WIRELESS_EXT > 17 */
2936        default:
2937                break;
2938        }
2939        return 0;
2940}
2941
2942#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK))
2943
2944static int
2945wl_iw_get_wpaauth(struct net_device *dev,
2946                  struct iw_request_info *info,
2947                  struct iw_param *vwrq, char *extra)
2948{
2949        int error;
2950        int paramid;
2951        int paramval = 0;
2952        int val;
2953        wl_iw_t *iw = *(wl_iw_t **) netdev_priv(dev);
2954
2955        WL_TRACE("%s: SIOCGIWAUTH\n", dev->name);
2956
2957        paramid = vwrq->flags & IW_AUTH_INDEX;
2958
2959        switch (paramid) {
2960        case IW_AUTH_WPA_VERSION:
2961                error = dev_wlc_intvar_get(dev, "wpa_auth", &val);
2962                if (error)
2963                        return error;
2964                if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED))
2965                        paramval = IW_AUTH_WPA_VERSION_DISABLED;
2966                else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED))
2967                        paramval = IW_AUTH_WPA_VERSION_WPA;
2968                else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED))
2969                        paramval = IW_AUTH_WPA_VERSION_WPA2;
2970                break;
2971        case IW_AUTH_CIPHER_PAIRWISE:
2972        case IW_AUTH_CIPHER_GROUP:
2973                if (paramid == IW_AUTH_CIPHER_PAIRWISE)
2974                        val = iw->pwsec;
2975                else
2976                        val = iw->gwsec;
2977
2978                paramval = 0;
2979                if (val) {
2980                        if (val & WEP_ENABLED)
2981                                paramval |=
2982                                    (IW_AUTH_CIPHER_WEP40 |
2983                                     IW_AUTH_CIPHER_WEP104);
2984                        if (val & TKIP_ENABLED)
2985                                paramval |= (IW_AUTH_CIPHER_TKIP);
2986                        if (val & AES_ENABLED)
2987                                paramval |= (IW_AUTH_CIPHER_CCMP);
2988                } else
2989                        paramval = IW_AUTH_CIPHER_NONE;
2990                break;
2991        case IW_AUTH_KEY_MGMT:
2992                error = dev_wlc_intvar_get(dev, "wpa_auth", &val);
2993                if (error)
2994                        return error;
2995                if (VAL_PSK(val))
2996                        paramval = IW_AUTH_KEY_MGMT_PSK;
2997                else
2998                        paramval = IW_AUTH_KEY_MGMT_802_1X;
2999
3000                break;
3001        case IW_AUTH_TKIP_COUNTERMEASURES:
3002                dev_wlc_bufvar_get(dev, "tkip_countermeasures",
3003                                   (char *)&paramval, 1);
3004                break;
3005
3006        case IW_AUTH_DROP_UNENCRYPTED:
3007                dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)&paramval, 1);
3008                break;
3009
3010        case IW_AUTH_RX_UNENCRYPTED_EAPOL:
3011                dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol",
3012                                   (char *)&paramval, 1);
3013                break;
3014
3015        case IW_AUTH_80211_AUTH_ALG:
3016                error = dev_wlc_intvar_get(dev, "auth", &val);
3017                if (error)
3018                        return error;
3019                if (!val)
3020                        paramval = IW_AUTH_ALG_OPEN_SYSTEM;
3021                else
3022                        paramval = IW_AUTH_ALG_SHARED_KEY;
3023                break;
3024        case IW_AUTH_WPA_ENABLED:
3025                error = dev_wlc_intvar_get(dev, "wpa_auth", &val);
3026                if (error)
3027                        return error;
3028                if (val)
3029                        paramval = true;
3030                else
3031                        paramval = false;
3032                break;
3033#if WIRELESS_EXT > 17
3034        case IW_AUTH_ROAMING_CONTROL:
3035                WL_ERROR("%s: IW_AUTH_ROAMING_CONTROL\n", __func__);
3036                break;
3037        case IW_AUTH_PRIVACY_INVOKED:
3038                paramval = iw->privacy_invoked;
3039                break;
3040
3041#endif
3042        }
3043        vwrq->value = paramval;
3044        return 0;
3045}
3046#endif                          /* WIRELESS_EXT > 17 */
3047
3048static const iw_handler wl_iw_handler[] = {
3049        (iw_handler) wl_iw_config_commit,
3050        (iw_handler) wl_iw_get_name,
3051        (iw_handler) NULL,
3052        (iw_handler) NULL,
3053        (iw_handler) wl_iw_set_freq,
3054        (iw_handler) wl_iw_get_freq,
3055        (iw_handler) wl_iw_set_mode,
3056        (iw_handler) wl_iw_get_mode,
3057        (iw_handler) NULL,
3058        (iw_handler) NULL,
3059        (iw_handler) NULL,
3060        (iw_handler) wl_iw_get_range,
3061        (iw_handler) NULL,
3062        (iw_handler) NULL,
3063        (iw_handler) NULL,
3064        (iw_handler) NULL,
3065        (iw_handler) wl_iw_set_spy,
3066        (iw_handler) wl_iw_get_spy,
3067        (iw_handler) NULL,
3068        (iw_handler) NULL,
3069        (iw_handler) wl_iw_set_wap,
3070        (iw_handler) wl_iw_get_wap,
3071#if WIRELESS_EXT > 17
3072        (iw_handler) wl_iw_mlme,
3073#else
3074        (iw_handler) NULL,
3075#endif
3076#if defined(WL_IW_USE_ISCAN)
3077        (iw_handler) wl_iw_iscan_get_aplist,
3078#else
3079        (iw_handler) wl_iw_get_aplist,
3080#endif
3081#if WIRELESS_EXT > 13
3082#if defined(WL_IW_USE_ISCAN)
3083        (iw_handler) wl_iw_iscan_set_scan,
3084        (iw_handler) wl_iw_iscan_get_scan,
3085#else
3086        (iw_handler) wl_iw_set_scan,
3087        (iw_handler) wl_iw_get_scan,
3088#endif
3089#else
3090        (iw_handler) NULL,
3091        (iw_handler) NULL,
3092#endif                          /* WIRELESS_EXT > 13 */
3093        (iw_handler) wl_iw_set_essid,
3094        (iw_handler) wl_iw_get_essid,
3095        (iw_handler) wl_iw_set_nick,
3096        (iw_handler) wl_iw_get_nick,
3097        (iw_handler) NULL,
3098        (iw_handler) NULL,
3099        (iw_handler) wl_iw_set_rate,
3100        (iw_handler) wl_iw_get_rate,
3101        (iw_handler) wl_iw_set_rts,
3102        (iw_handler) wl_iw_get_rts,
3103        (iw_handler) wl_iw_set_frag,
3104        (iw_handler) wl_iw_get_frag,
3105        (iw_handler) wl_iw_set_txpow,
3106        (iw_handler) wl_iw_get_txpow,
3107#if WIRELESS_EXT > 10
3108        (iw_handler) wl_iw_set_retry,
3109        (iw_handler) wl_iw_get_retry,
3110#endif
3111        (iw_handler) wl_iw_set_encode,
3112        (iw_handler) wl_iw_get_encode,
3113        (iw_handler) wl_iw_set_power,
3114        (iw_handler) wl_iw_get_power,
3115#if WIRELESS_EXT > 17
3116        (iw_handler) NULL,
3117        (iw_handler) NULL,
3118        (iw_handler) wl_iw_set_wpaie,
3119        (iw_handler) wl_iw_get_wpaie,
3120        (iw_handler) wl_iw_set_wpaauth,
3121        (iw_handler) wl_iw_get_wpaauth,
3122        (iw_handler) wl_iw_set_encodeext,
3123        (iw_handler) wl_iw_get_encodeext,
3124        (iw_handler) wl_iw_set_pmksa,
3125#endif                          /* WIRELESS_EXT > 17 */
3126};
3127
3128#if WIRELESS_EXT > 12
3129
3130const struct iw_handler_def wl_iw_handler_def = {
3131        .num_standard = ARRAY_SIZE(wl_iw_handler),
3132        .standard = (iw_handler *) wl_iw_handler,
3133        .num_private = 0,
3134        .num_private_args = 0,
3135        .private = 0,
3136        .private_args = 0,
3137
3138#if WIRELESS_EXT >= 19
3139        .get_wireless_stats = dhd_get_wireless_stats,
3140#endif
3141};
3142#endif                          /* WIRELESS_EXT > 12 */
3143
3144int wl_iw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
3145{
3146        struct iwreq *wrq = (struct iwreq *)rq;
3147        struct iw_request_info info;
3148        iw_handler handler;
3149        char *extra = NULL;
3150        int token_size = 1, max_tokens = 0, ret = 0;
3151
3152        WL_TRACE("\n%s, cmd:%x alled via dhd->do_ioctl()entry point\n",
3153                 __func__, cmd);
3154        if (cmd < SIOCIWFIRST ||
3155                IW_IOCTL_IDX(cmd) >= ARRAY_SIZE(wl_iw_handler)) {
3156                WL_ERROR("%s: error in cmd=%x : out of range\n",
3157                         __func__, cmd);
3158                return -EOPNOTSUPP;
3159        }
3160
3161        handler = wl_iw_handler[IW_IOCTL_IDX(cmd)];
3162        if (!handler) {
3163                WL_ERROR("%s: error in cmd=%x : not supported\n",
3164                         __func__, cmd);
3165                return -EOPNOTSUPP;
3166        }
3167
3168        switch (cmd) {
3169
3170        case SIOCSIWESSID:
3171        case SIOCGIWESSID:
3172        case SIOCSIWNICKN:
3173        case SIOCGIWNICKN:
3174                max_tokens = IW_ESSID_MAX_SIZE + 1;
3175                break;
3176
3177        case SIOCSIWENCODE:
3178        case SIOCGIWENCODE:
3179#if WIRELESS_EXT > 17
3180        case SIOCSIWENCODEEXT:
3181        case SIOCGIWENCODEEXT:
3182#endif
3183                max_tokens = wrq->u.data.length;
3184                break;
3185
3186        case SIOCGIWRANGE:
3187                max_tokens = sizeof(struct iw_range) + 500;
3188                break;
3189
3190        case SIOCGIWAPLIST:
3191                token_size =
3192                    sizeof(struct sockaddr) + sizeof(struct iw_quality);
3193                max_tokens = IW_MAX_AP;
3194                break;
3195
3196#if WIRELESS_EXT > 13
3197        case SIOCGIWSCAN:
3198#if defined(WL_IW_USE_ISCAN)
3199                if (g_iscan)
3200                        max_tokens = wrq->u.data.length;
3201                else
3202#endif
3203                        max_tokens = IW_SCAN_MAX_DATA;
3204                break;
3205#endif                          /* WIRELESS_EXT > 13 */
3206
3207        case SIOCSIWSPY:
3208                token_size = sizeof(struct sockaddr);
3209                max_tokens = IW_MAX_SPY;
3210                break;
3211
3212        case SIOCGIWSPY:
3213                token_size =
3214                    sizeof(struct sockaddr) + sizeof(struct iw_quality);
3215                max_tokens = IW_MAX_SPY;
3216                break;
3217
3218#if WIRELESS_EXT > 17
3219        case SIOCSIWPMKSA:
3220        case SIOCSIWGENIE:
3221#endif
3222        case SIOCSIWPRIV:
3223                max_tokens = wrq->u.data.length;
3224                break;
3225        }
3226
3227        if (max_tokens && wrq->u.data.pointer) {
3228                if (wrq->u.data.length > max_tokens) {
3229                        WL_ERROR("%s: error in cmd=%x wrq->u.data.length=%d > max_tokens=%d\n",
3230                                 __func__, cmd, wrq->u.data.length, max_tokens);
3231                        return -E2BIG;
3232                }
3233                extra = kmalloc(max_tokens * token_size, GFP_KERNEL);
3234                if (!extra)
3235                        return -ENOMEM;
3236
3237                if (copy_from_user
3238                    (extra, wrq->u.data.pointer,
3239                     wrq->u.data.length * token_size)) {
3240                        kfree(extra);
3241                        return -EFAULT;
3242                }
3243        }
3244
3245        info.cmd = cmd;
3246        info.flags = 0;
3247
3248        ret = handler(dev, &info, &wrq->u, extra);
3249
3250        if (extra) {
3251                if (copy_to_user
3252                    (wrq->u.data.pointer, extra,
3253                     wrq->u.data.length * token_size)) {
3254                        kfree(extra);
3255                        return -EFAULT;
3256                }
3257
3258                kfree(extra);
3259        }
3260
3261        return ret;
3262}
3263
3264bool
3265wl_iw_conn_status_str(u32 event_type, u32 status, u32 reason,
3266                      char *stringBuf, uint buflen)
3267{
3268        typedef struct conn_fail_event_map_t {
3269                u32 inEvent;
3270                u32 inStatus;
3271                u32 inReason;
3272                const char *outName;
3273                const char *outCause;
3274        } conn_fail_event_map_t;
3275
3276#define WL_IW_DONT_CARE 9999
3277        const conn_fail_event_map_t event_map[] = {
3278                {WLC_E_SET_SSID, WLC_E_STATUS_SUCCESS, WL_IW_DONT_CARE,
3279                 "Conn", "Success"},
3280                {WLC_E_SET_SSID, WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE,
3281                 "Conn", "NoNetworks"},
3282                {WLC_E_SET_SSID, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
3283                 "Conn", "ConfigMismatch"},
3284                {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_PRUNE_ENCR_MISMATCH,
3285                 "Conn", "EncrypMismatch"},
3286                {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_RSN_MISMATCH,
3287                 "Conn", "RsnMismatch"},
3288                {WLC_E_AUTH, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
3289                 "Conn", "AuthTimeout"},
3290                {WLC_E_AUTH, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
3291                 "Conn", "AuthFail"},
3292                {WLC_E_AUTH, WLC_E_STATUS_NO_ACK, WL_IW_DONT_CARE,
3293                 "Conn", "AuthNoAck"},
3294                {WLC_E_REASSOC, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
3295                 "Conn", "ReassocFail"},
3296                {WLC_E_REASSOC, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
3297                 "Conn", "ReassocTimeout"},
3298                {WLC_E_REASSOC, WLC_E_STATUS_ABORT, WL_IW_DONT_CARE,
3299                 "Conn", "ReassocAbort"},
3300                {WLC_E_PSK_SUP, WLC_SUP_KEYED, WL_IW_DONT_CARE,
3301                 "Sup", "ConnSuccess"},
3302                {WLC_E_PSK_SUP, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3303                 "Sup", "WpaHandshakeFail"},
3304                {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3305                 "Conn", "Deauth"},
3306                {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3307                 "Conn", "DisassocInd"},
3308                {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3309                 "Conn", "Disassoc"}
3310        };
3311
3312        const char *name = "";
3313        const char *cause = NULL;
3314        int i;
3315
3316        for (i = 0; i < sizeof(event_map) / sizeof(event_map[0]); i++) {
3317                const conn_fail_event_map_t *row = &event_map[i];
3318                if (row->inEvent == event_type &&
3319                    (row->inStatus == status
3320                     || row->inStatus == WL_IW_DONT_CARE)
3321                    && (row->inReason == reason
3322                        || row->inReason == WL_IW_DONT_CARE)) {
3323                        name = row->outName;
3324                        cause = row->outCause;
3325                        break;
3326                }
3327        }
3328
3329        if (cause) {
3330                memset(stringBuf, 0, buflen);
3331                snprintf(stringBuf, buflen, "%s %s %02d %02d",
3332                         name, cause, status, reason);
3333                WL_INFORM("Connection status: %s\n", stringBuf);
3334                return true;
3335        } else {
3336                return false;
3337        }
3338}
3339
3340#if WIRELESS_EXT > 14
3341
3342static bool
3343wl_iw_check_conn_fail(wl_event_msg_t *e, char *stringBuf, uint buflen)
3344{
3345        u32 event = ntoh32(e->event_type);
3346        u32 status = ntoh32(e->status);
3347        u32 reason = ntoh32(e->reason);
3348
3349        if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) {
3350                return true;
3351        } else
3352                return false;
3353}
3354#endif
3355
3356#ifndef IW_CUSTOM_MAX
3357#define IW_CUSTOM_MAX 256
3358#endif
3359
3360void wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void *data)
3361{
3362#if WIRELESS_EXT > 13
3363        union iwreq_data wrqu;
3364        char extra[IW_CUSTOM_MAX + 1];
3365        int cmd = 0;
3366        u32 event_type = ntoh32(e->event_type);
3367        u16 flags = ntoh16(e->flags);
3368        u32 datalen = ntoh32(e->datalen);
3369        u32 status = ntoh32(e->status);
3370        wl_iw_t *iw;
3371        u32 toto;
3372        memset(&wrqu, 0, sizeof(wrqu));
3373        memset(extra, 0, sizeof(extra));
3374        iw = 0;
3375
3376        if (!dev) {
3377                WL_ERROR("%s: dev is null\n", __func__);
3378                return;
3379        }
3380
3381        iw = *(wl_iw_t **) netdev_priv(dev);
3382
3383        WL_TRACE("%s: dev=%s event=%d\n", __func__, dev->name, event_type);
3384
3385        switch (event_type) {
3386        case WLC_E_TXFAIL:
3387                cmd = IWEVTXDROP;
3388                memcpy(wrqu.addr.sa_data, &e->addr, ETH_ALEN);
3389                wrqu.addr.sa_family = ARPHRD_ETHER;
3390                break;
3391#if WIRELESS_EXT > 14
3392        case WLC_E_JOIN:
3393        case WLC_E_ASSOC_IND:
3394        case WLC_E_REASSOC_IND:
3395                memcpy(wrqu.addr.sa_data, &e->addr, ETH_ALEN);
3396                wrqu.addr.sa_family = ARPHRD_ETHER;
3397                cmd = IWEVREGISTERED;
3398                break;
3399        case WLC_E_DEAUTH_IND:
3400        case WLC_E_DISASSOC_IND:
3401                cmd = SIOCGIWAP;
3402                memset(wrqu.addr.sa_data, 0, ETH_ALEN);
3403                wrqu.addr.sa_family = ARPHRD_ETHER;
3404                memset(&extra, 0, ETH_ALEN);
3405                break;
3406        case WLC_E_LINK:
3407        case WLC_E_NDIS_LINK:
3408                cmd = SIOCGIWAP;
3409                if (!(flags & WLC_EVENT_MSG_LINK)) {
3410                        memset(wrqu.addr.sa_data, 0, ETH_ALEN);
3411                        memset(&extra, 0, ETH_ALEN);
3412                        WAKE_LOCK_TIMEOUT(iw->pub, WAKE_LOCK_LINK_DOWN_TMOUT,
3413                                          20 * HZ);
3414                } else {
3415                        memcpy(wrqu.addr.sa_data, &e->addr, ETH_ALEN);
3416                        WL_TRACE("Link UP\n");
3417
3418                }
3419                wrqu.addr.sa_family = ARPHRD_ETHER;
3420                break;
3421        case WLC_E_ACTION_FRAME:
3422                cmd = IWEVCUSTOM;
3423                if (datalen + 1 <= sizeof(extra)) {
3424                        wrqu.data.length = datalen + 1;
3425                        extra[0] = WLC_E_ACTION_FRAME;
3426                        memcpy(&extra[1], data, datalen);
3427                        WL_TRACE("WLC_E_ACTION_FRAME len %d\n",
3428                                 wrqu.data.length);
3429                }
3430                break;
3431
3432        case WLC_E_ACTION_FRAME_COMPLETE:
3433                cmd = IWEVCUSTOM;
3434                memcpy(&toto, data, 4);
3435                if (sizeof(status) + 1 <= sizeof(extra)) {
3436                        wrqu.data.length = sizeof(status) + 1;
3437                        extra[0] = WLC_E_ACTION_FRAME_COMPLETE;
3438                        memcpy(&extra[1], &status, sizeof(status));
3439                        printf("wl_iw_event status %d PacketId %d\n", status,
3440                               toto);
3441                        printf("WLC_E_ACTION_FRAME_COMPLETE len %d\n",
3442                               wrqu.data.length);
3443                }
3444                break;
3445#endif                          /* WIRELESS_EXT > 14 */
3446#if WIRELESS_EXT > 17
3447        case WLC_E_MIC_ERROR:
3448                {
3449                        struct iw_michaelmicfailure *micerrevt =
3450                            (struct iw_michaelmicfailure *)&extra;
3451                        cmd = IWEVMICHAELMICFAILURE;
3452                        wrqu.data.length = sizeof(struct iw_michaelmicfailure);
3453                        if (flags & WLC_EVENT_MSG_GROUP)
3454                                micerrevt->flags |= IW_MICFAILURE_GROUP;
3455                        else
3456                                micerrevt->flags |= IW_MICFAILURE_PAIRWISE;
3457                        memcpy(micerrevt->src_addr.sa_data, &e->addr,
3458                               ETH_ALEN);
3459                        micerrevt->src_addr.sa_family = ARPHRD_ETHER;
3460
3461                        break;
3462                }
3463        case WLC_E_PMKID_CACHE:
3464                {
3465                        if (data) {
3466                                struct iw_pmkid_cand *iwpmkidcand =
3467                                    (struct iw_pmkid_cand *)&extra;
3468                                pmkid_cand_list_t *pmkcandlist;
3469                                pmkid_cand_t *pmkidcand;
3470                                int count;
3471
3472                                cmd = IWEVPMKIDCAND;
3473                                pmkcandlist = data;
3474                                count =
3475                                    ntoh32_ua((u8 *) &
3476                                              pmkcandlist->npmkid_cand);
3477                                ASSERT(count >= 0);
3478                                wrqu.data.length = sizeof(struct iw_pmkid_cand);
3479                                pmkidcand = pmkcandlist->pmkid_cand;
3480                                while (count) {
3481                                        memset(iwpmkidcand, 0,
3482                                              sizeof(struct iw_pmkid_cand));
3483                                        if (pmkidcand->preauth)
3484                                                iwpmkidcand->flags |=
3485                                                    IW_PMKID_CAND_PREAUTH;
3486                                        bcopy(&pmkidcand->BSSID,
3487                                              &iwpmkidcand->bssid.sa_data,
3488                                              ETH_ALEN);
3489#ifndef SANDGATE2G
3490                                        wireless_send_event(dev, cmd, &wrqu,
3491                                                            extra);
3492#endif
3493                                        pmkidcand++;
3494                                        count--;
3495                                }
3496                        }
3497                        return;
3498                }
3499#endif                          /* WIRELESS_EXT > 17 */
3500
3501        case WLC_E_SCAN_COMPLETE:
3502#if defined(WL_IW_USE_ISCAN)
3503                if ((g_iscan) && (g_iscan->sysioc_tsk) &&
3504                    (g_iscan->iscan_state != ISCAN_STATE_IDLE)) {
3505                        up(&g_iscan->sysioc_sem);
3506                } else {
3507                        cmd = SIOCGIWSCAN;
3508                        wrqu.data.length = strlen(extra);
3509                        WL_TRACE("Event WLC_E_SCAN_COMPLETE from specific scan %d\n",
3510                                 g_iscan->iscan_state);
3511                }
3512#else
3513                cmd = SIOCGIWSCAN;
3514                wrqu.data.length = strlen(extra);
3515                WL_TRACE("Event WLC_E_SCAN_COMPLETE\n");
3516#endif
3517                break;
3518
3519        case WLC_E_PFN_NET_FOUND:
3520                {
3521                        wlc_ssid_t *ssid;
3522                        ssid = (wlc_ssid_t *) data;
3523                        WL_ERROR("%s Event WLC_E_PFN_NET_FOUND, send %s up : find %s len=%d\n",
3524                                 __func__, PNO_EVENT_UP,
3525                                 ssid->SSID, ssid->SSID_len);
3526                        WAKE_LOCK_TIMEOUT(iw->pub, WAKE_LOCK_PNO_FIND_TMOUT,
3527                                          20 * HZ);
3528                        cmd = IWEVCUSTOM;
3529                        memset(&wrqu, 0, sizeof(wrqu));
3530                        strcpy(extra, PNO_EVENT_UP);
3531                        wrqu.data.length = strlen(extra);
3532                }
3533                break;
3534
3535        default:
3536                WL_TRACE("Unknown Event %d: ignoring\n", event_type);
3537                break;
3538        }
3539#ifndef SANDGATE2G
3540        if (cmd) {
3541                if (cmd == SIOCGIWSCAN)
3542                        wireless_send_event(dev, cmd, &wrqu, NULL);
3543                else
3544                        wireless_send_event(dev, cmd, &wrqu, extra);
3545        }
3546#endif
3547
3548#if WIRELESS_EXT > 14
3549        memset(extra, 0, sizeof(extra));
3550        if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) {
3551                cmd = IWEVCUSTOM;
3552                wrqu.data.length = strlen(extra);
3553#ifndef SANDGATE2G
3554                wireless_send_event(dev, cmd, &wrqu, extra);
3555#endif
3556        }
3557#endif                          /* WIRELESS_EXT > 14 */
3558#endif                          /* WIRELESS_EXT > 13 */
3559}
3560
3561int
3562wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats)
3563{
3564        int res = 0;
3565        wl_cnt_t cnt;
3566        int phy_noise;
3567        int rssi;
3568        scb_val_t scb_val;
3569
3570        phy_noise = 0;
3571        res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise,
3572                                sizeof(phy_noise));
3573        if (res)
3574                goto done;
3575
3576        phy_noise = dtoh32(phy_noise);
3577        WL_TRACE("wl_iw_get_wireless_stats phy noise=%d\n", phy_noise);
3578
3579        memset(&scb_val, 0, sizeof(scb_val_t));
3580        res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t));
3581        if (res)
3582                goto done;
3583
3584        rssi = dtoh32(scb_val.val);
3585        WL_TRACE("wl_iw_get_wireless_stats rssi=%d\n", rssi);
3586        if (rssi <= WL_IW_RSSI_NO_SIGNAL)
3587                wstats->qual.qual = 0;
3588        else if (rssi <= WL_IW_RSSI_VERY_LOW)
3589                wstats->qual.qual = 1;
3590        else if (rssi <= WL_IW_RSSI_LOW)
3591                wstats->qual.qual = 2;
3592        else if (rssi <= WL_IW_RSSI_GOOD)
3593                wstats->qual.qual = 3;
3594        else if (rssi <= WL_IW_RSSI_VERY_GOOD)
3595                wstats->qual.qual = 4;
3596        else
3597                wstats->qual.qual = 5;
3598
3599        wstats->qual.level = 0x100 + rssi;
3600        wstats->qual.noise = 0x100 + phy_noise;
3601#if WIRELESS_EXT > 18
3602        wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM);
3603#else
3604        wstats->qual.updated |= 7;
3605#endif
3606
3607#if WIRELESS_EXT > 11
3608        WL_TRACE("wl_iw_get_wireless_stats counters=%zu\n", sizeof(wl_cnt_t));
3609
3610        memset(&cnt, 0, sizeof(wl_cnt_t));
3611        res =
3612            dev_wlc_bufvar_get(dev, "counters", (char *)&cnt, sizeof(wl_cnt_t));
3613        if (res) {
3614                WL_ERROR("wl_iw_get_wireless_stats counters failed error=%d\n",
3615                         res);
3616                goto done;
3617        }
3618
3619        cnt.version = dtoh16(cnt.version);
3620        if (cnt.version != WL_CNT_T_VERSION) {
3621                WL_TRACE("\tIncorrect version of counters struct: expected %d; got %d\n",
3622                         WL_CNT_T_VERSION, cnt.version);
3623                goto done;
3624        }
3625
3626        wstats->discard.nwid = 0;
3627        wstats->discard.code = dtoh32(cnt.rxundec);
3628        wstats->discard.fragment = dtoh32(cnt.rxfragerr);
3629        wstats->discard.retries = dtoh32(cnt.txfail);
3630        wstats->discard.misc = dtoh32(cnt.rxrunt) + dtoh32(cnt.rxgiant);
3631        wstats->miss.beacon = 0;
3632
3633        WL_TRACE("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n",
3634                 dtoh32(cnt.txframe), dtoh32(cnt.txbyte));
3635        WL_TRACE("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n",
3636                  dtoh32(cnt.rxfrmtoolong));
3637        WL_TRACE("wl_iw_get_wireless_stats counters rxbadplcp=%d\n",
3638                  dtoh32(cnt.rxbadplcp));
3639        WL_TRACE("wl_iw_get_wireless_stats counters rxundec=%d\n",
3640                  dtoh32(cnt.rxundec));
3641        WL_TRACE("wl_iw_get_wireless_stats counters rxfragerr=%d\n",
3642                  dtoh32(cnt.rxfragerr));
3643        WL_TRACE("wl_iw_get_wireless_stats counters txfail=%d\n",
3644                  dtoh32(cnt.txfail));
3645        WL_TRACE("wl_iw_get_wireless_stats counters rxrunt=%d\n",
3646                  dtoh32(cnt.rxrunt));
3647        WL_TRACE("wl_iw_get_wireless_stats counters rxgiant=%d\n",
3648                  dtoh32(cnt.rxgiant));
3649#endif                          /* WIRELESS_EXT > 11 */
3650
3651done:
3652        return res;
3653}
3654
3655int wl_iw_attach(struct net_device *dev, void *dhdp)
3656{
3657        int params_size;
3658        wl_iw_t *iw;
3659#if defined(WL_IW_USE_ISCAN)
3660        iscan_info_t *iscan = NULL;
3661
3662        if (!dev)
3663                return 0;
3664
3665        memset(&g_wl_iw_params, 0, sizeof(wl_iw_extra_params_t));
3666
3667#ifdef CSCAN
3668        params_size =
3669            (WL_SCAN_PARAMS_FIXED_SIZE + offsetof(wl_iscan_params_t, params)) +
3670            (WL_NUMCHANNELS * sizeof(u16)) +
3671            WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
3672#else
3673        params_size =
3674            (WL_SCAN_PARAMS_FIXED_SIZE + offsetof(wl_iscan_params_t, params));
3675#endif
3676        iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL);
3677
3678        if (!iscan)
3679                return -ENOMEM;
3680        memset(iscan, 0, sizeof(iscan_info_t));
3681
3682        iscan->iscan_ex_params_p = kmalloc(params_size, GFP_KERNEL);
3683        if (!iscan->iscan_ex_params_p)
3684                return -ENOMEM;
3685        iscan->iscan_ex_param_size = params_size;
3686        iscan->sysioc_tsk = NULL;
3687
3688        g_iscan = iscan;
3689        iscan->dev = dev;
3690        iscan->iscan_state = ISCAN_STATE_IDLE;
3691
3692        iscan->timer_ms = 3000;
3693        init_timer(&iscan->timer);
3694        iscan->timer.data = (unsigned long) iscan;
3695        iscan->timer.function = wl_iw_timerfunc;
3696
3697        sema_init(&iscan->sysioc_sem, 0);
3698        iscan->sysioc_tsk = kthread_run(_iscan_sysioc_thread, iscan,
3699                                        "_iscan_sysioc");
3700        if (IS_ERR(iscan->sysioc_tsk)) {
3701                iscan->sysioc_tsk = NULL;
3702                return -ENOMEM;
3703        }
3704#endif                          /* defined(WL_IW_USE_ISCAN) */
3705
3706        iw = *(wl_iw_t **) netdev_priv(dev);
3707        iw->pub = (dhd_pub_t *) dhdp;
3708        MUTEX_LOCK_INIT(iw->pub);
3709        MUTEX_LOCK_WL_SCAN_SET_INIT();
3710#ifdef SOFTAP
3711        priv_dev = dev;
3712        MUTEX_LOCK_SOFTAP_SET_INIT(iw->pub);
3713#endif
3714        g_scan = kmalloc(G_SCAN_RESULTS, GFP_KERNEL);
3715        if (!g_scan)
3716                return -ENOMEM;
3717
3718        memset(g_scan, 0, G_SCAN_RESULTS);
3719        g_scan_specified_ssid = 0;
3720
3721        return 0;
3722}
3723
3724void wl_iw_detach(void)
3725{
3726#if defined(WL_IW_USE_ISCAN)
3727        iscan_buf_t *buf;
3728        iscan_info_t *iscan = g_iscan;
3729
3730        if (!iscan)
3731                return;
3732        if (iscan->sysioc_tsk) {
3733                send_sig(SIGTERM, iscan->sysioc_tsk, 1);
3734                kthread_stop(iscan->sysioc_tsk);
3735                iscan->sysioc_tsk = NULL;
3736        }
3737
3738        MUTEX_LOCK_WL_SCAN_SET();
3739        while (iscan->list_hdr) {
3740                buf = iscan->list_hdr->next;
3741                kfree(iscan->list_hdr);
3742                iscan->list_hdr = buf;
3743        }
3744        MUTEX_UNLOCK_WL_SCAN_SET();
3745        kfree(iscan->iscan_ex_params_p);
3746        kfree(iscan);
3747        g_iscan = NULL;
3748#endif                          /* WL_IW_USE_ISCAN */
3749
3750        kfree(g_scan);
3751
3752        g_scan = NULL;
3753}
3754