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", ieee80211_modes[network->mode]);
76 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
79 iwe.cmd = SIOCGIWMODE;
80 if (network->capability &
81 (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,
91 /* Add frequency/channel */
92 iwe.cmd = SIOCGIWFREQ;
93 /* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
95 iwe.u.freq.m = network->channel;
98 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
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;
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);
109 /* Add basic and extended rates */
112 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
113 for (i = 0, j = 0; i < network->rates_len; ) {
114 if (j < network->rates_ex_len &&
115 ((network->rates_ex[j] & 0x7F) <
116 (network->rates[i] & 0x7F)))
117 rate = network->rates_ex[j++] & 0x7F;
119 rate = network->rates[i++] & 0x7F;
122 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
123 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
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" : "");
133 iwe.cmd = SIOCGIWRATE;
134 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
135 iwe.u.bitrate.value = max_rate * 500000;
136 start = iwe_stream_add_event(start, stop, &iwe,
139 iwe.cmd = IWEVCUSTOM;
140 iwe.u.data.length = p - custom;
141 if (iwe.u.data.length)
142 start = iwe_stream_add_point(start, stop, &iwe, custom);
144 /* Add quality statistics */
145 /* TODO: Fix these values... */
147 iwe.u.qual.qual = network->stats.signal;
148 iwe.u.qual.level = network->stats.rssi;
149 iwe.u.qual.noise = network->stats.noise;
150 iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
151 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
152 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
153 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
154 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
155 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
156 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
158 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
160 iwe.cmd = IWEVCUSTOM;
163 iwe.u.data.length = p - custom;
164 if (iwe.u.data.length)
165 start = iwe_stream_add_point(start, stop, &iwe, custom);
167 if (ieee->wpa_enabled && network->wpa_ie_len){
168 char buf[MAX_WPA_IE_LEN * 2 + 30];
171 p += sprintf(p, "wpa_ie=");
172 for (i = 0; i < network->wpa_ie_len; i++) {
173 p += sprintf(p, "%02x", network->wpa_ie[i]);
176 memset(&iwe, 0, sizeof(iwe));
177 iwe.cmd = IWEVCUSTOM;
178 iwe.u.data.length = strlen(buf);
179 start = iwe_stream_add_point(start, stop, &iwe, buf);
182 if (ieee->wpa_enabled && network->rsn_ie_len){
183 char buf[MAX_WPA_IE_LEN * 2 + 30];
186 p += sprintf(p, "rsn_ie=");
187 for (i = 0; i < network->rsn_ie_len; i++) {
188 p += sprintf(p, "%02x", network->rsn_ie[i]);
191 memset(&iwe, 0, sizeof(iwe));
192 iwe.cmd = IWEVCUSTOM;
193 iwe.u.data.length = strlen(buf);
194 start = iwe_stream_add_point(start, stop, &iwe, buf);
197 /* Add EXTRA: Age to display seconds since last beacon/probe response
198 * for given network. */
199 iwe.cmd = IWEVCUSTOM;
201 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
202 " Last beacon: %lums ago", (jiffies - network->last_scanned) / (HZ / 100));
203 iwe.u.data.length = p - custom;
204 if (iwe.u.data.length)
205 start = iwe_stream_add_point(start, stop, &iwe, custom);
211 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
212 struct iw_request_info *info,
213 union iwreq_data *wrqu, char *extra)
215 struct ieee80211_network *network;
219 char *stop = ev + IW_SCAN_MAX_DATA;
222 IEEE80211_DEBUG_WX("Getting scan\n");
224 spin_lock_irqsave(&ieee->lock, flags);
226 list_for_each_entry(network, &ieee->network_list, list) {
228 if (ieee->scan_age == 0 ||
229 time_after(network->last_scanned + ieee->scan_age, jiffies))
230 ev = ipw2100_translate_scan(ieee, ev, stop, network);
232 IEEE80211_DEBUG_SCAN(
233 "Not showing network '%s ("
234 MAC_FMT ")' due to age (%lums).\n",
235 escape_essid(network->ssid,
237 MAC_ARG(network->bssid),
238 (jiffies - network->last_scanned) / (HZ / 100));
241 spin_unlock_irqrestore(&ieee->lock, flags);
243 wrqu->data.length = ev - extra;
244 wrqu->data.flags = 0;
246 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
251 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
252 struct iw_request_info *info,
253 union iwreq_data *wrqu, char *keybuf)
255 struct iw_point *erq = &(wrqu->encoding);
256 struct net_device *dev = ieee->dev;
257 struct ieee80211_security sec = {
260 int i, key, key_provided, len;
261 struct ieee80211_crypt_data **crypt;
263 IEEE80211_DEBUG_WX("SET_ENCODE\n");
265 key = erq->flags & IW_ENCODE_INDEX;
273 key = ieee->tx_keyidx;
276 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
277 "provided" : "default");
279 crypt = &ieee->crypt[key];
281 if (erq->flags & IW_ENCODE_DISABLED) {
282 if (key_provided && *crypt) {
283 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
285 ieee80211_crypt_delayed_deinit(ieee, crypt);
287 IEEE80211_DEBUG_WX("Disabling encryption.\n");
289 /* Check all the keys to see if any are still configured,
290 * and if no key index was provided, de-init them all */
291 for (i = 0; i < WEP_KEYS; i++) {
292 if (ieee->crypt[i] != NULL) {
295 ieee80211_crypt_delayed_deinit(
296 ieee, &ieee->crypt[i]);
302 sec.level = SEC_LEVEL_0;
303 sec.flags |= SEC_ENABLED | SEC_LEVEL;
312 sec.flags |= SEC_ENABLED;
314 if (*crypt != NULL && (*crypt)->ops != NULL &&
315 strcmp((*crypt)->ops->name, "WEP") != 0) {
316 /* changing to use WEP; deinit previously used algorithm
318 ieee80211_crypt_delayed_deinit(ieee, crypt);
321 if (*crypt == NULL) {
322 struct ieee80211_crypt_data *new_crypt;
324 /* take WEP into use */
325 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
327 if (new_crypt == NULL)
329 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
330 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
331 if (!new_crypt->ops) {
332 request_module("ieee80211_crypt_wep");
333 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
336 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
337 new_crypt->priv = new_crypt->ops->init(key);
339 if (!new_crypt->ops || !new_crypt->priv) {
343 printk(KERN_WARNING "%s: could not initialize WEP: "
344 "load module ieee80211_crypt_wep\n",
351 /* If a new key was provided, set it up */
352 if (erq->length > 0) {
353 len = erq->length <= 5 ? 5 : 13;
354 memcpy(sec.keys[key], keybuf, erq->length);
355 if (len > erq->length)
356 memset(sec.keys[key] + erq->length, 0,
358 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
359 key, escape_essid(sec.keys[key], len),
361 sec.key_sizes[key] = len;
362 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
364 sec.flags |= (1 << key);
365 /* This ensures a key will be activated if no key is
367 if (key == sec.active_key)
368 sec.flags |= SEC_ACTIVE_KEY;
370 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
371 NULL, (*crypt)->priv);
373 /* Set a default key of all 0 */
374 IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
376 memset(sec.keys[key], 0, 13);
377 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
379 sec.key_sizes[key] = 13;
380 sec.flags |= (1 << key);
383 /* No key data - just set the default TX key index */
386 "Setting key %d to default Tx key.\n", key);
387 ieee->tx_keyidx = key;
388 sec.active_key = key;
389 sec.flags |= SEC_ACTIVE_KEY;
394 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
395 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
396 sec.flags |= SEC_AUTH_MODE;
397 IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
398 "OPEN" : "SHARED KEY");
400 /* For now we just support WEP, so only set that security level...
401 * TODO: When WPA is added this is one place that needs to change */
402 sec.flags |= SEC_LEVEL;
403 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
405 if (ieee->set_security)
406 ieee->set_security(dev, &sec);
408 /* Do not reset port if card is in Managed mode since resetting will
409 * generate new IEEE 802.11 authentication which may end up in looping
410 * with IEEE 802.1X. If your hardware requires a reset after WEP
411 * configuration (for example... Prism2), implement the reset_port in
412 * the callbacks structures used to initialize the 802.11 stack. */
413 if (ieee->reset_on_keychange &&
414 ieee->iw_mode != IW_MODE_INFRA &&
415 ieee->reset_port && ieee->reset_port(dev)) {
416 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
422 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
423 struct iw_request_info *info,
424 union iwreq_data *wrqu, char *keybuf)
426 struct iw_point *erq = &(wrqu->encoding);
428 struct ieee80211_crypt_data *crypt;
430 IEEE80211_DEBUG_WX("GET_ENCODE\n");
432 key = erq->flags & IW_ENCODE_INDEX;
438 key = ieee->tx_keyidx;
440 crypt = ieee->crypt[key];
441 erq->flags = key + 1;
443 if (crypt == NULL || crypt->ops == NULL) {
445 erq->flags |= IW_ENCODE_DISABLED;
449 if (strcmp(crypt->ops->name, "WEP") != 0) {
450 /* only WEP is supported with wireless extensions, so just
451 * report that encryption is used */
453 erq->flags |= IW_ENCODE_ENABLED;
457 len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
458 erq->length = (len >= 0 ? len : 0);
460 erq->flags |= IW_ENCODE_ENABLED;
463 erq->flags |= IW_ENCODE_OPEN;
465 erq->flags |= IW_ENCODE_RESTRICTED;
470 EXPORT_SYMBOL(ieee80211_wx_get_scan);
471 EXPORT_SYMBOL(ieee80211_wx_set_encode);
472 EXPORT_SYMBOL(ieee80211_wx_get_encode);