510a1716a4f05e6cca080126df33dedc571f4a0f
[pandora-kernel.git] / 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
33 #include <linux/kmod.h>
34 #include <linux/module.h>
35
36 #include <net/ieee80211.h>
37 #include <linux/wireless.h>
38
39 static const char *ieee80211_modes[] = {
40         "?", "a", "b", "ab", "g", "ag", "bg", "abg"
41 };
42
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)
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 {
69                 iwe.u.data.length = min(network->ssid_len, (u8)32);
70                 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
71         }
72
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);
77
78         /* Add mode */
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;
84                 else
85                         iwe.u.mode = IW_MODE_ADHOC;
86
87                 start = iwe_stream_add_event(start, stop, &iwe,
88                                              IW_EV_UINT_LEN);
89         }
90
91         /* Add frequency/channel */
92         iwe.cmd = SIOCGIWFREQ;
93 /*      iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
94         iwe.u.freq.e = 3; */
95         iwe.u.freq.m = network->channel;
96         iwe.u.freq.e = 0;
97         iwe.u.freq.i = 0;
98         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
99
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;
104         else
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);
108
109         /* Add basic and extended rates */
110         max_rate = 0;
111         p = custom;
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;
118                 else
119                         rate = network->rates[i++] & 0x7F;
120                 if (rate > max_rate)
121                         max_rate = rate;
122                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
123                               "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
124         }
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" : "");
129                 if (rate > max_rate)
130                         max_rate = rate;
131         }
132
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,
137                                      IW_EV_PARAM_LEN);
138
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);
143
144         /* Add quality statistics */
145         /* TODO: Fix these values... */
146         iwe.cmd = IWEVQUAL;
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;
157
158         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
159
160         iwe.cmd = IWEVCUSTOM;
161         p = custom;
162
163         iwe.u.data.length = p - custom;
164         if (iwe.u.data.length)
165                 start = iwe_stream_add_point(start, stop, &iwe, custom);
166
167         if (ieee->wpa_enabled && network->wpa_ie_len){
168                 char buf[MAX_WPA_IE_LEN * 2 + 30];
169
170                 u8 *p = buf;
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]);
174                 }
175
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);
180         }
181
182         if (ieee->wpa_enabled && network->rsn_ie_len){
183                 char buf[MAX_WPA_IE_LEN * 2 + 30];
184
185                 u8 *p = buf;
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]);
189                 }
190
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);
195         }
196
197         /* Add EXTRA: Age to display seconds since last beacon/probe response
198          * for given network. */
199         iwe.cmd = IWEVCUSTOM;
200         p = custom;
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);
206
207
208         return start;
209 }
210
211 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
212                           struct iw_request_info *info,
213                           union iwreq_data *wrqu, char *extra)
214 {
215         struct ieee80211_network *network;
216         unsigned long flags;
217
218         char *ev = extra;
219         char *stop = ev + IW_SCAN_MAX_DATA;
220         int i = 0;
221
222         IEEE80211_DEBUG_WX("Getting scan\n");
223
224         spin_lock_irqsave(&ieee->lock, flags);
225
226         list_for_each_entry(network, &ieee->network_list, list) {
227                 i++;
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);
231                 else
232                         IEEE80211_DEBUG_SCAN(
233                                 "Not showing network '%s ("
234                                 MAC_FMT ")' due to age (%lums).\n",
235                                 escape_essid(network->ssid,
236                                              network->ssid_len),
237                                 MAC_ARG(network->bssid),
238                                 (jiffies - network->last_scanned) / (HZ / 100));
239         }
240
241         spin_unlock_irqrestore(&ieee->lock, flags);
242
243         wrqu->data.length = ev -  extra;
244         wrqu->data.flags = 0;
245
246         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
247
248         return 0;
249 }
250
251 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
252                             struct iw_request_info *info,
253                             union iwreq_data *wrqu, char *keybuf)
254 {
255         struct iw_point *erq = &(wrqu->encoding);
256         struct net_device *dev = ieee->dev;
257         struct ieee80211_security sec = {
258                 .flags = 0
259         };
260         int i, key, key_provided, len;
261         struct ieee80211_crypt_data **crypt;
262
263         IEEE80211_DEBUG_WX("SET_ENCODE\n");
264
265         key = erq->flags & IW_ENCODE_INDEX;
266         if (key) {
267                 if (key > WEP_KEYS)
268                         return -EINVAL;
269                 key--;
270                 key_provided = 1;
271         } else {
272                 key_provided = 0;
273                 key = ieee->tx_keyidx;
274         }
275
276         IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
277                            "provided" : "default");
278
279         crypt = &ieee->crypt[key];
280
281         if (erq->flags & IW_ENCODE_DISABLED) {
282                 if (key_provided && *crypt) {
283                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
284                                            key);
285                         ieee80211_crypt_delayed_deinit(ieee, crypt);
286                 } else
287                         IEEE80211_DEBUG_WX("Disabling encryption.\n");
288
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) {
293                                 if (key_provided)
294                                         break;
295                                 ieee80211_crypt_delayed_deinit(
296                                         ieee, &ieee->crypt[i]);
297                         }
298                 }
299
300                 if (i == WEP_KEYS) {
301                         sec.enabled = 0;
302                         sec.level = SEC_LEVEL_0;
303                         sec.flags |= SEC_ENABLED | SEC_LEVEL;
304                 }
305
306                 goto done;
307         }
308
309
310
311         sec.enabled = 1;
312         sec.flags |= SEC_ENABLED;
313
314         if (*crypt != NULL && (*crypt)->ops != NULL &&
315             strcmp((*crypt)->ops->name, "WEP") != 0) {
316                 /* changing to use WEP; deinit previously used algorithm
317                  * on this key */
318                 ieee80211_crypt_delayed_deinit(ieee, crypt);
319         }
320
321         if (*crypt == NULL) {
322                 struct ieee80211_crypt_data *new_crypt;
323
324                 /* take WEP into use */
325                 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
326                                     GFP_KERNEL);
327                 if (new_crypt == NULL)
328                         return -ENOMEM;
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");
334                 }
335
336                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
337                         new_crypt->priv = new_crypt->ops->init(key);
338
339                 if (!new_crypt->ops || !new_crypt->priv) {
340                         kfree(new_crypt);
341                         new_crypt = NULL;
342
343                         printk(KERN_WARNING "%s: could not initialize WEP: "
344                                "load module ieee80211_crypt_wep\n",
345                                dev->name);
346                         return -EOPNOTSUPP;
347                 }
348                 *crypt = new_crypt;
349         }
350
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,
357                                len - erq->length);
358                 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
359                                    key, escape_essid(sec.keys[key], len),
360                                    erq->length, len);
361                 sec.key_sizes[key] = len;
362                 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
363                                        (*crypt)->priv);
364                 sec.flags |= (1 << key);
365                 /* This ensures a key will be activated if no key is
366                  * explicitely set */
367                 if (key == sec.active_key)
368                         sec.flags |= SEC_ACTIVE_KEY;
369         } else {
370                 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
371                                              NULL, (*crypt)->priv);
372                 if (len == 0) {
373                         /* Set a default key of all 0 */
374                         IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
375                                            key);
376                         memset(sec.keys[key], 0, 13);
377                         (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
378                                                (*crypt)->priv);
379                         sec.key_sizes[key] = 13;
380                         sec.flags |= (1 << key);
381                 }
382
383                 /* No key data - just set the default TX key index */
384                 if (key_provided) {
385                         IEEE80211_DEBUG_WX(
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;
390                 }
391         }
392
393  done:
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");
399
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 */
404
405         if (ieee->set_security)
406                 ieee->set_security(dev, &sec);
407
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);
417                 return -EINVAL;
418         }
419         return 0;
420 }
421
422 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
423                             struct iw_request_info *info,
424                             union iwreq_data *wrqu, char *keybuf)
425 {
426         struct iw_point *erq = &(wrqu->encoding);
427         int len, key;
428         struct ieee80211_crypt_data *crypt;
429
430         IEEE80211_DEBUG_WX("GET_ENCODE\n");
431
432         key = erq->flags & IW_ENCODE_INDEX;
433         if (key) {
434                 if (key > WEP_KEYS)
435                         return -EINVAL;
436                 key--;
437         } else
438                 key = ieee->tx_keyidx;
439
440         crypt = ieee->crypt[key];
441         erq->flags = key + 1;
442
443         if (crypt == NULL || crypt->ops == NULL) {
444                 erq->length = 0;
445                 erq->flags |= IW_ENCODE_DISABLED;
446                 return 0;
447         }
448
449         if (strcmp(crypt->ops->name, "WEP") != 0) {
450                 /* only WEP is supported with wireless extensions, so just
451                  * report that encryption is used */
452                 erq->length = 0;
453                 erq->flags |= IW_ENCODE_ENABLED;
454                 return 0;
455         }
456
457         len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
458         erq->length = (len >= 0 ? len : 0);
459
460         erq->flags |= IW_ENCODE_ENABLED;
461
462         if (ieee->open_wep)
463                 erq->flags |= IW_ENCODE_OPEN;
464         else
465                 erq->flags |= IW_ENCODE_RESTRICTED;
466
467         return 0;
468 }
469
470 EXPORT_SYMBOL(ieee80211_wx_get_scan);
471 EXPORT_SYMBOL(ieee80211_wx_set_encode);
472 EXPORT_SYMBOL(ieee80211_wx_get_encode);