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