1 /******************************************************************************
3 Copyright(c) 2004 Intel Corporation. All rights reserved.
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
9 Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
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.
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
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.
24 The full GNU General Public License is included in this distribution in the
28 James P. Ketrenos <ipw2100-admin@linux.intel.com>
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
31 ******************************************************************************/
33 #include <linux/kmod.h>
34 #include <linux/module.h>
36 #include <net/ieee80211.h>
37 #include <linux/wireless.h>
39 static const char *ieee80211_modes[] = {
40 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
43 #define MAX_CUSTOM_LEN 64
44 static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
45 char *start, char *stop,
46 struct ieee80211_network *network)
48 char custom[MAX_CUSTOM_LEN];
54 /* First entry *MUST* be the AP MAC address */
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);
60 /* Remaining entries will be displayed in the order we provide them */
63 iwe.cmd = SIOCGIWESSID;
65 if (network->flags & NETWORK_EMPTY_ESSID) {
66 iwe.u.data.length = sizeof("<hidden>");
67 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
69 iwe.u.data.length = min(network->ssid_len, (u8) 32);
70 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
73 /* Add the protocol name */
74 iwe.cmd = SIOCGIWNAME;
75 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
76 ieee80211_modes[network->mode]);
77 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
80 iwe.cmd = SIOCGIWMODE;
81 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
82 if (network->capability & WLAN_CAPABILITY_ESS)
83 iwe.u.mode = IW_MODE_MASTER;
85 iwe.u.mode = IW_MODE_ADHOC;
87 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
90 /* Add frequency/channel */
91 iwe.cmd = SIOCGIWFREQ;
92 /* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
94 iwe.u.freq.m = network->channel;
97 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
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;
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);
108 /* Add basic and extended rates */
111 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
112 for (i = 0, j = 0; i < network->rates_len;) {
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;
118 rate = network->rates[i++] & 0x7F;
121 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
122 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
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" : "");
132 iwe.cmd = SIOCGIWRATE;
133 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
134 iwe.u.bitrate.value = max_rate * 500000;
135 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
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);
142 /* Add quality statistics */
144 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
145 IW_QUAL_NOISE_UPDATED;
147 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
148 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
149 IW_QUAL_LEVEL_INVALID;
151 iwe.u.qual.level = 0;
153 iwe.u.qual.level = network->stats.rssi;
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)
169 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
170 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
171 iwe.u.qual.noise = 0;
173 iwe.u.qual.noise = network->stats.noise;
176 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
178 iwe.cmd = IWEVCUSTOM;
181 iwe.u.data.length = p - custom;
182 if (iwe.u.data.length)
183 start = iwe_stream_add_point(start, stop, &iwe, custom);
185 if (network->wpa_ie_len) {
186 char buf[MAX_WPA_IE_LEN * 2 + 30];
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]);
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);
200 if (network->rsn_ie_len) {
201 char buf[MAX_WPA_IE_LEN * 2 + 30];
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]);
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);
215 /* Add EXTRA: Age to display seconds since last beacon/probe response
216 * for given network. */
217 iwe.cmd = IWEVCUSTOM;
219 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
220 " Last beacon: %lums ago",
221 (jiffies - network->last_scanned) / (HZ / 100));
222 iwe.u.data.length = p - custom;
223 if (iwe.u.data.length)
224 start = iwe_stream_add_point(start, stop, &iwe, custom);
229 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
230 struct iw_request_info *info,
231 union iwreq_data *wrqu, char *extra)
233 struct ieee80211_network *network;
237 char *stop = ev + IW_SCAN_MAX_DATA;
240 IEEE80211_DEBUG_WX("Getting scan\n");
242 spin_lock_irqsave(&ieee->lock, flags);
244 list_for_each_entry(network, &ieee->network_list, list) {
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);
250 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
251 MAC_FMT ")' due to age (%lums).\n",
252 escape_essid(network->ssid,
254 MAC_ARG(network->bssid),
256 network->last_scanned) / (HZ /
260 spin_unlock_irqrestore(&ieee->lock, flags);
262 wrqu->data.length = ev - extra;
263 wrqu->data.flags = 0;
265 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
270 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
271 struct iw_request_info *info,
272 union iwreq_data *wrqu, char *keybuf)
274 struct iw_point *erq = &(wrqu->encoding);
275 struct net_device *dev = ieee->dev;
276 struct ieee80211_security sec = {
279 int i, key, key_provided, len;
280 struct ieee80211_crypt_data **crypt;
281 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
283 IEEE80211_DEBUG_WX("SET_ENCODE\n");
285 key = erq->flags & IW_ENCODE_INDEX;
293 key = ieee->tx_keyidx;
296 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
297 "provided" : "default");
299 crypt = &ieee->crypt[key];
301 if (erq->flags & IW_ENCODE_DISABLED) {
302 if (key_provided && *crypt) {
303 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
305 ieee80211_crypt_delayed_deinit(ieee, crypt);
307 IEEE80211_DEBUG_WX("Disabling encryption.\n");
309 /* Check all the keys to see if any are still configured,
310 * and if no key index was provided, de-init them all */
311 for (i = 0; i < WEP_KEYS; i++) {
312 if (ieee->crypt[i] != NULL) {
315 ieee80211_crypt_delayed_deinit(ieee,
323 sec.level = SEC_LEVEL_0;
324 sec.flags |= SEC_ENABLED | SEC_LEVEL;
332 sec.flags |= SEC_ENABLED;
334 if (*crypt != NULL && (*crypt)->ops != NULL &&
335 strcmp((*crypt)->ops->name, "WEP") != 0) {
336 /* changing to use WEP; deinit previously used algorithm
338 ieee80211_crypt_delayed_deinit(ieee, crypt);
341 if (*crypt == NULL && host_crypto) {
342 struct ieee80211_crypt_data *new_crypt;
344 /* take WEP into use */
345 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
347 if (new_crypt == NULL)
349 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
350 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
351 if (!new_crypt->ops) {
352 request_module("ieee80211_crypt_wep");
353 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
356 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
357 new_crypt->priv = new_crypt->ops->init(ieee, key);
359 if (!new_crypt->ops || !new_crypt->priv) {
363 printk(KERN_WARNING "%s: could not initialize WEP: "
364 "load module ieee80211_crypt_wep\n", dev->name);
370 /* If a new key was provided, set it up */
371 if (erq->length > 0) {
372 len = erq->length <= 5 ? 5 : 13;
373 memcpy(sec.keys[key], keybuf, erq->length);
374 if (len > erq->length)
375 memset(sec.keys[key] + erq->length, 0,
377 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
378 key, escape_essid(sec.keys[key], len),
380 sec.key_sizes[key] = len;
382 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
384 sec.flags |= (1 << key);
385 /* This ensures a key will be activated if no key is
387 if (key == sec.active_key)
388 sec.flags |= SEC_ACTIVE_KEY;
392 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
393 NULL, (*crypt)->priv);
395 /* Set a default key of all 0 */
396 IEEE80211_DEBUG_WX("Setting key %d to all "
398 memset(sec.keys[key], 0, 13);
399 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
401 sec.key_sizes[key] = 13;
402 sec.flags |= (1 << key);
405 /* No key data - just set the default TX key index */
407 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
409 ieee->tx_keyidx = key;
410 sec.active_key = key;
411 sec.flags |= SEC_ACTIVE_KEY;
416 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
417 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
418 sec.flags |= SEC_AUTH_MODE;
419 IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
420 "OPEN" : "SHARED KEY");
422 /* For now we just support WEP, so only set that security level...
423 * TODO: When WPA is added this is one place that needs to change */
424 sec.flags |= SEC_LEVEL;
425 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
427 if (ieee->set_security)
428 ieee->set_security(dev, &sec);
430 /* Do not reset port if card is in Managed mode since resetting will
431 * generate new IEEE 802.11 authentication which may end up in looping
432 * with IEEE 802.1X. If your hardware requires a reset after WEP
433 * configuration (for example... Prism2), implement the reset_port in
434 * the callbacks structures used to initialize the 802.11 stack. */
435 if (ieee->reset_on_keychange &&
436 ieee->iw_mode != IW_MODE_INFRA &&
437 ieee->reset_port && ieee->reset_port(dev)) {
438 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
444 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
445 struct iw_request_info *info,
446 union iwreq_data *wrqu, char *keybuf)
448 struct iw_point *erq = &(wrqu->encoding);
450 struct ieee80211_crypt_data *crypt;
451 struct ieee80211_security *sec = &ieee->sec;
453 IEEE80211_DEBUG_WX("GET_ENCODE\n");
455 key = erq->flags & IW_ENCODE_INDEX;
461 key = ieee->tx_keyidx;
463 crypt = ieee->crypt[key];
464 erq->flags = key + 1;
468 erq->flags |= IW_ENCODE_DISABLED;
472 if (sec->level != SEC_LEVEL_1) {
473 /* only WEP is supported with wireless extensions, so just
474 * report that encryption is used */
476 erq->flags |= IW_ENCODE_ENABLED;
480 len = sec->key_sizes[key];
481 memcpy(keybuf, sec->keys[key], len);
483 erq->length = (len >= 0 ? len : 0);
484 erq->flags |= IW_ENCODE_ENABLED;
487 erq->flags |= IW_ENCODE_OPEN;
489 erq->flags |= IW_ENCODE_RESTRICTED;
494 EXPORT_SYMBOL(ieee80211_wx_get_scan);
495 EXPORT_SYMBOL(ieee80211_wx_set_encode);
496 EXPORT_SYMBOL(ieee80211_wx_get_encode);