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