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