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