net/ieee80211 -> drivers/net/ipw2x00/libipw_* rename
The old ieee80211 code only remains as a support library for the ipw2100 and ipw2200 drivers. So, move the code and rename it appropriately to reflects it's true purpose and status. Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
@@ -8,7 +8,7 @@ config IPW2100
|
||||
select WIRELESS_EXT
|
||||
select FW_LOADER
|
||||
select LIB80211
|
||||
select IEEE80211
|
||||
select LIBIPW
|
||||
---help---
|
||||
A driver for the Intel PRO/Wireless 2100 Network
|
||||
Connection 802.11b wireless network adapter.
|
||||
@@ -67,7 +67,7 @@ config IPW2200
|
||||
select WIRELESS_EXT
|
||||
select FW_LOADER
|
||||
select LIB80211
|
||||
select IEEE80211
|
||||
select LIBIPW
|
||||
---help---
|
||||
A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
|
||||
Connection adapters.
|
||||
@@ -148,3 +148,44 @@ config IPW2200_DEBUG
|
||||
|
||||
If you are not sure, say N here.
|
||||
|
||||
config LIBIPW
|
||||
tristate
|
||||
select WIRELESS_EXT
|
||||
select CRYPTO
|
||||
select CRYPTO_ARC4
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_MICHAEL_MIC
|
||||
select CRYPTO_ECB
|
||||
select CRC32
|
||||
select LIB80211
|
||||
select LIB80211_CRYPT_WEP
|
||||
select LIB80211_CRYPT_TKIP
|
||||
select LIB80211_CRYPT_CCMP
|
||||
---help---
|
||||
This option enables the hardware independent IEEE 802.11
|
||||
networking stack. This component is deprecated in favor of the
|
||||
mac80211 component.
|
||||
|
||||
config LIBIPW_DEBUG
|
||||
bool "Full debugging output for the LIBIPW component"
|
||||
depends on LIBIPW
|
||||
---help---
|
||||
This option will enable debug tracing output for the
|
||||
libipw component.
|
||||
|
||||
This will result in the kernel module being ~70k larger. You
|
||||
can control which debug output is sent to the kernel log by
|
||||
setting the value in
|
||||
|
||||
/proc/net/ieee80211/debug_level
|
||||
|
||||
For example:
|
||||
|
||||
% echo 0x00000FFO > /proc/net/ieee80211/debug_level
|
||||
|
||||
For a list of values you can assign to debug_level, you
|
||||
can look at the bit mask values in <net/ieee80211.h>
|
||||
|
||||
If you are not trying to debug or develop the libipw
|
||||
component, you most likely want to say N here.
|
||||
|
@@ -4,3 +4,11 @@
|
||||
|
||||
obj-$(CONFIG_IPW2100) += ipw2100.o
|
||||
obj-$(CONFIG_IPW2200) += ipw2200.o
|
||||
|
||||
obj-$(CONFIG_LIBIPW) += libipw.o
|
||||
libipw-objs := \
|
||||
libipw_module.o \
|
||||
libipw_tx.o \
|
||||
libipw_rx.o \
|
||||
libipw_wx.o \
|
||||
libipw_geo.o
|
||||
|
195
drivers/net/wireless/ipw2x00/libipw_geo.c
Normal file
195
drivers/net/wireless/ipw2x00/libipw_geo.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/******************************************************************************
|
||||
|
||||
Copyright(c) 2005 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
The full GNU General Public License is included in this distribution in the
|
||||
file called LICENSE.
|
||||
|
||||
Contact Information:
|
||||
James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
||||
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
||||
|
||||
******************************************************************************/
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
int ieee80211_is_valid_channel(struct ieee80211_device *ieee, u8 channel)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Driver needs to initialize the geography map before using
|
||||
* these helper functions */
|
||||
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
|
||||
return 0;
|
||||
|
||||
if (ieee->freq_band & IEEE80211_24GHZ_BAND)
|
||||
for (i = 0; i < ieee->geo.bg_channels; i++)
|
||||
/* NOTE: If G mode is currently supported but
|
||||
* this is a B only channel, we don't see it
|
||||
* as valid. */
|
||||
if ((ieee->geo.bg[i].channel == channel) &&
|
||||
!(ieee->geo.bg[i].flags & IEEE80211_CH_INVALID) &&
|
||||
(!(ieee->mode & IEEE_G) ||
|
||||
!(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY)))
|
||||
return IEEE80211_24GHZ_BAND;
|
||||
|
||||
if (ieee->freq_band & IEEE80211_52GHZ_BAND)
|
||||
for (i = 0; i < ieee->geo.a_channels; i++)
|
||||
if ((ieee->geo.a[i].channel == channel) &&
|
||||
!(ieee->geo.a[i].flags & IEEE80211_CH_INVALID))
|
||||
return IEEE80211_52GHZ_BAND;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_channel_to_index(struct ieee80211_device *ieee, u8 channel)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Driver needs to initialize the geography map before using
|
||||
* these helper functions */
|
||||
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
|
||||
return -1;
|
||||
|
||||
if (ieee->freq_band & IEEE80211_24GHZ_BAND)
|
||||
for (i = 0; i < ieee->geo.bg_channels; i++)
|
||||
if (ieee->geo.bg[i].channel == channel)
|
||||
return i;
|
||||
|
||||
if (ieee->freq_band & IEEE80211_52GHZ_BAND)
|
||||
for (i = 0; i < ieee->geo.a_channels; i++)
|
||||
if (ieee->geo.a[i].channel == channel)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, u8 channel)
|
||||
{
|
||||
const struct ieee80211_channel * ch;
|
||||
|
||||
/* Driver needs to initialize the geography map before using
|
||||
* these helper functions */
|
||||
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
|
||||
return 0;
|
||||
|
||||
ch = ieee80211_get_channel(ieee, channel);
|
||||
if (!ch->channel)
|
||||
return 0;
|
||||
return ch->freq;
|
||||
}
|
||||
|
||||
u8 ieee80211_freq_to_channel(struct ieee80211_device * ieee, u32 freq)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Driver needs to initialize the geography map before using
|
||||
* these helper functions */
|
||||
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
|
||||
return 0;
|
||||
|
||||
freq /= 100000;
|
||||
|
||||
if (ieee->freq_band & IEEE80211_24GHZ_BAND)
|
||||
for (i = 0; i < ieee->geo.bg_channels; i++)
|
||||
if (ieee->geo.bg[i].freq == freq)
|
||||
return ieee->geo.bg[i].channel;
|
||||
|
||||
if (ieee->freq_band & IEEE80211_52GHZ_BAND)
|
||||
for (i = 0; i < ieee->geo.a_channels; i++)
|
||||
if (ieee->geo.a[i].freq == freq)
|
||||
return ieee->geo.a[i].channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_set_geo(struct ieee80211_device *ieee,
|
||||
const struct ieee80211_geo *geo)
|
||||
{
|
||||
memcpy(ieee->geo.name, geo->name, 3);
|
||||
ieee->geo.name[3] = '\0';
|
||||
ieee->geo.bg_channels = geo->bg_channels;
|
||||
ieee->geo.a_channels = geo->a_channels;
|
||||
memcpy(ieee->geo.bg, geo->bg, geo->bg_channels *
|
||||
sizeof(struct ieee80211_channel));
|
||||
memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels *
|
||||
sizeof(struct ieee80211_channel));
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ieee80211_geo *ieee80211_get_geo(struct ieee80211_device *ieee)
|
||||
{
|
||||
return &ieee->geo;
|
||||
}
|
||||
|
||||
u8 ieee80211_get_channel_flags(struct ieee80211_device * ieee, u8 channel)
|
||||
{
|
||||
int index = ieee80211_channel_to_index(ieee, channel);
|
||||
|
||||
if (index == -1)
|
||||
return IEEE80211_CH_INVALID;
|
||||
|
||||
if (channel <= IEEE80211_24GHZ_CHANNELS)
|
||||
return ieee->geo.bg[index].flags;
|
||||
|
||||
return ieee->geo.a[index].flags;
|
||||
}
|
||||
|
||||
static const struct ieee80211_channel bad_channel = {
|
||||
.channel = 0,
|
||||
.flags = IEEE80211_CH_INVALID,
|
||||
.max_power = 0,
|
||||
};
|
||||
|
||||
const struct ieee80211_channel *ieee80211_get_channel(struct ieee80211_device
|
||||
*ieee, u8 channel)
|
||||
{
|
||||
int index = ieee80211_channel_to_index(ieee, channel);
|
||||
|
||||
if (index == -1)
|
||||
return &bad_channel;
|
||||
|
||||
if (channel <= IEEE80211_24GHZ_CHANNELS)
|
||||
return &ieee->geo.bg[index];
|
||||
|
||||
return &ieee->geo.a[index];
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ieee80211_get_channel);
|
||||
EXPORT_SYMBOL(ieee80211_get_channel_flags);
|
||||
EXPORT_SYMBOL(ieee80211_is_valid_channel);
|
||||
EXPORT_SYMBOL(ieee80211_freq_to_channel);
|
||||
EXPORT_SYMBOL(ieee80211_channel_to_freq);
|
||||
EXPORT_SYMBOL(ieee80211_channel_to_index);
|
||||
EXPORT_SYMBOL(ieee80211_set_geo);
|
||||
EXPORT_SYMBOL(ieee80211_get_geo);
|
293
drivers/net/wireless/ipw2x00/libipw_module.c
Normal file
293
drivers/net/wireless/ipw2x00/libipw_module.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/*******************************************************************************
|
||||
|
||||
Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
|
||||
|
||||
Portions of this file are based on the WEP enablement code provided by the
|
||||
Host AP project hostap-drivers v0.1.3
|
||||
Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
|
||||
<j@w1.fi>
|
||||
Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
The full GNU General Public License is included in this distribution in the
|
||||
file called LICENSE.
|
||||
|
||||
Contact Information:
|
||||
James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
||||
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
#define DRV_DESCRIPTION "802.11 data/management/control stack"
|
||||
#define DRV_NAME "ieee80211"
|
||||
#define DRV_VERSION IEEE80211_VERSION
|
||||
#define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>"
|
||||
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
MODULE_DESCRIPTION(DRV_DESCRIPTION);
|
||||
MODULE_AUTHOR(DRV_COPYRIGHT);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int ieee80211_networks_allocate(struct ieee80211_device *ieee)
|
||||
{
|
||||
if (ieee->networks)
|
||||
return 0;
|
||||
|
||||
ieee->networks =
|
||||
kzalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network),
|
||||
GFP_KERNEL);
|
||||
if (!ieee->networks) {
|
||||
printk(KERN_WARNING "%s: Out of memory allocating beacons\n",
|
||||
ieee->dev->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_network_reset(struct ieee80211_network *network)
|
||||
{
|
||||
if (!network)
|
||||
return;
|
||||
|
||||
if (network->ibss_dfs) {
|
||||
kfree(network->ibss_dfs);
|
||||
network->ibss_dfs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!ieee->networks)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_NETWORK_COUNT; i++)
|
||||
if (ieee->networks[i].ibss_dfs)
|
||||
kfree(ieee->networks[i].ibss_dfs);
|
||||
|
||||
kfree(ieee->networks);
|
||||
ieee->networks = NULL;
|
||||
}
|
||||
|
||||
static void ieee80211_networks_initialize(struct ieee80211_device *ieee)
|
||||
{
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(&ieee->network_free_list);
|
||||
INIT_LIST_HEAD(&ieee->network_list);
|
||||
for (i = 0; i < MAX_NETWORK_COUNT; i++)
|
||||
list_add_tail(&ieee->networks[i].list,
|
||||
&ieee->network_free_list);
|
||||
}
|
||||
|
||||
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
if ((new_mtu < 68) || (new_mtu > IEEE80211_DATA_LEN))
|
||||
return -EINVAL;
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_device_stats *ieee80211_generic_get_stats(
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct ieee80211_device *ieee = netdev_priv(dev);
|
||||
return &ieee->stats;
|
||||
}
|
||||
|
||||
struct net_device *alloc_ieee80211(int sizeof_priv)
|
||||
{
|
||||
struct ieee80211_device *ieee;
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
IEEE80211_DEBUG_INFO("Initializing...\n");
|
||||
|
||||
dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
|
||||
if (!dev) {
|
||||
IEEE80211_ERROR("Unable to allocate network device.\n");
|
||||
goto failed;
|
||||
}
|
||||
ieee = netdev_priv(dev);
|
||||
dev->hard_start_xmit = ieee80211_xmit;
|
||||
dev->change_mtu = ieee80211_change_mtu;
|
||||
|
||||
/* Drivers are free to override this if the generic implementation
|
||||
* does not meet their needs. */
|
||||
dev->get_stats = ieee80211_generic_get_stats;
|
||||
|
||||
ieee->dev = dev;
|
||||
|
||||
err = ieee80211_networks_allocate(ieee);
|
||||
if (err) {
|
||||
IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err);
|
||||
goto failed_free_netdev;
|
||||
}
|
||||
ieee80211_networks_initialize(ieee);
|
||||
|
||||
/* Default fragmentation threshold is maximum payload size */
|
||||
ieee->fts = DEFAULT_FTS;
|
||||
ieee->rts = DEFAULT_FTS;
|
||||
ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
|
||||
ieee->open_wep = 1;
|
||||
|
||||
/* Default to enabling full open WEP with host based encrypt/decrypt */
|
||||
ieee->host_encrypt = 1;
|
||||
ieee->host_decrypt = 1;
|
||||
ieee->host_mc_decrypt = 1;
|
||||
|
||||
/* Host fragementation in Open mode. Default is enabled.
|
||||
* Note: host fragmentation is always enabled if host encryption
|
||||
* is enabled. For cards can do hardware encryption, they must do
|
||||
* hardware fragmentation as well. So we don't need a variable
|
||||
* like host_enc_frag. */
|
||||
ieee->host_open_frag = 1;
|
||||
ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
|
||||
|
||||
spin_lock_init(&ieee->lock);
|
||||
|
||||
lib80211_crypt_info_init(&ieee->crypt_info, dev->name, &ieee->lock);
|
||||
|
||||
ieee->wpa_enabled = 0;
|
||||
ieee->drop_unencrypted = 0;
|
||||
ieee->privacy_invoked = 0;
|
||||
|
||||
return dev;
|
||||
|
||||
failed_free_netdev:
|
||||
free_netdev(dev);
|
||||
failed:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_ieee80211(struct net_device *dev)
|
||||
{
|
||||
struct ieee80211_device *ieee = netdev_priv(dev);
|
||||
|
||||
lib80211_crypt_info_free(&ieee->crypt_info);
|
||||
|
||||
ieee80211_networks_free(ieee);
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211_DEBUG
|
||||
|
||||
static int debug = 0;
|
||||
u32 ieee80211_debug_level = 0;
|
||||
EXPORT_SYMBOL_GPL(ieee80211_debug_level);
|
||||
static struct proc_dir_entry *ieee80211_proc = NULL;
|
||||
|
||||
static int show_debug_level(char *page, char **start, off_t offset,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
return snprintf(page, count, "0x%08X\n", ieee80211_debug_level);
|
||||
}
|
||||
|
||||
static int store_debug_level(struct file *file, const char __user * buffer,
|
||||
unsigned long count, void *data)
|
||||
{
|
||||
char buf[] = "0x00000000\n";
|
||||
unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
|
||||
unsigned long val;
|
||||
|
||||
if (copy_from_user(buf, buffer, len))
|
||||
return count;
|
||||
buf[len] = 0;
|
||||
if (sscanf(buf, "%li", &val) != 1)
|
||||
printk(KERN_INFO DRV_NAME
|
||||
": %s is not in hex or decimal form.\n", buf);
|
||||
else
|
||||
ieee80211_debug_level = val;
|
||||
|
||||
return strnlen(buf, len);
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211_DEBUG */
|
||||
|
||||
static int __init ieee80211_init(void)
|
||||
{
|
||||
#ifdef CONFIG_IEEE80211_DEBUG
|
||||
struct proc_dir_entry *e;
|
||||
|
||||
ieee80211_debug_level = debug;
|
||||
ieee80211_proc = proc_mkdir(DRV_NAME, init_net.proc_net);
|
||||
if (ieee80211_proc == NULL) {
|
||||
IEEE80211_ERROR("Unable to create " DRV_NAME
|
||||
" proc directory\n");
|
||||
return -EIO;
|
||||
}
|
||||
e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
|
||||
ieee80211_proc);
|
||||
if (!e) {
|
||||
remove_proc_entry(DRV_NAME, init_net.proc_net);
|
||||
ieee80211_proc = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
e->read_proc = show_debug_level;
|
||||
e->write_proc = store_debug_level;
|
||||
e->data = NULL;
|
||||
#endif /* CONFIG_IEEE80211_DEBUG */
|
||||
|
||||
printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
|
||||
printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ieee80211_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_IEEE80211_DEBUG
|
||||
if (ieee80211_proc) {
|
||||
remove_proc_entry("debug_level", ieee80211_proc);
|
||||
remove_proc_entry(DRV_NAME, init_net.proc_net);
|
||||
ieee80211_proc = NULL;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211_DEBUG */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211_DEBUG
|
||||
#include <linux/moduleparam.h>
|
||||
module_param(debug, int, 0444);
|
||||
MODULE_PARM_DESC(debug, "debug output mask");
|
||||
#endif /* CONFIG_IEEE80211_DEBUG */
|
||||
|
||||
module_exit(ieee80211_exit);
|
||||
module_init(ieee80211_init);
|
||||
|
||||
EXPORT_SYMBOL(alloc_ieee80211);
|
||||
EXPORT_SYMBOL(free_ieee80211);
|
1799
drivers/net/wireless/ipw2x00/libipw_rx.c
Normal file
1799
drivers/net/wireless/ipw2x00/libipw_rx.c
Normal file
File diff suppressed because it is too large
Load Diff
546
drivers/net/wireless/ipw2x00/libipw_tx.c
Normal file
546
drivers/net/wireless/ipw2x00/libipw_tx.c
Normal file
@@ -0,0 +1,546 @@
|
||||
/******************************************************************************
|
||||
|
||||
Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
The full GNU General Public License is included in this distribution in the
|
||||
file called LICENSE.
|
||||
|
||||
Contact Information:
|
||||
James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
||||
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
||||
|
||||
******************************************************************************/
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
/*
|
||||
|
||||
802.11 Data Frame
|
||||
|
||||
,-------------------------------------------------------------------.
|
||||
Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
|
||||
|------|------|---------|---------|---------|------|---------|------|
|
||||
Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
|
||||
| | tion | (BSSID) | | | ence | data | |
|
||||
`--------------------------------------------------| |------'
|
||||
Total: 28 non-data bytes `----.----'
|
||||
|
|
||||
.- 'Frame data' expands, if WEP enabled, to <----------'
|
||||
|
|
||||
V
|
||||
,-----------------------.
|
||||
Bytes | 4 | 0-2296 | 4 |
|
||||
|-----|-----------|-----|
|
||||
Desc. | IV | Encrypted | ICV |
|
||||
| | Packet | |
|
||||
`-----| |-----'
|
||||
`-----.-----'
|
||||
|
|
||||
.- 'Encrypted Packet' expands to
|
||||
|
|
||||
V
|
||||
,---------------------------------------------------.
|
||||
Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 |
|
||||
|------|------|---------|----------|------|---------|
|
||||
Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP |
|
||||
| DSAP | SSAP | | | | Packet |
|
||||
| 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | |
|
||||
`----------------------------------------------------
|
||||
Total: 8 non-data bytes
|
||||
|
||||
802.3 Ethernet Data Frame
|
||||
|
||||
,-----------------------------------------.
|
||||
Bytes | 6 | 6 | 2 | Variable | 4 |
|
||||
|-------|-------|------|-----------|------|
|
||||
Desc. | Dest. | Source| Type | IP Packet | fcs |
|
||||
| MAC | MAC | | | |
|
||||
`-----------------------------------------'
|
||||
Total: 18 non-data bytes
|
||||
|
||||
In the event that fragmentation is required, the incoming payload is split into
|
||||
N parts of size ieee->fts. The first fragment contains the SNAP header and the
|
||||
remaining packets are just data.
|
||||
|
||||
If encryption is enabled, each fragment payload size is reduced by enough space
|
||||
to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
|
||||
So if you have 1500 bytes of payload with ieee->fts set to 500 without
|
||||
encryption it will take 3 frames. With WEP it will take 4 frames as the
|
||||
payload of each frame is reduced to 492 bytes.
|
||||
|
||||
* SKB visualization
|
||||
*
|
||||
* ,- skb->data
|
||||
* |
|
||||
* | ETHERNET HEADER ,-<-- PAYLOAD
|
||||
* | | 14 bytes from skb->data
|
||||
* | 2 bytes for Type --> ,T. | (sizeof ethhdr)
|
||||
* | | | |
|
||||
* |,-Dest.--. ,--Src.---. | | |
|
||||
* | 6 bytes| | 6 bytes | | | |
|
||||
* v | | | | | |
|
||||
* 0 | v 1 | v | v 2
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* ^ | ^ | ^ |
|
||||
* | | | | | |
|
||||
* | | | | `T' <---- 2 bytes for Type
|
||||
* | | | |
|
||||
* | | '---SNAP--' <-------- 6 bytes for SNAP
|
||||
* | |
|
||||
* `-IV--' <-------------------- 4 bytes for IV (WEP)
|
||||
*
|
||||
* SNAP HEADER
|
||||
*
|
||||
*/
|
||||
|
||||
static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
|
||||
static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
|
||||
|
||||
static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
|
||||
{
|
||||
struct ieee80211_snap_hdr *snap;
|
||||
u8 *oui;
|
||||
|
||||
snap = (struct ieee80211_snap_hdr *)data;
|
||||
snap->dsap = 0xaa;
|
||||
snap->ssap = 0xaa;
|
||||
snap->ctrl = 0x03;
|
||||
|
||||
if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX))
|
||||
oui = P802_1H_OUI;
|
||||
else
|
||||
oui = RFC1042_OUI;
|
||||
snap->oui[0] = oui[0];
|
||||
snap->oui[1] = oui[1];
|
||||
snap->oui[2] = oui[2];
|
||||
|
||||
memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16));
|
||||
|
||||
return SNAP_SIZE + sizeof(u16);
|
||||
}
|
||||
|
||||
static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
|
||||
struct sk_buff *frag, int hdr_len)
|
||||
{
|
||||
struct lib80211_crypt_data *crypt =
|
||||
ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
|
||||
int res;
|
||||
|
||||
if (crypt == NULL)
|
||||
return -1;
|
||||
|
||||
/* To encrypt, frame format is:
|
||||
* IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
|
||||
atomic_inc(&crypt->refcnt);
|
||||
res = 0;
|
||||
if (crypt->ops && crypt->ops->encrypt_mpdu)
|
||||
res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
|
||||
|
||||
atomic_dec(&crypt->refcnt);
|
||||
if (res < 0) {
|
||||
printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
|
||||
ieee->dev->name, frag->len);
|
||||
ieee->ieee_stats.tx_discards++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_txb_free(struct ieee80211_txb *txb)
|
||||
{
|
||||
int i;
|
||||
if (unlikely(!txb))
|
||||
return;
|
||||
for (i = 0; i < txb->nr_frags; i++)
|
||||
if (txb->fragments[i])
|
||||
dev_kfree_skb_any(txb->fragments[i]);
|
||||
kfree(txb);
|
||||
}
|
||||
|
||||
static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size,
|
||||
int headroom, gfp_t gfp_mask)
|
||||
{
|
||||
struct ieee80211_txb *txb;
|
||||
int i;
|
||||
txb = kmalloc(sizeof(struct ieee80211_txb) + (sizeof(u8 *) * nr_frags),
|
||||
gfp_mask);
|
||||
if (!txb)
|
||||
return NULL;
|
||||
|
||||
memset(txb, 0, sizeof(struct ieee80211_txb));
|
||||
txb->nr_frags = nr_frags;
|
||||
txb->frag_size = txb_size;
|
||||
|
||||
for (i = 0; i < nr_frags; i++) {
|
||||
txb->fragments[i] = __dev_alloc_skb(txb_size + headroom,
|
||||
gfp_mask);
|
||||
if (unlikely(!txb->fragments[i])) {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
skb_reserve(txb->fragments[i], headroom);
|
||||
}
|
||||
if (unlikely(i != nr_frags)) {
|
||||
while (i >= 0)
|
||||
dev_kfree_skb_any(txb->fragments[i--]);
|
||||
kfree(txb);
|
||||
return NULL;
|
||||
}
|
||||
return txb;
|
||||
}
|
||||
|
||||
static int ieee80211_classify(struct sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
struct iphdr *ip;
|
||||
|
||||
eth = (struct ethhdr *)skb->data;
|
||||
if (eth->h_proto != htons(ETH_P_IP))
|
||||
return 0;
|
||||
|
||||
ip = ip_hdr(skb);
|
||||
switch (ip->tos & 0xfc) {
|
||||
case 0x20:
|
||||
return 2;
|
||||
case 0x40:
|
||||
return 1;
|
||||
case 0x60:
|
||||
return 3;
|
||||
case 0x80:
|
||||
return 4;
|
||||
case 0xa0:
|
||||
return 5;
|
||||
case 0xc0:
|
||||
return 6;
|
||||
case 0xe0:
|
||||
return 7;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Incoming skb is converted to a txb which consists of
|
||||
* a block of 802.11 fragment packets (stored as skbs) */
|
||||
int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct ieee80211_device *ieee = netdev_priv(dev);
|
||||
struct ieee80211_txb *txb = NULL;
|
||||
struct ieee80211_hdr_3addrqos *frag_hdr;
|
||||
int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
|
||||
rts_required;
|
||||
unsigned long flags;
|
||||
struct net_device_stats *stats = &ieee->stats;
|
||||
int encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
|
||||
__be16 ether_type;
|
||||
int bytes, fc, hdr_len;
|
||||
struct sk_buff *skb_frag;
|
||||
struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
|
||||
.duration_id = 0,
|
||||
.seq_ctl = 0,
|
||||
.qos_ctl = 0
|
||||
};
|
||||
u8 dest[ETH_ALEN], src[ETH_ALEN];
|
||||
struct lib80211_crypt_data *crypt;
|
||||
int priority = skb->priority;
|
||||
int snapped = 0;
|
||||
|
||||
if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority))
|
||||
return NETDEV_TX_BUSY;
|
||||
|
||||
spin_lock_irqsave(&ieee->lock, flags);
|
||||
|
||||
/* If there is no driver handler to take the TXB, dont' bother
|
||||
* creating it... */
|
||||
if (!ieee->hard_start_xmit) {
|
||||
printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
|
||||
printk(KERN_WARNING "%s: skb too small (%d).\n",
|
||||
ieee->dev->name, skb->len);
|
||||
goto success;
|
||||
}
|
||||
|
||||
ether_type = ((struct ethhdr *)skb->data)->h_proto;
|
||||
|
||||
crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
|
||||
|
||||
encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) &&
|
||||
ieee->sec.encrypt;
|
||||
|
||||
host_encrypt = ieee->host_encrypt && encrypt && crypt;
|
||||
host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt;
|
||||
host_build_iv = ieee->host_build_iv && encrypt && crypt;
|
||||
|
||||
if (!encrypt && ieee->ieee802_1x &&
|
||||
ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
|
||||
stats->tx_dropped++;
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* Save source and destination addresses */
|
||||
skb_copy_from_linear_data(skb, dest, ETH_ALEN);
|
||||
skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
|
||||
|
||||
if (host_encrypt || host_build_iv)
|
||||
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
|
||||
IEEE80211_FCTL_PROTECTED;
|
||||
else
|
||||
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
|
||||
|
||||
if (ieee->iw_mode == IW_MODE_INFRA) {
|
||||
fc |= IEEE80211_FCTL_TODS;
|
||||
/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
|
||||
memcpy(header.addr1, ieee->bssid, ETH_ALEN);
|
||||
memcpy(header.addr2, src, ETH_ALEN);
|
||||
memcpy(header.addr3, dest, ETH_ALEN);
|
||||
} else if (ieee->iw_mode == IW_MODE_ADHOC) {
|
||||
/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
|
||||
memcpy(header.addr1, dest, ETH_ALEN);
|
||||
memcpy(header.addr2, src, ETH_ALEN);
|
||||
memcpy(header.addr3, ieee->bssid, ETH_ALEN);
|
||||
}
|
||||
hdr_len = IEEE80211_3ADDR_LEN;
|
||||
|
||||
if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
|
||||
fc |= IEEE80211_STYPE_QOS_DATA;
|
||||
hdr_len += 2;
|
||||
|
||||
skb->priority = ieee80211_classify(skb);
|
||||
header.qos_ctl |= cpu_to_le16(skb->priority & IEEE80211_QCTL_TID);
|
||||
}
|
||||
header.frame_ctl = cpu_to_le16(fc);
|
||||
|
||||
/* Advance the SKB to the start of the payload */
|
||||
skb_pull(skb, sizeof(struct ethhdr));
|
||||
|
||||
/* Determine total amount of storage required for TXB packets */
|
||||
bytes = skb->len + SNAP_SIZE + sizeof(u16);
|
||||
|
||||
/* Encrypt msdu first on the whole data packet. */
|
||||
if ((host_encrypt || host_encrypt_msdu) &&
|
||||
crypt && crypt->ops && crypt->ops->encrypt_msdu) {
|
||||
int res = 0;
|
||||
int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len +
|
||||
crypt->ops->extra_msdu_postfix_len;
|
||||
struct sk_buff *skb_new = dev_alloc_skb(len);
|
||||
|
||||
if (unlikely(!skb_new))
|
||||
goto failed;
|
||||
|
||||
skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len);
|
||||
memcpy(skb_put(skb_new, hdr_len), &header, hdr_len);
|
||||
snapped = 1;
|
||||
ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
|
||||
ether_type);
|
||||
skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
|
||||
res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
|
||||
if (res < 0) {
|
||||
IEEE80211_ERROR("msdu encryption failed\n");
|
||||
dev_kfree_skb_any(skb_new);
|
||||
goto failed;
|
||||
}
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = skb_new;
|
||||
bytes += crypt->ops->extra_msdu_prefix_len +
|
||||
crypt->ops->extra_msdu_postfix_len;
|
||||
skb_pull(skb, hdr_len);
|
||||
}
|
||||
|
||||
if (host_encrypt || ieee->host_open_frag) {
|
||||
/* Determine fragmentation size based on destination (multicast
|
||||
* and broadcast are not fragmented) */
|
||||
if (is_multicast_ether_addr(dest) ||
|
||||
is_broadcast_ether_addr(dest))
|
||||
frag_size = MAX_FRAG_THRESHOLD;
|
||||
else
|
||||
frag_size = ieee->fts;
|
||||
|
||||
/* Determine amount of payload per fragment. Regardless of if
|
||||
* this stack is providing the full 802.11 header, one will
|
||||
* eventually be affixed to this fragment -- so we must account
|
||||
* for it when determining the amount of payload space. */
|
||||
bytes_per_frag = frag_size - hdr_len;
|
||||
if (ieee->config &
|
||||
(CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
|
||||
bytes_per_frag -= IEEE80211_FCS_LEN;
|
||||
|
||||
/* Each fragment may need to have room for encryptiong
|
||||
* pre/postfix */
|
||||
if (host_encrypt)
|
||||
bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
|
||||
crypt->ops->extra_mpdu_postfix_len;
|
||||
|
||||
/* Number of fragments is the total
|
||||
* bytes_per_frag / payload_per_fragment */
|
||||
nr_frags = bytes / bytes_per_frag;
|
||||
bytes_last_frag = bytes % bytes_per_frag;
|
||||
if (bytes_last_frag)
|
||||
nr_frags++;
|
||||
else
|
||||
bytes_last_frag = bytes_per_frag;
|
||||
} else {
|
||||
nr_frags = 1;
|
||||
bytes_per_frag = bytes_last_frag = bytes;
|
||||
frag_size = bytes + hdr_len;
|
||||
}
|
||||
|
||||
rts_required = (frag_size > ieee->rts
|
||||
&& ieee->config & CFG_IEEE80211_RTS);
|
||||
if (rts_required)
|
||||
nr_frags++;
|
||||
|
||||
/* When we allocate the TXB we allocate enough space for the reserve
|
||||
* and full fragment bytes (bytes_per_frag doesn't include prefix,
|
||||
* postfix, header, FCS, etc.) */
|
||||
txb = ieee80211_alloc_txb(nr_frags, frag_size,
|
||||
ieee->tx_headroom, GFP_ATOMIC);
|
||||
if (unlikely(!txb)) {
|
||||
printk(KERN_WARNING "%s: Could not allocate TXB\n",
|
||||
ieee->dev->name);
|
||||
goto failed;
|
||||
}
|
||||
txb->encrypted = encrypt;
|
||||
if (host_encrypt)
|
||||
txb->payload_size = frag_size * (nr_frags - 1) +
|
||||
bytes_last_frag;
|
||||
else
|
||||
txb->payload_size = bytes;
|
||||
|
||||
if (rts_required) {
|
||||
skb_frag = txb->fragments[0];
|
||||
frag_hdr =
|
||||
(struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
|
||||
|
||||
/*
|
||||
* Set header frame_ctl to the RTS.
|
||||
*/
|
||||
header.frame_ctl =
|
||||
cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
|
||||
memcpy(frag_hdr, &header, hdr_len);
|
||||
|
||||
/*
|
||||
* Restore header frame_ctl to the original data setting.
|
||||
*/
|
||||
header.frame_ctl = cpu_to_le16(fc);
|
||||
|
||||
if (ieee->config &
|
||||
(CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
|
||||
skb_put(skb_frag, 4);
|
||||
|
||||
txb->rts_included = 1;
|
||||
i = 1;
|
||||
} else
|
||||
i = 0;
|
||||
|
||||
for (; i < nr_frags; i++) {
|
||||
skb_frag = txb->fragments[i];
|
||||
|
||||
if (host_encrypt || host_build_iv)
|
||||
skb_reserve(skb_frag,
|
||||
crypt->ops->extra_mpdu_prefix_len);
|
||||
|
||||
frag_hdr =
|
||||
(struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
|
||||
memcpy(frag_hdr, &header, hdr_len);
|
||||
|
||||
/* If this is not the last fragment, then add the MOREFRAGS
|
||||
* bit to the frame control */
|
||||
if (i != nr_frags - 1) {
|
||||
frag_hdr->frame_ctl =
|
||||
cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS);
|
||||
bytes = bytes_per_frag;
|
||||
} else {
|
||||
/* The last fragment takes the remaining length */
|
||||
bytes = bytes_last_frag;
|
||||
}
|
||||
|
||||
if (i == 0 && !snapped) {
|
||||
ieee80211_copy_snap(skb_put
|
||||
(skb_frag, SNAP_SIZE + sizeof(u16)),
|
||||
ether_type);
|
||||
bytes -= SNAP_SIZE + sizeof(u16);
|
||||
}
|
||||
|
||||
skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
|
||||
|
||||
/* Advance the SKB... */
|
||||
skb_pull(skb, bytes);
|
||||
|
||||
/* Encryption routine will move the header forward in order
|
||||
* to insert the IV between the header and the payload */
|
||||
if (host_encrypt)
|
||||
ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
|
||||
else if (host_build_iv) {
|
||||
atomic_inc(&crypt->refcnt);
|
||||
if (crypt->ops->build_iv)
|
||||
crypt->ops->build_iv(skb_frag, hdr_len,
|
||||
ieee->sec.keys[ieee->sec.active_key],
|
||||
ieee->sec.key_sizes[ieee->sec.active_key],
|
||||
crypt->priv);
|
||||
atomic_dec(&crypt->refcnt);
|
||||
}
|
||||
|
||||
if (ieee->config &
|
||||
(CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
|
||||
skb_put(skb_frag, 4);
|
||||
}
|
||||
|
||||
success:
|
||||
spin_unlock_irqrestore(&ieee->lock, flags);
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
if (txb) {
|
||||
int ret = (*ieee->hard_start_xmit) (txb, dev, priority);
|
||||
if (ret == 0) {
|
||||
stats->tx_packets++;
|
||||
stats->tx_bytes += txb->payload_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ieee80211_txb_free(txb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
spin_unlock_irqrestore(&ieee->lock, flags);
|
||||
netif_stop_queue(dev);
|
||||
stats->tx_errors++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ieee80211_txb_free);
|
760
drivers/net/wireless/ipw2x00/libipw_wx.c
Normal file
760
drivers/net/wireless/ipw2x00/libipw_wx.c
Normal file
@@ -0,0 +1,760 @@
|
||||
/******************************************************************************
|
||||
|
||||
Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
|
||||
|
||||
Portions of this file are based on the WEP enablement code provided by the
|
||||
Host AP project hostap-drivers v0.1.3
|
||||
Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
|
||||
<j@w1.fi>
|
||||
Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
The full GNU General Public License is included in this distribution in the
|
||||
file called LICENSE.
|
||||
|
||||
Contact Information:
|
||||
James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
||||
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <net/lib80211.h>
|
||||
#include <net/ieee80211.h>
|
||||
#include <linux/wireless.h>
|
||||
|
||||
static const char *ieee80211_modes[] = {
|
||||
"?", "a", "b", "ab", "g", "ag", "bg", "abg"
|
||||
};
|
||||
|
||||
#define MAX_CUSTOM_LEN 64
|
||||
static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
|
||||
char *start, char *stop,
|
||||
struct ieee80211_network *network,
|
||||
struct iw_request_info *info)
|
||||
{
|
||||
char custom[MAX_CUSTOM_LEN];
|
||||
char *p;
|
||||
struct iw_event iwe;
|
||||
int i, j;
|
||||
char *current_val; /* For rates */
|
||||
u8 rate;
|
||||
|
||||
/* First entry *MUST* be the AP MAC address */
|
||||
iwe.cmd = SIOCGIWAP;
|
||||
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
|
||||
memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
|
||||
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
|
||||
|
||||
/* Remaining entries will be displayed in the order we provide them */
|
||||
|
||||
/* Add the ESSID */
|
||||
iwe.cmd = SIOCGIWESSID;
|
||||
iwe.u.data.flags = 1;
|
||||
iwe.u.data.length = min(network->ssid_len, (u8) 32);
|
||||
start = iwe_stream_add_point(info, start, stop,
|
||||
&iwe, network->ssid);
|
||||
|
||||
/* Add the protocol name */
|
||||
iwe.cmd = SIOCGIWNAME;
|
||||
snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
|
||||
ieee80211_modes[network->mode]);
|
||||
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
|
||||
|
||||
/* Add mode */
|
||||
iwe.cmd = SIOCGIWMODE;
|
||||
if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
|
||||
if (network->capability & WLAN_CAPABILITY_ESS)
|
||||
iwe.u.mode = IW_MODE_MASTER;
|
||||
else
|
||||
iwe.u.mode = IW_MODE_ADHOC;
|
||||
|
||||
start = iwe_stream_add_event(info, start, stop,
|
||||
&iwe, IW_EV_UINT_LEN);
|
||||
}
|
||||
|
||||
/* Add channel and frequency */
|
||||
/* Note : userspace automatically computes channel using iwrange */
|
||||
iwe.cmd = SIOCGIWFREQ;
|
||||
iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
|
||||
iwe.u.freq.e = 6;
|
||||
iwe.u.freq.i = 0;
|
||||
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
|
||||
|
||||
/* Add encryption capability */
|
||||
iwe.cmd = SIOCGIWENCODE;
|
||||
if (network->capability & WLAN_CAPABILITY_PRIVACY)
|
||||
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
|
||||
else
|
||||
iwe.u.data.flags = IW_ENCODE_DISABLED;
|
||||
iwe.u.data.length = 0;
|
||||
start = iwe_stream_add_point(info, start, stop,
|
||||
&iwe, network->ssid);
|
||||
|
||||
/* Add basic and extended rates */
|
||||
/* Rate : stuffing multiple values in a single event require a bit
|
||||
* more of magic - Jean II */
|
||||
current_val = start + iwe_stream_lcp_len(info);
|
||||
iwe.cmd = SIOCGIWRATE;
|
||||
/* Those two flags are ignored... */
|
||||
iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
|
||||
|
||||
for (i = 0, j = 0; i < network->rates_len;) {
|
||||
if (j < network->rates_ex_len &&
|
||||
((network->rates_ex[j] & 0x7F) <
|
||||
(network->rates[i] & 0x7F)))
|
||||
rate = network->rates_ex[j++] & 0x7F;
|
||||
else
|
||||
rate = network->rates[i++] & 0x7F;
|
||||
/* Bit rate given in 500 kb/s units (+ 0x80) */
|
||||
iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
|
||||
/* Add new value to event */
|
||||
current_val = iwe_stream_add_value(info, start, current_val,
|
||||
stop, &iwe, IW_EV_PARAM_LEN);
|
||||
}
|
||||
for (; j < network->rates_ex_len; j++) {
|
||||
rate = network->rates_ex[j] & 0x7F;
|
||||
/* Bit rate given in 500 kb/s units (+ 0x80) */
|
||||
iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
|
||||
/* Add new value to event */
|
||||
current_val = iwe_stream_add_value(info, start, current_val,
|
||||
stop, &iwe, IW_EV_PARAM_LEN);
|
||||
}
|
||||
/* Check if we added any rate */
|
||||
if ((current_val - start) > iwe_stream_lcp_len(info))
|
||||
start = current_val;
|
||||
|
||||
/* Add quality statistics */
|
||||
iwe.cmd = IWEVQUAL;
|
||||
iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
|
||||
IW_QUAL_NOISE_UPDATED;
|
||||
|
||||
if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
|
||||
iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
|
||||
IW_QUAL_LEVEL_INVALID;
|
||||
iwe.u.qual.qual = 0;
|
||||
} else {
|
||||
if (ieee->perfect_rssi == ieee->worst_rssi)
|
||||
iwe.u.qual.qual = 100;
|
||||
else
|
||||
iwe.u.qual.qual =
|
||||
(100 *
|
||||
(ieee->perfect_rssi - ieee->worst_rssi) *
|
||||
(ieee->perfect_rssi - ieee->worst_rssi) -
|
||||
(ieee->perfect_rssi - network->stats.rssi) *
|
||||
(15 * (ieee->perfect_rssi - ieee->worst_rssi) +
|
||||
62 * (ieee->perfect_rssi -
|
||||
network->stats.rssi))) /
|
||||
((ieee->perfect_rssi -
|
||||
ieee->worst_rssi) * (ieee->perfect_rssi -
|
||||
ieee->worst_rssi));
|
||||
if (iwe.u.qual.qual > 100)
|
||||
iwe.u.qual.qual = 100;
|
||||
else if (iwe.u.qual.qual < 1)
|
||||
iwe.u.qual.qual = 0;
|
||||
}
|
||||
|
||||
if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
|
||||
iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
|
||||
iwe.u.qual.noise = 0;
|
||||
} else {
|
||||
iwe.u.qual.noise = network->stats.noise;
|
||||
}
|
||||
|
||||
if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
|
||||
iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
|
||||
iwe.u.qual.level = 0;
|
||||
} else {
|
||||
iwe.u.qual.level = network->stats.signal;
|
||||
}
|
||||
|
||||
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
|
||||
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
p = custom;
|
||||
|
||||
iwe.u.data.length = p - custom;
|
||||
if (iwe.u.data.length)
|
||||
start = iwe_stream_add_point(info, start, stop, &iwe, custom);
|
||||
|
||||
memset(&iwe, 0, sizeof(iwe));
|
||||
if (network->wpa_ie_len) {
|
||||
char buf[MAX_WPA_IE_LEN];
|
||||
memcpy(buf, network->wpa_ie, network->wpa_ie_len);
|
||||
iwe.cmd = IWEVGENIE;
|
||||
iwe.u.data.length = network->wpa_ie_len;
|
||||
start = iwe_stream_add_point(info, start, stop, &iwe, buf);
|
||||
}
|
||||
|
||||
memset(&iwe, 0, sizeof(iwe));
|
||||
if (network->rsn_ie_len) {
|
||||
char buf[MAX_WPA_IE_LEN];
|
||||
memcpy(buf, network->rsn_ie, network->rsn_ie_len);
|
||||
iwe.cmd = IWEVGENIE;
|
||||
iwe.u.data.length = network->rsn_ie_len;
|
||||
start = iwe_stream_add_point(info, start, stop, &iwe, buf);
|
||||
}
|
||||
|
||||
/* Add EXTRA: Age to display seconds since last beacon/probe response
|
||||
* for given network. */
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
p = custom;
|
||||
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
|
||||
" Last beacon: %dms ago",
|
||||
jiffies_to_msecs(jiffies - network->last_scanned));
|
||||
iwe.u.data.length = p - custom;
|
||||
if (iwe.u.data.length)
|
||||
start = iwe_stream_add_point(info, start, stop, &iwe, custom);
|
||||
|
||||
/* Add spectrum management information */
|
||||
iwe.cmd = -1;
|
||||
p = custom;
|
||||
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
|
||||
|
||||
if (ieee80211_get_channel_flags(ieee, network->channel) &
|
||||
IEEE80211_CH_INVALID) {
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
|
||||
}
|
||||
|
||||
if (ieee80211_get_channel_flags(ieee, network->channel) &
|
||||
IEEE80211_CH_RADAR_DETECT) {
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
|
||||
}
|
||||
|
||||
if (iwe.cmd == IWEVCUSTOM) {
|
||||
iwe.u.data.length = p - custom;
|
||||
start = iwe_stream_add_point(info, start, stop, &iwe, custom);
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
#define SCAN_ITEM_SIZE 128
|
||||
|
||||
int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
struct ieee80211_network *network;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
char *ev = extra;
|
||||
char *stop = ev + wrqu->data.length;
|
||||
int i = 0;
|
||||
DECLARE_SSID_BUF(ssid);
|
||||
|
||||
IEEE80211_DEBUG_WX("Getting scan\n");
|
||||
|
||||
spin_lock_irqsave(&ieee->lock, flags);
|
||||
|
||||
list_for_each_entry(network, &ieee->network_list, list) {
|
||||
i++;
|
||||
if (stop - ev < SCAN_ITEM_SIZE) {
|
||||
err = -E2BIG;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ieee->scan_age == 0 ||
|
||||
time_after(network->last_scanned + ieee->scan_age, jiffies))
|
||||
ev = ieee80211_translate_scan(ieee, ev, stop, network,
|
||||
info);
|
||||
else
|
||||
IEEE80211_DEBUG_SCAN("Not showing network '%s ("
|
||||
"%pM)' due to age (%dms).\n",
|
||||
print_ssid(ssid, network->ssid,
|
||||
network->ssid_len),
|
||||
network->bssid,
|
||||
jiffies_to_msecs(jiffies -
|
||||
network->
|
||||
last_scanned));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ieee->lock, flags);
|
||||
|
||||
wrqu->data.length = ev - extra;
|
||||
wrqu->data.flags = 0;
|
||||
|
||||
IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *keybuf)
|
||||
{
|
||||
struct iw_point *erq = &(wrqu->encoding);
|
||||
struct net_device *dev = ieee->dev;
|
||||
struct ieee80211_security sec = {
|
||||
.flags = 0
|
||||
};
|
||||
int i, key, key_provided, len;
|
||||
struct lib80211_crypt_data **crypt;
|
||||
int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
|
||||
DECLARE_SSID_BUF(ssid);
|
||||
|
||||
IEEE80211_DEBUG_WX("SET_ENCODE\n");
|
||||
|
||||
key = erq->flags & IW_ENCODE_INDEX;
|
||||
if (key) {
|
||||
if (key > WEP_KEYS)
|
||||
return -EINVAL;
|
||||
key--;
|
||||
key_provided = 1;
|
||||
} else {
|
||||
key_provided = 0;
|
||||
key = ieee->crypt_info.tx_keyidx;
|
||||
}
|
||||
|
||||
IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
|
||||
"provided" : "default");
|
||||
|
||||
crypt = &ieee->crypt_info.crypt[key];
|
||||
|
||||
if (erq->flags & IW_ENCODE_DISABLED) {
|
||||
if (key_provided && *crypt) {
|
||||
IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
|
||||
key);
|
||||
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
|
||||
} else
|
||||
IEEE80211_DEBUG_WX("Disabling encryption.\n");
|
||||
|
||||
/* Check all the keys to see if any are still configured,
|
||||
* and if no key index was provided, de-init them all */
|
||||
for (i = 0; i < WEP_KEYS; i++) {
|
||||
if (ieee->crypt_info.crypt[i] != NULL) {
|
||||
if (key_provided)
|
||||
break;
|
||||
lib80211_crypt_delayed_deinit(&ieee->crypt_info,
|
||||
&ieee->crypt_info.crypt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == WEP_KEYS) {
|
||||
sec.enabled = 0;
|
||||
sec.encrypt = 0;
|
||||
sec.level = SEC_LEVEL_0;
|
||||
sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
sec.enabled = 1;
|
||||
sec.encrypt = 1;
|
||||
sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
|
||||
|
||||
if (*crypt != NULL && (*crypt)->ops != NULL &&
|
||||
strcmp((*crypt)->ops->name, "WEP") != 0) {
|
||||
/* changing to use WEP; deinit previously used algorithm
|
||||
* on this key */
|
||||
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
|
||||
}
|
||||
|
||||
if (*crypt == NULL && host_crypto) {
|
||||
struct lib80211_crypt_data *new_crypt;
|
||||
|
||||
/* take WEP into use */
|
||||
new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
|
||||
GFP_KERNEL);
|
||||
if (new_crypt == NULL)
|
||||
return -ENOMEM;
|
||||
new_crypt->ops = lib80211_get_crypto_ops("WEP");
|
||||
if (!new_crypt->ops) {
|
||||
request_module("lib80211_crypt_wep");
|
||||
new_crypt->ops = lib80211_get_crypto_ops("WEP");
|
||||
}
|
||||
|
||||
if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
|
||||
new_crypt->priv = new_crypt->ops->init(key);
|
||||
|
||||
if (!new_crypt->ops || !new_crypt->priv) {
|
||||
kfree(new_crypt);
|
||||
new_crypt = NULL;
|
||||
|
||||
printk(KERN_WARNING "%s: could not initialize WEP: "
|
||||
"load module lib80211_crypt_wep\n", dev->name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
*crypt = new_crypt;
|
||||
}
|
||||
|
||||
/* If a new key was provided, set it up */
|
||||
if (erq->length > 0) {
|
||||
#ifdef CONFIG_IEEE80211_DEBUG
|
||||
DECLARE_SSID_BUF(ssid);
|
||||
#endif
|
||||
|
||||
len = erq->length <= 5 ? 5 : 13;
|
||||
memcpy(sec.keys[key], keybuf, erq->length);
|
||||
if (len > erq->length)
|
||||
memset(sec.keys[key] + erq->length, 0,
|
||||
len - erq->length);
|
||||
IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
|
||||
key, print_ssid(ssid, sec.keys[key], len),
|
||||
erq->length, len);
|
||||
sec.key_sizes[key] = len;
|
||||
if (*crypt)
|
||||
(*crypt)->ops->set_key(sec.keys[key], len, NULL,
|
||||
(*crypt)->priv);
|
||||
sec.flags |= (1 << key);
|
||||
/* This ensures a key will be activated if no key is
|
||||
* explicitly set */
|
||||
if (key == sec.active_key)
|
||||
sec.flags |= SEC_ACTIVE_KEY;
|
||||
|
||||
} else {
|
||||
if (host_crypto) {
|
||||
len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
|
||||
NULL, (*crypt)->priv);
|
||||
if (len == 0) {
|
||||
/* Set a default key of all 0 */
|
||||
IEEE80211_DEBUG_WX("Setting key %d to all "
|
||||
"zero.\n", key);
|
||||
memset(sec.keys[key], 0, 13);
|
||||
(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
|
||||
(*crypt)->priv);
|
||||
sec.key_sizes[key] = 13;
|
||||
sec.flags |= (1 << key);
|
||||
}
|
||||
}
|
||||
/* No key data - just set the default TX key index */
|
||||
if (key_provided) {
|
||||
IEEE80211_DEBUG_WX("Setting key %d to default Tx "
|
||||
"key.\n", key);
|
||||
ieee->crypt_info.tx_keyidx = key;
|
||||
sec.active_key = key;
|
||||
sec.flags |= SEC_ACTIVE_KEY;
|
||||
}
|
||||
}
|
||||
if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
|
||||
ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
|
||||
sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
|
||||
WLAN_AUTH_SHARED_KEY;
|
||||
sec.flags |= SEC_AUTH_MODE;
|
||||
IEEE80211_DEBUG_WX("Auth: %s\n",
|
||||
sec.auth_mode == WLAN_AUTH_OPEN ?
|
||||
"OPEN" : "SHARED KEY");
|
||||
}
|
||||
|
||||
/* For now we just support WEP, so only set that security level...
|
||||
* TODO: When WPA is added this is one place that needs to change */
|
||||
sec.flags |= SEC_LEVEL;
|
||||
sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
|
||||
sec.encode_alg[key] = SEC_ALG_WEP;
|
||||
|
||||
done:
|
||||
if (ieee->set_security)
|
||||
ieee->set_security(dev, &sec);
|
||||
|
||||
/* Do not reset port if card is in Managed mode since resetting will
|
||||
* generate new IEEE 802.11 authentication which may end up in looping
|
||||
* with IEEE 802.1X. If your hardware requires a reset after WEP
|
||||
* configuration (for example... Prism2), implement the reset_port in
|
||||
* the callbacks structures used to initialize the 802.11 stack. */
|
||||
if (ieee->reset_on_keychange &&
|
||||
ieee->iw_mode != IW_MODE_INFRA &&
|
||||
ieee->reset_port && ieee->reset_port(dev)) {
|
||||
printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *keybuf)
|
||||
{
|
||||
struct iw_point *erq = &(wrqu->encoding);
|
||||
int len, key;
|
||||
struct lib80211_crypt_data *crypt;
|
||||
struct ieee80211_security *sec = &ieee->sec;
|
||||
|
||||
IEEE80211_DEBUG_WX("GET_ENCODE\n");
|
||||
|
||||
key = erq->flags & IW_ENCODE_INDEX;
|
||||
if (key) {
|
||||
if (key > WEP_KEYS)
|
||||
return -EINVAL;
|
||||
key--;
|
||||
} else
|
||||
key = ieee->crypt_info.tx_keyidx;
|
||||
|
||||
crypt = ieee->crypt_info.crypt[key];
|
||||
erq->flags = key + 1;
|
||||
|
||||
if (!sec->enabled) {
|
||||
erq->length = 0;
|
||||
erq->flags |= IW_ENCODE_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = sec->key_sizes[key];
|
||||
memcpy(keybuf, sec->keys[key], len);
|
||||
|
||||
erq->length = len;
|
||||
erq->flags |= IW_ENCODE_ENABLED;
|
||||
|
||||
if (ieee->open_wep)
|
||||
erq->flags |= IW_ENCODE_OPEN;
|
||||
else
|
||||
erq->flags |= IW_ENCODE_RESTRICTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
struct net_device *dev = ieee->dev;
|
||||
struct iw_point *encoding = &wrqu->encoding;
|
||||
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
|
||||
int i, idx, ret = 0;
|
||||
int group_key = 0;
|
||||
const char *alg, *module;
|
||||
struct lib80211_crypto_ops *ops;
|
||||
struct lib80211_crypt_data **crypt;
|
||||
|
||||
struct ieee80211_security sec = {
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
idx = encoding->flags & IW_ENCODE_INDEX;
|
||||
if (idx) {
|
||||
if (idx < 1 || idx > WEP_KEYS)
|
||||
return -EINVAL;
|
||||
idx--;
|
||||
} else
|
||||
idx = ieee->crypt_info.tx_keyidx;
|
||||
|
||||
if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
|
||||
crypt = &ieee->crypt_info.crypt[idx];
|
||||
group_key = 1;
|
||||
} else {
|
||||
/* some Cisco APs use idx>0 for unicast in dynamic WEP */
|
||||
if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
|
||||
return -EINVAL;
|
||||
if (ieee->iw_mode == IW_MODE_INFRA)
|
||||
crypt = &ieee->crypt_info.crypt[idx];
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
|
||||
if ((encoding->flags & IW_ENCODE_DISABLED) ||
|
||||
ext->alg == IW_ENCODE_ALG_NONE) {
|
||||
if (*crypt)
|
||||
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
|
||||
|
||||
for (i = 0; i < WEP_KEYS; i++)
|
||||
if (ieee->crypt_info.crypt[i] != NULL)
|
||||
break;
|
||||
|
||||
if (i == WEP_KEYS) {
|
||||
sec.enabled = 0;
|
||||
sec.encrypt = 0;
|
||||
sec.level = SEC_LEVEL_0;
|
||||
sec.flags |= SEC_LEVEL;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
sec.enabled = 1;
|
||||
sec.encrypt = 1;
|
||||
|
||||
if (group_key ? !ieee->host_mc_decrypt :
|
||||
!(ieee->host_encrypt || ieee->host_decrypt ||
|
||||
ieee->host_encrypt_msdu))
|
||||
goto skip_host_crypt;
|
||||
|
||||
switch (ext->alg) {
|
||||
case IW_ENCODE_ALG_WEP:
|
||||
alg = "WEP";
|
||||
module = "lib80211_crypt_wep";
|
||||
break;
|
||||
case IW_ENCODE_ALG_TKIP:
|
||||
alg = "TKIP";
|
||||
module = "lib80211_crypt_tkip";
|
||||
break;
|
||||
case IW_ENCODE_ALG_CCMP:
|
||||
alg = "CCMP";
|
||||
module = "lib80211_crypt_ccmp";
|
||||
break;
|
||||
default:
|
||||
IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
|
||||
dev->name, ext->alg);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ops = lib80211_get_crypto_ops(alg);
|
||||
if (ops == NULL) {
|
||||
request_module(module);
|
||||
ops = lib80211_get_crypto_ops(alg);
|
||||
}
|
||||
if (ops == NULL) {
|
||||
IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
|
||||
dev->name, ext->alg);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (*crypt == NULL || (*crypt)->ops != ops) {
|
||||
struct lib80211_crypt_data *new_crypt;
|
||||
|
||||
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
|
||||
|
||||
new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
|
||||
if (new_crypt == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
new_crypt->ops = ops;
|
||||
if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
|
||||
new_crypt->priv = new_crypt->ops->init(idx);
|
||||
if (new_crypt->priv == NULL) {
|
||||
kfree(new_crypt);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
*crypt = new_crypt;
|
||||
}
|
||||
|
||||
if (ext->key_len > 0 && (*crypt)->ops->set_key &&
|
||||
(*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
|
||||
(*crypt)->priv) < 0) {
|
||||
IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
skip_host_crypt:
|
||||
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
|
||||
ieee->crypt_info.tx_keyidx = idx;
|
||||
sec.active_key = idx;
|
||||
sec.flags |= SEC_ACTIVE_KEY;
|
||||
}
|
||||
|
||||
if (ext->alg != IW_ENCODE_ALG_NONE) {
|
||||
memcpy(sec.keys[idx], ext->key, ext->key_len);
|
||||
sec.key_sizes[idx] = ext->key_len;
|
||||
sec.flags |= (1 << idx);
|
||||
if (ext->alg == IW_ENCODE_ALG_WEP) {
|
||||
sec.encode_alg[idx] = SEC_ALG_WEP;
|
||||
sec.flags |= SEC_LEVEL;
|
||||
sec.level = SEC_LEVEL_1;
|
||||
} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
|
||||
sec.encode_alg[idx] = SEC_ALG_TKIP;
|
||||
sec.flags |= SEC_LEVEL;
|
||||
sec.level = SEC_LEVEL_2;
|
||||
} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
|
||||
sec.encode_alg[idx] = SEC_ALG_CCMP;
|
||||
sec.flags |= SEC_LEVEL;
|
||||
sec.level = SEC_LEVEL_3;
|
||||
}
|
||||
/* Don't set sec level for group keys. */
|
||||
if (group_key)
|
||||
sec.flags &= ~SEC_LEVEL;
|
||||
}
|
||||
done:
|
||||
if (ieee->set_security)
|
||||
ieee->set_security(ieee->dev, &sec);
|
||||
|
||||
/*
|
||||
* Do not reset port if card is in Managed mode since resetting will
|
||||
* generate new IEEE 802.11 authentication which may end up in looping
|
||||
* with IEEE 802.1X. If your hardware requires a reset after WEP
|
||||
* configuration (for example... Prism2), implement the reset_port in
|
||||
* the callbacks structures used to initialize the 802.11 stack.
|
||||
*/
|
||||
if (ieee->reset_on_keychange &&
|
||||
ieee->iw_mode != IW_MODE_INFRA &&
|
||||
ieee->reset_port && ieee->reset_port(dev)) {
|
||||
IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
struct iw_point *encoding = &wrqu->encoding;
|
||||
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
|
||||
struct ieee80211_security *sec = &ieee->sec;
|
||||
int idx, max_key_len;
|
||||
|
||||
max_key_len = encoding->length - sizeof(*ext);
|
||||
if (max_key_len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
idx = encoding->flags & IW_ENCODE_INDEX;
|
||||
if (idx) {
|
||||
if (idx < 1 || idx > WEP_KEYS)
|
||||
return -EINVAL;
|
||||
idx--;
|
||||
} else
|
||||
idx = ieee->crypt_info.tx_keyidx;
|
||||
|
||||
if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
|
||||
ext->alg != IW_ENCODE_ALG_WEP)
|
||||
if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
|
||||
return -EINVAL;
|
||||
|
||||
encoding->flags = idx + 1;
|
||||
memset(ext, 0, sizeof(*ext));
|
||||
|
||||
if (!sec->enabled) {
|
||||
ext->alg = IW_ENCODE_ALG_NONE;
|
||||
ext->key_len = 0;
|
||||
encoding->flags |= IW_ENCODE_DISABLED;
|
||||
} else {
|
||||
if (sec->encode_alg[idx] == SEC_ALG_WEP)
|
||||
ext->alg = IW_ENCODE_ALG_WEP;
|
||||
else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
|
||||
ext->alg = IW_ENCODE_ALG_TKIP;
|
||||
else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
|
||||
ext->alg = IW_ENCODE_ALG_CCMP;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ext->key_len = sec->key_sizes[idx];
|
||||
memcpy(ext->key, sec->keys[idx], ext->key_len);
|
||||
encoding->flags |= IW_ENCODE_ENABLED;
|
||||
if (ext->key_len &&
|
||||
(ext->alg == IW_ENCODE_ALG_TKIP ||
|
||||
ext->alg == IW_ENCODE_ALG_CCMP))
|
||||
ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
|
||||
EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
|
||||
|
||||
EXPORT_SYMBOL(ieee80211_wx_get_scan);
|
||||
EXPORT_SYMBOL(ieee80211_wx_set_encode);
|
||||
EXPORT_SYMBOL(ieee80211_wx_get_encode);
|
Reference in New Issue
Block a user