]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/ieee80211/ieee80211_wx.c
scripts/Lindent on ieee80211 subsystem.
[karo-tx-linux.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                 if (idx != 0)
526                         return -EINVAL;
527                 if (ieee->iw_mode == IW_MODE_INFRA)
528                         crypt = &ieee->crypt[idx];
529                 else
530                         return -EINVAL;
531         }
532
533         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
534         if ((encoding->flags & IW_ENCODE_DISABLED) ||
535             ext->alg == IW_ENCODE_ALG_NONE) {
536                 if (*crypt)
537                         ieee80211_crypt_delayed_deinit(ieee, crypt);
538
539                 for (i = 0; i < WEP_KEYS; i++)
540                         if (ieee->crypt[i] != NULL)
541                                 break;
542
543                 if (i == WEP_KEYS) {
544                         sec.enabled = 0;
545                         sec.encrypt = 0;
546                         sec.level = SEC_LEVEL_0;
547                         sec.flags |= SEC_LEVEL;
548                 }
549                 goto done;
550         }
551
552         sec.enabled = 1;
553         sec.encrypt = 1;
554
555         if (group_key ? !ieee->host_mc_decrypt :
556             !(ieee->host_encrypt || ieee->host_decrypt ||
557               ieee->host_encrypt_msdu))
558                 goto skip_host_crypt;
559
560         switch (ext->alg) {
561         case IW_ENCODE_ALG_WEP:
562                 alg = "WEP";
563                 module = "ieee80211_crypt_wep";
564                 break;
565         case IW_ENCODE_ALG_TKIP:
566                 alg = "TKIP";
567                 module = "ieee80211_crypt_tkip";
568                 break;
569         case IW_ENCODE_ALG_CCMP:
570                 alg = "CCMP";
571                 module = "ieee80211_crypt_ccmp";
572                 break;
573         default:
574                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
575                                    dev->name, ext->alg);
576                 ret = -EINVAL;
577                 goto done;
578         }
579
580         ops = ieee80211_get_crypto_ops(alg);
581         if (ops == NULL) {
582                 request_module(module);
583                 ops = ieee80211_get_crypto_ops(alg);
584         }
585         if (ops == NULL) {
586                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
587                                    dev->name, ext->alg);
588                 ret = -EINVAL;
589                 goto done;
590         }
591
592         if (*crypt == NULL || (*crypt)->ops != ops) {
593                 struct ieee80211_crypt_data *new_crypt;
594
595                 ieee80211_crypt_delayed_deinit(ieee, crypt);
596
597                 new_crypt = (struct ieee80211_crypt_data *)
598                     kmalloc(sizeof(*new_crypt), GFP_KERNEL);
599                 if (new_crypt == NULL) {
600                         ret = -ENOMEM;
601                         goto done;
602                 }
603                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
604                 new_crypt->ops = ops;
605                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
606                         new_crypt->priv = new_crypt->ops->init(idx);
607                 if (new_crypt->priv == NULL) {
608                         kfree(new_crypt);
609                         ret = -EINVAL;
610                         goto done;
611                 }
612                 *crypt = new_crypt;
613         }
614
615         if (ext->key_len > 0 && (*crypt)->ops->set_key &&
616             (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
617                                    (*crypt)->priv) < 0) {
618                 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
619                 ret = -EINVAL;
620                 goto done;
621         }
622
623       skip_host_crypt:
624         if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
625                 ieee->tx_keyidx = idx;
626                 sec.active_key = idx;
627                 sec.flags |= SEC_ACTIVE_KEY;
628         }
629
630         if (ext->alg != IW_ENCODE_ALG_NONE) {
631                 memcpy(sec.keys[idx], ext->key, ext->key_len);
632                 sec.key_sizes[idx] = ext->key_len;
633                 sec.flags |= (1 << idx);
634                 if (ext->alg == IW_ENCODE_ALG_WEP) {
635                         sec.encode_alg[idx] = SEC_ALG_WEP;
636                         sec.flags |= SEC_LEVEL;
637                         sec.level = SEC_LEVEL_1;
638                 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
639                         sec.encode_alg[idx] = SEC_ALG_TKIP;
640                         sec.flags |= SEC_LEVEL;
641                         sec.level = SEC_LEVEL_2;
642                 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
643                         sec.encode_alg[idx] = SEC_ALG_CCMP;
644                         sec.flags |= SEC_LEVEL;
645                         sec.level = SEC_LEVEL_3;
646                 }
647                 /* Don't set sec level for group keys. */
648                 if (group_key)
649                         sec.flags &= ~SEC_LEVEL;
650         }
651       done:
652         if (ieee->set_security)
653                 ieee->set_security(ieee->dev, &sec);
654
655         /*
656          * Do not reset port if card is in Managed mode since resetting will
657          * generate new IEEE 802.11 authentication which may end up in looping
658          * with IEEE 802.1X. If your hardware requires a reset after WEP
659          * configuration (for example... Prism2), implement the reset_port in
660          * the callbacks structures used to initialize the 802.11 stack.
661          */
662         if (ieee->reset_on_keychange &&
663             ieee->iw_mode != IW_MODE_INFRA &&
664             ieee->reset_port && ieee->reset_port(dev)) {
665                 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
666                 return -EINVAL;
667         }
668
669         return ret;
670 }
671
672 int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
673                                struct iw_request_info *info,
674                                union iwreq_data *wrqu, char *extra)
675 {
676         struct iw_point *encoding = &wrqu->encoding;
677         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
678         struct ieee80211_security *sec = &ieee->sec;
679         int idx, max_key_len;
680
681         max_key_len = encoding->length - sizeof(*ext);
682         if (max_key_len < 0)
683                 return -EINVAL;
684
685         idx = encoding->flags & IW_ENCODE_INDEX;
686         if (idx) {
687                 if (idx < 1 || idx > WEP_KEYS)
688                         return -EINVAL;
689                 idx--;
690         } else
691                 idx = ieee->tx_keyidx;
692
693         if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
694                 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
695                         return -EINVAL;
696
697         encoding->flags = idx + 1;
698         memset(ext, 0, sizeof(*ext));
699
700         if (!sec->enabled) {
701                 ext->alg = IW_ENCODE_ALG_NONE;
702                 ext->key_len = 0;
703                 encoding->flags |= IW_ENCODE_DISABLED;
704         } else {
705                 if (sec->encode_alg[idx] == SEC_ALG_WEP)
706                         ext->alg = IW_ENCODE_ALG_WEP;
707                 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
708                         ext->alg = IW_ENCODE_ALG_TKIP;
709                 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
710                         ext->alg = IW_ENCODE_ALG_CCMP;
711                 else
712                         return -EINVAL;
713
714                 ext->key_len = sec->key_sizes[idx];
715                 memcpy(ext->key, sec->keys[idx], ext->key_len);
716                 encoding->flags |= IW_ENCODE_ENABLED;
717                 if (ext->key_len &&
718                     (ext->alg == IW_ENCODE_ALG_TKIP ||
719                      ext->alg == IW_ENCODE_ALG_CCMP))
720                         ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
721
722         }
723
724         return 0;
725 }
726
727 EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
728 EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
729
730 EXPORT_SYMBOL(ieee80211_wx_get_scan);
731 EXPORT_SYMBOL(ieee80211_wx_set_encode);
732 EXPORT_SYMBOL(ieee80211_wx_get_encode);