Merge refs/heads/ieee80211-wifi from master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6
This commit is contained in:
commit
5d8c397f30
246
Documentation/networking/README.ipw2100
Normal file
246
Documentation/networking/README.ipw2100
Normal file
@ -0,0 +1,246 @@
|
||||
|
||||
===========================
|
||||
Intel(R) PRO/Wireless 2100 Network Connection Driver for Linux
|
||||
README.ipw2100
|
||||
|
||||
March 14, 2005
|
||||
|
||||
===========================
|
||||
Index
|
||||
---------------------------
|
||||
0. Introduction
|
||||
1. Release 1.1.0 Current Features
|
||||
2. Command Line Parameters
|
||||
3. Sysfs Helper Files
|
||||
4. Radio Kill Switch
|
||||
5. Dynamic Firmware
|
||||
6. Power Management
|
||||
7. Support
|
||||
8. License
|
||||
|
||||
|
||||
===========================
|
||||
0. Introduction
|
||||
------------ ----- ----- ---- --- -- -
|
||||
|
||||
This document provides a brief overview of the features supported by the
|
||||
IPW2100 driver project. The main project website, where the latest
|
||||
development version of the driver can be found, is:
|
||||
|
||||
http://ipw2100.sourceforge.net
|
||||
|
||||
There you can find the not only the latest releases, but also information about
|
||||
potential fixes and patches, as well as links to the development mailing list
|
||||
for the driver project.
|
||||
|
||||
|
||||
===========================
|
||||
1. Release 1.1.0 Current Supported Features
|
||||
---------------------------
|
||||
- Managed (BSS) and Ad-Hoc (IBSS)
|
||||
- WEP (shared key and open)
|
||||
- Wireless Tools support
|
||||
- 802.1x (tested with XSupplicant 1.0.1)
|
||||
|
||||
Enabled (but not supported) features:
|
||||
- Monitor/RFMon mode
|
||||
- WPA/WPA2
|
||||
|
||||
The distinction between officially supported and enabled is a reflection
|
||||
on the amount of validation and interoperability testing that has been
|
||||
performed on a given feature.
|
||||
|
||||
|
||||
===========================
|
||||
2. Command Line Parameters
|
||||
---------------------------
|
||||
|
||||
If the driver is built as a module, the following optional parameters are used
|
||||
by entering them on the command line with the modprobe command using this
|
||||
syntax:
|
||||
|
||||
modprobe ipw2100 [<option>=<VAL1><,VAL2>...]
|
||||
|
||||
For example, to disable the radio on driver loading, enter:
|
||||
|
||||
modprobe ipw2100 disable=1
|
||||
|
||||
The ipw2100 driver supports the following module parameters:
|
||||
|
||||
Name Value Example:
|
||||
debug 0x0-0xffffffff debug=1024
|
||||
mode 0,1,2 mode=1 /* AdHoc */
|
||||
channel int channel=3 /* Only valid in AdHoc or Monitor */
|
||||
associate boolean associate=0 /* Do NOT auto associate */
|
||||
disable boolean disable=1 /* Do not power the HW */
|
||||
|
||||
|
||||
===========================
|
||||
3. Sysfs Helper Files
|
||||
---------------------------
|
||||
|
||||
There are several ways to control the behavior of the driver. Many of the
|
||||
general capabilities are exposed through the Wireless Tools (iwconfig). There
|
||||
are a few capabilities that are exposed through entries in the Linux Sysfs.
|
||||
|
||||
|
||||
----- Driver Level ------
|
||||
For the driver level files, look in /sys/bus/pci/drivers/ipw2100/
|
||||
|
||||
debug_level
|
||||
|
||||
This controls the same global as the 'debug' module parameter. For
|
||||
information on the various debugging levels available, run the 'dvals'
|
||||
script found in the driver source directory.
|
||||
|
||||
NOTE: 'debug_level' is only enabled if CONFIG_IPW2100_DEBUG is turn
|
||||
on.
|
||||
|
||||
----- Device Level ------
|
||||
For the device level files look in
|
||||
|
||||
/sys/bus/pci/drivers/ipw2100/{PCI-ID}/
|
||||
|
||||
For example:
|
||||
/sys/bus/pci/drivers/ipw2100/0000:02:01.0
|
||||
|
||||
For the device level files, see /sys/bus/pci/drivers/ipw2100:
|
||||
|
||||
rf_kill
|
||||
read -
|
||||
0 = RF kill not enabled (radio on)
|
||||
1 = SW based RF kill active (radio off)
|
||||
2 = HW based RF kill active (radio off)
|
||||
3 = Both HW and SW RF kill active (radio off)
|
||||
write -
|
||||
0 = If SW based RF kill active, turn the radio back on
|
||||
1 = If radio is on, activate SW based RF kill
|
||||
|
||||
NOTE: If you enable the SW based RF kill and then toggle the HW
|
||||
based RF kill from ON -> OFF -> ON, the radio will NOT come back on
|
||||
|
||||
|
||||
===========================
|
||||
4. Radio Kill Switch
|
||||
---------------------------
|
||||
Most laptops provide the ability for the user to physically disable the radio.
|
||||
Some vendors have implemented this as a physical switch that requires no
|
||||
software to turn the radio off and on. On other laptops, however, the switch
|
||||
is controlled through a button being pressed and a software driver then making
|
||||
calls to turn the radio off and on. This is referred to as a "software based
|
||||
RF kill switch"
|
||||
|
||||
See the Sysfs helper file 'rf_kill' for determining the state of the RF switch
|
||||
on your system.
|
||||
|
||||
|
||||
===========================
|
||||
5. Dynamic Firmware
|
||||
---------------------------
|
||||
As the firmware is licensed under a restricted use license, it can not be
|
||||
included within the kernel sources. To enable the IPW2100 you will need a
|
||||
firmware image to load into the wireless NIC's processors.
|
||||
|
||||
You can obtain these images from <http://ipw2100.sf.net/firmware.php>.
|
||||
|
||||
See INSTALL for instructions on installing the firmware.
|
||||
|
||||
|
||||
===========================
|
||||
6. Power Management
|
||||
---------------------------
|
||||
The IPW2100 supports the configuration of the Power Save Protocol
|
||||
through a private wireless extension interface. The IPW2100 supports
|
||||
the following different modes:
|
||||
|
||||
off No power management. Radio is always on.
|
||||
on Automatic power management
|
||||
1-5 Different levels of power management. The higher the
|
||||
number the greater the power savings, but with an impact to
|
||||
packet latencies.
|
||||
|
||||
Power management works by powering down the radio after a certain
|
||||
interval of time has passed where no packets are passed through the
|
||||
radio. Once powered down, the radio remains in that state for a given
|
||||
period of time. For higher power savings, the interval between last
|
||||
packet processed to sleep is shorter and the sleep period is longer.
|
||||
|
||||
When the radio is asleep, the access point sending data to the station
|
||||
must buffer packets at the AP until the station wakes up and requests
|
||||
any buffered packets. If you have an AP that does not correctly support
|
||||
the PSP protocol you may experience packet loss or very poor performance
|
||||
while power management is enabled. If this is the case, you will need
|
||||
to try and find a firmware update for your AP, or disable power
|
||||
management (via `iwconfig eth1 power off`)
|
||||
|
||||
To configure the power level on the IPW2100 you use a combination of
|
||||
iwconfig and iwpriv. iwconfig is used to turn power management on, off,
|
||||
and set it to auto.
|
||||
|
||||
iwconfig eth1 power off Disables radio power down
|
||||
iwconfig eth1 power on Enables radio power management to
|
||||
last set level (defaults to AUTO)
|
||||
iwpriv eth1 set_power 0 Sets power level to AUTO and enables
|
||||
power management if not previously
|
||||
enabled.
|
||||
iwpriv eth1 set_power 1-5 Set the power level as specified,
|
||||
enabling power management if not
|
||||
previously enabled.
|
||||
|
||||
You can view the current power level setting via:
|
||||
|
||||
iwpriv eth1 get_power
|
||||
|
||||
It will return the current period or timeout that is configured as a string
|
||||
in the form of xxxx/yyyy (z) where xxxx is the timeout interval (amount of
|
||||
time after packet processing), yyyy is the period to sleep (amount of time to
|
||||
wait before powering the radio and querying the access point for buffered
|
||||
packets), and z is the 'power level'. If power management is turned off the
|
||||
xxxx/yyyy will be replaced with 'off' -- the level reported will be the active
|
||||
level if `iwconfig eth1 power on` is invoked.
|
||||
|
||||
|
||||
===========================
|
||||
7. Support
|
||||
---------------------------
|
||||
|
||||
For general development information and support,
|
||||
go to:
|
||||
|
||||
http://ipw2100.sf.net/
|
||||
|
||||
The ipw2100 1.1.0 driver and firmware can be downloaded from:
|
||||
|
||||
http://support.intel.com
|
||||
|
||||
For installation support on the ipw2100 1.1.0 driver on Linux kernels
|
||||
2.6.8 or greater, email support is available from:
|
||||
|
||||
http://supportmail.intel.com
|
||||
|
||||
===========================
|
||||
8. License
|
||||
---------------------------
|
||||
|
||||
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 the GNU General Public License (version 2) 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.
|
||||
|
||||
License Contact Information:
|
||||
James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
||||
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
||||
|
300
Documentation/networking/README.ipw2200
Normal file
300
Documentation/networking/README.ipw2200
Normal file
@ -0,0 +1,300 @@
|
||||
|
||||
Intel(R) PRO/Wireless 2915ABG Driver for Linux in support of:
|
||||
|
||||
Intel(R) PRO/Wireless 2200BG Network Connection
|
||||
Intel(R) PRO/Wireless 2915ABG Network Connection
|
||||
|
||||
Note: The Intel(R) PRO/Wireless 2915ABG Driver for Linux and Intel(R)
|
||||
PRO/Wireless 2200BG Driver for Linux is a unified driver that works on
|
||||
both hardware adapters listed above. In this document the Intel(R)
|
||||
PRO/Wireless 2915ABG Driver for Linux will be used to reference the
|
||||
unified driver.
|
||||
|
||||
Copyright (C) 2004-2005, Intel Corporation
|
||||
|
||||
README.ipw2200
|
||||
|
||||
Version: 1.0.0
|
||||
Date : January 31, 2005
|
||||
|
||||
|
||||
Index
|
||||
-----------------------------------------------
|
||||
1. Introduction
|
||||
1.1. Overview of features
|
||||
1.2. Module parameters
|
||||
1.3. Wireless Extension Private Methods
|
||||
1.4. Sysfs Helper Files
|
||||
2. About the Version Numbers
|
||||
3. Support
|
||||
4. License
|
||||
|
||||
|
||||
1. Introduction
|
||||
-----------------------------------------------
|
||||
The following sections attempt to provide a brief introduction to using
|
||||
the Intel(R) PRO/Wireless 2915ABG Driver for Linux.
|
||||
|
||||
This document is not meant to be a comprehensive manual on
|
||||
understanding or using wireless technologies, but should be sufficient
|
||||
to get you moving without wires on Linux.
|
||||
|
||||
For information on building and installing the driver, see the INSTALL
|
||||
file.
|
||||
|
||||
|
||||
1.1. Overview of Features
|
||||
-----------------------------------------------
|
||||
The current release (1.0.0) supports the following features:
|
||||
|
||||
+ BSS mode (Infrastructure, Managed)
|
||||
+ IBSS mode (Ad-Hoc)
|
||||
+ WEP (OPEN and SHARED KEY mode)
|
||||
+ 802.1x EAP via wpa_supplicant and xsupplicant
|
||||
+ Wireless Extension support
|
||||
+ Full B and G rate support (2200 and 2915)
|
||||
+ Full A rate support (2915 only)
|
||||
+ Transmit power control
|
||||
+ S state support (ACPI suspend/resume)
|
||||
+ long/short preamble support
|
||||
|
||||
|
||||
|
||||
1.2. Command Line Parameters
|
||||
-----------------------------------------------
|
||||
|
||||
Like many modules used in the Linux kernel, the Intel(R) PRO/Wireless
|
||||
2915ABG Driver for Linux allows certain configuration options to be
|
||||
provided as module parameters. The most common way to specify a module
|
||||
parameter is via the command line.
|
||||
|
||||
The general form is:
|
||||
|
||||
% modprobe ipw2200 parameter=value
|
||||
|
||||
Where the supported parameter are:
|
||||
|
||||
associate
|
||||
Set to 0 to disable the auto scan-and-associate functionality of the
|
||||
driver. If disabled, the driver will not attempt to scan
|
||||
for and associate to a network until it has been configured with
|
||||
one or more properties for the target network, for example configuring
|
||||
the network SSID. Default is 1 (auto-associate)
|
||||
|
||||
Example: % modprobe ipw2200 associate=0
|
||||
|
||||
auto_create
|
||||
Set to 0 to disable the auto creation of an Ad-Hoc network
|
||||
matching the channel and network name parameters provided.
|
||||
Default is 1.
|
||||
|
||||
channel
|
||||
channel number for association. The normal method for setting
|
||||
the channel would be to use the standard wireless tools
|
||||
(i.e. `iwconfig eth1 channel 10`), but it is useful sometimes
|
||||
to set this while debugging. Channel 0 means 'ANY'
|
||||
|
||||
debug
|
||||
If using a debug build, this is used to control the amount of debug
|
||||
info is logged. See the 'dval' and 'load' script for more info on
|
||||
how to use this (the dval and load scripts are provided as part
|
||||
of the ipw2200 development snapshot releases available from the
|
||||
SourceForge project at http://ipw2200.sf.net)
|
||||
|
||||
mode
|
||||
Can be used to set the default mode of the adapter.
|
||||
0 = Managed, 1 = Ad-Hoc
|
||||
|
||||
|
||||
1.3. Wireless Extension Private Methods
|
||||
-----------------------------------------------
|
||||
|
||||
As an interface designed to handle generic hardware, there are certain
|
||||
capabilities not exposed through the normal Wireless Tool interface. As
|
||||
such, a provision is provided for a driver to declare custom, or
|
||||
private, methods. The Intel(R) PRO/Wireless 2915ABG Driver for Linux
|
||||
defines several of these to configure various settings.
|
||||
|
||||
The general form of using the private wireless methods is:
|
||||
|
||||
% iwpriv $IFNAME method parameters
|
||||
|
||||
Where $IFNAME is the interface name the device is registered with
|
||||
(typically eth1, customized via one of the various network interface
|
||||
name managers, such as ifrename)
|
||||
|
||||
The supported private methods are:
|
||||
|
||||
get_mode
|
||||
Can be used to report out which IEEE mode the driver is
|
||||
configured to support. Example:
|
||||
|
||||
% iwpriv eth1 get_mode
|
||||
eth1 get_mode:802.11bg (6)
|
||||
|
||||
set_mode
|
||||
Can be used to configure which IEEE mode the driver will
|
||||
support.
|
||||
|
||||
Usage:
|
||||
% iwpriv eth1 set_mode {mode}
|
||||
Where {mode} is a number in the range 1-7:
|
||||
1 802.11a (2915 only)
|
||||
2 802.11b
|
||||
3 802.11ab (2915 only)
|
||||
4 802.11g
|
||||
5 802.11ag (2915 only)
|
||||
6 802.11bg
|
||||
7 802.11abg (2915 only)
|
||||
|
||||
get_preamble
|
||||
Can be used to report configuration of preamble length.
|
||||
|
||||
set_preamble
|
||||
Can be used to set the configuration of preamble length:
|
||||
|
||||
Usage:
|
||||
% iwpriv eth1 set_preamble {mode}
|
||||
Where {mode} is one of:
|
||||
1 Long preamble only
|
||||
0 Auto (long or short based on connection)
|
||||
|
||||
|
||||
1.4. Sysfs Helper Files:
|
||||
-----------------------------------------------
|
||||
|
||||
The Linux kernel provides a pseudo file system that can be used to
|
||||
access various components of the operating system. The Intel(R)
|
||||
PRO/Wireless 2915ABG Driver for Linux exposes several configuration
|
||||
parameters through this mechanism.
|
||||
|
||||
An entry in the sysfs can support reading and/or writing. You can
|
||||
typically query the contents of a sysfs entry through the use of cat,
|
||||
and can set the contents via echo. For example:
|
||||
|
||||
% cat /sys/bus/pci/drivers/ipw2200/debug_level
|
||||
|
||||
Will report the current debug level of the driver's logging subsystem
|
||||
(only available if CONFIG_IPW_DEBUG was configured when the driver was
|
||||
built).
|
||||
|
||||
You can set the debug level via:
|
||||
|
||||
% echo $VALUE > /sys/bus/pci/drivers/ipw2200/debug_level
|
||||
|
||||
Where $VALUE would be a number in the case of this sysfs entry. The
|
||||
input to sysfs files does not have to be a number. For example, the
|
||||
firmware loader used by hotplug utilizes sysfs entries for transferring
|
||||
the firmware image from user space into the driver.
|
||||
|
||||
The Intel(R) PRO/Wireless 2915ABG Driver for Linux exposes sysfs entries
|
||||
at two levels -- driver level, which apply to all instances of the
|
||||
driver (in the event that there are more than one device installed) and
|
||||
device level, which applies only to the single specific instance.
|
||||
|
||||
|
||||
1.4.1 Driver Level Sysfs Helper Files
|
||||
-----------------------------------------------
|
||||
|
||||
For the driver level files, look in /sys/bus/pci/drivers/ipw2200/
|
||||
|
||||
debug_level
|
||||
|
||||
This controls the same global as the 'debug' module parameter
|
||||
|
||||
|
||||
1.4.2 Device Level Sysfs Helper Files
|
||||
-----------------------------------------------
|
||||
|
||||
For the device level files, look in
|
||||
|
||||
/sys/bus/pci/drivers/ipw2200/{PCI-ID}/
|
||||
|
||||
For example:
|
||||
/sys/bus/pci/drivers/ipw2200/0000:02:01.0
|
||||
|
||||
For the device level files, see /sys/bus/pci/[drivers/ipw2200:
|
||||
|
||||
rf_kill
|
||||
read -
|
||||
0 = RF kill not enabled (radio on)
|
||||
1 = SW based RF kill active (radio off)
|
||||
2 = HW based RF kill active (radio off)
|
||||
3 = Both HW and SW RF kill active (radio off)
|
||||
write -
|
||||
0 = If SW based RF kill active, turn the radio back on
|
||||
1 = If radio is on, activate SW based RF kill
|
||||
|
||||
NOTE: If you enable the SW based RF kill and then toggle the HW
|
||||
based RF kill from ON -> OFF -> ON, the radio will NOT come back on
|
||||
|
||||
ucode
|
||||
read-only access to the ucode version number
|
||||
|
||||
|
||||
2. About the Version Numbers
|
||||
-----------------------------------------------
|
||||
|
||||
Due to the nature of open source development projects, there are
|
||||
frequently changes being incorporated that have not gone through
|
||||
a complete validation process. These changes are incorporated into
|
||||
development snapshot releases.
|
||||
|
||||
Releases are numbered with a three level scheme:
|
||||
|
||||
major.minor.development
|
||||
|
||||
Any version where the 'development' portion is 0 (for example
|
||||
1.0.0, 1.1.0, etc.) indicates a stable version that will be made
|
||||
available for kernel inclusion.
|
||||
|
||||
Any version where the 'development' portion is not a 0 (for
|
||||
example 1.0.1, 1.1.5, etc.) indicates a development version that is
|
||||
being made available for testing and cutting edge users. The stability
|
||||
and functionality of the development releases are not know. We make
|
||||
efforts to try and keep all snapshots reasonably stable, but due to the
|
||||
frequency of their release, and the desire to get those releases
|
||||
available as quickly as possible, unknown anomalies should be expected.
|
||||
|
||||
The major version number will be incremented when significant changes
|
||||
are made to the driver. Currently, there are no major changes planned.
|
||||
|
||||
|
||||
3. Support
|
||||
-----------------------------------------------
|
||||
|
||||
For installation support of the 1.0.0 version, you can contact
|
||||
http://supportmail.intel.com, or you can use the open source project
|
||||
support.
|
||||
|
||||
For general information and support, go to:
|
||||
|
||||
http://ipw2200.sf.net/
|
||||
|
||||
|
||||
4. License
|
||||
-----------------------------------------------
|
||||
|
||||
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 the GNU General Public License version 2 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
|
||||
|
||||
|
@ -991,6 +991,13 @@ M: mike.miller@hp.com
|
||||
L: iss_storagedev@hp.com
|
||||
S: Supported
|
||||
|
||||
HOST AP DRIVER
|
||||
P: Jouni Malinen
|
||||
M: jkmaline@cc.hut.fi
|
||||
L: hostap@shmoo.com
|
||||
W: http://hostap.epitest.fi/
|
||||
S: Maintained
|
||||
|
||||
HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
|
||||
P: Jaroslav Kysela
|
||||
M: perex@suse.cz
|
||||
|
@ -137,6 +137,110 @@ config PCMCIA_RAYCS
|
||||
comment "Wireless 802.11b ISA/PCI cards support"
|
||||
depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
|
||||
|
||||
config IPW2100
|
||||
tristate "Intel PRO/Wireless 2100 Network Connection"
|
||||
depends on NET_RADIO && PCI && IEEE80211
|
||||
select FW_LOADER
|
||||
---help---
|
||||
A driver for the Intel PRO/Wireless 2100 Network
|
||||
Connection 802.11b wireless network adapter.
|
||||
|
||||
See <file:Documentation/networking/README.ipw2100> for information on
|
||||
the capabilities currently enabled in this driver and for tips
|
||||
for debugging issues and problems.
|
||||
|
||||
In order to use this driver, you will need a firmware image for it.
|
||||
You can obtain the firmware from
|
||||
<http://ipw2100.sf.net/>. Once you have the firmware image, you
|
||||
will need to place it in /etc/firmware.
|
||||
|
||||
You will also very likely need the Wireless Tools in order to
|
||||
configure your card:
|
||||
|
||||
<http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
|
||||
|
||||
If you want to compile the driver as a module ( = code which can be
|
||||
inserted in and remvoed from the running kernel whenever you want),
|
||||
say M here and read <file:Documentation/modules.txt>. The module
|
||||
will be called ipw2100.ko.
|
||||
|
||||
config IPW2100_MONITOR
|
||||
bool "Enable promiscuous mode"
|
||||
depends on IPW2100
|
||||
---help---
|
||||
Enables promiscuous/monitor mode support for the ipw2100 driver.
|
||||
With this feature compiled into the driver, you can switch to
|
||||
promiscuous mode via the Wireless Tool's Monitor mode. While in this
|
||||
mode, no packets can be sent.
|
||||
|
||||
config IPW_DEBUG
|
||||
bool "Enable full debugging output in IPW2100 module."
|
||||
depends on IPW2100
|
||||
---help---
|
||||
This option will enable debug tracing output for the IPW2100.
|
||||
|
||||
This will result in the kernel module being ~60k larger. You can
|
||||
control which debug output is sent to the kernel log by setting the
|
||||
value in
|
||||
|
||||
/sys/bus/pci/drivers/ipw2100/debug_level
|
||||
|
||||
This entry will only exist if this option is enabled.
|
||||
|
||||
If you are not trying to debug or develop the IPW2100 driver, you
|
||||
most likely want to say N here.
|
||||
|
||||
config IPW2200
|
||||
tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
|
||||
depends on IEEE80211 && PCI
|
||||
select FW_LOADER
|
||||
---help---
|
||||
A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
|
||||
Connection adapters.
|
||||
|
||||
See <file:Documentation/networking/README.ipw2200> for
|
||||
information on the capabilities currently enabled in this
|
||||
driver and for tips for debugging issues and problems.
|
||||
|
||||
In order to use this driver, you will need a firmware image for it.
|
||||
You can obtain the firmware from
|
||||
<http://ipw2200.sf.net/>. See the above referenced README.ipw2200
|
||||
for information on where to install the firmare images.
|
||||
|
||||
You will also very likely need the Wireless Tools in order to
|
||||
configure your card:
|
||||
|
||||
<http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
|
||||
|
||||
If you want to compile the driver as a module ( = code which can be
|
||||
inserted in and remvoed from the running kernel whenever you want),
|
||||
say M here and read <file:Documentation/modules.txt>. The module
|
||||
will be called ipw2200.ko.
|
||||
|
||||
config IPW_DEBUG
|
||||
bool "Enable full debugging output in IPW2200 module."
|
||||
depends on IPW2200
|
||||
---help---
|
||||
This option will enable debug tracing output for the IPW2200.
|
||||
|
||||
This will result in the kernel module being ~100k larger. You can
|
||||
control which debug output is sent to the kernel log by setting the
|
||||
value in
|
||||
|
||||
/sys/bus/pci/drivers/ipw2200/debug_level
|
||||
|
||||
This entry will only exist if this option is enabled.
|
||||
|
||||
To set a value, simply echo an 8-byte hex value to the same file:
|
||||
|
||||
% echo 0x00000FFO > /sys/bus/pci/drivers/ipw2200/debug_level
|
||||
|
||||
You can find the list of debug mask values in
|
||||
drivers/net/wireless/ipw2200.h
|
||||
|
||||
If you are not trying to debug or develop the IPW2200 driver, you
|
||||
most likely want to say N here.
|
||||
|
||||
config AIRO
|
||||
tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
|
||||
depends on NET_RADIO && ISA && (PCI || BROKEN)
|
||||
@ -355,6 +459,8 @@ config PRISM54
|
||||
say M here and read <file:Documentation/modules.txt>. The module
|
||||
will be called prism54.ko.
|
||||
|
||||
source "drivers/net/wireless/hostap/Kconfig"
|
||||
|
||||
# yes, this works even when no drivers are selected
|
||||
config NET_WIRELESS
|
||||
bool
|
||||
|
@ -2,6 +2,10 @@
|
||||
# Makefile for the Linux Wireless network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IPW2100) += ipw2100.o
|
||||
|
||||
obj-$(CONFIG_IPW2200) += ipw2200.o
|
||||
|
||||
obj-$(CONFIG_STRIP) += strip.o
|
||||
obj-$(CONFIG_ARLAN) += arlan.o
|
||||
|
||||
@ -28,6 +32,8 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
|
||||
|
||||
obj-$(CONFIG_PRISM54) += prism54/
|
||||
|
||||
obj-$(CONFIG_HOSTAP) += hostap/
|
||||
|
||||
# 16-bit wireless PCMCIA client drivers
|
||||
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
|
||||
obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
|
||||
|
@ -1040,7 +1040,7 @@ typedef struct {
|
||||
u16 status;
|
||||
} WifiCtlHdr;
|
||||
|
||||
WifiCtlHdr wifictlhdr8023 = {
|
||||
static WifiCtlHdr wifictlhdr8023 = {
|
||||
.ctlhdr = {
|
||||
.ctl = HOST_DONT_RLSE,
|
||||
}
|
||||
@ -1111,13 +1111,13 @@ static int airo_thread(void *data);
|
||||
static void timer_func( struct net_device *dev );
|
||||
static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
|
||||
#ifdef WIRELESS_EXT
|
||||
struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
|
||||
static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
|
||||
static void airo_read_wireless_stats (struct airo_info *local);
|
||||
#endif /* WIRELESS_EXT */
|
||||
#ifdef CISCO_EXT
|
||||
static int readrids(struct net_device *dev, aironet_ioctl *comp);
|
||||
static int writerids(struct net_device *dev, aironet_ioctl *comp);
|
||||
int flashcard(struct net_device *dev, aironet_ioctl *comp);
|
||||
static int flashcard(struct net_device *dev, aironet_ioctl *comp);
|
||||
#endif /* CISCO_EXT */
|
||||
#ifdef MICSUPPORT
|
||||
static void micinit(struct airo_info *ai);
|
||||
@ -1226,6 +1226,12 @@ static int setup_proc_entry( struct net_device *dev,
|
||||
static int takedown_proc_entry( struct net_device *dev,
|
||||
struct airo_info *apriv );
|
||||
|
||||
static int cmdreset(struct airo_info *ai);
|
||||
static int setflashmode (struct airo_info *ai);
|
||||
static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
|
||||
static int flashputbuf(struct airo_info *ai);
|
||||
static int flashrestart(struct airo_info *ai,struct net_device *dev);
|
||||
|
||||
#ifdef MICSUPPORT
|
||||
/***********************************************************************
|
||||
* MIC ROUTINES *
|
||||
@ -1234,10 +1240,11 @@ static int takedown_proc_entry( struct net_device *dev,
|
||||
|
||||
static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
|
||||
static void MoveWindow(miccntx *context, u32 micSeq);
|
||||
void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
|
||||
void emmh32_init(emmh32_context *context);
|
||||
void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
|
||||
void emmh32_final(emmh32_context *context, u8 digest[4]);
|
||||
static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
|
||||
static void emmh32_init(emmh32_context *context);
|
||||
static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
|
||||
static void emmh32_final(emmh32_context *context, u8 digest[4]);
|
||||
static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
|
||||
|
||||
/* micinit - Initialize mic seed */
|
||||
|
||||
@ -1315,7 +1322,7 @@ static int micsetup(struct airo_info *ai) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
char micsnap[]= {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
|
||||
static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
|
||||
|
||||
/*===========================================================================
|
||||
* Description: Mic a packet
|
||||
@ -1570,7 +1577,7 @@ static void MoveWindow(miccntx *context, u32 micSeq)
|
||||
static unsigned char aes_counter[16];
|
||||
|
||||
/* expand the key to fill the MMH coefficient array */
|
||||
void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
|
||||
static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
|
||||
{
|
||||
/* take the keying material, expand if necessary, truncate at 16-bytes */
|
||||
/* run through AES counter mode to generate context->coeff[] */
|
||||
@ -1602,7 +1609,7 @@ void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto
|
||||
}
|
||||
|
||||
/* prepare for calculation of a new mic */
|
||||
void emmh32_init(emmh32_context *context)
|
||||
static void emmh32_init(emmh32_context *context)
|
||||
{
|
||||
/* prepare for new mic calculation */
|
||||
context->accum = 0;
|
||||
@ -1610,7 +1617,7 @@ void emmh32_init(emmh32_context *context)
|
||||
}
|
||||
|
||||
/* add some bytes to the mic calculation */
|
||||
void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
|
||||
static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
|
||||
{
|
||||
int coeff_position, byte_position;
|
||||
|
||||
@ -1652,7 +1659,7 @@ void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
|
||||
static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
|
||||
|
||||
/* calculate the mic */
|
||||
void emmh32_final(emmh32_context *context, u8 digest[4])
|
||||
static void emmh32_final(emmh32_context *context, u8 digest[4])
|
||||
{
|
||||
int coeff_position, byte_position;
|
||||
u32 val;
|
||||
@ -2255,7 +2262,7 @@ static void airo_read_stats(struct airo_info *ai) {
|
||||
ai->stats.rx_fifo_errors = vals[0];
|
||||
}
|
||||
|
||||
struct net_device_stats *airo_get_stats(struct net_device *dev)
|
||||
static struct net_device_stats *airo_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct airo_info *local = dev->priv;
|
||||
|
||||
@ -2414,7 +2421,7 @@ EXPORT_SYMBOL(stop_airo_card);
|
||||
|
||||
static int add_airo_dev( struct net_device *dev );
|
||||
|
||||
int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
|
||||
static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
|
||||
{
|
||||
memcpy(haddr, skb->mac.raw + 10, ETH_ALEN);
|
||||
return ETH_ALEN;
|
||||
@ -2681,7 +2688,7 @@ static struct net_device *init_wifidev(struct airo_info *ai,
|
||||
return dev;
|
||||
}
|
||||
|
||||
int reset_card( struct net_device *dev , int lock) {
|
||||
static int reset_card( struct net_device *dev , int lock) {
|
||||
struct airo_info *ai = dev->priv;
|
||||
|
||||
if (lock && down_interruptible(&ai->sem))
|
||||
@ -2696,9 +2703,9 @@ int reset_card( struct net_device *dev , int lock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct net_device *_init_airo_card( unsigned short irq, int port,
|
||||
int is_pcmcia, struct pci_dev *pci,
|
||||
struct device *dmdev )
|
||||
static struct net_device *_init_airo_card( unsigned short irq, int port,
|
||||
int is_pcmcia, struct pci_dev *pci,
|
||||
struct device *dmdev )
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct airo_info *ai;
|
||||
@ -7235,7 +7242,7 @@ static void airo_read_wireless_stats(struct airo_info *local)
|
||||
local->wstats.miss.beacon = vals[34];
|
||||
}
|
||||
|
||||
struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
|
||||
static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
|
||||
{
|
||||
struct airo_info *local = dev->priv;
|
||||
|
||||
@ -7450,14 +7457,8 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
|
||||
* Flash command switch table
|
||||
*/
|
||||
|
||||
int flashcard(struct net_device *dev, aironet_ioctl *comp) {
|
||||
static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
|
||||
int z;
|
||||
int cmdreset(struct airo_info *);
|
||||
int setflashmode(struct airo_info *);
|
||||
int flashgchar(struct airo_info *,int,int);
|
||||
int flashpchar(struct airo_info *,int,int);
|
||||
int flashputbuf(struct airo_info *);
|
||||
int flashrestart(struct airo_info *,struct net_device *);
|
||||
|
||||
/* Only super-user can modify flash */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
@ -7515,7 +7516,7 @@ int flashcard(struct net_device *dev, aironet_ioctl *comp) {
|
||||
* card.
|
||||
*/
|
||||
|
||||
int cmdreset(struct airo_info *ai) {
|
||||
static int cmdreset(struct airo_info *ai) {
|
||||
disable_MAC(ai, 1);
|
||||
|
||||
if(!waitbusy (ai)){
|
||||
@ -7539,7 +7540,7 @@ int cmdreset(struct airo_info *ai) {
|
||||
* mode
|
||||
*/
|
||||
|
||||
int setflashmode (struct airo_info *ai) {
|
||||
static int setflashmode (struct airo_info *ai) {
|
||||
set_bit (FLAG_FLASHING, &ai->flags);
|
||||
|
||||
OUT4500(ai, SWS0, FLASH_COMMAND);
|
||||
@ -7566,7 +7567,7 @@ int setflashmode (struct airo_info *ai) {
|
||||
* x 50us for echo .
|
||||
*/
|
||||
|
||||
int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
|
||||
static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
|
||||
int echo;
|
||||
int waittime;
|
||||
|
||||
@ -7606,7 +7607,7 @@ int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
|
||||
* Get a character from the card matching matchbyte
|
||||
* Step 3)
|
||||
*/
|
||||
int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
|
||||
static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
|
||||
int rchar;
|
||||
unsigned char rbyte=0;
|
||||
|
||||
@ -7637,7 +7638,7 @@ int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
|
||||
* send to the card
|
||||
*/
|
||||
|
||||
int flashputbuf(struct airo_info *ai){
|
||||
static int flashputbuf(struct airo_info *ai){
|
||||
int nwords;
|
||||
|
||||
/* Write stuff */
|
||||
@ -7659,7 +7660,7 @@ int flashputbuf(struct airo_info *ai){
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int flashrestart(struct airo_info *ai,struct net_device *dev){
|
||||
static int flashrestart(struct airo_info *ai,struct net_device *dev){
|
||||
int i,status;
|
||||
|
||||
ssleep(1); /* Added 12/7/00 */
|
||||
|
@ -68,7 +68,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/firmware.h>
|
||||
#include "ieee802_11.h"
|
||||
#include <net/ieee80211.h>
|
||||
#include "atmel.h"
|
||||
|
||||
#define DRIVER_MAJOR 0
|
||||
@ -618,12 +618,12 @@ static int atmel_lock_mac(struct atmel_private *priv);
|
||||
static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data);
|
||||
static void atmel_command_irq(struct atmel_private *priv);
|
||||
static int atmel_validate_channel(struct atmel_private *priv, int channel);
|
||||
static void atmel_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header,
|
||||
static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header,
|
||||
u16 frame_len, u8 rssi);
|
||||
static void atmel_management_timer(u_long a);
|
||||
static void atmel_send_command(struct atmel_private *priv, int command, void *cmd, int cmd_size);
|
||||
static int atmel_send_command_wait(struct atmel_private *priv, int command, void *cmd, int cmd_size);
|
||||
static void atmel_transmit_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header,
|
||||
static void atmel_transmit_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header,
|
||||
u8 *body, int body_len);
|
||||
|
||||
static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index);
|
||||
@ -827,7 +827,7 @@ static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, u16 l
|
||||
static int start_tx (struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct atmel_private *priv = netdev_priv(dev);
|
||||
struct ieee802_11_hdr header;
|
||||
struct ieee80211_hdr header;
|
||||
unsigned long flags;
|
||||
u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
|
||||
u8 SNAP_RFC1024[6] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
|
||||
@ -863,17 +863,17 @@ static int start_tx (struct sk_buff *skb, struct net_device *dev)
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame_ctl = IEEE802_11_FTYPE_DATA;
|
||||
frame_ctl = IEEE80211_FTYPE_DATA;
|
||||
header.duration_id = 0;
|
||||
header.seq_ctl = 0;
|
||||
if (priv->wep_is_on)
|
||||
frame_ctl |= IEEE802_11_FCTL_WEP;
|
||||
frame_ctl |= IEEE80211_FCTL_PROTECTED;
|
||||
if (priv->operating_mode == IW_MODE_ADHOC) {
|
||||
memcpy(&header.addr1, skb->data, 6);
|
||||
memcpy(&header.addr2, dev->dev_addr, 6);
|
||||
memcpy(&header.addr3, priv->BSSID, 6);
|
||||
} else {
|
||||
frame_ctl |= IEEE802_11_FCTL_TODS;
|
||||
frame_ctl |= IEEE80211_FCTL_TODS;
|
||||
memcpy(&header.addr1, priv->CurrentBSSID, 6);
|
||||
memcpy(&header.addr2, dev->dev_addr, 6);
|
||||
memcpy(&header.addr3, skb->data, 6);
|
||||
@ -902,7 +902,7 @@ static int start_tx (struct sk_buff *skb, struct net_device *dev)
|
||||
}
|
||||
|
||||
static void atmel_transmit_management_frame(struct atmel_private *priv,
|
||||
struct ieee802_11_hdr *header,
|
||||
struct ieee80211_hdr *header,
|
||||
u8 *body, int body_len)
|
||||
{
|
||||
u16 buff;
|
||||
@ -917,7 +917,7 @@ static void atmel_transmit_management_frame(struct atmel_private *priv,
|
||||
tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT);
|
||||
}
|
||||
|
||||
static void fast_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *header,
|
||||
static void fast_rx_path(struct atmel_private *priv, struct ieee80211_hdr *header,
|
||||
u16 msdu_size, u16 rx_packet_loc, u32 crc)
|
||||
{
|
||||
/* fast path: unfragmented packet copy directly into skbuf */
|
||||
@ -955,7 +955,7 @@ static void fast_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *head
|
||||
}
|
||||
|
||||
memcpy(skbp, header->addr1, 6); /* destination address */
|
||||
if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS)
|
||||
if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
|
||||
memcpy(&skbp[6], header->addr3, 6);
|
||||
else
|
||||
memcpy(&skbp[6], header->addr2, 6); /* source address */
|
||||
@ -990,14 +990,14 @@ static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size)
|
||||
return (crc ^ 0xffffffff) == netcrc;
|
||||
}
|
||||
|
||||
static void frag_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *header,
|
||||
static void frag_rx_path(struct atmel_private *priv, struct ieee80211_hdr *header,
|
||||
u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, u8 frag_no, int more_frags)
|
||||
{
|
||||
u8 mac4[6];
|
||||
u8 source[6];
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS)
|
||||
if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
|
||||
memcpy(source, header->addr3, 6);
|
||||
else
|
||||
memcpy(source, header->addr2, 6);
|
||||
@ -1082,7 +1082,7 @@ static void frag_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *head
|
||||
static void rx_done_irq(struct atmel_private *priv)
|
||||
{
|
||||
int i;
|
||||
struct ieee802_11_hdr header;
|
||||
struct ieee80211_hdr header;
|
||||
|
||||
for (i = 0;
|
||||
atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
|
||||
@ -1117,7 +1117,7 @@ static void rx_done_irq(struct atmel_private *priv)
|
||||
/* probe for CRC use here if needed once five packets have arrived with
|
||||
the same crc status, we assume we know what's happening and stop probing */
|
||||
if (priv->probe_crc) {
|
||||
if (!priv->wep_is_on || !(frame_ctl & IEEE802_11_FCTL_WEP)) {
|
||||
if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) {
|
||||
priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size);
|
||||
} else {
|
||||
priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24);
|
||||
@ -1132,16 +1132,16 @@ static void rx_done_irq(struct atmel_private *priv)
|
||||
}
|
||||
|
||||
/* don't CRC header when WEP in use */
|
||||
if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE802_11_FCTL_WEP))) {
|
||||
if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) {
|
||||
crc = crc32_le(0xffffffff, (unsigned char *)&header, 24);
|
||||
}
|
||||
msdu_size -= 24; /* header */
|
||||
|
||||
if ((frame_ctl & IEEE802_11_FCTL_FTYPE) == IEEE802_11_FTYPE_DATA) {
|
||||
if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
|
||||
|
||||
int more_fragments = frame_ctl & IEEE802_11_FCTL_MOREFRAGS;
|
||||
u8 packet_fragment_no = seq_control & IEEE802_11_SCTL_FRAG;
|
||||
u16 packet_sequence_no = (seq_control & IEEE802_11_SCTL_SEQ) >> 4;
|
||||
int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS;
|
||||
u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG;
|
||||
u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4;
|
||||
|
||||
if (!more_fragments && packet_fragment_no == 0 ) {
|
||||
fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc);
|
||||
@ -1151,7 +1151,7 @@ static void rx_done_irq(struct atmel_private *priv)
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame_ctl & IEEE802_11_FCTL_FTYPE) == IEEE802_11_FTYPE_MGMT) {
|
||||
if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
|
||||
/* copy rest of packet into buffer */
|
||||
atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);
|
||||
|
||||
@ -2663,10 +2663,10 @@ static void handle_beacon_probe(struct atmel_private *priv, u16 capability, u8 c
|
||||
|
||||
static void send_authentication_request(struct atmel_private *priv, u8 *challenge, int challenge_len)
|
||||
{
|
||||
struct ieee802_11_hdr header;
|
||||
struct ieee80211_hdr header;
|
||||
struct auth_body auth;
|
||||
|
||||
header.frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT | IEEE802_11_STYPE_AUTH);
|
||||
header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
|
||||
header.duration_id = cpu_to_le16(0x8000);
|
||||
header.seq_ctl = 0;
|
||||
memcpy(header.addr1, priv->CurrentBSSID, 6);
|
||||
@ -2677,7 +2677,7 @@ static void send_authentication_request(struct atmel_private *priv, u8 *challeng
|
||||
auth.alg = cpu_to_le16(C80211_MGMT_AAN_SHAREDKEY);
|
||||
/* no WEP for authentication frames with TrSeqNo 1 */
|
||||
if (priv->CurrentAuthentTransactionSeqNum != 1)
|
||||
header.frame_ctl |= cpu_to_le16(IEEE802_11_FCTL_WEP);
|
||||
header.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
||||
} else {
|
||||
auth.alg = cpu_to_le16(C80211_MGMT_AAN_OPENSYSTEM);
|
||||
}
|
||||
@ -2701,7 +2701,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
|
||||
{
|
||||
u8 *ssid_el_p;
|
||||
int bodysize;
|
||||
struct ieee802_11_hdr header;
|
||||
struct ieee80211_hdr header;
|
||||
struct ass_req_format {
|
||||
u16 capability;
|
||||
u16 listen_interval;
|
||||
@ -2714,8 +2714,8 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
|
||||
u8 rates[4];
|
||||
} body;
|
||||
|
||||
header.frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT |
|
||||
(is_reassoc ? IEEE802_11_STYPE_REASSOC_REQ : IEEE802_11_STYPE_ASSOC_REQ));
|
||||
header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
(is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ));
|
||||
header.duration_id = cpu_to_le16(0x8000);
|
||||
header.seq_ctl = 0;
|
||||
|
||||
@ -2751,9 +2751,9 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
|
||||
atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize);
|
||||
}
|
||||
|
||||
static int is_frame_from_current_bss(struct atmel_private *priv, struct ieee802_11_hdr *header)
|
||||
static int is_frame_from_current_bss(struct atmel_private *priv, struct ieee80211_hdr *header)
|
||||
{
|
||||
if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS)
|
||||
if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
|
||||
return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0;
|
||||
else
|
||||
return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0;
|
||||
@ -2801,7 +2801,7 @@ static int retrieve_bss(struct atmel_private *priv)
|
||||
}
|
||||
|
||||
|
||||
static void store_bss_info(struct atmel_private *priv, struct ieee802_11_hdr *header,
|
||||
static void store_bss_info(struct atmel_private *priv, struct ieee80211_hdr *header,
|
||||
u16 capability, u16 beacon_period, u8 channel, u8 rssi,
|
||||
u8 ssid_len, u8 *ssid, int is_beacon)
|
||||
{
|
||||
@ -3085,12 +3085,12 @@ static void atmel_smooth_qual(struct atmel_private *priv)
|
||||
}
|
||||
|
||||
/* deals with incoming managment frames. */
|
||||
static void atmel_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header,
|
||||
static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header,
|
||||
u16 frame_len, u8 rssi)
|
||||
{
|
||||
u16 subtype;
|
||||
|
||||
switch (subtype = le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_STYPE) {
|
||||
switch (subtype = le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_STYPE) {
|
||||
case C80211_SUBTYPE_MGMT_BEACON :
|
||||
case C80211_SUBTYPE_MGMT_ProbeResponse:
|
||||
|
||||
|
71
drivers/net/wireless/hostap/Kconfig
Normal file
71
drivers/net/wireless/hostap/Kconfig
Normal file
@ -0,0 +1,71 @@
|
||||
config HOSTAP
|
||||
tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
|
||||
depends on NET_RADIO
|
||||
---help---
|
||||
Shared driver code for IEEE 802.11b wireless cards based on
|
||||
Intersil Prism2/2.5/3 chipset. This driver supports so called
|
||||
Host AP mode that allows the card to act as an IEEE 802.11
|
||||
access point.
|
||||
|
||||
See <http://hostap.epitest.fi/> for more information about the
|
||||
Host AP driver configuration and tools. This site includes
|
||||
information and tools (hostapd and wpa_supplicant) for WPA/WPA2
|
||||
support.
|
||||
|
||||
This option includes the base Host AP driver code that is shared by
|
||||
different hardware models. You will also need to enable support for
|
||||
PLX/PCI/CS version of the driver to actually use the driver.
|
||||
|
||||
The driver can be compiled as a module and it will be called
|
||||
"hostap.ko".
|
||||
|
||||
config HOSTAP_FIRMWARE
|
||||
bool "Support downloading firmware images with Host AP driver"
|
||||
depends on HOSTAP
|
||||
---help---
|
||||
Configure Host AP driver to include support for firmware image
|
||||
download. Current version supports only downloading to volatile, i.e.,
|
||||
RAM memory. Flash upgrade is not yet supported.
|
||||
|
||||
Firmware image downloading needs user space tool, prism2_srec. It is
|
||||
available from http://hostap.epitest.fi/.
|
||||
|
||||
config HOSTAP_PLX
|
||||
tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors"
|
||||
depends on PCI && HOSTAP
|
||||
---help---
|
||||
Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based
|
||||
PCI adaptors.
|
||||
|
||||
"Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
|
||||
driver and its help text includes more information about the Host AP
|
||||
driver.
|
||||
|
||||
The driver can be compiled as a module and will be named
|
||||
"hostap_plx.ko".
|
||||
|
||||
config HOSTAP_PCI
|
||||
tristate "Host AP driver for Prism2.5 PCI adaptors"
|
||||
depends on PCI && HOSTAP
|
||||
---help---
|
||||
Host AP driver's version for Prism2.5 PCI adaptors.
|
||||
|
||||
"Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
|
||||
driver and its help text includes more information about the Host AP
|
||||
driver.
|
||||
|
||||
The driver can be compiled as a module and will be named
|
||||
"hostap_pci.ko".
|
||||
|
||||
config HOSTAP_CS
|
||||
tristate "Host AP driver for Prism2/2.5/3 PC Cards"
|
||||
depends on PCMCIA!=n && HOSTAP
|
||||
---help---
|
||||
Host AP driver's version for Prism2/2.5/3 PC Cards.
|
||||
|
||||
"Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
|
||||
driver and its help text includes more information about the Host AP
|
||||
driver.
|
||||
|
||||
The driver can be compiled as a module and will be named
|
||||
"hostap_cs.ko".
|
5
drivers/net/wireless/hostap/Makefile
Normal file
5
drivers/net/wireless/hostap/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
obj-$(CONFIG_HOSTAP) += hostap.o
|
||||
|
||||
obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o
|
||||
obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o
|
||||
obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o
|
1198
drivers/net/wireless/hostap/hostap.c
Normal file
1198
drivers/net/wireless/hostap/hostap.c
Normal file
File diff suppressed because it is too large
Load Diff
57
drivers/net/wireless/hostap/hostap.h
Normal file
57
drivers/net/wireless/hostap/hostap.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef HOSTAP_H
|
||||
#define HOSTAP_H
|
||||
|
||||
/* hostap.c */
|
||||
|
||||
extern struct proc_dir_entry *hostap_proc;
|
||||
|
||||
u16 hostap_tx_callback_register(local_info_t *local,
|
||||
void (*func)(struct sk_buff *, int ok, void *),
|
||||
void *data);
|
||||
int hostap_tx_callback_unregister(local_info_t *local, u16 idx);
|
||||
int hostap_set_word(struct net_device *dev, int rid, u16 val);
|
||||
int hostap_set_string(struct net_device *dev, int rid, const char *val);
|
||||
u16 hostap_get_porttype(local_info_t *local);
|
||||
int hostap_set_encryption(local_info_t *local);
|
||||
int hostap_set_antsel(local_info_t *local);
|
||||
int hostap_set_roaming(local_info_t *local);
|
||||
int hostap_set_auth_algs(local_info_t *local);
|
||||
void hostap_dump_rx_header(const char *name,
|
||||
const struct hfa384x_rx_frame *rx);
|
||||
void hostap_dump_tx_header(const char *name,
|
||||
const struct hfa384x_tx_frame *tx);
|
||||
int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr);
|
||||
int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr);
|
||||
int hostap_80211_get_hdrlen(u16 fc);
|
||||
struct net_device_stats *hostap_get_stats(struct net_device *dev);
|
||||
void hostap_setup_dev(struct net_device *dev, local_info_t *local,
|
||||
int main_dev);
|
||||
void hostap_set_multicast_list_queue(void *data);
|
||||
int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked);
|
||||
int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked);
|
||||
void hostap_cleanup(local_info_t *local);
|
||||
void hostap_cleanup_handler(void *data);
|
||||
struct net_device * hostap_add_interface(struct local_info *local,
|
||||
int type, int rtnl_locked,
|
||||
const char *prefix, const char *name);
|
||||
void hostap_remove_interface(struct net_device *dev, int rtnl_locked,
|
||||
int remove_from_list);
|
||||
int prism2_update_comms_qual(struct net_device *dev);
|
||||
int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype,
|
||||
u8 *body, size_t bodylen);
|
||||
int prism2_sta_deauth(local_info_t *local, u16 reason);
|
||||
|
||||
|
||||
/* hostap_proc.c */
|
||||
|
||||
void hostap_init_proc(local_info_t *local);
|
||||
void hostap_remove_proc(local_info_t *local);
|
||||
|
||||
|
||||
/* hostap_info.c */
|
||||
|
||||
void hostap_info_init(local_info_t *local);
|
||||
void hostap_info_process(local_info_t *local, struct sk_buff *skb);
|
||||
|
||||
|
||||
#endif /* HOSTAP_H */
|
96
drivers/net/wireless/hostap/hostap_80211.h
Normal file
96
drivers/net/wireless/hostap/hostap_80211.h
Normal file
@ -0,0 +1,96 @@
|
||||
#ifndef HOSTAP_80211_H
|
||||
#define HOSTAP_80211_H
|
||||
|
||||
struct hostap_ieee80211_mgmt {
|
||||
u16 frame_control;
|
||||
u16 duration;
|
||||
u8 da[6];
|
||||
u8 sa[6];
|
||||
u8 bssid[6];
|
||||
u16 seq_ctrl;
|
||||
union {
|
||||
struct {
|
||||
u16 auth_alg;
|
||||
u16 auth_transaction;
|
||||
u16 status_code;
|
||||
/* possibly followed by Challenge text */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) auth;
|
||||
struct {
|
||||
u16 reason_code;
|
||||
} __attribute__ ((packed)) deauth;
|
||||
struct {
|
||||
u16 capab_info;
|
||||
u16 listen_interval;
|
||||
/* followed by SSID and Supported rates */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) assoc_req;
|
||||
struct {
|
||||
u16 capab_info;
|
||||
u16 status_code;
|
||||
u16 aid;
|
||||
/* followed by Supported rates */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) assoc_resp, reassoc_resp;
|
||||
struct {
|
||||
u16 capab_info;
|
||||
u16 listen_interval;
|
||||
u8 current_ap[6];
|
||||
/* followed by SSID and Supported rates */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) reassoc_req;
|
||||
struct {
|
||||
u16 reason_code;
|
||||
} __attribute__ ((packed)) disassoc;
|
||||
struct {
|
||||
} __attribute__ ((packed)) probe_req;
|
||||
struct {
|
||||
u8 timestamp[8];
|
||||
u16 beacon_int;
|
||||
u16 capab_info;
|
||||
/* followed by some of SSID, Supported rates,
|
||||
* FH Params, DS Params, CF Params, IBSS Params, TIM */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) beacon, probe_resp;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
#define IEEE80211_MGMT_HDR_LEN 24
|
||||
#define IEEE80211_DATA_HDR3_LEN 24
|
||||
#define IEEE80211_DATA_HDR4_LEN 30
|
||||
|
||||
|
||||
struct hostap_80211_rx_status {
|
||||
u32 mac_time;
|
||||
u8 signal;
|
||||
u8 noise;
|
||||
u16 rate; /* in 100 kbps */
|
||||
};
|
||||
|
||||
|
||||
void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
|
||||
|
||||
/* prism2_rx_80211 'type' argument */
|
||||
enum {
|
||||
PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC,
|
||||
PRISM2_RX_NULLFUNC_ACK
|
||||
};
|
||||
|
||||
int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats, int type);
|
||||
void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
|
||||
void hostap_dump_tx_80211(const char *name, struct sk_buff *skb);
|
||||
int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
|
||||
struct ieee80211_crypt_data *crypt);
|
||||
int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
|
||||
#endif /* HOSTAP_80211_H */
|
1091
drivers/net/wireless/hostap/hostap_80211_rx.c
Normal file
1091
drivers/net/wireless/hostap/hostap_80211_rx.c
Normal file
File diff suppressed because it is too large
Load Diff
524
drivers/net/wireless/hostap/hostap_80211_tx.c
Normal file
524
drivers/net/wireless/hostap/hostap_80211_tx.c
Normal file
@ -0,0 +1,524 @@
|
||||
void hostap_dump_tx_80211(const char *name, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_hdr *hdr;
|
||||
u16 fc;
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
|
||||
printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n",
|
||||
name, skb->len, jiffies);
|
||||
|
||||
if (skb->len < 2)
|
||||
return;
|
||||
|
||||
fc = le16_to_cpu(hdr->frame_ctl);
|
||||
printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s",
|
||||
fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
|
||||
fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
|
||||
fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
|
||||
|
||||
if (skb->len < IEEE80211_DATA_HDR3_LEN) {
|
||||
printk("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
|
||||
le16_to_cpu(hdr->seq_ctl));
|
||||
|
||||
printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR,
|
||||
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3));
|
||||
if (skb->len >= 30)
|
||||
printk(" A4=" MACSTR, MAC2STR(hdr->addr4));
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
|
||||
/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta)
|
||||
* Convert Ethernet header into a suitable IEEE 802.11 header depending on
|
||||
* device configuration. */
|
||||
int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
int need_headroom, need_tailroom = 0;
|
||||
struct ieee80211_hdr hdr;
|
||||
u16 fc, ethertype = 0;
|
||||
enum {
|
||||
WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME
|
||||
} use_wds = WDS_NO;
|
||||
u8 *encaps_data;
|
||||
int hdr_len, encaps_len, skip_header_bytes;
|
||||
int to_assoc_ap = 0;
|
||||
struct hostap_skb_tx_data *meta;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
if (skb->len < ETH_HLEN) {
|
||||
printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb "
|
||||
"(len=%d)\n", dev->name, skb->len);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (local->ddev != dev) {
|
||||
use_wds = (local->iw_mode == IW_MODE_MASTER &&
|
||||
!(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ?
|
||||
WDS_OWN_FRAME : WDS_COMPLIANT_FRAME;
|
||||
if (dev == local->stadev) {
|
||||
to_assoc_ap = 1;
|
||||
use_wds = WDS_NO;
|
||||
} else if (dev == local->apdev) {
|
||||
printk(KERN_DEBUG "%s: prism2_tx: trying to use "
|
||||
"AP device with Ethernet net dev\n", dev->name);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (local->iw_mode == IW_MODE_REPEAT) {
|
||||
printk(KERN_DEBUG "%s: prism2_tx: trying to use "
|
||||
"non-WDS link in Repeater mode\n", dev->name);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
} else if (local->iw_mode == IW_MODE_INFRA &&
|
||||
(local->wds_type & HOSTAP_WDS_AP_CLIENT) &&
|
||||
memcmp(skb->data + ETH_ALEN, dev->dev_addr,
|
||||
ETH_ALEN) != 0) {
|
||||
/* AP client mode: send frames with foreign src addr
|
||||
* using 4-addr WDS frames */
|
||||
use_wds = WDS_COMPLIANT_FRAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload
|
||||
* ==>
|
||||
* Prism2 TX frame with 802.11 header:
|
||||
* txdesc (address order depending on used mode; includes dst_addr and
|
||||
* src_addr), possible encapsulation (RFC1042/Bridge-Tunnel;
|
||||
* proto[2], payload {, possible addr4[6]} */
|
||||
|
||||
ethertype = (skb->data[12] << 8) | skb->data[13];
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
|
||||
/* Length of data after IEEE 802.11 header */
|
||||
encaps_data = NULL;
|
||||
encaps_len = 0;
|
||||
skip_header_bytes = ETH_HLEN;
|
||||
if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
|
||||
encaps_data = bridge_tunnel_header;
|
||||
encaps_len = sizeof(bridge_tunnel_header);
|
||||
skip_header_bytes -= 2;
|
||||
} else if (ethertype >= 0x600) {
|
||||
encaps_data = rfc1042_header;
|
||||
encaps_len = sizeof(rfc1042_header);
|
||||
skip_header_bytes -= 2;
|
||||
}
|
||||
|
||||
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
|
||||
hdr_len = IEEE80211_DATA_HDR3_LEN;
|
||||
|
||||
if (use_wds != WDS_NO) {
|
||||
/* Note! Prism2 station firmware has problems with sending real
|
||||
* 802.11 frames with four addresses; until these problems can
|
||||
* be fixed or worked around, 4-addr frames needed for WDS are
|
||||
* using incompatible format: FromDS flag is not set and the
|
||||
* fourth address is added after the frame payload; it is
|
||||
* assumed, that the receiving station knows how to handle this
|
||||
* frame format */
|
||||
|
||||
if (use_wds == WDS_COMPLIANT_FRAME) {
|
||||
fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
|
||||
/* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA,
|
||||
* Addr4 = SA */
|
||||
memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
hdr_len += ETH_ALEN;
|
||||
} else {
|
||||
/* bogus 4-addr format to workaround Prism2 station
|
||||
* f/w bug */
|
||||
fc |= IEEE80211_FCTL_TODS;
|
||||
/* From DS: Addr1 = DA (used as RA),
|
||||
* Addr2 = BSSID (used as TA), Addr3 = SA (used as DA),
|
||||
*/
|
||||
|
||||
/* SA from skb->data + ETH_ALEN will be added after
|
||||
* frame payload; use hdr.addr4 as a temporary buffer
|
||||
*/
|
||||
memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
need_tailroom += ETH_ALEN;
|
||||
}
|
||||
|
||||
/* send broadcast and multicast frames to broadcast RA, if
|
||||
* configured; otherwise, use unicast RA of the WDS link */
|
||||
if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) &&
|
||||
skb->data[0] & 0x01)
|
||||
memset(&hdr.addr1, 0xff, ETH_ALEN);
|
||||
else if (iface->type == HOSTAP_INTERFACE_WDS)
|
||||
memcpy(&hdr.addr1, iface->u.wds.remote_addr,
|
||||
ETH_ALEN);
|
||||
else
|
||||
memcpy(&hdr.addr1, local->bssid, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, skb->data, ETH_ALEN);
|
||||
} else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) {
|
||||
fc |= IEEE80211_FCTL_FROMDS;
|
||||
/* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */
|
||||
memcpy(&hdr.addr1, skb->data, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
} else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) {
|
||||
fc |= IEEE80211_FCTL_TODS;
|
||||
/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
|
||||
memcpy(&hdr.addr1, to_assoc_ap ?
|
||||
local->assoc_ap_addr : local->bssid, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, skb->data, ETH_ALEN);
|
||||
} else if (local->iw_mode == IW_MODE_ADHOC) {
|
||||
/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
|
||||
memcpy(&hdr.addr1, skb->data, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, local->bssid, ETH_ALEN);
|
||||
}
|
||||
|
||||
hdr.frame_ctl = cpu_to_le16(fc);
|
||||
|
||||
skb_pull(skb, skip_header_bytes);
|
||||
need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len;
|
||||
if (skb_tailroom(skb) < need_tailroom) {
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
if (pskb_expand_head(skb, need_headroom, need_tailroom,
|
||||
GFP_ATOMIC)) {
|
||||
kfree_skb(skb);
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
} else if (skb_headroom(skb) < need_headroom) {
|
||||
struct sk_buff *tmp = skb;
|
||||
skb = skb_realloc_headroom(skb, need_headroom);
|
||||
kfree_skb(tmp);
|
||||
if (skb == NULL) {
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (encaps_data)
|
||||
memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
|
||||
memcpy(skb_push(skb, hdr_len), &hdr, hdr_len);
|
||||
if (use_wds == WDS_OWN_FRAME) {
|
||||
memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN);
|
||||
}
|
||||
|
||||
iface->stats.tx_packets++;
|
||||
iface->stats.tx_bytes += skb->len;
|
||||
|
||||
skb->mac.raw = skb->data;
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
memset(meta, 0, sizeof(*meta));
|
||||
meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
|
||||
if (use_wds)
|
||||
meta->flags |= HOSTAP_TX_FLAGS_WDS;
|
||||
meta->ethertype = ethertype;
|
||||
meta->iface = iface;
|
||||
|
||||
/* Send IEEE 802.11 encapsulated frame using the master radio device */
|
||||
skb->dev = local->dev;
|
||||
dev_queue_xmit(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* hard_start_xmit function for hostapd wlan#ap interfaces */
|
||||
int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
struct hostap_skb_tx_data *meta;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u16 fc;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
if (skb->len < 10) {
|
||||
printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb "
|
||||
"(len=%d)\n", dev->name, skb->len);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iface->stats.tx_packets++;
|
||||
iface->stats.tx_bytes += skb->len;
|
||||
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
memset(meta, 0, sizeof(*meta));
|
||||
meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
|
||||
meta->iface = iface;
|
||||
|
||||
if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) {
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
fc = le16_to_cpu(hdr->frame_ctl);
|
||||
if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
|
||||
WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_DATA) {
|
||||
u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN +
|
||||
sizeof(rfc1042_header)];
|
||||
meta->ethertype = (pos[0] << 8) | pos[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Send IEEE 802.11 encapsulated frame using the master radio device */
|
||||
skb->dev = local->dev;
|
||||
dev_queue_xmit(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called only from software IRQ */
|
||||
struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
|
||||
struct ieee80211_crypt_data *crypt)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u16 fc;
|
||||
int hdr_len, res;
|
||||
|
||||
iface = netdev_priv(skb->dev);
|
||||
local = iface->local;
|
||||
|
||||
if (skb->len < IEEE80211_DATA_HDR3_LEN) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (local->tkip_countermeasures &&
|
||||
crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
|
||||
"TX packet to " MACSTR "\n",
|
||||
local->dev->name, MAC2STR(hdr->addr1));
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
|
||||
if ((skb_headroom(skb) < crypt->ops->extra_prefix_len ||
|
||||
skb_tailroom(skb) < crypt->ops->extra_postfix_len) &&
|
||||
pskb_expand_head(skb, crypt->ops->extra_prefix_len,
|
||||
crypt->ops->extra_postfix_len, GFP_ATOMIC)) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
fc = le16_to_cpu(hdr->frame_ctl);
|
||||
hdr_len = hostap_80211_get_hdrlen(fc);
|
||||
|
||||
/* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
|
||||
* call both MSDU and MPDU encryption functions from here. */
|
||||
atomic_inc(&crypt->refcnt);
|
||||
res = 0;
|
||||
if (crypt->ops->encrypt_msdu)
|
||||
res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv);
|
||||
if (res == 0 && crypt->ops->encrypt_mpdu)
|
||||
res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv);
|
||||
atomic_dec(&crypt->refcnt);
|
||||
if (res < 0) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
|
||||
/* hard_start_xmit function for master radio interface wifi#.
|
||||
* AP processing (TX rate control, power save buffering, etc.).
|
||||
* Use hardware TX function to send the frame. */
|
||||
int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
int ret = 1;
|
||||
u16 fc;
|
||||
struct hostap_tx_data tx;
|
||||
ap_tx_ret tx_ret;
|
||||
struct hostap_skb_tx_data *meta;
|
||||
int no_encrypt = 0;
|
||||
struct ieee80211_hdr *hdr;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
tx.skb = skb;
|
||||
tx.sta_ptr = NULL;
|
||||
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
|
||||
printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
|
||||
"expected 0x%08x)\n",
|
||||
dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC);
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (local->host_encrypt) {
|
||||
/* Set crypt to default algorithm and key; will be replaced in
|
||||
* AP code if STA has own alg/key */
|
||||
tx.crypt = local->crypt[local->tx_keyidx];
|
||||
tx.host_encrypt = 1;
|
||||
} else {
|
||||
tx.crypt = NULL;
|
||||
tx.host_encrypt = 0;
|
||||
}
|
||||
|
||||
if (skb->len < 24) {
|
||||
printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb "
|
||||
"(len=%d)\n", dev->name, skb->len);
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* FIX (?):
|
||||
* Wi-Fi 802.11b test plan suggests that AP should ignore power save
|
||||
* bit in authentication and (re)association frames and assume tha
|
||||
* STA remains awake for the response. */
|
||||
tx_ret = hostap_handle_sta_tx(local, &tx);
|
||||
skb = tx.skb;
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
fc = le16_to_cpu(hdr->frame_ctl);
|
||||
switch (tx_ret) {
|
||||
case AP_TX_CONTINUE:
|
||||
break;
|
||||
case AP_TX_CONTINUE_NOT_AUTHORIZED:
|
||||
if (local->ieee_802_1x &&
|
||||
WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
|
||||
meta->ethertype != ETH_P_PAE &&
|
||||
!(meta->flags & HOSTAP_TX_FLAGS_WDS)) {
|
||||
printk(KERN_DEBUG "%s: dropped frame to unauthorized "
|
||||
"port (IEEE 802.1X): ethertype=0x%04x\n",
|
||||
dev->name, meta->ethertype);
|
||||
hostap_dump_tx_80211(dev->name, skb);
|
||||
|
||||
ret = 0; /* drop packet */
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case AP_TX_DROP:
|
||||
ret = 0; /* drop packet */
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
case AP_TX_RETRY:
|
||||
goto fail;
|
||||
case AP_TX_BUFFERED:
|
||||
/* do not free skb here, it will be freed when the
|
||||
* buffered frame is sent/timed out */
|
||||
ret = 0;
|
||||
goto tx_exit;
|
||||
}
|
||||
|
||||
/* Request TX callback if protocol version is 2 in 802.11 header;
|
||||
* this version 2 is a special case used between hostapd and kernel
|
||||
* driver */
|
||||
if (((fc & IEEE80211_FCTL_VERS) == BIT(1)) &&
|
||||
local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) {
|
||||
meta->tx_cb_idx = local->ap->tx_callback_idx;
|
||||
|
||||
/* remove special version from the frame header */
|
||||
fc &= ~IEEE80211_FCTL_VERS;
|
||||
hdr->frame_ctl = cpu_to_le16(fc);
|
||||
}
|
||||
|
||||
if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_DATA) {
|
||||
no_encrypt = 1;
|
||||
tx.crypt = NULL;
|
||||
}
|
||||
|
||||
if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt &&
|
||||
!(fc & IEEE80211_FCTL_VERS)) {
|
||||
no_encrypt = 1;
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing "
|
||||
"unencrypted EAPOL frame\n", dev->name);
|
||||
tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */
|
||||
}
|
||||
|
||||
if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu))
|
||||
tx.crypt = NULL;
|
||||
else if ((tx.crypt || local->crypt[local->tx_keyidx]) && !no_encrypt) {
|
||||
/* Add ISWEP flag both for firmware and host based encryption
|
||||
*/
|
||||
fc |= IEEE80211_FCTL_PROTECTED;
|
||||
hdr->frame_ctl = cpu_to_le16(fc);
|
||||
} else if (local->drop_unencrypted &&
|
||||
WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
|
||||
meta->ethertype != ETH_P_PAE) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "%s: dropped unencrypted TX data "
|
||||
"frame (drop_unencrypted=1)\n", dev->name);
|
||||
}
|
||||
iface->stats.tx_dropped++;
|
||||
ret = 0;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (tx.crypt) {
|
||||
skb = hostap_tx_encrypt(skb, tx.crypt);
|
||||
if (skb == NULL) {
|
||||
printk(KERN_DEBUG "%s: TX - encryption failed\n",
|
||||
dev->name);
|
||||
ret = 0;
|
||||
goto fail;
|
||||
}
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
|
||||
printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
|
||||
"expected 0x%08x) after hostap_tx_encrypt\n",
|
||||
dev->name, meta->magic,
|
||||
HOSTAP_SKB_TX_DATA_MAGIC);
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (local->func->tx == NULL || local->func->tx(skb, dev)) {
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
} else {
|
||||
ret = 0;
|
||||
iface->stats.tx_packets++;
|
||||
iface->stats.tx_bytes += skb->len;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (!ret && skb)
|
||||
dev_kfree_skb(skb);
|
||||
tx_exit:
|
||||
if (tx.sta_ptr)
|
||||
hostap_handle_sta_release(tx.sta_ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(hostap_dump_tx_80211);
|
||||
EXPORT_SYMBOL(hostap_tx_encrypt);
|
||||
EXPORT_SYMBOL(hostap_master_start_xmit);
|
3288
drivers/net/wireless/hostap/hostap_ap.c
Normal file
3288
drivers/net/wireless/hostap/hostap_ap.c
Normal file
File diff suppressed because it is too large
Load Diff
261
drivers/net/wireless/hostap/hostap_ap.h
Normal file
261
drivers/net/wireless/hostap/hostap_ap.h
Normal file
@ -0,0 +1,261 @@
|
||||
#ifndef HOSTAP_AP_H
|
||||
#define HOSTAP_AP_H
|
||||
|
||||
/* AP data structures for STAs */
|
||||
|
||||
/* maximum number of frames to buffer per STA */
|
||||
#define STA_MAX_TX_BUFFER 32
|
||||
|
||||
/* STA flags */
|
||||
#define WLAN_STA_AUTH BIT(0)
|
||||
#define WLAN_STA_ASSOC BIT(1)
|
||||
#define WLAN_STA_PS BIT(2)
|
||||
#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */
|
||||
#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */
|
||||
#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is
|
||||
* controlling whether STA is authorized to
|
||||
* send and receive non-IEEE 802.1X frames
|
||||
*/
|
||||
#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
|
||||
|
||||
#define WLAN_RATE_1M BIT(0)
|
||||
#define WLAN_RATE_2M BIT(1)
|
||||
#define WLAN_RATE_5M5 BIT(2)
|
||||
#define WLAN_RATE_11M BIT(3)
|
||||
#define WLAN_RATE_COUNT 4
|
||||
|
||||
/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8,
|
||||
* but some pre-standard IEEE 802.11g products use longer elements. */
|
||||
#define WLAN_SUPP_RATES_MAX 32
|
||||
|
||||
/* Try to increase TX rate after # successfully sent consecutive packets */
|
||||
#define WLAN_RATE_UPDATE_COUNT 50
|
||||
|
||||
/* Decrease TX rate after # consecutive dropped packets */
|
||||
#define WLAN_RATE_DECREASE_THRESHOLD 2
|
||||
|
||||
struct sta_info {
|
||||
struct list_head list;
|
||||
struct sta_info *hnext; /* next entry in hash table list */
|
||||
atomic_t users; /* number of users (do not remove if > 0) */
|
||||
struct proc_dir_entry *proc;
|
||||
|
||||
u8 addr[6];
|
||||
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
|
||||
u32 flags;
|
||||
u16 capability;
|
||||
u16 listen_interval; /* or beacon_int for APs */
|
||||
u8 supported_rates[WLAN_SUPP_RATES_MAX];
|
||||
|
||||
unsigned long last_auth;
|
||||
unsigned long last_assoc;
|
||||
unsigned long last_rx;
|
||||
unsigned long last_tx;
|
||||
unsigned long rx_packets, tx_packets;
|
||||
unsigned long rx_bytes, tx_bytes;
|
||||
struct sk_buff_head tx_buf;
|
||||
/* FIX: timeout buffers with an expiry time somehow derived from
|
||||
* listen_interval */
|
||||
|
||||
s8 last_rx_silence; /* Noise in dBm */
|
||||
s8 last_rx_signal; /* Signal strength in dBm */
|
||||
u8 last_rx_rate; /* TX rate in 0.1 Mbps */
|
||||
u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */
|
||||
|
||||
u8 tx_supp_rates; /* bit field of supported TX rates */
|
||||
u8 tx_rate; /* current TX rate (in 0.1 Mbps) */
|
||||
u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */
|
||||
u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */
|
||||
u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */
|
||||
u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate)
|
||||
*/
|
||||
u32 tx_since_last_failure;
|
||||
u32 tx_consecutive_exc;
|
||||
|
||||
struct ieee80211_crypt_data *crypt;
|
||||
|
||||
int ap; /* whether this station is an AP */
|
||||
|
||||
local_info_t *local;
|
||||
|
||||
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
|
||||
union {
|
||||
struct {
|
||||
char *challenge; /* shared key authentication
|
||||
* challenge */
|
||||
} sta;
|
||||
struct {
|
||||
int ssid_len;
|
||||
unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */
|
||||
int channel;
|
||||
unsigned long last_beacon; /* last RX beacon time */
|
||||
} ap;
|
||||
} u;
|
||||
|
||||
struct timer_list timer;
|
||||
enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next;
|
||||
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
};
|
||||
|
||||
|
||||
#define MAX_STA_COUNT 1024
|
||||
|
||||
/* Maximum number of AIDs to use for STAs; must be 2007 or lower
|
||||
* (8802.11 limitation) */
|
||||
#define MAX_AID_TABLE_SIZE 128
|
||||
|
||||
#define STA_HASH_SIZE 256
|
||||
#define STA_HASH(sta) (sta[5])
|
||||
|
||||
|
||||
/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC
|
||||
* has passed since last received frame from the station, a nullfunc data
|
||||
* frame is sent to the station. If this frame is not acknowledged and no other
|
||||
* frames have been received, the station will be disassociated after
|
||||
* AP_DISASSOC_DELAY. Similarily, a the station will be deauthenticated after
|
||||
* AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with
|
||||
* max inactivity timer. */
|
||||
#define AP_MAX_INACTIVITY_SEC (5 * 60)
|
||||
#define AP_DISASSOC_DELAY (HZ)
|
||||
#define AP_DEAUTH_DELAY (HZ)
|
||||
|
||||
/* ap_policy: whether to accept frames to/from other APs/IBSS */
|
||||
typedef enum {
|
||||
AP_OTHER_AP_SKIP_ALL = 0,
|
||||
AP_OTHER_AP_SAME_SSID = 1,
|
||||
AP_OTHER_AP_ALL = 2,
|
||||
AP_OTHER_AP_EVEN_IBSS = 3
|
||||
} ap_policy_enum;
|
||||
|
||||
#define PRISM2_AUTH_OPEN BIT(0)
|
||||
#define PRISM2_AUTH_SHARED_KEY BIT(1)
|
||||
|
||||
|
||||
/* MAC address-based restrictions */
|
||||
struct mac_entry {
|
||||
struct list_head list;
|
||||
u8 addr[6];
|
||||
};
|
||||
|
||||
struct mac_restrictions {
|
||||
enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy;
|
||||
unsigned int entries;
|
||||
struct list_head mac_list;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
||||
struct add_sta_proc_data {
|
||||
u8 addr[ETH_ALEN];
|
||||
struct add_sta_proc_data *next;
|
||||
};
|
||||
|
||||
|
||||
typedef enum { WDS_ADD, WDS_DEL } wds_oper_type;
|
||||
struct wds_oper_data {
|
||||
wds_oper_type type;
|
||||
u8 addr[ETH_ALEN];
|
||||
struct wds_oper_data *next;
|
||||
};
|
||||
|
||||
|
||||
struct ap_data {
|
||||
int initialized; /* whether ap_data has been initialized */
|
||||
local_info_t *local;
|
||||
int bridge_packets; /* send packet to associated STAs directly to the
|
||||
* wireless media instead of higher layers in the
|
||||
* kernel */
|
||||
unsigned int bridged_unicast; /* number of unicast frames bridged on
|
||||
* wireless media */
|
||||
unsigned int bridged_multicast; /* number of non-unicast frames
|
||||
* bridged on wireless media */
|
||||
unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped
|
||||
* because they were to an address that
|
||||
* was not associated */
|
||||
int nullfunc_ack; /* use workaround for nullfunc frame ACKs */
|
||||
|
||||
spinlock_t sta_table_lock;
|
||||
int num_sta; /* number of entries in sta_list */
|
||||
struct list_head sta_list; /* STA info list head */
|
||||
struct sta_info *sta_hash[STA_HASH_SIZE];
|
||||
|
||||
struct proc_dir_entry *proc;
|
||||
|
||||
ap_policy_enum ap_policy;
|
||||
unsigned int max_inactivity;
|
||||
int autom_ap_wds;
|
||||
|
||||
struct mac_restrictions mac_restrictions; /* MAC-based auth */
|
||||
int last_tx_rate;
|
||||
|
||||
struct work_struct add_sta_proc_queue;
|
||||
struct add_sta_proc_data *add_sta_proc_entries;
|
||||
|
||||
struct work_struct wds_oper_queue;
|
||||
struct wds_oper_data *wds_oper_entries;
|
||||
|
||||
u16 tx_callback_idx;
|
||||
|
||||
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
|
||||
/* pointers to STA info; based on allocated AID or NULL if AID free
|
||||
* AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1
|
||||
* and so on
|
||||
*/
|
||||
struct sta_info *sta_aid[MAX_AID_TABLE_SIZE];
|
||||
|
||||
u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll;
|
||||
|
||||
/* WEP operations for generating challenges to be used with shared key
|
||||
* authentication */
|
||||
struct ieee80211_crypto_ops *crypt;
|
||||
void *crypt_priv;
|
||||
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
};
|
||||
|
||||
|
||||
void hostap_rx(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
void hostap_init_data(local_info_t *local);
|
||||
void hostap_init_ap_proc(local_info_t *local);
|
||||
void hostap_free_data(struct ap_data *ap);
|
||||
void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver);
|
||||
|
||||
typedef enum {
|
||||
AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED,
|
||||
AP_TX_CONTINUE_NOT_AUTHORIZED
|
||||
} ap_tx_ret;
|
||||
struct hostap_tx_data {
|
||||
struct sk_buff *skb;
|
||||
int host_encrypt;
|
||||
struct ieee80211_crypt_data *crypt;
|
||||
void *sta_ptr;
|
||||
};
|
||||
ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx);
|
||||
void hostap_handle_sta_release(void *ptr);
|
||||
void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb);
|
||||
int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr);
|
||||
typedef enum {
|
||||
AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED
|
||||
} ap_rx_ret;
|
||||
ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
|
||||
struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats,
|
||||
int wds);
|
||||
int hostap_handle_sta_crypto(local_info_t *local, struct ieee80211_hdr *hdr,
|
||||
struct ieee80211_crypt_data **crypt,
|
||||
void **sta_ptr);
|
||||
int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr);
|
||||
int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr);
|
||||
int hostap_add_sta(struct ap_data *ap, u8 *sta_addr);
|
||||
int hostap_update_rx_stats(struct ap_data *ap, struct ieee80211_hdr *hdr,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
void hostap_update_rates(local_info_t *local);
|
||||
void hostap_add_wds_links(local_info_t *local);
|
||||
void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type);
|
||||
|
||||
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
|
||||
void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
|
||||
int resend);
|
||||
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
|
||||
#endif /* HOSTAP_AP_H */
|
435
drivers/net/wireless/hostap/hostap_common.h
Normal file
435
drivers/net/wireless/hostap/hostap_common.h
Normal file
@ -0,0 +1,435 @@
|
||||
#ifndef HOSTAP_COMMON_H
|
||||
#define HOSTAP_COMMON_H
|
||||
|
||||
#define BIT(x) (1 << (x))
|
||||
|
||||
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
|
||||
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
|
||||
|
||||
/* IEEE 802.11 defines */
|
||||
|
||||
/* Information Element IDs */
|
||||
#define WLAN_EID_SSID 0
|
||||
#define WLAN_EID_SUPP_RATES 1
|
||||
#define WLAN_EID_FH_PARAMS 2
|
||||
#define WLAN_EID_DS_PARAMS 3
|
||||
#define WLAN_EID_CF_PARAMS 4
|
||||
#define WLAN_EID_TIM 5
|
||||
#define WLAN_EID_IBSS_PARAMS 6
|
||||
#define WLAN_EID_CHALLENGE 16
|
||||
#define WLAN_EID_RSN 48
|
||||
#define WLAN_EID_GENERIC 221
|
||||
|
||||
|
||||
/* HFA384X Configuration RIDs */
|
||||
#define HFA384X_RID_CNFPORTTYPE 0xFC00
|
||||
#define HFA384X_RID_CNFOWNMACADDR 0xFC01
|
||||
#define HFA384X_RID_CNFDESIREDSSID 0xFC02
|
||||
#define HFA384X_RID_CNFOWNCHANNEL 0xFC03
|
||||
#define HFA384X_RID_CNFOWNSSID 0xFC04
|
||||
#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05
|
||||
#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06
|
||||
#define HFA384X_RID_CNFMAXDATALEN 0xFC07
|
||||
#define HFA384X_RID_CNFWDSADDRESS 0xFC08
|
||||
#define HFA384X_RID_CNFPMENABLED 0xFC09
|
||||
#define HFA384X_RID_CNFPMEPS 0xFC0A
|
||||
#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B
|
||||
#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C
|
||||
#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D
|
||||
#define HFA384X_RID_CNFOWNNAME 0xFC0E
|
||||
#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10
|
||||
#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */
|
||||
#define HFA384X_RID_UNKNOWN1 0xFC20
|
||||
#define HFA384X_RID_UNKNOWN2 0xFC21
|
||||
#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23
|
||||
#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24
|
||||
#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25
|
||||
#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26
|
||||
#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27
|
||||
#define HFA384X_RID_CNFWEPFLAGS 0xFC28
|
||||
#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29
|
||||
#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A
|
||||
#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */
|
||||
#define HFA384X_RID_CNFTXCONTROL 0xFC2C
|
||||
#define HFA384X_RID_CNFROAMINGMODE 0xFC2D
|
||||
#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */
|
||||
#define HFA384X_RID_CNFRCVCRCERROR 0xFC30
|
||||
#define HFA384X_RID_CNFMMLIFE 0xFC31
|
||||
#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32
|
||||
#define HFA384X_RID_CNFBEACONINT 0xFC33
|
||||
#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFSTAPCFINFO 0xFC35
|
||||
#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37
|
||||
#define HFA384X_RID_CNFTIMCTRL 0xFC40
|
||||
#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */
|
||||
#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */
|
||||
#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */
|
||||
#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0;
|
||||
* write only */
|
||||
#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */
|
||||
#define HFA384X_RID_GROUPADDRESSES 0xFC80
|
||||
#define HFA384X_RID_CREATEIBSS 0xFC81
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82
|
||||
#define HFA384X_RID_RTSTHRESHOLD 0xFC83
|
||||
#define HFA384X_RID_TXRATECONTROL 0xFC84
|
||||
#define HFA384X_RID_PROMISCUOUSMODE 0xFC85
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0
|
||||
#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1
|
||||
#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2
|
||||
#define HFA384X_RID_CNFBASICRATES 0xFCB3
|
||||
#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4
|
||||
#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */
|
||||
#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */
|
||||
#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */
|
||||
#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */
|
||||
#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */
|
||||
#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */
|
||||
#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */
|
||||
#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */
|
||||
#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */
|
||||
#define HFA384X_RID_TICKTIME 0xFCE0
|
||||
#define HFA384X_RID_SCANREQUEST 0xFCE1
|
||||
#define HFA384X_RID_JOINREQUEST 0xFCE2
|
||||
#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */
|
||||
#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */
|
||||
#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */
|
||||
|
||||
/* HFA384X Information RIDs */
|
||||
#define HFA384X_RID_MAXLOADTIME 0xFD00
|
||||
#define HFA384X_RID_DOWNLOADBUFFER 0xFD01
|
||||
#define HFA384X_RID_PRIID 0xFD02
|
||||
#define HFA384X_RID_PRISUPRANGE 0xFD03
|
||||
#define HFA384X_RID_CFIACTRANGES 0xFD04
|
||||
#define HFA384X_RID_NICSERNUM 0xFD0A
|
||||
#define HFA384X_RID_NICID 0xFD0B
|
||||
#define HFA384X_RID_MFISUPRANGE 0xFD0C
|
||||
#define HFA384X_RID_CFISUPRANGE 0xFD0D
|
||||
#define HFA384X_RID_CHANNELLIST 0xFD10
|
||||
#define HFA384X_RID_REGULATORYDOMAINS 0xFD11
|
||||
#define HFA384X_RID_TEMPTYPE 0xFD12
|
||||
#define HFA384X_RID_CIS 0xFD13
|
||||
#define HFA384X_RID_STAID 0xFD20
|
||||
#define HFA384X_RID_STASUPRANGE 0xFD21
|
||||
#define HFA384X_RID_MFIACTRANGES 0xFD22
|
||||
#define HFA384X_RID_CFIACTRANGES2 0xFD23
|
||||
#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1;
|
||||
* only Prism2.5(?) */
|
||||
#define HFA384X_RID_PORTSTATUS 0xFD40
|
||||
#define HFA384X_RID_CURRENTSSID 0xFD41
|
||||
#define HFA384X_RID_CURRENTBSSID 0xFD42
|
||||
#define HFA384X_RID_COMMSQUALITY 0xFD43
|
||||
#define HFA384X_RID_CURRENTTXRATE 0xFD44
|
||||
#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45
|
||||
#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46
|
||||
#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47
|
||||
#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48
|
||||
#define HFA384X_RID_LONGRETRYLIMIT 0xFD49
|
||||
#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A
|
||||
#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B
|
||||
#define HFA384X_RID_CFPOLLABLE 0xFD4C
|
||||
#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D
|
||||
#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F
|
||||
#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */
|
||||
#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */
|
||||
#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */
|
||||
#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */
|
||||
#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */
|
||||
#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */
|
||||
#define HFA384X_RID_PHYTYPE 0xFDC0
|
||||
#define HFA384X_RID_CURRENTCHANNEL 0xFDC1
|
||||
#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2
|
||||
#define HFA384X_RID_CCAMODE 0xFDC3
|
||||
#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6
|
||||
#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */
|
||||
#define HFA384X_RID_BUILDSEQ 0xFFFE
|
||||
#define HFA384X_RID_FWID 0xFFFF
|
||||
|
||||
|
||||
struct hfa384x_comp_ident
|
||||
{
|
||||
u16 id;
|
||||
u16 variant;
|
||||
u16 major;
|
||||
u16 minor;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define HFA384X_COMP_ID_PRI 0x15
|
||||
#define HFA384X_COMP_ID_STA 0x1f
|
||||
#define HFA384X_COMP_ID_FW_AP 0x14b
|
||||
|
||||
struct hfa384x_sup_range
|
||||
{
|
||||
u16 role;
|
||||
u16 id;
|
||||
u16 variant;
|
||||
u16 bottom;
|
||||
u16 top;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct hfa384x_build_id
|
||||
{
|
||||
u16 pri_seq;
|
||||
u16 sec_seq;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* FD01 - Download Buffer */
|
||||
struct hfa384x_rid_download_buffer
|
||||
{
|
||||
u16 page;
|
||||
u16 offset;
|
||||
u16 length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */
|
||||
struct hfa384x_comms_quality {
|
||||
u16 comm_qual; /* 0 .. 92 */
|
||||
u16 signal_level; /* 27 .. 154 */
|
||||
u16 noise_level; /* 27 .. 154 */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
|
||||
|
||||
/* New wireless extensions API - SET/GET convention (even ioctl numbers are
|
||||
* root only)
|
||||
*/
|
||||
#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
|
||||
#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
|
||||
#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
|
||||
#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
|
||||
#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
|
||||
#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
|
||||
#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
|
||||
#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
|
||||
#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
|
||||
#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
|
||||
#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
|
||||
#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
|
||||
#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
|
||||
#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
|
||||
|
||||
/* following are not in SIOCGIWPRIV list; check permission in the driver code
|
||||
*/
|
||||
#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
|
||||
#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
|
||||
enum {
|
||||
/* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
|
||||
PRISM2_PARAM_TXRATECTRL = 2,
|
||||
PRISM2_PARAM_BEACON_INT = 3,
|
||||
PRISM2_PARAM_PSEUDO_IBSS = 4,
|
||||
PRISM2_PARAM_ALC = 5,
|
||||
/* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
|
||||
PRISM2_PARAM_DUMP = 7,
|
||||
PRISM2_PARAM_OTHER_AP_POLICY = 8,
|
||||
PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
|
||||
PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
|
||||
PRISM2_PARAM_DTIM_PERIOD = 11,
|
||||
PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
|
||||
PRISM2_PARAM_MAX_WDS = 13,
|
||||
PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
|
||||
PRISM2_PARAM_AP_AUTH_ALGS = 15,
|
||||
PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
|
||||
PRISM2_PARAM_HOST_ENCRYPT = 17,
|
||||
PRISM2_PARAM_HOST_DECRYPT = 18,
|
||||
/* PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, REMOVED 2005-08-14 */
|
||||
/* PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, REMOVED 2005-08-14 */
|
||||
PRISM2_PARAM_HOST_ROAMING = 21,
|
||||
PRISM2_PARAM_BCRX_STA_KEY = 22,
|
||||
PRISM2_PARAM_IEEE_802_1X = 23,
|
||||
PRISM2_PARAM_ANTSEL_TX = 24,
|
||||
PRISM2_PARAM_ANTSEL_RX = 25,
|
||||
PRISM2_PARAM_MONITOR_TYPE = 26,
|
||||
PRISM2_PARAM_WDS_TYPE = 27,
|
||||
PRISM2_PARAM_HOSTSCAN = 28,
|
||||
PRISM2_PARAM_AP_SCAN = 29,
|
||||
PRISM2_PARAM_ENH_SEC = 30,
|
||||
PRISM2_PARAM_IO_DEBUG = 31,
|
||||
PRISM2_PARAM_BASIC_RATES = 32,
|
||||
PRISM2_PARAM_OPER_RATES = 33,
|
||||
PRISM2_PARAM_HOSTAPD = 34,
|
||||
PRISM2_PARAM_HOSTAPD_STA = 35,
|
||||
PRISM2_PARAM_WPA = 36,
|
||||
PRISM2_PARAM_PRIVACY_INVOKED = 37,
|
||||
PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
|
||||
PRISM2_PARAM_DROP_UNENCRYPTED = 39,
|
||||
PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
|
||||
};
|
||||
|
||||
enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
|
||||
HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
|
||||
enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
|
||||
AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
|
||||
AP_MAC_CMD_KICKALL = 4 };
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
|
||||
enum {
|
||||
PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
|
||||
/* Note! Old versions of prism2_srec have a fatal error in CRC-16
|
||||
* calculation, which will corrupt all non-volatile downloads.
|
||||
* PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
|
||||
* prevent use of old versions of prism2_srec for non-volatile
|
||||
* download. */
|
||||
PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
|
||||
PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
|
||||
/* Persistent versions of volatile download commands (keep firmware
|
||||
* data in memory and automatically re-download after hw_reset */
|
||||
PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
|
||||
PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
|
||||
};
|
||||
|
||||
struct prism2_download_param {
|
||||
u32 dl_cmd;
|
||||
u32 start_addr;
|
||||
u32 num_areas;
|
||||
struct prism2_download_area {
|
||||
u32 addr; /* wlan card address */
|
||||
u32 len;
|
||||
void __user *ptr; /* pointer to data in user space */
|
||||
} data[0];
|
||||
};
|
||||
|
||||
#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
|
||||
#define PRISM2_MAX_DOWNLOAD_LEN 262144
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
|
||||
enum {
|
||||
PRISM2_HOSTAPD_FLUSH = 1,
|
||||
PRISM2_HOSTAPD_ADD_STA = 2,
|
||||
PRISM2_HOSTAPD_REMOVE_STA = 3,
|
||||
PRISM2_HOSTAPD_GET_INFO_STA = 4,
|
||||
/* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
|
||||
PRISM2_SET_ENCRYPTION = 6,
|
||||
PRISM2_GET_ENCRYPTION = 7,
|
||||
PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
|
||||
PRISM2_HOSTAPD_GET_RID = 9,
|
||||
PRISM2_HOSTAPD_SET_RID = 10,
|
||||
PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
|
||||
PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
|
||||
PRISM2_HOSTAPD_MLME = 13,
|
||||
PRISM2_HOSTAPD_SCAN_REQ = 14,
|
||||
PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
|
||||
};
|
||||
|
||||
#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
|
||||
#define PRISM2_HOSTAPD_RID_HDR_LEN \
|
||||
((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
|
||||
#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
|
||||
((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
|
||||
|
||||
/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
|
||||
*/
|
||||
#define HOSTAP_CRYPT_ALG_NAME_LEN 16
|
||||
|
||||
|
||||
struct prism2_hostapd_param {
|
||||
u32 cmd;
|
||||
u8 sta_addr[ETH_ALEN];
|
||||
union {
|
||||
struct {
|
||||
u16 aid;
|
||||
u16 capability;
|
||||
u8 tx_supp_rates;
|
||||
} add_sta;
|
||||
struct {
|
||||
u32 inactive_sec;
|
||||
} get_info_sta;
|
||||
struct {
|
||||
u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
|
||||
u32 flags;
|
||||
u32 err;
|
||||
u8 idx;
|
||||
u8 seq[8]; /* sequence counter (set: RX, get: TX) */
|
||||
u16 key_len;
|
||||
u8 key[0];
|
||||
} crypt;
|
||||
struct {
|
||||
u32 flags_and;
|
||||
u32 flags_or;
|
||||
} set_flags_sta;
|
||||
struct {
|
||||
u16 rid;
|
||||
u16 len;
|
||||
u8 data[0];
|
||||
} rid;
|
||||
struct {
|
||||
u8 len;
|
||||
u8 data[0];
|
||||
} generic_elem;
|
||||
struct {
|
||||
#define MLME_STA_DEAUTH 0
|
||||
#define MLME_STA_DISASSOC 1
|
||||
u16 cmd;
|
||||
u16 reason_code;
|
||||
} mlme;
|
||||
struct {
|
||||
u8 ssid_len;
|
||||
u8 ssid[32];
|
||||
} scan_req;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
|
||||
#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
|
||||
|
||||
#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
|
||||
#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
|
||||
#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
|
||||
#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
|
||||
#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
|
||||
#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
|
||||
|
||||
|
||||
#endif /* HOSTAP_COMMON_H */
|
55
drivers/net/wireless/hostap/hostap_config.h
Normal file
55
drivers/net/wireless/hostap/hostap_config.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef HOSTAP_CONFIG_H
|
||||
#define HOSTAP_CONFIG_H
|
||||
|
||||
#define PRISM2_VERSION "0.4.4-kernel"
|
||||
|
||||
/* In the previous versions of Host AP driver, support for user space version
|
||||
* of IEEE 802.11 management (hostapd) used to be disabled in the default
|
||||
* configuration. From now on, support for hostapd is always included and it is
|
||||
* possible to disable kernel driver version of IEEE 802.11 management with a
|
||||
* separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */
|
||||
/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
|
||||
/* Maximum number of events handler per one interrupt */
|
||||
#define PRISM2_MAX_INTERRUPT_EVENTS 20
|
||||
|
||||
/* Include code for downloading firmware images into volatile RAM. */
|
||||
#define PRISM2_DOWNLOAD_SUPPORT
|
||||
|
||||
/* Allow kernel configuration to enable download support. */
|
||||
#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE)
|
||||
#define PRISM2_DOWNLOAD_SUPPORT
|
||||
#endif
|
||||
|
||||
#ifdef PRISM2_DOWNLOAD_SUPPORT
|
||||
/* Allow writing firmware images into flash, i.e., to non-volatile storage.
|
||||
* Before you enable this option, you should make absolutely sure that you are
|
||||
* using prism2_srec utility that comes with THIS version of the driver!
|
||||
* In addition, please note that it is possible to kill your card with
|
||||
* non-volatile download if you are using incorrect image. This feature has not
|
||||
* been fully tested, so please be careful with it. */
|
||||
/* #define PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
#endif /* PRISM2_DOWNLOAD_SUPPORT */
|
||||
|
||||
/* Save low-level I/O for debugging. This should not be enabled in normal use.
|
||||
*/
|
||||
/* #define PRISM2_IO_DEBUG */
|
||||
|
||||
/* Following defines can be used to remove unneeded parts of the driver, e.g.,
|
||||
* to limit the size of the kernel module. Definitions can be added here in
|
||||
* hostap_config.h or they can be added to make command with EXTRA_CFLAGS,
|
||||
* e.g.,
|
||||
* 'make pccard EXTRA_CFLAGS="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"'
|
||||
*/
|
||||
|
||||
/* Do not include debug messages into the driver */
|
||||
/* #define PRISM2_NO_DEBUG */
|
||||
|
||||
/* Do not include /proc/net/prism2/wlan#/{registers,debug} */
|
||||
/* #define PRISM2_NO_PROCFS_DEBUG */
|
||||
|
||||
/* Do not include station functionality (i.e., allow only Master (Host AP) mode
|
||||
*/
|
||||
/* #define PRISM2_NO_STATION_MODES */
|
||||
|
||||
#endif /* HOSTAP_CONFIG_H */
|
1030
drivers/net/wireless/hostap/hostap_cs.c
Normal file
1030
drivers/net/wireless/hostap/hostap_cs.c
Normal file
File diff suppressed because it is too large
Load Diff
766
drivers/net/wireless/hostap/hostap_download.c
Normal file
766
drivers/net/wireless/hostap/hostap_download.c
Normal file
@ -0,0 +1,766 @@
|
||||
static int prism2_enable_aux_port(struct net_device *dev, int enable)
|
||||
{
|
||||
u16 val, reg;
|
||||
int i, tries;
|
||||
unsigned long flags;
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
if (local->no_pri) {
|
||||
if (enable) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux "
|
||||
"port is already enabled\n", dev->name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&local->cmdlock, flags);
|
||||
|
||||
/* wait until busy bit is clear */
|
||||
tries = HFA384X_CMD_BUSY_TIMEOUT;
|
||||
while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
|
||||
tries--;
|
||||
udelay(1);
|
||||
}
|
||||
if (tries == 0) {
|
||||
reg = HFA384X_INW(HFA384X_CMD_OFF);
|
||||
spin_unlock_irqrestore(&local->cmdlock, flags);
|
||||
printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n",
|
||||
dev->name, reg);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
val = HFA384X_INW(HFA384X_CONTROL_OFF);
|
||||
|
||||
if (enable) {
|
||||
HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF);
|
||||
HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF);
|
||||
|
||||
if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED)
|
||||
printk("prism2_enable_aux_port: was not disabled!?\n");
|
||||
val &= ~HFA384X_AUX_PORT_MASK;
|
||||
val |= HFA384X_AUX_PORT_ENABLE;
|
||||
} else {
|
||||
HFA384X_OUTW(0, HFA384X_PARAM0_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
|
||||
if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED)
|
||||
printk("prism2_enable_aux_port: was not enabled!?\n");
|
||||
val &= ~HFA384X_AUX_PORT_MASK;
|
||||
val |= HFA384X_AUX_PORT_DISABLE;
|
||||
}
|
||||
HFA384X_OUTW(val, HFA384X_CONTROL_OFF);
|
||||
|
||||
udelay(5);
|
||||
|
||||
i = 10000;
|
||||
while (i > 0) {
|
||||
val = HFA384X_INW(HFA384X_CONTROL_OFF);
|
||||
val &= HFA384X_AUX_PORT_MASK;
|
||||
|
||||
if ((enable && val == HFA384X_AUX_PORT_ENABLED) ||
|
||||
(!enable && val == HFA384X_AUX_PORT_DISABLED))
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
i--;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&local->cmdlock, flags);
|
||||
|
||||
if (i == 0) {
|
||||
printk("prism2_enable_aux_port(%d) timed out\n",
|
||||
enable);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len,
|
||||
void *buf)
|
||||
{
|
||||
u16 page, offset;
|
||||
if (addr & 1 || len & 1)
|
||||
return -1;
|
||||
|
||||
page = addr >> 7;
|
||||
offset = addr & 0x7f;
|
||||
|
||||
HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
|
||||
HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
|
||||
|
||||
udelay(5);
|
||||
|
||||
#ifdef PRISM2_PCI
|
||||
{
|
||||
u16 *pos = (u16 *) buf;
|
||||
while (len > 0) {
|
||||
*pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF);
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
#else /* PRISM2_PCI */
|
||||
HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2);
|
||||
#endif /* PRISM2_PCI */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len,
|
||||
void *buf)
|
||||
{
|
||||
u16 page, offset;
|
||||
if (addr & 1 || len & 1)
|
||||
return -1;
|
||||
|
||||
page = addr >> 7;
|
||||
offset = addr & 0x7f;
|
||||
|
||||
HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
|
||||
HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
|
||||
|
||||
udelay(5);
|
||||
|
||||
#ifdef PRISM2_PCI
|
||||
{
|
||||
u16 *pos = (u16 *) buf;
|
||||
while (len > 0) {
|
||||
HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF);
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
#else /* PRISM2_PCI */
|
||||
HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2);
|
||||
#endif /* PRISM2_PCI */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_pda_ok(u8 *buf)
|
||||
{
|
||||
u16 *pda = (u16 *) buf;
|
||||
int pos;
|
||||
u16 len, pdr;
|
||||
|
||||
if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff &&
|
||||
buf[3] == 0x00)
|
||||
return 0;
|
||||
|
||||
pos = 0;
|
||||
while (pos + 1 < PRISM2_PDA_SIZE / 2) {
|
||||
len = le16_to_cpu(pda[pos]);
|
||||
pdr = le16_to_cpu(pda[pos + 1]);
|
||||
if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2)
|
||||
return 0;
|
||||
|
||||
if (pdr == 0x0000 && len == 2) {
|
||||
/* PDA end found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
pos += len + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_aux_dump(struct net_device *dev,
|
||||
unsigned int addr, int len, u8 *buf)
|
||||
{
|
||||
int res;
|
||||
|
||||
prism2_enable_aux_port(dev, 1);
|
||||
res = hfa384x_from_aux(dev, addr, len, buf);
|
||||
prism2_enable_aux_port(dev, 0);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 * prism2_read_pda(struct net_device *dev)
|
||||
{
|
||||
u8 *buf;
|
||||
int res, i, found = 0;
|
||||
#define NUM_PDA_ADDRS 4
|
||||
unsigned int pda_addr[NUM_PDA_ADDRS] = {
|
||||
0x7f0000 /* others than HFA3841 */,
|
||||
0x3f0000 /* HFA3841 */,
|
||||
0x390000 /* apparently used in older cards */,
|
||||
0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */,
|
||||
};
|
||||
|
||||
buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Note: wlan card should be in initial state (just after init cmd)
|
||||
* and no other operations should be performed concurrently. */
|
||||
|
||||
prism2_enable_aux_port(dev, 1);
|
||||
|
||||
for (i = 0; i < NUM_PDA_ADDRS; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x",
|
||||
dev->name, pda_addr[i]);
|
||||
res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf);
|
||||
if (res)
|
||||
continue;
|
||||
if (res == 0 && prism2_pda_ok(buf)) {
|
||||
PDEBUG2(DEBUG_EXTRA2, ": OK\n");
|
||||
found = 1;
|
||||
break;
|
||||
} else {
|
||||
PDEBUG2(DEBUG_EXTRA2, ": failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
prism2_enable_aux_port(dev, 0);
|
||||
|
||||
if (!found) {
|
||||
printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name);
|
||||
kfree(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_volatile(local_info_t *local,
|
||||
struct prism2_download_data *param)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
int ret = 0, i;
|
||||
u16 param0, param1;
|
||||
|
||||
if (local->hw_downloading) {
|
||||
printk(KERN_WARNING "%s: Already downloading - aborting new "
|
||||
"request\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
local->hw_downloading = 1;
|
||||
if (local->pri_only) {
|
||||
hfa384x_disable_interrupts(dev);
|
||||
} else {
|
||||
prism2_hw_shutdown(dev, 0);
|
||||
|
||||
if (prism2_hw_init(dev, 0)) {
|
||||
printk(KERN_WARNING "%s: Could not initialize card for"
|
||||
" download\n", dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (prism2_enable_aux_port(dev, 1)) {
|
||||
printk(KERN_WARNING "%s: Could not enable AUX port\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
param0 = param->start_addr & 0xffff;
|
||||
param1 = param->start_addr >> 16;
|
||||
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_ENABLE_VOLATILE << 8),
|
||||
param0)) {
|
||||
printk(KERN_WARNING "%s: Download command execution failed\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < param->num_areas; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
|
||||
dev->name, param->data[i].len, param->data[i].addr);
|
||||
if (hfa384x_to_aux(dev, param->data[i].addr,
|
||||
param->data[i].len, param->data[i].data)) {
|
||||
printk(KERN_WARNING "%s: RAM download at 0x%08x "
|
||||
"(len=%d) failed\n", dev->name,
|
||||
param->data[i].addr, param->data[i].len);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_DISABLE << 8), param0)) {
|
||||
printk(KERN_WARNING "%s: Download command execution failed\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
/* ProgMode disable causes the hardware to restart itself from the
|
||||
* given starting address. Give hw some time and ACK command just in
|
||||
* case restart did not happen. */
|
||||
mdelay(5);
|
||||
HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
|
||||
|
||||
if (prism2_enable_aux_port(dev, 0)) {
|
||||
printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
|
||||
dev->name);
|
||||
/* continue anyway.. restart should have taken care of this */
|
||||
}
|
||||
|
||||
mdelay(5);
|
||||
local->hw_downloading = 0;
|
||||
if (prism2_hw_config(dev, 2)) {
|
||||
printk(KERN_WARNING "%s: Card configuration after RAM "
|
||||
"download failed\n", dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
local->hw_downloading = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_enable_genesis(local_info_t *local, int hcr)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff };
|
||||
u8 readbuf[4];
|
||||
|
||||
printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n",
|
||||
dev->name, hcr);
|
||||
local->func->cor_sreset(local);
|
||||
hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
|
||||
local->func->genesis_reset(local, hcr);
|
||||
|
||||
/* Readback test */
|
||||
hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
|
||||
hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
|
||||
hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
|
||||
|
||||
if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) {
|
||||
printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n",
|
||||
hcr);
|
||||
return 0;
|
||||
} else {
|
||||
printk(KERN_DEBUG "Readback test failed, HCR 0x%02x "
|
||||
"write %02x %02x %02x %02x read %02x %02x %02x %02x\n",
|
||||
hcr, initseq[0], initseq[1], initseq[2], initseq[3],
|
||||
readbuf[0], readbuf[1], readbuf[2], readbuf[3]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int prism2_get_ram_size(local_info_t *local)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */
|
||||
if (prism2_enable_genesis(local, 0x1f) == 0)
|
||||
ret = 8;
|
||||
else if (prism2_enable_genesis(local, 0x0f) == 0)
|
||||
ret = 16;
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
/* Disable genesis mode */
|
||||
local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_genesis(local_info_t *local,
|
||||
struct prism2_download_data *param)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
int ram16 = 0, i;
|
||||
int ret = 0;
|
||||
|
||||
if (local->hw_downloading) {
|
||||
printk(KERN_WARNING "%s: Already downloading - aborting new "
|
||||
"request\n", dev->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!local->func->genesis_reset || !local->func->cor_sreset) {
|
||||
printk(KERN_INFO "%s: Genesis mode downloading not supported "
|
||||
"with this hwmodel\n", dev->name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
local->hw_downloading = 1;
|
||||
|
||||
if (prism2_enable_aux_port(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: failed to enable AUX port\n",
|
||||
dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->sram_type == -1) {
|
||||
/* 0x1F for x8 SRAM or 0x0F for x16 SRAM */
|
||||
if (prism2_enable_genesis(local, 0x1f) == 0) {
|
||||
ram16 = 0;
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 "
|
||||
"SRAM\n", dev->name);
|
||||
} else if (prism2_enable_genesis(local, 0x0f) == 0) {
|
||||
ram16 = 1;
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 "
|
||||
"SRAM\n", dev->name);
|
||||
} else {
|
||||
printk(KERN_DEBUG "%s: Could not initiate genesis "
|
||||
"mode\n", dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (prism2_enable_genesis(local, local->sram_type == 8 ?
|
||||
0x1f : 0x0f)) {
|
||||
printk(KERN_DEBUG "%s: Failed to set Genesis "
|
||||
"mode (sram_type=%d)\n", dev->name,
|
||||
local->sram_type);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ram16 = local->sram_type != 8;
|
||||
}
|
||||
|
||||
for (i = 0; i < param->num_areas; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
|
||||
dev->name, param->data[i].len, param->data[i].addr);
|
||||
if (hfa384x_to_aux(dev, param->data[i].addr,
|
||||
param->data[i].len, param->data[i].data)) {
|
||||
printk(KERN_WARNING "%s: RAM download at 0x%08x "
|
||||
"(len=%d) failed\n", dev->name,
|
||||
param->data[i].addr, param->data[i].len);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n");
|
||||
local->func->genesis_reset(local, ram16 ? 0x07 : 0x17);
|
||||
if (prism2_enable_aux_port(dev, 0)) {
|
||||
printk(KERN_DEBUG "%s: Failed to disable AUX port\n",
|
||||
dev->name);
|
||||
}
|
||||
|
||||
mdelay(5);
|
||||
local->hw_downloading = 0;
|
||||
|
||||
PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n");
|
||||
/*
|
||||
* Make sure the INIT command does not generate a command completion
|
||||
* event by disabling interrupts.
|
||||
*/
|
||||
hfa384x_disable_interrupts(dev);
|
||||
if (prism2_hw_init(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: Initialization after genesis mode "
|
||||
"download failed\n", dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n");
|
||||
if (prism2_hw_init2(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: Initialization(2) after genesis mode "
|
||||
"download failed\n", dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
local->hw_downloading = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
|
||||
/* Note! Non-volatile downloading functionality has not yet been tested
|
||||
* thoroughly and it may corrupt flash image and effectively kill the card that
|
||||
* is being updated. You have been warned. */
|
||||
|
||||
static inline int prism2_download_block(struct net_device *dev,
|
||||
u32 addr, u8 *data,
|
||||
u32 bufaddr, int rest_len)
|
||||
{
|
||||
u16 param0, param1;
|
||||
int block_len;
|
||||
|
||||
block_len = rest_len < 4096 ? rest_len : 4096;
|
||||
|
||||
param0 = addr & 0xffff;
|
||||
param1 = addr >> 16;
|
||||
|
||||
HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF);
|
||||
HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
|
||||
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8),
|
||||
param0)) {
|
||||
printk(KERN_WARNING "%s: Flash download command execution "
|
||||
"failed\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hfa384x_to_aux(dev, bufaddr, block_len, data)) {
|
||||
printk(KERN_WARNING "%s: flash download at 0x%08x "
|
||||
"(len=%d) failed\n", dev->name, addr, block_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8),
|
||||
0)) {
|
||||
printk(KERN_WARNING "%s: Flash write command execution "
|
||||
"failed\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return block_len;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_nonvolatile(local_info_t *local,
|
||||
struct prism2_download_data *dl)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
int ret = 0, i;
|
||||
struct {
|
||||
u16 page;
|
||||
u16 offset;
|
||||
u16 len;
|
||||
} dlbuffer;
|
||||
u32 bufaddr;
|
||||
|
||||
if (local->hw_downloading) {
|
||||
printk(KERN_WARNING "%s: Already downloading - aborting new "
|
||||
"request\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER,
|
||||
&dlbuffer, 6, 0);
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "%s: Could not read download buffer "
|
||||
"parameters\n", dev->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dlbuffer.page = le16_to_cpu(dlbuffer.page);
|
||||
dlbuffer.offset = le16_to_cpu(dlbuffer.offset);
|
||||
dlbuffer.len = le16_to_cpu(dlbuffer.len);
|
||||
|
||||
printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n",
|
||||
dlbuffer.len, dlbuffer.page, dlbuffer.offset);
|
||||
|
||||
bufaddr = (dlbuffer.page << 7) + dlbuffer.offset;
|
||||
|
||||
local->hw_downloading = 1;
|
||||
|
||||
if (!local->pri_only) {
|
||||
prism2_hw_shutdown(dev, 0);
|
||||
|
||||
if (prism2_hw_init(dev, 0)) {
|
||||
printk(KERN_WARNING "%s: Could not initialize card for"
|
||||
" download\n", dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
hfa384x_disable_interrupts(dev);
|
||||
|
||||
if (prism2_enable_aux_port(dev, 1)) {
|
||||
printk(KERN_WARNING "%s: Could not enable AUX port\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: starting flash download\n", dev->name);
|
||||
for (i = 0; i < dl->num_areas; i++) {
|
||||
int rest_len = dl->data[i].len;
|
||||
int data_off = 0;
|
||||
|
||||
while (rest_len > 0) {
|
||||
int block_len;
|
||||
|
||||
block_len = prism2_download_block(
|
||||
dev, dl->data[i].addr + data_off,
|
||||
dl->data[i].data + data_off, bufaddr,
|
||||
rest_len);
|
||||
|
||||
if (block_len < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rest_len -= block_len;
|
||||
data_off += block_len;
|
||||
}
|
||||
}
|
||||
|
||||
HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_DISABLE << 8), 0)) {
|
||||
printk(KERN_WARNING "%s: Download command execution failed\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (prism2_enable_aux_port(dev, 0)) {
|
||||
printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
|
||||
dev->name);
|
||||
/* continue anyway.. restart should have taken care of this */
|
||||
}
|
||||
|
||||
mdelay(5);
|
||||
|
||||
local->func->hw_reset(dev);
|
||||
local->hw_downloading = 0;
|
||||
if (prism2_hw_config(dev, 2)) {
|
||||
printk(KERN_WARNING "%s: Card configuration after flash "
|
||||
"download failed\n", dev->name);
|
||||
ret = -1;
|
||||
} else {
|
||||
printk(KERN_INFO "%s: Card initialized successfully after "
|
||||
"flash download\n", dev->name);
|
||||
}
|
||||
|
||||
out:
|
||||
local->hw_downloading = 0;
|
||||
return ret;
|
||||
}
|
||||
#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
|
||||
|
||||
static void prism2_download_free_data(struct prism2_download_data *dl)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dl == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < dl->num_areas; i++)
|
||||
kfree(dl->data[i].data);
|
||||
kfree(dl);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download(local_info_t *local,
|
||||
struct prism2_download_param *param)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
u32 total_len = 0;
|
||||
struct prism2_download_data *dl = NULL;
|
||||
|
||||
printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x "
|
||||
"num_areas=%d\n",
|
||||
param->dl_cmd, param->start_addr, param->num_areas);
|
||||
|
||||
if (param->num_areas > 100) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dl = kmalloc(sizeof(*dl) + param->num_areas *
|
||||
sizeof(struct prism2_download_data_area), GFP_KERNEL);
|
||||
if (dl == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(dl, 0, sizeof(*dl) + param->num_areas *
|
||||
sizeof(struct prism2_download_data_area));
|
||||
dl->dl_cmd = param->dl_cmd;
|
||||
dl->start_addr = param->start_addr;
|
||||
dl->num_areas = param->num_areas;
|
||||
for (i = 0; i < param->num_areas; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2,
|
||||
" area %d: addr=0x%08x len=%d ptr=0x%p\n",
|
||||
i, param->data[i].addr, param->data[i].len,
|
||||
param->data[i].ptr);
|
||||
|
||||
dl->data[i].addr = param->data[i].addr;
|
||||
dl->data[i].len = param->data[i].len;
|
||||
|
||||
total_len += param->data[i].len;
|
||||
if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN ||
|
||||
total_len > PRISM2_MAX_DOWNLOAD_LEN) {
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL);
|
||||
if (dl->data[i].data == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(dl->data[i].data, param->data[i].ptr,
|
||||
param->data[i].len)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
switch (param->dl_cmd) {
|
||||
case PRISM2_DOWNLOAD_VOLATILE:
|
||||
case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT:
|
||||
ret = prism2_download_volatile(local, dl);
|
||||
break;
|
||||
case PRISM2_DOWNLOAD_VOLATILE_GENESIS:
|
||||
case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT:
|
||||
ret = prism2_download_genesis(local, dl);
|
||||
break;
|
||||
case PRISM2_DOWNLOAD_NON_VOLATILE:
|
||||
#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
|
||||
ret = prism2_download_nonvolatile(local, dl);
|
||||
#else /* PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
printk(KERN_INFO "%s: non-volatile downloading not enabled\n",
|
||||
local->dev->name);
|
||||
ret = -EOPNOTSUPP;
|
||||
#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
break;
|
||||
default:
|
||||
printk(KERN_DEBUG "%s: unsupported download command %d\n",
|
||||
local->dev->name, param->dl_cmd);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
};
|
||||
|
||||
out:
|
||||
if (ret == 0 && dl &&
|
||||
param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) {
|
||||
prism2_download_free_data(local->dl_pri);
|
||||
local->dl_pri = dl;
|
||||
} else if (ret == 0 && dl &&
|
||||
param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) {
|
||||
prism2_download_free_data(local->dl_sec);
|
||||
local->dl_sec = dl;
|
||||
} else
|
||||
prism2_download_free_data(dl);
|
||||
|
||||
return ret;
|
||||
}
|
3445
drivers/net/wireless/hostap/hostap_hw.c
Normal file
3445
drivers/net/wireless/hostap/hostap_hw.c
Normal file
File diff suppressed because it is too large
Load Diff
499
drivers/net/wireless/hostap/hostap_info.c
Normal file
499
drivers/net/wireless/hostap/hostap_info.c
Normal file
@ -0,0 +1,499 @@
|
||||
/* Host AP driver Info Frame processing (part of hostap.o module) */
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
struct hfa384x_comm_tallies *tallies;
|
||||
|
||||
if (left < sizeof(struct hfa384x_comm_tallies)) {
|
||||
printk(KERN_DEBUG "%s: too short (len=%d) commtallies "
|
||||
"info frame\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
tallies = (struct hfa384x_comm_tallies *) buf;
|
||||
#define ADD_COMM_TALLIES(name) \
|
||||
local->comm_tallies.name += le16_to_cpu(tallies->name)
|
||||
ADD_COMM_TALLIES(tx_unicast_frames);
|
||||
ADD_COMM_TALLIES(tx_multicast_frames);
|
||||
ADD_COMM_TALLIES(tx_fragments);
|
||||
ADD_COMM_TALLIES(tx_unicast_octets);
|
||||
ADD_COMM_TALLIES(tx_multicast_octets);
|
||||
ADD_COMM_TALLIES(tx_deferred_transmissions);
|
||||
ADD_COMM_TALLIES(tx_single_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_multiple_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_retry_limit_exceeded);
|
||||
ADD_COMM_TALLIES(tx_discards);
|
||||
ADD_COMM_TALLIES(rx_unicast_frames);
|
||||
ADD_COMM_TALLIES(rx_multicast_frames);
|
||||
ADD_COMM_TALLIES(rx_fragments);
|
||||
ADD_COMM_TALLIES(rx_unicast_octets);
|
||||
ADD_COMM_TALLIES(rx_multicast_octets);
|
||||
ADD_COMM_TALLIES(rx_fcs_errors);
|
||||
ADD_COMM_TALLIES(rx_discards_no_buffer);
|
||||
ADD_COMM_TALLIES(tx_discards_wrong_sa);
|
||||
ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
|
||||
ADD_COMM_TALLIES(rx_message_in_msg_fragments);
|
||||
ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
|
||||
#undef ADD_COMM_TALLIES
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
struct hfa384x_comm_tallies32 *tallies;
|
||||
|
||||
if (left < sizeof(struct hfa384x_comm_tallies32)) {
|
||||
printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 "
|
||||
"info frame\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
tallies = (struct hfa384x_comm_tallies32 *) buf;
|
||||
#define ADD_COMM_TALLIES(name) \
|
||||
local->comm_tallies.name += le32_to_cpu(tallies->name)
|
||||
ADD_COMM_TALLIES(tx_unicast_frames);
|
||||
ADD_COMM_TALLIES(tx_multicast_frames);
|
||||
ADD_COMM_TALLIES(tx_fragments);
|
||||
ADD_COMM_TALLIES(tx_unicast_octets);
|
||||
ADD_COMM_TALLIES(tx_multicast_octets);
|
||||
ADD_COMM_TALLIES(tx_deferred_transmissions);
|
||||
ADD_COMM_TALLIES(tx_single_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_multiple_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_retry_limit_exceeded);
|
||||
ADD_COMM_TALLIES(tx_discards);
|
||||
ADD_COMM_TALLIES(rx_unicast_frames);
|
||||
ADD_COMM_TALLIES(rx_multicast_frames);
|
||||
ADD_COMM_TALLIES(rx_fragments);
|
||||
ADD_COMM_TALLIES(rx_unicast_octets);
|
||||
ADD_COMM_TALLIES(rx_multicast_octets);
|
||||
ADD_COMM_TALLIES(rx_fcs_errors);
|
||||
ADD_COMM_TALLIES(rx_discards_no_buffer);
|
||||
ADD_COMM_TALLIES(tx_discards_wrong_sa);
|
||||
ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
|
||||
ADD_COMM_TALLIES(rx_message_in_msg_fragments);
|
||||
ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
|
||||
#undef ADD_COMM_TALLIES
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_commtallies(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
if (local->tallies32)
|
||||
prism2_info_commtallies32(local, buf, left);
|
||||
else
|
||||
prism2_info_commtallies16(local, buf, left);
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
#ifndef PRISM2_NO_DEBUG
|
||||
static const char* hfa384x_linkstatus_str(u16 linkstatus)
|
||||
{
|
||||
switch (linkstatus) {
|
||||
case HFA384X_LINKSTATUS_CONNECTED:
|
||||
return "Connected";
|
||||
case HFA384X_LINKSTATUS_DISCONNECTED:
|
||||
return "Disconnected";
|
||||
case HFA384X_LINKSTATUS_AP_CHANGE:
|
||||
return "Access point change";
|
||||
case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE:
|
||||
return "Access point out of range";
|
||||
case HFA384X_LINKSTATUS_AP_IN_RANGE:
|
||||
return "Access point in range";
|
||||
case HFA384X_LINKSTATUS_ASSOC_FAILED:
|
||||
return "Association failed";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
#endif /* PRISM2_NO_DEBUG */
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
u16 val;
|
||||
int non_sta_mode;
|
||||
|
||||
/* Alloc new JoinRequests to occur since LinkStatus for the previous
|
||||
* has been received */
|
||||
local->last_join_time = 0;
|
||||
|
||||
if (left != 2) {
|
||||
printk(KERN_DEBUG "%s: invalid linkstatus info frame "
|
||||
"length %d\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
non_sta_mode = local->iw_mode == IW_MODE_MASTER ||
|
||||
local->iw_mode == IW_MODE_REPEAT ||
|
||||
local->iw_mode == IW_MODE_MONITOR;
|
||||
|
||||
val = buf[0] | (buf[1] << 8);
|
||||
if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) {
|
||||
PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n",
|
||||
local->dev->name, val, hfa384x_linkstatus_str(val));
|
||||
}
|
||||
|
||||
if (non_sta_mode) {
|
||||
netif_carrier_on(local->dev);
|
||||
netif_carrier_on(local->ddev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get current BSSID later in scheduled task */
|
||||
set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info);
|
||||
local->prev_link_status = val;
|
||||
schedule_work(&local->info_queue);
|
||||
}
|
||||
|
||||
|
||||
static void prism2_host_roaming(local_info_t *local)
|
||||
{
|
||||
struct hfa384x_join_request req;
|
||||
struct net_device *dev = local->dev;
|
||||
struct hfa384x_hostscan_result *selected, *entry;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
if (local->last_join_time &&
|
||||
time_before(jiffies, local->last_join_time + 10 * HZ)) {
|
||||
PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been "
|
||||
"completed - waiting for it before issuing new one\n",
|
||||
dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ScanResults are sorted: first ESS results in decreasing signal
|
||||
* quality then IBSS results in similar order.
|
||||
* Trivial roaming policy: just select the first entry.
|
||||
* This could probably be improved by adding hysteresis to limit
|
||||
* number of handoffs, etc.
|
||||
*
|
||||
* Could do periodic RID_SCANREQUEST or Inquire F101 to get new
|
||||
* ScanResults */
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
if (local->last_scan_results == NULL ||
|
||||
local->last_scan_results_count == 0) {
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n",
|
||||
dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
selected = &local->last_scan_results[0];
|
||||
|
||||
if (local->preferred_ap[0] || local->preferred_ap[1] ||
|
||||
local->preferred_ap[2] || local->preferred_ap[3] ||
|
||||
local->preferred_ap[4] || local->preferred_ap[5]) {
|
||||
/* Try to find preferred AP */
|
||||
PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID " MACSTR "\n",
|
||||
dev->name, MAC2STR(local->preferred_ap));
|
||||
for (i = 0; i < local->last_scan_results_count; i++) {
|
||||
entry = &local->last_scan_results[i];
|
||||
if (memcmp(local->preferred_ap, entry->bssid, 6) == 0)
|
||||
{
|
||||
PDEBUG(DEBUG_EXTRA, "%s: using preferred AP "
|
||||
"selection\n", dev->name);
|
||||
selected = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(req.bssid, selected->bssid, 6);
|
||||
req.channel = selected->chid;
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
|
||||
PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=" MACSTR " channel=%d\n",
|
||||
dev->name, MAC2STR(req.bssid), le16_to_cpu(req.channel));
|
||||
if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
|
||||
sizeof(req))) {
|
||||
printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name);
|
||||
}
|
||||
local->last_join_time = jiffies;
|
||||
}
|
||||
|
||||
|
||||
static void hostap_report_scan_complete(local_info_t *local)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
|
||||
/* Inform user space about new scan results (just empty event,
|
||||
* SIOCGIWSCAN can be used to fetch data */
|
||||
wrqu.data.length = 0;
|
||||
wrqu.data.flags = 0;
|
||||
wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL);
|
||||
|
||||
/* Allow SIOCGIWSCAN handling to occur since we have received
|
||||
* scanning result */
|
||||
local->scan_timestamp = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_scanresults(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
u16 *pos;
|
||||
int new_count, i;
|
||||
unsigned long flags;
|
||||
struct hfa384x_scan_result *res;
|
||||
struct hfa384x_hostscan_result *results, *prev;
|
||||
|
||||
if (left < 4) {
|
||||
printk(KERN_DEBUG "%s: invalid scanresult info frame "
|
||||
"length %d\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
pos = (u16 *) buf;
|
||||
pos++;
|
||||
pos++;
|
||||
left -= 4;
|
||||
|
||||
new_count = left / sizeof(struct hfa384x_scan_result);
|
||||
results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result),
|
||||
GFP_ATOMIC);
|
||||
if (results == NULL)
|
||||
return;
|
||||
|
||||
/* Convert to hostscan result format. */
|
||||
res = (struct hfa384x_scan_result *) pos;
|
||||
for (i = 0; i < new_count; i++) {
|
||||
memcpy(&results[i], &res[i],
|
||||
sizeof(struct hfa384x_scan_result));
|
||||
results[i].atim = 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
local->last_scan_type = PRISM2_SCAN;
|
||||
prev = local->last_scan_results;
|
||||
local->last_scan_results = results;
|
||||
local->last_scan_results_count = new_count;
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
kfree(prev);
|
||||
|
||||
hostap_report_scan_complete(local);
|
||||
|
||||
/* Perform rest of ScanResults handling later in scheduled task */
|
||||
set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info);
|
||||
schedule_work(&local->info_queue);
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_hostscanresults(local_info_t *local,
|
||||
unsigned char *buf, int left)
|
||||
{
|
||||
int i, result_size, copy_len, new_count;
|
||||
struct hfa384x_hostscan_result *results, *prev;
|
||||
unsigned long flags;
|
||||
u16 *pos;
|
||||
u8 *ptr;
|
||||
|
||||
wake_up_interruptible(&local->hostscan_wq);
|
||||
|
||||
if (left < 4) {
|
||||
printk(KERN_DEBUG "%s: invalid hostscanresult info frame "
|
||||
"length %d\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
pos = (u16 *) buf;
|
||||
copy_len = result_size = le16_to_cpu(*pos);
|
||||
if (result_size == 0) {
|
||||
printk(KERN_DEBUG "%s: invalid result_size (0) in "
|
||||
"hostscanresults\n", local->dev->name);
|
||||
return;
|
||||
}
|
||||
if (copy_len > sizeof(struct hfa384x_hostscan_result))
|
||||
copy_len = sizeof(struct hfa384x_hostscan_result);
|
||||
|
||||
pos++;
|
||||
pos++;
|
||||
left -= 4;
|
||||
ptr = (u8 *) pos;
|
||||
|
||||
new_count = left / result_size;
|
||||
results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result),
|
||||
GFP_ATOMIC);
|
||||
if (results == NULL)
|
||||
return;
|
||||
memset(results, 0, new_count * sizeof(struct hfa384x_hostscan_result));
|
||||
|
||||
for (i = 0; i < new_count; i++) {
|
||||
memcpy(&results[i], ptr, copy_len);
|
||||
ptr += result_size;
|
||||
left -= result_size;
|
||||
}
|
||||
|
||||
if (left) {
|
||||
printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n",
|
||||
local->dev->name, left, result_size);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
local->last_scan_type = PRISM2_HOSTSCAN;
|
||||
prev = local->last_scan_results;
|
||||
local->last_scan_results = results;
|
||||
local->last_scan_results_count = new_count;
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
kfree(prev);
|
||||
|
||||
hostap_report_scan_complete(local);
|
||||
}
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
void hostap_info_process(local_info_t *local, struct sk_buff *skb)
|
||||
{
|
||||
struct hfa384x_info_frame *info;
|
||||
unsigned char *buf;
|
||||
int left;
|
||||
#ifndef PRISM2_NO_DEBUG
|
||||
int i;
|
||||
#endif /* PRISM2_NO_DEBUG */
|
||||
|
||||
info = (struct hfa384x_info_frame *) skb->data;
|
||||
buf = skb->data + sizeof(*info);
|
||||
left = skb->len - sizeof(*info);
|
||||
|
||||
switch (info->type) {
|
||||
case HFA384X_INFO_COMMTALLIES:
|
||||
prism2_info_commtallies(local, buf, left);
|
||||
break;
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
case HFA384X_INFO_LINKSTATUS:
|
||||
prism2_info_linkstatus(local, buf, left);
|
||||
break;
|
||||
|
||||
case HFA384X_INFO_SCANRESULTS:
|
||||
prism2_info_scanresults(local, buf, left);
|
||||
break;
|
||||
|
||||
case HFA384X_INFO_HOSTSCANRESULTS:
|
||||
prism2_info_hostscanresults(local, buf, left);
|
||||
break;
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
#ifndef PRISM2_NO_DEBUG
|
||||
default:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n",
|
||||
local->dev->name, info->len, info->type);
|
||||
PDEBUG(DEBUG_EXTRA, "Unknown info frame:");
|
||||
for (i = 0; i < (left < 100 ? left : 100); i++)
|
||||
PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]);
|
||||
PDEBUG2(DEBUG_EXTRA, "\n");
|
||||
break;
|
||||
#endif /* PRISM2_NO_DEBUG */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
static void handle_info_queue_linkstatus(local_info_t *local)
|
||||
{
|
||||
int val = local->prev_link_status;
|
||||
int connected;
|
||||
union iwreq_data wrqu;
|
||||
|
||||
connected =
|
||||
val == HFA384X_LINKSTATUS_CONNECTED ||
|
||||
val == HFA384X_LINKSTATUS_AP_CHANGE ||
|
||||
val == HFA384X_LINKSTATUS_AP_IN_RANGE;
|
||||
|
||||
if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID,
|
||||
local->bssid, ETH_ALEN, 1) < 0) {
|
||||
printk(KERN_DEBUG "%s: could not read CURRENTBSSID after "
|
||||
"LinkStatus event\n", local->dev->name);
|
||||
} else {
|
||||
PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=" MACSTR "\n",
|
||||
local->dev->name,
|
||||
MAC2STR((unsigned char *) local->bssid));
|
||||
if (local->wds_type & HOSTAP_WDS_AP_CLIENT)
|
||||
hostap_add_sta(local->ap, local->bssid);
|
||||
}
|
||||
|
||||
/* Get BSSID if we have a valid AP address */
|
||||
if (connected) {
|
||||
netif_carrier_on(local->dev);
|
||||
netif_carrier_on(local->ddev);
|
||||
memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN);
|
||||
} else {
|
||||
netif_carrier_off(local->dev);
|
||||
netif_carrier_off(local->ddev);
|
||||
memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
|
||||
}
|
||||
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
|
||||
|
||||
/*
|
||||
* Filter out sequential disconnect events in order not to cause a
|
||||
* flood of SIOCGIWAP events that have a race condition with EAPOL
|
||||
* frames and can confuse wpa_supplicant about the current association
|
||||
* status.
|
||||
*/
|
||||
if (connected || local->prev_linkstatus_connected)
|
||||
wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
|
||||
local->prev_linkstatus_connected = connected;
|
||||
}
|
||||
|
||||
|
||||
static void handle_info_queue_scanresults(local_info_t *local)
|
||||
{
|
||||
if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA)
|
||||
prism2_host_roaming(local);
|
||||
|
||||
if (local->host_roaming == 2 && local->iw_mode == IW_MODE_INFRA &&
|
||||
memcmp(local->preferred_ap, "\x00\x00\x00\x00\x00\x00",
|
||||
ETH_ALEN) != 0) {
|
||||
/*
|
||||
* Firmware seems to be getting into odd state in host_roaming
|
||||
* mode 2 when hostscan is used without join command, so try
|
||||
* to fix this by re-joining the current AP. This does not
|
||||
* actually trigger a new association if the current AP is
|
||||
* still in the scan results.
|
||||
*/
|
||||
prism2_host_roaming(local);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Called only as scheduled task after receiving info frames (used to avoid
|
||||
* pending too much time in HW IRQ handler). */
|
||||
static void handle_info_queue(void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
|
||||
if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS,
|
||||
&local->pending_info))
|
||||
handle_info_queue_linkstatus(local);
|
||||
|
||||
if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS,
|
||||
&local->pending_info))
|
||||
handle_info_queue_scanresults(local);
|
||||
}
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
|
||||
void hostap_info_init(local_info_t *local)
|
||||
{
|
||||
skb_queue_head_init(&local->info_list);
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
INIT_WORK(&local->info_queue, handle_info_queue, local);
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(hostap_info_init);
|
||||
EXPORT_SYMBOL(hostap_info_process);
|
4102
drivers/net/wireless/hostap/hostap_ioctl.c
Normal file
4102
drivers/net/wireless/hostap/hostap_ioctl.c
Normal file
File diff suppressed because it is too large
Load Diff
473
drivers/net/wireless/hostap/hostap_pci.c
Normal file
473
drivers/net/wireless/hostap/hostap_pci.c
Normal file
@ -0,0 +1,473 @@
|
||||
#define PRISM2_PCI
|
||||
|
||||
/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on
|
||||
* driver patches from Reyk Floeter <reyk@vantronix.net> and
|
||||
* Andy Warner <andyw@pobox.com> */
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hostap_wlan.h"
|
||||
|
||||
|
||||
static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
|
||||
static char *dev_info = "hostap_pci";
|
||||
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN "
|
||||
"PCI cards.");
|
||||
MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(PRISM2_VERSION);
|
||||
|
||||
|
||||
/* struct local_info::hw_priv */
|
||||
struct hostap_pci_priv {
|
||||
void __iomem *mem_start;
|
||||
};
|
||||
|
||||
|
||||
/* FIX: do we need mb/wmb/rmb with memory operations? */
|
||||
|
||||
|
||||
static struct pci_device_id prism2_pci_id_table[] __devinitdata = {
|
||||
/* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */
|
||||
{ 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID },
|
||||
/* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */
|
||||
{ 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID },
|
||||
/* Samsung MagicLAN SWL-2210P */
|
||||
{ 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
|
||||
static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
|
||||
writeb(v, hw_priv->mem_start + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u8 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = readb(hw_priv->mem_start + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
|
||||
writew(v, hw_priv->mem_start + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u16 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = readw(hw_priv->mem_start + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
|
||||
#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
|
||||
#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
|
||||
#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
|
||||
#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), cpu_to_le16((v)))
|
||||
#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw_debug(dev, (a)))
|
||||
|
||||
#else /* PRISM2_IO_DEBUG */
|
||||
|
||||
static inline void hfa384x_outb(struct net_device *dev, int a, u8 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
struct hostap_pci_priv *hw_priv;
|
||||
iface = netdev_priv(dev);
|
||||
hw_priv = iface->local->hw_priv;
|
||||
writeb(v, hw_priv->mem_start + a);
|
||||
}
|
||||
|
||||
static inline u8 hfa384x_inb(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
struct hostap_pci_priv *hw_priv;
|
||||
iface = netdev_priv(dev);
|
||||
hw_priv = iface->local->hw_priv;
|
||||
return readb(hw_priv->mem_start + a);
|
||||
}
|
||||
|
||||
static inline void hfa384x_outw(struct net_device *dev, int a, u16 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
struct hostap_pci_priv *hw_priv;
|
||||
iface = netdev_priv(dev);
|
||||
hw_priv = iface->local->hw_priv;
|
||||
writew(v, hw_priv->mem_start + a);
|
||||
}
|
||||
|
||||
static inline u16 hfa384x_inw(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
struct hostap_pci_priv *hw_priv;
|
||||
iface = netdev_priv(dev);
|
||||
hw_priv = iface->local->hw_priv;
|
||||
return readw(hw_priv->mem_start + a);
|
||||
}
|
||||
|
||||
#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v))
|
||||
#define HFA384X_INB(a) hfa384x_inb(dev, (a))
|
||||
#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v))
|
||||
#define HFA384X_INW(a) hfa384x_inw(dev, (a))
|
||||
#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), cpu_to_le16((v)))
|
||||
#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw(dev, (a)))
|
||||
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
|
||||
|
||||
static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
|
||||
int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
for ( ; len > 1; len -= 2)
|
||||
*pos++ = HFA384X_INW_DATA(d_off);
|
||||
|
||||
if (len & 1)
|
||||
*((char *) pos) = HFA384X_INB(d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
for ( ; len > 1; len -= 2)
|
||||
HFA384X_OUTW_DATA(*pos++, d_off);
|
||||
|
||||
if (len & 1)
|
||||
HFA384X_OUTB(*((char *) pos), d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* FIX: This might change at some point.. */
|
||||
#include "hostap_hw.c"
|
||||
|
||||
static void prism2_pci_cor_sreset(local_info_t *local)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
u16 reg;
|
||||
|
||||
reg = HFA384X_INB(HFA384X_PCICOR_OFF);
|
||||
printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg);
|
||||
|
||||
/* linux-wlan-ng uses extremely long hold and settle times for
|
||||
* COR sreset. A comment in the driver code mentions that the long
|
||||
* delays appear to be necessary. However, at least IBM 22P6901 seems
|
||||
* to work fine with shorter delays.
|
||||
*
|
||||
* Longer delays can be configured by uncommenting following line: */
|
||||
/* #define PRISM2_PCI_USE_LONG_DELAYS */
|
||||
|
||||
#ifdef PRISM2_PCI_USE_LONG_DELAYS
|
||||
int i;
|
||||
|
||||
HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(250);
|
||||
|
||||
HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(500);
|
||||
|
||||
/* Wait for f/w to complete initialization (CMD:BUSY == 0) */
|
||||
i = 2000000 / 10;
|
||||
while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i)
|
||||
udelay(10);
|
||||
|
||||
#else /* PRISM2_PCI_USE_LONG_DELAYS */
|
||||
|
||||
HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(2);
|
||||
HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(2);
|
||||
|
||||
#endif /* PRISM2_PCI_USE_LONG_DELAYS */
|
||||
|
||||
if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) {
|
||||
printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void prism2_pci_genesis_reset(local_info_t *local, int hcr)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
|
||||
HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF);
|
||||
mdelay(10);
|
||||
HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF);
|
||||
mdelay(10);
|
||||
HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF);
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
|
||||
static struct prism2_helper_functions prism2_pci_funcs =
|
||||
{
|
||||
.card_present = NULL,
|
||||
.cor_sreset = prism2_pci_cor_sreset,
|
||||
.dev_open = NULL,
|
||||
.dev_close = NULL,
|
||||
.genesis_reset = prism2_pci_genesis_reset,
|
||||
.hw_type = HOSTAP_HW_PCI,
|
||||
};
|
||||
|
||||
|
||||
static int prism2_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
unsigned long phymem;
|
||||
void __iomem *mem = NULL;
|
||||
local_info_t *local = NULL;
|
||||
struct net_device *dev = NULL;
|
||||
static int cards_found /* = 0 */;
|
||||
int irq_registered = 0;
|
||||
struct hostap_interface *iface;
|
||||
struct hostap_pci_priv *hw_priv;
|
||||
|
||||
hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
|
||||
if (hw_priv == NULL)
|
||||
return -ENOMEM;
|
||||
memset(hw_priv, 0, sizeof(*hw_priv));
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
return -EIO;
|
||||
|
||||
phymem = pci_resource_start(pdev, 0);
|
||||
|
||||
if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) {
|
||||
printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n");
|
||||
goto err_out_disable;
|
||||
}
|
||||
|
||||
mem = ioremap(phymem, pci_resource_len(pdev, 0));
|
||||
if (mem == NULL) {
|
||||
printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dev = prism2_init_local_data(&prism2_pci_funcs, cards_found,
|
||||
&pdev->dev);
|
||||
if (dev == NULL)
|
||||
goto fail;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
local->hw_priv = hw_priv;
|
||||
cards_found++;
|
||||
|
||||
dev->irq = pdev->irq;
|
||||
hw_priv->mem_start = mem;
|
||||
|
||||
prism2_pci_cor_sreset(local);
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
|
||||
dev)) {
|
||||
printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
|
||||
goto fail;
|
||||
} else
|
||||
irq_registered = 1;
|
||||
|
||||
if (!local->pri_only && prism2_hw_config(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: hardware initialization failed\n",
|
||||
dev_info);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: Intersil Prism2.5 PCI: "
|
||||
"mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq);
|
||||
|
||||
return hostap_hw_ready(dev);
|
||||
|
||||
fail:
|
||||
kfree(hw_priv);
|
||||
|
||||
if (irq_registered && dev)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
if (mem)
|
||||
iounmap(mem);
|
||||
|
||||
release_mem_region(phymem, pci_resource_len(pdev, 0));
|
||||
|
||||
err_out_disable:
|
||||
pci_disable_device(pdev);
|
||||
kfree(hw_priv);
|
||||
if (local)
|
||||
local->hw_priv = NULL;
|
||||
prism2_free_local_data(dev);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct hostap_interface *iface;
|
||||
void __iomem *mem_start;
|
||||
struct hostap_pci_priv *hw_priv;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
iface = netdev_priv(dev);
|
||||
hw_priv = iface->local->hw_priv;
|
||||
|
||||
/* Reset the hardware, and ensure interrupts are disabled. */
|
||||
prism2_pci_cor_sreset(iface->local);
|
||||
hfa384x_disable_interrupts(dev);
|
||||
|
||||
if (dev->irq)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
mem_start = hw_priv->mem_start;
|
||||
kfree(hw_priv);
|
||||
iface->local->hw_priv = NULL;
|
||||
prism2_free_local_data(dev);
|
||||
|
||||
iounmap(mem_start);
|
||||
|
||||
release_mem_region(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int prism2_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
{
|
||||
struct net_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
if (netif_running(dev)) {
|
||||
netif_stop_queue(dev);
|
||||
netif_device_detach(dev);
|
||||
}
|
||||
prism2_suspend(dev);
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prism2_pci_resume(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
pci_enable_device(pdev);
|
||||
pci_restore_state(pdev);
|
||||
prism2_hw_config(dev, 0);
|
||||
if (netif_running(dev)) {
|
||||
netif_device_attach(dev);
|
||||
netif_start_queue(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, prism2_pci_id_table);
|
||||
|
||||
static struct pci_driver prism2_pci_drv_id = {
|
||||
.name = "prism2_pci",
|
||||
.id_table = prism2_pci_id_table,
|
||||
.probe = prism2_pci_probe,
|
||||
.remove = prism2_pci_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = prism2_pci_suspend,
|
||||
.resume = prism2_pci_resume,
|
||||
#endif /* CONFIG_PM */
|
||||
/* Linux 2.4.6 added save_state and enable_wake that are not used here
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
static int __init init_prism2_pci(void)
|
||||
{
|
||||
printk(KERN_INFO "%s: %s\n", dev_info, version);
|
||||
|
||||
return pci_register_driver(&prism2_pci_drv_id);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_prism2_pci(void)
|
||||
{
|
||||
pci_unregister_driver(&prism2_pci_drv_id);
|
||||
printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_prism2_pci);
|
||||
module_exit(exit_prism2_pci);
|
645
drivers/net/wireless/hostap/hostap_plx.c
Normal file
645
drivers/net/wireless/hostap/hostap_plx.c
Normal file
@ -0,0 +1,645 @@
|
||||
#define PRISM2_PLX
|
||||
|
||||
/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
|
||||
* based on:
|
||||
* - Host AP driver patch from james@madingley.org
|
||||
* - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hostap_wlan.h"
|
||||
|
||||
|
||||
static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
|
||||
static char *dev_info = "hostap_plx";
|
||||
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
|
||||
"cards (PLX).");
|
||||
MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(PRISM2_VERSION);
|
||||
|
||||
|
||||
static int ignore_cis;
|
||||
module_param(ignore_cis, int, 0444);
|
||||
MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
|
||||
|
||||
|
||||
/* struct local_info::hw_priv */
|
||||
struct hostap_plx_priv {
|
||||
void __iomem *attr_mem;
|
||||
unsigned int cor_offset;
|
||||
};
|
||||
|
||||
|
||||
#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */
|
||||
#define COR_SRESET 0x80
|
||||
#define COR_LEVLREQ 0x40
|
||||
#define COR_ENABLE_FUNC 0x01
|
||||
/* PCI Configuration Registers */
|
||||
#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */
|
||||
/* Local Configuration Registers */
|
||||
#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */
|
||||
#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
|
||||
#define PLX_CNTRL 0x50
|
||||
#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
|
||||
|
||||
|
||||
#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
|
||||
|
||||
static struct pci_device_id prism2_plx_id_table[] __devinitdata = {
|
||||
PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
|
||||
PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
|
||||
PLXDEV(0x126c, 0x8030, "Nortel emobility"),
|
||||
PLXDEV(0x1385, 0x4100, "Netgear MA301"),
|
||||
PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
|
||||
PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
|
||||
PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
|
||||
PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
|
||||
PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
|
||||
PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
|
||||
PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
|
||||
PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid
|
||||
* is not listed here, you will need to add it here to get the driver
|
||||
* initialized. */
|
||||
static struct prism2_plx_manfid {
|
||||
u16 manfid1, manfid2;
|
||||
} prism2_plx_known_manfids[] = {
|
||||
{ 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */,
|
||||
{ 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */,
|
||||
{ 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */,
|
||||
{ 0x0126, 0x8000 } /* Proxim RangeLAN */,
|
||||
{ 0x0138, 0x0002 } /* Compaq WL100 */,
|
||||
{ 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */,
|
||||
{ 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */,
|
||||
{ 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */,
|
||||
{ 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */,
|
||||
{ 0x028a, 0x0002 } /* D-Link DRC-650 */,
|
||||
{ 0x0250, 0x0002 } /* Samsung SWL2000-N */,
|
||||
{ 0xc250, 0x0002 } /* EMTAC A2424i */,
|
||||
{ 0xd601, 0x0002 } /* Z-Com XI300 */,
|
||||
{ 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
|
||||
{ 0, 0}
|
||||
};
|
||||
|
||||
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
|
||||
static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
|
||||
outb(v, dev->base_addr + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u8 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = inb(dev->base_addr + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
|
||||
outw(v, dev->base_addr + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u16 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = inw(dev->base_addr + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
|
||||
u8 *buf, int wc)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
|
||||
outsw(dev->base_addr + a, buf, wc);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline void hfa384x_insw_debug(struct net_device *dev, int a,
|
||||
u8 *buf, int wc)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
|
||||
insw(dev->base_addr + a, buf, wc);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
|
||||
#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
|
||||
#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
|
||||
#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
|
||||
#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
|
||||
#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
|
||||
|
||||
#else /* PRISM2_IO_DEBUG */
|
||||
|
||||
#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
|
||||
#define HFA384X_INB(a) inb(dev->base_addr + (a))
|
||||
#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
|
||||
#define HFA384X_INW(a) inw(dev->base_addr + (a))
|
||||
#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
|
||||
#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
|
||||
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
|
||||
|
||||
static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
|
||||
int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
if (len / 2)
|
||||
HFA384X_INSW(d_off, buf, len / 2);
|
||||
pos += len / 2;
|
||||
|
||||
if (len & 1)
|
||||
*((char *) pos) = HFA384X_INB(d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
if (len / 2)
|
||||
HFA384X_OUTSW(d_off, buf, len / 2);
|
||||
pos += len / 2;
|
||||
|
||||
if (len & 1)
|
||||
HFA384X_OUTB(*((char *) pos), d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* FIX: This might change at some point.. */
|
||||
#include "hostap_hw.c"
|
||||
|
||||
|
||||
static void prism2_plx_cor_sreset(local_info_t *local)
|
||||
{
|
||||
unsigned char corsave;
|
||||
struct hostap_plx_priv *hw_priv = local->hw_priv;
|
||||
|
||||
printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
|
||||
dev_info);
|
||||
|
||||
/* Set sreset bit of COR and clear it after hold time */
|
||||
|
||||
if (hw_priv->attr_mem == NULL) {
|
||||
/* TMD7160 - COR at card's first I/O addr */
|
||||
corsave = inb(hw_priv->cor_offset);
|
||||
outb(corsave | COR_SRESET, hw_priv->cor_offset);
|
||||
mdelay(2);
|
||||
outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
|
||||
mdelay(2);
|
||||
} else {
|
||||
/* PLX9052 */
|
||||
corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
|
||||
writeb(corsave | COR_SRESET,
|
||||
hw_priv->attr_mem + hw_priv->cor_offset);
|
||||
mdelay(2);
|
||||
writeb(corsave & ~COR_SRESET,
|
||||
hw_priv->attr_mem + hw_priv->cor_offset);
|
||||
mdelay(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
|
||||
{
|
||||
unsigned char corsave;
|
||||
struct hostap_plx_priv *hw_priv = local->hw_priv;
|
||||
|
||||
if (hw_priv->attr_mem == NULL) {
|
||||
/* TMD7160 - COR at card's first I/O addr */
|
||||
corsave = inb(hw_priv->cor_offset);
|
||||
outb(corsave | COR_SRESET, hw_priv->cor_offset);
|
||||
mdelay(10);
|
||||
outb(hcr, hw_priv->cor_offset + 2);
|
||||
mdelay(10);
|
||||
outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
|
||||
mdelay(10);
|
||||
} else {
|
||||
/* PLX9052 */
|
||||
corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
|
||||
writeb(corsave | COR_SRESET,
|
||||
hw_priv->attr_mem + hw_priv->cor_offset);
|
||||
mdelay(10);
|
||||
writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
|
||||
mdelay(10);
|
||||
writeb(corsave & ~COR_SRESET,
|
||||
hw_priv->attr_mem + hw_priv->cor_offset);
|
||||
mdelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct prism2_helper_functions prism2_plx_funcs =
|
||||
{
|
||||
.card_present = NULL,
|
||||
.cor_sreset = prism2_plx_cor_sreset,
|
||||
.dev_open = NULL,
|
||||
.dev_close = NULL,
|
||||
.genesis_reset = prism2_plx_genesis_reset,
|
||||
.hw_type = HOSTAP_HW_PLX,
|
||||
};
|
||||
|
||||
|
||||
static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
|
||||
unsigned int *cor_offset,
|
||||
unsigned int *cor_index)
|
||||
{
|
||||
#define CISTPL_CONFIG 0x1A
|
||||
#define CISTPL_MANFID 0x20
|
||||
#define CISTPL_END 0xFF
|
||||
#define CIS_MAX_LEN 256
|
||||
u8 *cis;
|
||||
int i, pos;
|
||||
unsigned int rmsz, rasz, manfid1, manfid2;
|
||||
struct prism2_plx_manfid *manfid;
|
||||
|
||||
cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
|
||||
if (cis == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* read CIS; it is in even offsets in the beginning of attr_mem */
|
||||
for (i = 0; i < CIS_MAX_LEN; i++)
|
||||
cis[i] = readb(attr_mem + 2 * i);
|
||||
printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n",
|
||||
dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]);
|
||||
|
||||
/* set reasonable defaults for Prism2 cards just in case CIS parsing
|
||||
* fails */
|
||||
*cor_offset = 0x3e0;
|
||||
*cor_index = 0x01;
|
||||
manfid1 = manfid2 = 0;
|
||||
|
||||
pos = 0;
|
||||
while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
|
||||
if (pos + cis[pos + 1] >= CIS_MAX_LEN)
|
||||
goto cis_error;
|
||||
|
||||
switch (cis[pos]) {
|
||||
case CISTPL_CONFIG:
|
||||
if (cis[pos + 1] < 1)
|
||||
goto cis_error;
|
||||
rmsz = (cis[pos + 2] & 0x3c) >> 2;
|
||||
rasz = cis[pos + 2] & 0x03;
|
||||
if (4 + rasz + rmsz > cis[pos + 1])
|
||||
goto cis_error;
|
||||
*cor_index = cis[pos + 3] & 0x3F;
|
||||
*cor_offset = 0;
|
||||
for (i = 0; i <= rasz; i++)
|
||||
*cor_offset += cis[pos + 4 + i] << (8 * i);
|
||||
printk(KERN_DEBUG "%s: cor_index=0x%x "
|
||||
"cor_offset=0x%x\n", dev_info,
|
||||
*cor_index, *cor_offset);
|
||||
if (*cor_offset > attr_len) {
|
||||
printk(KERN_ERR "%s: COR offset not within "
|
||||
"attr_mem\n", dev_info);
|
||||
kfree(cis);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case CISTPL_MANFID:
|
||||
if (cis[pos + 1] < 4)
|
||||
goto cis_error;
|
||||
manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
|
||||
manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
|
||||
printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
|
||||
dev_info, manfid1, manfid2);
|
||||
break;
|
||||
}
|
||||
|
||||
pos += cis[pos + 1] + 2;
|
||||
}
|
||||
|
||||
if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
|
||||
goto cis_error;
|
||||
|
||||
for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
|
||||
if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
|
||||
kfree(cis);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
|
||||
" not supported card\n", dev_info, manfid1, manfid2);
|
||||
goto fail;
|
||||
|
||||
cis_error:
|
||||
printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
|
||||
|
||||
fail:
|
||||
kfree(cis);
|
||||
if (ignore_cis) {
|
||||
printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
|
||||
"errors during CIS verification\n", dev_info);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_plx_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
unsigned int pccard_ioaddr, plx_ioaddr;
|
||||
unsigned long pccard_attr_mem;
|
||||
unsigned int pccard_attr_len;
|
||||
void __iomem *attr_mem = NULL;
|
||||
unsigned int cor_offset, cor_index;
|
||||
u32 reg;
|
||||
local_info_t *local = NULL;
|
||||
struct net_device *dev = NULL;
|
||||
struct hostap_interface *iface;
|
||||
static int cards_found /* = 0 */;
|
||||
int irq_registered = 0;
|
||||
int tmd7160;
|
||||
struct hostap_plx_priv *hw_priv;
|
||||
|
||||
hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
|
||||
if (hw_priv == NULL)
|
||||
return -ENOMEM;
|
||||
memset(hw_priv, 0, sizeof(*hw_priv));
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
return -EIO;
|
||||
|
||||
/* National Datacomm NCP130 based on TMD7160, not PLX9052. */
|
||||
tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
|
||||
|
||||
plx_ioaddr = pci_resource_start(pdev, 1);
|
||||
pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
|
||||
|
||||
if (tmd7160) {
|
||||
/* TMD7160 */
|
||||
attr_mem = NULL; /* no access to PC Card attribute memory */
|
||||
|
||||
printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
|
||||
"irq=%d, pccard_io=0x%x\n",
|
||||
plx_ioaddr, pdev->irq, pccard_ioaddr);
|
||||
|
||||
cor_offset = plx_ioaddr;
|
||||
cor_index = 0x04;
|
||||
|
||||
outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
|
||||
mdelay(1);
|
||||
reg = inb(plx_ioaddr);
|
||||
if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
|
||||
printk(KERN_ERR "%s: Error setting COR (expected="
|
||||
"0x%02x, was=0x%02x)\n", dev_info,
|
||||
cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* PLX9052 */
|
||||
pccard_attr_mem = pci_resource_start(pdev, 2);
|
||||
pccard_attr_len = pci_resource_len(pdev, 2);
|
||||
if (pccard_attr_len < PLX_MIN_ATTR_LEN)
|
||||
goto fail;
|
||||
|
||||
|
||||
attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
|
||||
if (attr_mem == NULL) {
|
||||
printk(KERN_ERR "%s: cannot remap attr_mem\n",
|
||||
dev_info);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
|
||||
"mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
|
||||
pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
|
||||
|
||||
if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
|
||||
&cor_offset, &cor_index)) {
|
||||
printk(KERN_INFO "Unknown PC Card CIS - not a "
|
||||
"Prism2/2.5 card?\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
|
||||
"adapter\n");
|
||||
|
||||
/* Write COR to enable PC Card */
|
||||
writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
|
||||
attr_mem + cor_offset);
|
||||
|
||||
/* Enable PCI interrupts if they are not already enabled */
|
||||
reg = inl(plx_ioaddr + PLX_INTCSR);
|
||||
printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
|
||||
if (!(reg & PLX_INTCSR_PCI_INTEN)) {
|
||||
outl(reg | PLX_INTCSR_PCI_INTEN,
|
||||
plx_ioaddr + PLX_INTCSR);
|
||||
if (!(inl(plx_ioaddr + PLX_INTCSR) &
|
||||
PLX_INTCSR_PCI_INTEN)) {
|
||||
printk(KERN_WARNING "%s: Could not enable "
|
||||
"Local Interrupts\n", dev_info);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
reg = inl(plx_ioaddr + PLX_CNTRL);
|
||||
printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
|
||||
"present=%d)\n",
|
||||
reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
|
||||
/* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
|
||||
* not present; but are there really such cards in use(?) */
|
||||
}
|
||||
|
||||
dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
|
||||
&pdev->dev);
|
||||
if (dev == NULL)
|
||||
goto fail;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
local->hw_priv = hw_priv;
|
||||
cards_found++;
|
||||
|
||||
dev->irq = pdev->irq;
|
||||
dev->base_addr = pccard_ioaddr;
|
||||
hw_priv->attr_mem = attr_mem;
|
||||
hw_priv->cor_offset = cor_offset;
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
|
||||
dev)) {
|
||||
printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
|
||||
goto fail;
|
||||
} else
|
||||
irq_registered = 1;
|
||||
|
||||
if (prism2_hw_config(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: hardware initialization failed\n",
|
||||
dev_info);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return hostap_hw_ready(dev);
|
||||
|
||||
fail:
|
||||
kfree(hw_priv);
|
||||
if (local)
|
||||
local->hw_priv = NULL;
|
||||
prism2_free_local_data(dev);
|
||||
|
||||
if (irq_registered && dev)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
if (attr_mem)
|
||||
iounmap(attr_mem);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_plx_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct hostap_interface *iface;
|
||||
struct hostap_plx_priv *hw_priv;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
iface = netdev_priv(dev);
|
||||
hw_priv = iface->local->hw_priv;
|
||||
|
||||
/* Reset the hardware, and ensure interrupts are disabled. */
|
||||
prism2_plx_cor_sreset(iface->local);
|
||||
hfa384x_disable_interrupts(dev);
|
||||
|
||||
if (hw_priv->attr_mem)
|
||||
iounmap(hw_priv->attr_mem);
|
||||
if (dev->irq)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
kfree(iface->local->hw_priv);
|
||||
iface->local->hw_priv = NULL;
|
||||
prism2_free_local_data(dev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
|
||||
|
||||
static struct pci_driver prism2_plx_drv_id = {
|
||||
.name = "prism2_plx",
|
||||
.id_table = prism2_plx_id_table,
|
||||
.probe = prism2_plx_probe,
|
||||
.remove = prism2_plx_remove,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.enable_wake = NULL
|
||||
};
|
||||
|
||||
|
||||
static int __init init_prism2_plx(void)
|
||||
{
|
||||
printk(KERN_INFO "%s: %s\n", dev_info, version);
|
||||
|
||||
return pci_register_driver(&prism2_plx_drv_id);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_prism2_plx(void)
|
||||
{
|
||||
pci_unregister_driver(&prism2_plx_drv_id);
|
||||
printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_prism2_plx);
|
||||
module_exit(exit_prism2_plx);
|
448
drivers/net/wireless/hostap/hostap_proc.c
Normal file
448
drivers/net/wireless/hostap/hostap_proc.c
Normal file
@ -0,0 +1,448 @@
|
||||
/* /proc routines for Host AP driver */
|
||||
|
||||
#define PROC_LIMIT (PAGE_SIZE - 80)
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_PROCFS_DEBUG
|
||||
static int prism2_debug_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int i;
|
||||
|
||||
if (off != 0) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "next_txfid=%d next_alloc=%d\n",
|
||||
local->next_txfid, local->next_alloc);
|
||||
for (i = 0; i < PRISM2_TXFID_COUNT; i++)
|
||||
p += sprintf(p, "FID: tx=%04X intransmit=%04X\n",
|
||||
local->txfid[i], local->intransmitfid[i]);
|
||||
p += sprintf(p, "FW TX rate control: %d\n", local->fw_tx_rate_control);
|
||||
p += sprintf(p, "beacon_int=%d\n", local->beacon_int);
|
||||
p += sprintf(p, "dtim_period=%d\n", local->dtim_period);
|
||||
p += sprintf(p, "wds_max_connections=%d\n",
|
||||
local->wds_max_connections);
|
||||
p += sprintf(p, "dev_enabled=%d\n", local->dev_enabled);
|
||||
p += sprintf(p, "sw_tick_stuck=%d\n", local->sw_tick_stuck);
|
||||
for (i = 0; i < WEP_KEYS; i++) {
|
||||
if (local->crypt[i] && local->crypt[i]->ops) {
|
||||
p += sprintf(p, "crypt[%d]=%s\n",
|
||||
i, local->crypt[i]->ops->name);
|
||||
}
|
||||
}
|
||||
p += sprintf(p, "pri_only=%d\n", local->pri_only);
|
||||
p += sprintf(p, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI);
|
||||
p += sprintf(p, "sram_type=%d\n", local->sram_type);
|
||||
p += sprintf(p, "no_pri=%d\n", local->no_pri);
|
||||
|
||||
return (p - page);
|
||||
}
|
||||
#endif /* PRISM2_NO_PROCFS_DEBUG */
|
||||
|
||||
|
||||
static int prism2_stats_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
struct comm_tallies_sums *sums = (struct comm_tallies_sums *)
|
||||
&local->comm_tallies;
|
||||
|
||||
if (off != 0) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "TxUnicastFrames=%u\n", sums->tx_unicast_frames);
|
||||
p += sprintf(p, "TxMulticastframes=%u\n", sums->tx_multicast_frames);
|
||||
p += sprintf(p, "TxFragments=%u\n", sums->tx_fragments);
|
||||
p += sprintf(p, "TxUnicastOctets=%u\n", sums->tx_unicast_octets);
|
||||
p += sprintf(p, "TxMulticastOctets=%u\n", sums->tx_multicast_octets);
|
||||
p += sprintf(p, "TxDeferredTransmissions=%u\n",
|
||||
sums->tx_deferred_transmissions);
|
||||
p += sprintf(p, "TxSingleRetryFrames=%u\n",
|
||||
sums->tx_single_retry_frames);
|
||||
p += sprintf(p, "TxMultipleRetryFrames=%u\n",
|
||||
sums->tx_multiple_retry_frames);
|
||||
p += sprintf(p, "TxRetryLimitExceeded=%u\n",
|
||||
sums->tx_retry_limit_exceeded);
|
||||
p += sprintf(p, "TxDiscards=%u\n", sums->tx_discards);
|
||||
p += sprintf(p, "RxUnicastFrames=%u\n", sums->rx_unicast_frames);
|
||||
p += sprintf(p, "RxMulticastFrames=%u\n", sums->rx_multicast_frames);
|
||||
p += sprintf(p, "RxFragments=%u\n", sums->rx_fragments);
|
||||
p += sprintf(p, "RxUnicastOctets=%u\n", sums->rx_unicast_octets);
|
||||
p += sprintf(p, "RxMulticastOctets=%u\n", sums->rx_multicast_octets);
|
||||
p += sprintf(p, "RxFCSErrors=%u\n", sums->rx_fcs_errors);
|
||||
p += sprintf(p, "RxDiscardsNoBuffer=%u\n",
|
||||
sums->rx_discards_no_buffer);
|
||||
p += sprintf(p, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa);
|
||||
p += sprintf(p, "RxDiscardsWEPUndecryptable=%u\n",
|
||||
sums->rx_discards_wep_undecryptable);
|
||||
p += sprintf(p, "RxMessageInMsgFragments=%u\n",
|
||||
sums->rx_message_in_msg_fragments);
|
||||
p += sprintf(p, "RxMessageInBadMsgFragments=%u\n",
|
||||
sums->rx_message_in_bad_msg_fragments);
|
||||
/* FIX: this may grow too long for one page(?) */
|
||||
|
||||
return (p - page);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_wds_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
struct list_head *ptr;
|
||||
struct hostap_interface *iface;
|
||||
|
||||
if (off > PROC_LIMIT) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
read_lock_bh(&local->iface_lock);
|
||||
list_for_each(ptr, &local->hostap_interfaces) {
|
||||
iface = list_entry(ptr, struct hostap_interface, list);
|
||||
if (iface->type != HOSTAP_INTERFACE_WDS)
|
||||
continue;
|
||||
p += sprintf(p, "%s\t" MACSTR "\n",
|
||||
iface->dev->name,
|
||||
MAC2STR(iface->u.wds.remote_addr));
|
||||
if ((p - page) > PROC_LIMIT) {
|
||||
printk(KERN_DEBUG "%s: wds proc did not fit\n",
|
||||
local->dev->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&local->iface_lock);
|
||||
|
||||
if ((p - page) <= off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = page + off;
|
||||
|
||||
return (p - page - off);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_bss_list_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
struct list_head *ptr;
|
||||
struct hostap_bss_info *bss;
|
||||
int i;
|
||||
|
||||
if (off > PROC_LIMIT) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t"
|
||||
"SSID(hex)\tWPA IE\n");
|
||||
spin_lock_bh(&local->lock);
|
||||
list_for_each(ptr, &local->bss_list) {
|
||||
bss = list_entry(ptr, struct hostap_bss_info, list);
|
||||
p += sprintf(p, MACSTR "\t%lu\t%u\t0x%x\t",
|
||||
MAC2STR(bss->bssid), bss->last_update,
|
||||
bss->count, bss->capab_info);
|
||||
for (i = 0; i < bss->ssid_len; i++) {
|
||||
p += sprintf(p, "%c",
|
||||
bss->ssid[i] >= 32 && bss->ssid[i] < 127 ?
|
||||
bss->ssid[i] : '_');
|
||||
}
|
||||
p += sprintf(p, "\t");
|
||||
for (i = 0; i < bss->ssid_len; i++) {
|
||||
p += sprintf(p, "%02x", bss->ssid[i]);
|
||||
}
|
||||
p += sprintf(p, "\t");
|
||||
for (i = 0; i < bss->wpa_ie_len; i++) {
|
||||
p += sprintf(p, "%02x", bss->wpa_ie[i]);
|
||||
}
|
||||
p += sprintf(p, "\n");
|
||||
if ((p - page) > PROC_LIMIT) {
|
||||
printk(KERN_DEBUG "%s: BSS proc did not fit\n",
|
||||
local->dev->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&local->lock);
|
||||
|
||||
if ((p - page) <= off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = page + off;
|
||||
|
||||
return (p - page - off);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_crypt_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int i;
|
||||
|
||||
if (off > PROC_LIMIT) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "tx_keyidx=%d\n", local->tx_keyidx);
|
||||
for (i = 0; i < WEP_KEYS; i++) {
|
||||
if (local->crypt[i] && local->crypt[i]->ops &&
|
||||
local->crypt[i]->ops->print_stats) {
|
||||
p = local->crypt[i]->ops->print_stats(
|
||||
p, local->crypt[i]->priv);
|
||||
}
|
||||
}
|
||||
|
||||
if ((p - page) <= off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = page + off;
|
||||
|
||||
return (p - page - off);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_pda_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
|
||||
if (local->pda == NULL || off >= PRISM2_PDA_SIZE) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (off + count > PRISM2_PDA_SIZE)
|
||||
count = PRISM2_PDA_SIZE - off;
|
||||
|
||||
memcpy(page, local->pda + off, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_aux_dump_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
|
||||
if (local->func->read_aux == NULL) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (local->func->read_aux(local->dev, off, count, page)) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
*start = page;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
static int prism2_io_debug_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int head = local->io_debug_head;
|
||||
int start_bytes, left, copy, copied;
|
||||
|
||||
if (off + count > PRISM2_IO_DEBUG_SIZE * 4) {
|
||||
*eof = 1;
|
||||
if (off >= PRISM2_IO_DEBUG_SIZE * 4)
|
||||
return 0;
|
||||
count = PRISM2_IO_DEBUG_SIZE * 4 - off;
|
||||
}
|
||||
|
||||
copied = 0;
|
||||
start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4;
|
||||
left = count;
|
||||
|
||||
if (off < start_bytes) {
|
||||
copy = start_bytes - off;
|
||||
if (copy > count)
|
||||
copy = count;
|
||||
memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy);
|
||||
left -= copy;
|
||||
if (left > 0)
|
||||
memcpy(&page[copy], local->io_debug, left);
|
||||
} else {
|
||||
memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes),
|
||||
left);
|
||||
}
|
||||
|
||||
*start = page;
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
static int prism2_scan_results_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int entry, i, len, total = 0;
|
||||
struct hfa384x_hostscan_result *scanres;
|
||||
u8 *pos;
|
||||
|
||||
p += sprintf(p, "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates "
|
||||
"SSID\n");
|
||||
|
||||
spin_lock_bh(&local->lock);
|
||||
for (entry = 0; entry < local->last_scan_results_count; entry++) {
|
||||
scanres = &local->last_scan_results[entry];
|
||||
|
||||
if (total + (p - page) <= off) {
|
||||
total += p - page;
|
||||
p = page;
|
||||
}
|
||||
if (total + (p - page) > off + count)
|
||||
break;
|
||||
if ((p - page) > (PAGE_SIZE - 200))
|
||||
break;
|
||||
|
||||
p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR " %d ",
|
||||
le16_to_cpu(scanres->chid),
|
||||
(s16) le16_to_cpu(scanres->anl),
|
||||
(s16) le16_to_cpu(scanres->sl),
|
||||
le16_to_cpu(scanres->beacon_interval),
|
||||
le16_to_cpu(scanres->capability),
|
||||
le16_to_cpu(scanres->rate),
|
||||
MAC2STR(scanres->bssid),
|
||||
le16_to_cpu(scanres->atim));
|
||||
|
||||
pos = scanres->sup_rates;
|
||||
for (i = 0; i < sizeof(scanres->sup_rates); i++) {
|
||||
if (pos[i] == 0)
|
||||
break;
|
||||
p += sprintf(p, "<%02x>", pos[i]);
|
||||
}
|
||||
p += sprintf(p, " ");
|
||||
|
||||
pos = scanres->ssid;
|
||||
len = le16_to_cpu(scanres->ssid_len);
|
||||
if (len > 32)
|
||||
len = 32;
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned char c = pos[i];
|
||||
if (c >= 32 && c < 127)
|
||||
p += sprintf(p, "%c", c);
|
||||
else
|
||||
p += sprintf(p, "<%02x>", c);
|
||||
}
|
||||
p += sprintf(p, "\n");
|
||||
}
|
||||
spin_unlock_bh(&local->lock);
|
||||
|
||||
total += (p - page);
|
||||
if (total >= off + count)
|
||||
*eof = 1;
|
||||
|
||||
if (total < off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = total - off;
|
||||
if (len > (p - page))
|
||||
len = p - page;
|
||||
*start = p - len;
|
||||
if (len > count)
|
||||
len = count;
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
|
||||
void hostap_init_proc(local_info_t *local)
|
||||
{
|
||||
local->proc = NULL;
|
||||
|
||||
if (hostap_proc == NULL) {
|
||||
printk(KERN_WARNING "%s: hostap proc directory not created\n",
|
||||
local->dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
local->proc = proc_mkdir(local->ddev->name, hostap_proc);
|
||||
if (local->proc == NULL) {
|
||||
printk(KERN_INFO "/proc/net/hostap/%s creation failed\n",
|
||||
local->ddev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef PRISM2_NO_PROCFS_DEBUG
|
||||
create_proc_read_entry("debug", 0, local->proc,
|
||||
prism2_debug_proc_read, local);
|
||||
#endif /* PRISM2_NO_PROCFS_DEBUG */
|
||||
create_proc_read_entry("stats", 0, local->proc,
|
||||
prism2_stats_proc_read, local);
|
||||
create_proc_read_entry("wds", 0, local->proc,
|
||||
prism2_wds_proc_read, local);
|
||||
create_proc_read_entry("pda", 0, local->proc,
|
||||
prism2_pda_proc_read, local);
|
||||
create_proc_read_entry("aux_dump", 0, local->proc,
|
||||
prism2_aux_dump_proc_read, local);
|
||||
create_proc_read_entry("bss_list", 0, local->proc,
|
||||
prism2_bss_list_proc_read, local);
|
||||
create_proc_read_entry("crypt", 0, local->proc,
|
||||
prism2_crypt_proc_read, local);
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
create_proc_read_entry("io_debug", 0, local->proc,
|
||||
prism2_io_debug_proc_read, local);
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
create_proc_read_entry("scan_results", 0, local->proc,
|
||||
prism2_scan_results_proc_read, local);
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
}
|
||||
|
||||
|
||||
void hostap_remove_proc(local_info_t *local)
|
||||
{
|
||||
if (local->proc != NULL) {
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
remove_proc_entry("scan_results", local->proc);
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
remove_proc_entry("io_debug", local->proc);
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
remove_proc_entry("pda", local->proc);
|
||||
remove_proc_entry("aux_dump", local->proc);
|
||||
remove_proc_entry("wds", local->proc);
|
||||
remove_proc_entry("stats", local->proc);
|
||||
remove_proc_entry("bss_list", local->proc);
|
||||
remove_proc_entry("crypt", local->proc);
|
||||
#ifndef PRISM2_NO_PROCFS_DEBUG
|
||||
remove_proc_entry("debug", local->proc);
|
||||
#endif /* PRISM2_NO_PROCFS_DEBUG */
|
||||
if (hostap_proc != NULL)
|
||||
remove_proc_entry(local->proc->name, hostap_proc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(hostap_init_proc);
|
||||
EXPORT_SYMBOL(hostap_remove_proc);
|
1033
drivers/net/wireless/hostap/hostap_wlan.h
Normal file
1033
drivers/net/wireless/hostap/hostap_wlan.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,78 +0,0 @@
|
||||
#ifndef _IEEE802_11_H
|
||||
#define _IEEE802_11_H
|
||||
|
||||
#define IEEE802_11_DATA_LEN 2304
|
||||
/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
|
||||
6.2.1.1.2.
|
||||
|
||||
The figure in section 7.1.2 suggests a body size of up to 2312
|
||||
bytes is allowed, which is a bit confusing, I suspect this
|
||||
represents the 2304 bytes of real data, plus a possible 8 bytes of
|
||||
WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */
|
||||
|
||||
|
||||
#define IEEE802_11_HLEN 30
|
||||
#define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN)
|
||||
|
||||
struct ieee802_11_hdr {
|
||||
u16 frame_ctl;
|
||||
u16 duration_id;
|
||||
u8 addr1[ETH_ALEN];
|
||||
u8 addr2[ETH_ALEN];
|
||||
u8 addr3[ETH_ALEN];
|
||||
u16 seq_ctl;
|
||||
u8 addr4[ETH_ALEN];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Frame control field constants */
|
||||
#define IEEE802_11_FCTL_VERS 0x0002
|
||||
#define IEEE802_11_FCTL_FTYPE 0x000c
|
||||
#define IEEE802_11_FCTL_STYPE 0x00f0
|
||||
#define IEEE802_11_FCTL_TODS 0x0100
|
||||
#define IEEE802_11_FCTL_FROMDS 0x0200
|
||||
#define IEEE802_11_FCTL_MOREFRAGS 0x0400
|
||||
#define IEEE802_11_FCTL_RETRY 0x0800
|
||||
#define IEEE802_11_FCTL_PM 0x1000
|
||||
#define IEEE802_11_FCTL_MOREDATA 0x2000
|
||||
#define IEEE802_11_FCTL_WEP 0x4000
|
||||
#define IEEE802_11_FCTL_ORDER 0x8000
|
||||
|
||||
#define IEEE802_11_FTYPE_MGMT 0x0000
|
||||
#define IEEE802_11_FTYPE_CTL 0x0004
|
||||
#define IEEE802_11_FTYPE_DATA 0x0008
|
||||
|
||||
/* management */
|
||||
#define IEEE802_11_STYPE_ASSOC_REQ 0x0000
|
||||
#define IEEE802_11_STYPE_ASSOC_RESP 0x0010
|
||||
#define IEEE802_11_STYPE_REASSOC_REQ 0x0020
|
||||
#define IEEE802_11_STYPE_REASSOC_RESP 0x0030
|
||||
#define IEEE802_11_STYPE_PROBE_REQ 0x0040
|
||||
#define IEEE802_11_STYPE_PROBE_RESP 0x0050
|
||||
#define IEEE802_11_STYPE_BEACON 0x0080
|
||||
#define IEEE802_11_STYPE_ATIM 0x0090
|
||||
#define IEEE802_11_STYPE_DISASSOC 0x00A0
|
||||
#define IEEE802_11_STYPE_AUTH 0x00B0
|
||||
#define IEEE802_11_STYPE_DEAUTH 0x00C0
|
||||
|
||||
/* control */
|
||||
#define IEEE802_11_STYPE_PSPOLL 0x00A0
|
||||
#define IEEE802_11_STYPE_RTS 0x00B0
|
||||
#define IEEE802_11_STYPE_CTS 0x00C0
|
||||
#define IEEE802_11_STYPE_ACK 0x00D0
|
||||
#define IEEE802_11_STYPE_CFEND 0x00E0
|
||||
#define IEEE802_11_STYPE_CFENDACK 0x00F0
|
||||
|
||||
/* data */
|
||||
#define IEEE802_11_STYPE_DATA 0x0000
|
||||
#define IEEE802_11_STYPE_DATA_CFACK 0x0010
|
||||
#define IEEE802_11_STYPE_DATA_CFPOLL 0x0020
|
||||
#define IEEE802_11_STYPE_DATA_CFACKPOLL 0x0030
|
||||
#define IEEE802_11_STYPE_NULLFUNC 0x0040
|
||||
#define IEEE802_11_STYPE_CFACK 0x0050
|
||||
#define IEEE802_11_STYPE_CFPOLL 0x0060
|
||||
#define IEEE802_11_STYPE_CFACKPOLL 0x0070
|
||||
|
||||
#define IEEE802_11_SCTL_FRAG 0x000F
|
||||
#define IEEE802_11_SCTL_SEQ 0xFFF0
|
||||
|
||||
#endif /* _IEEE802_11_H */
|
8679
drivers/net/wireless/ipw2100.c
Normal file
8679
drivers/net/wireless/ipw2100.c
Normal file
File diff suppressed because it is too large
Load Diff
1167
drivers/net/wireless/ipw2100.h
Normal file
1167
drivers/net/wireless/ipw2100.h
Normal file
File diff suppressed because it is too large
Load Diff
7353
drivers/net/wireless/ipw2200.c
Normal file
7353
drivers/net/wireless/ipw2200.c
Normal file
File diff suppressed because it is too large
Load Diff
1742
drivers/net/wireless/ipw2200.h
Normal file
1742
drivers/net/wireless/ipw2200.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -94,6 +94,8 @@
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
@ -101,7 +103,6 @@
|
||||
#include "hermes.h"
|
||||
#include "hermes_rid.h"
|
||||
#include "orinoco.h"
|
||||
#include "ieee802_11.h"
|
||||
|
||||
/********************************************************************/
|
||||
/* Module information */
|
||||
@ -150,7 +151,7 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
|
||||
#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
|
||||
|
||||
#define ORINOCO_MIN_MTU 256
|
||||
#define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD)
|
||||
#define ORINOCO_MAX_MTU (IEEE80211_DATA_LEN - ENCAPS_OVERHEAD)
|
||||
|
||||
#define SYMBOL_MAX_VER_LEN (14)
|
||||
#define USER_BAP 0
|
||||
@ -442,7 +443,7 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
|
||||
if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
|
||||
return -EINVAL;
|
||||
|
||||
if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) >
|
||||
if ( (new_mtu + ENCAPS_OVERHEAD + IEEE80211_HLEN) >
|
||||
(priv->nicbuf_size - ETH_HLEN) )
|
||||
return -EINVAL;
|
||||
|
||||
@ -918,7 +919,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
|
||||
data. */
|
||||
return;
|
||||
}
|
||||
if (length > IEEE802_11_DATA_LEN) {
|
||||
if (length > IEEE80211_DATA_LEN) {
|
||||
printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
|
||||
dev->name, length);
|
||||
stats->rx_length_errors++;
|
||||
@ -2272,7 +2273,7 @@ static int orinoco_init(struct net_device *dev)
|
||||
|
||||
/* No need to lock, the hw_unavailable flag is already set in
|
||||
* alloc_orinocodev() */
|
||||
priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
|
||||
priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN;
|
||||
|
||||
/* Initialize the firmware */
|
||||
err = orinoco_reinit_firmware(dev);
|
||||
|
@ -209,7 +209,7 @@ enum {
|
||||
NoStructure = 0, /* Really old firmware */
|
||||
StructuredMessages = 1, /* Parsable AT response msgs */
|
||||
ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */
|
||||
} FirmwareLevel;
|
||||
};
|
||||
|
||||
struct strip {
|
||||
int magic;
|
||||
|
@ -59,6 +59,12 @@
|
||||
/* Do *NOT* add other headers here, you are guaranteed to be wrong - Jean II */
|
||||
#include "wavelan_cs.p.h" /* Private header */
|
||||
|
||||
#ifdef WAVELAN_ROAMING
|
||||
static void wl_cell_expiry(unsigned long data);
|
||||
static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp);
|
||||
static void wv_nwid_filter(unsigned char mode, net_local *lp);
|
||||
#endif /* WAVELAN_ROAMING */
|
||||
|
||||
/************************* MISC SUBROUTINES **************************/
|
||||
/*
|
||||
* Subroutines which won't fit in one of the following category
|
||||
@ -500,9 +506,9 @@ fee_write(u_long base, /* i/o port of the card */
|
||||
|
||||
#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */
|
||||
|
||||
unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
|
||||
static unsigned char WAVELAN_BEACON_ADDRESS[] = {0x09,0x00,0x0e,0x20,0x03,0x00};
|
||||
|
||||
void wv_roam_init(struct net_device *dev)
|
||||
static void wv_roam_init(struct net_device *dev)
|
||||
{
|
||||
net_local *lp= netdev_priv(dev);
|
||||
|
||||
@ -531,7 +537,7 @@ void wv_roam_init(struct net_device *dev)
|
||||
printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
|
||||
}
|
||||
|
||||
void wv_roam_cleanup(struct net_device *dev)
|
||||
static void wv_roam_cleanup(struct net_device *dev)
|
||||
{
|
||||
wavepoint_history *ptr,*old_ptr;
|
||||
net_local *lp= netdev_priv(dev);
|
||||
@ -550,7 +556,7 @@ void wv_roam_cleanup(struct net_device *dev)
|
||||
}
|
||||
|
||||
/* Enable/Disable NWID promiscuous mode on a given device */
|
||||
void wv_nwid_filter(unsigned char mode, net_local *lp)
|
||||
static void wv_nwid_filter(unsigned char mode, net_local *lp)
|
||||
{
|
||||
mm_t m;
|
||||
unsigned long flags;
|
||||
@ -575,7 +581,7 @@ void wv_nwid_filter(unsigned char mode, net_local *lp)
|
||||
}
|
||||
|
||||
/* Find a record in the WavePoint table matching a given NWID */
|
||||
wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
|
||||
static wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
|
||||
{
|
||||
wavepoint_history *ptr=lp->wavepoint_table.head;
|
||||
|
||||
@ -588,7 +594,7 @@ wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
|
||||
}
|
||||
|
||||
/* Create a new wavepoint table entry */
|
||||
wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
|
||||
static wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
|
||||
{
|
||||
wavepoint_history *new_wavepoint;
|
||||
|
||||
@ -624,7 +630,7 @@ wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_
|
||||
}
|
||||
|
||||
/* Remove a wavepoint entry from WavePoint table */
|
||||
void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
|
||||
static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
|
||||
{
|
||||
if(wavepoint==NULL)
|
||||
return;
|
||||
@ -646,7 +652,7 @@ void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
|
||||
}
|
||||
|
||||
/* Timer callback function - checks WavePoint table for stale entries */
|
||||
void wl_cell_expiry(unsigned long data)
|
||||
static void wl_cell_expiry(unsigned long data)
|
||||
{
|
||||
net_local *lp=(net_local *)data;
|
||||
wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
|
||||
@ -686,7 +692,7 @@ void wl_cell_expiry(unsigned long data)
|
||||
}
|
||||
|
||||
/* Update SNR history of a wavepoint */
|
||||
void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
|
||||
static void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
|
||||
{
|
||||
int i=0,num_missed=0,ptr=0;
|
||||
int average_fast=0,average_slow=0;
|
||||
@ -723,7 +729,7 @@ void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsi
|
||||
}
|
||||
|
||||
/* Perform a handover to a new WavePoint */
|
||||
void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
|
||||
static void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
|
||||
{
|
||||
kio_addr_t base = lp->dev->base_addr;
|
||||
mm_t m;
|
||||
|
@ -62,7 +62,7 @@
|
||||
* like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
|
||||
* part to accommodate your hardware...
|
||||
*/
|
||||
const unsigned char MAC_ADDRESSES[][3] =
|
||||
static const unsigned char MAC_ADDRESSES[][3] =
|
||||
{
|
||||
{ 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */
|
||||
{ 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */
|
||||
@ -79,14 +79,14 @@ const unsigned char MAC_ADDRESSES[][3] =
|
||||
* (as read in the offset register of the dac area).
|
||||
* Used to map channel numbers used by `wfreqsel' to frequencies
|
||||
*/
|
||||
const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
|
||||
static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
|
||||
0xD0, 0xF0, 0xF8, 0x150 };
|
||||
|
||||
/* Frequencies of the 1.0 modem (fixed frequencies).
|
||||
* Use to map the PSA `subband' to a frequency
|
||||
* Note : all frequencies apart from the first one need to be multiplied by 10
|
||||
*/
|
||||
const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
|
||||
static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
|
||||
|
||||
|
||||
/*************************** PC INTERFACE ****************************/
|
||||
|
@ -647,23 +647,6 @@ struct net_local
|
||||
void __iomem *mem;
|
||||
};
|
||||
|
||||
/**************************** PROTOTYPES ****************************/
|
||||
|
||||
#ifdef WAVELAN_ROAMING
|
||||
/* ---------------------- ROAMING SUBROUTINES -----------------------*/
|
||||
|
||||
wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
|
||||
wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
|
||||
void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
|
||||
void wl_cell_expiry(unsigned long data);
|
||||
wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
|
||||
void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
|
||||
void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
|
||||
void wv_nwid_filter(unsigned char mode, net_local *lp);
|
||||
void wv_roam_init(struct net_device *dev);
|
||||
void wv_roam_cleanup(struct net_device *dev);
|
||||
#endif /* WAVELAN_ROAMING */
|
||||
|
||||
/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
|
||||
static inline u_char /* data */
|
||||
hasr_read(u_long); /* Read the host interface : base address */
|
||||
|
@ -2,7 +2,7 @@
|
||||
#define __WL3501_H__
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include "ieee802_11.h"
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
/* define for WLA 2.0 */
|
||||
#define WL3501_BLKSZ 256
|
||||
@ -548,7 +548,7 @@ struct wl3501_80211_tx_plcp_hdr {
|
||||
|
||||
struct wl3501_80211_tx_hdr {
|
||||
struct wl3501_80211_tx_plcp_hdr pclp_hdr;
|
||||
struct ieee802_11_hdr mac_hdr;
|
||||
struct ieee80211_hdr mac_hdr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
|
@ -296,7 +296,8 @@ static int wl3501_get_flash_mac_addr(struct wl3501_card *this)
|
||||
*
|
||||
* Move 'size' bytes from PC to card. (Shouldn't be interrupted)
|
||||
*/
|
||||
void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, int size)
|
||||
static void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src,
|
||||
int size)
|
||||
{
|
||||
/* switch to SRAM Page 0 */
|
||||
wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 :
|
||||
@ -317,8 +318,8 @@ void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, int size)
|
||||
*
|
||||
* Move 'size' bytes from card to PC. (Shouldn't be interrupted)
|
||||
*/
|
||||
void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
|
||||
int size)
|
||||
static void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
|
||||
int size)
|
||||
{
|
||||
/* switch to SRAM Page 0 */
|
||||
wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 :
|
||||
@ -1438,14 +1439,14 @@ fail:
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct net_device_stats *wl3501_get_stats(struct net_device *dev)
|
||||
static struct net_device_stats *wl3501_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct wl3501_card *this = dev->priv;
|
||||
|
||||
return &this->stats;
|
||||
}
|
||||
|
||||
struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
|
||||
static struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
|
||||
{
|
||||
struct wl3501_card *this = dev->priv;
|
||||
struct iw_statistics *wstats = &this->wstats;
|
||||
|
@ -8,5 +8,3 @@ obj-$(CONFIG_USB_PEGASUS) += pegasus.o
|
||||
obj-$(CONFIG_USB_RTL8150) += rtl8150.o
|
||||
obj-$(CONFIG_USB_USBNET) += usbnet.o
|
||||
obj-$(CONFIG_USB_ZD1201) += zd1201.o
|
||||
|
||||
CFLAGS_zd1201.o = -Idrivers/net/wireless/
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <ieee802_11.h>
|
||||
#include <net/ieee80211.h>
|
||||
#include "zd1201.h"
|
||||
|
||||
static struct usb_device_id zd1201_table[] = {
|
||||
@ -338,24 +338,24 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
if ((seq & IEEE802_11_SCTL_FRAG) ||
|
||||
(fc & IEEE802_11_FCTL_MOREFRAGS)) {
|
||||
if ((seq & IEEE80211_SCTL_FRAG) ||
|
||||
(fc & IEEE80211_FCTL_MOREFRAGS)) {
|
||||
struct zd1201_frag *frag = NULL;
|
||||
char *ptr;
|
||||
|
||||
if (datalen<14)
|
||||
goto resubmit;
|
||||
if ((seq & IEEE802_11_SCTL_FRAG) == 0) {
|
||||
if ((seq & IEEE80211_SCTL_FRAG) == 0) {
|
||||
frag = kmalloc(sizeof(*frag), GFP_ATOMIC);
|
||||
if (!frag)
|
||||
goto resubmit;
|
||||
skb = dev_alloc_skb(IEEE802_11_DATA_LEN +14+2);
|
||||
skb = dev_alloc_skb(IEEE80211_DATA_LEN +14+2);
|
||||
if (!skb) {
|
||||
kfree(frag);
|
||||
goto resubmit;
|
||||
}
|
||||
frag->skb = skb;
|
||||
frag->seq = seq & IEEE802_11_SCTL_SEQ;
|
||||
frag->seq = seq & IEEE80211_SCTL_SEQ;
|
||||
skb_reserve(skb, 2);
|
||||
memcpy(skb_put(skb, 12), &data[datalen-14], 12);
|
||||
memcpy(skb_put(skb, 2), &data[6], 2);
|
||||
@ -364,7 +364,7 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
|
||||
goto resubmit;
|
||||
}
|
||||
hlist_for_each_entry(frag, node, &zd->fraglist, fnode)
|
||||
if(frag->seq == (seq&IEEE802_11_SCTL_SEQ))
|
||||
if(frag->seq == (seq&IEEE80211_SCTL_SEQ))
|
||||
break;
|
||||
if (!frag)
|
||||
goto resubmit;
|
||||
@ -372,7 +372,7 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
|
||||
ptr = skb_put(skb, len);
|
||||
if (ptr)
|
||||
memcpy(ptr, data+8, len);
|
||||
if (fc & IEEE802_11_FCTL_MOREFRAGS)
|
||||
if (fc & IEEE80211_FCTL_MOREFRAGS)
|
||||
goto resubmit;
|
||||
hlist_del_init(&frag->fnode);
|
||||
kfree(frag);
|
||||
|
@ -69,6 +69,12 @@ static inline int is_multicast_ether_addr(const u8 *addr)
|
||||
return ((addr[0] != 0xff) && (0x01 & addr[0]));
|
||||
}
|
||||
|
||||
static inline int is_broadcast_ether_addr(const u8 *addr)
|
||||
{
|
||||
return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) &&
|
||||
(addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff));
|
||||
}
|
||||
|
||||
/**
|
||||
* is_valid_ether_addr - Determine if the given Ethernet address is valid
|
||||
* @addr: Pointer to a six-byte array containing the Ethernet address
|
||||
|
@ -20,18 +20,9 @@
|
||||
*/
|
||||
#ifndef IEEE80211_H
|
||||
#define IEEE80211_H
|
||||
|
||||
#include <linux/if_ether.h> /* ETH_ALEN */
|
||||
#include <linux/kernel.h> /* ARRAY_SIZE */
|
||||
|
||||
#if WIRELESS_EXT < 17
|
||||
#define IW_QUAL_QUAL_INVALID 0x10
|
||||
#define IW_QUAL_LEVEL_INVALID 0x20
|
||||
#define IW_QUAL_NOISE_INVALID 0x40
|
||||
#define IW_QUAL_QUAL_UPDATED 0x1
|
||||
#define IW_QUAL_LEVEL_UPDATED 0x2
|
||||
#define IW_QUAL_NOISE_UPDATED 0x4
|
||||
#endif
|
||||
#include <linux/wireless.h>
|
||||
|
||||
#define IEEE80211_DATA_LEN 2304
|
||||
/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
|
||||
@ -47,51 +38,22 @@
|
||||
#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
|
||||
|
||||
struct ieee80211_hdr {
|
||||
u16 frame_ctl;
|
||||
u16 duration_id;
|
||||
__le16 frame_ctl;
|
||||
__le16 duration_id;
|
||||
u8 addr1[ETH_ALEN];
|
||||
u8 addr2[ETH_ALEN];
|
||||
u8 addr3[ETH_ALEN];
|
||||
u16 seq_ctl;
|
||||
__le16 seq_ctl;
|
||||
u8 addr4[ETH_ALEN];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ieee80211_hdr_3addr {
|
||||
u16 frame_ctl;
|
||||
u16 duration_id;
|
||||
__le16 frame_ctl;
|
||||
__le16 duration_id;
|
||||
u8 addr1[ETH_ALEN];
|
||||
u8 addr2[ETH_ALEN];
|
||||
u8 addr3[ETH_ALEN];
|
||||
u16 seq_ctl;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum eap_type {
|
||||
EAP_PACKET = 0,
|
||||
EAPOL_START,
|
||||
EAPOL_LOGOFF,
|
||||
EAPOL_KEY,
|
||||
EAPOL_ENCAP_ASF_ALERT
|
||||
};
|
||||
|
||||
static const char *eap_types[] = {
|
||||
[EAP_PACKET] = "EAP-Packet",
|
||||
[EAPOL_START] = "EAPOL-Start",
|
||||
[EAPOL_LOGOFF] = "EAPOL-Logoff",
|
||||
[EAPOL_KEY] = "EAPOL-Key",
|
||||
[EAPOL_ENCAP_ASF_ALERT] = "EAPOL-Encap-ASF-Alert"
|
||||
};
|
||||
|
||||
static inline const char *eap_get_type(int type)
|
||||
{
|
||||
return (type >= ARRAY_SIZE(eap_types)) ? "Unknown" : eap_types[type];
|
||||
}
|
||||
|
||||
struct eapol {
|
||||
u8 snap[6];
|
||||
u16 ethertype;
|
||||
u8 version;
|
||||
u8 type;
|
||||
u16 length;
|
||||
__le16 seq_ctl;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IEEE80211_1ADDR_LEN 10
|
||||
@ -104,7 +66,7 @@ struct eapol {
|
||||
#define MAX_FRAG_THRESHOLD 2346U
|
||||
|
||||
/* Frame control field constants */
|
||||
#define IEEE80211_FCTL_VERS 0x0002
|
||||
#define IEEE80211_FCTL_VERS 0x0003
|
||||
#define IEEE80211_FCTL_FTYPE 0x000c
|
||||
#define IEEE80211_FCTL_STYPE 0x00f0
|
||||
#define IEEE80211_FCTL_TODS 0x0100
|
||||
@ -112,8 +74,8 @@ struct eapol {
|
||||
#define IEEE80211_FCTL_MOREFRAGS 0x0400
|
||||
#define IEEE80211_FCTL_RETRY 0x0800
|
||||
#define IEEE80211_FCTL_PM 0x1000
|
||||
#define IEEE80211_FCTL_MOREDATA 0x2000
|
||||
#define IEEE80211_FCTL_WEP 0x4000
|
||||
#define IEEE80211_FCTL_MOREDATA 0x2000
|
||||
#define IEEE80211_FCTL_PROTECTED 0x4000
|
||||
#define IEEE80211_FCTL_ORDER 0x8000
|
||||
|
||||
#define IEEE80211_FTYPE_MGMT 0x0000
|
||||
@ -132,6 +94,7 @@ struct eapol {
|
||||
#define IEEE80211_STYPE_DISASSOC 0x00A0
|
||||
#define IEEE80211_STYPE_AUTH 0x00B0
|
||||
#define IEEE80211_STYPE_DEAUTH 0x00C0
|
||||
#define IEEE80211_STYPE_ACTION 0x00D0
|
||||
|
||||
/* control */
|
||||
#define IEEE80211_STYPE_PSPOLL 0x00A0
|
||||
@ -167,8 +130,19 @@ do { if (ieee80211_debug_level & (level)) \
|
||||
#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0)
|
||||
#endif /* CONFIG_IEEE80211_DEBUG */
|
||||
|
||||
|
||||
/* debug macros not dependent on CONFIG_IEEE80211_DEBUG */
|
||||
|
||||
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
|
||||
|
||||
/* escape_essid() is intended to be used in debug (and possibly error)
|
||||
* messages. It should never be used for passing essid to user space. */
|
||||
const char *escape_essid(const char *essid, u8 essid_len);
|
||||
|
||||
|
||||
/*
|
||||
* To use the debug system;
|
||||
* To use the debug system:
|
||||
*
|
||||
* If you are defining a new debug classification, simply add it to the #define
|
||||
* list here in the form of:
|
||||
@ -184,11 +158,11 @@ do { if (ieee80211_debug_level & (level)) \
|
||||
*
|
||||
* To add your debug level to the list of levels seen when you perform
|
||||
*
|
||||
* % cat /proc/net/ipw/debug_level
|
||||
* % cat /proc/net/ieee80211/debug_level
|
||||
*
|
||||
* you simply need to add your entry to the ipw_debug_levels array.
|
||||
* you simply need to add your entry to the ieee80211_debug_level array.
|
||||
*
|
||||
* If you do not see debug_level in /proc/net/ipw then you do not have
|
||||
* If you do not see debug_level in /proc/net/ieee80211 then you do not have
|
||||
* CONFIG_IEEE80211_DEBUG defined in your kernel configuration
|
||||
*
|
||||
*/
|
||||
@ -199,7 +173,6 @@ do { if (ieee80211_debug_level & (level)) \
|
||||
#define IEEE80211_DL_STATE (1<<3)
|
||||
#define IEEE80211_DL_MGMT (1<<4)
|
||||
#define IEEE80211_DL_FRAG (1<<5)
|
||||
#define IEEE80211_DL_EAP (1<<6)
|
||||
#define IEEE80211_DL_DROP (1<<7)
|
||||
|
||||
#define IEEE80211_DL_TX (1<<8)
|
||||
@ -214,7 +187,6 @@ do { if (ieee80211_debug_level & (level)) \
|
||||
#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a)
|
||||
#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a)
|
||||
#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a)
|
||||
#define IEEE80211_DEBUG_EAP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_EAP, f, ## a)
|
||||
#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a)
|
||||
#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a)
|
||||
#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a)
|
||||
@ -223,9 +195,9 @@ do { if (ieee80211_debug_level & (level)) \
|
||||
#include <linux/if_arp.h> /* ARPHRD_ETHER */
|
||||
|
||||
#ifndef WIRELESS_SPY
|
||||
#define WIRELESS_SPY // enable iwspy support
|
||||
#define WIRELESS_SPY /* enable iwspy support */
|
||||
#endif
|
||||
#include <net/iw_handler.h> // new driver API
|
||||
#include <net/iw_handler.h> /* new driver API */
|
||||
|
||||
#ifndef ETH_P_PAE
|
||||
#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
|
||||
@ -252,6 +224,7 @@ struct ieee80211_snap_hdr {
|
||||
|
||||
#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
|
||||
|
||||
#define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS)
|
||||
#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE)
|
||||
#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE)
|
||||
|
||||
@ -264,7 +237,7 @@ struct ieee80211_snap_hdr {
|
||||
|
||||
#define WLAN_AUTH_CHALLENGE_LEN 128
|
||||
|
||||
#define WLAN_CAPABILITY_BSS (1<<0)
|
||||
#define WLAN_CAPABILITY_ESS (1<<0)
|
||||
#define WLAN_CAPABILITY_IBSS (1<<1)
|
||||
#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
|
||||
#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
|
||||
@ -272,34 +245,72 @@ struct ieee80211_snap_hdr {
|
||||
#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
|
||||
#define WLAN_CAPABILITY_PBCC (1<<6)
|
||||
#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
|
||||
#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
|
||||
#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
|
||||
#define WLAN_CAPABILITY_OSSS_OFDM (1<<13)
|
||||
|
||||
/* Status codes */
|
||||
#define WLAN_STATUS_SUCCESS 0
|
||||
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
|
||||
#define WLAN_STATUS_CAPS_UNSUPPORTED 10
|
||||
#define WLAN_STATUS_REASSOC_NO_ASSOC 11
|
||||
#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
|
||||
#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
|
||||
#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
|
||||
#define WLAN_STATUS_CHALLENGE_FAIL 15
|
||||
#define WLAN_STATUS_AUTH_TIMEOUT 16
|
||||
#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
|
||||
#define WLAN_STATUS_ASSOC_DENIED_RATES 18
|
||||
/* 802.11b */
|
||||
#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
|
||||
#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
|
||||
#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
|
||||
enum ieee80211_statuscode {
|
||||
WLAN_STATUS_SUCCESS = 0,
|
||||
WLAN_STATUS_UNSPECIFIED_FAILURE = 1,
|
||||
WLAN_STATUS_CAPS_UNSUPPORTED = 10,
|
||||
WLAN_STATUS_REASSOC_NO_ASSOC = 11,
|
||||
WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12,
|
||||
WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13,
|
||||
WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14,
|
||||
WLAN_STATUS_CHALLENGE_FAIL = 15,
|
||||
WLAN_STATUS_AUTH_TIMEOUT = 16,
|
||||
WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17,
|
||||
WLAN_STATUS_ASSOC_DENIED_RATES = 18,
|
||||
/* 802.11b */
|
||||
WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19,
|
||||
WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20,
|
||||
WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21,
|
||||
/* 802.11h */
|
||||
WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22,
|
||||
WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23,
|
||||
WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24,
|
||||
/* 802.11g */
|
||||
WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25,
|
||||
WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26,
|
||||
/* 802.11i */
|
||||
WLAN_STATUS_INVALID_IE = 40,
|
||||
WLAN_STATUS_INVALID_GROUP_CIPHER = 41,
|
||||
WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42,
|
||||
WLAN_STATUS_INVALID_AKMP = 43,
|
||||
WLAN_STATUS_UNSUPP_RSN_VERSION = 44,
|
||||
WLAN_STATUS_INVALID_RSN_IE_CAP = 45,
|
||||
WLAN_STATUS_CIPHER_SUITE_REJECTED = 46,
|
||||
};
|
||||
|
||||
/* Reason codes */
|
||||
#define WLAN_REASON_UNSPECIFIED 1
|
||||
#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
|
||||
#define WLAN_REASON_DEAUTH_LEAVING 3
|
||||
#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
|
||||
#define WLAN_REASON_DISASSOC_AP_BUSY 5
|
||||
#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
|
||||
#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
|
||||
#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
|
||||
#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
|
||||
enum ieee80211_reasoncode {
|
||||
WLAN_REASON_UNSPECIFIED = 1,
|
||||
WLAN_REASON_PREV_AUTH_NOT_VALID = 2,
|
||||
WLAN_REASON_DEAUTH_LEAVING = 3,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4,
|
||||
WLAN_REASON_DISASSOC_AP_BUSY = 5,
|
||||
WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6,
|
||||
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7,
|
||||
WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8,
|
||||
WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9,
|
||||
/* 802.11h */
|
||||
WLAN_REASON_DISASSOC_BAD_POWER = 10,
|
||||
WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11,
|
||||
/* 802.11i */
|
||||
WLAN_REASON_INVALID_IE = 13,
|
||||
WLAN_REASON_MIC_FAILURE = 14,
|
||||
WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
|
||||
WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16,
|
||||
WLAN_REASON_IE_DIFFERENT = 17,
|
||||
WLAN_REASON_INVALID_GROUP_CIPHER = 18,
|
||||
WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19,
|
||||
WLAN_REASON_INVALID_AKMP = 20,
|
||||
WLAN_REASON_UNSUPP_RSN_VERSION = 21,
|
||||
WLAN_REASON_INVALID_RSN_IE_CAP = 22,
|
||||
WLAN_REASON_IEEE8021X_FAILED = 23,
|
||||
WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
|
||||
};
|
||||
|
||||
|
||||
#define IEEE80211_STATMASK_SIGNAL (1<<0)
|
||||
@ -426,9 +437,7 @@ struct ieee80211_stats {
|
||||
|
||||
struct ieee80211_device;
|
||||
|
||||
#if 0 /* for later */
|
||||
#include "ieee80211_crypt.h"
|
||||
#endif
|
||||
|
||||
#define SEC_KEY_1 (1<<0)
|
||||
#define SEC_KEY_2 (1<<1)
|
||||
@ -480,17 +489,34 @@ Total: 28-2340 bytes
|
||||
#define BEACON_PROBE_SSID_ID_POSITION 12
|
||||
|
||||
/* Management Frame Information Element Types */
|
||||
#define MFIE_TYPE_SSID 0
|
||||
#define MFIE_TYPE_RATES 1
|
||||
#define MFIE_TYPE_FH_SET 2
|
||||
#define MFIE_TYPE_DS_SET 3
|
||||
#define MFIE_TYPE_CF_SET 4
|
||||
#define MFIE_TYPE_TIM 5
|
||||
#define MFIE_TYPE_IBSS_SET 6
|
||||
#define MFIE_TYPE_CHALLENGE 16
|
||||
#define MFIE_TYPE_RSN 48
|
||||
#define MFIE_TYPE_RATES_EX 50
|
||||
#define MFIE_TYPE_GENERIC 221
|
||||
enum ieee80211_mfie {
|
||||
MFIE_TYPE_SSID = 0,
|
||||
MFIE_TYPE_RATES = 1,
|
||||
MFIE_TYPE_FH_SET = 2,
|
||||
MFIE_TYPE_DS_SET = 3,
|
||||
MFIE_TYPE_CF_SET = 4,
|
||||
MFIE_TYPE_TIM = 5,
|
||||
MFIE_TYPE_IBSS_SET = 6,
|
||||
MFIE_TYPE_COUNTRY = 7,
|
||||
MFIE_TYPE_HOP_PARAMS = 8,
|
||||
MFIE_TYPE_HOP_TABLE = 9,
|
||||
MFIE_TYPE_REQUEST = 10,
|
||||
MFIE_TYPE_CHALLENGE = 16,
|
||||
MFIE_TYPE_POWER_CONSTRAINT = 32,
|
||||
MFIE_TYPE_POWER_CAPABILITY = 33,
|
||||
MFIE_TYPE_TPC_REQUEST = 34,
|
||||
MFIE_TYPE_TPC_REPORT = 35,
|
||||
MFIE_TYPE_SUPP_CHANNELS = 36,
|
||||
MFIE_TYPE_CSA = 37,
|
||||
MFIE_TYPE_MEASURE_REQUEST = 38,
|
||||
MFIE_TYPE_MEASURE_REPORT = 39,
|
||||
MFIE_TYPE_QUIET = 40,
|
||||
MFIE_TYPE_IBSS_DFS = 41,
|
||||
MFIE_TYPE_ERP_INFO = 42,
|
||||
MFIE_TYPE_RSN = 48,
|
||||
MFIE_TYPE_RATES_EX = 50,
|
||||
MFIE_TYPE_GENERIC = 221,
|
||||
};
|
||||
|
||||
struct ieee80211_info_element_hdr {
|
||||
u8 id;
|
||||
@ -522,9 +548,9 @@ struct ieee80211_info_element {
|
||||
|
||||
struct ieee80211_authentication {
|
||||
struct ieee80211_hdr_3addr header;
|
||||
u16 algorithm;
|
||||
u16 transaction;
|
||||
u16 status;
|
||||
__le16 algorithm;
|
||||
__le16 transaction;
|
||||
__le16 status;
|
||||
struct ieee80211_info_element info_element;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
@ -532,23 +558,23 @@ struct ieee80211_authentication {
|
||||
struct ieee80211_probe_response {
|
||||
struct ieee80211_hdr_3addr header;
|
||||
u32 time_stamp[2];
|
||||
u16 beacon_interval;
|
||||
u16 capability;
|
||||
__le16 beacon_interval;
|
||||
__le16 capability;
|
||||
struct ieee80211_info_element info_element;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ieee80211_assoc_request_frame {
|
||||
u16 capability;
|
||||
u16 listen_interval;
|
||||
__le16 capability;
|
||||
__le16 listen_interval;
|
||||
u8 current_ap[ETH_ALEN];
|
||||
struct ieee80211_info_element info_element;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ieee80211_assoc_response_frame {
|
||||
struct ieee80211_hdr_3addr header;
|
||||
u16 capability;
|
||||
u16 status;
|
||||
u16 aid;
|
||||
__le16 capability;
|
||||
__le16 status;
|
||||
__le16 aid;
|
||||
struct ieee80211_info_element info_element; /* supported rates */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
@ -563,7 +589,7 @@ struct ieee80211_txb {
|
||||
};
|
||||
|
||||
|
||||
/* SWEEP TABLE ENTRIES NUMBER*/
|
||||
/* SWEEP TABLE ENTRIES NUMBER */
|
||||
#define MAX_SWEEP_TAB_ENTRIES 42
|
||||
#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7
|
||||
/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs
|
||||
@ -624,8 +650,6 @@ enum ieee80211_state {
|
||||
|
||||
#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
|
||||
#define DEFAULT_FTS 2346
|
||||
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
|
||||
|
||||
|
||||
#define CFG_IEEE80211_RESERVE_FCS (1<<0)
|
||||
@ -793,8 +817,6 @@ extern struct net_device *alloc_ieee80211(int sizeof_priv);
|
||||
extern int ieee80211_set_encryption(struct ieee80211_device *ieee);
|
||||
|
||||
/* ieee80211_tx.c */
|
||||
|
||||
|
||||
extern int ieee80211_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
extern void ieee80211_txb_free(struct ieee80211_txb *);
|
||||
@ -807,7 +829,7 @@ extern void ieee80211_rx_mgt(struct ieee80211_device *ieee,
|
||||
struct ieee80211_hdr *header,
|
||||
struct ieee80211_rx_stats *stats);
|
||||
|
||||
/* iee80211_wx.c */
|
||||
/* ieee80211_wx.c */
|
||||
extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *key);
|
||||
@ -829,28 +851,5 @@ extern inline int ieee80211_get_scans(struct ieee80211_device *ieee)
|
||||
return ieee->scans;
|
||||
}
|
||||
|
||||
static inline const char *escape_essid(const char *essid, u8 essid_len) {
|
||||
static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
|
||||
const char *s = essid;
|
||||
char *d = escaped;
|
||||
|
||||
if (ieee80211_is_empty_essid(essid, essid_len)) {
|
||||
memcpy(escaped, "<hidden>", sizeof("<hidden>"));
|
||||
return escaped;
|
||||
}
|
||||
|
||||
essid_len = min(essid_len, (u8)IW_ESSID_MAX_SIZE);
|
||||
while (essid_len--) {
|
||||
if (*s == '\0') {
|
||||
*d++ = '\\';
|
||||
*d++ = '0';
|
||||
s++;
|
||||
} else {
|
||||
*d++ = *s++;
|
||||
}
|
||||
}
|
||||
*d = '\0';
|
||||
return escaped;
|
||||
}
|
||||
|
||||
#endif /* IEEE80211_H */
|
||||
|
86
include/net/ieee80211_crypt.h
Normal file
86
include/net/ieee80211_crypt.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Original code based on Host AP (software wireless LAN access point) driver
|
||||
* for Intersil Prism2/2.5/3.
|
||||
*
|
||||
* Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
|
||||
* <jkmaline@cc.hut.fi>
|
||||
* Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* Adaption to a generic IEEE 802.11 stack by James Ketrenos
|
||||
* <jketreno@linux.intel.com>
|
||||
*
|
||||
* Copyright (c) 2004, Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file defines the interface to the ieee80211 crypto module.
|
||||
*/
|
||||
#ifndef IEEE80211_CRYPT_H
|
||||
#define IEEE80211_CRYPT_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
struct ieee80211_crypto_ops {
|
||||
const char *name;
|
||||
|
||||
/* init new crypto context (e.g., allocate private data space,
|
||||
* select IV, etc.); returns NULL on failure or pointer to allocated
|
||||
* private data on success */
|
||||
void * (*init)(int keyidx);
|
||||
|
||||
/* deinitialize crypto context and free allocated private data */
|
||||
void (*deinit)(void *priv);
|
||||
|
||||
/* encrypt/decrypt return < 0 on error or >= 0 on success. The return
|
||||
* value from decrypt_mpdu is passed as the keyidx value for
|
||||
* decrypt_msdu. skb must have enough head and tail room for the
|
||||
* encryption; if not, error will be returned; these functions are
|
||||
* called for all MPDUs (i.e., fragments).
|
||||
*/
|
||||
int (*encrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv);
|
||||
int (*decrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv);
|
||||
|
||||
/* These functions are called for full MSDUs, i.e. full frames.
|
||||
* These can be NULL if full MSDU operations are not needed. */
|
||||
int (*encrypt_msdu)(struct sk_buff *skb, int hdr_len, void *priv);
|
||||
int (*decrypt_msdu)(struct sk_buff *skb, int keyidx, int hdr_len,
|
||||
void *priv);
|
||||
|
||||
int (*set_key)(void *key, int len, u8 *seq, void *priv);
|
||||
int (*get_key)(void *key, int len, u8 *seq, void *priv);
|
||||
|
||||
/* procfs handler for printing out key information and possible
|
||||
* statistics */
|
||||
char * (*print_stats)(char *p, void *priv);
|
||||
|
||||
/* maximum number of bytes added by encryption; encrypt buf is
|
||||
* allocated with extra_prefix_len bytes, copy of in_buf, and
|
||||
* extra_postfix_len; encrypt need not use all this space, but
|
||||
* the result must start at the beginning of the buffer and correct
|
||||
* length must be returned */
|
||||
int extra_prefix_len, extra_postfix_len;
|
||||
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
struct ieee80211_crypt_data {
|
||||
struct list_head list; /* delayed deletion list */
|
||||
struct ieee80211_crypto_ops *ops;
|
||||
void *priv;
|
||||
atomic_t refcnt;
|
||||
};
|
||||
|
||||
int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops);
|
||||
int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops);
|
||||
struct ieee80211_crypto_ops * ieee80211_get_crypto_ops(const char *name);
|
||||
void ieee80211_crypt_deinit_entries(struct ieee80211_device *, int);
|
||||
void ieee80211_crypt_deinit_handler(unsigned long);
|
||||
void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
|
||||
struct ieee80211_crypt_data **crypt);
|
||||
|
||||
#endif
|
@ -215,6 +215,7 @@ endmenu
|
||||
source "net/ax25/Kconfig"
|
||||
source "net/irda/Kconfig"
|
||||
source "net/bluetooth/Kconfig"
|
||||
source "net/ieee80211/Kconfig"
|
||||
|
||||
endif # if NET
|
||||
endmenu # Networking
|
||||
|
@ -44,6 +44,7 @@ obj-$(CONFIG_ECONET) += econet/
|
||||
obj-$(CONFIG_VLAN_8021Q) += 8021q/
|
||||
obj-$(CONFIG_IP_DCCP) += dccp/
|
||||
obj-$(CONFIG_IP_SCTP) += sctp/
|
||||
obj-$(CONFIG_IEEE80211) += ieee80211/
|
||||
|
||||
ifeq ($(CONFIG_NET),y)
|
||||
obj-$(CONFIG_SYSCTL) += sysctl_net.o
|
||||
|
69
net/ieee80211/Kconfig
Normal file
69
net/ieee80211/Kconfig
Normal file
@ -0,0 +1,69 @@
|
||||
config IEEE80211
|
||||
tristate "Generic IEEE 802.11 Networking Stack"
|
||||
select NET_RADIO
|
||||
---help---
|
||||
This option enables the hardware independent IEEE 802.11
|
||||
networking stack.
|
||||
|
||||
config IEEE80211_DEBUG
|
||||
bool "Enable full debugging output"
|
||||
depends on IEEE80211
|
||||
---help---
|
||||
This option will enable debug tracing output for the
|
||||
ieee80211 network stack.
|
||||
|
||||
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 ieee80211
|
||||
subsystem, you most likely want to say N here.
|
||||
|
||||
config IEEE80211_CRYPT_WEP
|
||||
tristate "IEEE 802.11 WEP encryption (802.1x)"
|
||||
depends on IEEE80211
|
||||
select CRYPTO
|
||||
select CRYPTO_ARC4
|
||||
select CRC32
|
||||
---help---
|
||||
Include software based cipher suites in support of IEEE
|
||||
802.11's WEP. This is needed for WEP as well as 802.1x.
|
||||
|
||||
This can be compiled as a modules and it will be called
|
||||
"ieee80211_crypt_wep".
|
||||
|
||||
config IEEE80211_CRYPT_CCMP
|
||||
tristate "IEEE 802.11i CCMP support"
|
||||
depends on IEEE80211
|
||||
select CRYPTO
|
||||
select CRYPTO_AES
|
||||
---help---
|
||||
Include software based cipher suites in support of IEEE 802.11i
|
||||
(aka TGi, WPA, WPA2, WPA-PSK, etc.) for use with CCMP enabled
|
||||
networks.
|
||||
|
||||
This can be compiled as a modules and it will be called
|
||||
"ieee80211_crypt_ccmp".
|
||||
|
||||
config IEEE80211_CRYPT_TKIP
|
||||
tristate "IEEE 802.11i TKIP encryption"
|
||||
depends on IEEE80211
|
||||
select CRYPTO
|
||||
select CRYPTO_MICHAEL_MIC
|
||||
---help---
|
||||
Include software based cipher suites in support of IEEE 802.11i
|
||||
(aka TGi, WPA, WPA2, WPA-PSK, etc.) for use with TKIP enabled
|
||||
networks.
|
||||
|
||||
This can be compiled as a modules and it will be called
|
||||
"ieee80211_crypt_tkip".
|
||||
|
11
net/ieee80211/Makefile
Normal file
11
net/ieee80211/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
obj-$(CONFIG_IEEE80211) += ieee80211.o
|
||||
obj-$(CONFIG_IEEE80211) += ieee80211_crypt.o
|
||||
obj-$(CONFIG_IEEE80211_CRYPT_WEP) += ieee80211_crypt_wep.o
|
||||
obj-$(CONFIG_IEEE80211_CRYPT_CCMP) += ieee80211_crypt_ccmp.o
|
||||
obj-$(CONFIG_IEEE80211_CRYPT_TKIP) += ieee80211_crypt_tkip.o
|
||||
ieee80211-objs := \
|
||||
ieee80211_module.o \
|
||||
ieee80211_tx.o \
|
||||
ieee80211_rx.o \
|
||||
ieee80211_wx.o
|
||||
|
259
net/ieee80211/ieee80211_crypt.c
Normal file
259
net/ieee80211/ieee80211_crypt.c
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Host AP crypto routines
|
||||
*
|
||||
* Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
* Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/string.h>
|
||||
#include <asm/errno.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("HostAP crypto");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct ieee80211_crypto_alg {
|
||||
struct list_head list;
|
||||
struct ieee80211_crypto_ops *ops;
|
||||
};
|
||||
|
||||
|
||||
struct ieee80211_crypto {
|
||||
struct list_head algs;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct ieee80211_crypto *hcrypt;
|
||||
|
||||
void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee,
|
||||
int force)
|
||||
{
|
||||
struct list_head *ptr, *n;
|
||||
struct ieee80211_crypt_data *entry;
|
||||
|
||||
for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
|
||||
ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
|
||||
entry = list_entry(ptr, struct ieee80211_crypt_data, list);
|
||||
|
||||
if (atomic_read(&entry->refcnt) != 0 && !force)
|
||||
continue;
|
||||
|
||||
list_del(ptr);
|
||||
|
||||
if (entry->ops) {
|
||||
entry->ops->deinit(entry->priv);
|
||||
module_put(entry->ops->owner);
|
||||
}
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_crypt_deinit_handler(unsigned long data)
|
||||
{
|
||||
struct ieee80211_device *ieee = (struct ieee80211_device *)data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ieee->lock, flags);
|
||||
ieee80211_crypt_deinit_entries(ieee, 0);
|
||||
if (!list_empty(&ieee->crypt_deinit_list)) {
|
||||
printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
|
||||
"deletion list\n", ieee->dev->name);
|
||||
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
||||
add_timer(&ieee->crypt_deinit_timer);
|
||||
}
|
||||
spin_unlock_irqrestore(&ieee->lock, flags);
|
||||
|
||||
}
|
||||
|
||||
void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
|
||||
struct ieee80211_crypt_data **crypt)
|
||||
{
|
||||
struct ieee80211_crypt_data *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
if (*crypt == NULL)
|
||||
return;
|
||||
|
||||
tmp = *crypt;
|
||||
*crypt = NULL;
|
||||
|
||||
/* must not run ops->deinit() while there may be pending encrypt or
|
||||
* decrypt operations. Use a list of delayed deinits to avoid needing
|
||||
* locking. */
|
||||
|
||||
spin_lock_irqsave(&ieee->lock, flags);
|
||||
list_add(&tmp->list, &ieee->crypt_deinit_list);
|
||||
if (!timer_pending(&ieee->crypt_deinit_timer)) {
|
||||
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
||||
add_timer(&ieee->crypt_deinit_timer);
|
||||
}
|
||||
spin_unlock_irqrestore(&ieee->lock, flags);
|
||||
}
|
||||
|
||||
int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ieee80211_crypto_alg *alg;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return -1;
|
||||
|
||||
alg = kmalloc(sizeof(*alg), GFP_KERNEL);
|
||||
if (alg == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(alg, 0, sizeof(*alg));
|
||||
alg->ops = ops;
|
||||
|
||||
spin_lock_irqsave(&hcrypt->lock, flags);
|
||||
list_add(&alg->list, &hcrypt->algs);
|
||||
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
||||
|
||||
printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n",
|
||||
ops->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *ptr;
|
||||
struct ieee80211_crypto_alg *del_alg = NULL;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return -1;
|
||||
|
||||
spin_lock_irqsave(&hcrypt->lock, flags);
|
||||
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
||||
struct ieee80211_crypto_alg *alg =
|
||||
(struct ieee80211_crypto_alg *) ptr;
|
||||
if (alg->ops == ops) {
|
||||
list_del(&alg->list);
|
||||
del_alg = alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
||||
|
||||
if (del_alg) {
|
||||
printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
|
||||
"'%s'\n", ops->name);
|
||||
kfree(del_alg);
|
||||
}
|
||||
|
||||
return del_alg ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
struct ieee80211_crypto_ops * ieee80211_get_crypto_ops(const char *name)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *ptr;
|
||||
struct ieee80211_crypto_alg *found_alg = NULL;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&hcrypt->lock, flags);
|
||||
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
||||
struct ieee80211_crypto_alg *alg =
|
||||
(struct ieee80211_crypto_alg *) ptr;
|
||||
if (strcmp(alg->ops->name, name) == 0) {
|
||||
found_alg = alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
||||
|
||||
if (found_alg)
|
||||
return found_alg->ops;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void * ieee80211_crypt_null_init(int keyidx) { return (void *) 1; }
|
||||
static void ieee80211_crypt_null_deinit(void *priv) {}
|
||||
|
||||
static struct ieee80211_crypto_ops ieee80211_crypt_null = {
|
||||
.name = "NULL",
|
||||
.init = ieee80211_crypt_null_init,
|
||||
.deinit = ieee80211_crypt_null_deinit,
|
||||
.encrypt_mpdu = NULL,
|
||||
.decrypt_mpdu = NULL,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = NULL,
|
||||
.get_key = NULL,
|
||||
.extra_prefix_len = 0,
|
||||
.extra_postfix_len = 0,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int __init ieee80211_crypto_init(void)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
|
||||
hcrypt = kmalloc(sizeof(*hcrypt), GFP_KERNEL);
|
||||
if (!hcrypt)
|
||||
goto out;
|
||||
|
||||
memset(hcrypt, 0, sizeof(*hcrypt));
|
||||
INIT_LIST_HEAD(&hcrypt->algs);
|
||||
spin_lock_init(&hcrypt->lock);
|
||||
|
||||
ret = ieee80211_register_crypto_ops(&ieee80211_crypt_null);
|
||||
if (ret < 0) {
|
||||
kfree(hcrypt);
|
||||
hcrypt = NULL;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void __exit ieee80211_crypto_deinit(void)
|
||||
{
|
||||
struct list_head *ptr, *n;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return;
|
||||
|
||||
for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
|
||||
ptr = n, n = ptr->next) {
|
||||
struct ieee80211_crypto_alg *alg =
|
||||
(struct ieee80211_crypto_alg *) ptr;
|
||||
list_del(ptr);
|
||||
printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
|
||||
"'%s' (deinit)\n", alg->ops->name);
|
||||
kfree(alg);
|
||||
}
|
||||
|
||||
kfree(hcrypt);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ieee80211_crypt_deinit_entries);
|
||||
EXPORT_SYMBOL(ieee80211_crypt_deinit_handler);
|
||||
EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit);
|
||||
|
||||
EXPORT_SYMBOL(ieee80211_register_crypto_ops);
|
||||
EXPORT_SYMBOL(ieee80211_unregister_crypto_ops);
|
||||
EXPORT_SYMBOL(ieee80211_get_crypto_ops);
|
||||
|
||||
module_init(ieee80211_crypto_init);
|
||||
module_exit(ieee80211_crypto_deinit);
|
470
net/ieee80211/ieee80211_crypt_ccmp.c
Normal file
470
net/ieee80211/ieee80211_crypt_ccmp.c
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Host AP crypt: host-based CCMP encryption implementation for Host AP driver
|
||||
*
|
||||
* Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <asm/string.h>
|
||||
#include <linux/wireless.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <asm/scatterlist.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Host AP crypt: CCMP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define AES_BLOCK_LEN 16
|
||||
#define CCMP_HDR_LEN 8
|
||||
#define CCMP_MIC_LEN 8
|
||||
#define CCMP_TK_LEN 16
|
||||
#define CCMP_PN_LEN 6
|
||||
|
||||
struct ieee80211_ccmp_data {
|
||||
u8 key[CCMP_TK_LEN];
|
||||
int key_set;
|
||||
|
||||
u8 tx_pn[CCMP_PN_LEN];
|
||||
u8 rx_pn[CCMP_PN_LEN];
|
||||
|
||||
u32 dot11RSNAStatsCCMPFormatErrors;
|
||||
u32 dot11RSNAStatsCCMPReplays;
|
||||
u32 dot11RSNAStatsCCMPDecryptErrors;
|
||||
|
||||
int key_idx;
|
||||
|
||||
struct crypto_tfm *tfm;
|
||||
|
||||
/* scratch buffers for virt_to_page() (crypto API) */
|
||||
u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
|
||||
tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
|
||||
u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
|
||||
};
|
||||
|
||||
static void ieee80211_ccmp_aes_encrypt(struct crypto_tfm *tfm,
|
||||
const u8 pt[16], u8 ct[16])
|
||||
{
|
||||
struct scatterlist src, dst;
|
||||
|
||||
src.page = virt_to_page(pt);
|
||||
src.offset = offset_in_page(pt);
|
||||
src.length = AES_BLOCK_LEN;
|
||||
|
||||
dst.page = virt_to_page(ct);
|
||||
dst.offset = offset_in_page(ct);
|
||||
dst.length = AES_BLOCK_LEN;
|
||||
|
||||
crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN);
|
||||
}
|
||||
|
||||
static void * ieee80211_ccmp_init(int key_idx)
|
||||
{
|
||||
struct ieee80211_ccmp_data *priv;
|
||||
|
||||
priv = kmalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->key_idx = key_idx;
|
||||
|
||||
priv->tfm = crypto_alloc_tfm("aes", 0);
|
||||
if (priv->tfm == NULL) {
|
||||
printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate "
|
||||
"crypto API aes\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm)
|
||||
crypto_free_tfm(priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211_ccmp_deinit(void *priv)
|
||||
{
|
||||
struct ieee80211_ccmp_data *_priv = priv;
|
||||
if (_priv && _priv->tfm)
|
||||
crypto_free_tfm(_priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
|
||||
static inline void xor_block(u8 *b, u8 *a, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
b[i] ^= a[i];
|
||||
}
|
||||
|
||||
|
||||
static void ccmp_init_blocks(struct crypto_tfm *tfm,
|
||||
struct ieee80211_hdr *hdr,
|
||||
u8 *pn, size_t dlen, u8 *b0, u8 *auth,
|
||||
u8 *s0)
|
||||
{
|
||||
u8 *pos, qc = 0;
|
||||
size_t aad_len;
|
||||
u16 fc;
|
||||
int a4_included, qc_included;
|
||||
u8 aad[2 * AES_BLOCK_LEN];
|
||||
|
||||
fc = le16_to_cpu(hdr->frame_ctl);
|
||||
a4_included = ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
|
||||
(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS));
|
||||
qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) &&
|
||||
(WLAN_FC_GET_STYPE(fc) & 0x08));
|
||||
aad_len = 22;
|
||||
if (a4_included)
|
||||
aad_len += 6;
|
||||
if (qc_included) {
|
||||
pos = (u8 *) &hdr->addr4;
|
||||
if (a4_included)
|
||||
pos += 6;
|
||||
qc = *pos & 0x0f;
|
||||
aad_len += 2;
|
||||
}
|
||||
|
||||
/* CCM Initial Block:
|
||||
* Flag (Include authentication header, M=3 (8-octet MIC),
|
||||
* L=1 (2-octet Dlen))
|
||||
* Nonce: 0x00 | A2 | PN
|
||||
* Dlen */
|
||||
b0[0] = 0x59;
|
||||
b0[1] = qc;
|
||||
memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
|
||||
memcpy(b0 + 8, pn, CCMP_PN_LEN);
|
||||
b0[14] = (dlen >> 8) & 0xff;
|
||||
b0[15] = dlen & 0xff;
|
||||
|
||||
/* AAD:
|
||||
* FC with bits 4..6 and 11..13 masked to zero; 14 is always one
|
||||
* A1 | A2 | A3
|
||||
* SC with bits 4..15 (seq#) masked to zero
|
||||
* A4 (if present)
|
||||
* QC (if present)
|
||||
*/
|
||||
pos = (u8 *) hdr;
|
||||
aad[0] = 0; /* aad_len >> 8 */
|
||||
aad[1] = aad_len & 0xff;
|
||||
aad[2] = pos[0] & 0x8f;
|
||||
aad[3] = pos[1] & 0xc7;
|
||||
memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
|
||||
pos = (u8 *) &hdr->seq_ctl;
|
||||
aad[22] = pos[0] & 0x0f;
|
||||
aad[23] = 0; /* all bits masked */
|
||||
memset(aad + 24, 0, 8);
|
||||
if (a4_included)
|
||||
memcpy(aad + 24, hdr->addr4, ETH_ALEN);
|
||||
if (qc_included) {
|
||||
aad[a4_included ? 30 : 24] = qc;
|
||||
/* rest of QC masked */
|
||||
}
|
||||
|
||||
/* Start with the first block and AAD */
|
||||
ieee80211_ccmp_aes_encrypt(tfm, b0, auth);
|
||||
xor_block(auth, aad, AES_BLOCK_LEN);
|
||||
ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
|
||||
ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
b0[0] &= 0x07;
|
||||
b0[14] = b0[15] = 0;
|
||||
ieee80211_ccmp_aes_encrypt(tfm, b0, s0);
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct ieee80211_ccmp_data *key = priv;
|
||||
int data_len, i, blocks, last, len;
|
||||
u8 *pos, *mic;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->tx_b0;
|
||||
u8 *b = key->tx_b;
|
||||
u8 *e = key->tx_e;
|
||||
u8 *s0 = key->tx_s0;
|
||||
|
||||
if (skb_headroom(skb) < CCMP_HDR_LEN ||
|
||||
skb_tailroom(skb) < CCMP_MIC_LEN ||
|
||||
skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
data_len = skb->len - hdr_len;
|
||||
pos = skb_push(skb, CCMP_HDR_LEN);
|
||||
memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
|
||||
pos += hdr_len;
|
||||
mic = skb_put(skb, CCMP_MIC_LEN);
|
||||
|
||||
i = CCMP_PN_LEN - 1;
|
||||
while (i >= 0) {
|
||||
key->tx_pn[i]++;
|
||||
if (key->tx_pn[i] != 0)
|
||||
break;
|
||||
i--;
|
||||
}
|
||||
|
||||
*pos++ = key->tx_pn[5];
|
||||
*pos++ = key->tx_pn[4];
|
||||
*pos++ = 0;
|
||||
*pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */;
|
||||
*pos++ = key->tx_pn[3];
|
||||
*pos++ = key->tx_pn[2];
|
||||
*pos++ = key->tx_pn[1];
|
||||
*pos++ = key->tx_pn[0];
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
|
||||
|
||||
blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Authentication */
|
||||
xor_block(b, pos, len);
|
||||
ieee80211_ccmp_aes_encrypt(key->tfm, b, b);
|
||||
/* Encryption, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
ieee80211_ccmp_aes_encrypt(key->tfm, b0, e);
|
||||
xor_block(pos, e, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
for (i = 0; i < CCMP_MIC_LEN; i++)
|
||||
mic[i] = b[i] ^ s0[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct ieee80211_ccmp_data *key = priv;
|
||||
u8 keyidx, *pos;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->rx_b0;
|
||||
u8 *b = key->rx_b;
|
||||
u8 *a = key->rx_a;
|
||||
u8 pn[6];
|
||||
int i, blocks, last, len;
|
||||
size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
|
||||
u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
|
||||
|
||||
if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
|
||||
key->dot11RSNAStatsCCMPFormatErrors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
pos = skb->data + hdr_len;
|
||||
keyidx = pos[3];
|
||||
if (!(keyidx & (1 << 5))) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: received packet without ExtIV"
|
||||
" flag from " MAC_FMT "\n", MAC_ARG(hdr->addr2));
|
||||
}
|
||||
key->dot11RSNAStatsCCMPFormatErrors++;
|
||||
return -2;
|
||||
}
|
||||
keyidx >>= 6;
|
||||
if (key->key_idx != keyidx) {
|
||||
printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
|
||||
"keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
|
||||
return -6;
|
||||
}
|
||||
if (!key->key_set) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: received packet from " MAC_FMT
|
||||
" with keyid=%d that does not have a configured"
|
||||
" key\n", MAC_ARG(hdr->addr2), keyidx);
|
||||
}
|
||||
return -3;
|
||||
}
|
||||
|
||||
pn[0] = pos[7];
|
||||
pn[1] = pos[6];
|
||||
pn[2] = pos[5];
|
||||
pn[3] = pos[4];
|
||||
pn[4] = pos[1];
|
||||
pn[5] = pos[0];
|
||||
pos += 8;
|
||||
|
||||
if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: replay detected: STA=" MAC_FMT
|
||||
" previous PN %02x%02x%02x%02x%02x%02x "
|
||||
"received PN %02x%02x%02x%02x%02x%02x\n",
|
||||
MAC_ARG(hdr->addr2), MAC_ARG(key->rx_pn),
|
||||
MAC_ARG(pn));
|
||||
}
|
||||
key->dot11RSNAStatsCCMPReplays++;
|
||||
return -4;
|
||||
}
|
||||
|
||||
ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
|
||||
xor_block(mic, b, CCMP_MIC_LEN);
|
||||
|
||||
blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Decrypt, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
ieee80211_ccmp_aes_encrypt(key->tfm, b0, b);
|
||||
xor_block(pos, b, len);
|
||||
/* Authentication */
|
||||
xor_block(a, pos, len);
|
||||
ieee80211_ccmp_aes_encrypt(key->tfm, a, a);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: decrypt failed: STA="
|
||||
MAC_FMT "\n", MAC_ARG(hdr->addr2));
|
||||
}
|
||||
key->dot11RSNAStatsCCMPDecryptErrors++;
|
||||
return -5;
|
||||
}
|
||||
|
||||
memcpy(key->rx_pn, pn, CCMP_PN_LEN);
|
||||
|
||||
/* Remove hdr and MIC */
|
||||
memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
|
||||
skb_pull(skb, CCMP_HDR_LEN);
|
||||
skb_trim(skb, skb->len - CCMP_MIC_LEN);
|
||||
|
||||
return keyidx;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ccmp_set_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct ieee80211_ccmp_data *data = priv;
|
||||
int keyidx;
|
||||
struct crypto_tfm *tfm = data->tfm;
|
||||
|
||||
keyidx = data->key_idx;
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->key_idx = keyidx;
|
||||
data->tfm = tfm;
|
||||
if (len == CCMP_TK_LEN) {
|
||||
memcpy(data->key, key, CCMP_TK_LEN);
|
||||
data->key_set = 1;
|
||||
if (seq) {
|
||||
data->rx_pn[0] = seq[5];
|
||||
data->rx_pn[1] = seq[4];
|
||||
data->rx_pn[2] = seq[3];
|
||||
data->rx_pn[3] = seq[2];
|
||||
data->rx_pn[4] = seq[1];
|
||||
data->rx_pn[5] = seq[0];
|
||||
}
|
||||
crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
|
||||
} else if (len == 0)
|
||||
data->key_set = 0;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ccmp_get_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct ieee80211_ccmp_data *data = priv;
|
||||
|
||||
if (len < CCMP_TK_LEN)
|
||||
return -1;
|
||||
|
||||
if (!data->key_set)
|
||||
return 0;
|
||||
memcpy(key, data->key, CCMP_TK_LEN);
|
||||
|
||||
if (seq) {
|
||||
seq[0] = data->tx_pn[5];
|
||||
seq[1] = data->tx_pn[4];
|
||||
seq[2] = data->tx_pn[3];
|
||||
seq[3] = data->tx_pn[2];
|
||||
seq[4] = data->tx_pn[1];
|
||||
seq[5] = data->tx_pn[0];
|
||||
}
|
||||
|
||||
return CCMP_TK_LEN;
|
||||
}
|
||||
|
||||
|
||||
static char * ieee80211_ccmp_print_stats(char *p, void *priv)
|
||||
{
|
||||
struct ieee80211_ccmp_data *ccmp = priv;
|
||||
p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
|
||||
"tx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"rx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"format_errors=%d replays=%d decrypt_errors=%d\n",
|
||||
ccmp->key_idx, ccmp->key_set,
|
||||
MAC_ARG(ccmp->tx_pn), MAC_ARG(ccmp->rx_pn),
|
||||
ccmp->dot11RSNAStatsCCMPFormatErrors,
|
||||
ccmp->dot11RSNAStatsCCMPReplays,
|
||||
ccmp->dot11RSNAStatsCCMPDecryptErrors);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = {
|
||||
.name = "CCMP",
|
||||
.init = ieee80211_ccmp_init,
|
||||
.deinit = ieee80211_ccmp_deinit,
|
||||
.encrypt_mpdu = ieee80211_ccmp_encrypt,
|
||||
.decrypt_mpdu = ieee80211_ccmp_decrypt,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = ieee80211_ccmp_set_key,
|
||||
.get_key = ieee80211_ccmp_get_key,
|
||||
.print_stats = ieee80211_ccmp_print_stats,
|
||||
.extra_prefix_len = CCMP_HDR_LEN,
|
||||
.extra_postfix_len = CCMP_MIC_LEN,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int __init ieee80211_crypto_ccmp_init(void)
|
||||
{
|
||||
return ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp);
|
||||
}
|
||||
|
||||
|
||||
static void __exit ieee80211_crypto_ccmp_exit(void)
|
||||
{
|
||||
ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp);
|
||||
}
|
||||
|
||||
|
||||
module_init(ieee80211_crypto_ccmp_init);
|
||||
module_exit(ieee80211_crypto_ccmp_exit);
|
708
net/ieee80211/ieee80211_crypt_tkip.c
Normal file
708
net/ieee80211/ieee80211_crypt_tkip.c
Normal file
@ -0,0 +1,708 @@
|
||||
/*
|
||||
* Host AP crypt: host-based TKIP encryption implementation for Host AP driver
|
||||
*
|
||||
* Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Host AP crypt: TKIP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct ieee80211_tkip_data {
|
||||
#define TKIP_KEY_LEN 32
|
||||
u8 key[TKIP_KEY_LEN];
|
||||
int key_set;
|
||||
|
||||
u32 tx_iv32;
|
||||
u16 tx_iv16;
|
||||
u16 tx_ttak[5];
|
||||
int tx_phase1_done;
|
||||
|
||||
u32 rx_iv32;
|
||||
u16 rx_iv16;
|
||||
u16 rx_ttak[5];
|
||||
int rx_phase1_done;
|
||||
u32 rx_iv32_new;
|
||||
u16 rx_iv16_new;
|
||||
|
||||
u32 dot11RSNAStatsTKIPReplays;
|
||||
u32 dot11RSNAStatsTKIPICVErrors;
|
||||
u32 dot11RSNAStatsTKIPLocalMICFailures;
|
||||
|
||||
int key_idx;
|
||||
|
||||
struct crypto_tfm *tfm_arc4;
|
||||
struct crypto_tfm *tfm_michael;
|
||||
|
||||
/* scratch buffers for virt_to_page() (crypto API) */
|
||||
u8 rx_hdr[16], tx_hdr[16];
|
||||
};
|
||||
|
||||
static void * ieee80211_tkip_init(int key_idx)
|
||||
{
|
||||
struct ieee80211_tkip_data *priv;
|
||||
|
||||
priv = kmalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->key_idx = key_idx;
|
||||
|
||||
priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0);
|
||||
if (priv->tfm_arc4 == NULL) {
|
||||
printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
|
||||
"crypto API arc4\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0);
|
||||
if (priv->tfm_michael == NULL) {
|
||||
printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
|
||||
"crypto API michael_mic\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm_michael)
|
||||
crypto_free_tfm(priv->tfm_michael);
|
||||
if (priv->tfm_arc4)
|
||||
crypto_free_tfm(priv->tfm_arc4);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211_tkip_deinit(void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *_priv = priv;
|
||||
if (_priv && _priv->tfm_michael)
|
||||
crypto_free_tfm(_priv->tfm_michael);
|
||||
if (_priv && _priv->tfm_arc4)
|
||||
crypto_free_tfm(_priv->tfm_arc4);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
|
||||
static inline u16 RotR1(u16 val)
|
||||
{
|
||||
return (val >> 1) | (val << 15);
|
||||
}
|
||||
|
||||
|
||||
static inline u8 Lo8(u16 val)
|
||||
{
|
||||
return val & 0xff;
|
||||
}
|
||||
|
||||
|
||||
static inline u8 Hi8(u16 val)
|
||||
{
|
||||
return val >> 8;
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Lo16(u32 val)
|
||||
{
|
||||
return val & 0xffff;
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Hi16(u32 val)
|
||||
{
|
||||
return val >> 16;
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Mk16(u8 hi, u8 lo)
|
||||
{
|
||||
return lo | (((u16) hi) << 8);
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Mk16_le(u16 *v)
|
||||
{
|
||||
return le16_to_cpu(*v);
|
||||
}
|
||||
|
||||
|
||||
static const u16 Sbox[256] =
|
||||
{
|
||||
0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
|
||||
0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
|
||||
0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
|
||||
0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
|
||||
0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
|
||||
0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
|
||||
0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
|
||||
0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
|
||||
0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
|
||||
0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
|
||||
0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
|
||||
0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
|
||||
0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
|
||||
0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
|
||||
0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
|
||||
0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
|
||||
0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
|
||||
0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
|
||||
0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
|
||||
0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
|
||||
0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
|
||||
0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
|
||||
0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
|
||||
0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
|
||||
0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
|
||||
0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
|
||||
0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
|
||||
0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
|
||||
0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
|
||||
0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
|
||||
0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
|
||||
0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
|
||||
};
|
||||
|
||||
|
||||
static inline u16 _S_(u16 v)
|
||||
{
|
||||
u16 t = Sbox[Hi8(v)];
|
||||
return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
|
||||
}
|
||||
|
||||
|
||||
#define PHASE1_LOOP_COUNT 8
|
||||
|
||||
static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
|
||||
TTAK[0] = Lo16(IV32);
|
||||
TTAK[1] = Hi16(IV32);
|
||||
TTAK[2] = Mk16(TA[1], TA[0]);
|
||||
TTAK[3] = Mk16(TA[3], TA[2]);
|
||||
TTAK[4] = Mk16(TA[5], TA[4]);
|
||||
|
||||
for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
|
||||
j = 2 * (i & 1);
|
||||
TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
|
||||
TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
|
||||
TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
|
||||
TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
|
||||
TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
|
||||
u16 IV16)
|
||||
{
|
||||
/* Make temporary area overlap WEP seed so that the final copy can be
|
||||
* avoided on little endian hosts. */
|
||||
u16 *PPK = (u16 *) &WEPSeed[4];
|
||||
|
||||
/* Step 1 - make copy of TTAK and bring in TSC */
|
||||
PPK[0] = TTAK[0];
|
||||
PPK[1] = TTAK[1];
|
||||
PPK[2] = TTAK[2];
|
||||
PPK[3] = TTAK[3];
|
||||
PPK[4] = TTAK[4];
|
||||
PPK[5] = TTAK[4] + IV16;
|
||||
|
||||
/* Step 2 - 96-bit bijective mixing using S-box */
|
||||
PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0]));
|
||||
PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2]));
|
||||
PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4]));
|
||||
PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6]));
|
||||
PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8]));
|
||||
PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10]));
|
||||
|
||||
PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12]));
|
||||
PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14]));
|
||||
PPK[2] += RotR1(PPK[1]);
|
||||
PPK[3] += RotR1(PPK[2]);
|
||||
PPK[4] += RotR1(PPK[3]);
|
||||
PPK[5] += RotR1(PPK[4]);
|
||||
|
||||
/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
|
||||
* WEPSeed[0..2] is transmitted as WEP IV */
|
||||
WEPSeed[0] = Hi8(IV16);
|
||||
WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
|
||||
WEPSeed[2] = Lo8(IV16);
|
||||
WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1);
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 6; i++)
|
||||
PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *tkey = priv;
|
||||
int len;
|
||||
u8 rc4key[16], *pos, *icv;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u32 crc;
|
||||
struct scatterlist sg;
|
||||
|
||||
if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 ||
|
||||
skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
if (!tkey->tx_phase1_done) {
|
||||
tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
|
||||
tkey->tx_iv32);
|
||||
tkey->tx_phase1_done = 1;
|
||||
}
|
||||
tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
|
||||
|
||||
len = skb->len - hdr_len;
|
||||
pos = skb_push(skb, 8);
|
||||
memmove(pos, pos + 8, hdr_len);
|
||||
pos += hdr_len;
|
||||
icv = skb_put(skb, 4);
|
||||
|
||||
*pos++ = rc4key[0];
|
||||
*pos++ = rc4key[1];
|
||||
*pos++ = rc4key[2];
|
||||
*pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */;
|
||||
*pos++ = tkey->tx_iv32 & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 8) & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 16) & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 24) & 0xff;
|
||||
|
||||
crc = ~crc32_le(~0, pos, len);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
|
||||
crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = len + 4;
|
||||
crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4);
|
||||
|
||||
tkey->tx_iv16++;
|
||||
if (tkey->tx_iv16 == 0) {
|
||||
tkey->tx_phase1_done = 0;
|
||||
tkey->tx_iv32++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *tkey = priv;
|
||||
u8 rc4key[16];
|
||||
u8 keyidx, *pos;
|
||||
u32 iv32;
|
||||
u16 iv16;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 icv[4];
|
||||
u32 crc;
|
||||
struct scatterlist sg;
|
||||
int plen;
|
||||
|
||||
if (skb->len < hdr_len + 8 + 4)
|
||||
return -1;
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
pos = skb->data + hdr_len;
|
||||
keyidx = pos[3];
|
||||
if (!(keyidx & (1 << 5))) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: received packet without ExtIV"
|
||||
" flag from " MAC_FMT "\n", MAC_ARG(hdr->addr2));
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
keyidx >>= 6;
|
||||
if (tkey->key_idx != keyidx) {
|
||||
printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
|
||||
"keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
|
||||
return -6;
|
||||
}
|
||||
if (!tkey->key_set) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: received packet from " MAC_FMT
|
||||
" with keyid=%d that does not have a configured"
|
||||
" key\n", MAC_ARG(hdr->addr2), keyidx);
|
||||
}
|
||||
return -3;
|
||||
}
|
||||
iv16 = (pos[0] << 8) | pos[2];
|
||||
iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
|
||||
pos += 8;
|
||||
|
||||
if (iv32 < tkey->rx_iv32 ||
|
||||
(iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: replay detected: STA=" MAC_FMT
|
||||
" previous TSC %08x%04x received TSC "
|
||||
"%08x%04x\n", MAC_ARG(hdr->addr2),
|
||||
tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
|
||||
}
|
||||
tkey->dot11RSNAStatsTKIPReplays++;
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
|
||||
tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
|
||||
tkey->rx_phase1_done = 1;
|
||||
}
|
||||
tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
|
||||
|
||||
plen = skb->len - hdr_len - 12;
|
||||
|
||||
crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = plen + 4;
|
||||
crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4);
|
||||
|
||||
crc = ~crc32_le(~0, pos, plen);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
if (memcmp(icv, pos + plen, 4) != 0) {
|
||||
if (iv32 != tkey->rx_iv32) {
|
||||
/* Previously cached Phase1 result was already lost, so
|
||||
* it needs to be recalculated for the next packet. */
|
||||
tkey->rx_phase1_done = 0;
|
||||
}
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: ICV error detected: STA="
|
||||
MAC_FMT "\n", MAC_ARG(hdr->addr2));
|
||||
}
|
||||
tkey->dot11RSNAStatsTKIPICVErrors++;
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Update real counters only after Michael MIC verification has
|
||||
* completed */
|
||||
tkey->rx_iv32_new = iv32;
|
||||
tkey->rx_iv16_new = iv16;
|
||||
|
||||
/* Remove IV and ICV */
|
||||
memmove(skb->data + 8, skb->data, hdr_len);
|
||||
skb_pull(skb, 8);
|
||||
skb_trim(skb, skb->len - 4);
|
||||
|
||||
return keyidx;
|
||||
}
|
||||
|
||||
|
||||
static int michael_mic(struct ieee80211_tkip_data *tkey, u8 *key, u8 *hdr,
|
||||
u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
struct scatterlist sg[2];
|
||||
|
||||
if (tkey->tfm_michael == NULL) {
|
||||
printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
|
||||
return -1;
|
||||
}
|
||||
sg[0].page = virt_to_page(hdr);
|
||||
sg[0].offset = offset_in_page(hdr);
|
||||
sg[0].length = 16;
|
||||
|
||||
sg[1].page = virt_to_page(data);
|
||||
sg[1].offset = offset_in_page(data);
|
||||
sg[1].length = data_len;
|
||||
|
||||
crypto_digest_init(tkey->tfm_michael);
|
||||
crypto_digest_setkey(tkey->tfm_michael, key, 8);
|
||||
crypto_digest_update(tkey->tfm_michael, sg, 2);
|
||||
crypto_digest_final(tkey->tfm_michael, mic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr)
|
||||
{
|
||||
struct ieee80211_hdr *hdr11;
|
||||
|
||||
hdr11 = (struct ieee80211_hdr *) skb->data;
|
||||
switch (le16_to_cpu(hdr11->frame_ctl) &
|
||||
(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
|
||||
case IEEE80211_FCTL_TODS:
|
||||
memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case IEEE80211_FCTL_FROMDS:
|
||||
memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
|
||||
memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case 0:
|
||||
memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
|
||||
break;
|
||||
}
|
||||
|
||||
hdr[12] = 0; /* priority */
|
||||
hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *tkey = priv;
|
||||
u8 *pos;
|
||||
|
||||
if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
|
||||
printk(KERN_DEBUG "Invalid packet for Michael MIC add "
|
||||
"(tailroom=%d hdr_len=%d skb->len=%d)\n",
|
||||
skb_tailroom(skb), hdr_len, skb->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
michael_mic_hdr(skb, tkey->tx_hdr);
|
||||
pos = skb_put(skb, 8);
|
||||
if (michael_mic(tkey, &tkey->key[16], tkey->tx_hdr,
|
||||
skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if WIRELESS_EXT >= 18
|
||||
static void ieee80211_michael_mic_failure(struct net_device *dev,
|
||||
struct ieee80211_hdr *hdr,
|
||||
int keyidx)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
struct iw_michaelmicfailure ev;
|
||||
|
||||
/* TODO: needed parameters: count, keyid, key type, TSC */
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
|
||||
if (hdr->addr1[0] & 0x01)
|
||||
ev.flags |= IW_MICFAILURE_GROUP;
|
||||
else
|
||||
ev.flags |= IW_MICFAILURE_PAIRWISE;
|
||||
ev.src_addr.sa_family = ARPHRD_ETHER;
|
||||
memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = sizeof(ev);
|
||||
wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *) &ev);
|
||||
}
|
||||
#elif WIRELESS_EXT >= 15
|
||||
static void ieee80211_michael_mic_failure(struct net_device *dev,
|
||||
struct ieee80211_hdr *hdr,
|
||||
int keyidx)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
char buf[128];
|
||||
|
||||
/* TODO: needed parameters: count, keyid, key type, TSC */
|
||||
sprintf(buf, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr="
|
||||
MAC_FMT ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
|
||||
MAC_ARG(hdr->addr2));
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = strlen(buf);
|
||||
wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
|
||||
}
|
||||
#else /* WIRELESS_EXT >= 15 */
|
||||
static inline void ieee80211_michael_mic_failure(struct net_device *dev,
|
||||
struct ieee80211_hdr *hdr,
|
||||
int keyidx)
|
||||
{
|
||||
}
|
||||
#endif /* WIRELESS_EXT >= 15 */
|
||||
|
||||
|
||||
static int ieee80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
|
||||
int hdr_len, void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *tkey = priv;
|
||||
u8 mic[8];
|
||||
|
||||
if (!tkey->key_set)
|
||||
return -1;
|
||||
|
||||
michael_mic_hdr(skb, tkey->rx_hdr);
|
||||
if (michael_mic(tkey, &tkey->key[24], tkey->rx_hdr,
|
||||
skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
|
||||
return -1;
|
||||
if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
|
||||
struct ieee80211_hdr *hdr;
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
printk(KERN_DEBUG "%s: Michael MIC verification failed for "
|
||||
"MSDU from " MAC_FMT " keyidx=%d\n",
|
||||
skb->dev ? skb->dev->name : "N/A", MAC_ARG(hdr->addr2),
|
||||
keyidx);
|
||||
if (skb->dev)
|
||||
ieee80211_michael_mic_failure(skb->dev, hdr, keyidx);
|
||||
tkey->dot11RSNAStatsTKIPLocalMICFailures++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Update TSC counters for RX now that the packet verification has
|
||||
* completed. */
|
||||
tkey->rx_iv32 = tkey->rx_iv32_new;
|
||||
tkey->rx_iv16 = tkey->rx_iv16_new;
|
||||
|
||||
skb_trim(skb, skb->len - 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_tkip_set_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *tkey = priv;
|
||||
int keyidx;
|
||||
struct crypto_tfm *tfm = tkey->tfm_michael;
|
||||
struct crypto_tfm *tfm2 = tkey->tfm_arc4;
|
||||
|
||||
keyidx = tkey->key_idx;
|
||||
memset(tkey, 0, sizeof(*tkey));
|
||||
tkey->key_idx = keyidx;
|
||||
tkey->tfm_michael = tfm;
|
||||
tkey->tfm_arc4 = tfm2;
|
||||
if (len == TKIP_KEY_LEN) {
|
||||
memcpy(tkey->key, key, TKIP_KEY_LEN);
|
||||
tkey->key_set = 1;
|
||||
tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
|
||||
if (seq) {
|
||||
tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
|
||||
(seq[3] << 8) | seq[2];
|
||||
tkey->rx_iv16 = (seq[1] << 8) | seq[0];
|
||||
}
|
||||
} else if (len == 0)
|
||||
tkey->key_set = 0;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_tkip_get_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *tkey = priv;
|
||||
|
||||
if (len < TKIP_KEY_LEN)
|
||||
return -1;
|
||||
|
||||
if (!tkey->key_set)
|
||||
return 0;
|
||||
memcpy(key, tkey->key, TKIP_KEY_LEN);
|
||||
|
||||
if (seq) {
|
||||
/* Return the sequence number of the last transmitted frame. */
|
||||
u16 iv16 = tkey->tx_iv16;
|
||||
u32 iv32 = tkey->tx_iv32;
|
||||
if (iv16 == 0)
|
||||
iv32--;
|
||||
iv16--;
|
||||
seq[0] = tkey->tx_iv16;
|
||||
seq[1] = tkey->tx_iv16 >> 8;
|
||||
seq[2] = tkey->tx_iv32;
|
||||
seq[3] = tkey->tx_iv32 >> 8;
|
||||
seq[4] = tkey->tx_iv32 >> 16;
|
||||
seq[5] = tkey->tx_iv32 >> 24;
|
||||
}
|
||||
|
||||
return TKIP_KEY_LEN;
|
||||
}
|
||||
|
||||
|
||||
static char * ieee80211_tkip_print_stats(char *p, void *priv)
|
||||
{
|
||||
struct ieee80211_tkip_data *tkip = priv;
|
||||
p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
|
||||
"tx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"rx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"replays=%d icv_errors=%d local_mic_failures=%d\n",
|
||||
tkip->key_idx, tkip->key_set,
|
||||
(tkip->tx_iv32 >> 24) & 0xff,
|
||||
(tkip->tx_iv32 >> 16) & 0xff,
|
||||
(tkip->tx_iv32 >> 8) & 0xff,
|
||||
tkip->tx_iv32 & 0xff,
|
||||
(tkip->tx_iv16 >> 8) & 0xff,
|
||||
tkip->tx_iv16 & 0xff,
|
||||
(tkip->rx_iv32 >> 24) & 0xff,
|
||||
(tkip->rx_iv32 >> 16) & 0xff,
|
||||
(tkip->rx_iv32 >> 8) & 0xff,
|
||||
tkip->rx_iv32 & 0xff,
|
||||
(tkip->rx_iv16 >> 8) & 0xff,
|
||||
tkip->rx_iv16 & 0xff,
|
||||
tkip->dot11RSNAStatsTKIPReplays,
|
||||
tkip->dot11RSNAStatsTKIPICVErrors,
|
||||
tkip->dot11RSNAStatsTKIPLocalMICFailures);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static struct ieee80211_crypto_ops ieee80211_crypt_tkip = {
|
||||
.name = "TKIP",
|
||||
.init = ieee80211_tkip_init,
|
||||
.deinit = ieee80211_tkip_deinit,
|
||||
.encrypt_mpdu = ieee80211_tkip_encrypt,
|
||||
.decrypt_mpdu = ieee80211_tkip_decrypt,
|
||||
.encrypt_msdu = ieee80211_michael_mic_add,
|
||||
.decrypt_msdu = ieee80211_michael_mic_verify,
|
||||
.set_key = ieee80211_tkip_set_key,
|
||||
.get_key = ieee80211_tkip_get_key,
|
||||
.print_stats = ieee80211_tkip_print_stats,
|
||||
.extra_prefix_len = 4 + 4, /* IV + ExtIV */
|
||||
.extra_postfix_len = 8 + 4, /* MIC + ICV */
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int __init ieee80211_crypto_tkip_init(void)
|
||||
{
|
||||
return ieee80211_register_crypto_ops(&ieee80211_crypt_tkip);
|
||||
}
|
||||
|
||||
|
||||
static void __exit ieee80211_crypto_tkip_exit(void)
|
||||
{
|
||||
ieee80211_unregister_crypto_ops(&ieee80211_crypt_tkip);
|
||||
}
|
||||
|
||||
|
||||
module_init(ieee80211_crypto_tkip_init);
|
||||
module_exit(ieee80211_crypto_tkip_exit);
|
272
net/ieee80211/ieee80211_crypt_wep.c
Normal file
272
net/ieee80211/ieee80211_crypt_wep.c
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Host AP crypt: host-based WEP encryption implementation for Host AP driver
|
||||
*
|
||||
* Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Host AP crypt: WEP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
struct prism2_wep_data {
|
||||
u32 iv;
|
||||
#define WEP_KEY_LEN 13
|
||||
u8 key[WEP_KEY_LEN + 1];
|
||||
u8 key_len;
|
||||
u8 key_idx;
|
||||
struct crypto_tfm *tfm;
|
||||
};
|
||||
|
||||
|
||||
static void * prism2_wep_init(int keyidx)
|
||||
{
|
||||
struct prism2_wep_data *priv;
|
||||
|
||||
priv = kmalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->key_idx = keyidx;
|
||||
|
||||
priv->tfm = crypto_alloc_tfm("arc4", 0);
|
||||
if (priv->tfm == NULL) {
|
||||
printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate "
|
||||
"crypto API arc4\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* start WEP IV from a random value */
|
||||
get_random_bytes(&priv->iv, 4);
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm)
|
||||
crypto_free_tfm(priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_wep_deinit(void *priv)
|
||||
{
|
||||
struct prism2_wep_data *_priv = priv;
|
||||
if (_priv && _priv->tfm)
|
||||
crypto_free_tfm(_priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
|
||||
/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
|
||||
* for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
|
||||
* so the payload length increases with 8 bytes.
|
||||
*
|
||||
* WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
|
||||
*/
|
||||
static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
u32 crc, klen, len;
|
||||
u8 key[WEP_KEY_LEN + 3];
|
||||
u8 *pos, *icv;
|
||||
struct scatterlist sg;
|
||||
|
||||
if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 ||
|
||||
skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
len = skb->len - hdr_len;
|
||||
pos = skb_push(skb, 4);
|
||||
memmove(pos, pos + 4, hdr_len);
|
||||
pos += hdr_len;
|
||||
|
||||
klen = 3 + wep->key_len;
|
||||
|
||||
wep->iv++;
|
||||
|
||||
/* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
|
||||
* scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
|
||||
* can be used to speedup attacks, so avoid using them. */
|
||||
if ((wep->iv & 0xff00) == 0xff00) {
|
||||
u8 B = (wep->iv >> 16) & 0xff;
|
||||
if (B >= 3 && B < klen)
|
||||
wep->iv += 0x0100;
|
||||
}
|
||||
|
||||
/* Prepend 24-bit IV to RC4 key and TX frame */
|
||||
*pos++ = key[0] = (wep->iv >> 16) & 0xff;
|
||||
*pos++ = key[1] = (wep->iv >> 8) & 0xff;
|
||||
*pos++ = key[2] = wep->iv & 0xff;
|
||||
*pos++ = wep->key_idx << 6;
|
||||
|
||||
/* Copy rest of the WEP key (the secret part) */
|
||||
memcpy(key + 3, wep->key, wep->key_len);
|
||||
|
||||
/* Append little-endian CRC32 and encrypt it to produce ICV */
|
||||
crc = ~crc32_le(~0, pos, len);
|
||||
icv = skb_put(skb, 4);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
|
||||
crypto_cipher_setkey(wep->tfm, key, klen);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = len + 4;
|
||||
crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
|
||||
* the frame: IV (4 bytes), encrypted payload (including SNAP header),
|
||||
* ICV (4 bytes). len includes both IV and ICV.
|
||||
*
|
||||
* Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
|
||||
* failure. If frame is OK, IV and ICV will be removed.
|
||||
*/
|
||||
static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
u32 crc, klen, plen;
|
||||
u8 key[WEP_KEY_LEN + 3];
|
||||
u8 keyidx, *pos, icv[4];
|
||||
struct scatterlist sg;
|
||||
|
||||
if (skb->len < hdr_len + 8)
|
||||
return -1;
|
||||
|
||||
pos = skb->data + hdr_len;
|
||||
key[0] = *pos++;
|
||||
key[1] = *pos++;
|
||||
key[2] = *pos++;
|
||||
keyidx = *pos++ >> 6;
|
||||
if (keyidx != wep->key_idx)
|
||||
return -1;
|
||||
|
||||
klen = 3 + wep->key_len;
|
||||
|
||||
/* Copy rest of the WEP key (the secret part) */
|
||||
memcpy(key + 3, wep->key, wep->key_len);
|
||||
|
||||
/* Apply RC4 to data and compute CRC32 over decrypted data */
|
||||
plen = skb->len - hdr_len - 8;
|
||||
|
||||
crypto_cipher_setkey(wep->tfm, key, klen);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = plen + 4;
|
||||
crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4);
|
||||
|
||||
crc = ~crc32_le(~0, pos, plen);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
if (memcmp(icv, pos + plen, 4) != 0) {
|
||||
/* ICV mismatch - drop frame */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Remove IV and ICV */
|
||||
memmove(skb->data + 4, skb->data, hdr_len);
|
||||
skb_pull(skb, 4);
|
||||
skb_trim(skb, skb->len - 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
|
||||
if (len < 0 || len > WEP_KEY_LEN)
|
||||
return -1;
|
||||
|
||||
memcpy(wep->key, key, len);
|
||||
wep->key_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
|
||||
if (len < wep->key_len)
|
||||
return -1;
|
||||
|
||||
memcpy(key, wep->key, wep->key_len);
|
||||
|
||||
return wep->key_len;
|
||||
}
|
||||
|
||||
|
||||
static char * prism2_wep_print_stats(char *p, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
p += sprintf(p, "key[%d] alg=WEP len=%d\n",
|
||||
wep->key_idx, wep->key_len);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static struct ieee80211_crypto_ops ieee80211_crypt_wep = {
|
||||
.name = "WEP",
|
||||
.init = prism2_wep_init,
|
||||
.deinit = prism2_wep_deinit,
|
||||
.encrypt_mpdu = prism2_wep_encrypt,
|
||||
.decrypt_mpdu = prism2_wep_decrypt,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = prism2_wep_set_key,
|
||||
.get_key = prism2_wep_get_key,
|
||||
.print_stats = prism2_wep_print_stats,
|
||||
.extra_prefix_len = 4, /* IV */
|
||||
.extra_postfix_len = 4, /* ICV */
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int __init ieee80211_crypto_wep_init(void)
|
||||
{
|
||||
return ieee80211_register_crypto_ops(&ieee80211_crypt_wep);
|
||||
}
|
||||
|
||||
|
||||
static void __exit ieee80211_crypto_wep_exit(void)
|
||||
{
|
||||
ieee80211_unregister_crypto_ops(&ieee80211_crypt_wep);
|
||||
}
|
||||
|
||||
|
||||
module_init(ieee80211_crypto_wep_init);
|
||||
module_exit(ieee80211_crypto_wep_exit);
|
299
net/ieee80211/ieee80211_module.c
Normal file
299
net/ieee80211/ieee80211_module.c
Normal file
@ -0,0 +1,299 @@
|
||||
/*******************************************************************************
|
||||
|
||||
Copyright(c) 2004 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
|
||||
<jkmaline@cc.hut.fi>
|
||||
Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.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/config.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/version.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
MODULE_DESCRIPTION("802.11 data/management/control stack");
|
||||
MODULE_AUTHOR("Copyright (C) 2004 Intel Corporation <jketreno@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define DRV_NAME "ieee80211"
|
||||
|
||||
static inline int ieee80211_networks_allocate(struct ieee80211_device *ieee)
|
||||
{
|
||||
if (ieee->networks)
|
||||
return 0;
|
||||
|
||||
ieee->networks = kmalloc(
|
||||
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;
|
||||
}
|
||||
|
||||
memset(ieee->networks, 0,
|
||||
MAX_NETWORK_COUNT * sizeof(struct ieee80211_network));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
|
||||
{
|
||||
if (!ieee->networks)
|
||||
return;
|
||||
kfree(ieee->networks);
|
||||
ieee->networks = NULL;
|
||||
}
|
||||
|
||||
static inline 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);
|
||||
}
|
||||
|
||||
|
||||
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 network device.\n");
|
||||
goto failed;
|
||||
}
|
||||
ieee = netdev_priv(dev);
|
||||
dev->hard_start_xmit = ieee80211_xmit;
|
||||
|
||||
ieee->dev = dev;
|
||||
|
||||
err = ieee80211_networks_allocate(ieee);
|
||||
if (err) {
|
||||
IEEE80211_ERROR("Unable to allocate beacon storage: %d\n",
|
||||
err);
|
||||
goto failed;
|
||||
}
|
||||
ieee80211_networks_initialize(ieee);
|
||||
|
||||
/* Default fragmentation threshold is maximum payload size */
|
||||
ieee->fts = 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->ieee802_1x = 1; /* Default to supporting 802.1x */
|
||||
|
||||
INIT_LIST_HEAD(&ieee->crypt_deinit_list);
|
||||
init_timer(&ieee->crypt_deinit_timer);
|
||||
ieee->crypt_deinit_timer.data = (unsigned long)ieee;
|
||||
ieee->crypt_deinit_timer.function = ieee80211_crypt_deinit_handler;
|
||||
|
||||
spin_lock_init(&ieee->lock);
|
||||
|
||||
ieee->wpa_enabled = 0;
|
||||
ieee->tkip_countermeasures = 0;
|
||||
ieee->drop_unencrypted = 0;
|
||||
ieee->privacy_invoked = 0;
|
||||
ieee->ieee802_1x = 1;
|
||||
|
||||
return dev;
|
||||
|
||||
failed:
|
||||
if (dev)
|
||||
free_netdev(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void free_ieee80211(struct net_device *dev)
|
||||
{
|
||||
struct ieee80211_device *ieee = netdev_priv(dev);
|
||||
|
||||
int i;
|
||||
|
||||
del_timer_sync(&ieee->crypt_deinit_timer);
|
||||
ieee80211_crypt_deinit_entries(ieee, 1);
|
||||
|
||||
for (i = 0; i < WEP_KEYS; i++) {
|
||||
struct ieee80211_crypt_data *crypt = ieee->crypt[i];
|
||||
if (crypt) {
|
||||
if (crypt->ops) {
|
||||
crypt->ops->deinit(crypt->priv);
|
||||
module_put(crypt->ops->owner);
|
||||
}
|
||||
kfree(crypt);
|
||||
ieee->crypt[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ieee80211_networks_free(ieee);
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211_DEBUG
|
||||
|
||||
static int debug = 0;
|
||||
u32 ieee80211_debug_level = 0;
|
||||
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";
|
||||
char *p = (char *)buf;
|
||||
unsigned long val;
|
||||
|
||||
if (count > sizeof(buf) - 1)
|
||||
count = sizeof(buf) - 1;
|
||||
|
||||
if (copy_from_user(buf, buffer, count))
|
||||
return count;
|
||||
buf[count] = 0;
|
||||
/*
|
||||
* what a FPOS... What, sscanf(buf, "%i", &val) would be too
|
||||
* scary?
|
||||
*/
|
||||
if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
|
||||
p++;
|
||||
if (p[0] == 'x' || p[0] == 'X')
|
||||
p++;
|
||||
val = simple_strtoul(p, &p, 16);
|
||||
} else
|
||||
val = simple_strtoul(p, &p, 10);
|
||||
if (p == buf)
|
||||
printk(KERN_INFO DRV_NAME
|
||||
": %s is not in hex or decimal form.\n", buf);
|
||||
else
|
||||
ieee80211_debug_level = val;
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static int __init ieee80211_init(void)
|
||||
{
|
||||
struct proc_dir_entry *e;
|
||||
|
||||
ieee80211_debug_level = debug;
|
||||
ieee80211_proc = create_proc_entry(DRV_NAME, S_IFDIR, 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, proc_net);
|
||||
ieee80211_proc = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
e->read_proc = show_debug_level;
|
||||
e->write_proc = store_debug_level;
|
||||
e->data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ieee80211_exit(void)
|
||||
{
|
||||
if (ieee80211_proc) {
|
||||
remove_proc_entry("debug_level", ieee80211_proc);
|
||||
remove_proc_entry(DRV_NAME, proc_net);
|
||||
ieee80211_proc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
module_param(debug, int, 0444);
|
||||
MODULE_PARM_DESC(debug, "debug output mask");
|
||||
|
||||
|
||||
module_exit(ieee80211_exit);
|
||||
module_init(ieee80211_init);
|
||||
#endif
|
||||
|
||||
|
||||
const char *escape_essid(const char *essid, u8 essid_len) {
|
||||
static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
|
||||
const char *s = essid;
|
||||
char *d = escaped;
|
||||
|
||||
if (ieee80211_is_empty_essid(essid, essid_len)) {
|
||||
memcpy(escaped, "<hidden>", sizeof("<hidden>"));
|
||||
return escaped;
|
||||
}
|
||||
|
||||
essid_len = min(essid_len, (u8)IW_ESSID_MAX_SIZE);
|
||||
while (essid_len--) {
|
||||
if (*s == '\0') {
|
||||
*d++ = '\\';
|
||||
*d++ = '0';
|
||||
s++;
|
||||
} else {
|
||||
*d++ = *s++;
|
||||
}
|
||||
}
|
||||
*d = '\0';
|
||||
return escaped;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(alloc_ieee80211);
|
||||
EXPORT_SYMBOL(free_ieee80211);
|
||||
EXPORT_SYMBOL(escape_essid);
|
1189
net/ieee80211/ieee80211_rx.c
Normal file
1189
net/ieee80211/ieee80211_rx.c
Normal file
File diff suppressed because it is too large
Load Diff
438
net/ieee80211/ieee80211_tx.c
Normal file
438
net/ieee80211/ieee80211_tx.c
Normal file
@ -0,0 +1,438 @@
|
||||
/******************************************************************************
|
||||
|
||||
Copyright(c) 2003 - 2004 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/config.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/version.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 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 `----.----'
|
||||
|
|
||||
.- 'IP Packet' expands, if WEP enabled, to <--'
|
||||
|
|
||||
V
|
||||
,-----------------------.
|
||||
Bytes | 4 | 0-2296 | 4 |
|
||||
|-----|-----------|-----|
|
||||
Desc. | IV | Encrypted | ICV |
|
||||
| | IP Packet | |
|
||||
`-----------------------'
|
||||
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 inline int ieee80211_put_snap(u8 *data, u16 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 == 0x8137 || h_proto == 0x80f3)
|
||||
oui = P802_1H_OUI;
|
||||
else
|
||||
oui = RFC1042_OUI;
|
||||
snap->oui[0] = oui[0];
|
||||
snap->oui[1] = oui[1];
|
||||
snap->oui[2] = oui[2];
|
||||
|
||||
*(u16 *)(data + SNAP_SIZE) = htons(h_proto);
|
||||
|
||||
return SNAP_SIZE + sizeof(u16);
|
||||
}
|
||||
|
||||
static inline int ieee80211_encrypt_fragment(
|
||||
struct ieee80211_device *ieee,
|
||||
struct sk_buff *frag,
|
||||
int hdr_len)
|
||||
{
|
||||
struct ieee80211_crypt_data* crypt = ieee->crypt[ieee->tx_keyidx];
|
||||
int res;
|
||||
|
||||
#ifdef CONFIG_IEEE80211_CRYPT_TKIP
|
||||
struct ieee80211_hdr *header;
|
||||
|
||||
if (ieee->tkip_countermeasures &&
|
||||
crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
|
||||
header = (struct ieee80211_hdr *) frag->data;
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
|
||||
"TX packet to " MAC_FMT "\n",
|
||||
ieee->dev->name, MAC_ARG(header->addr1));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
/* To encrypt, frame format is:
|
||||
* IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
|
||||
|
||||
// PR: FIXME: Copied from hostap. Check fragmentation/MSDU/MPDU encryption.
|
||||
/* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
|
||||
* call both MSDU and MPDU encryption functions from here. */
|
||||
atomic_inc(&crypt->refcnt);
|
||||
res = 0;
|
||||
if (crypt->ops->encrypt_msdu)
|
||||
res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv);
|
||||
if (res == 0 && 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 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);
|
||||
if (unlikely(!txb->fragments[i])) {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unlikely(i != nr_frags)) {
|
||||
while (i >= 0)
|
||||
dev_kfree_skb_any(txb->fragments[i--]);
|
||||
kfree(txb);
|
||||
return NULL;
|
||||
}
|
||||
return txb;
|
||||
}
|
||||
|
||||
/* SKBs are added to the ieee->tx_queue. */
|
||||
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 *frag_hdr;
|
||||
int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size;
|
||||
unsigned long flags;
|
||||
struct net_device_stats *stats = &ieee->stats;
|
||||
int ether_type, encrypt;
|
||||
int bytes, fc, hdr_len;
|
||||
struct sk_buff *skb_frag;
|
||||
struct ieee80211_hdr header = { /* Ensure zero initialized */
|
||||
.duration_id = 0,
|
||||
.seq_ctl = 0
|
||||
};
|
||||
u8 dest[ETH_ALEN], src[ETH_ALEN];
|
||||
|
||||
struct ieee80211_crypt_data* crypt;
|
||||
|
||||
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 = ntohs(((struct ethhdr *)skb->data)->h_proto);
|
||||
|
||||
crypt = ieee->crypt[ieee->tx_keyidx];
|
||||
|
||||
encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) &&
|
||||
ieee->host_encrypt && crypt && crypt->ops;
|
||||
|
||||
if (!encrypt && ieee->ieee802_1x &&
|
||||
ieee->drop_unencrypted && ether_type != ETH_P_PAE) {
|
||||
stats->tx_dropped++;
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* Save source and destination addresses */
|
||||
memcpy(&dest, skb->data, ETH_ALEN);
|
||||
memcpy(&src, skb->data+ETH_ALEN, ETH_ALEN);
|
||||
|
||||
/* 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);
|
||||
|
||||
if (encrypt)
|
||||
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);
|
||||
}
|
||||
header.frame_ctl = cpu_to_le16(fc);
|
||||
hdr_len = IEEE80211_3ADDR_LEN;
|
||||
|
||||
/* 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 - IEEE80211_3ADDR_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 (encrypt)
|
||||
bytes_per_frag -= crypt->ops->extra_prefix_len +
|
||||
crypt->ops->extra_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;
|
||||
|
||||
/* 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, GFP_ATOMIC);
|
||||
if (unlikely(!txb)) {
|
||||
printk(KERN_WARNING "%s: Could not allocate TXB\n",
|
||||
ieee->dev->name);
|
||||
goto failed;
|
||||
}
|
||||
txb->encrypted = encrypt;
|
||||
txb->payload_size = bytes;
|
||||
|
||||
for (i = 0; i < nr_frags; i++) {
|
||||
skb_frag = txb->fragments[i];
|
||||
|
||||
if (encrypt)
|
||||
skb_reserve(skb_frag, crypt->ops->extra_prefix_len);
|
||||
|
||||
frag_hdr = (struct ieee80211_hdr *)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;
|
||||
}
|
||||
|
||||
/* Put a SNAP header on the first fragment */
|
||||
if (i == 0) {
|
||||
ieee80211_put_snap(
|
||||
skb_put(skb_frag, SNAP_SIZE + sizeof(u16)),
|
||||
ether_type);
|
||||
bytes -= SNAP_SIZE + sizeof(u16);
|
||||
}
|
||||
|
||||
memcpy(skb_put(skb_frag, bytes), skb->data, 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 (encrypt)
|
||||
ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
|
||||
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) {
|
||||
if ((*ieee->hard_start_xmit)(txb, dev) == 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);
|
471
net/ieee80211/ieee80211_wx.c
Normal file
471
net/ieee80211/ieee80211_wx.c
Normal file
@ -0,0 +1,471 @@
|
||||
/******************************************************************************
|
||||
|
||||
Copyright(c) 2004 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
|
||||
<jkmaline@cc.hut.fi>
|
||||
Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.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/wireless.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <net/ieee80211.h>
|
||||
static const char *ieee80211_modes[] = {
|
||||
"?", "a", "b", "ab", "g", "ag", "bg", "abg"
|
||||
};
|
||||
|
||||
#define MAX_CUSTOM_LEN 64
|
||||
static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
|
||||
char *start, char *stop,
|
||||
struct ieee80211_network *network)
|
||||
{
|
||||
char custom[MAX_CUSTOM_LEN];
|
||||
char *p;
|
||||
struct iw_event iwe;
|
||||
int i, j;
|
||||
u8 max_rate, 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(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;
|
||||
if (network->flags & NETWORK_EMPTY_ESSID) {
|
||||
iwe.u.data.length = sizeof("<hidden>");
|
||||
start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
|
||||
} else {
|
||||
iwe.u.data.length = min(network->ssid_len, (u8)32);
|
||||
start = iwe_stream_add_point(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(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(start, stop, &iwe,
|
||||
IW_EV_UINT_LEN);
|
||||
}
|
||||
|
||||
/* Add frequency/channel */
|
||||
iwe.cmd = SIOCGIWFREQ;
|
||||
/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
|
||||
iwe.u.freq.e = 3; */
|
||||
iwe.u.freq.m = network->channel;
|
||||
iwe.u.freq.e = 0;
|
||||
iwe.u.freq.i = 0;
|
||||
start = iwe_stream_add_event(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(start, stop, &iwe, network->ssid);
|
||||
|
||||
/* Add basic and extended rates */
|
||||
max_rate = 0;
|
||||
p = custom;
|
||||
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
|
||||
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;
|
||||
if (rate > max_rate)
|
||||
max_rate = rate;
|
||||
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
|
||||
"%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
|
||||
}
|
||||
for (; j < network->rates_ex_len; j++) {
|
||||
rate = network->rates_ex[j] & 0x7F;
|
||||
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
|
||||
"%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
|
||||
if (rate > max_rate)
|
||||
max_rate = rate;
|
||||
}
|
||||
|
||||
iwe.cmd = SIOCGIWRATE;
|
||||
iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
|
||||
iwe.u.bitrate.value = max_rate * 500000;
|
||||
start = iwe_stream_add_event(start, stop, &iwe,
|
||||
IW_EV_PARAM_LEN);
|
||||
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
iwe.u.data.length = p - custom;
|
||||
if (iwe.u.data.length)
|
||||
start = iwe_stream_add_point(start, stop, &iwe, custom);
|
||||
|
||||
/* Add quality statistics */
|
||||
/* TODO: Fix these values... */
|
||||
iwe.cmd = IWEVQUAL;
|
||||
iwe.u.qual.qual = network->stats.signal;
|
||||
iwe.u.qual.level = network->stats.rssi;
|
||||
iwe.u.qual.noise = network->stats.noise;
|
||||
iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
|
||||
if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
|
||||
iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
|
||||
if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
|
||||
iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
|
||||
if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
|
||||
iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
|
||||
|
||||
start = iwe_stream_add_event(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(start, stop, &iwe, custom);
|
||||
|
||||
if (ieee->wpa_enabled && network->wpa_ie_len){
|
||||
char buf[MAX_WPA_IE_LEN * 2 + 30];
|
||||
|
||||
u8 *p = buf;
|
||||
p += sprintf(p, "wpa_ie=");
|
||||
for (i = 0; i < network->wpa_ie_len; i++) {
|
||||
p += sprintf(p, "%02x", network->wpa_ie[i]);
|
||||
}
|
||||
|
||||
memset(&iwe, 0, sizeof(iwe));
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
iwe.u.data.length = strlen(buf);
|
||||
start = iwe_stream_add_point(start, stop, &iwe, buf);
|
||||
}
|
||||
|
||||
if (ieee->wpa_enabled && network->rsn_ie_len){
|
||||
char buf[MAX_WPA_IE_LEN * 2 + 30];
|
||||
|
||||
u8 *p = buf;
|
||||
p += sprintf(p, "rsn_ie=");
|
||||
for (i = 0; i < network->rsn_ie_len; i++) {
|
||||
p += sprintf(p, "%02x", network->rsn_ie[i]);
|
||||
}
|
||||
|
||||
memset(&iwe, 0, sizeof(iwe));
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
iwe.u.data.length = strlen(buf);
|
||||
start = iwe_stream_add_point(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: %lums ago", (jiffies - network->last_scanned) / (HZ / 100));
|
||||
iwe.u.data.length = p - custom;
|
||||
if (iwe.u.data.length)
|
||||
start = iwe_stream_add_point(start, stop, &iwe, custom);
|
||||
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
char *ev = extra;
|
||||
char *stop = ev + IW_SCAN_MAX_DATA;
|
||||
int i = 0;
|
||||
|
||||
IEEE80211_DEBUG_WX("Getting scan\n");
|
||||
|
||||
spin_lock_irqsave(&ieee->lock, flags);
|
||||
|
||||
list_for_each_entry(network, &ieee->network_list, list) {
|
||||
i++;
|
||||
if (ieee->scan_age == 0 ||
|
||||
time_after(network->last_scanned + ieee->scan_age, jiffies))
|
||||
ev = ipw2100_translate_scan(ieee, ev, stop, network);
|
||||
else
|
||||
IEEE80211_DEBUG_SCAN(
|
||||
"Not showing network '%s ("
|
||||
MAC_FMT ")' due to age (%lums).\n",
|
||||
escape_essid(network->ssid,
|
||||
network->ssid_len),
|
||||
MAC_ARG(network->bssid),
|
||||
(jiffies - network->last_scanned) / (HZ / 100));
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
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 ieee80211_crypt_data **crypt;
|
||||
|
||||
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->tx_keyidx;
|
||||
}
|
||||
|
||||
IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
|
||||
"provided" : "default");
|
||||
|
||||
crypt = &ieee->crypt[key];
|
||||
|
||||
if (erq->flags & IW_ENCODE_DISABLED) {
|
||||
if (key_provided && *crypt) {
|
||||
IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
|
||||
key);
|
||||
ieee80211_crypt_delayed_deinit(ieee, 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[i] != NULL) {
|
||||
if (key_provided)
|
||||
break;
|
||||
ieee80211_crypt_delayed_deinit(
|
||||
ieee, &ieee->crypt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == WEP_KEYS) {
|
||||
sec.enabled = 0;
|
||||
sec.level = SEC_LEVEL_0;
|
||||
sec.flags |= SEC_ENABLED | SEC_LEVEL;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sec.enabled = 1;
|
||||
sec.flags |= SEC_ENABLED;
|
||||
|
||||
if (*crypt != NULL && (*crypt)->ops != NULL &&
|
||||
strcmp((*crypt)->ops->name, "WEP") != 0) {
|
||||
/* changing to use WEP; deinit previously used algorithm
|
||||
* on this key */
|
||||
ieee80211_crypt_delayed_deinit(ieee, crypt);
|
||||
}
|
||||
|
||||
if (*crypt == NULL) {
|
||||
struct ieee80211_crypt_data *new_crypt;
|
||||
|
||||
/* take WEP into use */
|
||||
new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
|
||||
GFP_KERNEL);
|
||||
if (new_crypt == NULL)
|
||||
return -ENOMEM;
|
||||
memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
|
||||
new_crypt->ops = ieee80211_get_crypto_ops("WEP");
|
||||
if (!new_crypt->ops) {
|
||||
request_module("ieee80211_crypt_wep");
|
||||
new_crypt->ops = ieee80211_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 ieee80211_crypt_wep\n",
|
||||
dev->name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
*crypt = new_crypt;
|
||||
}
|
||||
|
||||
/* If a new key was provided, set it up */
|
||||
if (erq->length > 0) {
|
||||
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, escape_essid(sec.keys[key], len),
|
||||
erq->length, len);
|
||||
sec.key_sizes[key] = len;
|
||||
(*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
|
||||
* explicitely set */
|
||||
if (key == sec.active_key)
|
||||
sec.flags |= SEC_ACTIVE_KEY;
|
||||
} else {
|
||||
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->tx_keyidx = key;
|
||||
sec.active_key = key;
|
||||
sec.flags |= SEC_ACTIVE_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
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 */
|
||||
|
||||
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 ieee80211_crypt_data *crypt;
|
||||
|
||||
IEEE80211_DEBUG_WX("GET_ENCODE\n");
|
||||
|
||||
key = erq->flags & IW_ENCODE_INDEX;
|
||||
if (key) {
|
||||
if (key > WEP_KEYS)
|
||||
return -EINVAL;
|
||||
key--;
|
||||
} else
|
||||
key = ieee->tx_keyidx;
|
||||
|
||||
crypt = ieee->crypt[key];
|
||||
erq->flags = key + 1;
|
||||
|
||||
if (crypt == NULL || crypt->ops == NULL) {
|
||||
erq->length = 0;
|
||||
erq->flags |= IW_ENCODE_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(crypt->ops->name, "WEP") != 0) {
|
||||
/* only WEP is supported with wireless extensions, so just
|
||||
* report that encryption is used */
|
||||
erq->length = 0;
|
||||
erq->flags |= IW_ENCODE_ENABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
|
||||
erq->length = (len >= 0 ? len : 0);
|
||||
|
||||
erq->flags |= IW_ENCODE_ENABLED;
|
||||
|
||||
if (ieee->open_wep)
|
||||
erq->flags |= IW_ENCODE_OPEN;
|
||||
else
|
||||
erq->flags |= IW_ENCODE_RESTRICTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ieee80211_wx_get_scan);
|
||||
EXPORT_SYMBOL(ieee80211_wx_set_encode);
|
||||
EXPORT_SYMBOL(ieee80211_wx_get_encode);
|
Loading…
Reference in New Issue
Block a user