blob: f16e60ee9ce9834d58214ff475ebe8940d9486d2 [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/*******************************************************************************
2
James Ketrenosebeaddc2005-09-21 11:58:43 -05003 Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Jeff Garzikb4538722005-05-12 22:48:20 -04004
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/compiler.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040034#include <linux/errno.h>
35#include <linux/if_arp.h>
36#include <linux/in6.h>
37#include <linux/in.h>
38#include <linux/ip.h>
39#include <linux/kernel.h>
40#include <linux/module.h>
41#include <linux/netdevice.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040042#include <linux/proc_fs.h>
43#include <linux/skbuff.h>
44#include <linux/slab.h>
45#include <linux/tcp.h>
46#include <linux/types.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040047#include <linux/wireless.h>
48#include <linux/etherdevice.h>
49#include <asm/uaccess.h>
50#include <net/arp.h>
51
52#include <net/ieee80211.h>
53
James Ketrenos31696162005-09-21 11:58:46 -050054#define DRV_DESCRIPTION "802.11 data/management/control stack"
55#define DRV_NAME "ieee80211"
56#define DRV_VERSION IEEE80211_VERSION
57#define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>"
Jeff Garzikb4538722005-05-12 22:48:20 -040058
James Ketrenos31696162005-09-21 11:58:46 -050059MODULE_VERSION(DRV_VERSION);
60MODULE_DESCRIPTION(DRV_DESCRIPTION);
61MODULE_AUTHOR(DRV_COPYRIGHT);
62MODULE_LICENSE("GPL");
Jeff Garzikb4538722005-05-12 22:48:20 -040063
Arjan van de Ven858119e2006-01-14 13:20:43 -080064static int ieee80211_networks_allocate(struct ieee80211_device *ieee)
Jeff Garzikb4538722005-05-12 22:48:20 -040065{
66 if (ieee->networks)
67 return 0;
68
Jeff Garzik0edd5b42005-09-07 00:48:31 -040069 ieee->networks =
Arnaldo Carvalho de Melo571d6ee2006-11-21 01:26:49 -020070 kzalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network),
Jeff Garzik0edd5b42005-09-07 00:48:31 -040071 GFP_KERNEL);
Jeff Garzikb4538722005-05-12 22:48:20 -040072 if (!ieee->networks) {
73 printk(KERN_WARNING "%s: Out of memory allocating beacons\n",
74 ieee->dev->name);
75 return -ENOMEM;
76 }
77
Jeff Garzikb4538722005-05-12 22:48:20 -040078 return 0;
79}
80
Zhu Yi15f38592006-01-19 16:21:35 +080081void ieee80211_network_reset(struct ieee80211_network *network)
82{
83 if (!network)
84 return;
85
86 if (network->ibss_dfs) {
87 kfree(network->ibss_dfs);
88 network->ibss_dfs = NULL;
89 }
90}
91
Jeff Garzikb4538722005-05-12 22:48:20 -040092static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
93{
Zhu Yi15f38592006-01-19 16:21:35 +080094 int i;
95
Jeff Garzikb4538722005-05-12 22:48:20 -040096 if (!ieee->networks)
97 return;
Zhu Yi15f38592006-01-19 16:21:35 +080098
99 for (i = 0; i < MAX_NETWORK_COUNT; i++)
100 if (ieee->networks[i].ibss_dfs)
101 kfree(ieee->networks[i].ibss_dfs);
102
Jeff Garzikb4538722005-05-12 22:48:20 -0400103 kfree(ieee->networks);
104 ieee->networks = NULL;
105}
106
Arjan van de Ven858119e2006-01-14 13:20:43 -0800107static void ieee80211_networks_initialize(struct ieee80211_device *ieee)
Jeff Garzikb4538722005-05-12 22:48:20 -0400108{
109 int i;
110
111 INIT_LIST_HEAD(&ieee->network_free_list);
112 INIT_LIST_HEAD(&ieee->network_list);
113 for (i = 0; i < MAX_NETWORK_COUNT; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400114 list_add_tail(&ieee->networks[i].list,
115 &ieee->network_free_list);
Jeff Garzikb4538722005-05-12 22:48:20 -0400116}
117
matthieu castet42a4cf92006-09-28 19:57:25 +0200118static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
119{
120 if ((new_mtu < 68) || (new_mtu > IEEE80211_DATA_LEN))
121 return -EINVAL;
122 dev->mtu = new_mtu;
123 return 0;
124}
125
Jeff Garzikb4538722005-05-12 22:48:20 -0400126struct net_device *alloc_ieee80211(int sizeof_priv)
127{
128 struct ieee80211_device *ieee;
129 struct net_device *dev;
130 int err;
131
132 IEEE80211_DEBUG_INFO("Initializing...\n");
133
134 dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
135 if (!dev) {
136 IEEE80211_ERROR("Unable to network device.\n");
137 goto failed;
138 }
139 ieee = netdev_priv(dev);
140 dev->hard_start_xmit = ieee80211_xmit;
matthieu castet42a4cf92006-09-28 19:57:25 +0200141 dev->change_mtu = ieee80211_change_mtu;
Jeff Garzikb4538722005-05-12 22:48:20 -0400142
143 ieee->dev = dev;
144
145 err = ieee80211_networks_allocate(ieee);
146 if (err) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400147 IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err);
Jeff Garzikb4538722005-05-12 22:48:20 -0400148 goto failed;
149 }
150 ieee80211_networks_initialize(ieee);
151
152 /* Default fragmentation threshold is maximum payload size */
153 ieee->fts = DEFAULT_FTS;
James Ketrenos3cdd00c2005-09-21 11:54:43 -0500154 ieee->rts = DEFAULT_FTS;
Jeff Garzikb4538722005-05-12 22:48:20 -0400155 ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
156 ieee->open_wep = 1;
157
158 /* Default to enabling full open WEP with host based encrypt/decrypt */
159 ieee->host_encrypt = 1;
160 ieee->host_decrypt = 1;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500161 ieee->host_mc_decrypt = 1;
162
James Ketrenos1264fc02005-09-21 11:54:53 -0500163 /* Host fragementation in Open mode. Default is enabled.
164 * Note: host fragmentation is always enabled if host encryption
165 * is enabled. For cards can do hardware encryption, they must do
166 * hardware fragmentation as well. So we don't need a variable
167 * like host_enc_frag. */
168 ieee->host_open_frag = 1;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400169 ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
Jeff Garzikb4538722005-05-12 22:48:20 -0400170
171 INIT_LIST_HEAD(&ieee->crypt_deinit_list);
172 init_timer(&ieee->crypt_deinit_timer);
173 ieee->crypt_deinit_timer.data = (unsigned long)ieee;
174 ieee->crypt_deinit_timer.function = ieee80211_crypt_deinit_handler;
James Ketrenos0ad0c3c2005-09-21 11:54:15 -0500175 ieee->crypt_quiesced = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400176
177 spin_lock_init(&ieee->lock);
178
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400179 ieee->wpa_enabled = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400180 ieee->drop_unencrypted = 0;
181 ieee->privacy_invoked = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400182
183 return dev;
184
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400185 failed:
Jeff Garzikb4538722005-05-12 22:48:20 -0400186 if (dev)
187 free_netdev(dev);
188 return NULL;
189}
190
Jeff Garzikb4538722005-05-12 22:48:20 -0400191void free_ieee80211(struct net_device *dev)
192{
193 struct ieee80211_device *ieee = netdev_priv(dev);
194
195 int i;
196
James Ketrenos0ad0c3c2005-09-21 11:54:15 -0500197 ieee80211_crypt_quiescing(ieee);
Jeff Garzikb4538722005-05-12 22:48:20 -0400198 del_timer_sync(&ieee->crypt_deinit_timer);
199 ieee80211_crypt_deinit_entries(ieee, 1);
200
201 for (i = 0; i < WEP_KEYS; i++) {
202 struct ieee80211_crypt_data *crypt = ieee->crypt[i];
203 if (crypt) {
204 if (crypt->ops) {
205 crypt->ops->deinit(crypt->priv);
206 module_put(crypt->ops->owner);
207 }
208 kfree(crypt);
209 ieee->crypt[i] = NULL;
210 }
211 }
212
213 ieee80211_networks_free(ieee);
214 free_netdev(dev);
215}
216
217#ifdef CONFIG_IEEE80211_DEBUG
218
219static int debug = 0;
220u32 ieee80211_debug_level = 0;
Johannes Bergb7cffb02006-01-04 20:58:10 +0100221static struct proc_dir_entry *ieee80211_proc = NULL;
Jeff Garzikb4538722005-05-12 22:48:20 -0400222
223static int show_debug_level(char *page, char **start, off_t offset,
224 int count, int *eof, void *data)
225{
226 return snprintf(page, count, "0x%08X\n", ieee80211_debug_level);
227}
228
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400229static int store_debug_level(struct file *file, const char __user * buffer,
Jeff Garzikb4538722005-05-12 22:48:20 -0400230 unsigned long count, void *data)
231{
James Ketrenos262d8e42005-09-13 17:42:53 -0500232 char buf[] = "0x00000000\n";
233 unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
Jeff Garzikb4538722005-05-12 22:48:20 -0400234 unsigned long val;
235
James Ketrenos262d8e42005-09-13 17:42:53 -0500236 if (copy_from_user(buf, buffer, len))
Jeff Garzikb4538722005-05-12 22:48:20 -0400237 return count;
James Ketrenos262d8e42005-09-13 17:42:53 -0500238 buf[len] = 0;
239 if (sscanf(buf, "%li", &val) != 1)
Jeff Garzikb4538722005-05-12 22:48:20 -0400240 printk(KERN_INFO DRV_NAME
241 ": %s is not in hex or decimal form.\n", buf);
242 else
243 ieee80211_debug_level = val;
244
James Ketrenos262d8e42005-09-13 17:42:53 -0500245 return strnlen(buf, len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400246}
James Ketrenos31696162005-09-21 11:58:46 -0500247#endif /* CONFIG_IEEE80211_DEBUG */
Jeff Garzikb4538722005-05-12 22:48:20 -0400248
249static int __init ieee80211_init(void)
250{
James Ketrenos31696162005-09-21 11:58:46 -0500251#ifdef CONFIG_IEEE80211_DEBUG
Jeff Garzikb4538722005-05-12 22:48:20 -0400252 struct proc_dir_entry *e;
253
254 ieee80211_debug_level = debug;
Al Viro66600222005-09-28 22:32:57 +0100255 ieee80211_proc = proc_mkdir(DRV_NAME, proc_net);
Jeff Garzikb4538722005-05-12 22:48:20 -0400256 if (ieee80211_proc == NULL) {
257 IEEE80211_ERROR("Unable to create " DRV_NAME
258 " proc directory\n");
259 return -EIO;
260 }
261 e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
262 ieee80211_proc);
263 if (!e) {
264 remove_proc_entry(DRV_NAME, proc_net);
265 ieee80211_proc = NULL;
266 return -EIO;
267 }
268 e->read_proc = show_debug_level;
269 e->write_proc = store_debug_level;
270 e->data = NULL;
James Ketrenos31696162005-09-21 11:58:46 -0500271#endif /* CONFIG_IEEE80211_DEBUG */
272
273 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
274 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400275
276 return 0;
277}
278
279static void __exit ieee80211_exit(void)
280{
James Ketrenos31696162005-09-21 11:58:46 -0500281#ifdef CONFIG_IEEE80211_DEBUG
Jeff Garzikb4538722005-05-12 22:48:20 -0400282 if (ieee80211_proc) {
283 remove_proc_entry("debug_level", ieee80211_proc);
284 remove_proc_entry(DRV_NAME, proc_net);
285 ieee80211_proc = NULL;
286 }
James Ketrenos31696162005-09-21 11:58:46 -0500287#endif /* CONFIG_IEEE80211_DEBUG */
Jeff Garzikb4538722005-05-12 22:48:20 -0400288}
289
James Ketrenos31696162005-09-21 11:58:46 -0500290#ifdef CONFIG_IEEE80211_DEBUG
Jeff Garzikb4538722005-05-12 22:48:20 -0400291#include <linux/moduleparam.h>
292module_param(debug, int, 0444);
293MODULE_PARM_DESC(debug, "debug output mask");
James Ketrenos31696162005-09-21 11:58:46 -0500294#endif /* CONFIG_IEEE80211_DEBUG */
Jeff Garzikb4538722005-05-12 22:48:20 -0400295
Jeff Garzikb4538722005-05-12 22:48:20 -0400296module_exit(ieee80211_exit);
297module_init(ieee80211_init);
Jeff Garzikb4538722005-05-12 22:48:20 -0400298
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400299const char *escape_essid(const char *essid, u8 essid_len)
300{
Jiri Bence88187e2005-08-25 20:00:53 -0400301 static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
302 const char *s = essid;
303 char *d = escaped;
304
305 if (ieee80211_is_empty_essid(essid, essid_len)) {
306 memcpy(escaped, "<hidden>", sizeof("<hidden>"));
307 return escaped;
308 }
309
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400310 essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
Jiri Bence88187e2005-08-25 20:00:53 -0400311 while (essid_len--) {
312 if (*s == '\0') {
313 *d++ = '\\';
314 *d++ = '0';
315 s++;
316 } else {
317 *d++ = *s++;
318 }
319 }
320 *d = '\0';
321 return escaped;
322}
323
Jeff Garzikb4538722005-05-12 22:48:20 -0400324EXPORT_SYMBOL(alloc_ieee80211);
325EXPORT_SYMBOL(free_ieee80211);
Jiri Bence88187e2005-08-25 20:00:53 -0400326EXPORT_SYMBOL(escape_essid);