mac80211: minstrel_ht: fix a crash in rate sorting
[cascardo/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/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         return 0;
478 }
479
480 int libipw_wx_get_encode(struct libipw_device *ieee,
481                             struct iw_request_info *info,
482                             union iwreq_data *wrqu, char *keybuf)
483 {
484         struct iw_point *erq = &(wrqu->encoding);
485         int len, key;
486         struct lib80211_crypt_data *crypt;
487         struct libipw_security *sec = &ieee->sec;
488
489         LIBIPW_DEBUG_WX("GET_ENCODE\n");
490
491         key = erq->flags & IW_ENCODE_INDEX;
492         if (key) {
493                 if (key > WEP_KEYS)
494                         return -EINVAL;
495                 key--;
496         } else
497                 key = ieee->crypt_info.tx_keyidx;
498
499         crypt = ieee->crypt_info.crypt[key];
500         erq->flags = key + 1;
501
502         if (!sec->enabled) {
503                 erq->length = 0;
504                 erq->flags |= IW_ENCODE_DISABLED;
505                 return 0;
506         }
507
508         len = sec->key_sizes[key];
509         memcpy(keybuf, sec->keys[key], len);
510
511         erq->length = len;
512         erq->flags |= IW_ENCODE_ENABLED;
513
514         if (ieee->open_wep)
515                 erq->flags |= IW_ENCODE_OPEN;
516         else
517                 erq->flags |= IW_ENCODE_RESTRICTED;
518
519         return 0;
520 }
521
522 int libipw_wx_set_encodeext(struct libipw_device *ieee,
523                                struct iw_request_info *info,
524                                union iwreq_data *wrqu, char *extra)
525 {
526         struct net_device *dev = ieee->dev;
527         struct iw_point *encoding = &wrqu->encoding;
528         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
529         int i, idx, ret = 0;
530         int group_key = 0;
531         const char *alg, *module;
532         struct lib80211_crypto_ops *ops;
533         struct lib80211_crypt_data **crypt;
534
535         struct libipw_security sec = {
536                 .flags = 0,
537         };
538
539         idx = encoding->flags & IW_ENCODE_INDEX;
540         if (idx) {
541                 if (idx < 1 || idx > WEP_KEYS)
542                         return -EINVAL;
543                 idx--;
544         } else
545                 idx = ieee->crypt_info.tx_keyidx;
546
547         if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
548                 crypt = &ieee->crypt_info.crypt[idx];
549                 group_key = 1;
550         } else {
551                 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
552                 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
553                         return -EINVAL;
554                 if (ieee->iw_mode == IW_MODE_INFRA)
555                         crypt = &ieee->crypt_info.crypt[idx];
556                 else
557                         return -EINVAL;
558         }
559
560         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
561         if ((encoding->flags & IW_ENCODE_DISABLED) ||
562             ext->alg == IW_ENCODE_ALG_NONE) {
563                 if (*crypt)
564                         lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
565
566                 for (i = 0; i < WEP_KEYS; i++)
567                         if (ieee->crypt_info.crypt[i] != NULL)
568                                 break;
569
570                 if (i == WEP_KEYS) {
571                         sec.enabled = 0;
572                         sec.encrypt = 0;
573                         sec.level = SEC_LEVEL_0;
574                         sec.flags |= SEC_LEVEL;
575                 }
576                 goto done;
577         }
578
579         sec.enabled = 1;
580         sec.encrypt = 1;
581
582         if (group_key ? !ieee->host_mc_decrypt :
583             !(ieee->host_encrypt || ieee->host_decrypt ||
584               ieee->host_encrypt_msdu))
585                 goto skip_host_crypt;
586
587         switch (ext->alg) {
588         case IW_ENCODE_ALG_WEP:
589                 alg = "WEP";
590                 module = "lib80211_crypt_wep";
591                 break;
592         case IW_ENCODE_ALG_TKIP:
593                 alg = "TKIP";
594                 module = "lib80211_crypt_tkip";
595                 break;
596         case IW_ENCODE_ALG_CCMP:
597                 alg = "CCMP";
598                 module = "lib80211_crypt_ccmp";
599                 break;
600         default:
601                 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
602                                    dev->name, ext->alg);
603                 ret = -EINVAL;
604                 goto done;
605         }
606
607         ops = lib80211_get_crypto_ops(alg);
608         if (ops == NULL) {
609                 request_module(module);
610                 ops = lib80211_get_crypto_ops(alg);
611         }
612         if (ops == NULL) {
613                 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
614                                    dev->name, ext->alg);
615                 ret = -EINVAL;
616                 goto done;
617         }
618
619         if (*crypt == NULL || (*crypt)->ops != ops) {
620                 struct lib80211_crypt_data *new_crypt;
621
622                 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
623
624                 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
625                 if (new_crypt == NULL) {
626                         ret = -ENOMEM;
627                         goto done;
628                 }
629                 new_crypt->ops = ops;
630                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
631                         new_crypt->priv = new_crypt->ops->init(idx);
632                 if (new_crypt->priv == NULL) {
633                         kfree(new_crypt);
634                         ret = -EINVAL;
635                         goto done;
636                 }
637                 *crypt = new_crypt;
638         }
639
640         if (ext->key_len > 0 && (*crypt)->ops->set_key &&
641             (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
642                                    (*crypt)->priv) < 0) {
643                 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
644                 ret = -EINVAL;
645                 goto done;
646         }
647
648       skip_host_crypt:
649         if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
650                 ieee->crypt_info.tx_keyidx = idx;
651                 sec.active_key = idx;
652                 sec.flags |= SEC_ACTIVE_KEY;
653         }
654
655         if (ext->alg != IW_ENCODE_ALG_NONE) {
656                 memcpy(sec.keys[idx], ext->key, ext->key_len);
657                 sec.key_sizes[idx] = ext->key_len;
658                 sec.flags |= (1 << idx);
659                 if (ext->alg == IW_ENCODE_ALG_WEP) {
660                         sec.encode_alg[idx] = SEC_ALG_WEP;
661                         sec.flags |= SEC_LEVEL;
662                         sec.level = SEC_LEVEL_1;
663                 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
664                         sec.encode_alg[idx] = SEC_ALG_TKIP;
665                         sec.flags |= SEC_LEVEL;
666                         sec.level = SEC_LEVEL_2;
667                 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
668                         sec.encode_alg[idx] = SEC_ALG_CCMP;
669                         sec.flags |= SEC_LEVEL;
670                         sec.level = SEC_LEVEL_3;
671                 }
672                 /* Don't set sec level for group keys. */
673                 if (group_key)
674                         sec.flags &= ~SEC_LEVEL;
675         }
676       done:
677         if (ieee->set_security)
678                 ieee->set_security(dev, &sec);
679
680         return ret;
681 }
682
683 int libipw_wx_get_encodeext(struct libipw_device *ieee,
684                                struct iw_request_info *info,
685                                union iwreq_data *wrqu, char *extra)
686 {
687         struct iw_point *encoding = &wrqu->encoding;
688         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
689         struct libipw_security *sec = &ieee->sec;
690         int idx, max_key_len;
691
692         max_key_len = encoding->length - sizeof(*ext);
693         if (max_key_len < 0)
694                 return -EINVAL;
695
696         idx = encoding->flags & IW_ENCODE_INDEX;
697         if (idx) {
698                 if (idx < 1 || idx > WEP_KEYS)
699                         return -EINVAL;
700                 idx--;
701         } else
702                 idx = ieee->crypt_info.tx_keyidx;
703
704         if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
705             ext->alg != IW_ENCODE_ALG_WEP)
706                 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
707                         return -EINVAL;
708
709         encoding->flags = idx + 1;
710         memset(ext, 0, sizeof(*ext));
711
712         if (!sec->enabled) {
713                 ext->alg = IW_ENCODE_ALG_NONE;
714                 ext->key_len = 0;
715                 encoding->flags |= IW_ENCODE_DISABLED;
716         } else {
717                 if (sec->encode_alg[idx] == SEC_ALG_WEP)
718                         ext->alg = IW_ENCODE_ALG_WEP;
719                 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
720                         ext->alg = IW_ENCODE_ALG_TKIP;
721                 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
722                         ext->alg = IW_ENCODE_ALG_CCMP;
723                 else
724                         return -EINVAL;
725
726                 ext->key_len = sec->key_sizes[idx];
727                 memcpy(ext->key, sec->keys[idx], ext->key_len);
728                 encoding->flags |= IW_ENCODE_ENABLED;
729                 if (ext->key_len &&
730                     (ext->alg == IW_ENCODE_ALG_TKIP ||
731                      ext->alg == IW_ENCODE_ALG_CCMP))
732                         ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
733
734         }
735
736         return 0;
737 }
738
739 EXPORT_SYMBOL(libipw_wx_set_encodeext);
740 EXPORT_SYMBOL(libipw_wx_get_encodeext);
741
742 EXPORT_SYMBOL(libipw_wx_get_scan);
743 EXPORT_SYMBOL(libipw_wx_set_encode);
744 EXPORT_SYMBOL(libipw_wx_get_encode);