linux/drivers/net/wireless/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        DECLARE_SSID_BUF(ssid);
 276
 277        LIBIPW_DEBUG_WX("Getting scan\n");
 278
 279        spin_lock_irqsave(&ieee->lock, flags);
 280
 281        list_for_each_entry(network, &ieee->network_list, list) {
 282                i++;
 283                if (stop - ev < SCAN_ITEM_SIZE) {
 284                        err = -E2BIG;
 285                        break;
 286                }
 287
 288                if (ieee->scan_age == 0 ||
 289                    time_after(network->last_scanned + ieee->scan_age, jiffies))
 290                        ev = libipw_translate_scan(ieee, ev, stop, network,
 291                                                      info);
 292                else {
 293                        LIBIPW_DEBUG_SCAN("Not showing network '%s ("
 294                                             "%pM)' due to age (%ums).\n",
 295                                             print_ssid(ssid, network->ssid,
 296                                                         network->ssid_len),
 297                                             network->bssid,
 298                                             elapsed_jiffies_msecs(
 299                                                       network->last_scanned));
 300                }
 301        }
 302
 303        spin_unlock_irqrestore(&ieee->lock, flags);
 304
 305        wrqu->data.length = ev - extra;
 306        wrqu->data.flags = 0;
 307
 308        LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
 309
 310        return err;
 311}
 312
 313int libipw_wx_set_encode(struct libipw_device *ieee,
 314                            struct iw_request_info *info,
 315                            union iwreq_data *wrqu, char *keybuf)
 316{
 317        struct iw_point *erq = &(wrqu->encoding);
 318        struct net_device *dev = ieee->dev;
 319        struct libipw_security sec = {
 320                .flags = 0
 321        };
 322        int i, key, key_provided, len;
 323        struct lib80211_crypt_data **crypt;
 324        int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
 325        DECLARE_SSID_BUF(ssid);
 326
 327        LIBIPW_DEBUG_WX("SET_ENCODE\n");
 328
 329        key = erq->flags & IW_ENCODE_INDEX;
 330        if (key) {
 331                if (key > WEP_KEYS)
 332                        return -EINVAL;
 333                key--;
 334                key_provided = 1;
 335        } else {
 336                key_provided = 0;
 337                key = ieee->crypt_info.tx_keyidx;
 338        }
 339
 340        LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
 341                           "provided" : "default");
 342
 343        crypt = &ieee->crypt_info.crypt[key];
 344
 345        if (erq->flags & IW_ENCODE_DISABLED) {
 346                if (key_provided && *crypt) {
 347                        LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
 348                                           key);
 349                        lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 350                } else
 351                        LIBIPW_DEBUG_WX("Disabling encryption.\n");
 352
 353                /* Check all the keys to see if any are still configured,
 354                 * and if no key index was provided, de-init them all */
 355                for (i = 0; i < WEP_KEYS; i++) {
 356                        if (ieee->crypt_info.crypt[i] != NULL) {
 357                                if (key_provided)
 358                                        break;
 359                                lib80211_crypt_delayed_deinit(&ieee->crypt_info,
 360                                                               &ieee->crypt_info.crypt[i]);
 361                        }
 362                }
 363
 364                if (i == WEP_KEYS) {
 365                        sec.enabled = 0;
 366                        sec.encrypt = 0;
 367                        sec.level = SEC_LEVEL_0;
 368                        sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
 369                }
 370
 371                goto done;
 372        }
 373
 374        sec.enabled = 1;
 375        sec.encrypt = 1;
 376        sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
 377
 378        if (*crypt != NULL && (*crypt)->ops != NULL &&
 379            strcmp((*crypt)->ops->name, "WEP") != 0) {
 380                /* changing to use WEP; deinit previously used algorithm
 381                 * on this key */
 382                lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 383        }
 384
 385        if (*crypt == NULL && host_crypto) {
 386                struct lib80211_crypt_data *new_crypt;
 387
 388                /* take WEP into use */
 389                new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
 390                                    GFP_KERNEL);
 391                if (new_crypt == NULL)
 392                        return -ENOMEM;
 393                new_crypt->ops = lib80211_get_crypto_ops("WEP");
 394                if (!new_crypt->ops) {
 395                        request_module("lib80211_crypt_wep");
 396                        new_crypt->ops = lib80211_get_crypto_ops("WEP");
 397                }
 398
 399                if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
 400                        new_crypt->priv = new_crypt->ops->init(key);
 401
 402                if (!new_crypt->ops || !new_crypt->priv) {
 403                        kfree(new_crypt);
 404                        new_crypt = NULL;
 405
 406                        printk(KERN_WARNING "%s: could not initialize WEP: "
 407                               "load module lib80211_crypt_wep\n", dev->name);
 408                        return -EOPNOTSUPP;
 409                }
 410                *crypt = new_crypt;
 411        }
 412
 413        /* If a new key was provided, set it up */
 414        if (erq->length > 0) {
 415                len = erq->length <= 5 ? 5 : 13;
 416                memcpy(sec.keys[key], keybuf, erq->length);
 417                if (len > erq->length)
 418                        memset(sec.keys[key] + erq->length, 0,
 419                               len - erq->length);
 420                LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
 421                                   key, print_ssid(ssid, sec.keys[key], len),
 422                                   erq->length, len);
 423                sec.key_sizes[key] = len;
 424                if (*crypt)
 425                        (*crypt)->ops->set_key(sec.keys[key], len, NULL,
 426                                               (*crypt)->priv);
 427                sec.flags |= (1 << key);
 428                /* This ensures a key will be activated if no key is
 429                 * explicitly set */
 430                if (key == sec.active_key)
 431                        sec.flags |= SEC_ACTIVE_KEY;
 432
 433        } else {
 434                if (host_crypto) {
 435                        len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
 436                                                     NULL, (*crypt)->priv);
 437                        if (len == 0) {
 438                                /* Set a default key of all 0 */
 439                                LIBIPW_DEBUG_WX("Setting key %d to all "
 440                                                   "zero.\n", key);
 441                                memset(sec.keys[key], 0, 13);
 442                                (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
 443                                                       (*crypt)->priv);
 444                                sec.key_sizes[key] = 13;
 445                                sec.flags |= (1 << key);
 446                        }
 447                }
 448                /* No key data - just set the default TX key index */
 449                if (key_provided) {
 450                        LIBIPW_DEBUG_WX("Setting key %d to default Tx "
 451                                           "key.\n", key);
 452                        ieee->crypt_info.tx_keyidx = key;
 453                        sec.active_key = key;
 454                        sec.flags |= SEC_ACTIVE_KEY;
 455                }
 456        }
 457        if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
 458                ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
 459                sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
 460                    WLAN_AUTH_SHARED_KEY;
 461                sec.flags |= SEC_AUTH_MODE;
 462                LIBIPW_DEBUG_WX("Auth: %s\n",
 463                                   sec.auth_mode == WLAN_AUTH_OPEN ?
 464                                   "OPEN" : "SHARED KEY");
 465        }
 466
 467        /* For now we just support WEP, so only set that security level...
 468         * TODO: When WPA is added this is one place that needs to change */
 469        sec.flags |= SEC_LEVEL;
 470        sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
 471        sec.encode_alg[key] = SEC_ALG_WEP;
 472
 473      done:
 474        if (ieee->set_security)
 475                ieee->set_security(dev, &sec);
 476
 477        return 0;
 478}
 479
 480int libipw_wx_get_encode(struct libipw_device *ieee,
 481                            struct iw_request_info *info,
 482                            union iwreq_data *wrqu, char *keybuf)
 483{
 484        struct iw_point *erq = &(wrqu->encoding);
 485        int len, key;
 486        struct lib80211_crypt_data *crypt;
 487        struct libipw_security *sec = &ieee->sec;
 488
 489        LIBIPW_DEBUG_WX("GET_ENCODE\n");
 490
 491        key = erq->flags & IW_ENCODE_INDEX;
 492        if (key) {
 493                if (key > WEP_KEYS)
 494                        return -EINVAL;
 495                key--;
 496        } else
 497                key = ieee->crypt_info.tx_keyidx;
 498
 499        crypt = ieee->crypt_info.crypt[key];
 500        erq->flags = key + 1;
 501
 502        if (!sec->enabled) {
 503                erq->length = 0;
 504                erq->flags |= IW_ENCODE_DISABLED;
 505                return 0;
 506        }
 507
 508        len = sec->key_sizes[key];
 509        memcpy(keybuf, sec->keys[key], len);
 510
 511        erq->length = len;
 512        erq->flags |= IW_ENCODE_ENABLED;
 513
 514        if (ieee->open_wep)
 515                erq->flags |= IW_ENCODE_OPEN;
 516        else
 517                erq->flags |= IW_ENCODE_RESTRICTED;
 518
 519        return 0;
 520}
 521
 522int libipw_wx_set_encodeext(struct libipw_device *ieee,
 523                               struct iw_request_info *info,
 524                               union iwreq_data *wrqu, char *extra)
 525{
 526        struct net_device *dev = ieee->dev;
 527        struct iw_point *encoding = &wrqu->encoding;
 528        struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 529        int i, idx, ret = 0;
 530        int group_key = 0;
 531        const char *alg, *module;
 532        struct lib80211_crypto_ops *ops;
 533        struct lib80211_crypt_data **crypt;
 534
 535        struct libipw_security sec = {
 536                .flags = 0,
 537        };
 538
 539        idx = encoding->flags & IW_ENCODE_INDEX;
 540        if (idx) {
 541                if (idx < 1 || idx > WEP_KEYS)
 542                        return -EINVAL;
 543                idx--;
 544        } else
 545                idx = ieee->crypt_info.tx_keyidx;
 546
 547        if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
 548                crypt = &ieee->crypt_info.crypt[idx];
 549                group_key = 1;
 550        } else {
 551                /* some Cisco APs use idx>0 for unicast in dynamic WEP */
 552                if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
 553                        return -EINVAL;
 554                if (ieee->iw_mode == IW_MODE_INFRA)
 555                        crypt = &ieee->crypt_info.crypt[idx];
 556                else
 557                        return -EINVAL;
 558        }
 559
 560        sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
 561        if ((encoding->flags & IW_ENCODE_DISABLED) ||
 562            ext->alg == IW_ENCODE_ALG_NONE) {
 563                if (*crypt)
 564                        lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 565
 566                for (i = 0; i < WEP_KEYS; i++)
 567                        if (ieee->crypt_info.crypt[i] != NULL)
 568                                break;
 569
 570                if (i == WEP_KEYS) {
 571                        sec.enabled = 0;
 572                        sec.encrypt = 0;
 573                        sec.level = SEC_LEVEL_0;
 574                        sec.flags |= SEC_LEVEL;
 575                }
 576                goto done;
 577        }
 578
 579        sec.enabled = 1;
 580        sec.encrypt = 1;
 581
 582        if (group_key ? !ieee->host_mc_decrypt :
 583            !(ieee->host_encrypt || ieee->host_decrypt ||
 584              ieee->host_encrypt_msdu))
 585                goto skip_host_crypt;
 586
 587        switch (ext->alg) {
 588        case IW_ENCODE_ALG_WEP:
 589                alg = "WEP";
 590                module = "lib80211_crypt_wep";
 591                break;
 592        case IW_ENCODE_ALG_TKIP:
 593                alg = "TKIP";
 594                module = "lib80211_crypt_tkip";
 595                break;
 596        case IW_ENCODE_ALG_CCMP:
 597                alg = "CCMP";
 598                module = "lib80211_crypt_ccmp";
 599                break;
 600        default:
 601                LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
 602                                   dev->name, ext->alg);
 603                ret = -EINVAL;
 604                goto done;
 605        }
 606
 607        ops = lib80211_get_crypto_ops(alg);
 608        if (ops == NULL) {
 609                request_module(module);
 610                ops = lib80211_get_crypto_ops(alg);
 611        }
 612        if (ops == NULL) {
 613                LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
 614                                   dev->name, ext->alg);
 615                ret = -EINVAL;
 616                goto done;
 617        }
 618
 619        if (*crypt == NULL || (*crypt)->ops != ops) {
 620                struct lib80211_crypt_data *new_crypt;
 621
 622                lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
 623
 624                new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
 625                if (new_crypt == NULL) {
 626                        ret = -ENOMEM;
 627                        goto done;
 628                }
 629                new_crypt->ops = ops;
 630                if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
 631                        new_crypt->priv = new_crypt->ops->init(idx);
 632                if (new_crypt->priv == NULL) {
 633                        kfree(new_crypt);
 634                        ret = -EINVAL;
 635                        goto done;
 636                }
 637                *crypt = new_crypt;
 638        }
 639
 640        if (ext->key_len > 0 && (*crypt)->ops->set_key &&
 641            (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
 642                                   (*crypt)->priv) < 0) {
 643                LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
 644                ret = -EINVAL;
 645                goto done;
 646        }
 647
 648      skip_host_crypt:
 649        if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
 650                ieee->crypt_info.tx_keyidx = idx;
 651                sec.active_key = idx;
 652                sec.flags |= SEC_ACTIVE_KEY;
 653        }
 654
 655        if (ext->alg != IW_ENCODE_ALG_NONE) {
 656                memcpy(sec.keys[idx], ext->key, ext->key_len);
 657                sec.key_sizes[idx] = ext->key_len;
 658                sec.flags |= (1 << idx);
 659                if (ext->alg == IW_ENCODE_ALG_WEP) {
 660                        sec.encode_alg[idx] = SEC_ALG_WEP;
 661                        sec.flags |= SEC_LEVEL;
 662                        sec.level = SEC_LEVEL_1;
 663                } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
 664                        sec.encode_alg[idx] = SEC_ALG_TKIP;
 665                        sec.flags |= SEC_LEVEL;
 666                        sec.level = SEC_LEVEL_2;
 667                } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
 668                        sec.encode_alg[idx] = SEC_ALG_CCMP;
 669                        sec.flags |= SEC_LEVEL;
 670                        sec.level = SEC_LEVEL_3;
 671                }
 672                /* Don't set sec level for group keys. */
 673                if (group_key)
 674                        sec.flags &= ~SEC_LEVEL;
 675        }
 676      done:
 677        if (ieee->set_security)
 678                ieee->set_security(dev, &sec);
 679
 680        return ret;
 681}
 682
 683int libipw_wx_get_encodeext(struct libipw_device *ieee,
 684                               struct iw_request_info *info,
 685                               union iwreq_data *wrqu, char *extra)
 686{
 687        struct iw_point *encoding = &wrqu->encoding;
 688        struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 689        struct libipw_security *sec = &ieee->sec;
 690        int idx, max_key_len;
 691
 692        max_key_len = encoding->length - sizeof(*ext);
 693        if (max_key_len < 0)
 694                return -EINVAL;
 695
 696        idx = encoding->flags & IW_ENCODE_INDEX;
 697        if (idx) {
 698                if (idx < 1 || idx > WEP_KEYS)
 699                        return -EINVAL;
 700                idx--;
 701        } else
 702                idx = ieee->crypt_info.tx_keyidx;
 703
 704        if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
 705            ext->alg != IW_ENCODE_ALG_WEP)
 706                if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
 707                        return -EINVAL;
 708
 709        encoding->flags = idx + 1;
 710        memset(ext, 0, sizeof(*ext));
 711
 712        if (!sec->enabled) {
 713                ext->alg = IW_ENCODE_ALG_NONE;
 714                ext->key_len = 0;
 715                encoding->flags |= IW_ENCODE_DISABLED;
 716        } else {
 717                if (sec->encode_alg[idx] == SEC_ALG_WEP)
 718                        ext->alg = IW_ENCODE_ALG_WEP;
 719                else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
 720                        ext->alg = IW_ENCODE_ALG_TKIP;
 721                else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
 722                        ext->alg = IW_ENCODE_ALG_CCMP;
 723                else
 724                        return -EINVAL;
 725
 726                ext->key_len = sec->key_sizes[idx];
 727                memcpy(ext->key, sec->keys[idx], ext->key_len);
 728                encoding->flags |= IW_ENCODE_ENABLED;
 729                if (ext->key_len &&
 730                    (ext->alg == IW_ENCODE_ALG_TKIP ||
 731                     ext->alg == IW_ENCODE_ALG_CCMP))
 732                        ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
 733
 734        }
 735
 736        return 0;
 737}
 738
 739EXPORT_SYMBOL(libipw_wx_set_encodeext);
 740EXPORT_SYMBOL(libipw_wx_get_encodeext);
 741
 742EXPORT_SYMBOL(libipw_wx_get_scan);
 743EXPORT_SYMBOL(libipw_wx_set_encode);
 744EXPORT_SYMBOL(libipw_wx_get_encode);
 745