Merge branch 'for-2.6.30' of git://linux-nfs.org/~bfields/linux
[linux-2.6] / drivers / net / wireless / ipw2x00 / libipw_wx.c
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   James P. Ketrenos <ipw2100-admin@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 "ieee80211.h"
41
42 static const char *ieee80211_modes[] = {
43         "?", "a", "b", "ab", "g", "ag", "bg", "abg"
44 };
45
46 static 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
57 static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
58                                       char *start, char *stop,
59                                       struct ieee80211_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                  ieee80211_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 = ieee80211_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 & IEEE80211_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 & IEEE80211_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 & IEEE80211_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 (ieee80211_get_channel_flags(ieee, network->channel) &
241             IEEE80211_CH_INVALID) {
242                 iwe.cmd = IWEVCUSTOM;
243                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
244         }
245
246         if (ieee80211_get_channel_flags(ieee, network->channel) &
247             IEEE80211_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
262 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
263                           struct iw_request_info *info,
264                           union iwreq_data *wrqu, char *extra)
265 {
266         struct ieee80211_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         IEEE80211_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 = ieee80211_translate_scan(ieee, ev, stop, network,
289                                                       info);
290                 else {
291                         IEEE80211_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         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
307
308         return err;
309 }
310
311 int ieee80211_wx_set_encode(struct ieee80211_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 ieee80211_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         IEEE80211_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         IEEE80211_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                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
346                                            key);
347                         lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
348                 } else
349                         IEEE80211_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_IEEE80211_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                 IEEE80211_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                                 IEEE80211_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                         IEEE80211_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                 IEEE80211_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
493 int ieee80211_wx_get_encode(struct ieee80211_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 ieee80211_security *sec = &ieee->sec;
501
502         IEEE80211_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
535 int ieee80211_wx_set_encodeext(struct ieee80211_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 ieee80211_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                 IEEE80211_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                 IEEE80211_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                 IEEE80211_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                 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
704                 return -EINVAL;
705         }
706
707         return ret;
708 }
709
710 int ieee80211_wx_get_encodeext(struct ieee80211_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 ieee80211_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
766 EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
767 EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
768
769 EXPORT_SYMBOL(ieee80211_wx_get_scan);
770 EXPORT_SYMBOL(ieee80211_wx_set_encode);
771 EXPORT_SYMBOL(ieee80211_wx_get_encode);