ee7a70a132500c5834c8bb8d5e826f2bb8c0c235
[pandora-kernel.git] / net / ieee80211 / ieee80211_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   <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(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         if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
416                 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
417                 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
418                     WLAN_AUTH_SHARED_KEY;
419                 sec.flags |= SEC_AUTH_MODE;
420                 IEEE80211_DEBUG_WX("Auth: %s\n",
421                                    sec.auth_mode == WLAN_AUTH_OPEN ?
422                                    "OPEN" : "SHARED KEY");
423         }
424
425         /* For now we just support WEP, so only set that security level...
426          * TODO: When WPA is added this is one place that needs to change */
427         sec.flags |= SEC_LEVEL;
428         sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
429         sec.encode_alg[key] = SEC_ALG_WEP;
430
431       done:
432         if (ieee->set_security)
433                 ieee->set_security(dev, &sec);
434
435         /* Do not reset port if card is in Managed mode since resetting will
436          * generate new IEEE 802.11 authentication which may end up in looping
437          * with IEEE 802.1X.  If your hardware requires a reset after WEP
438          * configuration (for example... Prism2), implement the reset_port in
439          * the callbacks structures used to initialize the 802.11 stack. */
440         if (ieee->reset_on_keychange &&
441             ieee->iw_mode != IW_MODE_INFRA &&
442             ieee->reset_port && ieee->reset_port(dev)) {
443                 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
444                 return -EINVAL;
445         }
446         return 0;
447 }
448
449 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
450                             struct iw_request_info *info,
451                             union iwreq_data *wrqu, char *keybuf)
452 {
453         struct iw_point *erq = &(wrqu->encoding);
454         int len, key;
455         struct ieee80211_crypt_data *crypt;
456         struct ieee80211_security *sec = &ieee->sec;
457
458         IEEE80211_DEBUG_WX("GET_ENCODE\n");
459
460         key = erq->flags & IW_ENCODE_INDEX;
461         if (key) {
462                 if (key > WEP_KEYS)
463                         return -EINVAL;
464                 key--;
465         } else
466                 key = ieee->tx_keyidx;
467
468         crypt = ieee->crypt[key];
469         erq->flags = key + 1;
470
471         if (!sec->enabled) {
472                 erq->length = 0;
473                 erq->flags |= IW_ENCODE_DISABLED;
474                 return 0;
475         }
476
477         len = sec->key_sizes[key];
478         memcpy(keybuf, sec->keys[key], len);
479
480         erq->length = (len >= 0 ? len : 0);
481         erq->flags |= IW_ENCODE_ENABLED;
482
483         if (ieee->open_wep)
484                 erq->flags |= IW_ENCODE_OPEN;
485         else
486                 erq->flags |= IW_ENCODE_RESTRICTED;
487
488         return 0;
489 }
490
491 int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
492                                struct iw_request_info *info,
493                                union iwreq_data *wrqu, char *extra)
494 {
495         struct net_device *dev = ieee->dev;
496         struct iw_point *encoding = &wrqu->encoding;
497         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
498         int i, idx, ret = 0;
499         int group_key = 0;
500         const char *alg, *module;
501         struct ieee80211_crypto_ops *ops;
502         struct ieee80211_crypt_data **crypt;
503
504         struct ieee80211_security sec = {
505                 .flags = 0,
506         };
507
508         idx = encoding->flags & IW_ENCODE_INDEX;
509         if (idx) {
510                 if (idx < 1 || idx > WEP_KEYS)
511                         return -EINVAL;
512                 idx--;
513         } else
514                 idx = ieee->tx_keyidx;
515
516         if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
517                 crypt = &ieee->crypt[idx];
518                 group_key = 1;
519         } else {
520                 if (idx != 0)
521                         return -EINVAL;
522                 if (ieee->iw_mode == IW_MODE_INFRA)
523                         crypt = &ieee->crypt[idx];
524                 else
525                         return -EINVAL;
526         }
527
528         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
529         if ((encoding->flags & IW_ENCODE_DISABLED) ||
530             ext->alg == IW_ENCODE_ALG_NONE) {
531                 if (*crypt)
532                         ieee80211_crypt_delayed_deinit(ieee, crypt);
533
534                 for (i = 0; i < WEP_KEYS; i++)
535                         if (ieee->crypt[i] != NULL)
536                                 break;
537
538                 if (i == WEP_KEYS) {
539                         sec.enabled = 0;
540                         sec.encrypt = 0;
541                         sec.level = SEC_LEVEL_0;
542                         sec.flags |= SEC_LEVEL;
543                 }
544                 goto done;
545         }
546
547         sec.enabled = 1;
548         sec.encrypt = 1;
549
550         if (group_key ? !ieee->host_mc_decrypt :
551             !(ieee->host_encrypt || ieee->host_decrypt ||
552               ieee->host_encrypt_msdu))
553                 goto skip_host_crypt;
554
555         switch (ext->alg) {
556         case IW_ENCODE_ALG_WEP:
557                 alg = "WEP";
558                 module = "ieee80211_crypt_wep";
559                 break;
560         case IW_ENCODE_ALG_TKIP:
561                 alg = "TKIP";
562                 module = "ieee80211_crypt_tkip";
563                 break;
564         case IW_ENCODE_ALG_CCMP:
565                 alg = "CCMP";
566                 module = "ieee80211_crypt_ccmp";
567                 break;
568         default:
569                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
570                                    dev->name, ext->alg);
571                 ret = -EINVAL;
572                 goto done;
573         }
574
575         ops = ieee80211_get_crypto_ops(alg);
576         if (ops == NULL) {
577                 request_module(module);
578                 ops = ieee80211_get_crypto_ops(alg);
579         }
580         if (ops == NULL) {
581                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
582                                    dev->name, ext->alg);
583                 ret = -EINVAL;
584                 goto done;
585         }
586
587         if (*crypt == NULL || (*crypt)->ops != ops) {
588                 struct ieee80211_crypt_data *new_crypt;
589
590                 ieee80211_crypt_delayed_deinit(ieee, crypt);
591
592                 new_crypt = (struct ieee80211_crypt_data *)
593                     kmalloc(sizeof(*new_crypt), GFP_KERNEL);
594                 if (new_crypt == NULL) {
595                         ret = -ENOMEM;
596                         goto done;
597                 }
598                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
599                 new_crypt->ops = ops;
600                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
601                         new_crypt->priv = new_crypt->ops->init(idx);
602                 if (new_crypt->priv == NULL) {
603                         kfree(new_crypt);
604                         ret = -EINVAL;
605                         goto done;
606                 }
607                 *crypt = new_crypt;
608         }
609
610         if (ext->key_len > 0 && (*crypt)->ops->set_key &&
611             (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
612                                    (*crypt)->priv) < 0) {
613                 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
614                 ret = -EINVAL;
615                 goto done;
616         }
617
618       skip_host_crypt:
619         if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
620                 ieee->tx_keyidx = idx;
621                 sec.active_key = idx;
622                 sec.flags |= SEC_ACTIVE_KEY;
623         }
624
625         if (ext->alg != IW_ENCODE_ALG_NONE) {
626                 memcpy(sec.keys[idx], ext->key, ext->key_len);
627                 sec.key_sizes[idx] = ext->key_len;
628                 sec.flags |= (1 << idx);
629                 if (ext->alg == IW_ENCODE_ALG_WEP) {
630                         sec.encode_alg[idx] = SEC_ALG_WEP;
631                         sec.flags |= SEC_LEVEL;
632                         sec.level = SEC_LEVEL_1;
633                 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
634                         sec.encode_alg[idx] = SEC_ALG_TKIP;
635                         sec.flags |= SEC_LEVEL;
636                         sec.level = SEC_LEVEL_2;
637                 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
638                         sec.encode_alg[idx] = SEC_ALG_CCMP;
639                         sec.flags |= SEC_LEVEL;
640                         sec.level = SEC_LEVEL_3;
641                 }
642                 /* Don't set sec level for group keys. */
643                 if (group_key)
644                         sec.flags &= ~SEC_LEVEL;
645         }
646       done:
647         if (ieee->set_security)
648                 ieee->set_security(ieee->dev, &sec);
649
650         /*
651          * Do not reset port if card is in Managed mode since resetting will
652          * generate new IEEE 802.11 authentication which may end up in looping
653          * with IEEE 802.1X. If your hardware requires a reset after WEP
654          * configuration (for example... Prism2), implement the reset_port in
655          * the callbacks structures used to initialize the 802.11 stack.
656          */
657         if (ieee->reset_on_keychange &&
658             ieee->iw_mode != IW_MODE_INFRA &&
659             ieee->reset_port && ieee->reset_port(dev)) {
660                 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
661                 return -EINVAL;
662         }
663
664         return ret;
665 }
666
667 int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
668                                struct iw_request_info *info,
669                                union iwreq_data *wrqu, char *extra)
670 {
671         struct iw_point *encoding = &wrqu->encoding;
672         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
673         struct ieee80211_security *sec = &ieee->sec;
674         int idx, max_key_len;
675
676         max_key_len = encoding->length - sizeof(*ext);
677         if (max_key_len < 0)
678                 return -EINVAL;
679
680         idx = encoding->flags & IW_ENCODE_INDEX;
681         if (idx) {
682                 if (idx < 1 || idx > WEP_KEYS)
683                         return -EINVAL;
684                 idx--;
685         } else
686                 idx = ieee->tx_keyidx;
687
688         if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
689                 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
690                         return -EINVAL;
691
692         encoding->flags = idx + 1;
693         memset(ext, 0, sizeof(*ext));
694
695         if (!sec->enabled) {
696                 ext->alg = IW_ENCODE_ALG_NONE;
697                 ext->key_len = 0;
698                 encoding->flags |= IW_ENCODE_DISABLED;
699         } else {
700                 if (sec->encode_alg[idx] == SEC_ALG_WEP)
701                         ext->alg = IW_ENCODE_ALG_WEP;
702                 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
703                         ext->alg = IW_ENCODE_ALG_TKIP;
704                 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
705                         ext->alg = IW_ENCODE_ALG_CCMP;
706                 else
707                         return -EINVAL;
708
709                 ext->key_len = sec->key_sizes[idx];
710                 memcpy(ext->key, sec->keys[idx], ext->key_len);
711                 encoding->flags |= IW_ENCODE_ENABLED;
712                 if (ext->key_len &&
713                     (ext->alg == IW_ENCODE_ALG_TKIP ||
714                      ext->alg == IW_ENCODE_ALG_CCMP))
715                         ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
716
717         }
718
719         return 0;
720 }
721
722 EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
723 EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
724
725 EXPORT_SYMBOL(ieee80211_wx_get_scan);
726 EXPORT_SYMBOL(ieee80211_wx_set_encode);
727 EXPORT_SYMBOL(ieee80211_wx_get_encode);