[PATCH] airo.c: add support for IW_ENCODE_TEMP (i.e. xsupplicant)
[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                 if (ieee->perfect_rssi == ieee->worst_rssi)
156                         iwe.u.qual.qual = 100;
157                 else
158                         iwe.u.qual.qual =
159                             (100 *
160                              (ieee->perfect_rssi - ieee->worst_rssi) *
161                              (ieee->perfect_rssi - ieee->worst_rssi) -
162                              (ieee->perfect_rssi - network->stats.rssi) *
163                              (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
164                               62 * (ieee->perfect_rssi -
165                                     network->stats.rssi))) /
166                             ((ieee->perfect_rssi -
167                               ieee->worst_rssi) * (ieee->perfect_rssi -
168                                                    ieee->worst_rssi));
169                 if (iwe.u.qual.qual > 100)
170                         iwe.u.qual.qual = 100;
171                 else if (iwe.u.qual.qual < 1)
172                         iwe.u.qual.qual = 0;
173         }
174
175         if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
176                 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
177                 iwe.u.qual.noise = 0;
178         } else {
179                 iwe.u.qual.noise = network->stats.noise;
180         }
181
182         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
183
184         iwe.cmd = IWEVCUSTOM;
185         p = custom;
186
187         iwe.u.data.length = p - custom;
188         if (iwe.u.data.length)
189                 start = iwe_stream_add_point(start, stop, &iwe, custom);
190
191         if (network->wpa_ie_len) {
192                 char buf[MAX_WPA_IE_LEN * 2 + 30];
193
194                 u8 *p = buf;
195                 p += sprintf(p, "wpa_ie=");
196                 for (i = 0; i < network->wpa_ie_len; i++) {
197                         p += sprintf(p, "%02x", network->wpa_ie[i]);
198                 }
199
200                 memset(&iwe, 0, sizeof(iwe));
201                 iwe.cmd = IWEVCUSTOM;
202                 iwe.u.data.length = strlen(buf);
203                 start = iwe_stream_add_point(start, stop, &iwe, buf);
204         }
205
206         if (network->rsn_ie_len) {
207                 char buf[MAX_WPA_IE_LEN * 2 + 30];
208
209                 u8 *p = buf;
210                 p += sprintf(p, "rsn_ie=");
211                 for (i = 0; i < network->rsn_ie_len; i++) {
212                         p += sprintf(p, "%02x", network->rsn_ie[i]);
213                 }
214
215                 memset(&iwe, 0, sizeof(iwe));
216                 iwe.cmd = IWEVCUSTOM;
217                 iwe.u.data.length = strlen(buf);
218                 start = iwe_stream_add_point(start, stop, &iwe, buf);
219         }
220
221         /* Add EXTRA: Age to display seconds since last beacon/probe response
222          * for given network. */
223         iwe.cmd = IWEVCUSTOM;
224         p = custom;
225         p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
226                       " Last beacon: %dms ago",
227                       jiffies_to_msecs(jiffies - network->last_scanned));
228         iwe.u.data.length = p - custom;
229         if (iwe.u.data.length)
230                 start = iwe_stream_add_point(start, stop, &iwe, custom);
231
232         return start;
233 }
234
235 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
236                           struct iw_request_info *info,
237                           union iwreq_data *wrqu, char *extra)
238 {
239         struct ieee80211_network *network;
240         unsigned long flags;
241
242         char *ev = extra;
243         char *stop = ev + IW_SCAN_MAX_DATA;
244         int i = 0;
245
246         IEEE80211_DEBUG_WX("Getting scan\n");
247
248         spin_lock_irqsave(&ieee->lock, flags);
249
250         list_for_each_entry(network, &ieee->network_list, list) {
251                 i++;
252                 if (ieee->scan_age == 0 ||
253                     time_after(network->last_scanned + ieee->scan_age, jiffies))
254                         ev = ipw2100_translate_scan(ieee, ev, stop, network);
255                 else
256                         IEEE80211_DEBUG_SCAN("Not showing network '%s ("
257                                              MAC_FMT ")' due to age (%dms).\n",
258                                              escape_essid(network->ssid,
259                                                           network->ssid_len),
260                                              MAC_ARG(network->bssid),
261                                              jiffies_to_msecs(jiffies -
262                                                               network->
263                                                               last_scanned));
264         }
265
266         spin_unlock_irqrestore(&ieee->lock, flags);
267
268         wrqu->data.length = ev - extra;
269         wrqu->data.flags = 0;
270
271         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
272
273         return 0;
274 }
275
276 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
277                             struct iw_request_info *info,
278                             union iwreq_data *wrqu, char *keybuf)
279 {
280         struct iw_point *erq = &(wrqu->encoding);
281         struct net_device *dev = ieee->dev;
282         struct ieee80211_security sec = {
283                 .flags = 0
284         };
285         int i, key, key_provided, len;
286         struct ieee80211_crypt_data **crypt;
287         int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
288
289         IEEE80211_DEBUG_WX("SET_ENCODE\n");
290
291         key = erq->flags & IW_ENCODE_INDEX;
292         if (key) {
293                 if (key > WEP_KEYS)
294                         return -EINVAL;
295                 key--;
296                 key_provided = 1;
297         } else {
298                 key_provided = 0;
299                 key = ieee->tx_keyidx;
300         }
301
302         IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
303                            "provided" : "default");
304
305         crypt = &ieee->crypt[key];
306
307         if (erq->flags & IW_ENCODE_DISABLED) {
308                 if (key_provided && *crypt) {
309                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
310                                            key);
311                         ieee80211_crypt_delayed_deinit(ieee, crypt);
312                 } else
313                         IEEE80211_DEBUG_WX("Disabling encryption.\n");
314
315                 /* Check all the keys to see if any are still configured,
316                  * and if no key index was provided, de-init them all */
317                 for (i = 0; i < WEP_KEYS; i++) {
318                         if (ieee->crypt[i] != NULL) {
319                                 if (key_provided)
320                                         break;
321                                 ieee80211_crypt_delayed_deinit(ieee,
322                                                                &ieee->crypt[i]);
323                         }
324                 }
325
326                 if (i == WEP_KEYS) {
327                         sec.enabled = 0;
328                         sec.encrypt = 0;
329                         sec.level = SEC_LEVEL_0;
330                         sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
331                 }
332
333                 goto done;
334         }
335
336         sec.enabled = 1;
337         sec.encrypt = 1;
338         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
339
340         if (*crypt != NULL && (*crypt)->ops != NULL &&
341             strcmp((*crypt)->ops->name, "WEP") != 0) {
342                 /* changing to use WEP; deinit previously used algorithm
343                  * on this key */
344                 ieee80211_crypt_delayed_deinit(ieee, crypt);
345         }
346
347         if (*crypt == NULL && host_crypto) {
348                 struct ieee80211_crypt_data *new_crypt;
349
350                 /* take WEP into use */
351                 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
352                                     GFP_KERNEL);
353                 if (new_crypt == NULL)
354                         return -ENOMEM;
355                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
356                 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
357                 if (!new_crypt->ops) {
358                         request_module("ieee80211_crypt_wep");
359                         new_crypt->ops = ieee80211_get_crypto_ops("WEP");
360                 }
361
362                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
363                         new_crypt->priv = new_crypt->ops->init(key);
364
365                 if (!new_crypt->ops || !new_crypt->priv) {
366                         kfree(new_crypt);
367                         new_crypt = NULL;
368
369                         printk(KERN_WARNING "%s: could not initialize WEP: "
370                                "load module ieee80211_crypt_wep\n", dev->name);
371                         return -EOPNOTSUPP;
372                 }
373                 *crypt = new_crypt;
374         }
375
376         /* If a new key was provided, set it up */
377         if (erq->length > 0) {
378                 len = erq->length <= 5 ? 5 : 13;
379                 memcpy(sec.keys[key], keybuf, erq->length);
380                 if (len > erq->length)
381                         memset(sec.keys[key] + erq->length, 0,
382                                len - erq->length);
383                 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
384                                    key, escape_essid(sec.keys[key], len),
385                                    erq->length, len);
386                 sec.key_sizes[key] = len;
387                 if (*crypt)
388                         (*crypt)->ops->set_key(sec.keys[key], len, NULL,
389                                                (*crypt)->priv);
390                 sec.flags |= (1 << key);
391                 /* This ensures a key will be activated if no key is
392                  * explicitely set */
393                 if (key == sec.active_key)
394                         sec.flags |= SEC_ACTIVE_KEY;
395
396         } else {
397                 if (host_crypto) {
398                         len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
399                                                      NULL, (*crypt)->priv);
400                         if (len == 0) {
401                                 /* Set a default key of all 0 */
402                                 IEEE80211_DEBUG_WX("Setting key %d to all "
403                                                    "zero.\n", key);
404                                 memset(sec.keys[key], 0, 13);
405                                 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
406                                                        (*crypt)->priv);
407                                 sec.key_sizes[key] = 13;
408                                 sec.flags |= (1 << key);
409                         }
410                 }
411                 /* No key data - just set the default TX key index */
412                 if (key_provided) {
413                         IEEE80211_DEBUG_WX("Setting key %d to default Tx "
414                                            "key.\n", key);
415                         ieee->tx_keyidx = key;
416                         sec.active_key = key;
417                         sec.flags |= SEC_ACTIVE_KEY;
418                 }
419         }
420         if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
421                 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
422                 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
423                     WLAN_AUTH_SHARED_KEY;
424                 sec.flags |= SEC_AUTH_MODE;
425                 IEEE80211_DEBUG_WX("Auth: %s\n",
426                                    sec.auth_mode == WLAN_AUTH_OPEN ?
427                                    "OPEN" : "SHARED KEY");
428         }
429
430         /* For now we just support WEP, so only set that security level...
431          * TODO: When WPA is added this is one place that needs to change */
432         sec.flags |= SEC_LEVEL;
433         sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
434         sec.encode_alg[key] = SEC_ALG_WEP;
435
436       done:
437         if (ieee->set_security)
438                 ieee->set_security(dev, &sec);
439
440         /* Do not reset port if card is in Managed mode since resetting will
441          * generate new IEEE 802.11 authentication which may end up in looping
442          * with IEEE 802.1X.  If your hardware requires a reset after WEP
443          * configuration (for example... Prism2), implement the reset_port in
444          * the callbacks structures used to initialize the 802.11 stack. */
445         if (ieee->reset_on_keychange &&
446             ieee->iw_mode != IW_MODE_INFRA &&
447             ieee->reset_port && ieee->reset_port(dev)) {
448                 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
449                 return -EINVAL;
450         }
451         return 0;
452 }
453
454 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
455                             struct iw_request_info *info,
456                             union iwreq_data *wrqu, char *keybuf)
457 {
458         struct iw_point *erq = &(wrqu->encoding);
459         int len, key;
460         struct ieee80211_crypt_data *crypt;
461         struct ieee80211_security *sec = &ieee->sec;
462
463         IEEE80211_DEBUG_WX("GET_ENCODE\n");
464
465         key = erq->flags & IW_ENCODE_INDEX;
466         if (key) {
467                 if (key > WEP_KEYS)
468                         return -EINVAL;
469                 key--;
470         } else
471                 key = ieee->tx_keyidx;
472
473         crypt = ieee->crypt[key];
474         erq->flags = key + 1;
475
476         if (!sec->enabled) {
477                 erq->length = 0;
478                 erq->flags |= IW_ENCODE_DISABLED;
479                 return 0;
480         }
481
482         len = sec->key_sizes[key];
483         memcpy(keybuf, sec->keys[key], len);
484
485         erq->length = (len >= 0 ? len : 0);
486         erq->flags |= IW_ENCODE_ENABLED;
487
488         if (ieee->open_wep)
489                 erq->flags |= IW_ENCODE_OPEN;
490         else
491                 erq->flags |= IW_ENCODE_RESTRICTED;
492
493         return 0;
494 }
495
496 int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
497                                struct iw_request_info *info,
498                                union iwreq_data *wrqu, char *extra)
499 {
500         struct net_device *dev = ieee->dev;
501         struct iw_point *encoding = &wrqu->encoding;
502         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
503         int i, idx, ret = 0;
504         int group_key = 0;
505         const char *alg, *module;
506         struct ieee80211_crypto_ops *ops;
507         struct ieee80211_crypt_data **crypt;
508
509         struct ieee80211_security sec = {
510                 .flags = 0,
511         };
512
513         idx = encoding->flags & IW_ENCODE_INDEX;
514         if (idx) {
515                 if (idx < 1 || idx > WEP_KEYS)
516                         return -EINVAL;
517                 idx--;
518         } else
519                 idx = ieee->tx_keyidx;
520
521         if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
522                 crypt = &ieee->crypt[idx];
523                 group_key = 1;
524         } else {
525                 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
526                 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
527                         return -EINVAL;
528                 if (ieee->iw_mode == IW_MODE_INFRA)
529                         crypt = &ieee->crypt[idx];
530                 else
531                         return -EINVAL;
532         }
533
534         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
535         if ((encoding->flags & IW_ENCODE_DISABLED) ||
536             ext->alg == IW_ENCODE_ALG_NONE) {
537                 if (*crypt)
538                         ieee80211_crypt_delayed_deinit(ieee, crypt);
539
540                 for (i = 0; i < WEP_KEYS; i++)
541                         if (ieee->crypt[i] != NULL)
542                                 break;
543
544                 if (i == WEP_KEYS) {
545                         sec.enabled = 0;
546                         sec.encrypt = 0;
547                         sec.level = SEC_LEVEL_0;
548                         sec.flags |= SEC_LEVEL;
549                 }
550                 goto done;
551         }
552
553         sec.enabled = 1;
554         sec.encrypt = 1;
555
556         if (group_key ? !ieee->host_mc_decrypt :
557             !(ieee->host_encrypt || ieee->host_decrypt ||
558               ieee->host_encrypt_msdu))
559                 goto skip_host_crypt;
560
561         switch (ext->alg) {
562         case IW_ENCODE_ALG_WEP:
563                 alg = "WEP";
564                 module = "ieee80211_crypt_wep";
565                 break;
566         case IW_ENCODE_ALG_TKIP:
567                 alg = "TKIP";
568                 module = "ieee80211_crypt_tkip";
569                 break;
570         case IW_ENCODE_ALG_CCMP:
571                 alg = "CCMP";
572                 module = "ieee80211_crypt_ccmp";
573                 break;
574         default:
575                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
576                                    dev->name, ext->alg);
577                 ret = -EINVAL;
578                 goto done;
579         }
580
581         ops = ieee80211_get_crypto_ops(alg);
582         if (ops == NULL) {
583                 request_module(module);
584                 ops = ieee80211_get_crypto_ops(alg);
585         }
586         if (ops == NULL) {
587                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
588                                    dev->name, ext->alg);
589                 ret = -EINVAL;
590                 goto done;
591         }
592
593         if (*crypt == NULL || (*crypt)->ops != ops) {
594                 struct ieee80211_crypt_data *new_crypt;
595
596                 ieee80211_crypt_delayed_deinit(ieee, crypt);
597
598                 new_crypt = (struct ieee80211_crypt_data *)
599                     kmalloc(sizeof(*new_crypt), GFP_KERNEL);
600                 if (new_crypt == NULL) {
601                         ret = -ENOMEM;
602                         goto done;
603                 }
604                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
605                 new_crypt->ops = ops;
606                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
607                         new_crypt->priv = new_crypt->ops->init(idx);
608                 if (new_crypt->priv == NULL) {
609                         kfree(new_crypt);
610                         ret = -EINVAL;
611                         goto done;
612                 }
613                 *crypt = new_crypt;
614         }
615
616         if (ext->key_len > 0 && (*crypt)->ops->set_key &&
617             (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
618                                    (*crypt)->priv) < 0) {
619                 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
620                 ret = -EINVAL;
621                 goto done;
622         }
623
624       skip_host_crypt:
625         if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
626                 ieee->tx_keyidx = idx;
627                 sec.active_key = idx;
628                 sec.flags |= SEC_ACTIVE_KEY;
629         }
630
631         if (ext->alg != IW_ENCODE_ALG_NONE) {
632                 memcpy(sec.keys[idx], ext->key, ext->key_len);
633                 sec.key_sizes[idx] = ext->key_len;
634                 sec.flags |= (1 << idx);
635                 if (ext->alg == IW_ENCODE_ALG_WEP) {
636                         sec.encode_alg[idx] = SEC_ALG_WEP;
637                         sec.flags |= SEC_LEVEL;
638                         sec.level = SEC_LEVEL_1;
639                 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
640                         sec.encode_alg[idx] = SEC_ALG_TKIP;
641                         sec.flags |= SEC_LEVEL;
642                         sec.level = SEC_LEVEL_2;
643                 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
644                         sec.encode_alg[idx] = SEC_ALG_CCMP;
645                         sec.flags |= SEC_LEVEL;
646                         sec.level = SEC_LEVEL_3;
647                 }
648                 /* Don't set sec level for group keys. */
649                 if (group_key)
650                         sec.flags &= ~SEC_LEVEL;
651         }
652       done:
653         if (ieee->set_security)
654                 ieee->set_security(ieee->dev, &sec);
655
656         /*
657          * Do not reset port if card is in Managed mode since resetting will
658          * generate new IEEE 802.11 authentication which may end up in looping
659          * with IEEE 802.1X. If your hardware requires a reset after WEP
660          * configuration (for example... Prism2), implement the reset_port in
661          * the callbacks structures used to initialize the 802.11 stack.
662          */
663         if (ieee->reset_on_keychange &&
664             ieee->iw_mode != IW_MODE_INFRA &&
665             ieee->reset_port && ieee->reset_port(dev)) {
666                 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
667                 return -EINVAL;
668         }
669
670         return ret;
671 }
672
673 int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
674                                struct iw_request_info *info,
675                                union iwreq_data *wrqu, char *extra)
676 {
677         struct iw_point *encoding = &wrqu->encoding;
678         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
679         struct ieee80211_security *sec = &ieee->sec;
680         int idx, max_key_len;
681
682         max_key_len = encoding->length - sizeof(*ext);
683         if (max_key_len < 0)
684                 return -EINVAL;
685
686         idx = encoding->flags & IW_ENCODE_INDEX;
687         if (idx) {
688                 if (idx < 1 || idx > WEP_KEYS)
689                         return -EINVAL;
690                 idx--;
691         } else
692                 idx = ieee->tx_keyidx;
693
694         if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
695             ext->alg != IW_ENCODE_ALG_WEP)
696                 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
697                         return -EINVAL;
698
699         encoding->flags = idx + 1;
700         memset(ext, 0, sizeof(*ext));
701
702         if (!sec->enabled) {
703                 ext->alg = IW_ENCODE_ALG_NONE;
704                 ext->key_len = 0;
705                 encoding->flags |= IW_ENCODE_DISABLED;
706         } else {
707                 if (sec->encode_alg[idx] == SEC_ALG_WEP)
708                         ext->alg = IW_ENCODE_ALG_WEP;
709                 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
710                         ext->alg = IW_ENCODE_ALG_TKIP;
711                 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
712                         ext->alg = IW_ENCODE_ALG_CCMP;
713                 else
714                         return -EINVAL;
715
716                 ext->key_len = sec->key_sizes[idx];
717                 memcpy(ext->key, sec->keys[idx], ext->key_len);
718                 encoding->flags |= IW_ENCODE_ENABLED;
719                 if (ext->key_len &&
720                     (ext->alg == IW_ENCODE_ALG_TKIP ||
721                      ext->alg == IW_ENCODE_ALG_CCMP))
722                         ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
723
724         }
725
726         return 0;
727 }
728
729 EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
730 EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
731
732 EXPORT_SYMBOL(ieee80211_wx_get_scan);
733 EXPORT_SYMBOL(ieee80211_wx_set_encode);
734 EXPORT_SYMBOL(ieee80211_wx_get_encode);