ipw2x00: move under intel vendor directory
[cascardo/linux.git] / drivers / net / wireless / intel / 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
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 '%*pE (%pM)' due to age (%ums).\n",
293                                           network->ssid_len, network->ssid,
294                                           network->bssid,
295                                           elapsed_jiffies_msecs(
296                                                        network->last_scanned));
297                 }
298         }
299
300         spin_unlock_irqrestore(&ieee->lock, flags);
301
302         wrqu->data.length = ev - extra;
303         wrqu->data.flags = 0;
304
305         LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
306
307         return err;
308 }
309
310 int libipw_wx_set_encode(struct libipw_device *ieee,
311                             struct iw_request_info *info,
312                             union iwreq_data *wrqu, char *keybuf)
313 {
314         struct iw_point *erq = &(wrqu->encoding);
315         struct net_device *dev = ieee->dev;
316         struct libipw_security sec = {
317                 .flags = 0
318         };
319         int i, key, key_provided, len;
320         struct lib80211_crypt_data **crypt;
321         int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
322
323         LIBIPW_DEBUG_WX("SET_ENCODE\n");
324
325         key = erq->flags & IW_ENCODE_INDEX;
326         if (key) {
327                 if (key > WEP_KEYS)
328                         return -EINVAL;
329                 key--;
330                 key_provided = 1;
331         } else {
332                 key_provided = 0;
333                 key = ieee->crypt_info.tx_keyidx;
334         }
335
336         LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
337                            "provided" : "default");
338
339         crypt = &ieee->crypt_info.crypt[key];
340
341         if (erq->flags & IW_ENCODE_DISABLED) {
342                 if (key_provided && *crypt) {
343                         LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
344                                            key);
345                         lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
346                 } else
347                         LIBIPW_DEBUG_WX("Disabling encryption.\n");
348
349                 /* Check all the keys to see if any are still configured,
350                  * and if no key index was provided, de-init them all */
351                 for (i = 0; i < WEP_KEYS; i++) {
352                         if (ieee->crypt_info.crypt[i] != NULL) {
353                                 if (key_provided)
354                                         break;
355                                 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
356                                                                &ieee->crypt_info.crypt[i]);
357                         }
358                 }
359
360                 if (i == WEP_KEYS) {
361                         sec.enabled = 0;
362                         sec.encrypt = 0;
363                         sec.level = SEC_LEVEL_0;
364                         sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
365                 }
366
367                 goto done;
368         }
369
370         sec.enabled = 1;
371         sec.encrypt = 1;
372         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
373
374         if (*crypt != NULL && (*crypt)->ops != NULL &&
375             strcmp((*crypt)->ops->name, "WEP") != 0) {
376                 /* changing to use WEP; deinit previously used algorithm
377                  * on this key */
378                 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
379         }
380
381         if (*crypt == NULL && host_crypto) {
382                 struct lib80211_crypt_data *new_crypt;
383
384                 /* take WEP into use */
385                 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
386                                     GFP_KERNEL);
387                 if (new_crypt == NULL)
388                         return -ENOMEM;
389                 new_crypt->ops = lib80211_get_crypto_ops("WEP");
390                 if (!new_crypt->ops) {
391                         request_module("lib80211_crypt_wep");
392                         new_crypt->ops = lib80211_get_crypto_ops("WEP");
393                 }
394
395                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
396                         new_crypt->priv = new_crypt->ops->init(key);
397
398                 if (!new_crypt->ops || !new_crypt->priv) {
399                         kfree(new_crypt);
400                         new_crypt = NULL;
401
402                         printk(KERN_WARNING "%s: could not initialize WEP: "
403                                "load module lib80211_crypt_wep\n", dev->name);
404                         return -EOPNOTSUPP;
405                 }
406                 *crypt = new_crypt;
407         }
408
409         /* If a new key was provided, set it up */
410         if (erq->length > 0) {
411                 len = erq->length <= 5 ? 5 : 13;
412                 memcpy(sec.keys[key], keybuf, erq->length);
413                 if (len > erq->length)
414                         memset(sec.keys[key] + erq->length, 0,
415                                len - erq->length);
416                 LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
417                                    key, len, sec.keys[key],
418                                    erq->length, len);
419                 sec.key_sizes[key] = len;
420                 if (*crypt)
421                         (*crypt)->ops->set_key(sec.keys[key], len, NULL,
422                                                (*crypt)->priv);
423                 sec.flags |= (1 << key);
424                 /* This ensures a key will be activated if no key is
425                  * explicitly set */
426                 if (key == sec.active_key)
427                         sec.flags |= SEC_ACTIVE_KEY;
428
429         } else {
430                 if (host_crypto) {
431                         len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
432                                                      NULL, (*crypt)->priv);
433                         if (len == 0) {
434                                 /* Set a default key of all 0 */
435                                 LIBIPW_DEBUG_WX("Setting key %d to all "
436                                                    "zero.\n", key);
437                                 memset(sec.keys[key], 0, 13);
438                                 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
439                                                        (*crypt)->priv);
440                                 sec.key_sizes[key] = 13;
441                                 sec.flags |= (1 << key);
442                         }
443                 }
444                 /* No key data - just set the default TX key index */
445                 if (key_provided) {
446                         LIBIPW_DEBUG_WX("Setting key %d to default Tx "
447                                            "key.\n", key);
448                         ieee->crypt_info.tx_keyidx = key;
449                         sec.active_key = key;
450                         sec.flags |= SEC_ACTIVE_KEY;
451                 }
452         }
453         if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
454                 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
455                 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
456                     WLAN_AUTH_SHARED_KEY;
457                 sec.flags |= SEC_AUTH_MODE;
458                 LIBIPW_DEBUG_WX("Auth: %s\n",
459                                    sec.auth_mode == WLAN_AUTH_OPEN ?
460                                    "OPEN" : "SHARED KEY");
461         }
462
463         /* For now we just support WEP, so only set that security level...
464          * TODO: When WPA is added this is one place that needs to change */
465         sec.flags |= SEC_LEVEL;
466         sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
467         sec.encode_alg[key] = SEC_ALG_WEP;
468
469       done:
470         if (ieee->set_security)
471                 ieee->set_security(dev, &sec);
472
473         return 0;
474 }
475
476 int libipw_wx_get_encode(struct libipw_device *ieee,
477                             struct iw_request_info *info,
478                             union iwreq_data *wrqu, char *keybuf)
479 {
480         struct iw_point *erq = &(wrqu->encoding);
481         int len, key;
482         struct lib80211_crypt_data *crypt;
483         struct libipw_security *sec = &ieee->sec;
484
485         LIBIPW_DEBUG_WX("GET_ENCODE\n");
486
487         key = erq->flags & IW_ENCODE_INDEX;
488         if (key) {
489                 if (key > WEP_KEYS)
490                         return -EINVAL;
491                 key--;
492         } else
493                 key = ieee->crypt_info.tx_keyidx;
494
495         crypt = ieee->crypt_info.crypt[key];
496         erq->flags = key + 1;
497
498         if (!sec->enabled) {
499                 erq->length = 0;
500                 erq->flags |= IW_ENCODE_DISABLED;
501                 return 0;
502         }
503
504         len = sec->key_sizes[key];
505         memcpy(keybuf, sec->keys[key], len);
506
507         erq->length = len;
508         erq->flags |= IW_ENCODE_ENABLED;
509
510         if (ieee->open_wep)
511                 erq->flags |= IW_ENCODE_OPEN;
512         else
513                 erq->flags |= IW_ENCODE_RESTRICTED;
514
515         return 0;
516 }
517
518 int libipw_wx_set_encodeext(struct libipw_device *ieee,
519                                struct iw_request_info *info,
520                                union iwreq_data *wrqu, char *extra)
521 {
522         struct net_device *dev = ieee->dev;
523         struct iw_point *encoding = &wrqu->encoding;
524         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
525         int i, idx, ret = 0;
526         int group_key = 0;
527         const char *alg, *module;
528         struct lib80211_crypto_ops *ops;
529         struct lib80211_crypt_data **crypt;
530
531         struct libipw_security sec = {
532                 .flags = 0,
533         };
534
535         idx = encoding->flags & IW_ENCODE_INDEX;
536         if (idx) {
537                 if (idx < 1 || idx > WEP_KEYS)
538                         return -EINVAL;
539                 idx--;
540         } else
541                 idx = ieee->crypt_info.tx_keyidx;
542
543         if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
544                 crypt = &ieee->crypt_info.crypt[idx];
545                 group_key = 1;
546         } else {
547                 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
548                 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
549                         return -EINVAL;
550                 if (ieee->iw_mode == IW_MODE_INFRA)
551                         crypt = &ieee->crypt_info.crypt[idx];
552                 else
553                         return -EINVAL;
554         }
555
556         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
557         if ((encoding->flags & IW_ENCODE_DISABLED) ||
558             ext->alg == IW_ENCODE_ALG_NONE) {
559                 if (*crypt)
560                         lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
561
562                 for (i = 0; i < WEP_KEYS; i++)
563                         if (ieee->crypt_info.crypt[i] != NULL)
564                                 break;
565
566                 if (i == WEP_KEYS) {
567                         sec.enabled = 0;
568                         sec.encrypt = 0;
569                         sec.level = SEC_LEVEL_0;
570                         sec.flags |= SEC_LEVEL;
571                 }
572                 goto done;
573         }
574
575         sec.enabled = 1;
576         sec.encrypt = 1;
577
578         if (group_key ? !ieee->host_mc_decrypt :
579             !(ieee->host_encrypt || ieee->host_decrypt ||
580               ieee->host_encrypt_msdu))
581                 goto skip_host_crypt;
582
583         switch (ext->alg) {
584         case IW_ENCODE_ALG_WEP:
585                 alg = "WEP";
586                 module = "lib80211_crypt_wep";
587                 break;
588         case IW_ENCODE_ALG_TKIP:
589                 alg = "TKIP";
590                 module = "lib80211_crypt_tkip";
591                 break;
592         case IW_ENCODE_ALG_CCMP:
593                 alg = "CCMP";
594                 module = "lib80211_crypt_ccmp";
595                 break;
596         default:
597                 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
598                                    dev->name, ext->alg);
599                 ret = -EINVAL;
600                 goto done;
601         }
602
603         ops = lib80211_get_crypto_ops(alg);
604         if (ops == NULL) {
605                 request_module(module);
606                 ops = lib80211_get_crypto_ops(alg);
607         }
608         if (ops == NULL) {
609                 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
610                                    dev->name, ext->alg);
611                 ret = -EINVAL;
612                 goto done;
613         }
614
615         if (*crypt == NULL || (*crypt)->ops != ops) {
616                 struct lib80211_crypt_data *new_crypt;
617
618                 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
619
620                 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
621                 if (new_crypt == NULL) {
622                         ret = -ENOMEM;
623                         goto done;
624                 }
625                 new_crypt->ops = ops;
626                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
627                         new_crypt->priv = new_crypt->ops->init(idx);
628                 if (new_crypt->priv == NULL) {
629                         kfree(new_crypt);
630                         ret = -EINVAL;
631                         goto done;
632                 }
633                 *crypt = new_crypt;
634         }
635
636         if (ext->key_len > 0 && (*crypt)->ops->set_key &&
637             (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
638                                    (*crypt)->priv) < 0) {
639                 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
640                 ret = -EINVAL;
641                 goto done;
642         }
643
644       skip_host_crypt:
645         if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
646                 ieee->crypt_info.tx_keyidx = idx;
647                 sec.active_key = idx;
648                 sec.flags |= SEC_ACTIVE_KEY;
649         }
650
651         if (ext->alg != IW_ENCODE_ALG_NONE) {
652                 memcpy(sec.keys[idx], ext->key, ext->key_len);
653                 sec.key_sizes[idx] = ext->key_len;
654                 sec.flags |= (1 << idx);
655                 if (ext->alg == IW_ENCODE_ALG_WEP) {
656                         sec.encode_alg[idx] = SEC_ALG_WEP;
657                         sec.flags |= SEC_LEVEL;
658                         sec.level = SEC_LEVEL_1;
659                 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
660                         sec.encode_alg[idx] = SEC_ALG_TKIP;
661                         sec.flags |= SEC_LEVEL;
662                         sec.level = SEC_LEVEL_2;
663                 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
664                         sec.encode_alg[idx] = SEC_ALG_CCMP;
665                         sec.flags |= SEC_LEVEL;
666                         sec.level = SEC_LEVEL_3;
667                 }
668                 /* Don't set sec level for group keys. */
669                 if (group_key)
670                         sec.flags &= ~SEC_LEVEL;
671         }
672       done:
673         if (ieee->set_security)
674                 ieee->set_security(dev, &sec);
675
676         return ret;
677 }
678
679 int libipw_wx_get_encodeext(struct libipw_device *ieee,
680                                struct iw_request_info *info,
681                                union iwreq_data *wrqu, char *extra)
682 {
683         struct iw_point *encoding = &wrqu->encoding;
684         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
685         struct libipw_security *sec = &ieee->sec;
686         int idx, max_key_len;
687
688         max_key_len = encoding->length - sizeof(*ext);
689         if (max_key_len < 0)
690                 return -EINVAL;
691
692         idx = encoding->flags & IW_ENCODE_INDEX;
693         if (idx) {
694                 if (idx < 1 || idx > WEP_KEYS)
695                         return -EINVAL;
696                 idx--;
697         } else
698                 idx = ieee->crypt_info.tx_keyidx;
699
700         if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
701             ext->alg != IW_ENCODE_ALG_WEP)
702                 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
703                         return -EINVAL;
704
705         encoding->flags = idx + 1;
706         memset(ext, 0, sizeof(*ext));
707
708         if (!sec->enabled) {
709                 ext->alg = IW_ENCODE_ALG_NONE;
710                 ext->key_len = 0;
711                 encoding->flags |= IW_ENCODE_DISABLED;
712         } else {
713                 if (sec->encode_alg[idx] == SEC_ALG_WEP)
714                         ext->alg = IW_ENCODE_ALG_WEP;
715                 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
716                         ext->alg = IW_ENCODE_ALG_TKIP;
717                 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
718                         ext->alg = IW_ENCODE_ALG_CCMP;
719                 else
720                         return -EINVAL;
721
722                 ext->key_len = sec->key_sizes[idx];
723                 memcpy(ext->key, sec->keys[idx], ext->key_len);
724                 encoding->flags |= IW_ENCODE_ENABLED;
725                 if (ext->key_len &&
726                     (ext->alg == IW_ENCODE_ALG_TKIP ||
727                      ext->alg == IW_ENCODE_ALG_CCMP))
728                         ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
729
730         }
731
732         return 0;
733 }
734
735 EXPORT_SYMBOL(libipw_wx_set_encodeext);
736 EXPORT_SYMBOL(libipw_wx_get_encodeext);
737
738 EXPORT_SYMBOL(libipw_wx_get_scan);
739 EXPORT_SYMBOL(libipw_wx_set_encode);
740 EXPORT_SYMBOL(libipw_wx_get_encode);