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