blob: 13b1e5fff7e4741de39c232ba35931ef84129b92 [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 =
70 kmalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network),
71 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
78 memset(ieee->networks, 0,
79 MAX_NETWORK_COUNT * sizeof(struct ieee80211_network));
80
81 return 0;
82}
83
Zhu Yi15f38592006-01-19 16:21:35 +080084void ieee80211_network_reset(struct ieee80211_network *network)
85{
86 if (!network)
87 return;
88
89 if (network->ibss_dfs) {
90 kfree(network->ibss_dfs);
91 network->ibss_dfs = NULL;
92 }
93}
94
Jeff Garzikb4538722005-05-12 22:48:20 -040095static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
96{
Zhu Yi15f38592006-01-19 16:21:35 +080097 int i;
98
Jeff Garzikb4538722005-05-12 22:48:20 -040099 if (!ieee->networks)
100 return;
Zhu Yi15f38592006-01-19 16:21:35 +0800101
102 for (i = 0; i < MAX_NETWORK_COUNT; i++)
103 if (ieee->networks[i].ibss_dfs)
104 kfree(ieee->networks[i].ibss_dfs);
105
Jeff Garzikb4538722005-05-12 22:48:20 -0400106 kfree(ieee->networks);
107 ieee->networks = NULL;
108}
109
Arjan van de Ven858119e2006-01-14 13:20:43 -0800110static void ieee80211_networks_initialize(struct ieee80211_device *ieee)
Jeff Garzikb4538722005-05-12 22:48:20 -0400111{
112 int i;
113
114 INIT_LIST_HEAD(&ieee->network_free_list);
115 INIT_LIST_HEAD(&ieee->network_list);
116 for (i = 0; i < MAX_NETWORK_COUNT; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400117 list_add_tail(&ieee->networks[i].list,
118 &ieee->network_free_list);
Jeff Garzikb4538722005-05-12 22:48:20 -0400119}
120
Jeff Garzikb4538722005-05-12 22:48:20 -0400121struct net_device *alloc_ieee80211(int sizeof_priv)
122{
123 struct ieee80211_device *ieee;
124 struct net_device *dev;
125 int err;
126
127 IEEE80211_DEBUG_INFO("Initializing...\n");
128
129 dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
130 if (!dev) {
131 IEEE80211_ERROR("Unable to network device.\n");
132 goto failed;
133 }
134 ieee = netdev_priv(dev);
135 dev->hard_start_xmit = ieee80211_xmit;
136
137 ieee->dev = dev;
138
139 err = ieee80211_networks_allocate(ieee);
140 if (err) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400141 IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err);
Jeff Garzikb4538722005-05-12 22:48:20 -0400142 goto failed;
143 }
144 ieee80211_networks_initialize(ieee);
145
146 /* Default fragmentation threshold is maximum payload size */
147 ieee->fts = DEFAULT_FTS;
James Ketrenos3cdd00c2005-09-21 11:54:43 -0500148 ieee->rts = DEFAULT_FTS;
Jeff Garzikb4538722005-05-12 22:48:20 -0400149 ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
150 ieee->open_wep = 1;
151
152 /* Default to enabling full open WEP with host based encrypt/decrypt */
153 ieee->host_encrypt = 1;
154 ieee->host_decrypt = 1;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500155 ieee->host_mc_decrypt = 1;
156
James Ketrenos1264fc02005-09-21 11:54:53 -0500157 /* Host fragementation in Open mode. Default is enabled.
158 * Note: host fragmentation is always enabled if host encryption
159 * is enabled. For cards can do hardware encryption, they must do
160 * hardware fragmentation as well. So we don't need a variable
161 * like host_enc_frag. */
162 ieee->host_open_frag = 1;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400163 ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
Jeff Garzikb4538722005-05-12 22:48:20 -0400164
165 INIT_LIST_HEAD(&ieee->crypt_deinit_list);
166 init_timer(&ieee->crypt_deinit_timer);
167 ieee->crypt_deinit_timer.data = (unsigned long)ieee;
168 ieee->crypt_deinit_timer.function = ieee80211_crypt_deinit_handler;
James Ketrenos0ad0c3c2005-09-21 11:54:15 -0500169 ieee->crypt_quiesced = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400170
171 spin_lock_init(&ieee->lock);
172
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400173 ieee->wpa_enabled = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400174 ieee->drop_unencrypted = 0;
175 ieee->privacy_invoked = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400176
177 return dev;
178
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400179 failed:
Jeff Garzikb4538722005-05-12 22:48:20 -0400180 if (dev)
181 free_netdev(dev);
182 return NULL;
183}
184
Jeff Garzikb4538722005-05-12 22:48:20 -0400185void free_ieee80211(struct net_device *dev)
186{
187 struct ieee80211_device *ieee = netdev_priv(dev);
188
189 int i;
190
James Ketrenos0ad0c3c2005-09-21 11:54:15 -0500191 ieee80211_crypt_quiescing(ieee);
Jeff Garzikb4538722005-05-12 22:48:20 -0400192 del_timer_sync(&ieee->crypt_deinit_timer);
193 ieee80211_crypt_deinit_entries(ieee, 1);
194
195 for (i = 0; i < WEP_KEYS; i++) {
196 struct ieee80211_crypt_data *crypt = ieee->crypt[i];
197 if (crypt) {
198 if (crypt->ops) {
199 crypt->ops->deinit(crypt->priv);
200 module_put(crypt->ops->owner);
201 }
202 kfree(crypt);
203 ieee->crypt[i] = NULL;
204 }
205 }
206
207 ieee80211_networks_free(ieee);
208 free_netdev(dev);
209}
210
211#ifdef CONFIG_IEEE80211_DEBUG
212
213static int debug = 0;
214u32 ieee80211_debug_level = 0;
Johannes Bergb7cffb02006-01-04 20:58:10 +0100215static struct proc_dir_entry *ieee80211_proc = NULL;
Jeff Garzikb4538722005-05-12 22:48:20 -0400216
217static int show_debug_level(char *page, char **start, off_t offset,
218 int count, int *eof, void *data)
219{
220 return snprintf(page, count, "0x%08X\n", ieee80211_debug_level);
221}
222
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400223static int store_debug_level(struct file *file, const char __user * buffer,
Jeff Garzikb4538722005-05-12 22:48:20 -0400224 unsigned long count, void *data)
225{
James Ketrenos262d8e42005-09-13 17:42:53 -0500226 char buf[] = "0x00000000\n";
227 unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
Jeff Garzikb4538722005-05-12 22:48:20 -0400228 unsigned long val;
229
James Ketrenos262d8e42005-09-13 17:42:53 -0500230 if (copy_from_user(buf, buffer, len))
Jeff Garzikb4538722005-05-12 22:48:20 -0400231 return count;
James Ketrenos262d8e42005-09-13 17:42:53 -0500232 buf[len] = 0;
233 if (sscanf(buf, "%li", &val) != 1)
Jeff Garzikb4538722005-05-12 22:48:20 -0400234 printk(KERN_INFO DRV_NAME
235 ": %s is not in hex or decimal form.\n", buf);
236 else
237 ieee80211_debug_level = val;
238
James Ketrenos262d8e42005-09-13 17:42:53 -0500239 return strnlen(buf, len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400240}
James Ketrenos31696162005-09-21 11:58:46 -0500241#endif /* CONFIG_IEEE80211_DEBUG */
Jeff Garzikb4538722005-05-12 22:48:20 -0400242
243static int __init ieee80211_init(void)
244{
James Ketrenos31696162005-09-21 11:58:46 -0500245#ifdef CONFIG_IEEE80211_DEBUG
Jeff Garzikb4538722005-05-12 22:48:20 -0400246 struct proc_dir_entry *e;
247
248 ieee80211_debug_level = debug;
Al Viro66600222005-09-28 22:32:57 +0100249 ieee80211_proc = proc_mkdir(DRV_NAME, proc_net);
Jeff Garzikb4538722005-05-12 22:48:20 -0400250 if (ieee80211_proc == NULL) {
251 IEEE80211_ERROR("Unable to create " DRV_NAME
252 " proc directory\n");
253 return -EIO;
254 }
255 e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
256 ieee80211_proc);
257 if (!e) {
258 remove_proc_entry(DRV_NAME, proc_net);
259 ieee80211_proc = NULL;
260 return -EIO;
261 }
262 e->read_proc = show_debug_level;
263 e->write_proc = store_debug_level;
264 e->data = NULL;
James Ketrenos31696162005-09-21 11:58:46 -0500265#endif /* CONFIG_IEEE80211_DEBUG */
266
267 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
268 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400269
270 return 0;
271}
272
273static void __exit ieee80211_exit(void)
274{
James Ketrenos31696162005-09-21 11:58:46 -0500275#ifdef CONFIG_IEEE80211_DEBUG
Jeff Garzikb4538722005-05-12 22:48:20 -0400276 if (ieee80211_proc) {
277 remove_proc_entry("debug_level", ieee80211_proc);
278 remove_proc_entry(DRV_NAME, proc_net);
279 ieee80211_proc = NULL;
280 }
James Ketrenos31696162005-09-21 11:58:46 -0500281#endif /* CONFIG_IEEE80211_DEBUG */
Jeff Garzikb4538722005-05-12 22:48:20 -0400282}
283
James Ketrenos31696162005-09-21 11:58:46 -0500284#ifdef CONFIG_IEEE80211_DEBUG
Jeff Garzikb4538722005-05-12 22:48:20 -0400285#include <linux/moduleparam.h>
286module_param(debug, int, 0444);
287MODULE_PARM_DESC(debug, "debug output mask");
James Ketrenos31696162005-09-21 11:58:46 -0500288#endif /* CONFIG_IEEE80211_DEBUG */
Jeff Garzikb4538722005-05-12 22:48:20 -0400289
Jeff Garzikb4538722005-05-12 22:48:20 -0400290module_exit(ieee80211_exit);
291module_init(ieee80211_init);
Jeff Garzikb4538722005-05-12 22:48:20 -0400292
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400293const char *escape_essid(const char *essid, u8 essid_len)
294{
Jiri Bence88187e2005-08-25 20:00:53 -0400295 static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
296 const char *s = essid;
297 char *d = escaped;
298
299 if (ieee80211_is_empty_essid(essid, essid_len)) {
300 memcpy(escaped, "<hidden>", sizeof("<hidden>"));
301 return escaped;
302 }
303
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400304 essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
Jiri Bence88187e2005-08-25 20:00:53 -0400305 while (essid_len--) {
306 if (*s == '\0') {
307 *d++ = '\\';
308 *d++ = '0';
309 s++;
310 } else {
311 *d++ = *s++;
312 }
313 }
314 *d = '\0';
315 return escaped;
316}
317
Jeff Garzikb4538722005-05-12 22:48:20 -0400318EXPORT_SYMBOL(alloc_ieee80211);
319EXPORT_SYMBOL(free_ieee80211);
Jiri Bence88187e2005-08-25 20:00:53 -0400320EXPORT_SYMBOL(escape_essid);