]> git.karo-electronics.de Git - mv-sheeva.git/blob - net/ieee80211/ieee80211_wx.c
[PATCH] ieee80211: Hardware crypto and fragmentation offload support
[mv-sheeva.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",
76                  ieee80211_modes[network->mode]);
77         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
78
79         /* Add mode */
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;
84                 else
85                         iwe.u.mode = IW_MODE_ADHOC;
86
87                 start = iwe_stream_add_event(start, stop, &iwe, 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, IW_EV_PARAM_LEN);
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 */
143         iwe.cmd = IWEVQUAL;
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)) {
170                 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
171                 iwe.u.qual.noise = 0;
172         } else {
173                 iwe.u.qual.noise = network->stats.noise;
174         }
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
185         if (network->wpa_ie_len) {
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
200         if (network->rsn_ie_len) {
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),
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);
225
226         return start;
227 }
228
229 int 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
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));
258         }
259
260         spin_unlock_irqrestore(&ieee->lock, flags);
261
262         wrqu->data.length = ev - extra;
263         wrqu->data.flags = 0;
264
265         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
266
267         return 0;
268 }
269
270 int 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         int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
282
283         IEEE80211_DEBUG_WX("SET_ENCODE\n");
284
285         key = erq->flags & IW_ENCODE_INDEX;
286         if (key) {
287                 if (key > WEP_KEYS)
288                         return -EINVAL;
289                 key--;
290                 key_provided = 1;
291         } else {
292                 key_provided = 0;
293                 key = ieee->tx_keyidx;
294         }
295
296         IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
297                            "provided" : "default");
298
299         crypt = &ieee->crypt[key];
300
301         if (erq->flags & IW_ENCODE_DISABLED) {
302                 if (key_provided && *crypt) {
303                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
304                                            key);
305                         ieee80211_crypt_delayed_deinit(ieee, crypt);
306                 } else
307                         IEEE80211_DEBUG_WX("Disabling encryption.\n");
308
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) {
313                                 if (key_provided)
314                                         break;
315                                 ieee80211_crypt_delayed_deinit(ieee,
316                                                                &ieee->crypt[i]);
317                         }
318                 }
319
320                 if (i == WEP_KEYS) {
321                         sec.enabled = 0;
322                         sec.encrypt = 0;
323                         sec.level = SEC_LEVEL_0;
324                         sec.flags |= SEC_ENABLED | SEC_LEVEL;
325                 }
326
327                 goto done;
328         }
329
330         sec.enabled = 1;
331         sec.encrypt = 1;
332         sec.flags |= SEC_ENABLED;
333
334         if (*crypt != NULL && (*crypt)->ops != NULL &&
335             strcmp((*crypt)->ops->name, "WEP") != 0) {
336                 /* changing to use WEP; deinit previously used algorithm
337                  * on this key */
338                 ieee80211_crypt_delayed_deinit(ieee, crypt);
339         }
340
341         if (*crypt == NULL && host_crypto) {
342                 struct ieee80211_crypt_data *new_crypt;
343
344                 /* take WEP into use */
345                 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
346                                     GFP_KERNEL);
347                 if (new_crypt == NULL)
348                         return -ENOMEM;
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");
354                 }
355
356                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
357                         new_crypt->priv = new_crypt->ops->init(ieee, key);
358
359                 if (!new_crypt->ops || !new_crypt->priv) {
360                         kfree(new_crypt);
361                         new_crypt = NULL;
362
363                         printk(KERN_WARNING "%s: could not initialize WEP: "
364                                "load module ieee80211_crypt_wep\n", dev->name);
365                         return -EOPNOTSUPP;
366                 }
367                 *crypt = new_crypt;
368         }
369
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,
376                                len - erq->length);
377                 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
378                                    key, escape_essid(sec.keys[key], len),
379                                    erq->length, len);
380                 sec.key_sizes[key] = len;
381                 if (*crypt)
382                         (*crypt)->ops->set_key(sec.keys[key], len, NULL,
383                                                (*crypt)->priv);
384                 sec.flags |= (1 << key);
385                 /* This ensures a key will be activated if no key is
386                  * explicitely set */
387                 if (key == sec.active_key)
388                         sec.flags |= SEC_ACTIVE_KEY;
389
390         } else {
391                 if (host_crypto) {
392                         len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
393                                                      NULL, (*crypt)->priv);
394                         if (len == 0) {
395                                 /* Set a default key of all 0 */
396                                 IEEE80211_DEBUG_WX("Setting key %d to all "
397                                                    "zero.\n", key);
398                                 memset(sec.keys[key], 0, 13);
399                                 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
400                                                        (*crypt)->priv);
401                                 sec.key_sizes[key] = 13;
402                                 sec.flags |= (1 << key);
403                         }
404                 }
405                 /* No key data - just set the default TX key index */
406                 if (key_provided) {
407                         IEEE80211_DEBUG_WX("Setting key %d to default Tx "
408                                            "key.\n", key);
409                         ieee->tx_keyidx = key;
410                         sec.active_key = key;
411                         sec.flags |= SEC_ACTIVE_KEY;
412                 }
413         }
414
415       done:
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");
421
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 */
426
427         if (ieee->set_security)
428                 ieee->set_security(dev, &sec);
429
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);
439                 return -EINVAL;
440         }
441         return 0;
442 }
443
444 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
445                             struct iw_request_info *info,
446                             union iwreq_data *wrqu, char *keybuf)
447 {
448         struct iw_point *erq = &(wrqu->encoding);
449         int len, key;
450         struct ieee80211_crypt_data *crypt;
451         struct ieee80211_security *sec = &ieee->sec;
452
453         IEEE80211_DEBUG_WX("GET_ENCODE\n");
454
455         key = erq->flags & IW_ENCODE_INDEX;
456         if (key) {
457                 if (key > WEP_KEYS)
458                         return -EINVAL;
459                 key--;
460         } else
461                 key = ieee->tx_keyidx;
462
463         crypt = ieee->crypt[key];
464         erq->flags = key + 1;
465
466         if (!sec->enabled) {
467                 erq->length = 0;
468                 erq->flags |= IW_ENCODE_DISABLED;
469                 return 0;
470         }
471
472         if (sec->level != SEC_LEVEL_1) {
473                 /* only WEP is supported with wireless extensions, so just
474                  * report that encryption is used */
475                 erq->length = 0;
476                 erq->flags |= IW_ENCODE_ENABLED;
477                 return 0;
478         }
479
480         len = sec->key_sizes[key];
481         memcpy(keybuf, sec->keys[key], len);
482
483         erq->length = (len >= 0 ? len : 0);
484         erq->flags |= IW_ENCODE_ENABLED;
485
486         if (ieee->open_wep)
487                 erq->flags |= IW_ENCODE_OPEN;
488         else
489                 erq->flags |= IW_ENCODE_RESTRICTED;
490
491         return 0;
492 }
493
494 EXPORT_SYMBOL(ieee80211_wx_get_scan);
495 EXPORT_SYMBOL(ieee80211_wx_set_encode);
496 EXPORT_SYMBOL(ieee80211_wx_get_encode);