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