[PATCH] ieee80211: Updated atmel to be compatible with ieee80211_hdr changes
[deliverable/linux.git] / net / ieee80211 / ieee80211_wx.c
CommitLineData
b453872c
JG
1/******************************************************************************
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******************************************************************************/
bbeec90b 32
b453872c
JG
33#include <linux/kmod.h>
34#include <linux/module.h>
35
36#include <net/ieee80211.h>
bbeec90b
JG
37#include <linux/wireless.h>
38
b453872c
JG
39static 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,
0edd5b44 45 char *start, char *stop,
b453872c
JG
46 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 {
0edd5b44 69 iwe.u.data.length = min(network->ssid_len, (u8) 32);
b453872c
JG
70 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
71 }
72
73 /* Add the protocol name */
74 iwe.cmd = SIOCGIWNAME;
0edd5b44
JG
75 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
76 ieee80211_modes[network->mode]);
b453872c
JG
77 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
78
0edd5b44
JG
79 /* Add mode */
80 iwe.cmd = SIOCGIWMODE;
81 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
1b5cca3a 82 if (network->capability & WLAN_CAPABILITY_ESS)
b453872c
JG
83 iwe.u.mode = IW_MODE_MASTER;
84 else
85 iwe.u.mode = IW_MODE_ADHOC;
86
0edd5b44 87 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
b453872c
JG
88 }
89
0edd5b44 90 /* Add frequency/channel */
b453872c
JG
91 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): ");
0edd5b44 112 for (i = 0, j = 0; i < network->rates_len;) {
b453872c
JG
113 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;
0edd5b44 135 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
b453872c
JG
136
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 */
b453872c 143 iwe.cmd = IWEVQUAL;
b1b508e1
JK
144 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)) {
b453872c 170 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
b1b508e1
JK
171 iwe.u.qual.noise = 0;
172 } else {
173 iwe.u.qual.noise = network->stats.noise;
174 }
b453872c
JG
175
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
0edd5b44 185 if (ieee->wpa_enabled && network->wpa_ie_len) {
b453872c
JG
186 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
0edd5b44 200 if (ieee->wpa_enabled && network->rsn_ie_len) {
b453872c
JG
201 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),
0edd5b44
JG
220 " Last beacon: %lums ago",
221 (jiffies - network->last_scanned) / (HZ / 100));
b453872c
JG
222 iwe.u.data.length = p - custom;
223 if (iwe.u.data.length)
224 start = iwe_stream_add_point(start, stop, &iwe, custom);
225
b453872c
JG
226 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
0edd5b44
JG
250 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));
b453872c
JG
258 }
259
260 spin_unlock_irqrestore(&ieee->lock, flags);
261
0edd5b44 262 wrqu->data.length = ev - extra;
b453872c
JG
263 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;
0edd5b44
JG
314 ieee80211_crypt_delayed_deinit(ieee,
315 &ieee->crypt[i]);
b453872c
JG
316 }
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
b453872c
JG
328 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))
354 new_crypt->priv = new_crypt->ops->init(key);
355
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: "
0edd5b44 361 "load module ieee80211_crypt_wep\n", dev->name);
b453872c
JG
362 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;
0edd5b44 378 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
b453872c
JG
379 (*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) {
0edd5b44
JG
401 IEEE80211_DEBUG_WX
402 ("Setting key %d to default Tx key.\n", key);
b453872c
JG
403 ieee->tx_keyidx = key;
404 sec.active_key = key;
405 sec.flags |= SEC_ACTIVE_KEY;
406 }
407 }
408
0edd5b44 409 done:
b453872c
JG
410 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;
0edd5b44 419 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
b453872c
JG
420
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);
This page took 0.057824 seconds and 5 git commands to generate.