linux/drivers/net/wireless/intel/ipw2x00/libipw_wx.c
<<
>>
Prefs
   1/******************************************************************************
   2
   3  Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
   4
   5  Portions of this file are based on the WEP enablement code provided by the
   6  Host AP project hostap-drivers v0.1.3
   7  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
   8  <j@w1.fi>
   9  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
  10
  11  This program is free software; you can redistribute it and/or modify it
  12  under the terms of version 2 of the GNU General Public License as
  13  published by the Free Software Foundation.
  14
  15  This program is distributed in the hope that it will be useful, but WITHOUT
  16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  17  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  18  more details.
  19
  20  You should have received a copy of the GNU General Public License along with
  21  this program; if not, write to the Free Software Foundation, Inc., 59
  22  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  23
  24  The full GNU General Public License is included in this distribution in the
  25  file called LICENSE.
  26
  27  Contact Information:
  28  Intel Linux Wireless <ilw@linux.intel.com>
  29  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  30
  31******************************************************************************/
  32
  33#include <linux/hardirq.h>
  34#include <linux/kmod.h>
  35#include <linux/slab.h>
  36#include <linux/module.h>
  37#include <linux/jiffies.h>
  38
  39#include <net/lib80211.h>
  40#include <linux/wireless.h>
  41
  42#include "libipw.h"
  43
  44static const char *libipw_modes[] = {
  45        "?", "a", "b", "ab", "g", "ag", "bg", "abg"
  46};
  47
  48static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
  49{
  50        unsigned long end = jiffies;
  51
  52        if (end >= start)
  53                return jiffies_to_msecs(end - start);
  54
  55        return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
  56}
  57
  58#define MAX_CUSTOM_LEN 64
  59static char *libipw_translate_scan(struct libipw_device *ieee,
  60                                      char *start, char *stop,
  61                                      struct libipw_network *network,
  62                                      struct iw_request_info *info)
  63{
  64        char custom[MAX_CUSTOM_LEN];
  65        char *p;
  66        struct iw_event iwe;
  67        int i, j;
  68        char *current_val;      /* For rates */
  69        u8 rate;
  70
  71        /* First entry *MUST* be the AP MAC address */
  72        iwe.cmd = SIOCGIWAP;
  73        iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
  74        memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
  75        start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
  76
  77        /* Remaining entries will be displayed in the order we provide them */
  78
  79        /* Add the ESSID */
  80        iwe.cmd = SIOCGIWESSID;
  81        iwe.u.data.flags = 1;
  82        iwe.u.data.length = min(network->ssid_len, (u8) 32);
  83        start = iwe_stream_add_point(info, start, stop,
  84                                     &iwe, network->ssid);
  85
  86        /* Add the protocol name */
  87        iwe.cmd = SIOCGIWNAME;
  88        snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
  89                 libipw_modes[network->mode]);
  90        start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
  91
  92        /* Add mode */
  93        iwe.cmd = SIOCGIWMODE;
  94        if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
  95                if (network->capability & WLAN_CAPABILITY_ESS)
  96                        iwe.u.mode = IW_MODE_MASTER;
  97                else
  98                        iwe.u.mode = IW_MODE_ADHOC;
  99
 100                start = iwe_stream_add_event(info, start, stop,
 101                                             &iwe, IW_EV_UINT_LEN);
 102        }
 103
 104        /* Add channel and frequency */
 105        /* Note : userspace automatically computes channel using iwrange */
 106        iwe.cmd = SIOCGIWFREQ;
 107        iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
 108        iwe.u.freq.e = 6;
 109        iwe.u.freq.i = 0;
 110        start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
 111
 112        /* Add encryption capability */
 113        iwe.cmd = SIOCGIWENCODE;
 114        if (network->capability & WLAN_CAPABILITY_PRIVACY)
 115                iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
 116        else
 117                iwe.u.data.flags = IW_ENCODE_DISABLED;
 118        iwe.u.data.length = 0;
 119        start = iwe_stream_add_point(info, start, stop,
 120                                     &iwe, network->ssid);
 121
 122        /* Add basic and extended rates */
 123        /* Rate : stuffing multiple values in a single event require a bit
 124         * more of magic - Jean II */
 125        current_val = start + iwe_stream_lcp_len(info);
 126        iwe.cmd = SIOCGIWRATE;
 127        /* Those two flags are ignored... */
 128        iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
 129
 130        for (i = 0, j = 0; i < network->rates_len;) {
 131                if (j < network->rates_ex_len &&
 132                    ((network->rates_ex[j] & 0x7F) <
 133                     (network->rates[i] & 0x7F)))
 134                        rate = network->rates_ex[j++] & 0x7F;
 135                else
 136                        rate = network->rates[i++] & 0x7F;
 137                /* Bit rate given in 500 kb/s units (+ 0x80) */
 138                iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
 139                /* Add new value to event */
 140                current_val = iwe_stream_add_value(info, start, current_val,
 141                                                   stop, &iwe, IW_EV_PARAM_LEN);
 142        }
 143        for (; j < network->rates_ex_len; j++) {
 144                rate = network->rates_ex[j] & 0x7F;
 145                /* Bit rate given in 500 kb/s units (+ 0x80) */
 146                iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
 147                /* Add new value to event */
 148                current_val = iwe_stream_add_value(info, start, current_val,
 149                                                   stop, &iwe, IW_EV_PARAM_LEN);
 150        }
 151        /* Check if we added any rate */
 152        if ((current_val - start) > iwe_stream_lcp_len(info))
 153                start = current_val;
 154
 155        /* Add quality statistics */
 156        iwe.cmd = IWEVQUAL;
 157        iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
 158            IW_QUAL_NOISE_UPDATED;
 159
 160        if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
 161                iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
 162                    IW_QUAL_LEVEL_INVALID;
 163                iwe.u.qual.qual = 0;
 164        } else {
 165                if (ieee->perfect_rssi == ieee->worst_rssi)
 166                        iwe.u.qual.qual = 100;
 167                else
 168                        iwe.u.qual.qual =
 169                            (100 *
 170                             (ieee->perfect_rssi - ieee->worst_rssi) *
 171                             (ieee->perfect_rssi - ieee->worst_rssi) -
 172                             (ieee->perfect_rssi - network->stats.rssi) *
 173                             (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
 174                              62 * (ieee->perfect_rssi -
 175                                    network->stats.rssi))) /
 176                            ((ieee->perfect_rssi -
 177                              ieee->worst_rssi) * (ieee->perfect_rssi -
 178                                                   ieee->worst_rssi));
 179                if (iwe.u.qual.qual > 100)
 180                        iwe.u.qual.qual = 100;
 181                else if (iwe.u.qual.qual < 1)
 182                        iwe.u.qual.qual = 0;
 183        }
 184
 185        if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
 186                iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
 187                iwe.u.qual.noise = 0;
 188        } else {
 189                iwe.u.qual.noise = network->stats.noise;
 190        }
 191
 192        if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
 193                iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
 194                iwe.u.qual.level = 0;
 195        } else {
 196                iwe.u.qual.level = network->stats.signal;
 197        }
 198
 199        start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
 200
 201        iwe.cmd = IWEVCUSTOM;
 202        p = custom;
 203
 204        iwe.u.data.length = p - custom;
 205        if (iwe.u.data.length)
 206                start = iwe_stream_add_point(info, start, stop, &iwe, custom);
 207
 208        memset(&iwe, 0, sizeof(iwe));
 209        if (network->wpa_ie_len) {
 210                char buf[MAX_WPA_IE_LEN];
 211                memcpy(buf, network->wpa_ie, network->wpa_ie_len);
 212                iwe.cmd = IWEVGENIE;
 213                iwe.u.data.length = network->wpa_ie_len;
 214                start = iwe_stream_add_point(info, start, stop, &iwe, buf);
 215        }
 216
 217        memset(&iwe, 0, sizeof(iwe));
 218        if (network->rsn_ie_len) {
 219                char buf[MAX_WPA_IE_LEN];
 220                memcpy(buf, network->rsn_ie, network->rsn_ie_len);
 221                iwe.cmd = IWEVGENIE;
 222                iwe.u.data.length = network->rsn_ie_len;
 223                start = iwe_stream_add_point(info, start, stop, &iwe, buf);
 224        }
 225
 226        /* Add EXTRA: Age to display seconds since last beacon/probe response
 227         * for given network. */
 228        iwe.cmd = IWEVCUSTOM;
 229        p = custom;
 230        p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
 231                      " Last beacon: %ums ago",
 232                      elapsed_jiffies_msecs(network->last_scanned));
 233        iwe.u.data.length = p - custom;
 234        if (iwe.u.data.length)
 235                start = iwe_stream_add_point(info, start, stop, &iwe, custom);
 236
 237        /* Add spectrum management information */
 238        iwe.cmd = -1;
 239        p = custom;
 240        p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
 241
 242        if (libipw_get_channel_flags(ieee, network->channel) &
 243            LIBIPW_CH_INVALID) {
 244                iwe.cmd = IWEVCUSTOM;
 245                p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
 246        }
 247
 248        if (libipw_get_channel_flags(ieee, network->channel) &
 249            LIBIPW_CH_RADAR_DETECT) {
 250                iwe.cmd = IWEVCUSTOM;
 251                p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
 252        }
 253
 254        if (iwe.cmd == IWEVCUSTOM) {
 255                iwe.u.data.length = p - custom;
 256                start = iwe_stream_add_point(info, start, stop, &iwe, custom);
 257        }
 258
 259        return start;
 260}
 261
 262#define SCAN_ITEM_SIZE 128
 263
 264int libipw_wx_get_scan(struct libipw_device *ieee,
 265                          struct iw_request_info *info,
 266                          union iwreq_data *wrqu, char *extra)
 267{
 268        struct libipw_network *network;
 269        unsigned long flags;
 270        int err = 0;
 271
 272        char *ev = extra;
 273        char *stop = ev + wrqu->data.length;
 274        int i = 0;
 275
 276        LIBIPW_DEBUG_WX("Getting scan\n");
 277
 278        spin_lock_irqsave(&ieee->lock, flags);
 279
 280        list_for_each_entry(network, &ieee->network_list, list) {
 281                i++;
 282                if (stop - ev < SCAN_ITEM_SIZE) {
 283                        err = -E2BIG;
 284                        break;
 285                }
 286
 287                if (ieee->scan_age == 0 ||
 288                    time_after(network->last_scanned + ieee->scan_age, jiffies))
 289                        ev = libipw_translate_scan(ieee, ev, stop, network,
 290                                                      info);
 291                else {
 292                        LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n",
 293                                          network->ssid_len, network->ssid,
 294                                          network->bssid,
 295                                          elapsed_jiffies_msecs(
 296                                                       network->last_scanned));
 297                }
 298        }
 299
 300        spin_unlock_irqrestore(&ieee->lock, flags);
 301
 302        wrqu->data.length = ev - extra;
 303        wrqu->data.flags = 0;
 304
 305        LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
 306
 307        return err;
 308}
 309
 310int libipw_wx_set_encode(struct libipw_device *ieee,
 311                            struct iw_request_info *info,
 312                            union iwreq_data *wrqu, char *keybuf)
 313{
 314        struct iw_point *erq = &(wrqu->encoding);
 315        struct net_device *dev = ieee->dev;
 316        struct libipw_security sec = {
 317                .flags = 0
 318        };
 319        int i, key, key_provided, len;
 320        struct lib80211_crypt_data **crypt;
 321        int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
 322
 323        LIBIPW_DEBUG_WX("SET_ENCODE\n");
 324
 325        key = erq->flags & IW_ENCODE_INDEX;
 326        if (key) {
 327                if (key > WEP_KEYS)
 328                        return -EINVAL;
 329                key--;
 330                key_provided = 1;
 331        } else {
 332                key_provided = 0;
 333                key = ieee->crypt_info.tx_keyidx;
 334        }
 335
 336        LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
 337                           "provided" : "default");
 338
 339        crypt = &ieee->crypt_info.crypt[key];
 340
 341        if (erq->flags & IW_ENCODE_DISABLED) {
 342                if (key_provided && *crypt) {
 343                        LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
 344                                           key);
 345                        lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 346                } else
 347                        LIBIPW_DEBUG_WX("Disabling encryption.\n");
 348
 349                /* Check all the keys to see if any are still configured,
 350                 * and if no key index was provided, de-init them all */
 351                for (i = 0; i < WEP_KEYS; i++) {
 352                        if (ieee->crypt_info.crypt[i] != NULL) {
 353                                if (key_provided)
 354                                        break;
 355                                lib80211_crypt_delayed_deinit(&ieee->crypt_info,
 356                                                               &ieee->crypt_info.crypt[i]);
 357                        }
 358                }
 359
 360                if (i == WEP_KEYS) {
 361                        sec.enabled = 0;
 362                        sec.encrypt = 0;
 363                        sec.level = SEC_LEVEL_0;
 364                        sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
 365                }
 366
 367                goto done;
 368        }
 369
 370        sec.enabled = 1;
 371        sec.encrypt = 1;
 372        sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
 373
 374        if (*crypt != NULL && (*crypt)->ops != NULL &&
 375            strcmp((*crypt)->ops->name, "WEP") != 0) {
 376                /* changing to use WEP; deinit previously used algorithm
 377                 * on this key */
 378                lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 379        }
 380
 381        if (*crypt == NULL && host_crypto) {
 382                struct lib80211_crypt_data *new_crypt;
 383
 384                /* take WEP into use */
 385                new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
 386                                    GFP_KERNEL);
 387                if (new_crypt == NULL)
 388                        return -ENOMEM;
 389                new_crypt->ops = lib80211_get_crypto_ops("WEP");
 390                if (!new_crypt->ops) {
 391                        request_module("lib80211_crypt_wep");
 392                        new_crypt->ops = lib80211_get_crypto_ops("WEP");
 393                }
 394
 395                if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
 396                        new_crypt->priv = new_crypt->ops->init(key);
 397
 398                if (!new_crypt->ops || !new_crypt->priv) {
 399                        kfree(new_crypt);
 400                        new_crypt = NULL;
 401
 402                        printk(KERN_WARNING "%s: could not initialize WEP: "
 403                               "load module lib80211_crypt_wep\n", dev->name);
 404                        return -EOPNOTSUPP;
 405                }
 406                *crypt = new_crypt;
 407        }
 408
 409        /* If a new key was provided, set it up */
 410        if (erq->length > 0) {
 411                len = erq->length <= 5 ? 5 : 13;
 412                memcpy(sec.keys[key], keybuf, erq->length);
 413                if (len > erq->length)
 414                        memset(sec.keys[key] + erq->length, 0,
 415                               len - erq->length);
 416                LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
 417                                   key, len, sec.keys[key],
 418                                   erq->length, len);
 419                sec.key_sizes[key] = len;
 420                if (*crypt)
 421                        (*crypt)->ops->set_key(sec.keys[key], len, NULL,
 422                                               (*crypt)->priv);
 423                sec.flags |= (1 << key);
 424                /* This ensures a key will be activated if no key is
 425                 * explicitly set */
 426                if (key == sec.active_key)
 427                        sec.flags |= SEC_ACTIVE_KEY;
 428
 429        } else {
 430                if (host_crypto) {
 431                        len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
 432                                                     NULL, (*crypt)->priv);
 433                        if (len == 0) {
 434                                /* Set a default key of all 0 */
 435                                LIBIPW_DEBUG_WX("Setting key %d to all "
 436                                                   "zero.\n", key);
 437                                memset(sec.keys[key], 0, 13);
 438                                (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
 439                                                       (*crypt)->priv);
 440                                sec.key_sizes[key] = 13;
 441                                sec.flags |= (1 << key);
 442                        }
 443                }
 444                /* No key data - just set the default TX key index */
 445                if (key_provided) {
 446                        LIBIPW_DEBUG_WX("Setting key %d to default Tx "
 447                                           "key.\n", key);
 448                        ieee->crypt_info.tx_keyidx = key;
 449                        sec.active_key = key;
 450                        sec.flags |= SEC_ACTIVE_KEY;
 451                }
 452        }
 453        if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
 454                ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
 455                sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
 456                    WLAN_AUTH_SHARED_KEY;
 457                sec.flags |= SEC_AUTH_MODE;
 458                LIBIPW_DEBUG_WX("Auth: %s\n",
 459                                   sec.auth_mode == WLAN_AUTH_OPEN ?
 460                                   "OPEN" : "SHARED KEY");
 461        }
 462
 463        /* For now we just support WEP, so only set that security level...
 464         * TODO: When WPA is added this is one place that needs to change */
 465        sec.flags |= SEC_LEVEL;
 466        sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
 467        sec.encode_alg[key] = SEC_ALG_WEP;
 468
 469      done:
 470        if (ieee->set_security)
 471                ieee->set_security(dev, &sec);
 472
 473        return 0;
 474}
 475
 476int libipw_wx_get_encode(struct libipw_device *ieee,
 477                            struct iw_request_info *info,
 478                            union iwreq_data *wrqu, char *keybuf)
 479{
 480        struct iw_point *erq = &(wrqu->encoding);
 481        int len, key;
 482        struct lib80211_crypt_data *crypt;
 483        struct libipw_security *sec = &ieee->sec;
 484
 485        LIBIPW_DEBUG_WX("GET_ENCODE\n");
 486
 487        key = erq->flags & IW_ENCODE_INDEX;
 488        if (key) {
 489                if (key > WEP_KEYS)
 490                        return -EINVAL;
 491                key--;
 492        } else
 493                key = ieee->crypt_info.tx_keyidx;
 494
 495        crypt = ieee->crypt_info.crypt[key];
 496        erq->flags = key + 1;
 497
 498        if (!sec->enabled) {
 499                erq->length = 0;
 500                erq->flags |= IW_ENCODE_DISABLED;
 501                return 0;
 502        }
 503
 504        len = sec->key_sizes[key];
 505        memcpy(keybuf, sec->keys[key], len);
 506
 507        erq->length = len;
 508        erq->flags |= IW_ENCODE_ENABLED;
 509
 510        if (ieee->open_wep)
 511                erq->flags |= IW_ENCODE_OPEN;
 512        else
 513                erq->flags |= IW_ENCODE_RESTRICTED;
 514
 515        return 0;
 516}
 517
 518int libipw_wx_set_encodeext(struct libipw_device *ieee,
 519                               struct iw_request_info *info,
 520                               union iwreq_data *wrqu, char *extra)
 521{
 522        struct net_device *dev = ieee->dev;
 523        struct iw_point *encoding = &wrqu->encoding;
 524        struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 525        int i, idx, ret = 0;
 526        int group_key = 0;
 527        const char *alg, *module;
 528        struct lib80211_crypto_ops *ops;
 529        struct lib80211_crypt_data **crypt;
 530
 531        struct libipw_security sec = {
 532                .flags = 0,
 533        };
 534
 535        idx = encoding->flags & IW_ENCODE_INDEX;
 536        if (idx) {
 537                if (idx < 1 || idx > WEP_KEYS)
 538                        return -EINVAL;
 539                idx--;
 540        } else
 541                idx = ieee->crypt_info.tx_keyidx;
 542
 543        if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
 544                crypt = &ieee->crypt_info.crypt[idx];
 545                group_key = 1;
 546        } else {
 547                /* some Cisco APs use idx>0 for unicast in dynamic WEP */
 548                if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
 549                        return -EINVAL;
 550                if (ieee->iw_mode == IW_MODE_INFRA)
 551                        crypt = &ieee->crypt_info.crypt[idx];
 552                else
 553                        return -EINVAL;
 554        }
 555
 556        sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
 557        if ((encoding->flags & IW_ENCODE_DISABLED) ||
 558            ext->alg == IW_ENCODE_ALG_NONE) {
 559                if (*crypt)
 560                        lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 561
 562                for (i = 0; i < WEP_KEYS; i++)
 563                        if (ieee->crypt_info.crypt[i] != NULL)
 564                                break;
 565
 566                if (i == WEP_KEYS) {
 567                        sec.enabled = 0;
 568                        sec.encrypt = 0;
 569                        sec.level = SEC_LEVEL_0;
 570                        sec.flags |= SEC_LEVEL;
 571                }
 572                goto done;
 573        }
 574
 575        sec.enabled = 1;
 576        sec.encrypt = 1;
 577
 578        if (group_key ? !ieee->host_mc_decrypt :
 579            !(ieee->host_encrypt || ieee->host_decrypt ||
 580              ieee->host_encrypt_msdu))
 581                goto skip_host_crypt;
 582
 583        switch (ext->alg) {
 584        case IW_ENCODE_ALG_WEP:
 585                alg = "WEP";
 586                module = "lib80211_crypt_wep";
 587                break;
 588        case IW_ENCODE_ALG_TKIP:
 589                alg = "TKIP";
 590                module = "lib80211_crypt_tkip";
 591                break;
 592        case IW_ENCODE_ALG_CCMP:
 593                alg = "CCMP";
 594                module = "lib80211_crypt_ccmp";
 595                break;
 596        default:
 597                LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
 598                                   dev->name, ext->alg);
 599                ret = -EINVAL;
 600                goto done;
 601        }
 602
 603        ops = lib80211_get_crypto_ops(alg);
 604        if (ops == NULL) {
 605                request_module(module);
 606                ops = lib80211_get_crypto_ops(alg);
 607        }
 608        if (ops == NULL) {
 609                LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
 610                                   dev->name, ext->alg);
 611                ret = -EINVAL;
 612                goto done;
 613        }
 614
 615        if (*crypt == NULL || (*crypt)->ops != ops) {
 616                struct lib80211_crypt_data *new_crypt;
 617
 618                lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 619
 620                new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
 621                if (new_crypt == NULL) {
 622                        ret = -ENOMEM;
 623                        goto done;
 624                }
 625                new_crypt->ops = ops;
 626                if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
 627                        new_crypt->priv = new_crypt->ops->init(idx);
 628                if (new_crypt->priv == NULL) {
 629                        kfree(new_crypt);
 630                        ret = -EINVAL;
 631                        goto done;
 632                }
 633                *crypt = new_crypt;
 634        }
 635
 636        if (ext->key_len > 0 && (*crypt)->ops->set_key &&
 637            (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
 638                                   (*crypt)->priv) < 0) {
 639                LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
 640                ret = -EINVAL;
 641                goto done;
 642        }
 643
 644      skip_host_crypt:
 645        if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
 646                ieee->crypt_info.tx_keyidx = idx;
 647                sec.active_key = idx;
 648                sec.flags |= SEC_ACTIVE_KEY;
 649        }
 650
 651        if (ext->alg != IW_ENCODE_ALG_NONE) {
 652                memcpy(sec.keys[idx], ext->key, ext->key_len);
 653                sec.key_sizes[idx] = ext->key_len;
 654                sec.flags |= (1 << idx);
 655                if (ext->alg == IW_ENCODE_ALG_WEP) {
 656                        sec.encode_alg[idx] = SEC_ALG_WEP;
 657                        sec.flags |= SEC_LEVEL;
 658                        sec.level = SEC_LEVEL_1;
 659                } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
 660                        sec.encode_alg[idx] = SEC_ALG_TKIP;
 661                        sec.flags |= SEC_LEVEL;
 662                        sec.level = SEC_LEVEL_2;
 663                } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
 664                        sec.encode_alg[idx] = SEC_ALG_CCMP;
 665                        sec.flags |= SEC_LEVEL;
 666                        sec.level = SEC_LEVEL_3;
 667                }
 668                /* Don't set sec level for group keys. */
 669                if (group_key)
 670                        sec.flags &= ~SEC_LEVEL;
 671        }
 672      done:
 673        if (ieee->set_security)
 674                ieee->set_security(dev, &sec);
 675
 676        return ret;
 677}
 678
 679int libipw_wx_get_encodeext(struct libipw_device *ieee,
 680                               struct iw_request_info *info,
 681                               union iwreq_data *wrqu, char *extra)
 682{
 683        struct iw_point *encoding = &wrqu->encoding;
 684        struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 685        struct libipw_security *sec = &ieee->sec;
 686        int idx, max_key_len;
 687
 688        max_key_len = encoding->length - sizeof(*ext);
 689        if (max_key_len < 0)
 690                return -EINVAL;
 691
 692        idx = encoding->flags & IW_ENCODE_INDEX;
 693        if (idx) {
 694                if (idx < 1 || idx > WEP_KEYS)
 695                        return -EINVAL;
 696                idx--;
 697        } else
 698                idx = ieee->crypt_info.tx_keyidx;
 699
 700        if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
 701            ext->alg != IW_ENCODE_ALG_WEP)
 702                if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
 703                        return -EINVAL;
 704
 705        encoding->flags = idx + 1;
 706        memset(ext, 0, sizeof(*ext));
 707
 708        if (!sec->enabled) {
 709                ext->alg = IW_ENCODE_ALG_NONE;
 710                ext->key_len = 0;
 711                encoding->flags |= IW_ENCODE_DISABLED;
 712        } else {
 713                if (sec->encode_alg[idx] == SEC_ALG_WEP)
 714                        ext->alg = IW_ENCODE_ALG_WEP;
 715                else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
 716                        ext->alg = IW_ENCODE_ALG_TKIP;
 717                else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
 718                        ext->alg = IW_ENCODE_ALG_CCMP;
 719                else
 720                        return -EINVAL;
 721
 722                ext->key_len = sec->key_sizes[idx];
 723                memcpy(ext->key, sec->keys[idx], ext->key_len);
 724                encoding->flags |= IW_ENCODE_ENABLED;
 725                if (ext->key_len &&
 726                    (ext->alg == IW_ENCODE_ALG_TKIP ||
 727                     ext->alg == IW_ENCODE_ALG_CCMP))
 728                        ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
 729
 730        }
 731
 732        return 0;
 733}
 734
 735EXPORT_SYMBOL(libipw_wx_set_encodeext);
 736EXPORT_SYMBOL(libipw_wx_get_encodeext);
 737
 738EXPORT_SYMBOL(libipw_wx_get_scan);
 739EXPORT_SYMBOL(libipw_wx_set_encode);
 740EXPORT_SYMBOL(libipw_wx_get_encode);
 741