Merge linux-2.6 into linux-acpi-2.6 test
[linux-2.6] / net / ieee80211 / ieee80211_wx.c
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 ******************************************************************************/
32 #include <linux/wireless.h>
33 #include <linux/version.h>
34 #include <linux/kmod.h>
35 #include <linux/module.h>
36
37 #include <net/ieee80211.h>
38 static const char *ieee80211_modes[] = {
39         "?", "a", "b", "ab", "g", "ag", "bg", "abg"
40 };
41
42 #define MAX_CUSTOM_LEN 64
43 static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
44                                            char *start, char *stop,
45                                            struct ieee80211_network *network)
46 {
47         char custom[MAX_CUSTOM_LEN];
48         char *p;
49         struct iw_event iwe;
50         int i, j;
51         u8 max_rate, rate;
52
53         /* First entry *MUST* be the AP MAC address */
54         iwe.cmd = SIOCGIWAP;
55         iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
56         memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
57         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
58
59         /* Remaining entries will be displayed in the order we provide them */
60
61         /* Add the ESSID */
62         iwe.cmd = SIOCGIWESSID;
63         iwe.u.data.flags = 1;
64         if (network->flags & NETWORK_EMPTY_ESSID) {
65                 iwe.u.data.length = sizeof("<hidden>");
66                 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
67         } else {
68                 iwe.u.data.length = min(network->ssid_len, (u8)32);
69                 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
70         }
71
72         /* Add the protocol name */
73         iwe.cmd = SIOCGIWNAME;
74         snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", ieee80211_modes[network->mode]);
75         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
76
77         /* Add mode */
78         iwe.cmd = SIOCGIWMODE;
79         if (network->capability &
80             (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
81                 if (network->capability & WLAN_CAPABILITY_ESS)
82                         iwe.u.mode = IW_MODE_MASTER;
83                 else
84                         iwe.u.mode = IW_MODE_ADHOC;
85
86                 start = iwe_stream_add_event(start, stop, &iwe,
87                                              IW_EV_UINT_LEN);
88         }
89
90         /* Add frequency/channel */
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): ");
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;
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;
135         start = iwe_stream_add_event(start, stop, &iwe,
136                                      IW_EV_PARAM_LEN);
137
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 */
144         /* TODO: Fix these values... */
145         iwe.cmd = IWEVQUAL;
146         iwe.u.qual.qual = network->stats.signal;
147         iwe.u.qual.level = network->stats.rssi;
148         iwe.u.qual.noise = network->stats.noise;
149         iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
150         if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
151                 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
152         if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
153                 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
154         if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
155                 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
156
157         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
158
159         iwe.cmd = IWEVCUSTOM;
160         p = custom;
161
162         iwe.u.data.length = p - custom;
163         if (iwe.u.data.length)
164                 start = iwe_stream_add_point(start, stop, &iwe, custom);
165
166         if (ieee->wpa_enabled && network->wpa_ie_len){
167                 char buf[MAX_WPA_IE_LEN * 2 + 30];
168
169                 u8 *p = buf;
170                 p += sprintf(p, "wpa_ie=");
171                 for (i = 0; i < network->wpa_ie_len; i++) {
172                         p += sprintf(p, "%02x", network->wpa_ie[i]);
173                 }
174
175                 memset(&iwe, 0, sizeof(iwe));
176                 iwe.cmd = IWEVCUSTOM;
177                 iwe.u.data.length = strlen(buf);
178                 start = iwe_stream_add_point(start, stop, &iwe, buf);
179         }
180
181         if (ieee->wpa_enabled && network->rsn_ie_len){
182                 char buf[MAX_WPA_IE_LEN * 2 + 30];
183
184                 u8 *p = buf;
185                 p += sprintf(p, "rsn_ie=");
186                 for (i = 0; i < network->rsn_ie_len; i++) {
187                         p += sprintf(p, "%02x", network->rsn_ie[i]);
188                 }
189
190                 memset(&iwe, 0, sizeof(iwe));
191                 iwe.cmd = IWEVCUSTOM;
192                 iwe.u.data.length = strlen(buf);
193                 start = iwe_stream_add_point(start, stop, &iwe, buf);
194         }
195
196         /* Add EXTRA: Age to display seconds since last beacon/probe response
197          * for given network. */
198         iwe.cmd = IWEVCUSTOM;
199         p = custom;
200         p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
201                       " Last beacon: %lums ago", (jiffies - network->last_scanned) / (HZ / 100));
202         iwe.u.data.length = p - custom;
203         if (iwe.u.data.length)
204                 start = iwe_stream_add_point(start, stop, &iwe, custom);
205
206
207         return start;
208 }
209
210 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
211                           struct iw_request_info *info,
212                           union iwreq_data *wrqu, char *extra)
213 {
214         struct ieee80211_network *network;
215         unsigned long flags;
216
217         char *ev = extra;
218         char *stop = ev + IW_SCAN_MAX_DATA;
219         int i = 0;
220
221         IEEE80211_DEBUG_WX("Getting scan\n");
222
223         spin_lock_irqsave(&ieee->lock, flags);
224
225         list_for_each_entry(network, &ieee->network_list, list) {
226                 i++;
227                 if (ieee->scan_age == 0 ||
228                     time_after(network->last_scanned + ieee->scan_age, jiffies))
229                         ev = ipw2100_translate_scan(ieee, ev, stop, network);
230                 else
231                         IEEE80211_DEBUG_SCAN(
232                                 "Not showing network '%s ("
233                                 MAC_FMT ")' due to age (%lums).\n",
234                                 escape_essid(network->ssid,
235                                              network->ssid_len),
236                                 MAC_ARG(network->bssid),
237                                 (jiffies - network->last_scanned) / (HZ / 100));
238         }
239
240         spin_unlock_irqrestore(&ieee->lock, flags);
241
242         wrqu->data.length = ev -  extra;
243         wrqu->data.flags = 0;
244
245         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
246
247         return 0;
248 }
249
250 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
251                             struct iw_request_info *info,
252                             union iwreq_data *wrqu, char *keybuf)
253 {
254         struct iw_point *erq = &(wrqu->encoding);
255         struct net_device *dev = ieee->dev;
256         struct ieee80211_security sec = {
257                 .flags = 0
258         };
259         int i, key, key_provided, len;
260         struct ieee80211_crypt_data **crypt;
261
262         IEEE80211_DEBUG_WX("SET_ENCODE\n");
263
264         key = erq->flags & IW_ENCODE_INDEX;
265         if (key) {
266                 if (key > WEP_KEYS)
267                         return -EINVAL;
268                 key--;
269                 key_provided = 1;
270         } else {
271                 key_provided = 0;
272                 key = ieee->tx_keyidx;
273         }
274
275         IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
276                            "provided" : "default");
277
278         crypt = &ieee->crypt[key];
279
280         if (erq->flags & IW_ENCODE_DISABLED) {
281                 if (key_provided && *crypt) {
282                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
283                                            key);
284                         ieee80211_crypt_delayed_deinit(ieee, crypt);
285                 } else
286                         IEEE80211_DEBUG_WX("Disabling encryption.\n");
287
288                 /* Check all the keys to see if any are still configured,
289                  * and if no key index was provided, de-init them all */
290                 for (i = 0; i < WEP_KEYS; i++) {
291                         if (ieee->crypt[i] != NULL) {
292                                 if (key_provided)
293                                         break;
294                                 ieee80211_crypt_delayed_deinit(
295                                         ieee, &ieee->crypt[i]);
296                         }
297                 }
298
299                 if (i == WEP_KEYS) {
300                         sec.enabled = 0;
301                         sec.level = SEC_LEVEL_0;
302                         sec.flags |= SEC_ENABLED | SEC_LEVEL;
303                 }
304
305                 goto done;
306         }
307
308
309
310         sec.enabled = 1;
311         sec.flags |= SEC_ENABLED;
312
313         if (*crypt != NULL && (*crypt)->ops != NULL &&
314             strcmp((*crypt)->ops->name, "WEP") != 0) {
315                 /* changing to use WEP; deinit previously used algorithm
316                  * on this key */
317                 ieee80211_crypt_delayed_deinit(ieee, crypt);
318         }
319
320         if (*crypt == NULL) {
321                 struct ieee80211_crypt_data *new_crypt;
322
323                 /* take WEP into use */
324                 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
325                                     GFP_KERNEL);
326                 if (new_crypt == NULL)
327                         return -ENOMEM;
328                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
329                 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
330                 if (!new_crypt->ops) {
331                         request_module("ieee80211_crypt_wep");
332                         new_crypt->ops = ieee80211_get_crypto_ops("WEP");
333                 }
334
335                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
336                         new_crypt->priv = new_crypt->ops->init(key);
337
338                 if (!new_crypt->ops || !new_crypt->priv) {
339                         kfree(new_crypt);
340                         new_crypt = NULL;
341
342                         printk(KERN_WARNING "%s: could not initialize WEP: "
343                                "load module ieee80211_crypt_wep\n",
344                                dev->name);
345                         return -EOPNOTSUPP;
346                 }
347                 *crypt = new_crypt;
348         }
349
350         /* If a new key was provided, set it up */
351         if (erq->length > 0) {
352                 len = erq->length <= 5 ? 5 : 13;
353                 memcpy(sec.keys[key], keybuf, erq->length);
354                 if (len > erq->length)
355                         memset(sec.keys[key] + erq->length, 0,
356                                len - erq->length);
357                 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
358                                    key, escape_essid(sec.keys[key], len),
359                                    erq->length, len);
360                 sec.key_sizes[key] = len;
361                 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
362                                        (*crypt)->priv);
363                 sec.flags |= (1 << key);
364                 /* This ensures a key will be activated if no key is
365                  * explicitely set */
366                 if (key == sec.active_key)
367                         sec.flags |= SEC_ACTIVE_KEY;
368         } else {
369                 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
370                                              NULL, (*crypt)->priv);
371                 if (len == 0) {
372                         /* Set a default key of all 0 */
373                         IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
374                                            key);
375                         memset(sec.keys[key], 0, 13);
376                         (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
377                                                (*crypt)->priv);
378                         sec.key_sizes[key] = 13;
379                         sec.flags |= (1 << key);
380                 }
381
382                 /* No key data - just set the default TX key index */
383                 if (key_provided) {
384                         IEEE80211_DEBUG_WX(
385                                 "Setting key %d to default Tx key.\n", key);
386                         ieee->tx_keyidx = key;
387                         sec.active_key = key;
388                         sec.flags |= SEC_ACTIVE_KEY;
389                 }
390         }
391
392  done:
393         ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
394         sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
395         sec.flags |= SEC_AUTH_MODE;
396         IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
397                            "OPEN" : "SHARED KEY");
398
399         /* For now we just support WEP, so only set that security level...
400          * TODO: When WPA is added this is one place that needs to change */
401         sec.flags |= SEC_LEVEL;
402         sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
403
404         if (ieee->set_security)
405                 ieee->set_security(dev, &sec);
406
407         /* Do not reset port if card is in Managed mode since resetting will
408          * generate new IEEE 802.11 authentication which may end up in looping
409          * with IEEE 802.1X.  If your hardware requires a reset after WEP
410          * configuration (for example... Prism2), implement the reset_port in
411          * the callbacks structures used to initialize the 802.11 stack. */
412         if (ieee->reset_on_keychange &&
413             ieee->iw_mode != IW_MODE_INFRA &&
414             ieee->reset_port && ieee->reset_port(dev)) {
415                 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
416                 return -EINVAL;
417         }
418         return 0;
419 }
420
421 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
422                             struct iw_request_info *info,
423                             union iwreq_data *wrqu, char *keybuf)
424 {
425         struct iw_point *erq = &(wrqu->encoding);
426         int len, key;
427         struct ieee80211_crypt_data *crypt;
428
429         IEEE80211_DEBUG_WX("GET_ENCODE\n");
430
431         key = erq->flags & IW_ENCODE_INDEX;
432         if (key) {
433                 if (key > WEP_KEYS)
434                         return -EINVAL;
435                 key--;
436         } else
437                 key = ieee->tx_keyidx;
438
439         crypt = ieee->crypt[key];
440         erq->flags = key + 1;
441
442         if (crypt == NULL || crypt->ops == NULL) {
443                 erq->length = 0;
444                 erq->flags |= IW_ENCODE_DISABLED;
445                 return 0;
446         }
447
448         if (strcmp(crypt->ops->name, "WEP") != 0) {
449                 /* only WEP is supported with wireless extensions, so just
450                  * report that encryption is used */
451                 erq->length = 0;
452                 erq->flags |= IW_ENCODE_ENABLED;
453                 return 0;
454         }
455
456         len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
457         erq->length = (len >= 0 ? len : 0);
458
459         erq->flags |= IW_ENCODE_ENABLED;
460
461         if (ieee->open_wep)
462                 erq->flags |= IW_ENCODE_OPEN;
463         else
464                 erq->flags |= IW_ENCODE_RESTRICTED;
465
466         return 0;
467 }
468
469 EXPORT_SYMBOL(ieee80211_wx_get_scan);
470 EXPORT_SYMBOL(ieee80211_wx_set_encode);
471 EXPORT_SYMBOL(ieee80211_wx_get_encode);