1/*******************************************************************************
2
3  Copyright(c) 2004-2005 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  <j@w1.fi>
9  Copyright (c) 2002-2003, Jouni Malinen <j@w1.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>
34#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>
42#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>
47#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
54#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>"
58
59MODULE_VERSION(DRV_VERSION);
60MODULE_DESCRIPTION(DRV_DESCRIPTION);
61MODULE_AUTHOR(DRV_COPYRIGHT);
62MODULE_LICENSE("GPL");
63
64static int ieee80211_networks_allocate(struct ieee80211_device *ieee)
65{
66	if (ieee->networks)
67		return 0;
68
69	ieee->networks =
70	    kzalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network),
71		    GFP_KERNEL);
72	if (!ieee->networks) {
73		printk(KERN_WARNING "%s: Out of memory allocating beacons\n",
74		       ieee->dev->name);
75		return -ENOMEM;
76	}
77
78	return 0;
79}
80
81void 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
92static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
93{
94	int i;
95
96	if (!ieee->networks)
97		return;
98
99	for (i = 0; i < MAX_NETWORK_COUNT; i++)
100		if (ieee->networks[i].ibss_dfs)
101			kfree(ieee->networks[i].ibss_dfs);
102
103	kfree(ieee->networks);
104	ieee->networks = NULL;
105}
106
107static void ieee80211_networks_initialize(struct ieee80211_device *ieee)
108{
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++)
114		list_add_tail(&ieee->networks[i].list,
115			      &ieee->network_free_list);
116}
117
118static 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
126static struct net_device_stats *ieee80211_generic_get_stats(
127	struct net_device *dev)
128{
129	struct ieee80211_device *ieee = netdev_priv(dev);
130	return &ieee->stats;
131}
132
133struct net_device *alloc_ieee80211(int sizeof_priv)
134{
135	struct ieee80211_device *ieee;
136	struct net_device *dev;
137	int err;
138
139	IEEE80211_DEBUG_INFO("Initializing...\n");
140
141	dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
142	if (!dev) {
143		IEEE80211_ERROR("Unable to allocate network device.\n");
144		goto failed;
145	}
146	ieee = netdev_priv(dev);
147	dev->hard_start_xmit = ieee80211_xmit;
148	dev->change_mtu = ieee80211_change_mtu;
149
150	/* Drivers are free to override this if the generic implementation
151	 * does not meet their needs. */
152	dev->get_stats = ieee80211_generic_get_stats;
153
154	ieee->dev = dev;
155
156	err = ieee80211_networks_allocate(ieee);
157	if (err) {
158		IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err);
159		goto failed;
160	}
161	ieee80211_networks_initialize(ieee);
162
163	/* Default fragmentation threshold is maximum payload size */
164	ieee->fts = DEFAULT_FTS;
165	ieee->rts = DEFAULT_FTS;
166	ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
167	ieee->open_wep = 1;
168
169	/* Default to enabling full open WEP with host based encrypt/decrypt */
170	ieee->host_encrypt = 1;
171	ieee->host_decrypt = 1;
172	ieee->host_mc_decrypt = 1;
173
174	/* Host fragementation in Open mode. Default is enabled.
175	 * Note: host fragmentation is always enabled if host encryption
176	 * is enabled. For cards can do hardware encryption, they must do
177	 * hardware fragmentation as well. So we don't need a variable
178	 * like host_enc_frag. */
179	ieee->host_open_frag = 1;
180	ieee->ieee802_1x = 1;	/* Default to supporting 802.1x */
181
182	INIT_LIST_HEAD(&ieee->crypt_deinit_list);
183	init_timer(&ieee->crypt_deinit_timer);
184	ieee->crypt_deinit_timer.data = (unsigned long)ieee;
185	ieee->crypt_deinit_timer.function = ieee80211_crypt_deinit_handler;
186	ieee->crypt_quiesced = 0;
187
188	spin_lock_init(&ieee->lock);
189
190	ieee->wpa_enabled = 0;
191	ieee->drop_unencrypted = 0;
192	ieee->privacy_invoked = 0;
193
194	return dev;
195
196      failed:
197	if (dev)
198		free_netdev(dev);
199	return NULL;
200}
201
202void free_ieee80211(struct net_device *dev)
203{
204	struct ieee80211_device *ieee = netdev_priv(dev);
205
206	int i;
207
208	ieee80211_crypt_quiescing(ieee);
209	del_timer_sync(&ieee->crypt_deinit_timer);
210	ieee80211_crypt_deinit_entries(ieee, 1);
211
212	for (i = 0; i < WEP_KEYS; i++) {
213		struct ieee80211_crypt_data *crypt = ieee->crypt[i];
214		if (crypt) {
215			if (crypt->ops) {
216				crypt->ops->deinit(crypt->priv);
217				module_put(crypt->ops->owner);
218			}
219			kfree(crypt);
220			ieee->crypt[i] = NULL;
221		}
222	}
223
224	ieee80211_networks_free(ieee);
225	free_netdev(dev);
226}
227
228#ifdef CONFIG_IEEE80211_DEBUG
229
230static int debug = 0;
231u32 ieee80211_debug_level = 0;
232EXPORT_SYMBOL_GPL(ieee80211_debug_level);
233static struct proc_dir_entry *ieee80211_proc = NULL;
234
235static int show_debug_level(char *page, char **start, off_t offset,
236			    int count, int *eof, void *data)
237{
238	return snprintf(page, count, "0x%08X\n", ieee80211_debug_level);
239}
240
241static int store_debug_level(struct file *file, const char __user * buffer,
242			     unsigned long count, void *data)
243{
244	char buf[] = "0x00000000\n";
245	unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
246	unsigned long val;
247
248	if (copy_from_user(buf, buffer, len))
249		return count;
250	buf[len] = 0;
251	if (sscanf(buf, "%li", &val) != 1)
252		printk(KERN_INFO DRV_NAME
253		       ": %s is not in hex or decimal form.\n", buf);
254	else
255		ieee80211_debug_level = val;
256
257	return strnlen(buf, len);
258}
259#endif				/* CONFIG_IEEE80211_DEBUG */
260
261static int __init ieee80211_init(void)
262{
263#ifdef CONFIG_IEEE80211_DEBUG
264	struct proc_dir_entry *e;
265
266	ieee80211_debug_level = debug;
267	ieee80211_proc = proc_mkdir(DRV_NAME, proc_net);
268	if (ieee80211_proc == NULL) {
269		IEEE80211_ERROR("Unable to create " DRV_NAME
270				" proc directory\n");
271		return -EIO;
272	}
273	e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
274			      ieee80211_proc);
275	if (!e) {
276		remove_proc_entry(DRV_NAME, proc_net);
277		ieee80211_proc = NULL;
278		return -EIO;
279	}
280	e->read_proc = show_debug_level;
281	e->write_proc = store_debug_level;
282	e->data = NULL;
283#endif				/* CONFIG_IEEE80211_DEBUG */
284
285	printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
286	printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
287
288	return 0;
289}
290
291static void __exit ieee80211_exit(void)
292{
293#ifdef CONFIG_IEEE80211_DEBUG
294	if (ieee80211_proc) {
295		remove_proc_entry("debug_level", ieee80211_proc);
296		remove_proc_entry(DRV_NAME, proc_net);
297		ieee80211_proc = NULL;
298	}
299#endif				/* CONFIG_IEEE80211_DEBUG */
300}
301
302#ifdef CONFIG_IEEE80211_DEBUG
303#include <linux/moduleparam.h>
304module_param(debug, int, 0444);
305MODULE_PARM_DESC(debug, "debug output mask");
306#endif				/* CONFIG_IEEE80211_DEBUG */
307
308module_exit(ieee80211_exit);
309module_init(ieee80211_init);
310
311const char *escape_essid(const char *essid, u8 essid_len)
312{
313	static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
314	const char *s = essid;
315	char *d = escaped;
316
317	if (ieee80211_is_empty_essid(essid, essid_len)) {
318		memcpy(escaped, "<hidden>", sizeof("<hidden>"));
319		return escaped;
320	}
321
322	essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
323	while (essid_len--) {
324		if (*s == '\0') {
325			*d++ = '\\';
326			*d++ = '0';
327			s++;
328		} else {
329			*d++ = *s++;
330		}
331	}
332	*d = '\0';
333	return escaped;
334}
335
336EXPORT_SYMBOL(alloc_ieee80211);
337EXPORT_SYMBOL(free_ieee80211);
338EXPORT_SYMBOL(escape_essid);
339