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