blob: f87c6b89f8450e513fd6c434864e7ae65b267ad0 [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/******************************************************************************
2
James Ketrenosebeaddc2005-09-21 11:58:43 -05003 Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Jeff Garzikb4538722005-05-12 22:48:20 -04004
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 <jkmaline@cc.hut.fi>
9 Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.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 James P. Ketrenos <ipw2100-admin@linux.intel.com>
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
Jeff Garzikbbeec902005-09-07 00:27:54 -040032
Jeff Garzikb4538722005-05-12 22:48:20 -040033#include <linux/kmod.h>
34#include <linux/module.h>
James Ketrenos42e349f2005-09-21 11:54:07 -050035#include <linux/jiffies.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040036
37#include <net/ieee80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040038#include <linux/wireless.h>
39
Jeff Garzikb4538722005-05-12 22:48:20 -040040static const char *ieee80211_modes[] = {
41 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
42};
43
44#define MAX_CUSTOM_LEN 64
Arjan van de Ven858119e2006-01-14 13:20:43 -080045static char *ipw2100_translate_scan(struct ieee80211_device *ieee,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040046 char *start, char *stop,
Jeff Garzikb4538722005-05-12 22:48:20 -040047 struct ieee80211_network *network)
48{
49 char custom[MAX_CUSTOM_LEN];
50 char *p;
51 struct iw_event iwe;
52 int i, j;
53 u8 max_rate, rate;
54
55 /* First entry *MUST* be the AP MAC address */
56 iwe.cmd = SIOCGIWAP;
57 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
58 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
59 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
60
61 /* Remaining entries will be displayed in the order we provide them */
62
63 /* Add the ESSID */
64 iwe.cmd = SIOCGIWESSID;
65 iwe.u.data.flags = 1;
66 if (network->flags & NETWORK_EMPTY_ESSID) {
67 iwe.u.data.length = sizeof("<hidden>");
68 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
69 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -040070 iwe.u.data.length = min(network->ssid_len, (u8) 32);
Jeff Garzikb4538722005-05-12 22:48:20 -040071 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
72 }
73
74 /* Add the protocol name */
75 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040076 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
77 ieee80211_modes[network->mode]);
Jeff Garzikb4538722005-05-12 22:48:20 -040078 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
79
Jeff Garzik0edd5b42005-09-07 00:48:31 -040080 /* Add mode */
81 iwe.cmd = SIOCGIWMODE;
82 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040083 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040084 iwe.u.mode = IW_MODE_MASTER;
85 else
86 iwe.u.mode = IW_MODE_ADHOC;
87
Jeff Garzik0edd5b42005-09-07 00:48:31 -040088 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040089 }
90
Jeff Garzik0edd5b42005-09-07 00:48:31 -040091 /* Add frequency/channel */
Jeff Garzikb4538722005-05-12 22:48:20 -040092 iwe.cmd = SIOCGIWFREQ;
93/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
94 iwe.u.freq.e = 3; */
95 iwe.u.freq.m = network->channel;
96 iwe.u.freq.e = 0;
97 iwe.u.freq.i = 0;
98 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
99
100 /* Add encryption capability */
101 iwe.cmd = SIOCGIWENCODE;
102 if (network->capability & WLAN_CAPABILITY_PRIVACY)
103 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
104 else
105 iwe.u.data.flags = IW_ENCODE_DISABLED;
106 iwe.u.data.length = 0;
107 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
108
109 /* Add basic and extended rates */
110 max_rate = 0;
111 p = custom;
112 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400113 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400114 if (j < network->rates_ex_len &&
115 ((network->rates_ex[j] & 0x7F) <
116 (network->rates[i] & 0x7F)))
117 rate = network->rates_ex[j++] & 0x7F;
118 else
119 rate = network->rates[i++] & 0x7F;
120 if (rate > max_rate)
121 max_rate = rate;
122 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
123 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
124 }
125 for (; j < network->rates_ex_len; j++) {
126 rate = network->rates_ex[j] & 0x7F;
127 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
128 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
129 if (rate > max_rate)
130 max_rate = rate;
131 }
132
133 iwe.cmd = SIOCGIWRATE;
134 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
135 iwe.u.bitrate.value = max_rate * 500000;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400136 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400137
138 iwe.cmd = IWEVCUSTOM;
139 iwe.u.data.length = p - custom;
140 if (iwe.u.data.length)
141 start = iwe_stream_add_point(start, stop, &iwe, custom);
142
143 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400144 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500145 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
146 IW_QUAL_NOISE_UPDATED;
147
148 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
149 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
150 IW_QUAL_LEVEL_INVALID;
151 iwe.u.qual.qual = 0;
152 iwe.u.qual.level = 0;
153 } else {
154 iwe.u.qual.level = network->stats.rssi;
Jiri Benc757d18f2005-10-10 19:16:53 +0200155 if (ieee->perfect_rssi == ieee->worst_rssi)
156 iwe.u.qual.qual = 100;
157 else
158 iwe.u.qual.qual =
159 (100 *
160 (ieee->perfect_rssi - ieee->worst_rssi) *
161 (ieee->perfect_rssi - ieee->worst_rssi) -
162 (ieee->perfect_rssi - network->stats.rssi) *
163 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500164 62 * (ieee->perfect_rssi -
165 network->stats.rssi))) /
166 ((ieee->perfect_rssi -
167 ieee->worst_rssi) * (ieee->perfect_rssi -
168 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500169 if (iwe.u.qual.qual > 100)
170 iwe.u.qual.qual = 100;
171 else if (iwe.u.qual.qual < 1)
172 iwe.u.qual.qual = 0;
173 }
174
175 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400176 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500177 iwe.u.qual.noise = 0;
178 } else {
179 iwe.u.qual.noise = network->stats.noise;
180 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400181
182 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
183
184 iwe.cmd = IWEVCUSTOM;
185 p = custom;
186
187 iwe.u.data.length = p - custom;
188 if (iwe.u.data.length)
189 start = iwe_stream_add_point(start, stop, &iwe, custom);
190
James Ketrenos20d64712005-09-21 11:53:43 -0500191 if (network->wpa_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400192 char buf[MAX_WPA_IE_LEN * 2 + 30];
193
194 u8 *p = buf;
195 p += sprintf(p, "wpa_ie=");
196 for (i = 0; i < network->wpa_ie_len; i++) {
197 p += sprintf(p, "%02x", network->wpa_ie[i]);
198 }
199
200 memset(&iwe, 0, sizeof(iwe));
201 iwe.cmd = IWEVCUSTOM;
202 iwe.u.data.length = strlen(buf);
203 start = iwe_stream_add_point(start, stop, &iwe, buf);
204 }
205
James Ketrenos20d64712005-09-21 11:53:43 -0500206 if (network->rsn_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400207 char buf[MAX_WPA_IE_LEN * 2 + 30];
208
209 u8 *p = buf;
210 p += sprintf(p, "rsn_ie=");
211 for (i = 0; i < network->rsn_ie_len; i++) {
212 p += sprintf(p, "%02x", network->rsn_ie[i]);
213 }
214
215 memset(&iwe, 0, sizeof(iwe));
216 iwe.cmd = IWEVCUSTOM;
217 iwe.u.data.length = strlen(buf);
218 start = iwe_stream_add_point(start, stop, &iwe, buf);
219 }
220
221 /* Add EXTRA: Age to display seconds since last beacon/probe response
222 * for given network. */
223 iwe.cmd = IWEVCUSTOM;
224 p = custom;
225 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500226 " Last beacon: %dms ago",
227 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400228 iwe.u.data.length = p - custom;
229 if (iwe.u.data.length)
230 start = iwe_stream_add_point(start, stop, &iwe, custom);
231
Jeff Garzikb4538722005-05-12 22:48:20 -0400232 return start;
233}
234
Zhu Yi55cd94a2006-01-19 16:20:59 +0800235#define SCAN_ITEM_SIZE 128
236
Jeff Garzikb4538722005-05-12 22:48:20 -0400237int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
238 struct iw_request_info *info,
239 union iwreq_data *wrqu, char *extra)
240{
241 struct ieee80211_network *network;
242 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800243 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400244
245 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800246 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400247 int i = 0;
248
249 IEEE80211_DEBUG_WX("Getting scan\n");
250
251 spin_lock_irqsave(&ieee->lock, flags);
252
253 list_for_each_entry(network, &ieee->network_list, list) {
254 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800255 if (stop - ev < SCAN_ITEM_SIZE) {
256 err = -E2BIG;
257 break;
258 }
259
Jeff Garzikb4538722005-05-12 22:48:20 -0400260 if (ieee->scan_age == 0 ||
261 time_after(network->last_scanned + ieee->scan_age, jiffies))
262 ev = ipw2100_translate_scan(ieee, ev, stop, network);
263 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400264 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500265 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400266 escape_essid(network->ssid,
267 network->ssid_len),
268 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500269 jiffies_to_msecs(jiffies -
270 network->
271 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400272 }
273
274 spin_unlock_irqrestore(&ieee->lock, flags);
275
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400276 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400277 wrqu->data.flags = 0;
278
279 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
280
Zhu Yi55cd94a2006-01-19 16:20:59 +0800281 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400282}
283
284int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
285 struct iw_request_info *info,
286 union iwreq_data *wrqu, char *keybuf)
287{
288 struct iw_point *erq = &(wrqu->encoding);
289 struct net_device *dev = ieee->dev;
290 struct ieee80211_security sec = {
291 .flags = 0
292 };
293 int i, key, key_provided, len;
294 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100295 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400296
297 IEEE80211_DEBUG_WX("SET_ENCODE\n");
298
299 key = erq->flags & IW_ENCODE_INDEX;
300 if (key) {
301 if (key > WEP_KEYS)
302 return -EINVAL;
303 key--;
304 key_provided = 1;
305 } else {
306 key_provided = 0;
307 key = ieee->tx_keyidx;
308 }
309
310 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
311 "provided" : "default");
312
313 crypt = &ieee->crypt[key];
314
315 if (erq->flags & IW_ENCODE_DISABLED) {
316 if (key_provided && *crypt) {
317 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
318 key);
319 ieee80211_crypt_delayed_deinit(ieee, crypt);
320 } else
321 IEEE80211_DEBUG_WX("Disabling encryption.\n");
322
323 /* Check all the keys to see if any are still configured,
324 * and if no key index was provided, de-init them all */
325 for (i = 0; i < WEP_KEYS; i++) {
326 if (ieee->crypt[i] != NULL) {
327 if (key_provided)
328 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400329 ieee80211_crypt_delayed_deinit(ieee,
330 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400331 }
332 }
333
334 if (i == WEP_KEYS) {
335 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500336 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400337 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500338 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400339 }
340
341 goto done;
342 }
343
Jeff Garzikb4538722005-05-12 22:48:20 -0400344 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500345 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500346 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400347
348 if (*crypt != NULL && (*crypt)->ops != NULL &&
349 strcmp((*crypt)->ops->name, "WEP") != 0) {
350 /* changing to use WEP; deinit previously used algorithm
351 * on this key */
352 ieee80211_crypt_delayed_deinit(ieee, crypt);
353 }
354
James Ketrenosf1bf6632005-09-21 11:53:54 -0500355 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400356 struct ieee80211_crypt_data *new_crypt;
357
358 /* take WEP into use */
359 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
360 GFP_KERNEL);
361 if (new_crypt == NULL)
362 return -ENOMEM;
363 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
364 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
365 if (!new_crypt->ops) {
366 request_module("ieee80211_crypt_wep");
367 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
368 }
369
370 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000371 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400372
373 if (!new_crypt->ops || !new_crypt->priv) {
374 kfree(new_crypt);
375 new_crypt = NULL;
376
377 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400378 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400379 return -EOPNOTSUPP;
380 }
381 *crypt = new_crypt;
382 }
383
384 /* If a new key was provided, set it up */
385 if (erq->length > 0) {
386 len = erq->length <= 5 ? 5 : 13;
387 memcpy(sec.keys[key], keybuf, erq->length);
388 if (len > erq->length)
389 memset(sec.keys[key] + erq->length, 0,
390 len - erq->length);
391 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
392 key, escape_essid(sec.keys[key], len),
393 erq->length, len);
394 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500395 if (*crypt)
396 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
397 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400398 sec.flags |= (1 << key);
399 /* This ensures a key will be activated if no key is
400 * explicitely set */
401 if (key == sec.active_key)
402 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400403
James Ketrenosf1bf6632005-09-21 11:53:54 -0500404 } else {
405 if (host_crypto) {
406 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
407 NULL, (*crypt)->priv);
408 if (len == 0) {
409 /* Set a default key of all 0 */
410 IEEE80211_DEBUG_WX("Setting key %d to all "
411 "zero.\n", key);
412 memset(sec.keys[key], 0, 13);
413 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
414 (*crypt)->priv);
415 sec.key_sizes[key] = 13;
416 sec.flags |= (1 << key);
417 }
418 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400419 /* No key data - just set the default TX key index */
420 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500421 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
422 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400423 ieee->tx_keyidx = key;
424 sec.active_key = key;
425 sec.flags |= SEC_ACTIVE_KEY;
426 }
427 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500428 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
429 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
430 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
431 WLAN_AUTH_SHARED_KEY;
432 sec.flags |= SEC_AUTH_MODE;
433 IEEE80211_DEBUG_WX("Auth: %s\n",
434 sec.auth_mode == WLAN_AUTH_OPEN ?
435 "OPEN" : "SHARED KEY");
436 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400437
438 /* For now we just support WEP, so only set that security level...
439 * TODO: When WPA is added this is one place that needs to change */
440 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400441 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500442 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400443
James Ketrenos259bf1f2005-09-21 11:54:22 -0500444 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400445 if (ieee->set_security)
446 ieee->set_security(dev, &sec);
447
448 /* Do not reset port if card is in Managed mode since resetting will
449 * generate new IEEE 802.11 authentication which may end up in looping
450 * with IEEE 802.1X. If your hardware requires a reset after WEP
451 * configuration (for example... Prism2), implement the reset_port in
452 * the callbacks structures used to initialize the 802.11 stack. */
453 if (ieee->reset_on_keychange &&
454 ieee->iw_mode != IW_MODE_INFRA &&
455 ieee->reset_port && ieee->reset_port(dev)) {
456 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
457 return -EINVAL;
458 }
459 return 0;
460}
461
462int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
463 struct iw_request_info *info,
464 union iwreq_data *wrqu, char *keybuf)
465{
466 struct iw_point *erq = &(wrqu->encoding);
467 int len, key;
468 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500469 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400470
471 IEEE80211_DEBUG_WX("GET_ENCODE\n");
472
473 key = erq->flags & IW_ENCODE_INDEX;
474 if (key) {
475 if (key > WEP_KEYS)
476 return -EINVAL;
477 key--;
478 } else
479 key = ieee->tx_keyidx;
480
481 crypt = ieee->crypt[key];
482 erq->flags = key + 1;
483
James Ketrenosf1bf6632005-09-21 11:53:54 -0500484 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400485 erq->length = 0;
486 erq->flags |= IW_ENCODE_DISABLED;
487 return 0;
488 }
489
James Ketrenosf1bf6632005-09-21 11:53:54 -0500490 len = sec->key_sizes[key];
491 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400492
James Ketrenosf1bf6632005-09-21 11:53:54 -0500493 erq->length = (len >= 0 ? len : 0);
Jeff Garzikb4538722005-05-12 22:48:20 -0400494 erq->flags |= IW_ENCODE_ENABLED;
495
496 if (ieee->open_wep)
497 erq->flags |= IW_ENCODE_OPEN;
498 else
499 erq->flags |= IW_ENCODE_RESTRICTED;
500
501 return 0;
502}
503
James Ketrenose0d369d2005-09-21 11:54:30 -0500504int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
505 struct iw_request_info *info,
506 union iwreq_data *wrqu, char *extra)
507{
508 struct net_device *dev = ieee->dev;
509 struct iw_point *encoding = &wrqu->encoding;
510 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
511 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500512 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500513 const char *alg, *module;
514 struct ieee80211_crypto_ops *ops;
515 struct ieee80211_crypt_data **crypt;
516
517 struct ieee80211_security sec = {
518 .flags = 0,
519 };
520
521 idx = encoding->flags & IW_ENCODE_INDEX;
522 if (idx) {
523 if (idx < 1 || idx > WEP_KEYS)
524 return -EINVAL;
525 idx--;
526 } else
527 idx = ieee->tx_keyidx;
528
James Ketrenosccd0fda2005-09-21 11:58:32 -0500529 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500530 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500531 group_key = 1;
532 } else {
Volker Braune1892772005-10-24 10:15:36 -0500533 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
534 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500535 return -EINVAL;
536 if (ieee->iw_mode == IW_MODE_INFRA)
537 crypt = &ieee->crypt[idx];
538 else
539 return -EINVAL;
540 }
541
542 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
543 if ((encoding->flags & IW_ENCODE_DISABLED) ||
544 ext->alg == IW_ENCODE_ALG_NONE) {
545 if (*crypt)
546 ieee80211_crypt_delayed_deinit(ieee, crypt);
547
548 for (i = 0; i < WEP_KEYS; i++)
549 if (ieee->crypt[i] != NULL)
550 break;
551
552 if (i == WEP_KEYS) {
553 sec.enabled = 0;
554 sec.encrypt = 0;
555 sec.level = SEC_LEVEL_0;
556 sec.flags |= SEC_LEVEL;
557 }
558 goto done;
559 }
560
561 sec.enabled = 1;
562 sec.encrypt = 1;
563
James Ketrenosccd0fda2005-09-21 11:58:32 -0500564 if (group_key ? !ieee->host_mc_decrypt :
565 !(ieee->host_encrypt || ieee->host_decrypt ||
566 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500567 goto skip_host_crypt;
568
569 switch (ext->alg) {
570 case IW_ENCODE_ALG_WEP:
571 alg = "WEP";
572 module = "ieee80211_crypt_wep";
573 break;
574 case IW_ENCODE_ALG_TKIP:
575 alg = "TKIP";
576 module = "ieee80211_crypt_tkip";
577 break;
578 case IW_ENCODE_ALG_CCMP:
579 alg = "CCMP";
580 module = "ieee80211_crypt_ccmp";
581 break;
582 default:
583 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
584 dev->name, ext->alg);
585 ret = -EINVAL;
586 goto done;
587 }
588
589 ops = ieee80211_get_crypto_ops(alg);
590 if (ops == NULL) {
591 request_module(module);
592 ops = ieee80211_get_crypto_ops(alg);
593 }
594 if (ops == NULL) {
595 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
596 dev->name, ext->alg);
597 ret = -EINVAL;
598 goto done;
599 }
600
601 if (*crypt == NULL || (*crypt)->ops != ops) {
602 struct ieee80211_crypt_data *new_crypt;
603
604 ieee80211_crypt_delayed_deinit(ieee, crypt);
605
606 new_crypt = (struct ieee80211_crypt_data *)
607 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
608 if (new_crypt == NULL) {
609 ret = -ENOMEM;
610 goto done;
611 }
612 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
613 new_crypt->ops = ops;
614 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000615 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500616 if (new_crypt->priv == NULL) {
617 kfree(new_crypt);
618 ret = -EINVAL;
619 goto done;
620 }
621 *crypt = new_crypt;
622 }
623
624 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
625 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
626 (*crypt)->priv) < 0) {
627 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
628 ret = -EINVAL;
629 goto done;
630 }
631
632 skip_host_crypt:
633 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
634 ieee->tx_keyidx = idx;
635 sec.active_key = idx;
636 sec.flags |= SEC_ACTIVE_KEY;
637 }
638
639 if (ext->alg != IW_ENCODE_ALG_NONE) {
640 memcpy(sec.keys[idx], ext->key, ext->key_len);
641 sec.key_sizes[idx] = ext->key_len;
642 sec.flags |= (1 << idx);
643 if (ext->alg == IW_ENCODE_ALG_WEP) {
644 sec.encode_alg[idx] = SEC_ALG_WEP;
645 sec.flags |= SEC_LEVEL;
646 sec.level = SEC_LEVEL_1;
647 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
648 sec.encode_alg[idx] = SEC_ALG_TKIP;
649 sec.flags |= SEC_LEVEL;
650 sec.level = SEC_LEVEL_2;
651 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
652 sec.encode_alg[idx] = SEC_ALG_CCMP;
653 sec.flags |= SEC_LEVEL;
654 sec.level = SEC_LEVEL_3;
655 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500656 /* Don't set sec level for group keys. */
657 if (group_key)
658 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500659 }
660 done:
661 if (ieee->set_security)
662 ieee->set_security(ieee->dev, &sec);
663
664 /*
665 * Do not reset port if card is in Managed mode since resetting will
666 * generate new IEEE 802.11 authentication which may end up in looping
667 * with IEEE 802.1X. If your hardware requires a reset after WEP
668 * configuration (for example... Prism2), implement the reset_port in
669 * the callbacks structures used to initialize the 802.11 stack.
670 */
671 if (ieee->reset_on_keychange &&
672 ieee->iw_mode != IW_MODE_INFRA &&
673 ieee->reset_port && ieee->reset_port(dev)) {
674 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
675 return -EINVAL;
676 }
677
678 return ret;
679}
680
681int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
682 struct iw_request_info *info,
683 union iwreq_data *wrqu, char *extra)
684{
685 struct iw_point *encoding = &wrqu->encoding;
686 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
687 struct ieee80211_security *sec = &ieee->sec;
688 int idx, max_key_len;
689
690 max_key_len = encoding->length - sizeof(*ext);
691 if (max_key_len < 0)
692 return -EINVAL;
693
694 idx = encoding->flags & IW_ENCODE_INDEX;
695 if (idx) {
696 if (idx < 1 || idx > WEP_KEYS)
697 return -EINVAL;
698 idx--;
699 } else
700 idx = ieee->tx_keyidx;
701
Volker Braune1892772005-10-24 10:15:36 -0500702 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
703 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500704 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
705 return -EINVAL;
706
707 encoding->flags = idx + 1;
708 memset(ext, 0, sizeof(*ext));
709
710 if (!sec->enabled) {
711 ext->alg = IW_ENCODE_ALG_NONE;
712 ext->key_len = 0;
713 encoding->flags |= IW_ENCODE_DISABLED;
714 } else {
715 if (sec->encode_alg[idx] == SEC_ALG_WEP)
716 ext->alg = IW_ENCODE_ALG_WEP;
717 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
718 ext->alg = IW_ENCODE_ALG_TKIP;
719 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
720 ext->alg = IW_ENCODE_ALG_CCMP;
721 else
722 return -EINVAL;
723
724 ext->key_len = sec->key_sizes[idx];
725 memcpy(ext->key, sec->keys[idx], ext->key_len);
726 encoding->flags |= IW_ENCODE_ENABLED;
727 if (ext->key_len &&
728 (ext->alg == IW_ENCODE_ALG_TKIP ||
729 ext->alg == IW_ENCODE_ALG_CCMP))
730 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
731
732 }
733
734 return 0;
735}
736
737EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
738EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500739
Jeff Garzikb4538722005-05-12 22:48:20 -0400740EXPORT_SYMBOL(ieee80211_wx_get_scan);
741EXPORT_SYMBOL(ieee80211_wx_set_encode);
742EXPORT_SYMBOL(ieee80211_wx_get_encode);