blob: fc4e1377aba7f8e6600c32e5f62c5eec8325b2cc [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/******************************************************************************
2
3 Copyright(c) 2004 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 <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>
35
36#include <net/ieee80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040037#include <linux/wireless.h>
38
Jeff Garzikb4538722005-05-12 22:48:20 -040039static const char *ieee80211_modes[] = {
40 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
41};
42
43#define MAX_CUSTOM_LEN 64
44static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040045 char *start, char *stop,
Jeff Garzikb4538722005-05-12 22:48:20 -040046 struct ieee80211_network *network)
47{
48 char custom[MAX_CUSTOM_LEN];
49 char *p;
50 struct iw_event iwe;
51 int i, j;
52 u8 max_rate, rate;
53
54 /* First entry *MUST* be the AP MAC address */
55 iwe.cmd = SIOCGIWAP;
56 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
57 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
58 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
59
60 /* Remaining entries will be displayed in the order we provide them */
61
62 /* Add the ESSID */
63 iwe.cmd = SIOCGIWESSID;
64 iwe.u.data.flags = 1;
65 if (network->flags & NETWORK_EMPTY_ESSID) {
66 iwe.u.data.length = sizeof("<hidden>");
67 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
68 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -040069 iwe.u.data.length = min(network->ssid_len, (u8) 32);
Jeff Garzikb4538722005-05-12 22:48:20 -040070 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
71 }
72
73 /* Add the protocol name */
74 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040075 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
76 ieee80211_modes[network->mode]);
Jeff Garzikb4538722005-05-12 22:48:20 -040077 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
78
Jeff Garzik0edd5b42005-09-07 00:48:31 -040079 /* Add mode */
80 iwe.cmd = SIOCGIWMODE;
81 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040082 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040083 iwe.u.mode = IW_MODE_MASTER;
84 else
85 iwe.u.mode = IW_MODE_ADHOC;
86
Jeff Garzik0edd5b42005-09-07 00:48:31 -040087 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040088 }
89
Jeff Garzik0edd5b42005-09-07 00:48:31 -040090 /* Add frequency/channel */
Jeff Garzikb4538722005-05-12 22:48:20 -040091 iwe.cmd = SIOCGIWFREQ;
92/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
93 iwe.u.freq.e = 3; */
94 iwe.u.freq.m = network->channel;
95 iwe.u.freq.e = 0;
96 iwe.u.freq.i = 0;
97 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
98
99 /* Add encryption capability */
100 iwe.cmd = SIOCGIWENCODE;
101 if (network->capability & WLAN_CAPABILITY_PRIVACY)
102 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
103 else
104 iwe.u.data.flags = IW_ENCODE_DISABLED;
105 iwe.u.data.length = 0;
106 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
107
108 /* Add basic and extended rates */
109 max_rate = 0;
110 p = custom;
111 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400112 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400113 if (j < network->rates_ex_len &&
114 ((network->rates_ex[j] & 0x7F) <
115 (network->rates[i] & 0x7F)))
116 rate = network->rates_ex[j++] & 0x7F;
117 else
118 rate = network->rates[i++] & 0x7F;
119 if (rate > max_rate)
120 max_rate = rate;
121 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
122 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
123 }
124 for (; j < network->rates_ex_len; j++) {
125 rate = network->rates_ex[j] & 0x7F;
126 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
127 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
128 if (rate > max_rate)
129 max_rate = rate;
130 }
131
132 iwe.cmd = SIOCGIWRATE;
133 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
134 iwe.u.bitrate.value = max_rate * 500000;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400135 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400136
137 iwe.cmd = IWEVCUSTOM;
138 iwe.u.data.length = p - custom;
139 if (iwe.u.data.length)
140 start = iwe_stream_add_point(start, stop, &iwe, custom);
141
142 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400143 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500144 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
145 IW_QUAL_NOISE_UPDATED;
146
147 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
148 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
149 IW_QUAL_LEVEL_INVALID;
150 iwe.u.qual.qual = 0;
151 iwe.u.qual.level = 0;
152 } else {
153 iwe.u.qual.level = network->stats.rssi;
154 iwe.u.qual.qual =
155 (100 *
156 (ieee->perfect_rssi - ieee->worst_rssi) *
157 (ieee->perfect_rssi - ieee->worst_rssi) -
158 (ieee->perfect_rssi - network->stats.rssi) *
159 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
160 62 * (ieee->perfect_rssi - network->stats.rssi))) /
161 ((ieee->perfect_rssi - ieee->worst_rssi) *
162 (ieee->perfect_rssi - ieee->worst_rssi));
163 if (iwe.u.qual.qual > 100)
164 iwe.u.qual.qual = 100;
165 else if (iwe.u.qual.qual < 1)
166 iwe.u.qual.qual = 0;
167 }
168
169 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400170 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500171 iwe.u.qual.noise = 0;
172 } else {
173 iwe.u.qual.noise = network->stats.noise;
174 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400175
176 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
177
178 iwe.cmd = IWEVCUSTOM;
179 p = custom;
180
181 iwe.u.data.length = p - custom;
182 if (iwe.u.data.length)
183 start = iwe_stream_add_point(start, stop, &iwe, custom);
184
James Ketrenos20d64712005-09-21 11:53:43 -0500185 if (network->wpa_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400186 char buf[MAX_WPA_IE_LEN * 2 + 30];
187
188 u8 *p = buf;
189 p += sprintf(p, "wpa_ie=");
190 for (i = 0; i < network->wpa_ie_len; i++) {
191 p += sprintf(p, "%02x", network->wpa_ie[i]);
192 }
193
194 memset(&iwe, 0, sizeof(iwe));
195 iwe.cmd = IWEVCUSTOM;
196 iwe.u.data.length = strlen(buf);
197 start = iwe_stream_add_point(start, stop, &iwe, buf);
198 }
199
James Ketrenos20d64712005-09-21 11:53:43 -0500200 if (network->rsn_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400201 char buf[MAX_WPA_IE_LEN * 2 + 30];
202
203 u8 *p = buf;
204 p += sprintf(p, "rsn_ie=");
205 for (i = 0; i < network->rsn_ie_len; i++) {
206 p += sprintf(p, "%02x", network->rsn_ie[i]);
207 }
208
209 memset(&iwe, 0, sizeof(iwe));
210 iwe.cmd = IWEVCUSTOM;
211 iwe.u.data.length = strlen(buf);
212 start = iwe_stream_add_point(start, stop, &iwe, buf);
213 }
214
215 /* Add EXTRA: Age to display seconds since last beacon/probe response
216 * for given network. */
217 iwe.cmd = IWEVCUSTOM;
218 p = custom;
219 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400220 " Last beacon: %lums ago",
221 (jiffies - network->last_scanned) / (HZ / 100));
Jeff Garzikb4538722005-05-12 22:48:20 -0400222 iwe.u.data.length = p - custom;
223 if (iwe.u.data.length)
224 start = iwe_stream_add_point(start, stop, &iwe, custom);
225
Jeff Garzikb4538722005-05-12 22:48:20 -0400226 return start;
227}
228
229int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
230 struct iw_request_info *info,
231 union iwreq_data *wrqu, char *extra)
232{
233 struct ieee80211_network *network;
234 unsigned long flags;
235
236 char *ev = extra;
237 char *stop = ev + IW_SCAN_MAX_DATA;
238 int i = 0;
239
240 IEEE80211_DEBUG_WX("Getting scan\n");
241
242 spin_lock_irqsave(&ieee->lock, flags);
243
244 list_for_each_entry(network, &ieee->network_list, list) {
245 i++;
246 if (ieee->scan_age == 0 ||
247 time_after(network->last_scanned + ieee->scan_age, jiffies))
248 ev = ipw2100_translate_scan(ieee, ev, stop, network);
249 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400250 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
251 MAC_FMT ")' due to age (%lums).\n",
252 escape_essid(network->ssid,
253 network->ssid_len),
254 MAC_ARG(network->bssid),
255 (jiffies -
256 network->last_scanned) / (HZ /
257 100));
Jeff Garzikb4538722005-05-12 22:48:20 -0400258 }
259
260 spin_unlock_irqrestore(&ieee->lock, flags);
261
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400262 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400263 wrqu->data.flags = 0;
264
265 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
266
267 return 0;
268}
269
270int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
271 struct iw_request_info *info,
272 union iwreq_data *wrqu, char *keybuf)
273{
274 struct iw_point *erq = &(wrqu->encoding);
275 struct net_device *dev = ieee->dev;
276 struct ieee80211_security sec = {
277 .flags = 0
278 };
279 int i, key, key_provided, len;
280 struct ieee80211_crypt_data **crypt;
281
282 IEEE80211_DEBUG_WX("SET_ENCODE\n");
283
284 key = erq->flags & IW_ENCODE_INDEX;
285 if (key) {
286 if (key > WEP_KEYS)
287 return -EINVAL;
288 key--;
289 key_provided = 1;
290 } else {
291 key_provided = 0;
292 key = ieee->tx_keyidx;
293 }
294
295 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
296 "provided" : "default");
297
298 crypt = &ieee->crypt[key];
299
300 if (erq->flags & IW_ENCODE_DISABLED) {
301 if (key_provided && *crypt) {
302 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
303 key);
304 ieee80211_crypt_delayed_deinit(ieee, crypt);
305 } else
306 IEEE80211_DEBUG_WX("Disabling encryption.\n");
307
308 /* Check all the keys to see if any are still configured,
309 * and if no key index was provided, de-init them all */
310 for (i = 0; i < WEP_KEYS; i++) {
311 if (ieee->crypt[i] != NULL) {
312 if (key_provided)
313 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400314 ieee80211_crypt_delayed_deinit(ieee,
315 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400316 }
317 }
318
319 if (i == WEP_KEYS) {
320 sec.enabled = 0;
321 sec.level = SEC_LEVEL_0;
322 sec.flags |= SEC_ENABLED | SEC_LEVEL;
323 }
324
325 goto done;
326 }
327
Jeff Garzikb4538722005-05-12 22:48:20 -0400328 sec.enabled = 1;
329 sec.flags |= SEC_ENABLED;
330
331 if (*crypt != NULL && (*crypt)->ops != NULL &&
332 strcmp((*crypt)->ops->name, "WEP") != 0) {
333 /* changing to use WEP; deinit previously used algorithm
334 * on this key */
335 ieee80211_crypt_delayed_deinit(ieee, crypt);
336 }
337
338 if (*crypt == NULL) {
339 struct ieee80211_crypt_data *new_crypt;
340
341 /* take WEP into use */
342 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
343 GFP_KERNEL);
344 if (new_crypt == NULL)
345 return -ENOMEM;
346 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
347 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
348 if (!new_crypt->ops) {
349 request_module("ieee80211_crypt_wep");
350 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
351 }
352
353 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos20d64712005-09-21 11:53:43 -0500354 new_crypt->priv = new_crypt->ops->init(ieee, key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400355
356 if (!new_crypt->ops || !new_crypt->priv) {
357 kfree(new_crypt);
358 new_crypt = NULL;
359
360 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400361 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400362 return -EOPNOTSUPP;
363 }
364 *crypt = new_crypt;
365 }
366
367 /* If a new key was provided, set it up */
368 if (erq->length > 0) {
369 len = erq->length <= 5 ? 5 : 13;
370 memcpy(sec.keys[key], keybuf, erq->length);
371 if (len > erq->length)
372 memset(sec.keys[key] + erq->length, 0,
373 len - erq->length);
374 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
375 key, escape_essid(sec.keys[key], len),
376 erq->length, len);
377 sec.key_sizes[key] = len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400378 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
Jeff Garzikb4538722005-05-12 22:48:20 -0400379 (*crypt)->priv);
380 sec.flags |= (1 << key);
381 /* This ensures a key will be activated if no key is
382 * explicitely set */
383 if (key == sec.active_key)
384 sec.flags |= SEC_ACTIVE_KEY;
385 } else {
386 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
387 NULL, (*crypt)->priv);
388 if (len == 0) {
389 /* Set a default key of all 0 */
390 IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
391 key);
392 memset(sec.keys[key], 0, 13);
393 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
394 (*crypt)->priv);
395 sec.key_sizes[key] = 13;
396 sec.flags |= (1 << key);
397 }
398
399 /* No key data - just set the default TX key index */
400 if (key_provided) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400401 IEEE80211_DEBUG_WX
402 ("Setting key %d to default Tx key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400403 ieee->tx_keyidx = key;
404 sec.active_key = key;
405 sec.flags |= SEC_ACTIVE_KEY;
406 }
407 }
408
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400409 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400410 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
411 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
412 sec.flags |= SEC_AUTH_MODE;
413 IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
414 "OPEN" : "SHARED KEY");
415
416 /* For now we just support WEP, so only set that security level...
417 * TODO: When WPA is added this is one place that needs to change */
418 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400419 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
Jeff Garzikb4538722005-05-12 22:48:20 -0400420
421 if (ieee->set_security)
422 ieee->set_security(dev, &sec);
423
424 /* Do not reset port if card is in Managed mode since resetting will
425 * generate new IEEE 802.11 authentication which may end up in looping
426 * with IEEE 802.1X. If your hardware requires a reset after WEP
427 * configuration (for example... Prism2), implement the reset_port in
428 * the callbacks structures used to initialize the 802.11 stack. */
429 if (ieee->reset_on_keychange &&
430 ieee->iw_mode != IW_MODE_INFRA &&
431 ieee->reset_port && ieee->reset_port(dev)) {
432 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
433 return -EINVAL;
434 }
435 return 0;
436}
437
438int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
439 struct iw_request_info *info,
440 union iwreq_data *wrqu, char *keybuf)
441{
442 struct iw_point *erq = &(wrqu->encoding);
443 int len, key;
444 struct ieee80211_crypt_data *crypt;
445
446 IEEE80211_DEBUG_WX("GET_ENCODE\n");
447
448 key = erq->flags & IW_ENCODE_INDEX;
449 if (key) {
450 if (key > WEP_KEYS)
451 return -EINVAL;
452 key--;
453 } else
454 key = ieee->tx_keyidx;
455
456 crypt = ieee->crypt[key];
457 erq->flags = key + 1;
458
459 if (crypt == NULL || crypt->ops == NULL) {
460 erq->length = 0;
461 erq->flags |= IW_ENCODE_DISABLED;
462 return 0;
463 }
464
465 if (strcmp(crypt->ops->name, "WEP") != 0) {
466 /* only WEP is supported with wireless extensions, so just
467 * report that encryption is used */
468 erq->length = 0;
469 erq->flags |= IW_ENCODE_ENABLED;
470 return 0;
471 }
472
473 len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
474 erq->length = (len >= 0 ? len : 0);
475
476 erq->flags |= IW_ENCODE_ENABLED;
477
478 if (ieee->open_wep)
479 erq->flags |= IW_ENCODE_OPEN;
480 else
481 erq->flags |= IW_ENCODE_RESTRICTED;
482
483 return 0;
484}
485
486EXPORT_SYMBOL(ieee80211_wx_get_scan);
487EXPORT_SYMBOL(ieee80211_wx_set_encode);
488EXPORT_SYMBOL(ieee80211_wx_get_encode);