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