Merge branch 'next/soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/linux...
[linux-2.6-block.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:
c1eb2c82 28 Intel Linux Wireless <ilw@linux.intel.com>
b453872c
JG
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
bbeec90b 32
a6b7a407 33#include <linux/hardirq.h>
b453872c 34#include <linux/kmod.h>
5a0e3ad6 35#include <linux/slab.h>
b453872c 36#include <linux/module.h>
42e349fd 37#include <linux/jiffies.h>
b453872c 38
9387b7ca 39#include <net/lib80211.h>
bbeec90b
JG
40#include <linux/wireless.h>
41
b0a4e7d8 42#include "libipw.h"
f3734ee6 43
b0a4e7d8 44static const char *libipw_modes[] = {
b453872c
JG
45 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
46};
47
c3d72b96
DW
48static 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
b453872c 58#define MAX_CUSTOM_LEN 64
b0a4e7d8 59static char *libipw_translate_scan(struct libipw_device *ieee,
ccc58057 60 char *start, char *stop,
b0a4e7d8 61 struct libipw_network *network,
ccc58057 62 struct iw_request_info *info)
b453872c
JG
63{
64 char custom[MAX_CUSTOM_LEN];
65 char *p;
66 struct iw_event iwe;
67 int i, j;
09593047
ZY
68 char *current_val; /* For rates */
69 u8 rate;
b453872c
JG
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);
ccc58057 75 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
b453872c
JG
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;
c5d3dce8
JL
82 iwe.u.data.length = min(network->ssid_len, (u8) 32);
83 start = iwe_stream_add_point(info, start, stop,
84 &iwe, network->ssid);
b453872c
JG
85
86 /* Add the protocol name */
87 iwe.cmd = SIOCGIWNAME;
0edd5b44 88 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
b0a4e7d8 89 libipw_modes[network->mode]);
ccc58057 90 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
b453872c 91
0edd5b44
JG
92 /* Add mode */
93 iwe.cmd = SIOCGIWMODE;
94 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
1b5cca3a 95 if (network->capability & WLAN_CAPABILITY_ESS)
b453872c
JG
96 iwe.u.mode = IW_MODE_MASTER;
97 else
98 iwe.u.mode = IW_MODE_ADHOC;
99
ccc58057
DM
100 start = iwe_stream_add_event(info, start, stop,
101 &iwe, IW_EV_UINT_LEN);
b453872c
JG
102 }
103
93afe3da 104 /* Add channel and frequency */
90869b24 105 /* Note : userspace automatically computes channel using iwrange */
b453872c 106 iwe.cmd = SIOCGIWFREQ;
b0a4e7d8 107 iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
93afe3da 108 iwe.u.freq.e = 6;
90869b24 109 iwe.u.freq.i = 0;
ccc58057 110 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
93afe3da 111
b453872c
JG
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;
ccc58057
DM
119 start = iwe_stream_add_point(info, start, stop,
120 &iwe, network->ssid);
b453872c
JG
121
122 /* Add basic and extended rates */
09593047
ZY
123 /* Rate : stuffing multiple values in a single event require a bit
124 * more of magic - Jean II */
ccc58057 125 current_val = start + iwe_stream_lcp_len(info);
09593047
ZY
126 iwe.cmd = SIOCGIWRATE;
127 /* Those two flags are ignored... */
128 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
129
0edd5b44 130 for (i = 0, j = 0; i < network->rates_len;) {
b453872c
JG
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;
09593047
ZY
137 /* Bit rate given in 500 kb/s units (+ 0x80) */
138 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
139 /* Add new value to event */
ccc58057
DM
140 current_val = iwe_stream_add_value(info, start, current_val,
141 stop, &iwe, IW_EV_PARAM_LEN);
b453872c
JG
142 }
143 for (; j < network->rates_ex_len; j++) {
144 rate = network->rates_ex[j] & 0x7F;
09593047
ZY
145 /* Bit rate given in 500 kb/s units (+ 0x80) */
146 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
147 /* Add new value to event */
ccc58057
DM
148 current_val = iwe_stream_add_value(info, start, current_val,
149 stop, &iwe, IW_EV_PARAM_LEN);
b453872c 150 }
09593047 151 /* Check if we added any rate */
ccc58057 152 if ((current_val - start) > iwe_stream_lcp_len(info))
09593047 153 start = current_val;
b453872c
JG
154
155 /* Add quality statistics */
b453872c 156 iwe.cmd = IWEVQUAL;
b1b508e1
JK
157 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
158 IW_QUAL_NOISE_UPDATED;
159
b0a4e7d8 160 if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
b1b508e1
JK
161 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
162 IW_QUAL_LEVEL_INVALID;
163 iwe.u.qual.qual = 0;
b1b508e1 164 } else {
757d18fa
JB
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) +
81f87520
JK
174 62 * (ieee->perfect_rssi -
175 network->stats.rssi))) /
176 ((ieee->perfect_rssi -
177 ieee->worst_rssi) * (ieee->perfect_rssi -
178 ieee->worst_rssi));
b1b508e1
JK
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
b0a4e7d8 185 if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
b453872c 186 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
b1b508e1
JK
187 iwe.u.qual.noise = 0;
188 } else {
189 iwe.u.qual.noise = network->stats.noise;
190 }
b453872c 191
b0a4e7d8 192 if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
7bd64366
ZY
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
ccc58057 199 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
b453872c
JG
200
201 iwe.cmd = IWEVCUSTOM;
202 p = custom;
203
204 iwe.u.data.length = p - custom;
205 if (iwe.u.data.length)
ccc58057 206 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
b453872c 207
47168082 208 memset(&iwe, 0, sizeof(iwe));
20d64713 209 if (network->wpa_ie_len) {
47168082
ZY
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;
ccc58057 214 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
b453872c
JG
215 }
216
47168082 217 memset(&iwe, 0, sizeof(iwe));
20d64713 218 if (network->rsn_ie_len) {
47168082
ZY
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;
ccc58057 223 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
b453872c
JG
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),
c3d72b96
DW
231 " Last beacon: %ums ago",
232 elapsed_jiffies_msecs(network->last_scanned));
b453872c
JG
233 iwe.u.data.length = p - custom;
234 if (iwe.u.data.length)
ccc58057 235 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
b453872c 236
7bd64366
ZY
237 /* Add spectrum management information */
238 iwe.cmd = -1;
239 p = custom;
240 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241
b0a4e7d8
JL
242 if (libipw_get_channel_flags(ieee, network->channel) &
243 LIBIPW_CH_INVALID) {
7bd64366
ZY
244 iwe.cmd = IWEVCUSTOM;
245 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246 }
247
b0a4e7d8
JL
248 if (libipw_get_channel_flags(ieee, network->channel) &
249 LIBIPW_CH_RADAR_DETECT) {
7bd64366
ZY
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;
ccc58057 256 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
7bd64366
ZY
257 }
258
b453872c
JG
259 return start;
260}
261
55cd94aa
ZY
262#define SCAN_ITEM_SIZE 128
263
b0a4e7d8 264int libipw_wx_get_scan(struct libipw_device *ieee,
b453872c
JG
265 struct iw_request_info *info,
266 union iwreq_data *wrqu, char *extra)
267{
b0a4e7d8 268 struct libipw_network *network;
b453872c 269 unsigned long flags;
55cd94aa 270 int err = 0;
b453872c
JG
271
272 char *ev = extra;
55cd94aa 273 char *stop = ev + wrqu->data.length;
b453872c 274 int i = 0;
9387b7ca 275 DECLARE_SSID_BUF(ssid);
b453872c 276
b0a4e7d8 277 LIBIPW_DEBUG_WX("Getting scan\n");
b453872c
JG
278
279 spin_lock_irqsave(&ieee->lock, flags);
280
281 list_for_each_entry(network, &ieee->network_list, list) {
282 i++;
55cd94aa
ZY
283 if (stop - ev < SCAN_ITEM_SIZE) {
284 err = -E2BIG;
285 break;
286 }
287
b453872c
JG
288 if (ieee->scan_age == 0 ||
289 time_after(network->last_scanned + ieee->scan_age, jiffies))
b0a4e7d8 290 ev = libipw_translate_scan(ieee, ev, stop, network,
ccc58057 291 info);
c3d72b96 292 else {
b0a4e7d8 293 LIBIPW_DEBUG_SCAN("Not showing network '%s ("
c3d72b96 294 "%pM)' due to age (%ums).\n",
9387b7ca 295 print_ssid(ssid, network->ssid,
7e272fcf 296 network->ssid_len),
e174961c 297 network->bssid,
c3d72b96
DW
298 elapsed_jiffies_msecs(
299 network->last_scanned));
300 }
b453872c
JG
301 }
302
303 spin_unlock_irqrestore(&ieee->lock, flags);
304
0edd5b44 305 wrqu->data.length = ev - extra;
b453872c
JG
306 wrqu->data.flags = 0;
307
b0a4e7d8 308 LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
b453872c 309
55cd94aa 310 return err;
b453872c
JG
311}
312
b0a4e7d8 313int libipw_wx_set_encode(struct libipw_device *ieee,
b453872c
JG
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;
b0a4e7d8 319 struct libipw_security sec = {
b453872c
JG
320 .flags = 0
321 };
322 int i, key, key_provided, len;
274bfb8d 323 struct lib80211_crypt_data **crypt;
3289a836 324 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
9387b7ca 325 DECLARE_SSID_BUF(ssid);
b453872c 326
b0a4e7d8 327 LIBIPW_DEBUG_WX("SET_ENCODE\n");
b453872c
JG
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;
274bfb8d 337 key = ieee->crypt_info.tx_keyidx;
b453872c
JG
338 }
339
b0a4e7d8 340 LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
b453872c
JG
341 "provided" : "default");
342
274bfb8d 343 crypt = &ieee->crypt_info.crypt[key];
b453872c
JG
344
345 if (erq->flags & IW_ENCODE_DISABLED) {
346 if (key_provided && *crypt) {
b0a4e7d8 347 LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
b453872c 348 key);
274bfb8d 349 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
b453872c 350 } else
b0a4e7d8 351 LIBIPW_DEBUG_WX("Disabling encryption.\n");
b453872c
JG
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++) {
274bfb8d 356 if (ieee->crypt_info.crypt[i] != NULL) {
b453872c
JG
357 if (key_provided)
358 break;
274bfb8d
JL
359 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
360 &ieee->crypt_info.crypt[i]);
b453872c
JG
361 }
362 }
363
364 if (i == WEP_KEYS) {
365 sec.enabled = 0;
f1bf6638 366 sec.encrypt = 0;
b453872c 367 sec.level = SEC_LEVEL_0;
259bf1fd 368 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
b453872c
JG
369 }
370
371 goto done;
372 }
373
b453872c 374 sec.enabled = 1;
f1bf6638 375 sec.encrypt = 1;
259bf1fd 376 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
b453872c
JG
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 */
274bfb8d 382 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
b453872c
JG
383 }
384
f1bf6638 385 if (*crypt == NULL && host_crypto) {
274bfb8d 386 struct lib80211_crypt_data *new_crypt;
b453872c
JG
387
388 /* take WEP into use */
274bfb8d 389 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
b453872c
JG
390 GFP_KERNEL);
391 if (new_crypt == NULL)
392 return -ENOMEM;
274bfb8d 393 new_crypt->ops = lib80211_get_crypto_ops("WEP");
b453872c 394 if (!new_crypt->ops) {
274bfb8d
JL
395 request_module("lib80211_crypt_wep");
396 new_crypt->ops = lib80211_get_crypto_ops("WEP");
b453872c
JG
397 }
398
399 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
6eb6edf0 400 new_crypt->priv = new_crypt->ops->init(key);
b453872c
JG
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: "
274bfb8d 407 "load module lib80211_crypt_wep\n", dev->name);
b453872c
JG
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);
b0a4e7d8 420 LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
9387b7ca 421 key, print_ssid(ssid, sec.keys[key], len),
b453872c
JG
422 erq->length, len);
423 sec.key_sizes[key] = len;
f1bf6638
JK
424 if (*crypt)
425 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
426 (*crypt)->priv);
b453872c
JG
427 sec.flags |= (1 << key);
428 /* This ensures a key will be activated if no key is
c03983ac 429 * explicitly set */
b453872c
JG
430 if (key == sec.active_key)
431 sec.flags |= SEC_ACTIVE_KEY;
f1bf6638 432
b453872c 433 } else {
f1bf6638
JK
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 */
b0a4e7d8 439 LIBIPW_DEBUG_WX("Setting key %d to all "
f1bf6638
JK
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 }
b453872c 447 }
b453872c
JG
448 /* No key data - just set the default TX key index */
449 if (key_provided) {
b0a4e7d8 450 LIBIPW_DEBUG_WX("Setting key %d to default Tx "
f1bf6638 451 "key.\n", key);
274bfb8d 452 ieee->crypt_info.tx_keyidx = key;
b453872c
JG
453 sec.active_key = key;
454 sec.flags |= SEC_ACTIVE_KEY;
455 }
456 }
7dc888fe
JK
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;
b0a4e7d8 462 LIBIPW_DEBUG_WX("Auth: %s\n",
7dc888fe
JK
463 sec.auth_mode == WLAN_AUTH_OPEN ?
464 "OPEN" : "SHARED KEY");
465 }
b453872c
JG
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;
0edd5b44 470 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
e0d369d1 471 sec.encode_alg[key] = SEC_ALG_WEP;
b453872c 472
259bf1fd 473 done:
b453872c
JG
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
b0a4e7d8 491int libipw_wx_get_encode(struct libipw_device *ieee,
b453872c
JG
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;
274bfb8d 497 struct lib80211_crypt_data *crypt;
b0a4e7d8 498 struct libipw_security *sec = &ieee->sec;
b453872c 499
b0a4e7d8 500 LIBIPW_DEBUG_WX("GET_ENCODE\n");
b453872c
JG
501
502 key = erq->flags & IW_ENCODE_INDEX;
503 if (key) {
504 if (key > WEP_KEYS)
505 return -EINVAL;
506 key--;
507 } else
274bfb8d 508 key = ieee->crypt_info.tx_keyidx;
b453872c 509
274bfb8d 510 crypt = ieee->crypt_info.crypt[key];
b453872c
JG
511 erq->flags = key + 1;
512
f1bf6638 513 if (!sec->enabled) {
b453872c
JG
514 erq->length = 0;
515 erq->flags |= IW_ENCODE_DISABLED;
516 return 0;
517 }
518
f1bf6638
JK
519 len = sec->key_sizes[key];
520 memcpy(keybuf, sec->keys[key], len);
b453872c 521
6274115c 522 erq->length = len;
b453872c
JG
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
b0a4e7d8 533int libipw_wx_set_encodeext(struct libipw_device *ieee,
e0d369d1
JK
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;
ccd0fda3 541 int group_key = 0;
e0d369d1 542 const char *alg, *module;
274bfb8d
JL
543 struct lib80211_crypto_ops *ops;
544 struct lib80211_crypt_data **crypt;
e0d369d1 545
b0a4e7d8 546 struct libipw_security sec = {
e0d369d1
JK
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
274bfb8d 556 idx = ieee->crypt_info.tx_keyidx;
e0d369d1 557
ccd0fda3 558 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
274bfb8d 559 crypt = &ieee->crypt_info.crypt[idx];
ccd0fda3
JK
560 group_key = 1;
561 } else {
e189277a
VB
562 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
563 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
e0d369d1
JK
564 return -EINVAL;
565 if (ieee->iw_mode == IW_MODE_INFRA)
274bfb8d 566 crypt = &ieee->crypt_info.crypt[idx];
e0d369d1
JK
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)
274bfb8d 575 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
e0d369d1
JK
576
577 for (i = 0; i < WEP_KEYS; i++)
274bfb8d 578 if (ieee->crypt_info.crypt[i] != NULL)
e0d369d1
JK
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
ccd0fda3
JK
593 if (group_key ? !ieee->host_mc_decrypt :
594 !(ieee->host_encrypt || ieee->host_decrypt ||
595 ieee->host_encrypt_msdu))
e0d369d1
JK
596 goto skip_host_crypt;
597
598 switch (ext->alg) {
599 case IW_ENCODE_ALG_WEP:
600 alg = "WEP";
274bfb8d 601 module = "lib80211_crypt_wep";
e0d369d1
JK
602 break;
603 case IW_ENCODE_ALG_TKIP:
604 alg = "TKIP";
274bfb8d 605 module = "lib80211_crypt_tkip";
e0d369d1
JK
606 break;
607 case IW_ENCODE_ALG_CCMP:
608 alg = "CCMP";
274bfb8d 609 module = "lib80211_crypt_ccmp";
e0d369d1
JK
610 break;
611 default:
b0a4e7d8 612 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
e0d369d1
JK
613 dev->name, ext->alg);
614 ret = -EINVAL;
615 goto done;
616 }
617
274bfb8d 618 ops = lib80211_get_crypto_ops(alg);
e0d369d1
JK
619 if (ops == NULL) {
620 request_module(module);
274bfb8d 621 ops = lib80211_get_crypto_ops(alg);
e0d369d1
JK
622 }
623 if (ops == NULL) {
b0a4e7d8 624 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
e0d369d1
JK
625 dev->name, ext->alg);
626 ret = -EINVAL;
627 goto done;
628 }
629
630 if (*crypt == NULL || (*crypt)->ops != ops) {
274bfb8d 631 struct lib80211_crypt_data *new_crypt;
e0d369d1 632
274bfb8d 633 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
e0d369d1 634
0da974f4 635 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
e0d369d1
JK
636 if (new_crypt == NULL) {
637 ret = -ENOMEM;
638 goto done;
639 }
e0d369d1
JK
640 new_crypt->ops = ops;
641 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
6eb6edf0 642 new_crypt->priv = new_crypt->ops->init(idx);
e0d369d1
JK
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) {
b0a4e7d8 654 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
e0d369d1
JK
655 ret = -EINVAL;
656 goto done;
657 }
658
659 skip_host_crypt:
660 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
274bfb8d 661 ieee->crypt_info.tx_keyidx = idx;
e0d369d1
JK
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 }
ccd0fda3
JK
683 /* Don't set sec level for group keys. */
684 if (group_key)
685 sec.flags &= ~SEC_LEVEL;
e0d369d1
JK
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)) {
b0a4e7d8 701 LIBIPW_DEBUG_WX("%s: reset_port failed\n", dev->name);
e0d369d1
JK
702 return -EINVAL;
703 }
704
705 return ret;
706}
707
b0a4e7d8 708int libipw_wx_get_encodeext(struct libipw_device *ieee,
e0d369d1
JK
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;
b0a4e7d8 714 struct libipw_security *sec = &ieee->sec;
e0d369d1
JK
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
274bfb8d 727 idx = ieee->crypt_info.tx_keyidx;
e0d369d1 728
f59d9782 729 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
e189277a 730 ext->alg != IW_ENCODE_ALG_WEP)
e0d369d1
JK
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
b0a4e7d8
JL
764EXPORT_SYMBOL(libipw_wx_set_encodeext);
765EXPORT_SYMBOL(libipw_wx_get_encodeext);
e0d369d1 766
b0a4e7d8
JL
767EXPORT_SYMBOL(libipw_wx_get_scan);
768EXPORT_SYMBOL(libipw_wx_set_encode);
769EXPORT_SYMBOL(libipw_wx_get_encode);