]> err.no Git - linux-2.6/commitdiff
Merge HEAD from master.kernel.org:/home/rmk/linux-2.6-serial
authorLinus Torvalds <torvalds@evo.osdl.org>
Fri, 2 Sep 2005 07:53:36 +0000 (00:53 -0700)
committerLinus Torvalds <torvalds@evo.osdl.org>
Fri, 2 Sep 2005 07:53:36 +0000 (00:53 -0700)
142 files changed:
Documentation/networking/README.ipw2100 [new file with mode: 0644]
Documentation/networking/README.ipw2200 [new file with mode: 0644]
MAINTAINERS
arch/arm/Kconfig
arch/arm/common/gic.c
arch/arm/kernel/calls.S
arch/arm/kernel/entry-common.S
arch/arm/kernel/sys_arm.c
arch/arm/kernel/time.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-ixp4xx/coyote-pci.c
arch/arm/mach-ixp4xx/coyote-setup.c
arch/arm/mach-ixp4xx/gtwx5715-pci.c
arch/arm/mach-ixp4xx/gtwx5715-setup.c
arch/arm/mach-ixp4xx/ixdp425-pci.c
arch/arm/mach-ixp4xx/ixdp425-setup.c
arch/arm/mach-ixp4xx/ixdpg425-pci.c
arch/arm/mach-pxa/time.c
arch/arm/mach-s3c2410/clock.c
arch/arm/mach-s3c2410/s3c2440-clock.c
arch/arm/mach-sa1100/time.c
arch/arm/mm/alignment.c
arch/arm/mm/mm-armv.c
arch/ia64/Kconfig
arch/ia64/hp/sim/boot/fw-emu.c
arch/ia64/ia32/ia32_signal.c
arch/ia64/kernel/Makefile
arch/ia64/kernel/cpufreq/Kconfig [new file with mode: 0644]
arch/ia64/kernel/cpufreq/Makefile [new file with mode: 0644]
arch/ia64/kernel/cpufreq/acpi-cpufreq.c [new file with mode: 0644]
arch/ia64/kernel/sys_ia64.c
arch/ia64/kernel/uncached.c
arch/ia64/lib/Makefile
arch/ia64/lib/swiotlb.c
arch/ia64/mm/hugetlbpage.c
arch/ia64/pci/pci.c
arch/ia64/sn/include/tio.h
arch/ia64/sn/include/xtalk/hubdev.h
arch/ia64/sn/kernel/bte.c
arch/ia64/sn/kernel/huberror.c
arch/ia64/sn/kernel/io_init.c
arch/ia64/sn/kernel/irq.c
arch/ia64/sn/kernel/setup.c
arch/ia64/sn/kernel/sn2/ptc_deadlock.S
arch/ia64/sn/kernel/sn2/sn2_smp.c
arch/ia64/sn/kernel/sn2/sn_hwperf.c
arch/ia64/sn/kernel/sn2/sn_proc_fs.c
arch/ia64/sn/kernel/sn2/timer_interrupt.c
arch/ia64/sn/pci/Makefile
arch/ia64/sn/pci/pcibr/pcibr_dma.c
arch/ia64/sn/pci/pcibr/pcibr_provider.c
arch/ia64/sn/pci/tioca_provider.c
arch/ia64/sn/pci/tioce_provider.c [new file with mode: 0644]
arch/ppc/kernel/cpu_setup_6xx.S
arch/ppc/kernel/l2cr.S
arch/ppc64/mm/slb_low.S
drivers/char/snsc_event.c
drivers/media/dvb/ttpci/Kconfig
drivers/net/iseries_veth.c
drivers/net/iseries_veth.h [deleted file]
drivers/net/tulip/de2104x.c
drivers/net/tulip/tulip_core.c
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/airo.c
drivers/net/wireless/atmel.c
drivers/net/wireless/hostap/Kconfig [new file with mode: 0644]
drivers/net/wireless/hostap/Makefile [new file with mode: 0644]
drivers/net/wireless/hostap/hostap.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_80211.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_80211_rx.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_80211_tx.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_ap.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_ap.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_common.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_config.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_cs.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_download.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_hw.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_info.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_ioctl.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_pci.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_plx.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_proc.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_wlan.h [new file with mode: 0644]
drivers/net/wireless/ieee802_11.h [deleted file]
drivers/net/wireless/ipw2100.c [new file with mode: 0644]
drivers/net/wireless/ipw2100.h [new file with mode: 0644]
drivers/net/wireless/ipw2200.c [new file with mode: 0644]
drivers/net/wireless/ipw2200.h [new file with mode: 0644]
drivers/net/wireless/orinoco.c
drivers/net/wireless/strip.c
drivers/net/wireless/wavelan_cs.c
drivers/net/wireless/wavelan_cs.h
drivers/net/wireless/wavelan_cs.p.h
drivers/net/wireless/wl3501.h
drivers/net/wireless/wl3501_cs.c
drivers/pci/rom.c
drivers/usb/net/Makefile
drivers/usb/net/zd1201.c
include/asm-arm/arch-ixp4xx/io.h
include/asm-arm/arch-ixp4xx/platform.h
include/asm-arm/arch-pxa/pxa-regs.h
include/asm-arm/arch-s3c2410/regs-clock.h
include/asm-arm/unistd.h
include/asm-ia64/acpi.h
include/asm-ia64/fcntl.h
include/asm-ia64/io.h
include/asm-ia64/mmu.h
include/asm-ia64/mmu_context.h
include/asm-ia64/page.h
include/asm-ia64/pal.h
include/asm-ia64/pgtable.h
include/asm-ia64/rwsem.h
include/asm-ia64/sn/addrs.h
include/asm-ia64/sn/geo.h
include/asm-ia64/sn/intr.h
include/asm-ia64/sn/nodepda.h
include/asm-ia64/sn/pcibus_provider_defs.h
include/asm-ia64/sn/pda.h
include/asm-ia64/sn/sn2/sn_hwperf.h
include/asm-ia64/sn/sn_sal.h
include/asm-ia64/sn/tioce.h [new file with mode: 0644]
include/asm-ia64/sn/tioce_provider.h [new file with mode: 0644]
include/asm-ia64/spinlock.h
include/asm-ia64/system.h
include/linux/etherdevice.h
include/net/ieee80211.h
include/net/ieee80211_crypt.h [new file with mode: 0644]
net/Kconfig
net/Makefile
net/ieee80211/Kconfig [new file with mode: 0644]
net/ieee80211/Makefile [new file with mode: 0644]
net/ieee80211/ieee80211_crypt.c [new file with mode: 0644]
net/ieee80211/ieee80211_crypt_ccmp.c [new file with mode: 0644]
net/ieee80211/ieee80211_crypt_tkip.c [new file with mode: 0644]
net/ieee80211/ieee80211_crypt_wep.c [new file with mode: 0644]
net/ieee80211/ieee80211_module.c [new file with mode: 0644]
net/ieee80211/ieee80211_rx.c [new file with mode: 0644]
net/ieee80211/ieee80211_tx.c [new file with mode: 0644]
net/ieee80211/ieee80211_wx.c [new file with mode: 0644]

diff --git a/Documentation/networking/README.ipw2100 b/Documentation/networking/README.ipw2100
new file mode 100644 (file)
index 0000000..2046948
--- /dev/null
@@ -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
+
diff --git a/Documentation/networking/README.ipw2200 b/Documentation/networking/README.ipw2200
new file mode 100644 (file)
index 0000000..6916080
--- /dev/null
@@ -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
+
+
index e8214fe53a5e919feb0e640b189d3b6126a991e3..671e32905d28629728b7e600eb5208cc040fa074 100644 (file)
@@ -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
index 4bf0e8737e1fd514477ec4009ba4e908f189b9a9..68dfdba71d74126512efcd4996282ffce11b63a4 100644 (file)
@@ -365,8 +365,8 @@ config NO_IDLE_HZ
 
          Please note that dynamic tick may affect the accuracy of
          timekeeping on some platforms depending on the implementation.
-         Currently at least OMAP platform is known to have accurate
-         timekeeping with dynamic tick.
+         Currently at least OMAP, PXA2xx and SA11x0 platforms are known
+         to have accurate timekeeping with dynamic tick.
 
 config ARCH_DISCONTIGMEM_ENABLE
        bool
index 51dbf5489b6b31fb5a0cd515c9e4839e7bfb9e50..d7499071755976a0a14b7b0c26eb8db62555e7b9 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/smp.h>
+#include <linux/cpumask.h>
 
 #include <asm/irq.h>
 #include <asm/io.h>
index 2b6b4c786e654c125cfa62b750337bc5894b9731..db07ce42b3b2c6873f9b767389ae766d0f89ad6f 100644 (file)
@@ -284,7 +284,7 @@ __syscall_start:
                .long   sys_fstatfs64
                .long   sys_tgkill
                .long   sys_utimes
-/* 270 */      .long   sys_fadvise64_64
+/* 270 */      .long   sys_arm_fadvise64_64_wrapper
                .long   sys_pciconfig_iobase
                .long   sys_pciconfig_read
                .long   sys_pciconfig_write
index 3f8d0e3aefabf71c6bf82783024a72c39988974f..6281d488ac975d225aa10f536b5849db1b528956 100644 (file)
@@ -265,6 +265,10 @@ sys_futex_wrapper:
                str     r5, [sp, #4]            @ push sixth arg
                b       sys_futex
 
+sys_arm_fadvise64_64_wrapper:
+               str     r5, [sp, #4]            @ push r5 to stack
+               b       sys_arm_fadvise64_64
+
 /*
  * Note: off_4k (r5) is always units of 4K.  If we can't do the requested
  * offset, we return EINVAL.
index f897ce2ccf0d358cc79a6493b23d1cf33d91a05a..42629ff84f5a8864787c1d862f001e47a3000765 100644 (file)
@@ -311,3 +311,13 @@ long execve(const char *filename, char **argv, char **envp)
        return ret;
 }
 EXPORT_SYMBOL(execve);
+
+/*
+ * Since loff_t is a 64 bit type we avoid a lot of ABI hastle
+ * with a different argument ordering.
+ */
+asmlinkage long sys_arm_fadvise64_64(int fd, int advice,
+                                    loff_t offset, loff_t len)
+{
+       return sys_fadvise64_64(fd, offset, len, advice);
+}
index 1b7fcd50c3e25c8a208bc3347dc55dc7d0605e1e..8880482dcbffd4dbbfb20ea98def232de3b439d1 100644 (file)
@@ -433,10 +433,12 @@ void timer_dyn_reprogram(void)
 {
        struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
 
-       write_seqlock(&xtime_lock);
-       if (dyn_tick->state & DYN_TICK_ENABLED)
-               dyn_tick->reprogram(next_timer_interrupt() - jiffies);
-       write_sequnlock(&xtime_lock);
+       if (dyn_tick) {
+               write_seqlock(&xtime_lock);
+               if (dyn_tick->state & DYN_TICK_ENABLED)
+                       dyn_tick->reprogram(next_timer_interrupt() - jiffies);
+               write_sequnlock(&xtime_lock);
+       }
 }
 
 static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf)
index 04490a9f8f6ecfc2158d8b21ec3aa096b888e1f2..0422e906cc9a64e4ff11d43e866049a72ba52cd0 100644 (file)
 #include <asm/mach/irq.h>
 #include <asm/mach/time.h>
 
-enum ixp4xx_irq_type {
-       IXP4XX_IRQ_LEVEL, IXP4XX_IRQ_EDGE
-};
-static void ixp4xx_config_irq(unsigned irq, enum ixp4xx_irq_type type);
-
-/*************************************************************************
- * GPIO acces functions
- *************************************************************************/
-
-/*
- * Configure GPIO line for input, interrupt, or output operation
- *
- * TODO: Enable/disable the irq_desc based on interrupt or output mode.
- * TODO: Should these be named ixp4xx_gpio_?
- */
-void gpio_line_config(u8 line, u32 style)
-{
-       static const int gpio2irq[] = {
-               6, 7, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
-       };
-       u32 enable;
-       volatile u32 *int_reg;
-       u32 int_style;
-       enum ixp4xx_irq_type irq_type;
-
-       enable = *IXP4XX_GPIO_GPOER;
-
-       if (style & IXP4XX_GPIO_OUT) {
-               enable &= ~((1) << line);
-       } else if (style & IXP4XX_GPIO_IN) {
-               enable |= ((1) << line);
-
-               switch (style & IXP4XX_GPIO_INTSTYLE_MASK)
-               {
-               case (IXP4XX_GPIO_ACTIVE_HIGH):
-                       int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH;
-                       irq_type = IXP4XX_IRQ_LEVEL;
-                       break;
-               case (IXP4XX_GPIO_ACTIVE_LOW):
-                       int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW;
-                       irq_type = IXP4XX_IRQ_LEVEL;
-                       break;
-               case (IXP4XX_GPIO_RISING_EDGE):
-                       int_style = IXP4XX_GPIO_STYLE_RISING_EDGE;
-                       irq_type = IXP4XX_IRQ_EDGE;
-                       break;
-               case (IXP4XX_GPIO_FALLING_EDGE):
-                       int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE;
-                       irq_type = IXP4XX_IRQ_EDGE;
-                       break;
-               case (IXP4XX_GPIO_TRANSITIONAL):
-                       int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL;
-                       irq_type = IXP4XX_IRQ_EDGE;
-                       break;
-               default:
-                       int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH;
-                       irq_type = IXP4XX_IRQ_LEVEL;
-                       break;
-               }
-
-               if (style & IXP4XX_GPIO_INTSTYLE_MASK)
-                       ixp4xx_config_irq(gpio2irq[line], irq_type);
-
-               if (line >= 8) {        /* pins 8-15 */ 
-                       line -= 8;
-                       int_reg = IXP4XX_GPIO_GPIT2R;
-               }
-               else {                  /* pins 0-7 */
-                       int_reg = IXP4XX_GPIO_GPIT1R;
-               }
-
-               /* Clear the style for the appropriate pin */
-               *int_reg &= ~(IXP4XX_GPIO_STYLE_CLEAR << 
-                               (line * IXP4XX_GPIO_STYLE_SIZE));
-
-               /* Set the new style */
-               *int_reg |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE));
-       }
-
-       *IXP4XX_GPIO_GPOER = enable;
-}
-
-EXPORT_SYMBOL(gpio_line_config);
-
 /*************************************************************************
  * IXP4xx chipset I/O mapping
  *************************************************************************/
@@ -165,6 +81,69 @@ void __init ixp4xx_map_io(void)
  *       (be it PCI or something else) configures that GPIO line
  *       as an IRQ.
  **************************************************************************/
+enum ixp4xx_irq_type {
+       IXP4XX_IRQ_LEVEL, IXP4XX_IRQ_EDGE
+};
+
+static void ixp4xx_config_irq(unsigned irq, enum ixp4xx_irq_type type);
+
+/*
+ * IRQ -> GPIO mapping table
+ */
+static int irq2gpio[32] = {
+       -1, -1, -1, -1, -1, -1,  0,  1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1,  2,  3,  4,  5,  6,
+        7,  8,  9, 10, 11, 12, -1, -1,
+};
+
+static int ixp4xx_set_irq_type(unsigned int irq, unsigned int type)
+{
+       int line = irq2gpio[irq];
+       u32 int_style;
+       enum ixp4xx_irq_type irq_type;
+       volatile u32 *int_reg;
+
+       /*
+        * Only for GPIO IRQs
+        */
+       if (line < 0)
+               return -EINVAL;
+
+       if (type & IRQT_BOTHEDGE) {
+               int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL;
+               irq_type = IXP4XX_IRQ_EDGE;
+       } else  if (type & IRQT_RISING) {
+               int_style = IXP4XX_GPIO_STYLE_RISING_EDGE;
+               irq_type = IXP4XX_IRQ_EDGE;
+       } else if (type & IRQT_FALLING) {
+               int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE;
+               irq_type = IXP4XX_IRQ_EDGE;
+       } else if (type & IRQT_HIGH) {
+               int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH;
+               irq_type = IXP4XX_IRQ_LEVEL;
+       } else if (type & IRQT_LOW) {
+               int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW;
+               irq_type = IXP4XX_IRQ_LEVEL;
+       }
+
+       ixp4xx_config_irq(irq, irq_type);
+
+       if (line >= 8) {        /* pins 8-15 */
+               line -= 8;
+               int_reg = IXP4XX_GPIO_GPIT2R;
+       } else {                /* pins 0-7 */
+               int_reg = IXP4XX_GPIO_GPIT1R;
+       }
+
+       /* Clear the style for the appropriate pin */
+       *int_reg &= ~(IXP4XX_GPIO_STYLE_CLEAR <<
+                       (line * IXP4XX_GPIO_STYLE_SIZE));
+
+       /* Set the new style */
+       *int_reg |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE));
+}
+
 static void ixp4xx_irq_mask(unsigned int irq)
 {
        if (cpu_is_ixp46x() && irq >= 32)
@@ -183,12 +162,6 @@ static void ixp4xx_irq_unmask(unsigned int irq)
 
 static void ixp4xx_irq_ack(unsigned int irq)
 {
-       static int irq2gpio[32] = {
-               -1, -1, -1, -1, -1, -1,  0,  1,
-               -1, -1, -1, -1, -1, -1, -1, -1,
-               -1, -1, -1,  2,  3,  4,  5,  6,
-                7,  8,  9, 10, 11, 12, -1, -1,
-       };
        int line = (irq < 32) ? irq2gpio[irq] : -1;
 
        if (line >= 0)
@@ -209,12 +182,14 @@ static struct irqchip ixp4xx_irq_level_chip = {
        .ack    = ixp4xx_irq_mask,
        .mask   = ixp4xx_irq_mask,
        .unmask = ixp4xx_irq_level_unmask,
+       .type   = ixp4xx_set_irq_type
 };
 
 static struct irqchip ixp4xx_irq_edge_chip = {
        .ack    = ixp4xx_irq_ack,
        .mask   = ixp4xx_irq_mask,
        .unmask = ixp4xx_irq_unmask,
+       .type   = ixp4xx_set_irq_type
 };
 
 static void ixp4xx_config_irq(unsigned irq, enum ixp4xx_irq_type type)
index afafb42ae12951c8a96bc615328bee61a5565b07..60de8a94cff5f828cf93840d0d80cff53cc395da 100644 (file)
@@ -30,11 +30,8 @@ extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys);
 
 void __init coyote_pci_preinit(void)
 {
-       gpio_line_config(COYOTE_PCI_SLOT0_PIN,
-                       IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
-
-       gpio_line_config(COYOTE_PCI_SLOT1_PIN,
-                       IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+       set_irq_type(IRQ_COYOTE_PCI_SLOT0, IRQT_LOW);
+       set_irq_type(IRQ_COYOTE_PCI_SLOT1, IRQT_LOW);
 
        gpio_line_isr_clear(COYOTE_PCI_SLOT0_PIN);
        gpio_line_isr_clear(COYOTE_PCI_SLOT1_PIN);
index 411ea999619055a7b0e73bd3678ecd9ad8806989..8b2f25322452b2c449ca02296128075e4a09918e 100644 (file)
 #include <asm/mach/arch.h>
 #include <asm/mach/flash.h>
 
-void __init coyote_map_io(void)
-{
-       ixp4xx_map_io();
-}
-
 static struct flash_platform_data coyote_flash_data = {
        .map_name       = "cfi_probe",
        .width          = 2,
@@ -107,7 +102,7 @@ MACHINE_START(ADI_COYOTE, "ADI Engineering Coyote")
        .phys_ram       = PHYS_OFFSET,
        .phys_io        = IXP4XX_PERIPHERAL_BASE_PHYS,
        .io_pg_offst    = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc,
-       .map_io         = coyote_map_io,
+       .map_io         = ixp4xx_map_io,
        .init_irq       = ixp4xx_init_irq,
        .timer          = &ixp4xx_timer,
        .boot_params    = 0x0100,
@@ -125,7 +120,7 @@ MACHINE_START(IXDPG425, "Intel IXDPG425")
        .phys_ram       = PHYS_OFFSET,
        .phys_io        = IXP4XX_PERIPHERAL_BASE_PHYS,
        .io_pg_offst    = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc,
-       .map_io         = coyote_map_io,
+       .map_io         = ixp4xx_map_io,
        .init_irq       = ixp4xx_init_irq,
        .timer          = &ixp4xx_timer,
        .boot_params    = 0x0100,
index b18035824e3e386c59a38b2c5d6e6db321fe6258..a66484b63d36b12b0dccd06770b3b3a9d2931a7a 100644 (file)
@@ -35,26 +35,20 @@ extern void ixp4xx_pci_preinit(void);
 extern int ixp4xx_setup(int nr, struct pci_sys_data *sys);
 extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys);
 
-        /*
-        * The exact GPIO pins and IRQs are defined in arch-ixp4xx/gtwx5715.h
-        * Slot 0 isn't actually populated with a card connector but
-        * we initialize it anyway in case a future version has the
-        * slot populated or someone with good soldering skills has
-        * some free time.
-        */
-
-
-static void gtwx5715_init_gpio(u8 pin, u32 style)
-{
-       gpio_line_config(pin, style | IXP4XX_GPIO_ACTIVE_LOW);
-
-       if (style & IXP4XX_GPIO_IN) gpio_line_isr_clear(pin);
-}
 
+/*
+ * The exact GPIO pins and IRQs are defined in arch-ixp4xx/gtwx5715.h
+ * Slot 0 isn't actually populated with a card connector but
+ * we initialize it anyway in case a future version has the
+ * slot populated or someone with good soldering skills has
+ * some free time.
+ */
 void __init gtwx5715_pci_preinit(void)
 {
-       gtwx5715_init_gpio(GTWX5715_PCI_SLOT0_INTA_GPIO,        IXP4XX_GPIO_IN);
-       gtwx5715_init_gpio(GTWX5715_PCI_SLOT1_INTA_GPIO,        IXP4XX_GPIO_IN);
+       set_irq_type(GTWX5715_PCI_SLOT0_INTA_IRQ, IRQT_LOW);
+       set_irq_type(GTWX5715_PCI_SLOT0_INTB_IRQ, IRQT_LOW);
+       set_irq_type(GTWX5715_PCI_SLOT1_INTA_IRQ, IRQT_LOW);
+       set_irq_type(GTWX5715_PCI_SLOT1_INTB_IRQ, IRQT_LOW);
 
        ixp4xx_pci_preinit();
 }
index 333459d6aa464bb5394fd46783e9b6690b6ed4aa..3fd92c5cbaa83e21566595aab80090a93dcdd192 100644 (file)
@@ -101,12 +101,6 @@ static struct platform_device gtwx5715_uart_device = {
        .resource       = gtwx5715_uart_resources,
 };
 
-
-void __init gtwx5715_map_io(void)
-{
-       ixp4xx_map_io();
-}
-
 static struct flash_platform_data gtwx5715_flash_data = {
        .map_name       = "cfi_probe",
        .width          = 2,
@@ -144,7 +138,7 @@ MACHINE_START(GTWX5715, "Gemtek GTWX5715 (Linksys WRV54G)")
        .phys_ram       = PHYS_OFFSET,
        .phys_io        = IXP4XX_UART2_BASE_PHYS,
        .io_pg_offst    = ((IXP4XX_UART2_BASE_VIRT) >> 18) & 0xfffc,
-       .map_io         = gtwx5715_map_io,
+       .map_io         = ixp4xx_map_io,
        .init_irq       = ixp4xx_init_irq,
        .timer          = &ixp4xx_timer,
        .boot_params    = 0x0100,
index c2ab9ebb5980cac5d68f1e57f68973461b36e1fa..f9a1d3e7d6922d30c6226690d06162facec746b2 100644 (file)
 
 void __init ixdp425_pci_preinit(void)
 {
-       gpio_line_config(IXDP425_PCI_INTA_PIN,
-                               IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
-       gpio_line_config(IXDP425_PCI_INTB_PIN, 
-                               IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
-       gpio_line_config(IXDP425_PCI_INTC_PIN, 
-                               IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
-       gpio_line_config(IXDP425_PCI_INTD_PIN, 
-                               IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+       set_irq_type(IRQ_IXDP425_PCI_INTA, IRQT_LOW);
+       set_irq_type(IRQ_IXDP425_PCI_INTB, IRQT_LOW);
+       set_irq_type(IRQ_IXDP425_PCI_INTC, IRQT_LOW);
+       set_irq_type(IRQ_IXDP425_PCI_INTD, IRQT_LOW);
 
        gpio_line_isr_clear(IXDP425_PCI_INTA_PIN);
        gpio_line_isr_clear(IXDP425_PCI_INTB_PIN);
index fa0646c8693b096c7c1ee6b6a16dc3074bff2679..6c14ff3c23a04706df50f9f8ad38e5179994e21b 100644 (file)
 #include <asm/mach/arch.h>
 #include <asm/mach/flash.h>
 
-void __init ixdp425_map_io(void) 
-{
-       ixp4xx_map_io();
-}
-
 static struct flash_platform_data ixdp425_flash_data = {
        .map_name       = "cfi_probe",
        .width          = 2,
@@ -133,7 +128,7 @@ MACHINE_START(IXDP425, "Intel IXDP425 Development Platform")
        .phys_ram       = PHYS_OFFSET,
        .phys_io        = IXP4XX_PERIPHERAL_BASE_PHYS,
        .io_pg_offst    = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc,
-       .map_io         = ixdp425_map_io,
+       .map_io         = ixp4xx_map_io,
        .init_irq       = ixp4xx_init_irq,
        .timer          = &ixp4xx_timer,
        .boot_params    = 0x0100,
@@ -145,7 +140,7 @@ MACHINE_START(IXDP465, "Intel IXDP465 Development Platform")
        .phys_ram       = PHYS_OFFSET,
        .phys_io        = IXP4XX_PERIPHERAL_BASE_PHYS,
        .io_pg_offst    = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc,
-       .map_io         = ixdp425_map_io,
+       .map_io         = ixp4xx_map_io,
        .init_irq       = ixp4xx_init_irq,
        .timer          = &ixp4xx_timer,
        .boot_params    = 0x0100,
@@ -157,7 +152,7 @@ MACHINE_START(IXCDP1100, "Intel IXCDP1100 Development Platform")
        .phys_ram       = PHYS_OFFSET,
        .phys_io        = IXP4XX_PERIPHERAL_BASE_PHYS,
        .io_pg_offst    = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc,
-       .map_io         = ixdp425_map_io,
+       .map_io         = ixp4xx_map_io,
        .init_irq       = ixp4xx_init_irq,
        .timer          = &ixp4xx_timer,
        .boot_params    = 0x0100,
@@ -176,7 +171,7 @@ MACHINE_START(AVILA, "Gateworks Avila Network Platform")
        .phys_ram       = PHYS_OFFSET,
        .phys_io        = IXP4XX_PERIPHERAL_BASE_PHYS,
        .io_pg_offst    = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc,
-       .map_io         = ixdp425_map_io,
+       .map_io         = ixp4xx_map_io,
        .init_irq       = ixp4xx_init_irq,
        .timer          = &ixp4xx_timer,
        .boot_params    = 0x0100,
index ce4563f006766b411a8758005b782c8752faed2c..fe5e7660de1d5e855594b45a6fdaf7cd4c152655 100644 (file)
@@ -29,8 +29,8 @@ extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys);
 
 void __init ixdpg425_pci_preinit(void)
 {
-       gpio_line_config(6, IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
-       gpio_line_config(7, IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+       set_irq_type(IRQ_IXP4XX_GPIO6, IRQT_LOW);
+       set_irq_type(IRQ_IXP4XX_GPIO7, IRQT_LOW);
 
        gpio_line_isr_clear(6);
        gpio_line_isr_clear(7);
index 6e5202154f911321a82e53e0b93d716726b57d5e..7dad3f1465e076028954b1f18169ed8b82b2502e 100644 (file)
@@ -70,6 +70,11 @@ static unsigned long pxa_gettimeoffset (void)
        return usec;
 }
 
+#ifdef CONFIG_NO_IDLE_HZ
+static unsigned long initial_match;
+static int match_posponed;
+#endif
+
 static irqreturn_t
 pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -77,11 +82,19 @@ pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
        write_seqlock(&xtime_lock);
 
+#ifdef CONFIG_NO_IDLE_HZ
+       if (match_posponed) {
+               match_posponed = 0;
+               OSMR0 = initial_match;
+       }
+#endif
+
        /* Loop until we get ahead of the free running timer.
         * This ensures an exact clock tick count and time accuracy.
-        * IRQs are disabled inside the loop to ensure coherence between
-        * lost_ticks (updated in do_timer()) and the match reg value, so we
-        * can use do_gettimeofday() from interrupt handlers.
+        * Since IRQs are disabled at this point, coherence between
+        * lost_ticks(updated in do_timer()) and the match reg value is
+        * ensured, hence we can use do_gettimeofday() from interrupt
+        * handlers.
         *
         * HACK ALERT: it seems that the PXA timer regs aren't updated right
         * away in all cases when a write occurs.  We therefore compare with
@@ -126,6 +139,42 @@ static void __init pxa_timer_init(void)
        OSCR = 0;               /* initialize free-running timer, force first match */
 }
 
+#ifdef CONFIG_NO_IDLE_HZ
+static int pxa_dyn_tick_enable_disable(void)
+{
+       /* nothing to do */
+       return 0;
+}
+
+static void pxa_dyn_tick_reprogram(unsigned long ticks)
+{
+       if (ticks > 1) {
+               initial_match = OSMR0;
+               OSMR0 = initial_match + ticks * LATCH;
+               match_posponed = 1;
+       }
+}
+
+static irqreturn_t
+pxa_dyn_tick_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       if (match_posponed) {
+               match_posponed = 0;
+               OSMR0 = initial_match;
+               if ( (signed long)(initial_match - OSCR) <= 8 )
+                       return pxa_timer_interrupt(irq, dev_id, regs);
+       }
+       return IRQ_NONE;
+}
+
+static struct dyn_tick_timer pxa_dyn_tick = {
+       .enable         = pxa_dyn_tick_enable_disable,
+       .disable        = pxa_dyn_tick_enable_disable,
+       .reprogram      = pxa_dyn_tick_reprogram,
+       .handler        = pxa_dyn_tick_handler,
+};
+#endif
+
 #ifdef CONFIG_PM
 static unsigned long osmr[4], oier;
 
@@ -161,4 +210,7 @@ struct sys_timer pxa_timer = {
        .suspend        = pxa_timer_suspend,
        .resume         = pxa_timer_resume,
        .offset         = pxa_gettimeoffset,
+#ifdef CONFIG_NO_IDLE_HZ
+       .dyn_tick       = &pxa_dyn_tick,
+#endif
 };
index 9a66050e887d0e99db09d53d1ab91bda120f6353..f59608268751f6769a920c49b73b75fa8ab1fce4 100644 (file)
@@ -388,6 +388,7 @@ int __init s3c24xx_setup_clocks(unsigned long xtal,
                                unsigned long hclk,
                                unsigned long pclk)
 {
+       unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
        struct clk *clkp = init_clocks;
        int ptr;
        int ret;
@@ -446,5 +447,13 @@ int __init s3c24xx_setup_clocks(unsigned long xtal,
                }
        }
 
+       /* show the clock-slow value */
+
+       printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
+              print_mhz(xtal / ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
+              (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
+              (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
+              (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
+
        return 0;
 }
index b018a1f680cef41c83b461f1a0b28c4203b91dc0..c67e0979aec38476635be1f2a9e990079c4c6082 100644 (file)
@@ -68,6 +68,7 @@ static struct clk s3c2440_clk_ac97 = {
 static int s3c2440_clk_add(struct sys_device *sysdev)
 {
        unsigned long upllcon = __raw_readl(S3C2410_UPLLCON);
+       unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN);
        struct clk *clk_h;
        struct clk *clk_p;
        struct clk *clk_xtal;
@@ -80,8 +81,9 @@ static int s3c2440_clk_add(struct sys_device *sysdev)
 
        s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal->rate);
 
-       printk("S3C2440: Clock Support, UPLL %ld.%03ld MHz\n",
-              print_mhz(s3c2440_clk_upll.rate));
+       printk("S3C2440: Clock Support, UPLL %ld.%03ld MHz, DVS %s\n",
+              print_mhz(s3c2440_clk_upll.rate),
+              (camdivn & S3C2440_CAMDIVN_DVSEN) ? "on" : "off");
 
        clk_p = clk_get(NULL, "pclk");
        clk_h = clk_get(NULL, "hclk");
index 0eeb3616ffea739652f8221504da7a1548374171..47e0420623fc25083a486f74887bff522260108b 100644 (file)
@@ -70,15 +70,11 @@ static unsigned long sa1100_gettimeoffset (void)
        return usec;
 }
 
-/*
- * We will be entered with IRQs enabled.
- *
- * Loop until we get ahead of the free running timer.
- * This ensures an exact clock tick count and time accuracy.
- * IRQs are disabled inside the loop to ensure coherence between
- * lost_ticks (updated in do_timer()) and the match reg value, so we
- * can use do_gettimeofday() from interrupt handlers.
- */
+#ifdef CONFIG_NO_IDLE_HZ
+static unsigned long initial_match;
+static int match_posponed;
+#endif
+
 static irqreturn_t
 sa1100_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -86,6 +82,21 @@ sa1100_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
        write_seqlock(&xtime_lock);
 
+#ifdef CONFIG_NO_IDLE_HZ
+       if (match_posponed) {
+               match_posponed = 0;
+               OSMR0 = initial_match;
+       }
+#endif
+
+       /*
+        * Loop until we get ahead of the free running timer.
+        * This ensures an exact clock tick count and time accuracy.
+        * Since IRQs are disabled at this point, coherence between
+        * lost_ticks(updated in do_timer()) and the match reg value is
+        * ensured, hence we can use do_gettimeofday() from interrupt
+        * handlers.
+        */
        do {
                timer_tick(regs);
                OSSR = OSSR_M0;  /* Clear match on timer 0 */
@@ -120,6 +131,42 @@ static void __init sa1100_timer_init(void)
        OSCR = 0;               /* initialize free-running timer, force first match */
 }
 
+#ifdef CONFIG_NO_IDLE_HZ
+static int sa1100_dyn_tick_enable_disable(void)
+{
+       /* nothing to do */
+       return 0;
+}
+
+static void sa1100_dyn_tick_reprogram(unsigned long ticks)
+{
+       if (ticks > 1) {
+               initial_match = OSMR0;
+               OSMR0 = initial_match + ticks * LATCH;
+               match_posponed = 1;
+       }
+}
+
+static irqreturn_t
+sa1100_dyn_tick_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       if (match_posponed) {
+               match_posponed = 0;
+               OSMR0 = initial_match;
+               if ((signed long)(initial_match - OSCR) <= 0)
+                       return sa1100_timer_interrupt(irq, dev_id, regs);
+       }
+       return IRQ_NONE;
+}
+
+static struct dyn_tick_timer sa1100_dyn_tick = {
+       .enable         = sa1100_dyn_tick_enable_disable,
+       .disable        = sa1100_dyn_tick_enable_disable,
+       .reprogram      = sa1100_dyn_tick_reprogram,
+       .handler        = sa1100_dyn_tick_handler,
+};
+#endif
+
 #ifdef CONFIG_PM
 unsigned long osmr[4], oier;
 
@@ -156,4 +203,7 @@ struct sys_timer sa1100_timer = {
        .suspend        = sa1100_timer_suspend,
        .resume         = sa1100_timer_resume,
        .offset         = sa1100_gettimeoffset,
+#ifdef CONFIG_NO_IDLE_HZ
+       .dyn_tick       = &sa1100_dyn_tick,
+#endif
 };
index 81f4a8a2d34b2c7af8c93074ca8f0c443fb80bc2..4b39d867ac14ef5e4ced59cd5ba3c6157e6427de 100644 (file)
@@ -45,7 +45,7 @@
 
 #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0)
 
-#define LDSTH_I_BIT(i) (i & (1 << 22))         /* half-word immed      */
+#define LDSTHD_I_BIT(i)        (i & (1 << 22))         /* double/half-word immed */
 #define LDM_S_BIT(i)   (i & (1 << 22))         /* write CPSR from SPSR */
 
 #define RN_BITS(i)     ((i >> 16) & 15)        /* Rn                   */
@@ -68,6 +68,7 @@ static unsigned long ai_sys;
 static unsigned long ai_skipped;
 static unsigned long ai_half;
 static unsigned long ai_word;
+static unsigned long ai_dword;
 static unsigned long ai_multi;
 static int ai_usermode;
 
@@ -93,6 +94,8 @@ proc_alignment_read(char *page, char **start, off_t off, int count, int *eof,
        p += sprintf(p, "Skipped:\t%lu\n", ai_skipped);
        p += sprintf(p, "Half:\t\t%lu\n", ai_half);
        p += sprintf(p, "Word:\t\t%lu\n", ai_word);
+       if (cpu_architecture() >= CPU_ARCH_ARMv5TE)
+               p += sprintf(p, "DWord:\t\t%lu\n", ai_dword);
        p += sprintf(p, "Multi:\t\t%lu\n", ai_multi);
        p += sprintf(p, "User faults:\t%i (%s)\n", ai_usermode,
                        usermode_action[ai_usermode]);
@@ -283,12 +286,6 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
 {
        unsigned int rd = RD_BITS(instr);
 
-       if ((instr & 0x01f00ff0) == 0x01000090)
-               goto swp;
-
-       if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0)
-               goto bad;
-
        ai_half += 1;
 
        if (user_mode(regs))
@@ -323,10 +320,47 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
 
        return TYPE_LDST;
 
- swp:
-       printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
- bad:
-       return TYPE_ERROR;
+ fault:
+       return TYPE_FAULT;
+}
+
+static int
+do_alignment_ldrdstrd(unsigned long addr, unsigned long instr,
+                     struct pt_regs *regs)
+{
+       unsigned int rd = RD_BITS(instr);
+
+       ai_dword += 1;
+
+       if (user_mode(regs))
+               goto user;
+
+       if ((instr & 0xf0) == 0xd0) {
+               unsigned long val;
+               get32_unaligned_check(val, addr);
+               regs->uregs[rd] = val;
+               get32_unaligned_check(val, addr+4);
+               regs->uregs[rd+1] = val;
+       } else {
+               put32_unaligned_check(regs->uregs[rd], addr);
+               put32_unaligned_check(regs->uregs[rd+1], addr+4);
+       }
+
+       return TYPE_LDST;
+
+ user:
+       if ((instr & 0xf0) == 0xd0) {
+               unsigned long val;
+               get32t_unaligned_check(val, addr);
+               regs->uregs[rd] = val;
+               get32t_unaligned_check(val, addr+4);
+               regs->uregs[rd+1] = val;
+       } else {
+               put32t_unaligned_check(regs->uregs[rd], addr);
+               put32t_unaligned_check(regs->uregs[rd+1], addr+4);
+       }
+
+       return TYPE_LDST;
 
  fault:
        return TYPE_FAULT;
@@ -617,12 +651,20 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        regs->ARM_pc += thumb_mode(regs) ? 2 : 4;
 
        switch (CODING_BITS(instr)) {
-       case 0x00000000:        /* ldrh or strh */
-               if (LDSTH_I_BIT(instr))
+       case 0x00000000:        /* 3.13.4 load/store instruction extensions */
+               if (LDSTHD_I_BIT(instr))
                        offset.un = (instr & 0xf00) >> 4 | (instr & 15);
                else
                        offset.un = regs->uregs[RM_BITS(instr)];
-               handler = do_alignment_ldrhstrh;
+
+               if ((instr & 0x000000f0) == 0x000000b0 || /* LDRH, STRH */
+                   (instr & 0x001000f0) == 0x001000f0)   /* LDRSH */
+                       handler = do_alignment_ldrhstrh;
+               else if ((instr & 0x001000f0) == 0x000000d0 || /* LDRD */
+                        (instr & 0x001000f0) == 0x000000f0)   /* STRD */
+                       handler = do_alignment_ldrdstrd;
+               else
+                       goto bad;
                break;
 
        case 0x04000000:        /* ldr or str immediate */
index 3c655c54e23131b10cbf33d3d1fb1fe4a81d52be..d125a3dc061c8fb5efb27ceed11ce731ddafe4a5 100644 (file)
@@ -275,11 +275,9 @@ alloc_init_supersection(unsigned long virt, unsigned long phys, int prot)
        int i;
 
        for (i = 0; i < 16; i += 1) {
-               alloc_init_section(virt, phys & SUPERSECTION_MASK,
-                                  prot | PMD_SECT_SUPER);
+               alloc_init_section(virt, phys, prot | PMD_SECT_SUPER);
 
                virt += (PGDIR_SIZE / 2);
-               phys += (PGDIR_SIZE / 2);
        }
 }
 
@@ -297,14 +295,10 @@ alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pg
        pte_t *ptep;
 
        if (pmd_none(*pmdp)) {
-               unsigned long pmdval;
                ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE *
                                               sizeof(pte_t));
 
-               pmdval = __pa(ptep) | prot_l1;
-               pmdp[0] = __pmd(pmdval);
-               pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
-               flush_pmd_entry(pmdp);
+               __pmd_populate(pmdp, __pa(ptep) | prot_l1);
        }
        ptep = pte_offset_kernel(pmdp, virt);
 
@@ -459,7 +453,7 @@ static void __init build_mem_type_table(void)
 
        for (i = 0; i < 16; i++) {
                unsigned long v = pgprot_val(protection_map[i]);
-               v &= (~(PTE_BUFFERABLE|PTE_CACHEABLE)) | user_pgprot;
+               v = (v & ~(PTE_BUFFERABLE|PTE_CACHEABLE)) | user_pgprot;
                protection_map[i] = __pgprot(v);
        }
 
@@ -583,23 +577,23 @@ static void __init create_mapping(struct map_desc *md)
  */
 void setup_mm_for_reboot(char mode)
 {
-       unsigned long pmdval;
+       unsigned long base_pmdval;
        pgd_t *pgd;
-       pmd_t *pmd;
        int i;
-       int cpu_arch = cpu_architecture();
 
        if (current->mm && current->mm->pgd)
                pgd = current->mm->pgd;
        else
                pgd = init_mm.pgd;
 
-       for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++) {
-               pmdval = (i << PGDIR_SHIFT) |
-                        PMD_SECT_AP_WRITE | PMD_SECT_AP_READ |
-                        PMD_TYPE_SECT;
-               if (cpu_arch <= CPU_ARCH_ARMv5TEJ)
-                       pmdval |= PMD_BIT4;
+       base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT;
+       if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ)
+               base_pmdval |= PMD_BIT4;
+
+       for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++, pgd++) {
+               unsigned long pmdval = (i << PGDIR_SHIFT) | base_pmdval;
+               pmd_t *pmd;
+
                pmd = pmd_off(pgd, i << PGDIR_SHIFT);
                pmd[0] = __pmd(pmdval);
                pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
index 80988136f26d7dec0791f62c1df59eca73af35ea..3deced637f07c814706698e6e90a10088aebe889 100644 (file)
@@ -383,6 +383,12 @@ source "drivers/acpi/Kconfig"
 
 endif
 
+if PM
+
+source "arch/ia64/kernel/cpufreq/Kconfig"
+
+endif
+
 endmenu
 
 if !IA64_HP_SIM
index 5c46928e3dc655739bf0081ce19f503848930054..30fdfb1d0a53e3d08c9f817eaf34a0bd7b9a78f3 100644 (file)
@@ -237,17 +237,6 @@ sal_emulator (long index, unsigned long in1, unsigned long in2,
        return ((struct sal_ret_values) {status, r9, r10, r11});
 }
 
-
-/*
- * This is here to work around a bug in egcs-1.1.1b that causes the
- * compiler to crash (seems like a bug in the new alias analysis code.
- */
-void *
-id (long addr)
-{
-       return (void *) addr;
-}
-
 struct ia64_boot_param *
 sys_fw_init (const char *args, int arglen)
 {
index ebb89be2aa2dba5acbcaf7773414ff7ca51a1b87..aa891c9bc9b67ac268970391f49122f37af4b22a 100644 (file)
@@ -29,7 +29,6 @@
 #include <asm/uaccess.h>
 #include <asm/rse.h>
 #include <asm/sigcontext.h>
-#include <asm/segment.h>
 
 #include "ia32priv.h"
 
index e1fb68ddec26328f5ea7f19ce099628fea81b664..b242594be55b46716d0ddf0a08e01dab60df14dd 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_SMP)             += smp.o smpboot.o domain.o
 obj-$(CONFIG_NUMA)             += numa.o
 obj-$(CONFIG_PERFMON)          += perfmon_default_smpl.o
 obj-$(CONFIG_IA64_CYCLONE)     += cyclone.o
+obj-$(CONFIG_CPU_FREQ)         += cpufreq/
 obj-$(CONFIG_IA64_MCA_RECOVERY)        += mca_recovery.o
 obj-$(CONFIG_KPROBES)          += kprobes.o jprobes.o
 obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR)  += uncached.o
diff --git a/arch/ia64/kernel/cpufreq/Kconfig b/arch/ia64/kernel/cpufreq/Kconfig
new file mode 100644 (file)
index 0000000..2d9d527
--- /dev/null
@@ -0,0 +1,29 @@
+
+#
+# CPU Frequency scaling
+#
+
+menu "CPU Frequency scaling"
+
+source "drivers/cpufreq/Kconfig"
+
+if CPU_FREQ
+
+comment "CPUFreq processor drivers"
+
+config IA64_ACPI_CPUFREQ
+       tristate "ACPI Processor P-States driver"
+       select CPU_FREQ_TABLE
+       depends on ACPI_PROCESSOR
+       help
+       This driver adds a CPUFreq driver which utilizes the ACPI
+       Processor Performance States.
+
+       For details, take a look at <file:Documentation/cpu-freq/>.
+
+       If in doubt, say N.
+
+endif   # CPU_FREQ
+
+endmenu
+
diff --git a/arch/ia64/kernel/cpufreq/Makefile b/arch/ia64/kernel/cpufreq/Makefile
new file mode 100644 (file)
index 0000000..f748d34
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_IA64_ACPI_CPUFREQ)                += acpi-cpufreq.o
diff --git a/arch/ia64/kernel/cpufreq/acpi-cpufreq.c b/arch/ia64/kernel/cpufreq/acpi-cpufreq.c
new file mode 100644 (file)
index 0000000..da4d5cf
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * arch/ia64/kernel/cpufreq/acpi-cpufreq.c
+ * This file provides the ACPI based P-state support. This
+ * module works with generic cpufreq infrastructure. Most of
+ * the code is based on i386 version
+ * (arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c)
+ *
+ * Copyright (C) 2005 Intel Corp
+ *      Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/pal.h>
+
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
+
+MODULE_AUTHOR("Venkatesh Pallipadi");
+MODULE_DESCRIPTION("ACPI Processor P-States Driver");
+MODULE_LICENSE("GPL");
+
+
+struct cpufreq_acpi_io {
+       struct acpi_processor_performance       acpi_data;
+       struct cpufreq_frequency_table          *freq_table;
+       unsigned int                            resume;
+};
+
+static struct cpufreq_acpi_io  *acpi_io_data[NR_CPUS];
+
+static struct cpufreq_driver acpi_cpufreq_driver;
+
+
+static int
+processor_set_pstate (
+       u32     value)
+{
+       s64 retval;
+
+       dprintk("processor_set_pstate\n");
+
+       retval = ia64_pal_set_pstate((u64)value);
+
+       if (retval) {
+               dprintk("Failed to set freq to 0x%x, with error 0x%x\n",
+                       value, retval);
+               return -ENODEV;
+       }
+       return (int)retval;
+}
+
+
+static int
+processor_get_pstate (
+       u32     *value)
+{
+       u64     pstate_index = 0;
+       s64     retval;
+
+       dprintk("processor_get_pstate\n");
+
+       retval = ia64_pal_get_pstate(&pstate_index);
+       *value = (u32) pstate_index;
+
+       if (retval)
+               dprintk("Failed to get current freq with "
+                       "error 0x%x, idx 0x%x\n", retval, *value);
+
+       return (int)retval;
+}
+
+
+/* To be used only after data->acpi_data is initialized */
+static unsigned
+extract_clock (
+       struct cpufreq_acpi_io *data,
+       unsigned value,
+       unsigned int cpu)
+{
+       unsigned long i;
+
+       dprintk("extract_clock\n");
+
+       for (i = 0; i < data->acpi_data.state_count; i++) {
+               if (value >= data->acpi_data.states[i].control)
+                       return data->acpi_data.states[i].core_frequency;
+       }
+       return data->acpi_data.states[i-1].core_frequency;
+}
+
+
+static unsigned int
+processor_get_freq (
+       struct cpufreq_acpi_io  *data,
+       unsigned int            cpu)
+{
+       int                     ret = 0;
+       u32                     value = 0;
+       cpumask_t               saved_mask;
+       unsigned long           clock_freq;
+
+       dprintk("processor_get_freq\n");
+
+       saved_mask = current->cpus_allowed;
+       set_cpus_allowed(current, cpumask_of_cpu(cpu));
+       if (smp_processor_id() != cpu) {
+               ret = -EAGAIN;
+               goto migrate_end;
+       }
+
+       /*
+        * processor_get_pstate gets the average frequency since the
+        * last get. So, do two PAL_get_freq()...
+        */
+       ret = processor_get_pstate(&value);
+       ret = processor_get_pstate(&value);
+
+       if (ret) {
+               set_cpus_allowed(current, saved_mask);
+               printk(KERN_WARNING "get performance failed with error %d\n",
+                      ret);
+               ret = -EAGAIN;
+               goto migrate_end;
+       }
+       clock_freq = extract_clock(data, value, cpu);
+       ret = (clock_freq*1000);
+
+migrate_end:
+       set_cpus_allowed(current, saved_mask);
+       return ret;
+}
+
+
+static int
+processor_set_freq (
+       struct cpufreq_acpi_io  *data,
+       unsigned int            cpu,
+       int                     state)
+{
+       int                     ret = 0;
+       u32                     value = 0;
+       struct cpufreq_freqs    cpufreq_freqs;
+       cpumask_t               saved_mask;
+       int                     retval;
+
+       dprintk("processor_set_freq\n");
+
+       saved_mask = current->cpus_allowed;
+       set_cpus_allowed(current, cpumask_of_cpu(cpu));
+       if (smp_processor_id() != cpu) {
+               retval = -EAGAIN;
+               goto migrate_end;
+       }
+
+       if (state == data->acpi_data.state) {
+               if (unlikely(data->resume)) {
+                       dprintk("Called after resume, resetting to P%d\n", state);
+                       data->resume = 0;
+               } else {
+                       dprintk("Already at target state (P%d)\n", state);
+                       retval = 0;
+                       goto migrate_end;
+               }
+       }
+
+       dprintk("Transitioning from P%d to P%d\n",
+               data->acpi_data.state, state);
+
+       /* cpufreq frequency struct */
+       cpufreq_freqs.cpu = cpu;
+       cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency;
+       cpufreq_freqs.new = data->freq_table[state].frequency;
+
+       /* notify cpufreq */
+       cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
+
+       /*
+        * First we write the target state's 'control' value to the
+        * control_register.
+        */
+
+       value = (u32) data->acpi_data.states[state].control;
+
+       dprintk("Transitioning to state: 0x%08x\n", value);
+
+       ret = processor_set_pstate(value);
+       if (ret) {
+               unsigned int tmp = cpufreq_freqs.new;
+               cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
+               cpufreq_freqs.new = cpufreq_freqs.old;
+               cpufreq_freqs.old = tmp;
+               cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
+               cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
+               printk(KERN_WARNING "Transition failed with error %d\n", ret);
+               retval = -ENODEV;
+               goto migrate_end;
+       }
+
+       cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
+
+       data->acpi_data.state = state;
+
+       retval = 0;
+
+migrate_end:
+       set_cpus_allowed(current, saved_mask);
+       return (retval);
+}
+
+
+static unsigned int
+acpi_cpufreq_get (
+       unsigned int            cpu)
+{
+       struct cpufreq_acpi_io *data = acpi_io_data[cpu];
+
+       dprintk("acpi_cpufreq_get\n");
+
+       return processor_get_freq(data, cpu);
+}
+
+
+static int
+acpi_cpufreq_target (
+       struct cpufreq_policy   *policy,
+       unsigned int target_freq,
+       unsigned int relation)
+{
+       struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
+       unsigned int next_state = 0;
+       unsigned int result = 0;
+
+       dprintk("acpi_cpufreq_setpolicy\n");
+
+       result = cpufreq_frequency_table_target(policy,
+                       data->freq_table, target_freq, relation, &next_state);
+       if (result)
+               return (result);
+
+       result = processor_set_freq(data, policy->cpu, next_state);
+
+       return (result);
+}
+
+
+static int
+acpi_cpufreq_verify (
+       struct cpufreq_policy   *policy)
+{
+       unsigned int result = 0;
+       struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
+
+       dprintk("acpi_cpufreq_verify\n");
+
+       result = cpufreq_frequency_table_verify(policy,
+                       data->freq_table);
+
+       return (result);
+}
+
+
+/*
+ * processor_init_pdc - let BIOS know about the SMP capabilities
+ * of this driver
+ * @perf: processor-specific acpi_io_data struct
+ * @cpu: CPU being initialized
+ *
+ * To avoid issues with legacy OSes, some BIOSes require to be informed of
+ * the SMP capabilities of OS P-state driver. Here we set the bits in _PDC
+ * accordingly. Actual call to _PDC is done in driver/acpi/processor.c
+ */
+static void
+processor_init_pdc (
+               struct acpi_processor_performance *perf,
+               unsigned int cpu,
+               struct acpi_object_list *obj_list
+               )
+{
+       union acpi_object *obj;
+       u32 *buf;
+
+       dprintk("processor_init_pdc\n");
+
+       perf->pdc = NULL;
+       /* Initialize pdc. It will be used later. */
+       if (!obj_list)
+               return;
+
+       if (!(obj_list->count && obj_list->pointer))
+               return;
+
+       obj = obj_list->pointer;
+       if ((obj->buffer.length == 12) && obj->buffer.pointer) {
+               buf = (u32 *)obj->buffer.pointer;
+                       buf[0] = ACPI_PDC_REVISION_ID;
+                       buf[1] = 1;
+                       buf[2] = ACPI_PDC_EST_CAPABILITY_SMP;
+               perf->pdc = obj_list;
+       }
+       return;
+}
+
+
+static int
+acpi_cpufreq_cpu_init (
+       struct cpufreq_policy   *policy)
+{
+       unsigned int            i;
+       unsigned int            cpu = policy->cpu;
+       struct cpufreq_acpi_io  *data;
+       unsigned int            result = 0;
+
+       union acpi_object               arg0 = {ACPI_TYPE_BUFFER};
+       u32                             arg0_buf[3];
+       struct acpi_object_list         arg_list = {1, &arg0};
+
+       dprintk("acpi_cpufreq_cpu_init\n");
+       /* setup arg_list for _PDC settings */
+        arg0.buffer.length = 12;
+        arg0.buffer.pointer = (u8 *) arg0_buf;
+
+       data = kmalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL);
+       if (!data)
+               return (-ENOMEM);
+
+       memset(data, 0, sizeof(struct cpufreq_acpi_io));
+
+       acpi_io_data[cpu] = data;
+
+       processor_init_pdc(&data->acpi_data, cpu, &arg_list);
+       result = acpi_processor_register_performance(&data->acpi_data, cpu);
+       data->acpi_data.pdc = NULL;
+
+       if (result)
+               goto err_free;
+
+       /* capability check */
+       if (data->acpi_data.state_count <= 1) {
+               dprintk("No P-States\n");
+               result = -ENODEV;
+               goto err_unreg;
+       }
+
+       if ((data->acpi_data.control_register.space_id !=
+                                       ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+           (data->acpi_data.status_register.space_id !=
+                                       ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+               dprintk("Unsupported address space [%d, %d]\n",
+                       (u32) (data->acpi_data.control_register.space_id),
+                       (u32) (data->acpi_data.status_register.space_id));
+               result = -ENODEV;
+               goto err_unreg;
+       }
+
+       /* alloc freq_table */
+       data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
+                                  (data->acpi_data.state_count + 1),
+                                  GFP_KERNEL);
+       if (!data->freq_table) {
+               result = -ENOMEM;
+               goto err_unreg;
+       }
+
+       /* detect transition latency */
+       policy->cpuinfo.transition_latency = 0;
+       for (i=0; i<data->acpi_data.state_count; i++) {
+               if ((data->acpi_data.states[i].transition_latency * 1000) >
+                   policy->cpuinfo.transition_latency) {
+                       policy->cpuinfo.transition_latency =
+                           data->acpi_data.states[i].transition_latency * 1000;
+               }
+       }
+       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+       policy->cur = processor_get_freq(data, policy->cpu);
+
+       /* table init */
+       for (i = 0; i <= data->acpi_data.state_count; i++)
+       {
+               data->freq_table[i].index = i;
+               if (i < data->acpi_data.state_count) {
+                       data->freq_table[i].frequency =
+                             data->acpi_data.states[i].core_frequency * 1000;
+               } else {
+                       data->freq_table[i].frequency = CPUFREQ_TABLE_END;
+               }
+       }
+
+       result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
+       if (result) {
+               goto err_freqfree;
+       }
+
+       /* notify BIOS that we exist */
+       acpi_processor_notify_smm(THIS_MODULE);
+
+       printk(KERN_INFO "acpi-cpufreq: CPU%u - ACPI performance management "
+              "activated.\n", cpu);
+
+       for (i = 0; i < data->acpi_data.state_count; i++)
+               dprintk("     %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n",
+                       (i == data->acpi_data.state?'*':' '), i,
+                       (u32) data->acpi_data.states[i].core_frequency,
+                       (u32) data->acpi_data.states[i].power,
+                       (u32) data->acpi_data.states[i].transition_latency,
+                       (u32) data->acpi_data.states[i].bus_master_latency,
+                       (u32) data->acpi_data.states[i].status,
+                       (u32) data->acpi_data.states[i].control);
+
+       cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu);
+
+       /* the first call to ->target() should result in us actually
+        * writing something to the appropriate registers. */
+       data->resume = 1;
+
+       return (result);
+
+ err_freqfree:
+       kfree(data->freq_table);
+ err_unreg:
+       acpi_processor_unregister_performance(&data->acpi_data, cpu);
+ err_free:
+       kfree(data);
+       acpi_io_data[cpu] = NULL;
+
+       return (result);
+}
+
+
+static int
+acpi_cpufreq_cpu_exit (
+       struct cpufreq_policy   *policy)
+{
+       struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
+
+       dprintk("acpi_cpufreq_cpu_exit\n");
+
+       if (data) {
+               cpufreq_frequency_table_put_attr(policy->cpu);
+               acpi_io_data[policy->cpu] = NULL;
+               acpi_processor_unregister_performance(&data->acpi_data,
+                                                     policy->cpu);
+               kfree(data);
+       }
+
+       return (0);
+}
+
+
+static struct freq_attr* acpi_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
+
+static struct cpufreq_driver acpi_cpufreq_driver = {
+       .verify         = acpi_cpufreq_verify,
+       .target         = acpi_cpufreq_target,
+       .get            = acpi_cpufreq_get,
+       .init           = acpi_cpufreq_cpu_init,
+       .exit           = acpi_cpufreq_cpu_exit,
+       .name           = "acpi-cpufreq",
+       .owner          = THIS_MODULE,
+       .attr           = acpi_cpufreq_attr,
+};
+
+
+static int __init
+acpi_cpufreq_init (void)
+{
+       dprintk("acpi_cpufreq_init\n");
+
+       return cpufreq_register_driver(&acpi_cpufreq_driver);
+}
+
+
+static void __exit
+acpi_cpufreq_exit (void)
+{
+       dprintk("acpi_cpufreq_exit\n");
+
+       cpufreq_unregister_driver(&acpi_cpufreq_driver);
+       return;
+}
+
+
+late_initcall(acpi_cpufreq_init);
+module_exit(acpi_cpufreq_exit);
+
index 770fab37928ee9e4c3870b8875473f7a53d1213b..f2dbcd1db0d4dc99b3baca0d0b677c3ed788a58c 100644 (file)
@@ -35,7 +35,7 @@ arch_get_unmapped_area (struct file *filp, unsigned long addr, unsigned long len
                return -ENOMEM;
 
 #ifdef CONFIG_HUGETLB_PAGE
-       if (REGION_NUMBER(addr) == REGION_HPAGE)
+       if (REGION_NUMBER(addr) == RGN_HPAGE)
                addr = 0;
 #endif
        if (!addr)
index 490dfc9ab47ffd51bd02167047e7dcb3a9de5041..4e9d06c48a8ba40c3cb70d39e352efa793f26f48 100644 (file)
@@ -184,7 +184,7 @@ uncached_free_page(unsigned long maddr)
 {
        int node;
 
-       node = nasid_to_cnodeid(NASID_GET(maddr));
+       node = paddr_to_nid(maddr - __IA64_UNCACHED_OFFSET);
 
        dprintk(KERN_DEBUG "uncached_free_page(%lx) on node %i\n", maddr, node);
 
@@ -217,7 +217,7 @@ uncached_build_memmap(unsigned long start, unsigned long end, void *arg)
 
        memset((char *)vstart, 0, length);
 
-       node = nasid_to_cnodeid(NASID_GET(start));
+       node = paddr_to_nid(start);
 
        for (; vstart < vend ; vstart += PAGE_SIZE) {
                dprintk(KERN_INFO "sticking %lx into the pool!\n", vstart);
index 1902c3c2ef923f57ff19b9550cd34bb75ce7a4bb..799407e7726f2a9c54f301ce50f71c253f514174 100644 (file)
@@ -6,7 +6,7 @@ obj-y := io.o
 
 lib-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o                 \
        __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o                   \
-       bitop.o checksum.o clear_page.o csum_partial_copy.o copy_page.o \
+       bitop.o checksum.o clear_page.o csum_partial_copy.o             \
        clear_user.o strncpy_from_user.o strlen_user.o strnlen_user.o   \
        flush.o ip_fast_csum.o do_csum.o                                \
        memset.o strlen.o swiotlb.o
index ab7b3ad99a7fa9f9c87c78474ee7fda27d3f01b0..dbc0b3e449c5fe7ad8e81c284d00c85b0bf600fe 100644 (file)
@@ -93,8 +93,7 @@ static int __init
 setup_io_tlb_npages(char *str)
 {
        if (isdigit(*str)) {
-               io_tlb_nslabs = simple_strtoul(str, &str, 0) <<
-                       (PAGE_SHIFT - IO_TLB_SHIFT);
+               io_tlb_nslabs = simple_strtoul(str, &str, 0);
                /* avoid tail segment of size < IO_TLB_SEGSIZE */
                io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE);
        }
@@ -117,7 +116,7 @@ swiotlb_init_with_default_size (size_t default_size)
        unsigned long i;
 
        if (!io_tlb_nslabs) {
-               io_tlb_nslabs = (default_size >> PAGE_SHIFT);
+               io_tlb_nslabs = (default_size >> IO_TLB_SHIFT);
                io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE);
        }
 
index e0a776a3044c89f5614de6e2284c6a338cda6170..2d13889d0a9915da645b074ecbef1f8a0b56d116 100644 (file)
@@ -76,7 +76,7 @@ int is_aligned_hugepage_range(unsigned long addr, unsigned long len)
                return -EINVAL;
        if (addr & ~HPAGE_MASK)
                return -EINVAL;
-       if (REGION_NUMBER(addr) != REGION_HPAGE)
+       if (REGION_NUMBER(addr) != RGN_HPAGE)
                return -EINVAL;
 
        return 0;
@@ -87,7 +87,7 @@ struct page *follow_huge_addr(struct mm_struct *mm, unsigned long addr, int writ
        struct page *page;
        pte_t *ptep;
 
-       if (REGION_NUMBER(addr) != REGION_HPAGE)
+       if (REGION_NUMBER(addr) != RGN_HPAGE)
                return ERR_PTR(-EINVAL);
 
        ptep = huge_pte_offset(mm, addr);
@@ -142,8 +142,8 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, u
                return -ENOMEM;
        if (len & ~HPAGE_MASK)
                return -EINVAL;
-       /* This code assumes that REGION_HPAGE != 0. */
-       if ((REGION_NUMBER(addr) != REGION_HPAGE) || (addr & (HPAGE_SIZE - 1)))
+       /* This code assumes that RGN_HPAGE != 0. */
+       if ((REGION_NUMBER(addr) != RGN_HPAGE) || (addr & (HPAGE_SIZE - 1)))
                addr = HPAGE_REGION_BASE;
        else
                addr = ALIGN(addr, HPAGE_SIZE);
index f9472c50ab4298a072fd25cfca6743ff69122b91..9977c122e9fa2c1a314e2d90fb5260dba87b0cb7 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <asm/machvec.h>
 #include <asm/page.h>
-#include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/sal.h>
index 0139124dd54a66935124494ff4cff213f0452d89..6b2e7b75eb19136cee3f7f5943667c55ee75da90 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2000-2005 Silicon Graphics, Inc. All rights reserved.
  */
 
 #ifndef _ASM_IA64_SN_TIO_H
 #define TIO_ITTE_VALID_MASK    0x1
 #define TIO_ITTE_VALID_SHIFT   16
 
+#define TIO_ITTE_WIDGET(itte) \
+       (((itte) >> TIO_ITTE_WIDGET_SHIFT) & TIO_ITTE_WIDGET_MASK)
+#define TIO_ITTE_VALID(itte) \
+       (((itte) >> TIO_ITTE_VALID_SHIFT) & TIO_ITTE_VALID_MASK)
 
 #define TIO_ITTE_PUT(nasid, bigwin, widget, addr, valid) \
         REMOTE_HUB_S((nasid), TIO_ITTE(bigwin), \
index 580a1c0403a73c373c60cf34cc59f985bb5a16f0..71c2b271b4c687daabd4e886023fe85dacfeeb94 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 1992 - 1997, 2000-2005 Silicon Graphics, Inc. All rights reserved.
  */
 #ifndef _ASM_IA64_SN_XTALK_HUBDEV_H
 #define _ASM_IA64_SN_XTALK_HUBDEV_H
@@ -16,6 +16,9 @@
 #define IIO_ITTE_WIDGET_MASK    ((1<<IIO_ITTE_WIDGET_BITS)-1)
 #define IIO_ITTE_WIDGET_SHIFT   8
 
+#define IIO_ITTE_WIDGET(itte)  \
+       (((itte) >> IIO_ITTE_WIDGET_SHIFT) & IIO_ITTE_WIDGET_MASK)
+
 /*
  * Use the top big window as a surrogate for the first small window
  */
@@ -34,7 +37,8 @@ struct sn_flush_device_list {
        unsigned long sfdl_force_int_addr;
        unsigned long sfdl_flush_value;
        volatile unsigned long *sfdl_flush_addr;
-       uint64_t sfdl_persistent_busnum;
+       uint32_t sfdl_persistent_busnum;
+       uint32_t sfdl_persistent_segment;
        struct pcibus_info *sfdl_pcibus_info;
        spinlock_t sfdl_flush_lock;
 };
@@ -58,7 +62,8 @@ struct hubdev_info {
 
        void                            *hdi_nodepda;
        void                            *hdi_node_vertex;
-       void                            *hdi_xtalk_vertex;
+       uint32_t                        max_segment_number;
+       uint32_t                        max_pcibus_number;
 };
 
 extern void hubdev_init_node(nodepda_t *, cnodeid_t);
index 647deae9bfcd57c3fef46b03d7f47912f6c985b1..45854c637e9ca035f2d16dac86da2698ec300030 100644 (file)
 
 /* two interfaces on two btes */
 #define MAX_INTERFACES_TO_TRY          4
+#define MAX_NODES_TO_TRY               2
 
 static struct bteinfo_s *bte_if_on_node(nasid_t nasid, int interface)
 {
        nodepda_t *tmp_nodepda;
 
+       if (nasid_to_cnodeid(nasid) == -1)
+               return (struct bteinfo_s *)NULL;;
+
        tmp_nodepda = NODEPDA(nasid_to_cnodeid(nasid));
        return &tmp_nodepda->bte_if[interface];
 
 }
 
+static inline void bte_start_transfer(struct bteinfo_s *bte, u64 len, u64 mode)
+{
+       if (is_shub2()) {
+               BTE_CTRL_STORE(bte, (IBLS_BUSY | ((len) | (mode) << 24)));
+       } else {
+               BTE_LNSTAT_STORE(bte, len);
+               BTE_CTRL_STORE(bte, mode);
+       }
+}
+
 /************************************************************************
  * Block Transfer Engine copy related functions.
  *
@@ -67,13 +81,15 @@ bte_result_t bte_copy(u64 src, u64 dest, u64 len, u64 mode, void *notification)
 {
        u64 transfer_size;
        u64 transfer_stat;
+       u64 notif_phys_addr;
        struct bteinfo_s *bte;
        bte_result_t bte_status;
        unsigned long irq_flags;
        unsigned long itc_end = 0;
-       struct bteinfo_s *btes_to_try[MAX_INTERFACES_TO_TRY];
-       int bte_if_index;
-       int bte_pri, bte_sec;
+       int nasid_to_try[MAX_NODES_TO_TRY];
+       int my_nasid = get_nasid();
+       int bte_if_index, nasid_index;
+       int bte_first, btes_per_node = BTES_PER_NODE;
 
        BTE_PRINTK(("bte_copy(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%p)\n",
                    src, dest, len, mode, notification));
@@ -86,36 +102,26 @@ bte_result_t bte_copy(u64 src, u64 dest, u64 len, u64 mode, void *notification)
                 (src & L1_CACHE_MASK) || (dest & L1_CACHE_MASK));
        BUG_ON(!(len < ((BTE_LEN_MASK + 1) << L1_CACHE_SHIFT)));
 
-       /* CPU 0 (per node) tries bte0 first, CPU 1 try bte1 first */
-       if (cpuid_to_subnode(smp_processor_id()) == 0) {
-               bte_pri = 0;
-               bte_sec = 1;
-       } else {
-               bte_pri = 1;
-               bte_sec = 0;
-       }
+       /*
+        * Start with interface corresponding to cpu number
+        */
+       bte_first = raw_smp_processor_id() % btes_per_node;
 
        if (mode & BTE_USE_DEST) {
                /* try remote then local */
-               btes_to_try[0] = bte_if_on_node(NASID_GET(dest), bte_pri);
-               btes_to_try[1] = bte_if_on_node(NASID_GET(dest), bte_sec);
+               nasid_to_try[0] = NASID_GET(dest);
                if (mode & BTE_USE_ANY) {
-                       btes_to_try[2] = bte_if_on_node(get_nasid(), bte_pri);
-                       btes_to_try[3] = bte_if_on_node(get_nasid(), bte_sec);
+                       nasid_to_try[1] = my_nasid;
                } else {
-                       btes_to_try[2] = NULL;
-                       btes_to_try[3] = NULL;
+                       nasid_to_try[1] = (int)NULL;
                }
        } else {
                /* try local then remote */
-               btes_to_try[0] = bte_if_on_node(get_nasid(), bte_pri);
-               btes_to_try[1] = bte_if_on_node(get_nasid(), bte_sec);
+               nasid_to_try[0] = my_nasid;
                if (mode & BTE_USE_ANY) {
-                       btes_to_try[2] = bte_if_on_node(NASID_GET(dest), bte_pri);
-                       btes_to_try[3] = bte_if_on_node(NASID_GET(dest), bte_sec);
+                       nasid_to_try[1] = NASID_GET(dest);
                } else {
-                       btes_to_try[2] = NULL;
-                       btes_to_try[3] = NULL;
+                       nasid_to_try[1] = (int)NULL;
                }
        }
 
@@ -123,11 +129,12 @@ retry_bteop:
        do {
                local_irq_save(irq_flags);
 
-               bte_if_index = 0;
+               bte_if_index = bte_first;
+               nasid_index = 0;
 
                /* Attempt to lock one of the BTE interfaces. */
-               while (bte_if_index < MAX_INTERFACES_TO_TRY) {
-                       bte = btes_to_try[bte_if_index++];
+               while (nasid_index < MAX_NODES_TO_TRY) {
+                       bte = bte_if_on_node(nasid_to_try[nasid_index],bte_if_index);
 
                        if (bte == NULL) {
                                continue;
@@ -143,6 +150,15 @@ retry_bteop:
                                        break;
                                }
                        }
+
+                       bte_if_index = (bte_if_index + 1) % btes_per_node; /* Next interface */
+                       if (bte_if_index == bte_first) {
+                               /*
+                                * We've tried all interfaces on this node
+                                */
+                               nasid_index++;
+                       }
+
                        bte = NULL;
                }
 
@@ -169,7 +185,13 @@ retry_bteop:
 
        /* Initialize the notification to a known value. */
        *bte->most_rcnt_na = BTE_WORD_BUSY;
+       notif_phys_addr = TO_PHYS(ia64_tpa((unsigned long)bte->most_rcnt_na));
 
+       if (is_shub2()) {
+               src = SH2_TIO_PHYS_TO_DMA(src);
+               dest = SH2_TIO_PHYS_TO_DMA(dest);
+               notif_phys_addr = SH2_TIO_PHYS_TO_DMA(notif_phys_addr);
+       }
        /* Set the source and destination registers */
        BTE_PRINTKV(("IBSA = 0x%lx)\n", (TO_PHYS(src))));
        BTE_SRC_STORE(bte, TO_PHYS(src));
@@ -177,14 +199,12 @@ retry_bteop:
        BTE_DEST_STORE(bte, TO_PHYS(dest));
 
        /* Set the notification register */
-       BTE_PRINTKV(("IBNA = 0x%lx)\n",
-                    TO_PHYS(ia64_tpa((unsigned long)bte->most_rcnt_na))));
-       BTE_NOTIF_STORE(bte,
-                       TO_PHYS(ia64_tpa((unsigned long)bte->most_rcnt_na)));
+       BTE_PRINTKV(("IBNA = 0x%lx)\n", notif_phys_addr));
+       BTE_NOTIF_STORE(bte, notif_phys_addr);
 
        /* Initiate the transfer */
        BTE_PRINTK(("IBCT = 0x%lx)\n", BTE_VALID_MODE(mode)));
-       BTE_START_TRANSFER(bte, transfer_size, BTE_VALID_MODE(mode));
+       bte_start_transfer(bte, transfer_size, BTE_VALID_MODE(mode));
 
        itc_end = ia64_get_itc() + (40000000 * local_cpu_data->cyc_per_usec);
 
@@ -195,6 +215,7 @@ retry_bteop:
        }
 
        while ((transfer_stat = *bte->most_rcnt_na) == BTE_WORD_BUSY) {
+               cpu_relax();
                if (ia64_get_itc() > itc_end) {
                        BTE_PRINTK(("BTE timeout nasid 0x%x bte%d IBLS = 0x%lx na 0x%lx\n",
                                NASID_GET(bte->bte_base_addr), bte->bte_num,
index 5c39b43ba3c029604103bb1757dbe3d5db1faa5c..5c5eb01c50f02b53f97513eda0e1e65e3daa2c00 100644 (file)
@@ -76,7 +76,7 @@ void hubiio_crb_free(struct hubdev_info *hubdev_info, int crbnum)
         */
        REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICDR, (IIO_ICDR_PND | crbnum));
        while (REMOTE_HUB_L(hubdev_info->hdi_nasid, IIO_ICDR) & IIO_ICDR_PND)
-               udelay(1);
+               cpu_relax();
 
 }
 
index 414cdf2e3c965e124223833a1ed036783e4c4392..4564ed0b5ff31e32aab74c819369e3762382f512 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/sn/simulator.h>
 #include <asm/sn/sn_sal.h>
 #include <asm/sn/tioca_provider.h>
+#include <asm/sn/tioce_provider.h>
 #include "xtalk/hubdev.h"
 #include "xtalk/xwidgetdev.h"
 
@@ -44,6 +45,9 @@ int sn_ioif_inited = 0;               /* SN I/O infrastructure initialized? */
 
 struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES];      /* indexed by asic type */
 
+static int max_segment_number = 0; /* Default highest segment number */
+static int max_pcibus_number = 255; /* Default highest pci bus number */
+
 /*
  * Hooks and struct for unsupported pci providers
  */
@@ -157,13 +161,28 @@ static void sn_fixup_ionodes(void)
        uint64_t nasid;
        int i, widget;
 
+       /*
+        * Get SGI Specific HUB chipset information.
+        * Inform Prom that this kernel can support domain bus numbering.
+        */
        for (i = 0; i < numionodes; i++) {
                hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo);
                nasid = cnodeid_to_nasid(i);
+               hubdev->max_segment_number = 0xffffffff;
+               hubdev->max_pcibus_number = 0xff;
                status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev));
                if (status)
                        continue;
 
+               /* Save the largest Domain and pcibus numbers found. */
+               if (hubdev->max_segment_number) {
+                       /*
+                        * Dealing with a Prom that supports segments.
+                        */
+                       max_segment_number = hubdev->max_segment_number;
+                       max_pcibus_number = hubdev->max_pcibus_number;
+               }
+
                /* Attach the error interrupt handlers */
                if (nasid & 1)
                        ice_error_init(hubdev);
@@ -230,7 +249,7 @@ void sn_pci_unfixup_slot(struct pci_dev *dev)
 void sn_pci_fixup_slot(struct pci_dev *dev)
 {
        int idx;
-       int segment = 0;
+       int segment = pci_domain_nr(dev->bus);
        int status = 0;
        struct pcibus_bussoft *bs;
        struct pci_bus *host_pci_bus;
@@ -283,9 +302,9 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
         * PCI host_pci_dev struct and set up host bus linkages
         */
 
-       bus_no = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32;
+       bus_no = (SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32) & 0xff;
        devfn = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle & 0xffffffff;
-       host_pci_bus = pci_find_bus(pci_domain_nr(dev->bus), bus_no);
+       host_pci_bus = pci_find_bus(segment, bus_no);
        host_pci_dev = pci_get_slot(host_pci_bus, devfn);
 
        SN_PCIDEV_INFO(dev)->host_pci_dev = host_pci_dev;
@@ -333,6 +352,7 @@ void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
        prom_bussoft_ptr = __va(prom_bussoft_ptr);
 
        controller = kcalloc(1,sizeof(struct pci_controller), GFP_KERNEL);
+       controller->segment = segment;
        if (!controller)
                BUG();
 
@@ -390,7 +410,7 @@ void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
        if (controller->node >= num_online_nodes()) {
                struct pcibus_bussoft *b = SN_PCIBUS_BUSSOFT(bus);
 
-               printk(KERN_WARNING "Device ASIC=%u XID=%u PBUSNUM=%lu"
+               printk(KERN_WARNING "Device ASIC=%u XID=%u PBUSNUM=%u"
                                    "L_IO=%lx L_MEM=%lx BASE=%lx\n",
                        b->bs_asic_type, b->bs_xid, b->bs_persist_busnum,
                        b->bs_legacy_io, b->bs_legacy_mem, b->bs_base);
@@ -445,6 +465,7 @@ sn_sysdata_free_start:
 static int __init sn_pci_init(void)
 {
        int i = 0;
+       int j = 0;
        struct pci_dev *pci_dev = NULL;
        extern void sn_init_cpei_timer(void);
 #ifdef CONFIG_PROC_FS
@@ -464,6 +485,7 @@ static int __init sn_pci_init(void)
 
        pcibr_init_provider();
        tioca_init_provider();
+       tioce_init_provider();
 
        /*
         * This is needed to avoid bounce limit checks in the blk layer
@@ -479,8 +501,9 @@ static int __init sn_pci_init(void)
 #endif
 
        /* busses are not known yet ... */
-       for (i = 0; i < PCI_BUSES_TO_SCAN; i++)
-               sn_pci_controller_fixup(0, i, NULL);
+       for (i = 0; i <= max_segment_number; i++)
+               for (j = 0; j <= max_pcibus_number; j++)
+                       sn_pci_controller_fixup(i, j, NULL);
 
        /*
         * Generic Linux PCI Layer has created the pci_bus and pci_dev 
index 84d276a14ecb178d696d239f0758efaff251ee47..9fc74631ba8aa92568a8fed1e27c0a202e00f92f 100644 (file)
@@ -5,7 +5,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  */
 
 #include <linux/irq.h>
@@ -76,16 +76,14 @@ static void sn_enable_irq(unsigned int irq)
 
 static void sn_ack_irq(unsigned int irq)
 {
-       uint64_t event_occurred, mask = 0;
-       int nasid;
+       u64 event_occurred, mask = 0;
 
        irq = irq & 0xff;
-       nasid = get_nasid();
        event_occurred =
-           HUB_L((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED));
+           HUB_L((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED));
        mask = event_occurred & SH_ALL_INT_MASK;
-       HUB_S((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED_ALIAS),
-                mask);
+       HUB_S((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS),
+             mask);
        __set_bit(irq, (volatile void *)pda->sn_in_service_ivecs);
 
        move_irq(irq);
@@ -93,15 +91,12 @@ static void sn_ack_irq(unsigned int irq)
 
 static void sn_end_irq(unsigned int irq)
 {
-       int nasid;
        int ivec;
-       uint64_t event_occurred;
+       u64 event_occurred;
 
        ivec = irq & 0xff;
        if (ivec == SGI_UART_VECTOR) {
-               nasid = get_nasid();
-               event_occurred = HUB_L((uint64_t *) GLOBAL_MMR_ADDR
-                                      (nasid, SH_EVENT_OCCURRED));
+               event_occurred = HUB_L((u64*)LOCAL_MMR_ADDR (SH_EVENT_OCCURRED));
                /* If the UART bit is set here, we may have received an
                 * interrupt from the UART that the driver missed.  To
                 * make sure, we IPI ourselves to force us to look again.
@@ -132,6 +127,7 @@ static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
                int local_widget, status;
                nasid_t local_nasid;
                struct sn_irq_info *new_irq_info;
+               struct sn_pcibus_provider *pci_provider;
 
                new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC);
                if (new_irq_info == NULL)
@@ -171,8 +167,9 @@ static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
                new_irq_info->irq_cpuid = cpuid;
                register_intr_pda(new_irq_info);
 
-               if (IS_PCI_BRIDGE_ASIC(new_irq_info->irq_bridge_type))
-                       pcibr_change_devices_irq(new_irq_info);
+               pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type];
+               if (pci_provider && pci_provider->target_interrupt)
+                       (pci_provider->target_interrupt)(new_irq_info);
 
                spin_lock(&sn_irq_info_lock);
                list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
@@ -317,6 +314,16 @@ void sn_irq_unfixup(struct pci_dev *pci_dev)
        pci_dev_put(pci_dev);
 }
 
+static inline void
+sn_call_force_intr_provider(struct sn_irq_info *sn_irq_info)
+{
+       struct sn_pcibus_provider *pci_provider;
+
+       pci_provider = sn_pci_provider[sn_irq_info->irq_bridge_type];
+       if (pci_provider && pci_provider->force_interrupt)
+               (*pci_provider->force_interrupt)(sn_irq_info);
+}
+
 static void force_interrupt(int irq)
 {
        struct sn_irq_info *sn_irq_info;
@@ -325,11 +332,9 @@ static void force_interrupt(int irq)
                return;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[irq], list) {
-               if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
-                   (sn_irq_info->irq_bridge != NULL))
-                       pcibr_force_interrupt(sn_irq_info);
-       }
+       list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[irq], list)
+               sn_call_force_intr_provider(sn_irq_info);
+
        rcu_read_unlock();
 }
 
@@ -351,6 +356,14 @@ static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info)
        struct pcidev_info *pcidev_info;
        struct pcibus_info *pcibus_info;
 
+       /*
+        * Bridge types attached to TIO (anything but PIC) do not need this WAR
+        * since they do not target Shub II interrupt registers.  If that
+        * ever changes, this check needs to accomodate.
+        */
+       if (sn_irq_info->irq_bridge_type != PCIIO_ASIC_TYPE_PIC)
+               return;
+
        pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
        if (!pcidev_info)
                return;
@@ -377,16 +390,12 @@ static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info)
                break;
        }
        if (!test_bit(irr_bit, &irr_reg)) {
-               if (!test_bit(irq, pda->sn_soft_irr)) {
-                       if (!test_bit(irq, pda->sn_in_service_ivecs)) {
-                               regval &= 0xff;
-                               if (sn_irq_info->irq_int_bit & regval &
-                                   sn_irq_info->irq_last_intr) {
-                                       regval &=
-                                           ~(sn_irq_info->
-                                             irq_int_bit & regval);
-                                       pcibr_force_interrupt(sn_irq_info);
-                               }
+               if (!test_bit(irq, pda->sn_in_service_ivecs)) {
+                       regval &= 0xff;
+                       if (sn_irq_info->irq_int_bit & regval &
+                           sn_irq_info->irq_last_intr) {
+                               regval &= ~(sn_irq_info->irq_int_bit & regval);
+                               sn_call_force_intr_provider(sn_irq_info);
                        }
                }
        }
@@ -404,13 +413,7 @@ void sn_lb_int_war_check(void)
        rcu_read_lock();
        for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) {
                list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[i], list) {
-                       /*
-                        * Only call for PCI bridges that are fully
-                        * initialized.
-                        */
-                       if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
-                           (sn_irq_info->irq_bridge != NULL))
-                               sn_check_intr(i, sn_irq_info);
+                       sn_check_intr(i, sn_irq_info);
                }
        }
        rcu_read_unlock();
index 7c7fe441d62371ec513365bd89814397e0888b2e..a594aca959e6e141eb83a8769887f9199dc8b42a 100644 (file)
@@ -80,8 +80,6 @@ EXPORT_PER_CPU_SYMBOL(__sn_cnodeid_to_nasid);
 DEFINE_PER_CPU(struct nodepda_s *, __sn_nodepda);
 EXPORT_PER_CPU_SYMBOL(__sn_nodepda);
 
-partid_t sn_partid = -1;
-EXPORT_SYMBOL(sn_partid);
 char sn_system_serial_number_string[128];
 EXPORT_SYMBOL(sn_system_serial_number_string);
 u64 sn_partition_serial_number;
@@ -403,6 +401,7 @@ static void __init sn_init_pdas(char **cmdline_p)
                memset(nodepdaindr[cnode], 0, sizeof(nodepda_t));
                memset(nodepdaindr[cnode]->phys_cpuid, -1,
                    sizeof(nodepdaindr[cnode]->phys_cpuid));
+               spin_lock_init(&nodepdaindr[cnode]->ptc_lock);
        }
 
        /*
@@ -532,8 +531,8 @@ void __init sn_cpu_init(void)
         */
        {
                u64 pio1[] = {SH1_PIO_WRITE_STATUS_0, 0, SH1_PIO_WRITE_STATUS_1, 0};
-               u64 pio2[] = {SH2_PIO_WRITE_STATUS_0, SH2_PIO_WRITE_STATUS_1,
-                       SH2_PIO_WRITE_STATUS_2, SH2_PIO_WRITE_STATUS_3};
+               u64 pio2[] = {SH2_PIO_WRITE_STATUS_0, SH2_PIO_WRITE_STATUS_2,
+                       SH2_PIO_WRITE_STATUS_1, SH2_PIO_WRITE_STATUS_3};
                u64 *pio;
                pio = is_shub1() ? pio1 : pio2;
                pda->pio_write_status_addr = (volatile unsigned long *) LOCAL_MMR_ADDR(pio[slice]);
index 96cb71d156820f9b33df9a07ea5049afe3bc77b9..3fa95065a4460e618a4cb4d4e1979ed830fc6cee 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2000-2005 Silicon Graphics, Inc. All rights reserved.
  */
 
 #include <asm/types.h>
@@ -11,7 +11,7 @@
 
 #define DEADLOCKBIT    SH_PIO_WRITE_STATUS_WRITE_DEADLOCK_SHFT
 #define WRITECOUNTMASK SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK
-#define ALIAS_OFFSET   (SH1_PIO_WRITE_STATUS_0_ALIAS-SH1_PIO_WRITE_STATUS_0)
+#define ALIAS_OFFSET   8
 
 
        .global sn2_ptc_deadlock_recovery_core
@@ -36,13 +36,15 @@ sn2_ptc_deadlock_recovery_core:
        extr.u  piowcphy=piowc,0,61;;   // Convert piowc to uncached physical address
        dep     piowcphy=-1,piowcphy,63,1
        movl    mask=WRITECOUNTMASK
+       mov     r8=r0
 
 1:
        add     scr2=ALIAS_OFFSET,piowc // Address of WRITE_STATUS alias register 
-       mov     scr1=7;;                // Clear DEADLOCK, WRITE_ERROR, MULTI_WRITE_ERROR
-       st8.rel [scr2]=scr1;;
+       ;;
+       ld8.acq scr1=[scr2];;
 
 5:     ld8.acq scr1=[piowc];;          // Wait for PIOs to complete.
+       hint    @pause
        and     scr2=scr1,mask;;        // mask of writecount bits
        cmp.ne  p6,p0=zeroval,scr2
 (p6)   br.cond.sptk 5b
@@ -57,6 +59,7 @@ sn2_ptc_deadlock_recovery_core:
        st8.rel [ptc0]=data0            // Write PTC0 & wait for completion.
 
 5:     ld8.acq scr1=[piowcphy];;       // Wait for PIOs to complete.
+       hint    @pause
        and     scr2=scr1,mask;;        // mask of writecount bits
        cmp.ne  p6,p0=zeroval,scr2
 (p6)   br.cond.sptk 5b;;
@@ -67,6 +70,7 @@ sn2_ptc_deadlock_recovery_core:
 (p7)   st8.rel [ptc1]=data1;;          // Now write PTC1.
 
 5:     ld8.acq scr1=[piowcphy];;       // Wait for PIOs to complete.
+       hint    @pause
        and     scr2=scr1,mask;;        // mask of writecount bits
        cmp.ne  p6,p0=zeroval,scr2
 (p6)   br.cond.sptk 5b
@@ -77,6 +81,7 @@ sn2_ptc_deadlock_recovery_core:
        srlz.i;;
        ////////////// END   PHYSICAL MODE ////////////////////
 
+(p8)   add     r8=1,r8
 (p8)   br.cond.spnt 1b;;               // Repeat if DEADLOCK occurred.
 
        br.ret.sptk     rp
index 7af05a7ac743a2416774ef75245f51b0d0b203a4..0a4ee50c302f94750f6c9f8876970e58b19d8b0e 100644 (file)
@@ -5,7 +5,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2000-2005 Silicon Graphics, Inc. All rights reserved.
  */
 
 #include <linux/init.h>
@@ -20,6 +20,8 @@
 #include <linux/module.h>
 #include <linux/bitops.h>
 #include <linux/nodemask.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 
 #include <asm/processor.h>
 #include <asm/irq.h>
 #include <asm/sn/nodepda.h>
 #include <asm/sn/rw_mmr.h>
 
-void sn2_ptc_deadlock_recovery(volatile unsigned long *, unsigned long data0, 
-       volatile unsigned long *, unsigned long data1);
+DEFINE_PER_CPU(struct ptc_stats, ptcstats);
+DECLARE_PER_CPU(struct ptc_stats, ptcstats);
 
 static  __cacheline_aligned DEFINE_SPINLOCK(sn2_global_ptc_lock);
 
-static unsigned long sn2_ptc_deadlock_count;
+void sn2_ptc_deadlock_recovery(short *, short, int, volatile unsigned long *, unsigned long data0,
+       volatile unsigned long *, unsigned long data1);
+
+#ifdef DEBUG_PTC
+/*
+ * ptctest:
+ *
+ *     xyz - 3 digit hex number:
+ *             x - Force PTC purges to use shub:
+ *                     0 - no force
+ *                     1 - force
+ *             y - interupt enable
+ *                     0 - disable interrupts
+ *                     1 - leave interuupts enabled
+ *             z - type of lock:
+ *                     0 - global lock
+ *                     1 - node local lock
+ *                     2 - no lock
+ *
+ *     Note: on shub1, only ptctest == 0 is supported. Don't try other values!
+ */
+
+static unsigned int sn2_ptctest = 0;
+
+static int __init ptc_test(char *str)
+{
+       get_option(&str, &sn2_ptctest);
+       return 1;
+}
+__setup("ptctest=", ptc_test);
+
+static inline int ptc_lock(unsigned long *flagp)
+{
+       unsigned long opt = sn2_ptctest & 255;
+
+       switch (opt) {
+       case 0x00:
+               spin_lock_irqsave(&sn2_global_ptc_lock, *flagp);
+               break;
+       case 0x01:
+               spin_lock_irqsave(&sn_nodepda->ptc_lock, *flagp);
+               break;
+       case 0x02:
+               local_irq_save(*flagp);
+               break;
+       case 0x10:
+               spin_lock(&sn2_global_ptc_lock);
+               break;
+       case 0x11:
+               spin_lock(&sn_nodepda->ptc_lock);
+               break;
+       case 0x12:
+               break;
+       default:
+               BUG();
+       }
+       return opt;
+}
+
+static inline void ptc_unlock(unsigned long flags, int opt)
+{
+       switch (opt) {
+       case 0x00:
+               spin_unlock_irqrestore(&sn2_global_ptc_lock, flags);
+               break;
+       case 0x01:
+               spin_unlock_irqrestore(&sn_nodepda->ptc_lock, flags);
+               break;
+       case 0x02:
+               local_irq_restore(flags);
+               break;
+       case 0x10:
+               spin_unlock(&sn2_global_ptc_lock);
+               break;
+       case 0x11:
+               spin_unlock(&sn_nodepda->ptc_lock);
+               break;
+       case 0x12:
+               break;
+       default:
+               BUG();
+       }
+}
+#else
+
+#define sn2_ptctest    0
+
+static inline int ptc_lock(unsigned long *flagp)
+{
+       spin_lock_irqsave(&sn2_global_ptc_lock, *flagp);
+       return 0;
+}
+
+static inline void ptc_unlock(unsigned long flags, int opt)
+{
+       spin_unlock_irqrestore(&sn2_global_ptc_lock, flags);
+}
+#endif
+
+struct ptc_stats {
+       unsigned long ptc_l;
+       unsigned long change_rid;
+       unsigned long shub_ptc_flushes;
+       unsigned long nodes_flushed;
+       unsigned long deadlocks;
+       unsigned long lock_itc_clocks;
+       unsigned long shub_itc_clocks;
+       unsigned long shub_itc_clocks_max;
+};
 
 static inline unsigned long wait_piowc(void)
 {
@@ -89,9 +199,9 @@ void
 sn2_global_tlb_purge(unsigned long start, unsigned long end,
                     unsigned long nbits)
 {
-       int i, shub1, cnode, mynasid, cpu, lcpu = 0, nasid, flushed = 0;
+       int i, opt, shub1, cnode, mynasid, cpu, lcpu = 0, nasid, flushed = 0;
        volatile unsigned long *ptc0, *ptc1;
-       unsigned long flags = 0, data0 = 0, data1 = 0;
+       unsigned long itc, itc2, flags, data0 = 0, data1 = 0;
        struct mm_struct *mm = current->active_mm;
        short nasids[MAX_NUMNODES], nix;
        nodemask_t nodes_flushed;
@@ -114,16 +224,19 @@ sn2_global_tlb_purge(unsigned long start, unsigned long end,
                        start += (1UL << nbits);
                } while (start < end);
                ia64_srlz_i();
+               __get_cpu_var(ptcstats).ptc_l++;
                preempt_enable();
                return;
        }
 
        if (atomic_read(&mm->mm_users) == 1) {
                flush_tlb_mm(mm);
+               __get_cpu_var(ptcstats).change_rid++;
                preempt_enable();
                return;
        }
 
+       itc = ia64_get_itc();
        nix = 0;
        for_each_node_mask(cnode, nodes_flushed)
                nasids[nix++] = cnodeid_to_nasid(cnode);
@@ -148,7 +261,12 @@ sn2_global_tlb_purge(unsigned long start, unsigned long end,
 
        mynasid = get_nasid();
 
-       spin_lock_irqsave(&sn2_global_ptc_lock, flags);
+       itc = ia64_get_itc();
+       opt = ptc_lock(&flags);
+       itc2 = ia64_get_itc();
+       __get_cpu_var(ptcstats).lock_itc_clocks += itc2 - itc;
+       __get_cpu_var(ptcstats).shub_ptc_flushes++;
+       __get_cpu_var(ptcstats).nodes_flushed += nix;
 
        do {
                if (shub1)
@@ -157,7 +275,7 @@ sn2_global_tlb_purge(unsigned long start, unsigned long end,
                        data0 = (data0 & ~SH2_PTC_ADDR_MASK) | (start & SH2_PTC_ADDR_MASK);
                for (i = 0; i < nix; i++) {
                        nasid = nasids[i];
-                       if (unlikely(nasid == mynasid)) {
+                       if ((!(sn2_ptctest & 3)) && unlikely(nasid == mynasid)) {
                                ia64_ptcga(start, nbits << 2);
                                ia64_srlz_i();
                        } else {
@@ -169,18 +287,22 @@ sn2_global_tlb_purge(unsigned long start, unsigned long end,
                                flushed = 1;
                        }
                }
-
                if (flushed
                    && (wait_piowc() &
-                       SH_PIO_WRITE_STATUS_WRITE_DEADLOCK_MASK)) {
-                       sn2_ptc_deadlock_recovery(ptc0, data0, ptc1, data1);
+                               (SH_PIO_WRITE_STATUS_WRITE_DEADLOCK_MASK))) {
+                       sn2_ptc_deadlock_recovery(nasids, nix, mynasid, ptc0, data0, ptc1, data1);
                }
 
                start += (1UL << nbits);
 
        } while (start < end);
 
-       spin_unlock_irqrestore(&sn2_global_ptc_lock, flags);
+       itc2 = ia64_get_itc() - itc2;
+       __get_cpu_var(ptcstats).shub_itc_clocks += itc2;
+       if (itc2 > __get_cpu_var(ptcstats).shub_itc_clocks_max)
+               __get_cpu_var(ptcstats).shub_itc_clocks_max = itc2;
+
+       ptc_unlock(flags, opt);
 
        preempt_enable();
 }
@@ -192,31 +314,29 @@ sn2_global_tlb_purge(unsigned long start, unsigned long end,
  * TLB flush transaction.  The recovery sequence is somewhat tricky & is
  * coded in assembly language.
  */
-void sn2_ptc_deadlock_recovery(volatile unsigned long *ptc0, unsigned long data0,
+void sn2_ptc_deadlock_recovery(short *nasids, short nix, int mynasid, volatile unsigned long *ptc0, unsigned long data0,
        volatile unsigned long *ptc1, unsigned long data1)
 {
        extern void sn2_ptc_deadlock_recovery_core(volatile unsigned long *, unsigned long,
                volatile unsigned long *, unsigned long, volatile unsigned long *, unsigned long);
-       int cnode, mycnode, nasid;
-       volatile unsigned long *piows;
-       volatile unsigned long zeroval;
+       short nasid, i;
+       unsigned long *piows, zeroval;
 
-       sn2_ptc_deadlock_count++;
+       __get_cpu_var(ptcstats).deadlocks++;
 
-       piows = pda->pio_write_status_addr;
+       piows = (unsigned long *) pda->pio_write_status_addr;
        zeroval = pda->pio_write_status_val;
 
-       mycnode = numa_node_id();
-
-       for_each_online_node(cnode) {
-               if (is_headless_node(cnode) || cnode == mycnode)
+       for (i=0; i < nix; i++) {
+               nasid = nasids[i];
+               if (!(sn2_ptctest & 3) && nasid == mynasid)
                        continue;
-               nasid = cnodeid_to_nasid(cnode);
                ptc0 = CHANGE_NASID(nasid, ptc0);
                if (ptc1)
                        ptc1 = CHANGE_NASID(nasid, ptc1);
                sn2_ptc_deadlock_recovery_core(ptc0, data0, ptc1, data1, piows, zeroval);
        }
+
 }
 
 /**
@@ -293,3 +413,93 @@ void sn2_send_IPI(int cpuid, int vector, int delivery_mode, int redirect)
 
        sn_send_IPI_phys(nasid, physid, vector, delivery_mode);
 }
+
+#ifdef CONFIG_PROC_FS
+
+#define PTC_BASENAME   "sgi_sn/ptc_statistics"
+
+static void *sn2_ptc_seq_start(struct seq_file *file, loff_t * offset)
+{
+       if (*offset < NR_CPUS)
+               return offset;
+       return NULL;
+}
+
+static void *sn2_ptc_seq_next(struct seq_file *file, void *data, loff_t * offset)
+{
+       (*offset)++;
+       if (*offset < NR_CPUS)
+               return offset;
+       return NULL;
+}
+
+static void sn2_ptc_seq_stop(struct seq_file *file, void *data)
+{
+}
+
+static int sn2_ptc_seq_show(struct seq_file *file, void *data)
+{
+       struct ptc_stats *stat;
+       int cpu;
+
+       cpu = *(loff_t *) data;
+
+       if (!cpu) {
+               seq_printf(file, "# ptc_l change_rid shub_ptc_flushes shub_nodes_flushed deadlocks lock_nsec shub_nsec shub_nsec_max\n");
+               seq_printf(file, "# ptctest %d\n", sn2_ptctest);
+       }
+
+       if (cpu < NR_CPUS && cpu_online(cpu)) {
+               stat = &per_cpu(ptcstats, cpu);
+               seq_printf(file, "cpu %d %ld %ld %ld %ld %ld %ld %ld %ld\n", cpu, stat->ptc_l,
+                               stat->change_rid, stat->shub_ptc_flushes, stat->nodes_flushed,
+                               stat->deadlocks,
+                               1000 * stat->lock_itc_clocks / per_cpu(cpu_info, cpu).cyc_per_usec,
+                               1000 * stat->shub_itc_clocks / per_cpu(cpu_info, cpu).cyc_per_usec,
+                               1000 * stat->shub_itc_clocks_max / per_cpu(cpu_info, cpu).cyc_per_usec);
+       }
+
+       return 0;
+}
+
+static struct seq_operations sn2_ptc_seq_ops = {
+       .start = sn2_ptc_seq_start,
+       .next = sn2_ptc_seq_next,
+       .stop = sn2_ptc_seq_stop,
+       .show = sn2_ptc_seq_show
+};
+
+int sn2_ptc_proc_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &sn2_ptc_seq_ops);
+}
+
+static struct file_operations proc_sn2_ptc_operations = {
+       .open = sn2_ptc_proc_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
+static struct proc_dir_entry *proc_sn2_ptc;
+
+static int __init sn2_ptc_init(void)
+{
+       if (!(proc_sn2_ptc = create_proc_entry(PTC_BASENAME, 0444, NULL))) {
+               printk(KERN_ERR "unable to create %s proc entry", PTC_BASENAME);
+               return -EINVAL;
+       }
+       proc_sn2_ptc->proc_fops = &proc_sn2_ptc_operations;
+       spin_lock_init(&sn2_global_ptc_lock);
+       return 0;
+}
+
+static void __exit sn2_ptc_exit(void)
+{
+       remove_proc_entry(PTC_BASENAME, NULL);
+}
+
+module_init(sn2_ptc_init);
+module_exit(sn2_ptc_exit);
+#endif /* CONFIG_PROC_FS */
+
index 833e700fdac93e4ada8c663c8c182b4d899cf05b..0513aacac8c13898a8e7edf57cfe9530b13e553d 100644 (file)
@@ -36,7 +36,6 @@
 #include <asm/topology.h>
 #include <asm/smp.h>
 #include <asm/semaphore.h>
-#include <asm/segment.h>
 #include <asm/uaccess.h>
 #include <asm/sal.h>
 #include <asm/sn/io.h>
@@ -59,7 +58,7 @@ static int sn_hwperf_enum_objects(int *nobj, struct sn_hwperf_object_info **ret)
        struct sn_hwperf_object_info *objbuf = NULL;
 
        if ((e = sn_hwperf_init()) < 0) {
-               printk("sn_hwperf_init failed: err %d\n", e);
+               printk(KERN_ERR "sn_hwperf_init failed: err %d\n", e);
                goto out;
        }
 
@@ -111,7 +110,7 @@ static int sn_hwperf_geoid_to_cnode(char *location)
        if (sn_hwperf_location_to_bpos(location, &rack, &bay, &slot, &slab))
                return -1;
 
-       for (cnode = 0; cnode < numionodes; cnode++) {
+       for_each_node(cnode) {
                geoid = cnodeid_get_geoid(cnode);
                module_id = geo_module(geoid);
                this_rack = MODULE_GET_RACK(module_id);
@@ -124,11 +123,13 @@ static int sn_hwperf_geoid_to_cnode(char *location)
                }
        }
 
-       return cnode < numionodes ? cnode : -1;
+       return node_possible(cnode) ? cnode : -1;
 }
 
 static int sn_hwperf_obj_to_cnode(struct sn_hwperf_object_info * obj)
 {
+       if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj))
+               BUG();
        if (!obj->sn_hwp_this_part)
                return -1;
        return sn_hwperf_geoid_to_cnode(obj->location);
@@ -174,31 +175,199 @@ static const char *sn_hwperf_get_slabname(struct sn_hwperf_object_info *obj,
        return slabname;
 }
 
-static void print_pci_topology(struct seq_file *s,
-       struct sn_hwperf_object_info *obj, int *ordinal,
-       u64 rack, u64 bay, u64 slot, u64 slab)
+static void print_pci_topology(struct seq_file *s)
+{
+       char *p;
+       size_t sz;
+       int e;
+
+       for (sz = PAGE_SIZE; sz < 16 * PAGE_SIZE; sz += PAGE_SIZE) {
+               if (!(p = (char *)kmalloc(sz, GFP_KERNEL)))
+                       break;
+               e = ia64_sn_ioif_get_pci_topology(__pa(p), sz);
+               if (e == SALRET_OK)
+                       seq_puts(s, p);
+               kfree(p);
+               if (e == SALRET_OK || e == SALRET_NOT_IMPLEMENTED)
+                       break;
+       }
+}
+
+static inline int sn_hwperf_has_cpus(cnodeid_t node)
+{
+       return node_online(node) && nr_cpus_node(node);
+}
+
+static inline int sn_hwperf_has_mem(cnodeid_t node)
+{
+       return node_online(node) && NODE_DATA(node)->node_present_pages;
+}
+
+static struct sn_hwperf_object_info *
+sn_hwperf_findobj_id(struct sn_hwperf_object_info *objbuf,
+       int nobj, int id)
 {
-       char *p1;
-       char *p2;
-       char *pg;
-
-       if (!(pg = (char *)get_zeroed_page(GFP_KERNEL)))
-               return; /* ignore */
-       if (ia64_sn_ioif_get_pci_topology(rack, bay, slot, slab,
-               __pa(pg), PAGE_SIZE) == SN_HWPERF_OP_OK) {
-               for (p1=pg; *p1 && p1 < pg + PAGE_SIZE;) {
-                       if (!(p2 = strchr(p1, '\n')))
+       int i;
+       struct sn_hwperf_object_info *p = objbuf;
+
+       for (i=0; i < nobj; i++, p++) {
+               if (p->id == id)
+                       return p;
+       }
+
+       return NULL;
+
+}
+
+static int sn_hwperf_get_nearest_node_objdata(struct sn_hwperf_object_info *objbuf,
+       int nobj, cnodeid_t node, cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
+{
+       int e;
+       struct sn_hwperf_object_info *nodeobj = NULL;
+       struct sn_hwperf_object_info *op;
+       struct sn_hwperf_object_info *dest;
+       struct sn_hwperf_object_info *router;
+       struct sn_hwperf_port_info ptdata[16];
+       int sz, i, j;
+       cnodeid_t c;
+       int found_mem = 0;
+       int found_cpu = 0;
+
+       if (!node_possible(node))
+               return -EINVAL;
+
+       if (sn_hwperf_has_cpus(node)) {
+               if (near_cpu_node)
+                       *near_cpu_node = node;
+               found_cpu++;
+       }
+
+       if (sn_hwperf_has_mem(node)) {
+               if (near_mem_node)
+                       *near_mem_node = node;
+               found_mem++;
+       }
+
+       if (found_cpu && found_mem)
+               return 0; /* trivially successful */
+
+       /* find the argument node object */
+       for (i=0, op=objbuf; i < nobj; i++, op++) {
+               if (!SN_HWPERF_IS_NODE(op) && !SN_HWPERF_IS_IONODE(op))
+                       continue;
+               if (node == sn_hwperf_obj_to_cnode(op)) {
+                       nodeobj = op;
+                       break;
+               }
+       }
+       if (!nodeobj) {
+               e = -ENOENT;
+               goto err;
+       }
+
+       /* get it's interconnect topology */
+       sz = op->ports * sizeof(struct sn_hwperf_port_info);
+       if (sz > sizeof(ptdata))
+               BUG();
+       e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
+                             SN_HWPERF_ENUM_PORTS, nodeobj->id, sz,
+                             (u64)&ptdata, 0, 0, NULL);
+       if (e != SN_HWPERF_OP_OK) {
+               e = -EINVAL;
+               goto err;
+       }
+
+       /* find nearest node with cpus and nearest memory */
+       for (router=NULL, j=0; j < op->ports; j++) {
+               dest = sn_hwperf_findobj_id(objbuf, nobj, ptdata[j].conn_id);
+               if (!dest || SN_HWPERF_FOREIGN(dest) ||
+                   !SN_HWPERF_IS_NODE(dest) || SN_HWPERF_IS_IONODE(dest)) {
+                       continue;
+               }
+               c = sn_hwperf_obj_to_cnode(dest);
+               if (!found_cpu && sn_hwperf_has_cpus(c)) {
+                       if (near_cpu_node)
+                               *near_cpu_node = c;
+                       found_cpu++;
+               }
+               if (!found_mem && sn_hwperf_has_mem(c)) {
+                       if (near_mem_node)
+                               *near_mem_node = c;
+                       found_mem++;
+               }
+               if (SN_HWPERF_IS_ROUTER(dest))
+                       router = dest;
+       }
+
+       if (router && (!found_cpu || !found_mem)) {
+               /* search for a node connected to the same router */
+               sz = router->ports * sizeof(struct sn_hwperf_port_info);
+               if (sz > sizeof(ptdata))
+                       BUG();
+               e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
+                                     SN_HWPERF_ENUM_PORTS, router->id, sz,
+                                     (u64)&ptdata, 0, 0, NULL);
+               if (e != SN_HWPERF_OP_OK) {
+                       e = -EINVAL;
+                       goto err;
+               }
+               for (j=0; j < router->ports; j++) {
+                       dest = sn_hwperf_findobj_id(objbuf, nobj,
+                               ptdata[j].conn_id);
+                       if (!dest || dest->id == node ||
+                           SN_HWPERF_FOREIGN(dest) ||
+                           !SN_HWPERF_IS_NODE(dest) ||
+                           SN_HWPERF_IS_IONODE(dest)) {
+                               continue;
+                       }
+                       c = sn_hwperf_obj_to_cnode(dest);
+                       if (!found_cpu && sn_hwperf_has_cpus(c)) {
+                               if (near_cpu_node)
+                                       *near_cpu_node = c;
+                               found_cpu++;
+                       }
+                       if (!found_mem && sn_hwperf_has_mem(c)) {
+                               if (near_mem_node)
+                                       *near_mem_node = c;
+                               found_mem++;
+                       }
+                       if (found_cpu && found_mem)
+                               break;
+               }
+       }
+
+       if (!found_cpu || !found_mem) {
+               /* resort to _any_ node with CPUs and memory */
+               for (i=0, op=objbuf; i < nobj; i++, op++) {
+                       if (SN_HWPERF_FOREIGN(op) ||
+                           SN_HWPERF_IS_IONODE(op) ||
+                           !SN_HWPERF_IS_NODE(op)) {
+                               continue;
+                       }
+                       c = sn_hwperf_obj_to_cnode(op);
+                       if (!found_cpu && sn_hwperf_has_cpus(c)) {
+                               if (near_cpu_node)
+                                       *near_cpu_node = c;
+                               found_cpu++;
+                       }
+                       if (!found_mem && sn_hwperf_has_mem(c)) {
+                               if (near_mem_node)
+                                       *near_mem_node = c;
+                               found_mem++;
+                       }
+                       if (found_cpu && found_mem)
                                break;
-                       *p2 = '\0';
-                       seq_printf(s, "pcibus %d %s-%s\n",
-                               *ordinal, obj->location, p1);
-                       (*ordinal)++;
-                       p1 = p2 + 1;
                }
        }
-       free_page((unsigned long)pg);
+
+       if (!found_cpu || !found_mem)
+               e = -ENODATA;
+
+err:
+       return e;
 }
 
+
 static int sn_topology_show(struct seq_file *s, void *d)
 {
        int sz;
@@ -215,7 +384,6 @@ static int sn_topology_show(struct seq_file *s, void *d)
        struct sn_hwperf_object_info *p;
        struct sn_hwperf_object_info *obj = d;  /* this object */
        struct sn_hwperf_object_info *objs = s->private; /* all objects */
-       int rack, bay, slot, slab;
        u8 shubtype;
        u8 system_size;
        u8 sharing_size;
@@ -225,7 +393,6 @@ static int sn_topology_show(struct seq_file *s, void *d)
        u8 region_size;
        u16 nasid_mask;
        int nasid_msb;
-       int pci_bus_ordinal = 0;
 
        if (obj == objs) {
                seq_printf(s, "# sn_topology version 2\n");
@@ -253,6 +420,8 @@ static int sn_topology_show(struct seq_file *s, void *d)
                        shubtype ? "shub2" : "shub1", 
                        (u64)nasid_mask << nasid_shift, nasid_msb, nasid_shift,
                        system_size, sharing_size, coher, region_size);
+
+               print_pci_topology(s);
        }
 
        if (SN_HWPERF_FOREIGN(obj)) {
@@ -272,11 +441,24 @@ static int sn_topology_show(struct seq_file *s, void *d)
        if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj))
                seq_putc(s, '\n');
        else {
+               cnodeid_t near_mem = -1;
+               cnodeid_t near_cpu = -1;
+
                seq_printf(s, ", nasid 0x%x", cnodeid_to_nasid(ordinal));
-               for (i=0; i < numionodes; i++) {
-                       seq_printf(s, i ? ":%d" : ", dist %d",
-                               node_distance(ordinal, i));
+
+               if (sn_hwperf_get_nearest_node_objdata(objs, sn_hwperf_obj_cnt,
+                       ordinal, &near_mem, &near_cpu) == 0) {
+                       seq_printf(s, ", near_mem_nodeid %d, near_cpu_nodeid %d",
+                               near_mem, near_cpu);
+               }
+
+               if (!SN_HWPERF_IS_IONODE(obj)) {
+                       for_each_online_node(i) {
+                               seq_printf(s, i ? ":%d" : ", dist %d",
+                                       node_distance(ordinal, i));
+                       }
                }
+
                seq_putc(s, '\n');
 
                /*
@@ -300,17 +482,6 @@ static int sn_topology_show(struct seq_file *s, void *d)
                                seq_putc(s, '\n');
                        }
                }
-
-               /*
-                * PCI busses attached to this node, if any
-                */
-               if (sn_hwperf_location_to_bpos(obj->location,
-                       &rack, &bay, &slot, &slab)) {
-                       /* export pci bus info */
-                       print_pci_topology(s, obj, &pci_bus_ordinal,
-                               rack, bay, slot, slab);
-
-               }
        }
 
        if (obj->ports) {
@@ -572,6 +743,8 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
                if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) {
                        memset(p, 0, a.sz);
                        for (i = 0; i < nobj; i++) {
+                               if (!SN_HWPERF_IS_NODE(objs + i))
+                                       continue;
                                node = sn_hwperf_obj_to_cnode(objs + i);
                                for_each_online_cpu(j) {
                                        if (node != cpu_to_node(j))
@@ -598,7 +771,7 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
 
        case SN_HWPERF_GET_NODE_NASID:
                if (a.sz != sizeof(u64) ||
-                  (node = a.arg) < 0 || node >= numionodes) {
+                  (node = a.arg) < 0 || !node_possible(node)) {
                        r = -EINVAL;
                        goto error;
                }
@@ -627,6 +800,14 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
                                vfree(objs);
                                goto error;
                        }
+
+                       if (!SN_HWPERF_IS_NODE(objs + i) &&
+                           !SN_HWPERF_IS_IONODE(objs + i)) {
+                               r = -ENOENT;
+                               vfree(objs);
+                               goto error;
+                       }
+
                        *(u64 *)p = (u64)sn_hwperf_obj_to_cnode(objs + i);
                        vfree(objs);
                }
@@ -692,6 +873,7 @@ static int sn_hwperf_init(void)
 
        /* single threaded, once-only initialization */
        down(&sn_hwperf_init_mutex);
+
        if (sn_hwperf_salheap) {
                up(&sn_hwperf_init_mutex);
                return e;
@@ -742,19 +924,6 @@ out:
                sn_hwperf_salheap = NULL;
                sn_hwperf_obj_cnt = 0;
        }
-
-       if (!e) {
-               /*
-                * Register a dynamic misc device for ioctl. Platforms
-                * supporting hotplug will create /dev/sn_hwperf, else
-                * user can to look up the minor number in /proc/misc.
-                */
-               if ((e = misc_register(&sn_hwperf_dev)) != 0) {
-                       printk(KERN_ERR "sn_hwperf_init: misc register "
-                              "for \"sn_hwperf\" failed, err %d\n", e);
-               }
-       }
-
        up(&sn_hwperf_init_mutex);
        return e;
 }
@@ -782,3 +951,41 @@ int sn_topology_release(struct inode *inode, struct file *file)
        vfree(seq->private);
        return seq_release(inode, file);
 }
+
+int sn_hwperf_get_nearest_node(cnodeid_t node,
+       cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
+{
+       int e;
+       int nobj;
+       struct sn_hwperf_object_info *objbuf;
+
+       if ((e = sn_hwperf_enum_objects(&nobj, &objbuf)) == 0) {
+               e = sn_hwperf_get_nearest_node_objdata(objbuf, nobj,
+                       node, near_mem_node, near_cpu_node);
+               vfree(objbuf);
+       }
+
+       return e;
+}
+
+static int __devinit sn_hwperf_misc_register_init(void)
+{
+       int e;
+
+       sn_hwperf_init();
+
+       /*
+        * Register a dynamic misc device for hwperf ioctls. Platforms
+        * supporting hotplug will create /dev/sn_hwperf, else user
+        * can to look up the minor number in /proc/misc.
+        */
+       if ((e = misc_register(&sn_hwperf_dev)) != 0) {
+               printk(KERN_ERR "sn_hwperf_misc_register_init: failed to "
+               "register misc device for \"%s\"\n", sn_hwperf_dev.name);
+       }
+
+       return e;
+}
+
+device_initcall(sn_hwperf_misc_register_init); /* after misc_init() */
+EXPORT_SYMBOL(sn_hwperf_get_nearest_node);
index 6a80fca807b9c59119ca08367da12e1d7ceca651..51bf82720d994188faf55a74b68888908f78c3e1 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2000-2005 Silicon Graphics, Inc. All rights reserved.
  */
 #include <linux/config.h>
 #include <asm/uaccess.h>
@@ -15,7 +15,7 @@
 
 static int partition_id_show(struct seq_file *s, void *p)
 {
-       seq_printf(s, "%d\n", sn_local_partid());
+       seq_printf(s, "%d\n", sn_partition_id);
        return 0;
 }
 
index cde7375390b0db4e4160ffaaf44d6bc44dfb0763..adf5db2e2afeb4e2742457b40c062e0546e42972 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *
  *
- * Copyright (c) 2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2005 Silicon Graphics, Inc.  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 
@@ -50,14 +50,16 @@ void sn_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                             LED_CPU_HEARTBEAT, LED_CPU_HEARTBEAT);
        }
 
-       if (enable_shub_wars_1_1()) {
-               /* Bugfix code for SHUB 1.1 */
-               if (pda->pio_shub_war_cam_addr)
-                       *pda->pio_shub_war_cam_addr = 0x8000000000000010UL;
+       if (is_shub1()) {
+               if (enable_shub_wars_1_1()) {
+                       /* Bugfix code for SHUB 1.1 */
+                       if (pda->pio_shub_war_cam_addr)
+                               *pda->pio_shub_war_cam_addr = 0x8000000000000010UL;
+               }
+               if (pda->sn_lb_int_war_ticks == 0)
+                       sn_lb_int_war_check();
+               pda->sn_lb_int_war_ticks++;
+               if (pda->sn_lb_int_war_ticks >= SN_LB_INT_WAR_INTERVAL)
+                       pda->sn_lb_int_war_ticks = 0;
        }
-       if (pda->sn_lb_int_war_ticks == 0)
-               sn_lb_int_war_check();
-       pda->sn_lb_int_war_ticks++;
-       if (pda->sn_lb_int_war_ticks >= SN_LB_INT_WAR_INTERVAL)
-               pda->sn_lb_int_war_ticks = 0;
 }
index 2f915bce25f9e6734040ccb0611b2236acac12ee..321576b1b425f8916858c875058326148216fa8f 100644 (file)
@@ -7,4 +7,4 @@
 #
 # Makefile for the sn pci general routines.
 
-obj-y := pci_dma.o tioca_provider.o pcibr/ 
+obj-y := pci_dma.o tioca_provider.o tioce_provider.o pcibr/
index b058dc2a0b9d3dfb7a17fe73f2ae35f72266ebd3..34093476e9652fc03938edeadeddb4596d439d34 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2001-2005 Silicon Graphics, Inc. All rights reserved.
  */
 
 #include <linux/types.h>
@@ -215,8 +215,8 @@ void sn_dma_flush(uint64_t addr)
        int is_tio;
        int wid_num;
        int i, j;
-       int bwin;
        uint64_t flags;
+       uint64_t itte;
        struct hubdev_info *hubinfo;
        volatile struct sn_flush_device_list *p;
        struct sn_flush_nasid_entry *flush_nasid_list;
@@ -233,31 +233,36 @@ void sn_dma_flush(uint64_t addr)
        if (!hubinfo) {
                BUG();
        }
-       is_tio = (nasid & 1);
-       if (is_tio) {
-               wid_num = TIO_SWIN_WIDGETNUM(addr);
-               bwin = TIO_BWIN_WINDOWNUM(addr);
-       } else {
-               wid_num = SWIN_WIDGETNUM(addr);
-               bwin = BWIN_WINDOWNUM(addr);
-       }
 
        flush_nasid_list = &hubinfo->hdi_flush_nasid_list;
        if (flush_nasid_list->widget_p == NULL)
                return;
-       if (bwin > 0) {
-               uint64_t itte = flush_nasid_list->iio_itte[bwin];
 
-               if (is_tio) {
-                       wid_num = (itte >> TIO_ITTE_WIDGET_SHIFT) &
-                           TIO_ITTE_WIDGET_MASK;
-               } else {
-                       wid_num = (itte >> IIO_ITTE_WIDGET_SHIFT) &
-                           IIO_ITTE_WIDGET_MASK;
-               }
+       is_tio = (nasid & 1);
+       if (is_tio) {
+               int itte_index;
+
+               if (TIO_HWIN(addr))
+                       itte_index = 0;
+               else if (TIO_BWIN_WINDOWNUM(addr))
+                       itte_index = TIO_BWIN_WINDOWNUM(addr);
+               else
+                       itte_index = -1;
+
+               if (itte_index >= 0) {
+                       itte = flush_nasid_list->iio_itte[itte_index];
+                       if (! TIO_ITTE_VALID(itte))
+                               return;
+                       wid_num = TIO_ITTE_WIDGET(itte);
+               } else
+                       wid_num = TIO_SWIN_WIDGETNUM(addr);
+       } else {
+               if (BWIN_WINDOWNUM(addr)) {
+                       itte = flush_nasid_list->iio_itte[BWIN_WINDOWNUM(addr)];
+                       wid_num = IIO_ITTE_WIDGET(itte);
+               } else
+                       wid_num = SWIN_WIDGETNUM(addr);
        }
-       if (flush_nasid_list->widget_p == NULL)
-               return;
        if (flush_nasid_list->widget_p[wid_num] == NULL)
                return;
        p = &flush_nasid_list->widget_p[wid_num][0];
@@ -283,10 +288,16 @@ void sn_dma_flush(uint64_t addr)
        /*
         * For TIOCP use the Device(x) Write Request Buffer Flush Bridge
         * register since it ensures the data has entered the coherence
-        * domain, unlike PIC
+        * domain, unlike PIC.
         */
        if (is_tio) {
-               uint32_t tio_id = REMOTE_HUB_L(nasid, TIO_NODE_ID);
+               /*
+                * Note:  devices behind TIOCE should never be matched in the
+                * above code, and so the following code is PIC/CP centric.
+                * If CE ever needs the sn_dma_flush mechanism, we will have
+                * to account for that here and in tioce_bus_fixup().
+                */
+               uint32_t tio_id = HUB_L(TIO_IOSPACE_ADDR(nasid, TIO_NODE_ID));
                uint32_t revnum = XWIDGET_PART_REV_NUM(tio_id);
 
                /* TIOCP BRINGUP WAR (PV907516): Don't write buffer flush reg */
@@ -306,7 +317,8 @@ void sn_dma_flush(uint64_t addr)
                *(volatile uint32_t *)(p->sfdl_force_int_addr) = 1;
 
                /* wait for the interrupt to come back. */
-               while (*(p->sfdl_flush_addr) != 0x10f) ;
+               while (*(p->sfdl_flush_addr) != 0x10f)
+                       cpu_relax();
 
                /* okay, everything is synched up. */
                spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags);
index b95e928636a1bb6c4417e23381b9bfd27992c6fc..7b03b8084ffc441d90d3725e601816f6b856d741 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/sn/pcibus_provider_defs.h>
 #include <asm/sn/pcidev.h>
 #include <asm/sn/sn_sal.h>
+#include <asm/sn/sn2/sn_hwperf.h>
 #include "xtalk/xwidgetdev.h"
 #include "xtalk/hubdev.h"
 
@@ -60,7 +61,7 @@ static int sal_pcibr_error_interrupt(struct pcibus_info *soft)
        ret_stuff.status = 0;
        ret_stuff.v0 = 0;
 
-       segment = 0;
+       segment = soft->pbi_buscommon.bs_persist_segment;
        busnum = soft->pbi_buscommon.bs_persist_busnum;
        SAL_CALL_NOLOCK(ret_stuff,
                        (u64) SN_SAL_IOIF_ERROR_INTERRUPT,
@@ -88,6 +89,7 @@ void *
 pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *controller)
 {
        int nasid, cnode, j;
+       cnodeid_t near_cnode;
        struct hubdev_info *hubdev_info;
        struct pcibus_info *soft;
        struct sn_flush_device_list *sn_flush_device_list;
@@ -115,7 +117,7 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont
        /*
         * register the bridge's error interrupt handler
         */
-       if (request_irq(SGI_PCIBR_ERROR, (void *)pcibr_error_intr_handler,
+       if (request_irq(SGI_PCIASIC_ERROR, (void *)pcibr_error_intr_handler,
                        SA_SHIRQ, "PCIBR error", (void *)(soft))) {
                printk(KERN_WARNING
                       "pcibr cannot allocate interrupt for error handler\n");
@@ -142,9 +144,12 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont
                             j++, sn_flush_device_list++) {
                                if (sn_flush_device_list->sfdl_slot == -1)
                                        continue;
-                               if (sn_flush_device_list->
-                                   sfdl_persistent_busnum ==
-                                   soft->pbi_buscommon.bs_persist_busnum)
+                               if ((sn_flush_device_list->
+                                    sfdl_persistent_segment ==
+                                    soft->pbi_buscommon.bs_persist_segment) &&
+                                    (sn_flush_device_list->
+                                    sfdl_persistent_busnum ==
+                                    soft->pbi_buscommon.bs_persist_busnum))
                                        sn_flush_device_list->sfdl_pcibus_info =
                                            soft;
                        }
@@ -158,12 +163,18 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont
        memset(soft->pbi_int_ate_resource.ate, 0,
               (soft->pbi_int_ate_size * sizeof(uint64_t)));
 
-       if (prom_bussoft->bs_asic_type == PCIIO_ASIC_TYPE_TIOCP)
-               /*
-                * TIO PCI Bridge with no closest node information.
-                * FIXME: Find another way to determine the closest node
-                */
-               controller->node = -1;
+       if (prom_bussoft->bs_asic_type == PCIIO_ASIC_TYPE_TIOCP) {
+               /* TIO PCI Bridge: find nearest node with CPUs */
+               int e = sn_hwperf_get_nearest_node(cnode, NULL, &near_cnode);
+
+               if (e < 0) {
+                       near_cnode = (cnodeid_t)-1; /* use any node */
+                       printk(KERN_WARNING "pcibr_bus_fixup: failed to find "
+                               "near node with CPUs to TIO node %d, err=%d\n",
+                               cnode, e);
+               }
+               controller->node = near_cnode;
+       }
        else
                controller->node = cnode;
        return soft;
@@ -175,6 +186,9 @@ void pcibr_force_interrupt(struct sn_irq_info *sn_irq_info)
        struct pcibus_info *pcibus_info;
        int bit = sn_irq_info->irq_int_bit;
 
+       if (! sn_irq_info->irq_bridge)
+               return;
+
        pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
        if (pcidev_info) {
                pcibus_info =
@@ -184,7 +198,7 @@ void pcibr_force_interrupt(struct sn_irq_info *sn_irq_info)
        }
 }
 
-void pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info)
+void pcibr_target_interrupt(struct sn_irq_info *sn_irq_info)
 {
        struct pcidev_info *pcidev_info;
        struct pcibus_info *pcibus_info;
@@ -219,6 +233,8 @@ struct sn_pcibus_provider pcibr_provider = {
        .dma_map_consistent = pcibr_dma_map_consistent,
        .dma_unmap = pcibr_dma_unmap,
        .bus_fixup = pcibr_bus_fixup,
+       .force_interrupt = pcibr_force_interrupt,
+       .target_interrupt = pcibr_target_interrupt
 };
 
 int
index 5d76a758146597d45d822474789223db97dcb4bc..ea09c12f02586de9d6bc49507951fb919420ea70 100644 (file)
@@ -559,7 +559,7 @@ tioca_error_intr_handler(int irq, void *arg, struct pt_regs *pt)
        ret_stuff.status = 0;
        ret_stuff.v0 = 0;
 
-       segment = 0;
+       segment = soft->ca_common.bs_persist_segment;
        busnum = soft->ca_common.bs_persist_busnum;
 
        SAL_CALL_NOLOCK(ret_stuff,
@@ -622,7 +622,8 @@ tioca_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont
            nasid_to_cnodeid(tioca_common->ca_closest_nasid);
        tioca_common->ca_kernel_private = (uint64_t) tioca_kern;
 
-       bus = pci_find_bus(0, tioca_common->ca_common.bs_persist_busnum);
+       bus = pci_find_bus(tioca_common->ca_common.bs_persist_segment,
+               tioca_common->ca_common.bs_persist_busnum);
        BUG_ON(!bus);
        tioca_kern->ca_devices = &bus->devices;
 
@@ -656,6 +657,8 @@ static struct sn_pcibus_provider tioca_pci_interfaces = {
        .dma_map_consistent = tioca_dma_map,
        .dma_unmap = tioca_dma_unmap,
        .bus_fixup = tioca_bus_fixup,
+       .force_interrupt = NULL,
+       .target_interrupt = NULL
 };
 
 /**
diff --git a/arch/ia64/sn/pci/tioce_provider.c b/arch/ia64/sn/pci/tioce_provider.c
new file mode 100644 (file)
index 0000000..8e75db2
--- /dev/null
@@ -0,0 +1,771 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/pcidev.h>
+#include <asm/sn/pcibus_provider_defs.h>
+#include <asm/sn/tioce_provider.h>
+
+/**
+ * Bus address ranges for the 5 flavors of TIOCE DMA
+ */
+
+#define TIOCE_D64_MIN  0x8000000000000000UL
+#define TIOCE_D64_MAX  0xffffffffffffffffUL
+#define TIOCE_D64_ADDR(a)      ((a) >= TIOCE_D64_MIN)
+
+#define TIOCE_D32_MIN  0x0000000080000000UL
+#define TIOCE_D32_MAX  0x00000000ffffffffUL
+#define TIOCE_D32_ADDR(a)      ((a) >= TIOCE_D32_MIN && (a) <= TIOCE_D32_MAX)
+
+#define TIOCE_M32_MIN  0x0000000000000000UL
+#define TIOCE_M32_MAX  0x000000007fffffffUL
+#define TIOCE_M32_ADDR(a)      ((a) >= TIOCE_M32_MIN && (a) <= TIOCE_M32_MAX)
+
+#define TIOCE_M40_MIN  0x0000004000000000UL
+#define TIOCE_M40_MAX  0x0000007fffffffffUL
+#define TIOCE_M40_ADDR(a)      ((a) >= TIOCE_M40_MIN && (a) <= TIOCE_M40_MAX)
+
+#define TIOCE_M40S_MIN 0x0000008000000000UL
+#define TIOCE_M40S_MAX 0x000000ffffffffffUL
+#define TIOCE_M40S_ADDR(a)     ((a) >= TIOCE_M40S_MIN && (a) <= TIOCE_M40S_MAX)
+
+/*
+ * ATE manipulation macros.
+ */
+
+#define ATE_PAGESHIFT(ps)      (__ffs(ps))
+#define ATE_PAGEMASK(ps)       ((ps)-1)
+
+#define ATE_PAGE(x, ps) ((x) >> ATE_PAGESHIFT(ps))
+#define ATE_NPAGES(start, len, pagesize) \
+       (ATE_PAGE((start)+(len)-1, pagesize) - ATE_PAGE(start, pagesize) + 1)
+
+#define ATE_VALID(ate) ((ate) & (1UL << 63))
+#define ATE_MAKE(addr, ps) (((addr) & ~ATE_PAGEMASK(ps)) | (1UL << 63))
+
+/*
+ * Flavors of ate-based mapping supported by tioce_alloc_map()
+ */
+
+#define TIOCE_ATE_M32  1
+#define TIOCE_ATE_M40  2
+#define TIOCE_ATE_M40S 3
+
+#define KB(x)  ((x) << 10)
+#define MB(x)  ((x) << 20)
+#define GB(x)  ((x) << 30)
+
+/**
+ * tioce_dma_d64 - create a DMA mapping using 64-bit direct mode
+ * @ct_addr: system coretalk address
+ *
+ * Map @ct_addr into 64-bit CE bus space.  No device context is necessary
+ * and no CE mapping are consumed.
+ *
+ * Bits 53:0 come from the coretalk address.  The remaining bits are set as
+ * follows:
+ *
+ * 63    - must be 1 to indicate d64 mode to CE hardware
+ * 62    - barrier bit ... controlled with tioce_dma_barrier()
+ * 61    - 0 since this is not an MSI transaction
+ * 60:54 - reserved, MBZ
+ */
+static uint64_t
+tioce_dma_d64(unsigned long ct_addr)
+{
+       uint64_t bus_addr;
+
+       bus_addr = ct_addr | (1UL << 63);
+
+       return bus_addr;
+}
+
+/**
+ * pcidev_to_tioce - return misc ce related pointers given a pci_dev
+ * @pci_dev: pci device context
+ * @base: ptr to store struct tioce_mmr * for the CE holding this device
+ * @kernel: ptr to store struct tioce_kernel * for the CE holding this device
+ * @port: ptr to store the CE port number that this device is on
+ *
+ * Return pointers to various CE-related structures for the CE upstream of
+ * @pci_dev.
+ */
+static inline void
+pcidev_to_tioce(struct pci_dev *pdev, struct tioce **base,
+               struct tioce_kernel **kernel, int *port)
+{
+       struct pcidev_info *pcidev_info;
+       struct tioce_common *ce_common;
+       struct tioce_kernel *ce_kernel;
+
+       pcidev_info = SN_PCIDEV_INFO(pdev);
+       ce_common = (struct tioce_common *)pcidev_info->pdi_pcibus_info;
+       ce_kernel = (struct tioce_kernel *)ce_common->ce_kernel_private;
+
+       if (base)
+               *base = (struct tioce *)ce_common->ce_pcibus.bs_base;
+       if (kernel)
+               *kernel = ce_kernel;
+
+       /*
+        * we use port as a zero-based value internally, even though the
+        * documentation is 1-based.
+        */
+       if (port)
+               *port =
+                   (pdev->bus->number < ce_kernel->ce_port1_secondary) ? 0 : 1;
+}
+
+/**
+ * tioce_alloc_map - Given a coretalk address, map it to pcie bus address
+ * space using one of the various ATE-based address modes.
+ * @ce_kern: tioce context
+ * @type: map mode to use
+ * @port: 0-based port that the requesting device is downstream of
+ * @ct_addr: the coretalk address to map
+ * @len: number of bytes to map
+ *
+ * Given the addressing type, set up various paramaters that define the
+ * ATE pool to use.  Search for a contiguous block of entries to cover the
+ * length, and if enough resources exist, fill in the ATE's and construct a
+ * tioce_dmamap struct to track the mapping.
+ */
+static uint64_t
+tioce_alloc_map(struct tioce_kernel *ce_kern, int type, int port,
+               uint64_t ct_addr, int len)
+{
+       int i;
+       int j;
+       int first;
+       int last;
+       int entries;
+       int nates;
+       int pagesize;
+       uint64_t *ate_shadow;
+       uint64_t *ate_reg;
+       uint64_t addr;
+       struct tioce *ce_mmr;
+       uint64_t bus_base;
+       struct tioce_dmamap *map;
+
+       ce_mmr = (struct tioce *)ce_kern->ce_common->ce_pcibus.bs_base;
+
+       switch (type) {
+       case TIOCE_ATE_M32:
+               /*
+                * The first 64 entries of the ate3240 pool are dedicated to
+                * super-page (TIOCE_ATE_M40S) mode.
+                */
+               first = 64;
+               entries = TIOCE_NUM_M3240_ATES - 64;
+               ate_shadow = ce_kern->ce_ate3240_shadow;
+               ate_reg = ce_mmr->ce_ure_ate3240;
+               pagesize = ce_kern->ce_ate3240_pagesize;
+               bus_base = TIOCE_M32_MIN;
+               break;
+       case TIOCE_ATE_M40:
+               first = 0;
+               entries = TIOCE_NUM_M40_ATES;
+               ate_shadow = ce_kern->ce_ate40_shadow;
+               ate_reg = ce_mmr->ce_ure_ate40;
+               pagesize = MB(64);
+               bus_base = TIOCE_M40_MIN;
+               break;
+       case TIOCE_ATE_M40S:
+               /*
+                * ate3240 entries 0-31 are dedicated to port1 super-page
+                * mappings.  ate3240 entries 32-63 are dedicated to port2.
+                */
+               first = port * 32;
+               entries = 32;
+               ate_shadow = ce_kern->ce_ate3240_shadow;
+               ate_reg = ce_mmr->ce_ure_ate3240;
+               pagesize = GB(16);
+               bus_base = TIOCE_M40S_MIN;
+               break;
+       default:
+               return 0;
+       }
+
+       nates = ATE_NPAGES(ct_addr, len, pagesize);
+       if (nates > entries)
+               return 0;
+
+       last = first + entries - nates;
+       for (i = first; i <= last; i++) {
+               if (ATE_VALID(ate_shadow[i]))
+                       continue;
+
+               for (j = i; j < i + nates; j++)
+                       if (ATE_VALID(ate_shadow[j]))
+                               break;
+
+               if (j >= i + nates)
+                       break;
+       }
+
+       if (i > last)
+               return 0;
+
+       map = kcalloc(1, sizeof(struct tioce_dmamap), GFP_ATOMIC);
+       if (!map)
+               return 0;
+
+       addr = ct_addr;
+       for (j = 0; j < nates; j++) {
+               uint64_t ate;
+
+               ate = ATE_MAKE(addr, pagesize);
+               ate_shadow[i + j] = ate;
+               ate_reg[i + j] = ate;
+               addr += pagesize;
+       }
+
+       map->refcnt = 1;
+       map->nbytes = nates * pagesize;
+       map->ct_start = ct_addr & ~ATE_PAGEMASK(pagesize);
+       map->pci_start = bus_base + (i * pagesize);
+       map->ate_hw = &ate_reg[i];
+       map->ate_shadow = &ate_shadow[i];
+       map->ate_count = nates;
+
+       list_add(&map->ce_dmamap_list, &ce_kern->ce_dmamap_list);
+
+       return (map->pci_start + (ct_addr - map->ct_start));
+}
+
+/**
+ * tioce_dma_d32 - create a DMA mapping using 32-bit direct mode
+ * @pdev: linux pci_dev representing the function
+ * @paddr: system physical address
+ *
+ * Map @paddr into 32-bit bus space of the CE associated with @pcidev_info.
+ */
+static uint64_t
+tioce_dma_d32(struct pci_dev *pdev, uint64_t ct_addr)
+{
+       int dma_ok;
+       int port;
+       struct tioce *ce_mmr;
+       struct tioce_kernel *ce_kern;
+       uint64_t ct_upper;
+       uint64_t ct_lower;
+       dma_addr_t bus_addr;
+
+       ct_upper = ct_addr & ~0x3fffffffUL;
+       ct_lower = ct_addr & 0x3fffffffUL;
+
+       pcidev_to_tioce(pdev, &ce_mmr, &ce_kern, &port);
+
+       if (ce_kern->ce_port[port].dirmap_refcnt == 0) {
+               volatile uint64_t tmp;
+
+               ce_kern->ce_port[port].dirmap_shadow = ct_upper;
+               ce_mmr->ce_ure_dir_map[port] = ct_upper;
+               tmp = ce_mmr->ce_ure_dir_map[port];
+               dma_ok = 1;
+       } else
+               dma_ok = (ce_kern->ce_port[port].dirmap_shadow == ct_upper);
+
+       if (dma_ok) {
+               ce_kern->ce_port[port].dirmap_refcnt++;
+               bus_addr = TIOCE_D32_MIN + ct_lower;
+       } else
+               bus_addr = 0;
+
+       return bus_addr;
+}
+
+/**
+ * tioce_dma_barrier - swizzle a TIOCE bus address to include or exclude
+ * the barrier bit.
+ * @bus_addr:  bus address to swizzle
+ *
+ * Given a TIOCE bus address, set the appropriate bit to indicate barrier
+ * attributes.
+ */
+static uint64_t
+tioce_dma_barrier(uint64_t bus_addr, int on)
+{
+       uint64_t barrier_bit;
+
+       /* barrier not supported in M40/M40S mode */
+       if (TIOCE_M40_ADDR(bus_addr) || TIOCE_M40S_ADDR(bus_addr))
+               return bus_addr;
+
+       if (TIOCE_D64_ADDR(bus_addr))
+               barrier_bit = (1UL << 62);
+       else                    /* must be m32 or d32 */
+               barrier_bit = (1UL << 30);
+
+       return (on) ? (bus_addr | barrier_bit) : (bus_addr & ~barrier_bit);
+}
+
+/**
+ * tioce_dma_unmap - release CE mapping resources
+ * @pdev: linux pci_dev representing the function
+ * @bus_addr: bus address returned by an earlier tioce_dma_map
+ * @dir: mapping direction (unused)
+ *
+ * Locate mapping resources associated with @bus_addr and release them.
+ * For mappings created using the direct modes there are no resources
+ * to release.
+ */
+void
+tioce_dma_unmap(struct pci_dev *pdev, dma_addr_t bus_addr, int dir)
+{
+       int i;
+       int port;
+       struct tioce_kernel *ce_kern;
+       struct tioce *ce_mmr;
+       unsigned long flags;
+
+       bus_addr = tioce_dma_barrier(bus_addr, 0);
+       pcidev_to_tioce(pdev, &ce_mmr, &ce_kern, &port);
+
+       /* nothing to do for D64 */
+
+       if (TIOCE_D64_ADDR(bus_addr))
+               return;
+
+       spin_lock_irqsave(&ce_kern->ce_lock, flags);
+
+       if (TIOCE_D32_ADDR(bus_addr)) {
+               if (--ce_kern->ce_port[port].dirmap_refcnt == 0) {
+                       ce_kern->ce_port[port].dirmap_shadow = 0;
+                       ce_mmr->ce_ure_dir_map[port] = 0;
+               }
+       } else {
+               struct tioce_dmamap *map;
+
+               list_for_each_entry(map, &ce_kern->ce_dmamap_list,
+                                   ce_dmamap_list) {
+                       uint64_t last;
+
+                       last = map->pci_start + map->nbytes - 1;
+                       if (bus_addr >= map->pci_start && bus_addr <= last)
+                               break;
+               }
+
+               if (&map->ce_dmamap_list == &ce_kern->ce_dmamap_list) {
+                       printk(KERN_WARNING
+                              "%s:  %s - no map found for bus_addr 0x%lx\n",
+                              __FUNCTION__, pci_name(pdev), bus_addr);
+               } else if (--map->refcnt == 0) {
+                       for (i = 0; i < map->ate_count; i++) {
+                               map->ate_shadow[i] = 0;
+                               map->ate_hw[i] = 0;
+                       }
+
+                       list_del(&map->ce_dmamap_list);
+                       kfree(map);
+               }
+       }
+
+       spin_unlock_irqrestore(&ce_kern->ce_lock, flags);
+}
+
+/**
+ * tioce_do_dma_map - map pages for PCI DMA
+ * @pdev: linux pci_dev representing the function
+ * @paddr: host physical address to map
+ * @byte_count: bytes to map
+ *
+ * This is the main wrapper for mapping host physical pages to CE PCI space.
+ * The mapping mode used is based on the device's dma_mask.
+ */
+static uint64_t
+tioce_do_dma_map(struct pci_dev *pdev, uint64_t paddr, size_t byte_count,
+                int barrier)
+{
+       unsigned long flags;
+       uint64_t ct_addr;
+       uint64_t mapaddr = 0;
+       struct tioce_kernel *ce_kern;
+       struct tioce_dmamap *map;
+       int port;
+       uint64_t dma_mask;
+
+       dma_mask = (barrier) ? pdev->dev.coherent_dma_mask : pdev->dma_mask;
+
+       /* cards must be able to address at least 31 bits */
+       if (dma_mask < 0x7fffffffUL)
+               return 0;
+
+       ct_addr = PHYS_TO_TIODMA(paddr);
+
+       /*
+        * If the device can generate 64 bit addresses, create a D64 map.
+        * Since this should never fail, bypass the rest of the checks.
+        */
+       if (dma_mask == ~0UL) {
+               mapaddr = tioce_dma_d64(ct_addr);
+               goto dma_map_done;
+       }
+
+       pcidev_to_tioce(pdev, NULL, &ce_kern, &port);
+
+       spin_lock_irqsave(&ce_kern->ce_lock, flags);
+
+       /*
+        * D64 didn't work ... See if we have an existing map that covers
+        * this address range.  Must account for devices dma_mask here since
+        * an existing map might have been done in a mode using more pci
+        * address bits than this device can support.
+        */
+       list_for_each_entry(map, &ce_kern->ce_dmamap_list, ce_dmamap_list) {
+               uint64_t last;
+
+               last = map->ct_start + map->nbytes - 1;
+               if (ct_addr >= map->ct_start &&
+                   ct_addr + byte_count - 1 <= last &&
+                   map->pci_start <= dma_mask) {
+                       map->refcnt++;
+                       mapaddr = map->pci_start + (ct_addr - map->ct_start);
+                       break;
+               }
+       }
+
+       /*
+        * If we don't have a map yet, and the card can generate 40
+        * bit addresses, try the M40/M40S modes.  Note these modes do not
+        * support a barrier bit, so if we need a consistent map these
+        * won't work.
+        */
+       if (!mapaddr && !barrier && dma_mask >= 0xffffffffffUL) {
+               /*
+                * We have two options for 40-bit mappings:  16GB "super" ATE's
+                * and 64MB "regular" ATE's.  We'll try both if needed for a
+                * given mapping but which one we try first depends on the
+                * size.  For requests >64MB, prefer to use a super page with
+                * regular as the fallback. Otherwise, try in the reverse order.
+                */
+
+               if (byte_count > MB(64)) {
+                       mapaddr = tioce_alloc_map(ce_kern, TIOCE_ATE_M40S,
+                                                 port, ct_addr, byte_count);
+                       if (!mapaddr)
+                               mapaddr =
+                                   tioce_alloc_map(ce_kern, TIOCE_ATE_M40, -1,
+                                                   ct_addr, byte_count);
+               } else {
+                       mapaddr = tioce_alloc_map(ce_kern, TIOCE_ATE_M40, -1,
+                                                 ct_addr, byte_count);
+                       if (!mapaddr)
+                               mapaddr =
+                                   tioce_alloc_map(ce_kern, TIOCE_ATE_M40S,
+                                                   port, ct_addr, byte_count);
+               }
+       }
+
+       /*
+        * 32-bit direct is the next mode to try
+        */
+       if (!mapaddr && dma_mask >= 0xffffffffUL)
+               mapaddr = tioce_dma_d32(pdev, ct_addr);
+
+       /*
+        * Last resort, try 32-bit ATE-based map.
+        */
+       if (!mapaddr)
+               mapaddr =
+                   tioce_alloc_map(ce_kern, TIOCE_ATE_M32, -1, ct_addr,
+                                   byte_count);
+
+       spin_unlock_irqrestore(&ce_kern->ce_lock, flags);
+
+dma_map_done:
+       if (mapaddr & barrier)
+               mapaddr = tioce_dma_barrier(mapaddr, 1);
+
+       return mapaddr;
+}
+
+/**
+ * tioce_dma - standard pci dma map interface
+ * @pdev: pci device requesting the map
+ * @paddr: system physical address to map into pci space
+ * @byte_count: # bytes to map
+ *
+ * Simply call tioce_do_dma_map() to create a map with the barrier bit clear
+ * in the address.
+ */
+static uint64_t
+tioce_dma(struct pci_dev *pdev, uint64_t paddr, size_t byte_count)
+{
+       return tioce_do_dma_map(pdev, paddr, byte_count, 0);
+}
+
+/**
+ * tioce_dma_consistent - consistent pci dma map interface
+ * @pdev: pci device requesting the map
+ * @paddr: system physical address to map into pci space
+ * @byte_count: # bytes to map
+ *
+ * Simply call tioce_do_dma_map() to create a map with the barrier bit set
+ * in the address.
+ */ static uint64_t
+tioce_dma_consistent(struct pci_dev *pdev, uint64_t paddr, size_t byte_count)
+{
+       return tioce_do_dma_map(pdev, paddr, byte_count, 1);
+}
+
+/**
+ * tioce_error_intr_handler - SGI TIO CE error interrupt handler
+ * @irq: unused
+ * @arg: pointer to tioce_common struct for the given CE
+ * @pt: unused
+ *
+ * Handle a CE error interrupt.  Simply a wrapper around a SAL call which
+ * defers processing to the SGI prom.
+ */ static irqreturn_t
+tioce_error_intr_handler(int irq, void *arg, struct pt_regs *pt)
+{
+       struct tioce_common *soft = arg;
+       struct ia64_sal_retval ret_stuff;
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+
+       SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_ERROR_INTERRUPT,
+                       soft->ce_pcibus.bs_persist_segment,
+                       soft->ce_pcibus.bs_persist_busnum, 0, 0, 0, 0, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * tioce_kern_init - init kernel structures related to a given TIOCE
+ * @tioce_common: ptr to a cached tioce_common struct that originated in prom
+ */ static struct tioce_kernel *
+tioce_kern_init(struct tioce_common *tioce_common)
+{
+       int i;
+       uint32_t tmp;
+       struct tioce *tioce_mmr;
+       struct tioce_kernel *tioce_kern;
+
+       tioce_kern = kcalloc(1, sizeof(struct tioce_kernel), GFP_KERNEL);
+       if (!tioce_kern) {
+               return NULL;
+       }
+
+       tioce_kern->ce_common = tioce_common;
+       spin_lock_init(&tioce_kern->ce_lock);
+       INIT_LIST_HEAD(&tioce_kern->ce_dmamap_list);
+       tioce_common->ce_kernel_private = (uint64_t) tioce_kern;
+
+       /*
+        * Determine the secondary bus number of the port2 logical PPB.
+        * This is used to decide whether a given pci device resides on
+        * port1 or port2.  Note:  We don't have enough plumbing set up
+        * here to use pci_read_config_xxx() so use the raw_pci_ops vector.
+        */
+
+       raw_pci_ops->read(tioce_common->ce_pcibus.bs_persist_segment,
+                         tioce_common->ce_pcibus.bs_persist_busnum,
+                         PCI_DEVFN(2, 0), PCI_SECONDARY_BUS, 1, &tmp);
+       tioce_kern->ce_port1_secondary = (uint8_t) tmp;
+
+       /*
+        * Set PMU pagesize to the largest size available, and zero out
+        * the ate's.
+        */
+
+       tioce_mmr = (struct tioce *)tioce_common->ce_pcibus.bs_base;
+       tioce_mmr->ce_ure_page_map &= ~CE_URE_PAGESIZE_MASK;
+       tioce_mmr->ce_ure_page_map |= CE_URE_256K_PAGESIZE;
+       tioce_kern->ce_ate3240_pagesize = KB(256);
+
+       for (i = 0; i < TIOCE_NUM_M40_ATES; i++) {
+               tioce_kern->ce_ate40_shadow[i] = 0;
+               tioce_mmr->ce_ure_ate40[i] = 0;
+       }
+
+       for (i = 0; i < TIOCE_NUM_M3240_ATES; i++) {
+               tioce_kern->ce_ate3240_shadow[i] = 0;
+               tioce_mmr->ce_ure_ate3240[i] = 0;
+       }
+
+       return tioce_kern;
+}
+
+/**
+ * tioce_force_interrupt - implement altix force_interrupt() backend for CE
+ * @sn_irq_info: sn asic irq that we need an interrupt generated for
+ *
+ * Given an sn_irq_info struct, set the proper bit in ce_adm_force_int to
+ * force a secondary interrupt to be generated.  This is to work around an
+ * asic issue where there is a small window of opportunity for a legacy device
+ * interrupt to be lost.
+ */
+static void
+tioce_force_interrupt(struct sn_irq_info *sn_irq_info)
+{
+       struct pcidev_info *pcidev_info;
+       struct tioce_common *ce_common;
+       struct tioce *ce_mmr;
+       uint64_t force_int_val;
+
+       if (!sn_irq_info->irq_bridge)
+               return;
+
+       if (sn_irq_info->irq_bridge_type != PCIIO_ASIC_TYPE_TIOCE)
+               return;
+
+       pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
+       if (!pcidev_info)
+               return;
+
+       ce_common = (struct tioce_common *)pcidev_info->pdi_pcibus_info;
+       ce_mmr = (struct tioce *)ce_common->ce_pcibus.bs_base;
+
+       /*
+        * irq_int_bit is originally set up by prom, and holds the interrupt
+        * bit shift (not mask) as defined by the bit definitions in the
+        * ce_adm_int mmr.  These shifts are not the same for the
+        * ce_adm_force_int register, so do an explicit mapping here to make
+        * things clearer.
+        */
+
+       switch (sn_irq_info->irq_int_bit) {
+       case CE_ADM_INT_PCIE_PORT1_DEV_A_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT1_DEV_A_SHFT;
+               break;
+       case CE_ADM_INT_PCIE_PORT1_DEV_B_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT1_DEV_B_SHFT;
+               break;
+       case CE_ADM_INT_PCIE_PORT1_DEV_C_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT1_DEV_C_SHFT;
+               break;
+       case CE_ADM_INT_PCIE_PORT1_DEV_D_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT1_DEV_D_SHFT;
+               break;
+       case CE_ADM_INT_PCIE_PORT2_DEV_A_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT2_DEV_A_SHFT;
+               break;
+       case CE_ADM_INT_PCIE_PORT2_DEV_B_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT2_DEV_B_SHFT;
+               break;
+       case CE_ADM_INT_PCIE_PORT2_DEV_C_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT2_DEV_C_SHFT;
+               break;
+       case CE_ADM_INT_PCIE_PORT2_DEV_D_SHFT:
+               force_int_val = 1UL << CE_ADM_FORCE_INT_PCIE_PORT2_DEV_D_SHFT;
+               break;
+       default:
+               return;
+       }
+       ce_mmr->ce_adm_force_int = force_int_val;
+}
+
+/**
+ * tioce_target_interrupt - implement set_irq_affinity for tioce resident
+ * functions.  Note:  only applies to line interrupts, not MSI's.
+ *
+ * @sn_irq_info: SN IRQ context
+ *
+ * Given an sn_irq_info, set the associated CE device's interrupt destination
+ * register.  Since the interrupt destination registers are on a per-ce-slot
+ * basis, this will retarget line interrupts for all functions downstream of
+ * the slot.
+ */
+static void
+tioce_target_interrupt(struct sn_irq_info *sn_irq_info)
+{
+       struct pcidev_info *pcidev_info;
+       struct tioce_common *ce_common;
+       struct tioce *ce_mmr;
+       int bit;
+
+       pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
+       if (!pcidev_info)
+               return;
+
+       ce_common = (struct tioce_common *)pcidev_info->pdi_pcibus_info;
+       ce_mmr = (struct tioce *)ce_common->ce_pcibus.bs_base;
+
+       bit = sn_irq_info->irq_int_bit;
+
+       ce_mmr->ce_adm_int_mask |= (1UL << bit);
+       ce_mmr->ce_adm_int_dest[bit] =
+               ((uint64_t)sn_irq_info->irq_irq << INTR_VECTOR_SHFT) |
+                          sn_irq_info->irq_xtalkaddr;
+       ce_mmr->ce_adm_int_mask &= ~(1UL << bit);
+
+       tioce_force_interrupt(sn_irq_info);
+}
+
+/**
+ * tioce_bus_fixup - perform final PCI fixup for a TIO CE bus
+ * @prom_bussoft: Common prom/kernel struct representing the bus
+ *
+ * Replicates the tioce_common pointed to by @prom_bussoft in kernel
+ * space.  Allocates and initializes a kernel-only area for a given CE,
+ * and sets up an irq for handling CE error interrupts.
+ *
+ * On successful setup, returns the kernel version of tioce_common back to
+ * the caller.
+ */
+static void *
+tioce_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *controller)
+{
+       struct tioce_common *tioce_common;
+
+       /*
+        * Allocate kernel bus soft and copy from prom.
+        */
+
+       tioce_common = kcalloc(1, sizeof(struct tioce_common), GFP_KERNEL);
+       if (!tioce_common)
+               return NULL;
+
+       memcpy(tioce_common, prom_bussoft, sizeof(struct tioce_common));
+       tioce_common->ce_pcibus.bs_base |= __IA64_UNCACHED_OFFSET;
+
+       if (tioce_kern_init(tioce_common) == NULL) {
+               kfree(tioce_common);
+               return NULL;
+       }
+
+       if (request_irq(SGI_PCIASIC_ERROR,
+                       tioce_error_intr_handler,
+                       SA_SHIRQ, "TIOCE error", (void *)tioce_common))
+               printk(KERN_WARNING
+                      "%s:  Unable to get irq %d.  "
+                      "Error interrupts won't be routed for "
+                      "TIOCE bus %04x:%02x\n",
+                      __FUNCTION__, SGI_PCIASIC_ERROR,
+                      tioce_common->ce_pcibus.bs_persist_segment,
+                      tioce_common->ce_pcibus.bs_persist_busnum);
+
+       return tioce_common;
+}
+
+static struct sn_pcibus_provider tioce_pci_interfaces = {
+       .dma_map = tioce_dma,
+       .dma_map_consistent = tioce_dma_consistent,
+       .dma_unmap = tioce_dma_unmap,
+       .bus_fixup = tioce_bus_fixup,
+       .force_interrupt = tioce_force_interrupt,
+       .target_interrupt = tioce_target_interrupt
+};
+
+/**
+ * tioce_init_provider - init SN PCI provider ops for TIO CE
+ */
+int
+tioce_init_provider(void)
+{
+       sn_pci_provider[PCIIO_ASIC_TYPE_TIOCE] = &tioce_pci_interfaces;
+       return 0;
+}
index 468721d9ebd214c1048f9b293f0fa3d9a56d807d..3fb1fb619d2c9510b67a27df2b830b1ab93ffac9 100644 (file)
@@ -249,8 +249,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_NO_DPM)
        sync
        isync
 
-       /* Enable L2 HW prefetch
+       /* Enable L2 HW prefetch, if L2 is enabled
         */
+       mfspr   r3,SPRN_L2CR
+       andis.  r3,r3,L2CR_L2E@h
+       beqlr
        mfspr   r3,SPRN_MSSCR0
        ori     r3,r3,3
        sync
index c39441048266f7ba918f3793f41fa8588267d309..861115249b3595fcadcb6319fef16e97de96c940 100644 (file)
@@ -156,6 +156,26 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
               The bit moved on the 7450.....
          ****/
 
+BEGIN_FTR_SECTION
+       /* Disable L2 prefetch on some 745x and try to ensure
+        * L2 prefetch engines are idle. As explained by errata
+        * text, we can't be sure they are, we just hope very hard
+        * that well be enough (sic !). At least I noticed Apple
+        * doesn't even bother doing the dcbf's here...
+        */
+       mfspr   r4,SPRN_MSSCR0
+       rlwinm  r4,r4,0,0,29
+       sync
+       mtspr   SPRN_MSSCR0,r4
+       sync
+       isync
+       lis     r4,KERNELBASE@h
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
+
        /* TODO: use HW flush assist when available */
 
        lis     r4,0x0002
@@ -230,7 +250,16 @@ END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
        oris    r3,r3,0x8000
        mtspr   SPRN_L2CR,r3
        sync
-
+       
+       /* Enable L2 HW prefetch on 744x/745x */
+BEGIN_FTR_SECTION
+       mfspr   r3,SPRN_MSSCR0
+       ori     r3,r3,3
+       sync
+       mtspr   SPRN_MSSCR0,r3
+       sync
+       isync
+END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
 4:
 
        /* Restore HID0[DPM] to whatever it was before */
index bab255889c58214c60214606d3577e9093bc24ca..698d6b9ed6d19566d83a2299715f72a512ef96bb 100644 (file)
@@ -97,25 +97,21 @@ BEGIN_FTR_SECTION
        lhz     r9,PACAHIGHHTLBAREAS(r13)
        srdi    r11,r3,(HTLB_AREA_SHIFT-SID_SHIFT)
        srd     r9,r9,r11
-       andi.   r9,r9,1
-       bne     5f
+       lhz     r11,PACALOWHTLBAREAS(r13)
+       srd     r11,r11,r3
+       or      r9,r9,r11
+END_FTR_SECTION_IFSET(CPU_FTR_16M_PAGE)
+#endif /* CONFIG_HUGETLB_PAGE */
 
        li      r11,SLB_VSID_USER
 
-       cmpldi  r3,16
-       bge     6f
-
-       lhz     r9,PACALOWHTLBAREAS(r13)
-       srd     r9,r9,r3
-       andi.   r9,r9,1
-
-       beq     6f
-
-5:     li      r11,SLB_VSID_USER|SLB_VSID_L
+#ifdef CONFIG_HUGETLB_PAGE
+BEGIN_FTR_SECTION
+       rldimi  r11,r9,8,55             /* shift masked bit into SLB_VSID_L */
 END_FTR_SECTION_IFSET(CPU_FTR_16M_PAGE)
 #endif /* CONFIG_HUGETLB_PAGE */
 
-6:     ld      r9,PACACONTEXTID(r13)
+       ld      r9,PACACONTEXTID(r13)
        rldimi  r3,r9,USER_ESID_BITS,0
 
 9:     /* r3 = protovsid, r11 = flags, r10 = esid_data, cr7 = <>KERNELBASE */
index d692af57213a7cbfac0a7e884749c94a35fb4b73..baaa365285fa973776beddcb0ae5c09389ffbe65 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/byteorder/generic.h>
 #include <asm/sn/sn_sal.h>
+#include <asm/unaligned.h>
 #include "snsc.h"
 
 static struct subch_data_s *event_sd;
@@ -62,13 +63,16 @@ static int
 scdrv_parse_event(char *event, int *src, int *code, int *esp_code, char *desc)
 {
        char *desc_end;
+       __be32 from_buf;
 
        /* record event source address */
-       *src = be32_to_cpup((__be32 *)event);
+       from_buf = get_unaligned((__be32 *)event);
+       *src = be32_to_cpup(&from_buf);
        event += 4;                     /* move on to event code */
 
        /* record the system controller's event code */
-       *code = be32_to_cpup((__be32 *)event);
+       from_buf = get_unaligned((__be32 *)event);
+       *code = be32_to_cpup(&from_buf);
        event += 4;                     /* move on to event arguments */
 
        /* how many arguments are in the packet? */
@@ -82,7 +86,8 @@ scdrv_parse_event(char *event, int *src, int *code, int *esp_code, char *desc)
                /* not an integer argument, so give up */
                return -1;
        }
-       *esp_code = be32_to_cpup((__be32 *)event);
+       from_buf = get_unaligned((__be32 *)event);
+       *esp_code = be32_to_cpup(&from_buf);
        event += 4;
 
        /* parse out the event description */
index bf3c011d2cfb3cbc77852c36712d665fe5e25e6f..d8bf65877897bd6a0e1650c19e825584ead7ea9c 100644 (file)
@@ -102,6 +102,9 @@ config DVB_BUDGET_AV
        select VIDEO_DEV
        select VIDEO_SAA7146_VV
        select DVB_STV0299
+       select DVB_TDA1004X
+       select DVB_TDA10021
+       select FW_LOADER
        help
          Support for simple SAA7146 based DVB cards
          (so called Budget- or Nova-PCI cards) without onboard
index 183ba97785b0dbadeff6745838d254845fa5a6b1..dc5d089bf184a8091c704de78d2c8fd6451c2224 100644 (file)
 #include <asm/iommu.h>
 #include <asm/vio.h>
 
-#include "iseries_veth.h"
+#undef DEBUG
 
 MODULE_AUTHOR("Kyle Lucke <klucke@us.ibm.com>");
 MODULE_DESCRIPTION("iSeries Virtual ethernet driver");
 MODULE_LICENSE("GPL");
 
+#define VETH_EVENT_CAP (0)
+#define VETH_EVENT_FRAMES      (1)
+#define VETH_EVENT_MONITOR     (2)
+#define VETH_EVENT_FRAMES_ACK  (3)
+
+#define VETH_MAX_ACKS_PER_MSG  (20)
+#define VETH_MAX_FRAMES_PER_MSG        (6)
+
+struct veth_frames_data {
+       u32 addr[VETH_MAX_FRAMES_PER_MSG];
+       u16 len[VETH_MAX_FRAMES_PER_MSG];
+       u32 eofmask;
+};
+#define VETH_EOF_SHIFT         (32-VETH_MAX_FRAMES_PER_MSG)
+
+struct veth_frames_ack_data {
+       u16 token[VETH_MAX_ACKS_PER_MSG];
+};
+
+struct veth_cap_data {
+       u8 caps_version;
+       u8 rsvd1;
+       u16 num_buffers;
+       u16 ack_threshold;
+       u16 rsvd2;
+       u32 ack_timeout;
+       u32 rsvd3;
+       u64 rsvd4[3];
+};
+
+struct veth_lpevent {
+       struct HvLpEvent base_event;
+       union {
+               struct veth_cap_data caps_data;
+               struct veth_frames_data frames_data;
+               struct veth_frames_ack_data frames_ack_data;
+       } u;
+
+};
+
+#define DRV_NAME       "iseries_veth"
+#define DRV_VERSION    "2.0"
+
 #define VETH_NUMBUFFERS                (120)
 #define VETH_ACKTIMEOUT        (1000000) /* microseconds */
 #define VETH_MAX_MCAST         (12)
@@ -113,9 +156,9 @@ MODULE_LICENSE("GPL");
 
 struct veth_msg {
        struct veth_msg *next;
-       struct VethFramesData data;
+       struct veth_frames_data data;
        int token;
-       unsigned long in_use;
+       int in_use;
        struct sk_buff *skb;
        struct device *dev;
 };
@@ -125,23 +168,28 @@ struct veth_lpar_connection {
        struct work_struct statemachine_wq;
        struct veth_msg *msgs;
        int num_events;
-       struct VethCapData local_caps;
+       struct veth_cap_data local_caps;
 
+       struct kobject kobject;
        struct timer_list ack_timer;
 
+       struct timer_list reset_timer;
+       unsigned int reset_timeout;
+       unsigned long last_contact;
+       int outstanding_tx;
+
        spinlock_t lock;
        unsigned long state;
        HvLpInstanceId src_inst;
        HvLpInstanceId dst_inst;
-       struct VethLpEvent cap_event, cap_ack_event;
+       struct veth_lpevent cap_event, cap_ack_event;
        u16 pending_acks[VETH_MAX_ACKS_PER_MSG];
        u32 num_pending_acks;
 
        int num_ack_events;
-       struct VethCapData remote_caps;
+       struct veth_cap_data remote_caps;
        u32 ack_timeout;
 
-       spinlock_t msg_stack_lock;
        struct veth_msg *msg_stack_head;
 };
 
@@ -151,15 +199,17 @@ struct veth_port {
        u64 mac_addr;
        HvLpIndexMap lpar_map;
 
-       spinlock_t pending_gate;
-       struct sk_buff *pending_skb;
-       HvLpIndexMap pending_lpmask;
+       /* queue_lock protects the stopped_map and dev's queue. */
+       spinlock_t queue_lock;
+       HvLpIndexMap stopped_map;
 
+       /* mcast_gate protects promiscuous, num_mcast & mcast_addr. */
        rwlock_t mcast_gate;
        int promiscuous;
-       int all_mcast;
        int num_mcast;
        u64 mcast_addr[VETH_MAX_MCAST];
+
+       struct kobject kobject;
 };
 
 static HvLpIndex this_lp;
@@ -168,44 +218,56 @@ static struct net_device *veth_dev[HVMAXARCHITECTEDVIRTUALLANS]; /* = 0 */
 
 static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *);
-static void veth_flush_pending(struct veth_lpar_connection *cnx);
-static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *);
-static void veth_timed_ack(unsigned long connectionPtr);
+static void veth_wake_queues(struct veth_lpar_connection *cnx);
+static void veth_stop_queues(struct veth_lpar_connection *cnx);
+static void veth_receive(struct veth_lpar_connection *, struct veth_lpevent *);
+static void veth_release_connection(struct kobject *kobject);
+static void veth_timed_ack(unsigned long ptr);
+static void veth_timed_reset(unsigned long ptr);
 
 /*
  * Utility functions
  */
 
-#define veth_printk(prio, fmt, args...) \
-       printk(prio "%s: " fmt, __FILE__, ## args)
+#define veth_info(fmt, args...) \
+       printk(KERN_INFO DRV_NAME ": " fmt, ## args)
 
 #define veth_error(fmt, args...) \
-       printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args)
+       printk(KERN_ERR DRV_NAME ": Error: " fmt, ## args)
+
+#ifdef DEBUG
+#define veth_debug(fmt, args...) \
+       printk(KERN_DEBUG DRV_NAME ": " fmt, ## args)
+#else
+#define veth_debug(fmt, args...) do {} while (0)
+#endif
 
+/* You must hold the connection's lock when you call this function. */
 static inline void veth_stack_push(struct veth_lpar_connection *cnx,
                                   struct veth_msg *msg)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&cnx->msg_stack_lock, flags);
        msg->next = cnx->msg_stack_head;
        cnx->msg_stack_head = msg;
-       spin_unlock_irqrestore(&cnx->msg_stack_lock, flags);
 }
 
+/* You must hold the connection's lock when you call this function. */
 static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx)
 {
-       unsigned long flags;
        struct veth_msg *msg;
 
-       spin_lock_irqsave(&cnx->msg_stack_lock, flags);
        msg = cnx->msg_stack_head;
        if (msg)
                cnx->msg_stack_head = cnx->msg_stack_head->next;
-       spin_unlock_irqrestore(&cnx->msg_stack_lock, flags);
+
        return msg;
 }
 
+/* You must hold the connection's lock when you call this function. */
+static inline int veth_stack_is_empty(struct veth_lpar_connection *cnx)
+{
+       return cnx->msg_stack_head == NULL;
+}
+
 static inline HvLpEvent_Rc
 veth_signalevent(struct veth_lpar_connection *cnx, u16 subtype,
                 HvLpEvent_AckInd ackind, HvLpEvent_AckType acktype,
@@ -249,13 +311,144 @@ static int veth_allocate_events(HvLpIndex rlp, int number)
        struct veth_allocation vc = { COMPLETION_INITIALIZER(vc.c), 0 };
 
        mf_allocate_lp_events(rlp, HvLpEvent_Type_VirtualLan,
-                           sizeof(struct VethLpEvent), number,
+                           sizeof(struct veth_lpevent), number,
                            &veth_complete_allocation, &vc);
        wait_for_completion(&vc.c);
 
        return vc.num;
 }
 
+/*
+ * sysfs support
+ */
+
+struct veth_cnx_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct veth_lpar_connection *, char *buf);
+       ssize_t (*store)(struct veth_lpar_connection *, const char *buf);
+};
+
+static ssize_t veth_cnx_attribute_show(struct kobject *kobj,
+               struct attribute *attr, char *buf)
+{
+       struct veth_cnx_attribute *cnx_attr;
+       struct veth_lpar_connection *cnx;
+
+       cnx_attr = container_of(attr, struct veth_cnx_attribute, attr);
+       cnx = container_of(kobj, struct veth_lpar_connection, kobject);
+
+       if (!cnx_attr->show)
+               return -EIO;
+
+       return cnx_attr->show(cnx, buf);
+}
+
+#define CUSTOM_CNX_ATTR(_name, _format, _expression)                   \
+static ssize_t _name##_show(struct veth_lpar_connection *cnx, char *buf)\
+{                                                                      \
+       return sprintf(buf, _format, _expression);                      \
+}                                                                      \
+struct veth_cnx_attribute veth_cnx_attr_##_name = __ATTR_RO(_name)
+
+#define SIMPLE_CNX_ATTR(_name) \
+       CUSTOM_CNX_ATTR(_name, "%lu\n", (unsigned long)cnx->_name)
+
+SIMPLE_CNX_ATTR(outstanding_tx);
+SIMPLE_CNX_ATTR(remote_lp);
+SIMPLE_CNX_ATTR(num_events);
+SIMPLE_CNX_ATTR(src_inst);
+SIMPLE_CNX_ATTR(dst_inst);
+SIMPLE_CNX_ATTR(num_pending_acks);
+SIMPLE_CNX_ATTR(num_ack_events);
+CUSTOM_CNX_ATTR(ack_timeout, "%d\n", jiffies_to_msecs(cnx->ack_timeout));
+CUSTOM_CNX_ATTR(reset_timeout, "%d\n", jiffies_to_msecs(cnx->reset_timeout));
+CUSTOM_CNX_ATTR(state, "0x%.4lX\n", cnx->state);
+CUSTOM_CNX_ATTR(last_contact, "%d\n", cnx->last_contact ?
+               jiffies_to_msecs(jiffies - cnx->last_contact) : 0);
+
+#define GET_CNX_ATTR(_name)    (&veth_cnx_attr_##_name.attr)
+
+static struct attribute *veth_cnx_default_attrs[] = {
+       GET_CNX_ATTR(outstanding_tx),
+       GET_CNX_ATTR(remote_lp),
+       GET_CNX_ATTR(num_events),
+       GET_CNX_ATTR(reset_timeout),
+       GET_CNX_ATTR(last_contact),
+       GET_CNX_ATTR(state),
+       GET_CNX_ATTR(src_inst),
+       GET_CNX_ATTR(dst_inst),
+       GET_CNX_ATTR(num_pending_acks),
+       GET_CNX_ATTR(num_ack_events),
+       GET_CNX_ATTR(ack_timeout),
+       NULL
+};
+
+static struct sysfs_ops veth_cnx_sysfs_ops = {
+               .show = veth_cnx_attribute_show
+};
+
+static struct kobj_type veth_lpar_connection_ktype = {
+       .release        = veth_release_connection,
+       .sysfs_ops      = &veth_cnx_sysfs_ops,
+       .default_attrs  = veth_cnx_default_attrs
+};
+
+struct veth_port_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct veth_port *, char *buf);
+       ssize_t (*store)(struct veth_port *, const char *buf);
+};
+
+static ssize_t veth_port_attribute_show(struct kobject *kobj,
+               struct attribute *attr, char *buf)
+{
+       struct veth_port_attribute *port_attr;
+       struct veth_port *port;
+
+       port_attr = container_of(attr, struct veth_port_attribute, attr);
+       port = container_of(kobj, struct veth_port, kobject);
+
+       if (!port_attr->show)
+               return -EIO;
+
+       return port_attr->show(port, buf);
+}
+
+#define CUSTOM_PORT_ATTR(_name, _format, _expression)                  \
+static ssize_t _name##_show(struct veth_port *port, char *buf)         \
+{                                                                      \
+       return sprintf(buf, _format, _expression);                      \
+}                                                                      \
+struct veth_port_attribute veth_port_attr_##_name = __ATTR_RO(_name)
+
+#define SIMPLE_PORT_ATTR(_name)        \
+       CUSTOM_PORT_ATTR(_name, "%lu\n", (unsigned long)port->_name)
+
+SIMPLE_PORT_ATTR(promiscuous);
+SIMPLE_PORT_ATTR(num_mcast);
+CUSTOM_PORT_ATTR(lpar_map, "0x%X\n", port->lpar_map);
+CUSTOM_PORT_ATTR(stopped_map, "0x%X\n", port->stopped_map);
+CUSTOM_PORT_ATTR(mac_addr, "0x%lX\n", port->mac_addr);
+
+#define GET_PORT_ATTR(_name)   (&veth_port_attr_##_name.attr)
+static struct attribute *veth_port_default_attrs[] = {
+       GET_PORT_ATTR(mac_addr),
+       GET_PORT_ATTR(lpar_map),
+       GET_PORT_ATTR(stopped_map),
+       GET_PORT_ATTR(promiscuous),
+       GET_PORT_ATTR(num_mcast),
+       NULL
+};
+
+static struct sysfs_ops veth_port_sysfs_ops = {
+       .show = veth_port_attribute_show
+};
+
+static struct kobj_type veth_port_ktype = {
+       .sysfs_ops      = &veth_port_sysfs_ops,
+       .default_attrs  = veth_port_default_attrs
+};
+
 /*
  * LPAR connection code
  */
@@ -266,7 +459,7 @@ static inline void veth_kick_statemachine(struct veth_lpar_connection *cnx)
 }
 
 static void veth_take_cap(struct veth_lpar_connection *cnx,
-                         struct VethLpEvent *event)
+                         struct veth_lpevent *event)
 {
        unsigned long flags;
 
@@ -278,7 +471,7 @@ static void veth_take_cap(struct veth_lpar_connection *cnx,
                                                  HvLpEvent_Type_VirtualLan);
 
        if (cnx->state & VETH_STATE_GOTCAPS) {
-               veth_error("Received a second capabilities from lpar %d\n",
+               veth_error("Received a second capabilities from LPAR %d.\n",
                           cnx->remote_lp);
                event->base_event.xRc = HvLpEvent_Rc_BufferNotAvailable;
                HvCallEvent_ackLpEvent((struct HvLpEvent *) event);
@@ -291,13 +484,13 @@ static void veth_take_cap(struct veth_lpar_connection *cnx,
 }
 
 static void veth_take_cap_ack(struct veth_lpar_connection *cnx,
-                             struct VethLpEvent *event)
+                             struct veth_lpevent *event)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&cnx->lock, flags);
        if (cnx->state & VETH_STATE_GOTCAPACK) {
-               veth_error("Received a second capabilities ack from lpar %d\n",
+               veth_error("Received a second capabilities ack from LPAR %d.\n",
                           cnx->remote_lp);
        } else {
                memcpy(&cnx->cap_ack_event, event,
@@ -309,19 +502,24 @@ static void veth_take_cap_ack(struct veth_lpar_connection *cnx,
 }
 
 static void veth_take_monitor_ack(struct veth_lpar_connection *cnx,
-                                 struct VethLpEvent *event)
+                                 struct veth_lpevent *event)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&cnx->lock, flags);
-       veth_printk(KERN_DEBUG, "Monitor ack returned for lpar %d\n",
-                   cnx->remote_lp);
-       cnx->state |= VETH_STATE_RESET;
-       veth_kick_statemachine(cnx);
+       veth_debug("cnx %d: lost connection.\n", cnx->remote_lp);
+
+       /* Avoid kicking the statemachine once we're shutdown.
+        * It's unnecessary and it could break veth_stop_connection(). */
+
+       if (! (cnx->state & VETH_STATE_SHUTDOWN)) {
+               cnx->state |= VETH_STATE_RESET;
+               veth_kick_statemachine(cnx);
+       }
        spin_unlock_irqrestore(&cnx->lock, flags);
 }
 
-static void veth_handle_ack(struct VethLpEvent *event)
+static void veth_handle_ack(struct veth_lpevent *event)
 {
        HvLpIndex rlp = event->base_event.xTargetLp;
        struct veth_lpar_connection *cnx = veth_cnx[rlp];
@@ -329,58 +527,67 @@ static void veth_handle_ack(struct VethLpEvent *event)
        BUG_ON(! cnx);
 
        switch (event->base_event.xSubtype) {
-       case VethEventTypeCap:
+       case VETH_EVENT_CAP:
                veth_take_cap_ack(cnx, event);
                break;
-       case VethEventTypeMonitor:
+       case VETH_EVENT_MONITOR:
                veth_take_monitor_ack(cnx, event);
                break;
        default:
-               veth_error("Unknown ack type %d from lpar %d\n",
-                          event->base_event.xSubtype, rlp);
+               veth_error("Unknown ack type %d from LPAR %d.\n",
+                               event->base_event.xSubtype, rlp);
        };
 }
 
-static void veth_handle_int(struct VethLpEvent *event)
+static void veth_handle_int(struct veth_lpevent *event)
 {
        HvLpIndex rlp = event->base_event.xSourceLp;
        struct veth_lpar_connection *cnx = veth_cnx[rlp];
        unsigned long flags;
-       int i;
+       int i, acked = 0;
 
        BUG_ON(! cnx);
 
        switch (event->base_event.xSubtype) {
-       case VethEventTypeCap:
+       case VETH_EVENT_CAP:
                veth_take_cap(cnx, event);
                break;
-       case VethEventTypeMonitor:
+       case VETH_EVENT_MONITOR:
                /* do nothing... this'll hang out here til we're dead,
                 * and the hypervisor will return it for us. */
                break;
-       case VethEventTypeFramesAck:
+       case VETH_EVENT_FRAMES_ACK:
                spin_lock_irqsave(&cnx->lock, flags);
+
                for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) {
                        u16 msgnum = event->u.frames_ack_data.token[i];
 
-                       if (msgnum < VETH_NUMBUFFERS)
+                       if (msgnum < VETH_NUMBUFFERS) {
                                veth_recycle_msg(cnx, cnx->msgs + msgnum);
+                               cnx->outstanding_tx--;
+                               acked++;
+                       }
+               }
+
+               if (acked > 0) {
+                       cnx->last_contact = jiffies;
+                       veth_wake_queues(cnx);
                }
+
                spin_unlock_irqrestore(&cnx->lock, flags);
-               veth_flush_pending(cnx);
                break;
-       case VethEventTypeFrames:
+       case VETH_EVENT_FRAMES:
                veth_receive(cnx, event);
                break;
        default:
-               veth_error("Unknown interrupt type %d from lpar %d\n",
-                          event->base_event.xSubtype, rlp);
+               veth_error("Unknown interrupt type %d from LPAR %d.\n",
+                               event->base_event.xSubtype, rlp);
        };
 }
 
 static void veth_handle_event(struct HvLpEvent *event, struct pt_regs *regs)
 {
-       struct VethLpEvent *veth_event = (struct VethLpEvent *)event;
+       struct veth_lpevent *veth_event = (struct veth_lpevent *)event;
 
        if (event->xFlags.xFunction == HvLpEvent_Function_Ack)
                veth_handle_ack(veth_event);
@@ -390,7 +597,7 @@ static void veth_handle_event(struct HvLpEvent *event, struct pt_regs *regs)
 
 static int veth_process_caps(struct veth_lpar_connection *cnx)
 {
-       struct VethCapData *remote_caps = &cnx->remote_caps;
+       struct veth_cap_data *remote_caps = &cnx->remote_caps;
        int num_acks_needed;
 
        /* Convert timer to jiffies */
@@ -400,8 +607,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx)
             || (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG)
             || (remote_caps->ack_threshold == 0)
             || (cnx->ack_timeout == 0) ) {
-               veth_error("Received incompatible capabilities from lpar %d\n",
-                          cnx->remote_lp);
+               veth_error("Received incompatible capabilities from LPAR %d.\n",
+                               cnx->remote_lp);
                return HvLpEvent_Rc_InvalidSubtypeData;
        }
 
@@ -418,8 +625,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx)
                        cnx->num_ack_events += num;
 
                if (cnx->num_ack_events < num_acks_needed) {
-                       veth_error("Couldn't allocate enough ack events for lpar %d\n",
-                                  cnx->remote_lp);
+                       veth_error("Couldn't allocate enough ack events "
+                                       "for LPAR %d.\n", cnx->remote_lp);
 
                        return HvLpEvent_Rc_BufferNotAvailable;
                }
@@ -440,15 +647,15 @@ static void veth_statemachine(void *p)
 
  restart:
        if (cnx->state & VETH_STATE_RESET) {
-               int i;
-
-               del_timer(&cnx->ack_timer);
-
                if (cnx->state & VETH_STATE_OPEN)
                        HvCallEvent_closeLpEventPath(cnx->remote_lp,
                                                     HvLpEvent_Type_VirtualLan);
 
-               /* reset ack data */
+               /*
+                * Reset ack data. This prevents the ack_timer actually
+                * doing anything, even if it runs one more time when
+                * we drop the lock below.
+                */
                memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks));
                cnx->num_pending_acks = 0;
 
@@ -458,14 +665,32 @@ static void veth_statemachine(void *p)
                                | VETH_STATE_SENTCAPACK | VETH_STATE_READY);
 
                /* Clean up any leftover messages */
-               if (cnx->msgs)
+               if (cnx->msgs) {
+                       int i;
                        for (i = 0; i < VETH_NUMBUFFERS; ++i)
                                veth_recycle_msg(cnx, cnx->msgs + i);
+               }
+
+               cnx->outstanding_tx = 0;
+               veth_wake_queues(cnx);
+
+               /* Drop the lock so we can do stuff that might sleep or
+                * take other locks. */
                spin_unlock_irq(&cnx->lock);
-               veth_flush_pending(cnx);
+
+               del_timer_sync(&cnx->ack_timer);
+               del_timer_sync(&cnx->reset_timer);
+
                spin_lock_irq(&cnx->lock);
+
                if (cnx->state & VETH_STATE_RESET)
                        goto restart;
+
+               /* Hack, wait for the other end to reset itself. */
+               if (! (cnx->state & VETH_STATE_SHUTDOWN)) {
+                       schedule_delayed_work(&cnx->statemachine_wq, 5 * HZ);
+                       goto out;
+               }
        }
 
        if (cnx->state & VETH_STATE_SHUTDOWN)
@@ -488,7 +713,7 @@ static void veth_statemachine(void *p)
 
        if ( (cnx->state & VETH_STATE_OPEN)
             && !(cnx->state & VETH_STATE_SENTMON) ) {
-               rc = veth_signalevent(cnx, VethEventTypeMonitor,
+               rc = veth_signalevent(cnx, VETH_EVENT_MONITOR,
                                      HvLpEvent_AckInd_DoAck,
                                      HvLpEvent_AckType_DeferredAck,
                                      0, 0, 0, 0, 0, 0);
@@ -498,9 +723,8 @@ static void veth_statemachine(void *p)
                } else {
                        if ( (rc != HvLpEvent_Rc_PartitionDead)
                             && (rc != HvLpEvent_Rc_PathClosed) )
-                               veth_error("Error sending monitor to "
-                                          "lpar %d, rc=%x\n",
-                                          rlp, (int) rc);
+                               veth_error("Error sending monitor to LPAR %d, "
+                                               "rc = %d\n", rlp, rc);
 
                        /* Oh well, hope we get a cap from the other
                         * end and do better when that kicks us */
@@ -512,7 +736,7 @@ static void veth_statemachine(void *p)
             && !(cnx->state & VETH_STATE_SENTCAPS)) {
                u64 *rawcap = (u64 *)&cnx->local_caps;
 
-               rc = veth_signalevent(cnx, VethEventTypeCap,
+               rc = veth_signalevent(cnx, VETH_EVENT_CAP,
                                      HvLpEvent_AckInd_DoAck,
                                      HvLpEvent_AckType_ImmediateAck,
                                      0, rawcap[0], rawcap[1], rawcap[2],
@@ -523,9 +747,9 @@ static void veth_statemachine(void *p)
                } else {
                        if ( (rc != HvLpEvent_Rc_PartitionDead)
                             && (rc != HvLpEvent_Rc_PathClosed) )
-                               veth_error("Error sending caps to "
-                                          "lpar %d, rc=%x\n",
-                                          rlp, (int) rc);
+                               veth_error("Error sending caps to LPAR %d, "
+                                               "rc = %d\n", rlp, rc);
+
                        /* Oh well, hope we get a cap from the other
                         * end and do better when that kicks us */
                        goto out;
@@ -534,7 +758,7 @@ static void veth_statemachine(void *p)
 
        if ((cnx->state & VETH_STATE_GOTCAPS)
            && !(cnx->state & VETH_STATE_SENTCAPACK)) {
-               struct VethCapData *remote_caps = &cnx->remote_caps;
+               struct veth_cap_data *remote_caps = &cnx->remote_caps;
 
                memcpy(remote_caps, &cnx->cap_event.u.caps_data,
                       sizeof(*remote_caps));
@@ -565,10 +789,8 @@ static void veth_statemachine(void *p)
                        add_timer(&cnx->ack_timer);
                        cnx->state |= VETH_STATE_READY;
                } else {
-                       veth_printk(KERN_ERR, "Caps rejected (rc=%d) by "
-                                   "lpar %d\n",
-                                   cnx->cap_ack_event.base_event.xRc,
-                                   rlp);
+                       veth_error("Caps rejected by LPAR %d, rc = %d\n",
+                                       rlp, cnx->cap_ack_event.base_event.xRc);
                        goto cant_cope;
                }
        }
@@ -581,8 +803,8 @@ static void veth_statemachine(void *p)
        /* FIXME: we get here if something happens we really can't
         * cope with.  The link will never work once we get here, and
         * all we can do is not lock the rest of the system up */
-       veth_error("Badness on connection to lpar %d (state=%04lx) "
-                  " - shutting down\n", rlp, cnx->state);
+       veth_error("Unrecoverable error on connection to LPAR %d, shutting down"
+                       " (state = 0x%04lx)\n", rlp, cnx->state);
        cnx->state |= VETH_STATE_SHUTDOWN;
        spin_unlock_irq(&cnx->lock);
 }
@@ -591,7 +813,7 @@ static int veth_init_connection(u8 rlp)
 {
        struct veth_lpar_connection *cnx;
        struct veth_msg *msgs;
-       int i;
+       int i, rc;
 
        if ( (rlp == this_lp)
             || ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) )
@@ -605,22 +827,36 @@ static int veth_init_connection(u8 rlp)
        cnx->remote_lp = rlp;
        spin_lock_init(&cnx->lock);
        INIT_WORK(&cnx->statemachine_wq, veth_statemachine, cnx);
+
        init_timer(&cnx->ack_timer);
        cnx->ack_timer.function = veth_timed_ack;
        cnx->ack_timer.data = (unsigned long) cnx;
+
+       init_timer(&cnx->reset_timer);
+       cnx->reset_timer.function = veth_timed_reset;
+       cnx->reset_timer.data = (unsigned long) cnx;
+       cnx->reset_timeout = 5 * HZ * (VETH_ACKTIMEOUT / 1000000);
+
        memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks));
 
        veth_cnx[rlp] = cnx;
 
+       /* This gets us 1 reference, which is held on behalf of the driver
+        * infrastructure. It's released at module unload. */
+       kobject_init(&cnx->kobject);
+       cnx->kobject.ktype = &veth_lpar_connection_ktype;
+       rc = kobject_set_name(&cnx->kobject, "cnx%.2d", rlp);
+       if (rc != 0)
+               return rc;
+
        msgs = kmalloc(VETH_NUMBUFFERS * sizeof(struct veth_msg), GFP_KERNEL);
        if (! msgs) {
-               veth_error("Can't allocate buffers for lpar %d\n", rlp);
+               veth_error("Can't allocate buffers for LPAR %d.\n", rlp);
                return -ENOMEM;
        }
 
        cnx->msgs = msgs;
        memset(msgs, 0, VETH_NUMBUFFERS * sizeof(struct veth_msg));
-       spin_lock_init(&cnx->msg_stack_lock);
 
        for (i = 0; i < VETH_NUMBUFFERS; i++) {
                msgs[i].token = i;
@@ -630,8 +866,7 @@ static int veth_init_connection(u8 rlp)
        cnx->num_events = veth_allocate_events(rlp, 2 + VETH_NUMBUFFERS);
 
        if (cnx->num_events < (2 + VETH_NUMBUFFERS)) {
-               veth_error("Can't allocate events for lpar %d, only got %d\n",
-                          rlp, cnx->num_events);
+               veth_error("Can't allocate enough events for LPAR %d.\n", rlp);
                return -ENOMEM;
        }
 
@@ -642,11 +877,9 @@ static int veth_init_connection(u8 rlp)
        return 0;
 }
 
-static void veth_stop_connection(u8 rlp)
+static void veth_stop_connection(struct veth_lpar_connection *cnx)
 {
-       struct veth_lpar_connection *cnx = veth_cnx[rlp];
-
-       if (! cnx)
+       if (!cnx)
                return;
 
        spin_lock_irq(&cnx->lock);
@@ -654,12 +887,23 @@ static void veth_stop_connection(u8 rlp)
        veth_kick_statemachine(cnx);
        spin_unlock_irq(&cnx->lock);
 
+       /* There's a slim chance the reset code has just queued the
+        * statemachine to run in five seconds. If so we need to cancel
+        * that and requeue the work to run now. */
+       if (cancel_delayed_work(&cnx->statemachine_wq)) {
+               spin_lock_irq(&cnx->lock);
+               veth_kick_statemachine(cnx);
+               spin_unlock_irq(&cnx->lock);
+       }
+
+       /* Wait for the state machine to run. */
        flush_scheduled_work();
+}
 
-       /* FIXME: not sure if this is necessary - will already have
-        * been deleted by the state machine, just want to make sure
-        * its not running any more */
-       del_timer_sync(&cnx->ack_timer);
+static void veth_destroy_connection(struct veth_lpar_connection *cnx)
+{
+       if (!cnx)
+               return;
 
        if (cnx->num_events > 0)
                mf_deallocate_lp_events(cnx->remote_lp,
@@ -671,18 +915,18 @@ static void veth_stop_connection(u8 rlp)
                                      HvLpEvent_Type_VirtualLan,
                                      cnx->num_ack_events,
                                      NULL, NULL);
-}
-
-static void veth_destroy_connection(u8 rlp)
-{
-       struct veth_lpar_connection *cnx = veth_cnx[rlp];
-
-       if (! cnx)
-               return;
 
        kfree(cnx->msgs);
+       veth_cnx[cnx->remote_lp] = NULL;
        kfree(cnx);
-       veth_cnx[rlp] = NULL;
+}
+
+static void veth_release_connection(struct kobject *kobj)
+{
+       struct veth_lpar_connection *cnx;
+       cnx = container_of(kobj, struct veth_lpar_connection, kobject);
+       veth_stop_connection(cnx);
+       veth_destroy_connection(cnx);
 }
 
 /*
@@ -726,17 +970,15 @@ static void veth_set_multicast_list(struct net_device *dev)
 
        write_lock_irqsave(&port->mcast_gate, flags);
 
-       if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
-               printk(KERN_INFO "%s: Promiscuous mode enabled.\n",
-                      dev->name);
+       if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+                       (dev->mc_count > VETH_MAX_MCAST)) {
                port->promiscuous = 1;
-       } else if ( (dev->flags & IFF_ALLMULTI)
-                   || (dev->mc_count > VETH_MAX_MCAST) ) {
-               port->all_mcast = 1;
        } else {
                struct dev_mc_list *dmi = dev->mc_list;
                int i;
 
+               port->promiscuous = 0;
+
                /* Update table */
                port->num_mcast = 0;
 
@@ -758,9 +1000,10 @@ static void veth_set_multicast_list(struct net_device *dev)
 
 static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
-       strncpy(info->driver, "veth", sizeof(info->driver) - 1);
+       strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
        info->driver[sizeof(info->driver) - 1] = '\0';
-       strncpy(info->version, "1.0", sizeof(info->version) - 1);
+       strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
+       info->version[sizeof(info->version) - 1] = '\0';
 }
 
 static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
@@ -791,49 +1034,6 @@ static struct ethtool_ops ops = {
        .get_link = veth_get_link,
 };
 
-static void veth_tx_timeout(struct net_device *dev)
-{
-       struct veth_port *port = (struct veth_port *)dev->priv;
-       struct net_device_stats *stats = &port->stats;
-       unsigned long flags;
-       int i;
-
-       stats->tx_errors++;
-
-       spin_lock_irqsave(&port->pending_gate, flags);
-
-       if (!port->pending_lpmask) {
-               spin_unlock_irqrestore(&port->pending_gate, flags);
-               return;
-       }
-
-       printk(KERN_WARNING "%s: Tx timeout!  Resetting lp connections: %08x\n",
-              dev->name, port->pending_lpmask);
-
-       for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
-               struct veth_lpar_connection *cnx = veth_cnx[i];
-
-               if (! (port->pending_lpmask & (1<<i)))
-                       continue;
-
-               /* If we're pending on it, we must be connected to it,
-                * so we should certainly have a structure for it. */
-               BUG_ON(! cnx);
-
-               /* Theoretically we could be kicking a connection
-                * which doesn't deserve it, but in practice if we've
-                * had a Tx timeout, the pending_lpmask will have
-                * exactly one bit set - the connection causing the
-                * problem. */
-               spin_lock(&cnx->lock);
-               cnx->state |= VETH_STATE_RESET;
-               veth_kick_statemachine(cnx);
-               spin_unlock(&cnx->lock);
-       }
-
-       spin_unlock_irqrestore(&port->pending_gate, flags);
-}
-
 static struct net_device * __init veth_probe_one(int vlan, struct device *vdev)
 {
        struct net_device *dev;
@@ -848,8 +1048,9 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev)
 
        port = (struct veth_port *) dev->priv;
 
-       spin_lock_init(&port->pending_gate);
+       spin_lock_init(&port->queue_lock);
        rwlock_init(&port->mcast_gate);
+       port->stopped_map = 0;
 
        for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
                HvLpVirtualLanIndexMap map;
@@ -882,22 +1083,24 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev)
        dev->set_multicast_list = veth_set_multicast_list;
        SET_ETHTOOL_OPS(dev, &ops);
 
-       dev->watchdog_timeo = 2 * (VETH_ACKTIMEOUT * HZ / 1000000);
-       dev->tx_timeout = veth_tx_timeout;
-
        SET_NETDEV_DEV(dev, vdev);
 
        rc = register_netdev(dev);
        if (rc != 0) {
-               veth_printk(KERN_ERR,
-                           "Failed to register ethernet device for vlan %d\n",
-                           vlan);
+               veth_error("Failed registering net device for vlan%d.\n", vlan);
                free_netdev(dev);
                return NULL;
        }
 
-       veth_printk(KERN_DEBUG, "%s attached to iSeries vlan %d (lpar_map=0x%04x)\n",
-                   dev->name, vlan, port->lpar_map);
+       kobject_init(&port->kobject);
+       port->kobject.parent = &dev->class_dev.kobj;
+       port->kobject.ktype  = &veth_port_ktype;
+       kobject_set_name(&port->kobject, "veth_port");
+       if (0 != kobject_add(&port->kobject))
+               veth_error("Failed adding port for %s to sysfs.\n", dev->name);
+
+       veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n",
+                       dev->name, vlan, port->lpar_map);
 
        return dev;
 }
@@ -912,98 +1115,95 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp,
        struct veth_lpar_connection *cnx = veth_cnx[rlp];
        struct veth_port *port = (struct veth_port *) dev->priv;
        HvLpEvent_Rc rc;
-       u32 dma_address, dma_length;
        struct veth_msg *msg = NULL;
-       int err = 0;
        unsigned long flags;
 
-       if (! cnx) {
-               port->stats.tx_errors++;
-               dev_kfree_skb(skb);
+       if (! cnx)
                return 0;
-       }
 
        spin_lock_irqsave(&cnx->lock, flags);
 
        if (! (cnx->state & VETH_STATE_READY))
-               goto drop;
+               goto no_error;
 
-       if ((skb->len - 14) > VETH_MAX_MTU)
+       if ((skb->len - ETH_HLEN) > VETH_MAX_MTU)
                goto drop;
 
        msg = veth_stack_pop(cnx);
-
-       if (! msg) {
-               err = 1;
+       if (! msg)
                goto drop;
-       }
 
-       dma_length = skb->len;
-       dma_address = dma_map_single(port->dev, skb->data,
-                                    dma_length, DMA_TO_DEVICE);
+       msg->in_use = 1;
+       msg->skb = skb_get(skb);
+
+       msg->data.addr[0] = dma_map_single(port->dev, skb->data,
+                               skb->len, DMA_TO_DEVICE);
 
-       if (dma_mapping_error(dma_address))
+       if (dma_mapping_error(msg->data.addr[0]))
                goto recycle_and_drop;
 
-       /* Is it really necessary to check the length and address
-        * fields of the first entry here? */
-       msg->skb = skb;
        msg->dev = port->dev;
-       msg->data.addr[0] = dma_address;
-       msg->data.len[0] = dma_length;
+       msg->data.len[0] = skb->len;
        msg->data.eofmask = 1 << VETH_EOF_SHIFT;
-       set_bit(0, &(msg->in_use));
-       rc = veth_signaldata(cnx, VethEventTypeFrames, msg->token, &msg->data);
+
+       rc = veth_signaldata(cnx, VETH_EVENT_FRAMES, msg->token, &msg->data);
 
        if (rc != HvLpEvent_Rc_Good)
                goto recycle_and_drop;
 
+       /* If the timer's not already running, start it now. */
+       if (0 == cnx->outstanding_tx)
+               mod_timer(&cnx->reset_timer, jiffies + cnx->reset_timeout);
+
+       cnx->last_contact = jiffies;
+       cnx->outstanding_tx++;
+
+       if (veth_stack_is_empty(cnx))
+               veth_stop_queues(cnx);
+
+ no_error:
        spin_unlock_irqrestore(&cnx->lock, flags);
        return 0;
 
  recycle_and_drop:
-       msg->skb = NULL;
-       /* need to set in use to make veth_recycle_msg in case this
-        * was a mapping failure */
-       set_bit(0, &msg->in_use);
        veth_recycle_msg(cnx, msg);
  drop:
-       port->stats.tx_errors++;
-       dev_kfree_skb(skb);
        spin_unlock_irqrestore(&cnx->lock, flags);
-       return err;
+       return 1;
 }
 
-static HvLpIndexMap veth_transmit_to_many(struct sk_buff *skb,
+static void veth_transmit_to_many(struct sk_buff *skb,
                                          HvLpIndexMap lpmask,
                                          struct net_device *dev)
 {
        struct veth_port *port = (struct veth_port *) dev->priv;
-       int i;
-       int rc;
+       int i, success, error;
+
+       success = error = 0;
 
        for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
                if ((lpmask & (1 << i)) == 0)
                        continue;
 
-               rc = veth_transmit_to_one(skb_get(skb), i, dev);
-               if (! rc)
-                       lpmask &= ~(1<<i);
+               if (veth_transmit_to_one(skb, i, dev))
+                       error = 1;
+               else
+                       success = 1;
        }
 
-       if (! lpmask) {
+       if (error)
+               port->stats.tx_errors++;
+
+       if (success) {
                port->stats.tx_packets++;
                port->stats.tx_bytes += skb->len;
        }
-
-       return lpmask;
 }
 
 static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        unsigned char *frame = skb->data;
        struct veth_port *port = (struct veth_port *) dev->priv;
-       unsigned long flags;
        HvLpIndexMap lpmask;
 
        if (! (frame[0] & 0x01)) {
@@ -1020,44 +1220,27 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
                lpmask = port->lpar_map;
        }
 
-       spin_lock_irqsave(&port->pending_gate, flags);
-
-       lpmask = veth_transmit_to_many(skb, lpmask, dev);
-
-       dev->trans_start = jiffies;
+       veth_transmit_to_many(skb, lpmask, dev);
 
-       if (! lpmask) {
-               dev_kfree_skb(skb);
-       } else {
-               if (port->pending_skb) {
-                       veth_error("%s: Tx while skb was pending!\n",
-                                  dev->name);
-                       dev_kfree_skb(skb);
-                       spin_unlock_irqrestore(&port->pending_gate, flags);
-                       return 1;
-               }
-
-               port->pending_skb = skb;
-               port->pending_lpmask = lpmask;
-               netif_stop_queue(dev);
-       }
-
-       spin_unlock_irqrestore(&port->pending_gate, flags);
+       dev_kfree_skb(skb);
 
        return 0;
 }
 
+/* You must hold the connection's lock when you call this function. */
 static void veth_recycle_msg(struct veth_lpar_connection *cnx,
                             struct veth_msg *msg)
 {
        u32 dma_address, dma_length;
 
-       if (test_and_clear_bit(0, &msg->in_use)) {
+       if (msg->in_use) {
+               msg->in_use = 0;
                dma_address = msg->data.addr[0];
                dma_length = msg->data.len[0];
 
-               dma_unmap_single(msg->dev, dma_address, dma_length,
-                                DMA_TO_DEVICE);
+               if (!dma_mapping_error(dma_address))
+                       dma_unmap_single(msg->dev, dma_address, dma_length,
+                                       DMA_TO_DEVICE);
 
                if (msg->skb) {
                        dev_kfree_skb_any(msg->skb);
@@ -1066,15 +1249,16 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx,
 
                memset(&msg->data, 0, sizeof(msg->data));
                veth_stack_push(cnx, msg);
-       } else
-               if (cnx->state & VETH_STATE_OPEN)
-                       veth_error("Bogus frames ack from lpar %d (#%d)\n",
-                                  cnx->remote_lp, msg->token);
+       } else if (cnx->state & VETH_STATE_OPEN) {
+               veth_error("Non-pending frame (# %d) acked by LPAR %d.\n",
+                               cnx->remote_lp, msg->token);
+       }
 }
 
-static void veth_flush_pending(struct veth_lpar_connection *cnx)
+static void veth_wake_queues(struct veth_lpar_connection *cnx)
 {
        int i;
+
        for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
                struct net_device *dev = veth_dev[i];
                struct veth_port *port;
@@ -1088,20 +1272,77 @@ static void veth_flush_pending(struct veth_lpar_connection *cnx)
                if (! (port->lpar_map & (1<<cnx->remote_lp)))
                        continue;
 
-               spin_lock_irqsave(&port->pending_gate, flags);
-               if (port->pending_skb) {
-                       port->pending_lpmask =
-                               veth_transmit_to_many(port->pending_skb,
-                                                     port->pending_lpmask,
-                                                     dev);
-                       if (! port->pending_lpmask) {
-                               dev_kfree_skb_any(port->pending_skb);
-                               port->pending_skb = NULL;
-                               netif_wake_queue(dev);
-                       }
+               spin_lock_irqsave(&port->queue_lock, flags);
+
+               port->stopped_map &= ~(1 << cnx->remote_lp);
+
+               if (0 == port->stopped_map && netif_queue_stopped(dev)) {
+                       veth_debug("cnx %d: woke queue for %s.\n",
+                                       cnx->remote_lp, dev->name);
+                       netif_wake_queue(dev);
+               }
+               spin_unlock_irqrestore(&port->queue_lock, flags);
+       }
+}
+
+static void veth_stop_queues(struct veth_lpar_connection *cnx)
+{
+       int i;
+
+       for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
+               struct net_device *dev = veth_dev[i];
+               struct veth_port *port;
+
+               if (! dev)
+                       continue;
+
+               port = (struct veth_port *)dev->priv;
+
+               /* If this cnx is not on the vlan for this port, continue */
+               if (! (port->lpar_map & (1 << cnx->remote_lp)))
+                       continue;
+
+               spin_lock(&port->queue_lock);
+
+               netif_stop_queue(dev);
+               port->stopped_map |= (1 << cnx->remote_lp);
+
+               veth_debug("cnx %d: stopped queue for %s, map = 0x%x.\n",
+                               cnx->remote_lp, dev->name, port->stopped_map);
+
+               spin_unlock(&port->queue_lock);
+       }
+}
+
+static void veth_timed_reset(unsigned long ptr)
+{
+       struct veth_lpar_connection *cnx = (struct veth_lpar_connection *)ptr;
+       unsigned long trigger_time, flags;
+
+       /* FIXME is it possible this fires after veth_stop_connection()?
+        * That would reschedule the statemachine for 5 seconds and probably
+        * execute it after the module's been unloaded. Hmm. */
+
+       spin_lock_irqsave(&cnx->lock, flags);
+
+       if (cnx->outstanding_tx > 0) {
+               trigger_time = cnx->last_contact + cnx->reset_timeout;
+
+               if (trigger_time < jiffies) {
+                       cnx->state |= VETH_STATE_RESET;
+                       veth_kick_statemachine(cnx);
+                       veth_error("%d packets not acked by LPAR %d within %d "
+                                       "seconds, resetting.\n",
+                                       cnx->outstanding_tx, cnx->remote_lp,
+                                       cnx->reset_timeout / HZ);
+               } else {
+                       /* Reschedule the timer */
+                       trigger_time = jiffies + cnx->reset_timeout;
+                       mod_timer(&cnx->reset_timer, trigger_time);
                }
-               spin_unlock_irqrestore(&port->pending_gate, flags);
        }
+
+       spin_unlock_irqrestore(&cnx->lock, flags);
 }
 
 /*
@@ -1117,12 +1358,9 @@ static inline int veth_frame_wanted(struct veth_port *port, u64 mac_addr)
        if ( (mac_addr == port->mac_addr) || (mac_addr == 0xffffffffffff0000) )
                return 1;
 
-       if (! (((char *) &mac_addr)[0] & 0x01))
-               return 0;
-
        read_lock_irqsave(&port->mcast_gate, flags);
 
-       if (port->promiscuous || port->all_mcast) {
+       if (port->promiscuous) {
                wanted = 1;
                goto out;
        }
@@ -1175,21 +1413,21 @@ static void veth_flush_acks(struct veth_lpar_connection *cnx)
 {
        HvLpEvent_Rc rc;
 
-       rc = veth_signaldata(cnx, VethEventTypeFramesAck,
+       rc = veth_signaldata(cnx, VETH_EVENT_FRAMES_ACK,
                             0, &cnx->pending_acks);
 
        if (rc != HvLpEvent_Rc_Good)
-               veth_error("Error 0x%x acking frames from lpar %d!\n",
-                          (unsigned)rc, cnx->remote_lp);
+               veth_error("Failed acking frames from LPAR %d, rc = %d\n",
+                               cnx->remote_lp, (int)rc);
 
        cnx->num_pending_acks = 0;
        memset(&cnx->pending_acks, 0xff, sizeof(cnx->pending_acks));
 }
 
 static void veth_receive(struct veth_lpar_connection *cnx,
-                        struct VethLpEvent *event)
+                        struct veth_lpevent *event)
 {
-       struct VethFramesData *senddata = &event->u.frames_data;
+       struct veth_frames_data *senddata = &event->u.frames_data;
        int startchunk = 0;
        int nchunks;
        unsigned long flags;
@@ -1216,9 +1454,10 @@ static void veth_receive(struct veth_lpar_connection *cnx,
                /* make sure that we have at least 1 EOF entry in the
                 * remaining entries */
                if (! (senddata->eofmask >> (startchunk + VETH_EOF_SHIFT))) {
-                       veth_error("missing EOF frag in event "
-                                  "eofmask=0x%x startchunk=%d\n",
-                                  (unsigned) senddata->eofmask, startchunk);
+                       veth_error("Missing EOF fragment in event "
+                                       "eofmask = 0x%x startchunk = %d\n",
+                                       (unsigned)senddata->eofmask,
+                                       startchunk);
                        break;
                }
 
@@ -1237,8 +1476,9 @@ static void veth_receive(struct veth_lpar_connection *cnx,
                /* nchunks == # of chunks in this frame */
 
                if ((length - ETH_HLEN) > VETH_MAX_MTU) {
-                       veth_error("Received oversize frame from lpar %d "
-                                  "(length=%d)\n", cnx->remote_lp, length);
+                       veth_error("Received oversize frame from LPAR %d "
+                                       "(length = %d)\n",
+                                       cnx->remote_lp, length);
                        continue;
                }
 
@@ -1331,15 +1571,33 @@ static void veth_timed_ack(unsigned long ptr)
 
 static int veth_remove(struct vio_dev *vdev)
 {
-       int i = vdev->unit_address;
+       struct veth_lpar_connection *cnx;
        struct net_device *dev;
+       struct veth_port *port;
+       int i;
 
-       dev = veth_dev[i];
-       if (dev != NULL) {
-               veth_dev[i] = NULL;
-               unregister_netdev(dev);
-               free_netdev(dev);
+       dev = veth_dev[vdev->unit_address];
+
+       if (! dev)
+               return 0;
+
+       port = netdev_priv(dev);
+
+       for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
+               cnx = veth_cnx[i];
+
+               if (cnx && (port->lpar_map & (1 << i))) {
+                       /* Drop our reference to connections on our VLAN */
+                       kobject_put(&cnx->kobject);
+               }
        }
+
+       veth_dev[vdev->unit_address] = NULL;
+       kobject_del(&port->kobject);
+       kobject_put(&port->kobject);
+       unregister_netdev(dev);
+       free_netdev(dev);
+
        return 0;
 }
 
@@ -1347,6 +1605,7 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id)
 {
        int i = vdev->unit_address;
        struct net_device *dev;
+       struct veth_port *port;
 
        dev = veth_probe_one(i, &vdev->dev);
        if (dev == NULL) {
@@ -1355,11 +1614,23 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        }
        veth_dev[i] = dev;
 
-       /* Start the state machine on each connection, to commence
-        * link negotiation */
-       for (i = 0; i < HVMAXARCHITECTEDLPS; i++)
-               if (veth_cnx[i])
-                       veth_kick_statemachine(veth_cnx[i]);
+       port = (struct veth_port*)netdev_priv(dev);
+
+       /* Start the state machine on each connection on this vlan. If we're
+        * the first dev to do so this will commence link negotiation */
+       for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
+               struct veth_lpar_connection *cnx;
+
+               if (! (port->lpar_map & (1 << i)))
+                       continue;
+
+               cnx = veth_cnx[i];
+               if (!cnx)
+                       continue;
+
+               kobject_get(&cnx->kobject);
+               veth_kick_statemachine(cnx);
+       }
 
        return 0;
 }
@@ -1375,7 +1646,7 @@ static struct vio_device_id veth_device_table[] __devinitdata = {
 MODULE_DEVICE_TABLE(vio, veth_device_table);
 
 static struct vio_driver veth_driver = {
-       .name = "iseries_veth",
+       .name = DRV_NAME,
        .id_table = veth_device_table,
        .probe = veth_probe,
        .remove = veth_remove
@@ -1388,29 +1659,29 @@ static struct vio_driver veth_driver = {
 void __exit veth_module_cleanup(void)
 {
        int i;
+       struct veth_lpar_connection *cnx;
 
-       /* Stop the queues first to stop any new packets being sent. */
-       for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++)
-               if (veth_dev[i])
-                       netif_stop_queue(veth_dev[i]);
-
-       /* Stop the connections before we unregister the driver. This
-        * ensures there's no skbs lying around holding the device open. */
-       for (i = 0; i < HVMAXARCHITECTEDLPS; ++i)
-               veth_stop_connection(i);
-
+       /* Disconnect our "irq" to stop events coming from the Hypervisor. */
        HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan);
 
-       /* Hypervisor callbacks may have scheduled more work while we
-        * were stoping connections. Now that we've disconnected from
-        * the hypervisor make sure everything's finished. */
+       /* Make sure any work queued from Hypervisor callbacks is finished. */
        flush_scheduled_work();
 
-       vio_unregister_driver(&veth_driver);
+       for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
+               cnx = veth_cnx[i];
+
+               if (!cnx)
+                       continue;
 
-       for (i = 0; i < HVMAXARCHITECTEDLPS; ++i)
-               veth_destroy_connection(i);
+               /* Remove the connection from sysfs */
+               kobject_del(&cnx->kobject);
+               /* Drop the driver's reference to the connection */
+               kobject_put(&cnx->kobject);
+       }
 
+       /* Unregister the driver, which will close all the netdevs and stop
+        * the connections when they're no longer referenced. */
+       vio_unregister_driver(&veth_driver);
 }
 module_exit(veth_module_cleanup);
 
@@ -1423,15 +1694,37 @@ int __init veth_module_init(void)
 
        for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
                rc = veth_init_connection(i);
-               if (rc != 0) {
-                       veth_module_cleanup();
-                       return rc;
-               }
+               if (rc != 0)
+                       goto error;
        }
 
        HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan,
                                  &veth_handle_event);
 
-       return vio_register_driver(&veth_driver);
+       rc = vio_register_driver(&veth_driver);
+       if (rc != 0)
+               goto error;
+
+       for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
+               struct kobject *kobj;
+
+               if (!veth_cnx[i])
+                       continue;
+
+               kobj = &veth_cnx[i]->kobject;
+               kobj->parent = &veth_driver.driver.kobj;
+               /* If the add failes, complain but otherwise continue */
+               if (0 != kobject_add(kobj))
+                       veth_error("cnx %d: Failed adding to sysfs.\n", i);
+       }
+
+       return 0;
+
+error:
+       for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
+               veth_destroy_connection(veth_cnx[i]);
+       }
+
+       return rc;
 }
 module_init(veth_module_init);
diff --git a/drivers/net/iseries_veth.h b/drivers/net/iseries_veth.h
deleted file mode 100644 (file)
index d9370f7..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* File veth.h created by Kyle A. Lucke on Mon Aug  7 2000. */
-
-#ifndef _ISERIES_VETH_H
-#define _ISERIES_VETH_H
-
-#define VethEventTypeCap       (0)
-#define VethEventTypeFrames    (1)
-#define VethEventTypeMonitor   (2)
-#define VethEventTypeFramesAck (3)
-
-#define VETH_MAX_ACKS_PER_MSG  (20)
-#define VETH_MAX_FRAMES_PER_MSG        (6)
-
-struct VethFramesData {
-       u32 addr[VETH_MAX_FRAMES_PER_MSG];
-       u16 len[VETH_MAX_FRAMES_PER_MSG];
-       u32 eofmask;
-};
-#define VETH_EOF_SHIFT         (32-VETH_MAX_FRAMES_PER_MSG)
-
-struct VethFramesAckData {
-       u16 token[VETH_MAX_ACKS_PER_MSG];
-};
-
-struct VethCapData {
-       u8 caps_version;
-       u8 rsvd1;
-       u16 num_buffers;
-       u16 ack_threshold;
-       u16 rsvd2;
-       u32 ack_timeout;
-       u32 rsvd3;
-       u64 rsvd4[3];
-};
-
-struct VethLpEvent {
-       struct HvLpEvent base_event;
-       union {
-               struct VethCapData caps_data;
-               struct VethFramesData frames_data;
-               struct VethFramesAckData frames_ack_data;
-       } u;
-
-};
-
-#endif /* _ISERIES_VETH_H */
index fc353e348f9aeaab909b216d8301706d014178af..a22d00198e4d6b5cde5b46ebff6cb1e569e6f68e 100644 (file)
@@ -1934,7 +1934,7 @@ static int __init de_init_one (struct pci_dev *pdev,
        struct de_private *de;
        int rc;
        void __iomem *regs;
-       long pciaddr;
+       unsigned long pciaddr;
        static int board_idx = -1;
 
        board_idx++;
index 05da5bea564c9481846b9109aac24712b6f3ed3c..6266a9a7e6e3a36430b33039af682f3dbdd15607 100644 (file)
@@ -238,6 +238,7 @@ static struct pci_device_id tulip_pci_tbl[] = {
        { 0x17B3, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
        { 0x10b7, 0x9300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* 3Com 3CSOHO100B-TX */
        { 0x14ea, 0xab08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Planex FNW-3602-TX */
+       { 0x1414, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
        { } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, tulip_pci_tbl);
index ec3f75a030d2073248791d443d9f432027dd35bf..dd7dbf7b14d4514965655361f03d3e4ba9a707ee 100644 (file)
@@ -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
index 2b87841322cc8f7d27fac0a36bdb063d204d7420..0953cc0cdee66c81b23f8f138e1f94c85b2eafeb 100644 (file)
@@ -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
index df20adcd0730aa1cf5fb899d7b2017209782fb6c..6db1fb6461def034c898badd7c9a30bda7c4383d 100644 (file)
@@ -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 */
index 18a7d38d2a1301f833dd36eb4ed587d73390e109..f48a6e7292245298e3b73d7c31d7c1cd84b9b37c 100644 (file)
@@ -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:
                
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
new file mode 100644 (file)
index 0000000..1445f3f
--- /dev/null
@@ -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".
diff --git a/drivers/net/wireless/hostap/Makefile b/drivers/net/wireless/hostap/Makefile
new file mode 100644 (file)
index 0000000..fc62235
--- /dev/null
@@ -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
diff --git a/drivers/net/wireless/hostap/hostap.c b/drivers/net/wireless/hostap/hostap.c
new file mode 100644 (file)
index 0000000..e7f5821
--- /dev/null
@@ -0,0 +1,1198 @@
+/*
+ * Host AP (software wireless LAN access point) driver for
+ * Intersil Prism2/2.5/3 - hostap.o module, common routines
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2005, 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/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/workqueue.h>
+#include <linux/kmod.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211_crypt.h>
+#include <asm/uaccess.h>
+
+#include "hostap_wlan.h"
+#include "hostap_80211.h"
+#include "hostap_ap.h"
+#include "hostap.h"
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP common routines");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+#define TX_TIMEOUT (2 * HZ)
+
+#define PRISM2_MAX_FRAME_SIZE 2304
+#define PRISM2_MIN_MTU 256
+/* FIX: */
+#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */))
+
+
+/* hostap.c */
+static int prism2_wds_add(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked);
+static int prism2_wds_del(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked, int do_not_remove);
+
+/* hostap_ap.c */
+static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
+                                 struct iw_quality qual[], int buf_size,
+                                 int aplist);
+static int prism2_ap_translate_scan(struct net_device *dev, char *buffer);
+static int prism2_hostapd(struct ap_data *ap,
+                         struct prism2_hostapd_param *param);
+static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
+                               struct ieee80211_crypt_data ***crypt);
+static void ap_control_kickall(struct ap_data *ap);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static int ap_control_add_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac);
+static int ap_control_del_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac);
+static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions);
+static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev,
+                              u8 *mac);
+#endif /* !PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+                                 2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+#define FREQ_COUNT (sizeof(freq_list) / sizeof(freq_list[0]))
+
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+
+/* FIX: these could be compiled separately and linked together to hostap.o */
+#include "hostap_ap.c"
+#include "hostap_info.c"
+#include "hostap_ioctl.c"
+#include "hostap_proc.c"
+#include "hostap_80211_rx.c"
+#include "hostap_80211_tx.c"
+
+
+struct net_device * hostap_add_interface(struct local_info *local,
+                                        int type, int rtnl_locked,
+                                        const char *prefix,
+                                        const char *name)
+{
+       struct net_device *dev, *mdev;
+       struct hostap_interface *iface;
+       int ret;
+
+       dev = alloc_etherdev(sizeof(struct hostap_interface));
+       if (dev == NULL)
+               return NULL;
+
+       iface = netdev_priv(dev);
+       iface->dev = dev;
+       iface->local = local;
+       iface->type = type;
+       list_add(&iface->list, &local->hostap_interfaces);
+
+       mdev = local->dev;
+       memcpy(dev->dev_addr, mdev->dev_addr, ETH_ALEN);
+       dev->base_addr = mdev->base_addr;
+       dev->irq = mdev->irq;
+       dev->mem_start = mdev->mem_start;
+       dev->mem_end = mdev->mem_end;
+
+       hostap_setup_dev(dev, local, 0);
+       dev->destructor = free_netdev;
+
+       sprintf(dev->name, "%s%s", prefix, name);
+       if (!rtnl_locked)
+               rtnl_lock();
+
+       ret = 0;
+       if (strchr(dev->name, '%'))
+               ret = dev_alloc_name(dev, dev->name);
+
+       SET_NETDEV_DEV(dev, mdev->class_dev.dev);
+       if (ret >= 0)
+               ret = register_netdevice(dev);
+
+       if (!rtnl_locked)
+               rtnl_unlock();
+
+       if (ret < 0) {
+               printk(KERN_WARNING "%s: failed to add new netdevice!\n",
+                      dev->name);
+               free_netdev(dev);
+               return NULL;
+       }
+
+       printk(KERN_DEBUG "%s: registered netdevice %s\n",
+              mdev->name, dev->name);
+
+       return dev;
+}
+
+
+void hostap_remove_interface(struct net_device *dev, int rtnl_locked,
+                            int remove_from_list)
+{
+       struct hostap_interface *iface;
+
+       if (!dev)
+               return;
+
+       iface = netdev_priv(dev);
+
+       if (remove_from_list) {
+               list_del(&iface->list);
+       }
+
+       if (dev == iface->local->ddev)
+               iface->local->ddev = NULL;
+       else if (dev == iface->local->apdev)
+               iface->local->apdev = NULL;
+       else if (dev == iface->local->stadev)
+               iface->local->stadev = NULL;
+
+       if (rtnl_locked)
+               unregister_netdevice(dev);
+       else
+               unregister_netdev(dev);
+
+       /* dev->destructor = free_netdev() will free the device data, including
+        * private data, when removing the device */
+}
+
+
+static inline int prism2_wds_special_addr(u8 *addr)
+{
+       if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5])
+               return 0;
+
+       return 1;
+}
+
+
+static int prism2_wds_add(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked)
+{
+       struct net_device *dev;
+       struct list_head *ptr;
+       struct hostap_interface *iface, *empty, *match;
+
+       empty = match = NULL;
+       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;
+
+               if (prism2_wds_special_addr(iface->u.wds.remote_addr))
+                       empty = iface;
+               else if (memcmp(iface->u.wds.remote_addr, remote_addr,
+                               ETH_ALEN) == 0) {
+                       match = iface;
+                       break;
+               }
+       }
+       if (!match && empty && !prism2_wds_special_addr(remote_addr)) {
+               /* take pre-allocated entry into use */
+               memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN);
+               read_unlock_bh(&local->iface_lock);
+               printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n",
+                      local->dev->name, empty->dev->name);
+               return 0;
+       }
+       read_unlock_bh(&local->iface_lock);
+
+       if (!prism2_wds_special_addr(remote_addr)) {
+               if (match)
+                       return -EEXIST;
+               hostap_add_sta(local->ap, remote_addr);
+       }
+
+       if (local->wds_connections >= local->wds_max_connections)
+               return -ENOBUFS;
+
+       /* verify that there is room for wds# postfix in the interface name */
+       if (strlen(local->dev->name) > IFNAMSIZ - 5) {
+               printk(KERN_DEBUG "'%s' too long base device name\n",
+                      local->dev->name);
+               return -EINVAL;
+       }
+
+       dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked,
+                                  local->ddev->name, "wds%d");
+       if (dev == NULL)
+               return -ENOMEM;
+
+       iface = netdev_priv(dev);
+       memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN);
+
+       local->wds_connections++;
+
+       return 0;
+}
+
+
+static int prism2_wds_del(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked, int do_not_remove)
+{
+       unsigned long flags;
+       struct list_head *ptr;
+       struct hostap_interface *iface, *selected = NULL;
+
+       write_lock_irqsave(&local->iface_lock, flags);
+       list_for_each(ptr, &local->hostap_interfaces) {
+               iface = list_entry(ptr, struct hostap_interface, list);
+               if (iface->type != HOSTAP_INTERFACE_WDS)
+                       continue;
+
+               if (memcmp(iface->u.wds.remote_addr, remote_addr,
+                          ETH_ALEN) == 0) {
+                       selected = iface;
+                       break;
+               }
+       }
+       if (selected && !do_not_remove)
+               list_del(&selected->list);
+       write_unlock_irqrestore(&local->iface_lock, flags);
+
+       if (selected) {
+               if (do_not_remove)
+                       memset(selected->u.wds.remote_addr, 0, ETH_ALEN);
+               else {
+                       hostap_remove_interface(selected->dev, rtnl_locked, 0);
+                       local->wds_connections--;
+               }
+       }
+
+       return selected ? 0 : -ENODEV;
+}
+
+
+u16 hostap_tx_callback_register(local_info_t *local,
+                               void (*func)(struct sk_buff *, int ok, void *),
+                               void *data)
+{
+       unsigned long flags;
+       struct hostap_tx_callback_info *entry;
+
+       entry = (struct hostap_tx_callback_info *) kmalloc(sizeof(*entry),
+                                                          GFP_ATOMIC);
+       if (entry == NULL)
+               return 0;
+
+       entry->func = func;
+       entry->data = data;
+
+       spin_lock_irqsave(&local->lock, flags);
+       entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1;
+       entry->next = local->tx_callback;
+       local->tx_callback = entry;
+       spin_unlock_irqrestore(&local->lock, flags);
+
+       return entry->idx;
+}
+
+
+int hostap_tx_callback_unregister(local_info_t *local, u16 idx)
+{
+       unsigned long flags;
+       struct hostap_tx_callback_info *cb, *prev = NULL;
+
+       spin_lock_irqsave(&local->lock, flags);
+       cb = local->tx_callback;
+       while (cb != NULL && cb->idx != idx) {
+               prev = cb;
+               cb = cb->next;
+       }
+       if (cb) {
+               if (prev == NULL)
+                       local->tx_callback = cb->next;
+               else
+                       prev->next = cb->next;
+               kfree(cb);
+       }
+       spin_unlock_irqrestore(&local->lock, flags);
+
+       return cb ? 0 : -1;
+}
+
+
+/* val is in host byte order */
+int hostap_set_word(struct net_device *dev, int rid, u16 val)
+{
+       struct hostap_interface *iface;
+       u16 tmp = cpu_to_le16(val);
+       iface = netdev_priv(dev);
+       return iface->local->func->set_rid(dev, rid, &tmp, 2);
+}
+
+
+int hostap_set_string(struct net_device *dev, int rid, const char *val)
+{
+       struct hostap_interface *iface;
+       char buf[MAX_SSID_LEN + 2];
+       int len;
+
+       iface = netdev_priv(dev);
+       len = strlen(val);
+       if (len > MAX_SSID_LEN)
+               return -1;
+       memset(buf, 0, sizeof(buf));
+       buf[0] = len; /* little endian 16 bit word */
+       memcpy(buf + 2, val, len);
+
+       return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2);
+}
+
+
+u16 hostap_get_porttype(local_info_t *local)
+{
+       if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc)
+               return HFA384X_PORTTYPE_PSEUDO_IBSS;
+       if (local->iw_mode == IW_MODE_ADHOC)
+               return HFA384X_PORTTYPE_IBSS;
+       if (local->iw_mode == IW_MODE_INFRA)
+               return HFA384X_PORTTYPE_BSS;
+       if (local->iw_mode == IW_MODE_REPEAT)
+               return HFA384X_PORTTYPE_WDS;
+       if (local->iw_mode == IW_MODE_MONITOR)
+               return HFA384X_PORTTYPE_PSEUDO_IBSS;
+       return HFA384X_PORTTYPE_HOSTAP;
+}
+
+
+int hostap_set_encryption(local_info_t *local)
+{
+       u16 val, old_val;
+       int i, keylen, len, idx;
+       char keybuf[WEP_KEY_LEN + 1];
+       enum { NONE, WEP, OTHER } encrypt_type;
+
+       idx = local->tx_keyidx;
+       if (local->crypt[idx] == NULL || local->crypt[idx]->ops == NULL)
+               encrypt_type = NONE;
+       else if (strcmp(local->crypt[idx]->ops->name, "WEP") == 0)
+               encrypt_type = WEP;
+       else
+               encrypt_type = OTHER;
+
+       if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2,
+                                1) < 0) {
+               printk(KERN_DEBUG "Could not read current WEP flags.\n");
+               goto fail;
+       }
+       le16_to_cpus(&val);
+       old_val = val;
+
+       if (encrypt_type != NONE || local->privacy_invoked)
+               val |= HFA384X_WEPFLAGS_PRIVACYINVOKED;
+       else
+               val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED;
+
+       if (local->open_wep || encrypt_type == NONE ||
+           ((local->ieee_802_1x || local->wpa) && local->host_decrypt))
+               val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED;
+       else
+               val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED;
+
+       if ((encrypt_type != NONE || local->privacy_invoked) &&
+           (encrypt_type == OTHER || local->host_encrypt))
+               val |= HFA384X_WEPFLAGS_HOSTENCRYPT;
+       else
+               val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT;
+       if ((encrypt_type != NONE || local->privacy_invoked) &&
+           (encrypt_type == OTHER || local->host_decrypt))
+               val |= HFA384X_WEPFLAGS_HOSTDECRYPT;
+       else
+               val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT;
+
+       if (val != old_val &&
+           hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) {
+               printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n",
+                      val);
+               goto fail;
+       }
+
+       if (encrypt_type != WEP)
+               return 0;
+
+       /* 104-bit support seems to require that all the keys are set to the
+        * same keylen */
+       keylen = 6; /* first 5 octets */
+       len = local->crypt[idx]->ops->get_key(keybuf, sizeof(keybuf),
+                                             NULL, local->crypt[idx]->priv);
+       if (idx >= 0 && idx < WEP_KEYS && len > 5)
+               keylen = WEP_KEY_LEN + 1; /* first 13 octets */
+
+       for (i = 0; i < WEP_KEYS; i++) {
+               memset(keybuf, 0, sizeof(keybuf));
+               if (local->crypt[i]) {
+                       (void) local->crypt[i]->ops->get_key(
+                               keybuf, sizeof(keybuf),
+                               NULL, local->crypt[i]->priv);
+               }
+               if (local->func->set_rid(local->dev,
+                                        HFA384X_RID_CNFDEFAULTKEY0 + i,
+                                        keybuf, keylen)) {
+                       printk(KERN_DEBUG "Could not set key %d (len=%d)\n",
+                              i, keylen);
+                       goto fail;
+               }
+       }
+       if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) {
+               printk(KERN_DEBUG "Could not set default keyid %d\n", idx);
+               goto fail;
+       }
+
+       return 0;
+
+ fail:
+       printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name);
+       return -1;
+}
+
+
+int hostap_set_antsel(local_info_t *local)
+{
+       u16 val;
+       int ret = 0;
+
+       if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH &&
+           local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF,
+                            HFA386X_CR_TX_CONFIGURE,
+                            NULL, &val) == 0) {
+               val &= ~(BIT(2) | BIT(1));
+               switch (local->antsel_tx) {
+               case HOSTAP_ANTSEL_DIVERSITY:
+                       val |= BIT(1);
+                       break;
+               case HOSTAP_ANTSEL_LOW:
+                       break;
+               case HOSTAP_ANTSEL_HIGH:
+                       val |= BIT(2);
+                       break;
+               }
+
+               if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF,
+                                    HFA386X_CR_TX_CONFIGURE, &val, NULL)) {
+                       printk(KERN_INFO "%s: setting TX AntSel failed\n",
+                              local->dev->name);
+                       ret = -1;
+               }
+       }
+
+       if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH &&
+           local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF,
+                            HFA386X_CR_RX_CONFIGURE,
+                            NULL, &val) == 0) {
+               val &= ~(BIT(1) | BIT(0));
+               switch (local->antsel_rx) {
+               case HOSTAP_ANTSEL_DIVERSITY:
+                       break;
+               case HOSTAP_ANTSEL_LOW:
+                       val |= BIT(0);
+                       break;
+               case HOSTAP_ANTSEL_HIGH:
+                       val |= BIT(0) | BIT(1);
+                       break;
+               }
+
+               if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF,
+                                    HFA386X_CR_RX_CONFIGURE, &val, NULL)) {
+                       printk(KERN_INFO "%s: setting RX AntSel failed\n",
+                              local->dev->name);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+
+int hostap_set_roaming(local_info_t *local)
+{
+       u16 val;
+
+       switch (local->host_roaming) {
+       case 1:
+               val = HFA384X_ROAMING_HOST;
+               break;
+       case 2:
+               val = HFA384X_ROAMING_DISABLED;
+               break;
+       case 0:
+       default:
+               val = HFA384X_ROAMING_FIRMWARE;
+               break;
+       }
+
+       return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val);
+}
+
+
+int hostap_set_auth_algs(local_info_t *local)
+{
+       int val = local->auth_algs;
+       /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication
+        * set to include both Open and Shared Key flags. It tries to use
+        * Shared Key authentication in that case even if WEP keys are not
+        * configured.. STA f/w v0.7.6 is able to handle such configuration,
+        * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */
+       if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) &&
+           val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY)
+               val = PRISM2_AUTH_OPEN;
+
+       if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) {
+               printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x "
+                      "failed\n", local->dev->name, local->auth_algs);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx)
+{
+       u16 status, fc;
+
+       status = __le16_to_cpu(rx->status);
+
+       printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, "
+              "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; "
+              "jiffies=%ld\n",
+              name, status, (status >> 8) & 0x07, status >> 13, status & 1,
+              rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies);
+
+       fc = __le16_to_cpu(rx->frame_control);
+       printk(KERN_DEBUG "   FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
+              "data_len=%d%s%s\n",
+              fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+              __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl),
+              __le16_to_cpu(rx->data_len),
+              fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
+              fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
+
+       printk(KERN_DEBUG "   A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4="
+              MACSTR "\n",
+              MAC2STR(rx->addr1), MAC2STR(rx->addr2), MAC2STR(rx->addr3),
+              MAC2STR(rx->addr4));
+
+       printk(KERN_DEBUG "   dst=" MACSTR " src=" MACSTR " len=%d\n",
+              MAC2STR(rx->dst_addr), MAC2STR(rx->src_addr),
+              __be16_to_cpu(rx->len));
+}
+
+
+void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
+{
+       u16 fc;
+
+       printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d "
+              "tx_control=0x%04x; jiffies=%ld\n",
+              name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate,
+              __le16_to_cpu(tx->tx_control), jiffies);
+
+       fc = __le16_to_cpu(tx->frame_control);
+       printk(KERN_DEBUG "   FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
+              "data_len=%d%s%s\n",
+              fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+              __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl),
+              __le16_to_cpu(tx->data_len),
+              fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
+              fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
+
+       printk(KERN_DEBUG "   A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4="
+              MACSTR "\n",
+              MAC2STR(tx->addr1), MAC2STR(tx->addr2), MAC2STR(tx->addr3),
+              MAC2STR(tx->addr4));
+
+       printk(KERN_DEBUG "   dst=" MACSTR " src=" MACSTR " len=%d\n",
+              MAC2STR(tx->dst_addr), MAC2STR(tx->src_addr),
+              __be16_to_cpu(tx->len));
+}
+
+
+int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+       memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */
+       return ETH_ALEN;
+}
+
+
+int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+       if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) {
+               memcpy(haddr, skb->mac.raw +
+                      sizeof(struct linux_wlan_ng_prism_hdr) + 10,
+                      ETH_ALEN); /* addr2 */
+       } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */
+               memcpy(haddr, skb->mac.raw +
+                      sizeof(struct linux_wlan_ng_cap_hdr) + 10,
+                      ETH_ALEN); /* addr2 */
+       }
+       return ETH_ALEN;
+}
+
+
+int hostap_80211_get_hdrlen(u16 fc)
+{
+       int hdrlen = 24;
+
+       switch (WLAN_FC_GET_TYPE(fc)) {
+       case IEEE80211_FTYPE_DATA:
+               if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
+                       hdrlen = 30; /* Addr4 */
+               break;
+       case IEEE80211_FTYPE_CTL:
+               switch (WLAN_FC_GET_STYPE(fc)) {
+               case IEEE80211_STYPE_CTS:
+               case IEEE80211_STYPE_ACK:
+                       hdrlen = 10;
+                       break;
+               default:
+                       hdrlen = 16;
+                       break;
+               }
+               break;
+       }
+
+       return hdrlen;
+}
+
+
+struct net_device_stats *hostap_get_stats(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       iface = netdev_priv(dev);
+       return &iface->stats;
+}
+
+
+static int prism2_close(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name);
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (dev == local->ddev) {
+               prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING);
+       }
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (!local->hostapd && dev == local->dev &&
+           (!local->func->card_present || local->func->card_present(local)) &&
+           local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER)
+               hostap_deauth_all_stas(dev, local->ap, 1);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       if (local->func->dev_close && local->func->dev_close(local))
+               return 0;
+
+       if (dev == local->dev) {
+               local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL);
+       }
+
+       if (netif_running(dev)) {
+               netif_stop_queue(dev);
+               netif_device_detach(dev);
+       }
+
+       flush_scheduled_work();
+
+       module_put(local->hw_module);
+
+       local->num_dev_open--;
+
+       if (dev != local->dev && local->dev->flags & IFF_UP &&
+           local->master_dev_auto_open && local->num_dev_open == 1) {
+               /* Close master radio interface automatically if it was also
+                * opened automatically and we are now closing the last
+                * remaining non-master device. */
+               dev_close(local->dev);
+       }
+
+       return 0;
+}
+
+
+static int prism2_open(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name);
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->no_pri) {
+               printk(KERN_DEBUG "%s: could not set interface UP - no PRI "
+                      "f/w\n", dev->name);
+               return 1;
+       }
+
+       if ((local->func->card_present && !local->func->card_present(local)) ||
+           local->hw_downloading)
+               return -ENODEV;
+
+       if (local->func->dev_open && local->func->dev_open(local))
+               return 1;
+
+       if (!try_module_get(local->hw_module))
+               return -ENODEV;
+       local->num_dev_open++;
+
+       if (!local->dev_enabled && local->func->hw_enable(dev, 1)) {
+               printk(KERN_WARNING "%s: could not enable MAC port\n",
+                      dev->name);
+               prism2_close(dev);
+               return 1;
+       }
+       if (!local->dev_enabled)
+               prism2_callback(local, PRISM2_CALLBACK_ENABLE);
+       local->dev_enabled = 1;
+
+       if (dev != local->dev && !(local->dev->flags & IFF_UP)) {
+               /* Master radio interface is needed for all operation, so open
+                * it automatically when any virtual net_device is opened. */
+               local->master_dev_auto_open = 1;
+               dev_open(local->dev);
+       }
+
+       netif_device_attach(dev);
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+
+static int prism2_set_mac_address(struct net_device *dev, void *p)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct list_head *ptr;
+       struct sockaddr *addr = p;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data,
+                                ETH_ALEN) < 0 || local->func->reset_port(dev))
+               return -EINVAL;
+
+       read_lock_bh(&local->iface_lock);
+       list_for_each(ptr, &local->hostap_interfaces) {
+               iface = list_entry(ptr, struct hostap_interface, list);
+               memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN);
+       }
+       memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN);
+       read_unlock_bh(&local->iface_lock);
+
+       return 0;
+}
+
+
+/* TODO: to be further implemented as soon as Prism2 fully supports
+ *       GroupAddresses and correct documentation is available */
+void hostap_set_multicast_list_queue(void *data)
+{
+       struct net_device *dev = (struct net_device *) data;
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE,
+                           local->is_promisc)) {
+               printk(KERN_INFO "%s: %sabling promiscuous mode failed\n",
+                      dev->name, local->is_promisc ? "en" : "dis");
+       }
+}
+
+
+static void hostap_set_multicast_list(struct net_device *dev)
+{
+#if 0
+       /* FIX: promiscuous mode seems to be causing a lot of problems with
+        * some station firmware versions (FCSErr frames, invalid MACPort, etc.
+        * corrupted incoming frames). This code is now commented out while the
+        * problems are investigated. */
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) {
+               local->is_promisc = 1;
+       } else {
+               local->is_promisc = 0;
+       }
+
+       schedule_work(&local->set_multicast_list_queue);
+#endif
+}
+
+
+static int prism2_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU)
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+
+static void prism2_tx_timeout(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hfa384x_regs regs;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name);
+       netif_stop_queue(local->dev);
+
+       local->func->read_regs(dev, &regs);
+       printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x "
+              "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n",
+              dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1,
+              regs.swsupport0);
+
+       local->func->schedule_reset(local);
+}
+
+
+void hostap_setup_dev(struct net_device *dev, local_info_t *local,
+                     int main_dev)
+{
+       struct hostap_interface *iface;
+
+       iface = netdev_priv(dev);
+       ether_setup(dev);
+
+       /* kernel callbacks */
+       dev->get_stats = hostap_get_stats;
+       if (iface) {
+               /* Currently, we point to the proper spy_data only on
+                * the main_dev. This could be fixed. Jean II */
+               iface->wireless_data.spy_data = &iface->spy_data;
+               dev->wireless_data = &iface->wireless_data;
+       }
+       dev->wireless_handlers =
+               (struct iw_handler_def *) &hostap_iw_handler_def;
+       dev->do_ioctl = hostap_ioctl;
+       dev->open = prism2_open;
+       dev->stop = prism2_close;
+       dev->hard_start_xmit = hostap_data_start_xmit;
+       dev->set_mac_address = prism2_set_mac_address;
+       dev->set_multicast_list = hostap_set_multicast_list;
+       dev->change_mtu = prism2_change_mtu;
+       dev->tx_timeout = prism2_tx_timeout;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       dev->mtu = local->mtu;
+       if (!main_dev) {
+               /* use main radio device queue */
+               dev->tx_queue_len = 0;
+       }
+
+       SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops);
+
+       netif_stop_queue(dev);
+}
+
+
+static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       if (local->apdev)
+               return -EEXIST;
+
+       printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name);
+
+       local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP,
+                                           rtnl_locked, local->ddev->name,
+                                           "ap");
+       if (local->apdev == NULL)
+               return -ENOMEM;
+
+       local->apdev->hard_start_xmit = hostap_mgmt_start_xmit;
+       local->apdev->type = ARPHRD_IEEE80211;
+       local->apdev->hard_header_parse = hostap_80211_header_parse;
+
+       return 0;
+}
+
+
+static int hostap_disable_hostapd(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name);
+
+       hostap_remove_interface(local->apdev, rtnl_locked, 1);
+       local->apdev = NULL;
+
+       return 0;
+}
+
+
+static int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       if (local->stadev)
+               return -EEXIST;
+
+       printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name);
+
+       local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA,
+                                            rtnl_locked, local->ddev->name,
+                                            "sta");
+       if (local->stadev == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+
+static int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name);
+
+       hostap_remove_interface(local->stadev, rtnl_locked, 1);
+       local->stadev = NULL;
+
+       return 0;
+}
+
+
+int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked)
+{
+       int ret;
+
+       if (val < 0 || val > 1)
+               return -EINVAL;
+
+       if (local->hostapd == val)
+               return 0;
+
+       if (val) {
+               ret = hostap_enable_hostapd(local, rtnl_locked);
+               if (ret == 0)
+                       local->hostapd = 1;
+       } else {
+               local->hostapd = 0;
+               ret = hostap_disable_hostapd(local, rtnl_locked);
+               if (ret != 0)
+                       local->hostapd = 1;
+       }
+
+       return ret;
+}
+
+
+int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked)
+{
+       int ret;
+
+       if (val < 0 || val > 1)
+               return -EINVAL;
+
+       if (local->hostapd_sta == val)
+               return 0;
+
+       if (val) {
+               ret = hostap_enable_hostapd_sta(local, rtnl_locked);
+               if (ret == 0)
+                       local->hostapd_sta = 1;
+       } else {
+               local->hostapd_sta = 0;
+               ret = hostap_disable_hostapd_sta(local, rtnl_locked);
+               if (ret != 0)
+                       local->hostapd_sta = 1;
+       }
+
+
+       return ret;
+}
+
+
+int prism2_update_comms_qual(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 0;
+       struct hfa384x_comms_quality sq;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       if (!local->sta_fw_ver)
+               ret = -1;
+       else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) {
+               if (local->func->get_rid(local->dev,
+                                        HFA384X_RID_DBMCOMMSQUALITY,
+                                        &sq, sizeof(sq), 1) >= 0) {
+                       local->comms_qual = (s16) le16_to_cpu(sq.comm_qual);
+                       local->avg_signal = (s16) le16_to_cpu(sq.signal_level);
+                       local->avg_noise = (s16) le16_to_cpu(sq.noise_level);
+                       local->last_comms_qual_update = jiffies;
+               } else
+                       ret = -1;
+       } else {
+               if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY,
+                                        &sq, sizeof(sq), 1) >= 0) {
+                       local->comms_qual = le16_to_cpu(sq.comm_qual);
+                       local->avg_signal = HFA384X_LEVEL_TO_dBm(
+                               le16_to_cpu(sq.signal_level));
+                       local->avg_noise = HFA384X_LEVEL_TO_dBm(
+                               le16_to_cpu(sq.noise_level));
+                       local->last_comms_qual_update = jiffies;
+               } else
+                       ret = -1;
+       }
+
+       return ret;
+}
+
+
+int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype,
+                        u8 *body, size_t bodylen)
+{
+       struct sk_buff *skb;
+       struct hostap_ieee80211_mgmt *mgmt;
+       struct hostap_skb_tx_data *meta;
+       struct net_device *dev = local->dev;
+
+       skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       mgmt = (struct hostap_ieee80211_mgmt *)
+               skb_put(skb, IEEE80211_MGMT_HDR_LEN);
+       memset(mgmt, 0, IEEE80211_MGMT_HDR_LEN);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+       memcpy(mgmt->da, dst, ETH_ALEN);
+       memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->bssid, dst, ETH_ALEN);
+       if (body)
+               memcpy(skb_put(skb, bodylen), body, bodylen);
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       memset(meta, 0, sizeof(*meta));
+       meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+       meta->iface = netdev_priv(dev);
+
+       skb->dev = dev;
+       skb->mac.raw = skb->nh.raw = skb->data;
+       dev_queue_xmit(skb);
+
+       return 0;
+}
+
+
+int prism2_sta_deauth(local_info_t *local, u16 reason)
+{
+       union iwreq_data wrqu;
+       int ret;
+
+       if (local->iw_mode != IW_MODE_INFRA ||
+           memcmp(local->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0 ||
+           memcmp(local->bssid, "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0)
+               return 0;
+
+       reason = cpu_to_le16(reason);
+       ret = prism2_sta_send_mgmt(local, local->bssid, IEEE80211_STYPE_DEAUTH,
+                                  (u8 *) &reason, 2);
+       memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+       wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
+       return ret;
+}
+
+
+struct proc_dir_entry *hostap_proc;
+
+static int __init hostap_init(void)
+{
+       if (proc_net != NULL) {
+               hostap_proc = proc_mkdir("hostap", proc_net);
+               if (!hostap_proc)
+                       printk(KERN_WARNING "Failed to mkdir "
+                              "/proc/net/hostap\n");
+       } else
+               hostap_proc = NULL;
+
+       return 0;
+}
+
+
+static void __exit hostap_exit(void)
+{
+       if (hostap_proc != NULL) {
+               hostap_proc = NULL;
+               remove_proc_entry("hostap", proc_net);
+       }
+}
+
+
+EXPORT_SYMBOL(hostap_set_word);
+EXPORT_SYMBOL(hostap_set_string);
+EXPORT_SYMBOL(hostap_get_porttype);
+EXPORT_SYMBOL(hostap_set_encryption);
+EXPORT_SYMBOL(hostap_set_antsel);
+EXPORT_SYMBOL(hostap_set_roaming);
+EXPORT_SYMBOL(hostap_set_auth_algs);
+EXPORT_SYMBOL(hostap_dump_rx_header);
+EXPORT_SYMBOL(hostap_dump_tx_header);
+EXPORT_SYMBOL(hostap_80211_header_parse);
+EXPORT_SYMBOL(hostap_80211_prism_header_parse);
+EXPORT_SYMBOL(hostap_80211_get_hdrlen);
+EXPORT_SYMBOL(hostap_get_stats);
+EXPORT_SYMBOL(hostap_setup_dev);
+EXPORT_SYMBOL(hostap_proc);
+EXPORT_SYMBOL(hostap_set_multicast_list_queue);
+EXPORT_SYMBOL(hostap_set_hostapd);
+EXPORT_SYMBOL(hostap_set_hostapd_sta);
+EXPORT_SYMBOL(hostap_add_interface);
+EXPORT_SYMBOL(hostap_remove_interface);
+EXPORT_SYMBOL(prism2_update_comms_qual);
+
+module_init(hostap_init);
+module_exit(hostap_exit);
diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h
new file mode 100644 (file)
index 0000000..5fac89b
--- /dev/null
@@ -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 */
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h
new file mode 100644 (file)
index 0000000..bf506f5
--- /dev/null
@@ -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 */
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
new file mode 100644 (file)
index 0000000..b050124
--- /dev/null
@@ -0,0 +1,1091 @@
+#include <linux/etherdevice.h>
+
+#include "hostap_80211.h"
+#include "hostap.h"
+
+void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d "
+              "jiffies=%ld\n",
+              name, rx_stats->signal, rx_stats->noise, rx_stats->rate,
+              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");
+}
+
+
+/* Send RX frame to netif with 802.11 (and possible prism) header.
+ * Called from hardware or software IRQ context. */
+int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
+                   struct hostap_80211_rx_status *rx_stats, int type)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int hdrlen, phdrlen, head_need, tail_need;
+       u16 fc;
+       int prism_header, ret;
+       struct ieee80211_hdr *hdr;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       dev->last_rx = jiffies;
+
+       if (dev->type == ARPHRD_IEEE80211_PRISM) {
+               if (local->monitor_type == PRISM2_MONITOR_PRISM) {
+                       prism_header = 1;
+                       phdrlen = sizeof(struct linux_wlan_ng_prism_hdr);
+               } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */
+                       prism_header = 2;
+                       phdrlen = sizeof(struct linux_wlan_ng_cap_hdr);
+               }
+       } else {
+               prism_header = 0;
+               phdrlen = 0;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) {
+               printk(KERN_DEBUG "%s: dropped management frame with header "
+                      "version %d\n", dev->name, fc & IEEE80211_FCTL_VERS);
+               dev_kfree_skb_any(skb);
+               return 0;
+       }
+
+       hdrlen = hostap_80211_get_hdrlen(fc);
+
+       /* check if there is enough room for extra data; if not, expand skb
+        * buffer to be large enough for the changes */
+       head_need = phdrlen;
+       tail_need = 0;
+#ifdef PRISM2_ADD_BOGUS_CRC
+       tail_need += 4;
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+       head_need -= skb_headroom(skb);
+       tail_need -= skb_tailroom(skb);
+
+       if (head_need > 0 || tail_need > 0) {
+               if (pskb_expand_head(skb, head_need > 0 ? head_need : 0,
+                                    tail_need > 0 ? tail_need : 0,
+                                    GFP_ATOMIC)) {
+                       printk(KERN_DEBUG "%s: prism2_rx_80211 failed to "
+                              "reallocate skb buffer\n", dev->name);
+                       dev_kfree_skb_any(skb);
+                       return 0;
+               }
+       }
+
+       /* We now have an skb with enough head and tail room, so just insert
+        * the extra data */
+
+#ifdef PRISM2_ADD_BOGUS_CRC
+       memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+       if (prism_header == 1) {
+               struct linux_wlan_ng_prism_hdr *hdr;
+               hdr = (struct linux_wlan_ng_prism_hdr *)
+                       skb_push(skb, phdrlen);
+               memset(hdr, 0, phdrlen);
+               hdr->msgcode = LWNG_CAP_DID_BASE;
+               hdr->msglen = sizeof(*hdr);
+               memcpy(hdr->devname, dev->name, sizeof(hdr->devname));
+#define LWNG_SETVAL(f,i,s,l,d) \
+hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \
+hdr->f.status = s; hdr->f.len = l; hdr->f.data = d
+               LWNG_SETVAL(hosttime, 1, 0, 4, jiffies);
+               LWNG_SETVAL(mactime, 2, 0, 4, rx_stats->mac_time);
+               LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0);
+               LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0);
+               LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0);
+               LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal);
+               LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise);
+               LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5);
+               LWNG_SETVAL(istx, 9, 0, 4, 0);
+               LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen);
+#undef LWNG_SETVAL
+       } else if (prism_header == 2) {
+               struct linux_wlan_ng_cap_hdr *hdr;
+               hdr = (struct linux_wlan_ng_cap_hdr *)
+                       skb_push(skb, phdrlen);
+               memset(hdr, 0, phdrlen);
+               hdr->version    = htonl(LWNG_CAPHDR_VERSION);
+               hdr->length     = htonl(phdrlen);
+               hdr->mactime    = __cpu_to_be64(rx_stats->mac_time);
+               hdr->hosttime   = __cpu_to_be64(jiffies);
+               hdr->phytype    = htonl(4); /* dss_dot11_b */
+               hdr->channel    = htonl(local->channel);
+               hdr->datarate   = htonl(rx_stats->rate);
+               hdr->antenna    = htonl(0); /* unknown */
+               hdr->priority   = htonl(0); /* unknown */
+               hdr->ssi_type   = htonl(3); /* raw */
+               hdr->ssi_signal = htonl(rx_stats->signal);
+               hdr->ssi_noise  = htonl(rx_stats->noise);
+               hdr->preamble   = htonl(0); /* unknown */
+               hdr->encoding   = htonl(1); /* cck */
+       }
+
+       ret = skb->len - phdrlen;
+       skb->dev = dev;
+       skb->mac.raw = skb->data;
+       skb_pull(skb, hdrlen);
+       if (prism_header)
+               skb_pull(skb, phdrlen);
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = __constant_htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void monitor_rx(struct net_device *dev, struct sk_buff *skb,
+                      struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device_stats *stats;
+       int len;
+
+       len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR);
+       stats = hostap_get_stats(dev);
+       stats->rx_packets++;
+       stats->rx_bytes += len;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct prism2_frag_entry *
+prism2_frag_cache_find(local_info_t *local, unsigned int seq,
+                      unsigned int frag, u8 *src, u8 *dst)
+{
+       struct prism2_frag_entry *entry;
+       int i;
+
+       for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) {
+               entry = &local->frag_cache[i];
+               if (entry->skb != NULL &&
+                   time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+                       printk(KERN_DEBUG "%s: expiring fragment cache entry "
+                              "seq=%u last_frag=%u\n",
+                              local->dev->name, entry->seq, entry->last_frag);
+                       dev_kfree_skb(entry->skb);
+                       entry->skb = NULL;
+               }
+
+               if (entry->skb != NULL && entry->seq == seq &&
+                   (entry->last_frag + 1 == frag || frag == -1) &&
+                   memcmp(entry->src_addr, src, ETH_ALEN) == 0 &&
+                   memcmp(entry->dst_addr, dst, ETH_ALEN) == 0)
+                       return entry;
+       }
+
+       return NULL;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *
+prism2_frag_cache_get(local_info_t *local, struct ieee80211_hdr *hdr)
+{
+       struct sk_buff *skb = NULL;
+       u16 sc;
+       unsigned int frag, seq;
+       struct prism2_frag_entry *entry;
+
+       sc = le16_to_cpu(hdr->seq_ctl);
+       frag = WLAN_GET_SEQ_FRAG(sc);
+       seq = WLAN_GET_SEQ_SEQ(sc) >> 4;
+
+       if (frag == 0) {
+               /* Reserve enough space to fit maximum frame length */
+               skb = dev_alloc_skb(local->dev->mtu +
+                                   sizeof(struct ieee80211_hdr) +
+                                   8 /* LLC */ +
+                                   2 /* alignment */ +
+                                   8 /* WEP */ + ETH_ALEN /* WDS */);
+               if (skb == NULL)
+                       return NULL;
+
+               entry = &local->frag_cache[local->frag_next_idx];
+               local->frag_next_idx++;
+               if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN)
+                       local->frag_next_idx = 0;
+
+               if (entry->skb != NULL)
+                       dev_kfree_skb(entry->skb);
+
+               entry->first_frag_time = jiffies;
+               entry->seq = seq;
+               entry->last_frag = frag;
+               entry->skb = skb;
+               memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+               memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+       } else {
+               /* received a fragment of a frame for which the head fragment
+                * should have already been received */
+               entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2,
+                                              hdr->addr1);
+               if (entry != NULL) {
+                       entry->last_frag = frag;
+                       skb = entry->skb;
+               }
+       }
+
+       return skb;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int prism2_frag_cache_invalidate(local_info_t *local,
+                                       struct ieee80211_hdr *hdr)
+{
+       u16 sc;
+       unsigned int seq;
+       struct prism2_frag_entry *entry;
+
+       sc = le16_to_cpu(hdr->seq_ctl);
+       seq = WLAN_GET_SEQ_SEQ(sc) >> 4;
+
+       entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1);
+
+       if (entry == NULL) {
+               printk(KERN_DEBUG "%s: could not invalidate fragment cache "
+                      "entry (seq=%u)\n",
+                      local->dev->name, seq);
+               return -1;
+       }
+
+       entry->skb = NULL;
+       return 0;
+}
+
+
+static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid,
+                                               u8 *ssid, size_t ssid_len)
+{
+       struct list_head *ptr;
+       struct hostap_bss_info *bss;
+
+       list_for_each(ptr, &local->bss_list) {
+               bss = list_entry(ptr, struct hostap_bss_info, list);
+               if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+                   (ssid == NULL ||
+                    (ssid_len == bss->ssid_len &&
+                     memcmp(ssid, bss->ssid, ssid_len) == 0))) {
+                       list_move(&bss->list, &local->bss_list);
+                       return bss;
+               }
+       }
+
+       return NULL;
+}
+
+
+static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid,
+                                               u8 *ssid, size_t ssid_len)
+{
+       struct hostap_bss_info *bss;
+
+       if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) {
+               bss = list_entry(local->bss_list.prev,
+                                struct hostap_bss_info, list);
+               list_del(&bss->list);
+               local->num_bss_info--;
+       } else {
+               bss = (struct hostap_bss_info *)
+                       kmalloc(sizeof(*bss), GFP_ATOMIC);
+               if (bss == NULL)
+                       return NULL;
+       }
+
+       memset(bss, 0, sizeof(*bss));
+       memcpy(bss->bssid, bssid, ETH_ALEN);
+       memcpy(bss->ssid, ssid, ssid_len);
+       bss->ssid_len = ssid_len;
+       local->num_bss_info++;
+       list_add(&bss->list, &local->bss_list);
+       return bss;
+}
+
+
+static void __hostap_expire_bss(local_info_t *local)
+{
+       struct hostap_bss_info *bss;
+
+       while (local->num_bss_info > 0) {
+               bss = list_entry(local->bss_list.prev,
+                                struct hostap_bss_info, list);
+               if (!time_after(jiffies, bss->last_update + 60 * HZ))
+                       break;
+
+               list_del(&bss->list);
+               local->num_bss_info--;
+               kfree(bss);
+       }
+}
+
+
+/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so
+ * the same routine can be used to parse both of them. */
+static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb,
+                                int stype)
+{
+       struct hostap_ieee80211_mgmt *mgmt;
+       int left, chan = 0;
+       u8 *pos;
+       u8 *ssid = NULL, *wpa = NULL, *rsn = NULL;
+       size_t ssid_len = 0, wpa_len = 0, rsn_len = 0;
+       struct hostap_bss_info *bss;
+
+       if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon))
+               return;
+
+       mgmt = (struct hostap_ieee80211_mgmt *) skb->data;
+       pos = mgmt->u.beacon.variable;
+       left = skb->len - (pos - skb->data);
+
+       while (left >= 2) {
+               if (2 + pos[1] > left)
+                       return; /* parse failed */
+               switch (*pos) {
+               case WLAN_EID_SSID:
+                       ssid = pos + 2;
+                       ssid_len = pos[1];
+                       break;
+               case WLAN_EID_GENERIC:
+                       if (pos[1] >= 4 &&
+                           pos[2] == 0x00 && pos[3] == 0x50 &&
+                           pos[4] == 0xf2 && pos[5] == 1) {
+                               wpa = pos;
+                               wpa_len = pos[1] + 2;
+                       }
+                       break;
+               case WLAN_EID_RSN:
+                       rsn = pos;
+                       rsn_len = pos[1] + 2;
+                       break;
+               case WLAN_EID_DS_PARAMS:
+                       if (pos[1] >= 1)
+                               chan = pos[2];
+                       break;
+               }
+               left -= 2 + pos[1];
+               pos += 2 + pos[1];
+       }
+
+       if (wpa_len > MAX_WPA_IE_LEN)
+               wpa_len = MAX_WPA_IE_LEN;
+       if (rsn_len > MAX_WPA_IE_LEN)
+               rsn_len = MAX_WPA_IE_LEN;
+       if (ssid_len > sizeof(bss->ssid))
+               ssid_len = sizeof(bss->ssid);
+
+       spin_lock(&local->lock);
+       bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len);
+       if (bss == NULL)
+               bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len);
+       if (bss) {
+               bss->last_update = jiffies;
+               bss->count++;
+               bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info);
+               if (wpa) {
+                       memcpy(bss->wpa_ie, wpa, wpa_len);
+                       bss->wpa_ie_len = wpa_len;
+               } else
+                       bss->wpa_ie_len = 0;
+               if (rsn) {
+                       memcpy(bss->rsn_ie, rsn, rsn_len);
+                       bss->rsn_ie_len = rsn_len;
+               } else
+                       bss->rsn_ie_len = 0;
+               bss->chan = chan;
+       }
+       __hostap_expire_bss(local);
+       spin_unlock(&local->lock);
+}
+
+
+static inline int
+hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb,
+                    struct hostap_80211_rx_status *rx_stats, u16 type,
+                    u16 stype)
+{
+       if (local->iw_mode == IW_MODE_MASTER) {
+               hostap_update_sta_ps(local, (struct ieee80211_hdr *)
+                                    skb->data);
+       }
+
+       if (local->hostapd && type == IEEE80211_FTYPE_MGMT) {
+               if (stype == IEEE80211_STYPE_BEACON &&
+                   local->iw_mode == IW_MODE_MASTER) {
+                       struct sk_buff *skb2;
+                       /* Process beacon frames also in kernel driver to
+                        * update STA(AP) table statistics */
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb2)
+                               hostap_rx(skb2->dev, skb2, rx_stats);
+               }
+
+               /* send management frames to the user space daemon for
+                * processing */
+               local->apdevstats.rx_packets++;
+               local->apdevstats.rx_bytes += skb->len;
+               if (local->apdev == NULL)
+                       return -1;
+               prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT);
+               return 0;
+       }
+
+       if (local->iw_mode == IW_MODE_MASTER) {
+               if (type != IEEE80211_FTYPE_MGMT &&
+                   type != IEEE80211_FTYPE_CTL) {
+                       printk(KERN_DEBUG "%s: unknown management frame "
+                              "(type=0x%02x, stype=0x%02x) dropped\n",
+                              skb->dev->name, type >> 2, stype >> 4);
+                       return -1;
+               }
+
+               hostap_rx(skb->dev, skb, rx_stats);
+               return 0;
+       } else if (type == IEEE80211_FTYPE_MGMT &&
+                  (stype == IEEE80211_STYPE_BEACON ||
+                   stype == IEEE80211_STYPE_PROBE_RESP)) {
+               hostap_rx_sta_beacon(local, skb, stype);
+               return -1;
+       } else if (type == IEEE80211_FTYPE_MGMT &&
+                  (stype == IEEE80211_STYPE_ASSOC_RESP ||
+                   stype == IEEE80211_STYPE_REASSOC_RESP)) {
+               /* Ignore (Re)AssocResp silently since these are not currently
+                * needed but are still received when WPA/RSN mode is enabled.
+                */
+               return -1;
+       } else {
+               printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled"
+                      " management frame in non-Host AP mode (type=%d:%d)\n",
+                      skb->dev->name, type >> 2, stype >> 4);
+               return -1;
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline struct net_device *prism2_rx_get_wds(local_info_t *local,
+                                                  u8 *addr)
+{
+       struct hostap_interface *iface = NULL;
+       struct list_head *ptr;
+
+       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 &&
+                   memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0)
+                       break;
+               iface = NULL;
+       }
+       read_unlock_bh(&local->iface_lock);
+
+       return iface ? iface->dev : NULL;
+}
+
+
+static inline int
+hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr *hdr,
+                   u16 fc, struct net_device **wds)
+{
+       /* FIX: is this really supposed to accept WDS frames only in Master
+        * mode? What about Repeater or Managed with WDS frames? */
+       if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) !=
+           (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS) &&
+           (local->iw_mode != IW_MODE_MASTER || !(fc & IEEE80211_FCTL_TODS)))
+               return 0; /* not a WDS frame */
+
+       /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS)
+        * or own non-standard frame with 4th address after payload */
+       if (memcmp(hdr->addr1, local->dev->dev_addr, ETH_ALEN) != 0 &&
+           (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff ||
+            hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff ||
+            hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) {
+               /* RA (or BSSID) is not ours - drop */
+               PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with "
+                      "not own or broadcast %s=" MACSTR "\n",
+                      local->dev->name,
+                      fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID",
+                      MAC2STR(hdr->addr1));
+               return -1;
+       }
+
+       /* check if the frame came from a registered WDS connection */
+       *wds = prism2_rx_get_wds(local, hdr->addr2);
+       if (*wds == NULL && fc & IEEE80211_FCTL_FROMDS &&
+           (local->iw_mode != IW_MODE_INFRA ||
+            !(local->wds_type & HOSTAP_WDS_AP_CLIENT) ||
+            memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) {
+               /* require that WDS link has been registered with TA or the
+                * frame is from current AP when using 'AP client mode' */
+               PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame "
+                      "from unknown TA=" MACSTR "\n",
+                      local->dev->name, MAC2STR(hdr->addr2));
+               if (local->ap && local->ap->autom_ap_wds)
+                       hostap_wds_link_oper(local, hdr->addr2, WDS_ADD);
+               return -1;
+       }
+
+       if (*wds && !(fc & IEEE80211_FCTL_FROMDS) && local->ap &&
+           hostap_is_sta_assoc(local->ap, hdr->addr2)) {
+               /* STA is actually associated with us even though it has a
+                * registered WDS link. Assume it is in 'AP client' mode.
+                * Since this is a 3-addr frame, assume it is not (bogus) WDS
+                * frame and process it like any normal ToDS frame from
+                * associated STA. */
+               *wds = NULL;
+       }
+
+       return 0;
+}
+
+
+static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb)
+{
+       struct net_device *dev = local->dev;
+       u16 fc, ethertype;
+       struct ieee80211_hdr *hdr;
+       u8 *pos;
+
+       if (skb->len < 24)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       /* check that the frame is unicast frame to us */
+       if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+           IEEE80211_FCTL_TODS &&
+           memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 &&
+           memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+               /* ToDS frame with own addr BSSID and DA */
+       } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+                  IEEE80211_FCTL_FROMDS &&
+                  memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+               /* FromDS frame with own addr as DA */
+       } else
+               return 0;
+
+       if (skb->len < 24 + 8)
+               return 0;
+
+       /* check for port access entity Ethernet type */
+       pos = skb->data + 24;
+       ethertype = (pos[6] << 8) | pos[7];
+       if (ethertype == ETH_P_PAE)
+               return 1;
+
+       return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline int
+hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb,
+                       struct ieee80211_crypt_data *crypt)
+{
+       struct ieee80211_hdr *hdr;
+       int res, hdrlen;
+
+       if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+       if (local->tkip_countermeasures &&
+           strcmp(crypt->ops->name, "TKIP") == 0) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+                              "received packet from " MACSTR "\n",
+                              local->dev->name, MAC2STR(hdr->addr2));
+               }
+               return -1;
+       }
+
+       atomic_inc(&crypt->refcnt);
+       res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
+       atomic_dec(&crypt->refcnt);
+       if (res < 0) {
+               printk(KERN_DEBUG "%s: decryption failed (SA=" MACSTR
+                      ") res=%d\n",
+                      local->dev->name, MAC2STR(hdr->addr2), res);
+               local->comm_tallies.rx_discards_wep_undecryptable++;
+               return -1;
+       }
+
+       return res;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline int
+hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb,
+                            int keyidx, struct ieee80211_crypt_data *crypt)
+{
+       struct ieee80211_hdr *hdr;
+       int res, hdrlen;
+
+       if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+       atomic_inc(&crypt->refcnt);
+       res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
+       atomic_dec(&crypt->refcnt);
+       if (res < 0) {
+               printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
+                      " (SA=" MACSTR " keyidx=%d)\n",
+                      local->dev->name, MAC2STR(hdr->addr2), keyidx);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/* All received frames are sent to this function. @skb contains the frame in
+ * IEEE 802.11 format, i.e., in the format it was sent over air.
+ * This function is called only as a tasklet (software IRQ). */
+void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
+                    struct hostap_80211_rx_status *rx_stats)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct ieee80211_hdr *hdr;
+       size_t hdrlen;
+       u16 fc, type, stype, sc;
+       struct net_device *wds = NULL;
+       struct net_device_stats *stats;
+       unsigned int frag;
+       u8 *payload;
+       struct sk_buff *skb2 = NULL;
+       u16 ethertype;
+       int frame_authorized = 0;
+       int from_assoc_ap = 0;
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       struct ieee80211_crypt_data *crypt = NULL;
+       void *sta = NULL;
+       int keyidx = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       iface->stats.rx_packets++;
+       iface->stats.rx_bytes += skb->len;
+
+       /* dev is the master radio device; change this to be the default
+        * virtual interface (this may be changed to WDS device below) */
+       dev = local->ddev;
+       iface = netdev_priv(dev);
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       stats = hostap_get_stats(dev);
+
+       if (skb->len < 10)
+               goto rx_dropped;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+       sc = le16_to_cpu(hdr->seq_ctl);
+       frag = WLAN_GET_SEQ_FRAG(sc);
+       hdrlen = hostap_80211_get_hdrlen(fc);
+
+       /* Put this code here so that we avoid duplicating it in all
+        * Rx paths. - Jean II */
+#ifdef IW_WIRELESS_SPY         /* defined in iw_handler.h */
+       /* If spy monitoring on */
+       if (iface->spy_data.spy_number > 0) {
+               struct iw_quality wstats;
+               wstats.level = rx_stats->signal;
+               wstats.noise = rx_stats->noise;
+               wstats.updated = 6;     /* No qual value */
+               /* Update spy records */
+               wireless_spy_update(dev, hdr->addr2, &wstats);
+       }
+#endif /* IW_WIRELESS_SPY */
+       hostap_update_rx_stats(local->ap, hdr, rx_stats);
+
+       if (local->iw_mode == IW_MODE_MONITOR) {
+               monitor_rx(dev, skb, rx_stats);
+               return;
+       }
+
+       if (local->host_decrypt) {
+               int idx = 0;
+               if (skb->len >= hdrlen + 3)
+                       idx = skb->data[hdrlen + 3] >> 6;
+               crypt = local->crypt[idx];
+               sta = NULL;
+
+               /* Use station specific key to override default keys if the
+                * receiver address is a unicast address ("individual RA"). If
+                * bcrx_sta_key parameter is set, station specific key is used
+                * even with broad/multicast targets (this is against IEEE
+                * 802.11, but makes it easier to use different keys with
+                * stations that do not support WEP key mapping). */
+
+               if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
+                       (void) hostap_handle_sta_crypto(local, hdr, &crypt,
+                                                       &sta);
+
+               /* allow NULL decrypt to indicate an station specific override
+                * for default encryption */
+               if (crypt && (crypt->ops == NULL ||
+                             crypt->ops->decrypt_mpdu == NULL))
+                       crypt = NULL;
+
+               if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) {
+#if 0
+                       /* This seems to be triggered by some (multicast?)
+                        * frames from other than current BSS, so just drop the
+                        * frames silently instead of filling system log with
+                        * these reports. */
+                       printk(KERN_DEBUG "%s: WEP decryption failed (not set)"
+                              " (SA=" MACSTR ")\n",
+                              local->dev->name, MAC2STR(hdr->addr2));
+#endif
+                       local->comm_tallies.rx_discards_wep_undecryptable++;
+                       goto rx_dropped;
+               }
+       }
+
+       if (type != IEEE80211_FTYPE_DATA) {
+               if (type == IEEE80211_FTYPE_MGMT &&
+                   stype == IEEE80211_STYPE_AUTH &&
+                   fc & IEEE80211_FCTL_PROTECTED && local->host_decrypt &&
+                   (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0)
+               {
+                       printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
+                              "from " MACSTR "\n", dev->name,
+                              MAC2STR(hdr->addr2));
+                       /* TODO: could inform hostapd about this so that it
+                        * could send auth failure report */
+                       goto rx_dropped;
+               }
+
+               if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype))
+                       goto rx_dropped;
+               else
+                       goto rx_exit;
+       }
+
+       /* Data frame - extract src/dst addresses */
+       if (skb->len < IEEE80211_DATA_HDR3_LEN)
+               goto rx_dropped;
+
+       switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+       case IEEE80211_FCTL_FROMDS:
+               memcpy(dst, hdr->addr1, ETH_ALEN);
+               memcpy(src, hdr->addr3, ETH_ALEN);
+               break;
+       case IEEE80211_FCTL_TODS:
+               memcpy(dst, hdr->addr3, ETH_ALEN);
+               memcpy(src, hdr->addr2, ETH_ALEN);
+               break;
+       case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+               if (skb->len < IEEE80211_DATA_HDR4_LEN)
+                       goto rx_dropped;
+               memcpy(dst, hdr->addr3, ETH_ALEN);
+               memcpy(src, hdr->addr4, ETH_ALEN);
+               break;
+       case 0:
+               memcpy(dst, hdr->addr1, ETH_ALEN);
+               memcpy(src, hdr->addr2, ETH_ALEN);
+               break;
+       }
+
+       if (hostap_rx_frame_wds(local, hdr, fc, &wds))
+               goto rx_dropped;
+       if (wds) {
+               skb->dev = dev = wds;
+               stats = hostap_get_stats(dev);
+       }
+
+       if (local->iw_mode == IW_MODE_MASTER && !wds &&
+           (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+           IEEE80211_FCTL_FROMDS &&
+           local->stadev &&
+           memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) {
+               /* Frame from BSSID of the AP for which we are a client */
+               skb->dev = dev = local->stadev;
+               stats = hostap_get_stats(dev);
+               from_assoc_ap = 1;
+       }
+
+       dev->last_rx = jiffies;
+
+       if ((local->iw_mode == IW_MODE_MASTER ||
+            local->iw_mode == IW_MODE_REPEAT) &&
+           !from_assoc_ap) {
+               switch (hostap_handle_sta_rx(local, dev, skb, rx_stats,
+                                            wds != NULL)) {
+               case AP_RX_CONTINUE_NOT_AUTHORIZED:
+                       frame_authorized = 0;
+                       break;
+               case AP_RX_CONTINUE:
+                       frame_authorized = 1;
+                       break;
+               case AP_RX_DROP:
+                       goto rx_dropped;
+               case AP_RX_EXIT:
+                       goto rx_exit;
+               }
+       }
+
+       /* Nullfunc frames may have PS-bit set, so they must be passed to
+        * hostap_handle_sta_rx() before being dropped here. */
+       if (stype != IEEE80211_STYPE_DATA &&
+           stype != IEEE80211_STYPE_DATA_CFACK &&
+           stype != IEEE80211_STYPE_DATA_CFPOLL &&
+           stype != IEEE80211_STYPE_DATA_CFACKPOLL) {
+               if (stype != IEEE80211_STYPE_NULLFUNC)
+                       printk(KERN_DEBUG "%s: RX: dropped data frame "
+                              "with no data (type=0x%02x, subtype=0x%02x)\n",
+                              dev->name, type >> 2, stype >> 4);
+               goto rx_dropped;
+       }
+
+       /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
+
+       if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+           (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0)
+               goto rx_dropped;
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       /* skb: hdr + (possibly fragmented) plaintext payload */
+
+       if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+           (frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) {
+               int flen;
+               struct sk_buff *frag_skb =
+                       prism2_frag_cache_get(local, hdr);
+               if (!frag_skb) {
+                       printk(KERN_DEBUG "%s: Rx cannot get skb from "
+                              "fragment cache (morefrag=%d seq=%u frag=%u)\n",
+                              dev->name, (fc & IEEE80211_FCTL_MOREFRAGS) != 0,
+                              WLAN_GET_SEQ_SEQ(sc) >> 4, frag);
+                       goto rx_dropped;
+               }
+
+               flen = skb->len;
+               if (frag != 0)
+                       flen -= hdrlen;
+
+               if (frag_skb->tail + flen > frag_skb->end) {
+                       printk(KERN_WARNING "%s: host decrypted and "
+                              "reassembled frame did not fit skb\n",
+                              dev->name);
+                       prism2_frag_cache_invalidate(local, hdr);
+                       goto rx_dropped;
+               }
+
+               if (frag == 0) {
+                       /* copy first fragment (including full headers) into
+                        * beginning of the fragment cache skb */
+                       memcpy(skb_put(frag_skb, flen), skb->data, flen);
+               } else {
+                       /* append frame payload to the end of the fragment
+                        * cache skb */
+                       memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
+                              flen);
+               }
+               dev_kfree_skb(skb);
+               skb = NULL;
+
+               if (fc & IEEE80211_FCTL_MOREFRAGS) {
+                       /* more fragments expected - leave the skb in fragment
+                        * cache for now; it will be delivered to upper layers
+                        * after all fragments have been received */
+                       goto rx_exit;
+               }
+
+               /* this was the last fragment and the frame will be
+                * delivered, so remove skb from fragment cache */
+               skb = frag_skb;
+               hdr = (struct ieee80211_hdr *) skb->data;
+               prism2_frag_cache_invalidate(local, hdr);
+       }
+
+       /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
+        * encrypted/authenticated */
+
+       if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+           hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt))
+               goto rx_dropped;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !local->open_wep) {
+               if (local->ieee_802_1x &&
+                   hostap_is_eapol_frame(local, skb)) {
+                       /* pass unencrypted EAPOL frames even if encryption is
+                        * configured */
+                       PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing "
+                              "unencrypted EAPOL frame\n", local->dev->name);
+               } else {
+                       printk(KERN_DEBUG "%s: encryption configured, but RX "
+                              "frame not encrypted (SA=" MACSTR ")\n",
+                              local->dev->name, MAC2STR(hdr->addr2));
+                       goto rx_dropped;
+               }
+       }
+
+       if (local->drop_unencrypted && !(fc & IEEE80211_FCTL_PROTECTED) &&
+           !hostap_is_eapol_frame(local, skb)) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: dropped unencrypted RX data "
+                              "frame from " MACSTR " (drop_unencrypted=1)\n",
+                              dev->name, MAC2STR(hdr->addr2));
+               }
+               goto rx_dropped;
+       }
+
+       /* skb: hdr + (possible reassembled) full plaintext payload */
+
+       payload = skb->data + hdrlen;
+       ethertype = (payload[6] << 8) | payload[7];
+
+       /* If IEEE 802.1X is used, check whether the port is authorized to send
+        * the received frame. */
+       if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) {
+               if (ethertype == ETH_P_PAE) {
+                       PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n",
+                              dev->name);
+                       if (local->hostapd && local->apdev) {
+                               /* Send IEEE 802.1X frames to the user
+                                * space daemon for processing */
+                               prism2_rx_80211(local->apdev, skb, rx_stats,
+                                               PRISM2_RX_MGMT);
+                               local->apdevstats.rx_packets++;
+                               local->apdevstats.rx_bytes += skb->len;
+                               goto rx_exit;
+                       }
+               } else if (!frame_authorized) {
+                       printk(KERN_DEBUG "%s: dropped frame from "
+                              "unauthorized port (IEEE 802.1X): "
+                              "ethertype=0x%04x\n",
+                              dev->name, ethertype);
+                       goto rx_dropped;
+               }
+       }
+
+       /* convert hdr + possible LLC headers into Ethernet header */
+       if (skb->len - hdrlen >= 8 &&
+           ((memcmp(payload, rfc1042_header, 6) == 0 &&
+             ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+            memcmp(payload, bridge_tunnel_header, 6) == 0)) {
+               /* remove RFC1042 or Bridge-Tunnel encapsulation and
+                * replace EtherType */
+               skb_pull(skb, hdrlen + 6);
+               memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+               memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+       } else {
+               u16 len;
+               /* Leave Ethernet header part of hdr and full payload */
+               skb_pull(skb, hdrlen);
+               len = htons(skb->len);
+               memcpy(skb_push(skb, 2), &len, 2);
+               memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+               memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+       }
+
+       if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+                   IEEE80211_FCTL_TODS) &&
+           skb->len >= ETH_HLEN + ETH_ALEN) {
+               /* Non-standard frame: get addr4 from its bogus location after
+                * the payload */
+               memcpy(skb->data + ETH_ALEN,
+                      skb->data + skb->len - ETH_ALEN, ETH_ALEN);
+               skb_trim(skb, skb->len - ETH_ALEN);
+       }
+
+       stats->rx_packets++;
+       stats->rx_bytes += skb->len;
+
+       if (local->iw_mode == IW_MODE_MASTER && !wds &&
+           local->ap->bridge_packets) {
+               if (dst[0] & 0x01) {
+                       /* copy multicast frame both to the higher layers and
+                        * to the wireless media */
+                       local->ap->bridged_multicast++;
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb2 == NULL)
+                               printk(KERN_DEBUG "%s: skb_clone failed for "
+                                      "multicast frame\n", dev->name);
+               } else if (hostap_is_sta_authorized(local->ap, dst)) {
+                       /* send frame directly to the associated STA using
+                        * wireless media and not passing to higher layers */
+                       local->ap->bridged_unicast++;
+                       skb2 = skb;
+                       skb = NULL;
+               }
+       }
+
+       if (skb2 != NULL) {
+               /* send to wireless media */
+               skb2->protocol = __constant_htons(ETH_P_802_3);
+               skb2->mac.raw = skb2->nh.raw = skb2->data;
+               /* skb2->nh.raw = skb2->data + ETH_HLEN; */
+               skb2->dev = dev;
+               dev_queue_xmit(skb2);
+       }
+
+       if (skb) {
+               skb->protocol = eth_type_trans(skb, dev);
+               memset(skb->cb, 0, sizeof(skb->cb));
+               skb->dev = dev;
+               netif_rx(skb);
+       }
+
+ rx_exit:
+       if (sta)
+               hostap_handle_sta_release(sta);
+       return;
+
+ rx_dropped:
+       dev_kfree_skb(skb);
+
+       stats->rx_dropped++;
+       goto rx_exit;
+}
+
+
+EXPORT_SYMBOL(hostap_80211_rx);
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
new file mode 100644 (file)
index 0000000..6358015
--- /dev/null
@@ -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);
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
new file mode 100644 (file)
index 0000000..930cef8
--- /dev/null
@@ -0,0 +1,3288 @@
+/*
+ * Intersil Prism2 driver with Host AP (software access point) support
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This file is to be included into hostap.c when S/W AP functionality is
+ * compiled.
+ *
+ * AP:  FIX:
+ * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from
+ *   unauthenticated STA, send deauth. frame (8802.11: 5.5)
+ * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received
+ *   from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5)
+ * - if unicast Class 3 received from unauthenticated STA, send deauth. frame
+ *   (8802.11: 5.5)
+ */
+
+static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL,
+                                                DEF_INTS };
+module_param_array(other_ap_policy, int, NULL, 0444);
+MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");
+
+static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC,
+                                                  DEF_INTS };
+module_param_array(ap_max_inactivity, int, NULL, 0444);
+MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station "
+                "inactivity");
+
+static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };
+module_param_array(ap_bridge_packets, int, NULL, 0444);
+MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between "
+                "stations");
+
+static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };
+module_param_array(autom_ap_wds, int, NULL, 0444);
+MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs "
+                "automatically");
+
+
+static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);
+static void hostap_event_expired_sta(struct net_device *dev,
+                                    struct sta_info *sta);
+static void handle_add_proc_queue(void *data);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static void handle_wds_oper_queue(void *data);
+static void prism2_send_mgmt(struct net_device *dev,
+                            u16 type_subtype, char *body,
+                            int body_len, u8 *addr, u16 tx_cb_idx);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+static int ap_debug_proc_read(char *page, char **start, off_t off,
+                             int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ap_data *ap = (struct ap_data *) data;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       p += sprintf(p, "BridgedUnicastFrames=%u\n", ap->bridged_unicast);
+       p += sprintf(p, "BridgedMulticastFrames=%u\n", ap->bridged_multicast);
+       p += sprintf(p, "max_inactivity=%u\n", ap->max_inactivity / HZ);
+       p += sprintf(p, "bridge_packets=%u\n", ap->bridge_packets);
+       p += sprintf(p, "nullfunc_ack=%u\n", ap->nullfunc_ack);
+       p += sprintf(p, "autom_ap_wds=%u\n", ap->autom_ap_wds);
+       p += sprintf(p, "auth_algs=%u\n", ap->local->auth_algs);
+       p += sprintf(p, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc);
+
+       return (p - page);
+}
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+
+static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
+{
+       sta->hnext = ap->sta_hash[STA_HASH(sta->addr)];
+       ap->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
+{
+       struct sta_info *s;
+
+       s = ap->sta_hash[STA_HASH(sta->addr)];
+       if (s == NULL) return;
+       if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
+               ap->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+               return;
+       }
+
+       while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN)
+              != 0)
+               s = s->hnext;
+       if (s->hnext != NULL)
+               s->hnext = s->hnext->hnext;
+       else
+               printk("AP: could not remove STA " MACSTR " from hash table\n",
+                      MAC2STR(sta->addr));
+}
+
+static void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
+{
+       if (sta->ap && sta->local)
+               hostap_event_expired_sta(sta->local->dev, sta);
+
+       if (ap->proc != NULL) {
+               char name[20];
+               sprintf(name, MACSTR, MAC2STR(sta->addr));
+               remove_proc_entry(name, ap->proc);
+       }
+
+       if (sta->crypt) {
+               sta->crypt->ops->deinit(sta->crypt->priv);
+               kfree(sta->crypt);
+               sta->crypt = NULL;
+       }
+
+       skb_queue_purge(&sta->tx_buf);
+
+       ap->num_sta--;
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (sta->aid > 0)
+               ap->sta_aid[sta->aid - 1] = NULL;
+
+       if (!sta->ap && sta->u.sta.challenge)
+               kfree(sta->u.sta.challenge);
+       del_timer(&sta->timer);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       kfree(sta);
+}
+
+
+static void hostap_set_tim(local_info_t *local, int aid, int set)
+{
+       if (local->func->set_tim)
+               local->func->set_tim(local->dev, aid, set);
+}
+
+
+static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta)
+{
+       union iwreq_data wrqu;
+       memset(&wrqu, 0, sizeof(wrqu));
+       memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
+       wrqu.addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);
+}
+
+
+static void hostap_event_expired_sta(struct net_device *dev,
+                                    struct sta_info *sta)
+{
+       union iwreq_data wrqu;
+       memset(&wrqu, 0, sizeof(wrqu));
+       memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
+       wrqu.addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void ap_handle_timer(unsigned long data)
+{
+       struct sta_info *sta = (struct sta_info *) data;
+       local_info_t *local;
+       struct ap_data *ap;
+       unsigned long next_time = 0;
+       int was_assoc;
+
+       if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
+               PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
+               return;
+       }
+
+       local = sta->local;
+       ap = local->ap;
+       was_assoc = sta->flags & WLAN_STA_ASSOC;
+
+       if (atomic_read(&sta->users) != 0)
+               next_time = jiffies + HZ;
+       else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH))
+               next_time = jiffies + ap->max_inactivity;
+
+       if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) {
+               /* station activity detected; reset timeout state */
+               sta->timeout_next = STA_NULLFUNC;
+               next_time = sta->last_rx + ap->max_inactivity;
+       } else if (sta->timeout_next == STA_DISASSOC &&
+                  !(sta->flags & WLAN_STA_PENDING_POLL)) {
+               /* STA ACKed data nullfunc frame poll */
+               sta->timeout_next = STA_NULLFUNC;
+               next_time = jiffies + ap->max_inactivity;
+       }
+
+       if (next_time) {
+               sta->timer.expires = next_time;
+               add_timer(&sta->timer);
+               return;
+       }
+
+       if (sta->ap)
+               sta->timeout_next = STA_DEAUTH;
+
+       if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) {
+               spin_lock(&ap->sta_table_lock);
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+               spin_unlock(&ap->sta_table_lock);
+               sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+       } else if (sta->timeout_next == STA_DISASSOC)
+               sta->flags &= ~WLAN_STA_ASSOC;
+
+       if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+               hostap_event_expired_sta(local->dev, sta);
+
+       if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 &&
+           !skb_queue_empty(&sta->tx_buf)) {
+               hostap_set_tim(local, sta->aid, 0);
+               sta->flags &= ~WLAN_STA_TIM;
+       }
+
+       if (sta->ap) {
+               if (ap->autom_ap_wds) {
+                       PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
+                              "connection to AP " MACSTR "\n",
+                              local->dev->name, MAC2STR(sta->addr));
+                       hostap_wds_link_oper(local, sta->addr, WDS_DEL);
+               }
+       } else if (sta->timeout_next == STA_NULLFUNC) {
+               /* send data frame to poll STA and check whether this frame
+                * is ACKed */
+               /* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
+                * it is apparently not retried so TX Exc events are not
+                * received for it */
+               sta->flags |= WLAN_STA_PENDING_POLL;
+               prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_DATA, NULL, 0,
+                                sta->addr, ap->tx_callback_poll);
+       } else {
+               int deauth = sta->timeout_next == STA_DEAUTH;
+               u16 resp;
+               PDEBUG(DEBUG_AP, "%s: sending %s info to STA " MACSTR
+                      "(last=%lu, jiffies=%lu)\n",
+                      local->dev->name,
+                      deauth ? "deauthentication" : "disassociation",
+                      MAC2STR(sta->addr), sta->last_rx, jiffies);
+
+               resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
+                                  WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+               prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT |
+                                (deauth ? IEEE80211_STYPE_DEAUTH :
+                                 IEEE80211_STYPE_DISASSOC),
+                                (char *) &resp, 2, sta->addr, 0);
+       }
+
+       if (sta->timeout_next == STA_DEAUTH) {
+               if (sta->flags & WLAN_STA_PERM) {
+                       PDEBUG(DEBUG_AP, "%s: STA " MACSTR " would have been "
+                              "removed, but it has 'perm' flag\n",
+                              local->dev->name, MAC2STR(sta->addr));
+               } else
+                       ap_free_sta(ap, sta);
+               return;
+       }
+
+       if (sta->timeout_next == STA_NULLFUNC) {
+               sta->timeout_next = STA_DISASSOC;
+               sta->timer.expires = jiffies + AP_DISASSOC_DELAY;
+       } else {
+               sta->timeout_next = STA_DEAUTH;
+               sta->timer.expires = jiffies + AP_DEAUTH_DELAY;
+       }
+
+       add_timer(&sta->timer);
+}
+
+
+void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
+                           int resend)
+{
+       u8 addr[ETH_ALEN];
+       u16 resp;
+       int i;
+
+       PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name);
+       memset(addr, 0xff, ETH_ALEN);
+
+       resp = __constant_cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+       /* deauth message sent; try to resend it few times; the message is
+        * broadcast, so it may be delayed until next DTIM; there is not much
+        * else we can do at this point since the driver is going to be shut
+        * down */
+       for (i = 0; i < 5; i++) {
+               prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+                                IEEE80211_STYPE_DEAUTH,
+                                (char *) &resp, 2, addr, 0);
+
+               if (!resend || ap->num_sta <= 0)
+                       return;
+
+               mdelay(50);
+       }
+}
+
+
+static int ap_control_proc_read(char *page, char **start, off_t off,
+                               int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ap_data *ap = (struct ap_data *) data;
+       char *policy_txt;
+       struct list_head *ptr;
+       struct mac_entry *entry;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       switch (ap->mac_restrictions.policy) {
+       case MAC_POLICY_OPEN:
+               policy_txt = "open";
+               break;
+       case MAC_POLICY_ALLOW:
+               policy_txt = "allow";
+               break;
+       case MAC_POLICY_DENY:
+               policy_txt = "deny";
+               break;
+       default:
+               policy_txt = "unknown";
+               break;
+       };
+       p += sprintf(p, "MAC policy: %s\n", policy_txt);
+       p += sprintf(p, "MAC entries: %u\n", ap->mac_restrictions.entries);
+       p += sprintf(p, "MAC list:\n");
+       spin_lock_bh(&ap->mac_restrictions.lock);
+       for (ptr = ap->mac_restrictions.mac_list.next;
+            ptr != &ap->mac_restrictions.mac_list; ptr = ptr->next) {
+               if (p - page > PAGE_SIZE - 80) {
+                       p += sprintf(p, "All entries did not fit one page.\n");
+                       break;
+               }
+
+               entry = list_entry(ptr, struct mac_entry, list);
+               p += sprintf(p, MACSTR "\n", MAC2STR(entry->addr));
+       }
+       spin_unlock_bh(&ap->mac_restrictions.lock);
+
+       return (p - page);
+}
+
+
+static int ap_control_add_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac)
+{
+       struct mac_entry *entry;
+
+       entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL);
+       if (entry == NULL)
+               return -1;
+
+       memcpy(entry->addr, mac, ETH_ALEN);
+
+       spin_lock_bh(&mac_restrictions->lock);
+       list_add_tail(&entry->list, &mac_restrictions->mac_list);
+       mac_restrictions->entries++;
+       spin_unlock_bh(&mac_restrictions->lock);
+
+       return 0;
+}
+
+
+static int ap_control_del_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac)
+{
+       struct list_head *ptr;
+       struct mac_entry *entry;
+
+       spin_lock_bh(&mac_restrictions->lock);
+       for (ptr = mac_restrictions->mac_list.next;
+            ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
+               entry = list_entry(ptr, struct mac_entry, list);
+
+               if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
+                       list_del(ptr);
+                       kfree(entry);
+                       mac_restrictions->entries--;
+                       spin_unlock_bh(&mac_restrictions->lock);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&mac_restrictions->lock);
+       return -1;
+}
+
+
+static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions,
+                              u8 *mac)
+{
+       struct list_head *ptr;
+       struct mac_entry *entry;
+       int found = 0;
+
+       if (mac_restrictions->policy == MAC_POLICY_OPEN)
+               return 0;
+
+       spin_lock_bh(&mac_restrictions->lock);
+       for (ptr = mac_restrictions->mac_list.next;
+            ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
+               entry = list_entry(ptr, struct mac_entry, list);
+
+               if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+       spin_unlock_bh(&mac_restrictions->lock);
+
+       if (mac_restrictions->policy == MAC_POLICY_ALLOW)
+               return !found;
+       else
+               return found;
+}
+
+
+static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions)
+{
+       struct list_head *ptr, *n;
+       struct mac_entry *entry;
+
+       if (mac_restrictions->entries == 0)
+               return;
+
+       spin_lock_bh(&mac_restrictions->lock);
+       for (ptr = mac_restrictions->mac_list.next, n = ptr->next;
+            ptr != &mac_restrictions->mac_list;
+            ptr = n, n = ptr->next) {
+               entry = list_entry(ptr, struct mac_entry, list);
+               list_del(ptr);
+               kfree(entry);
+       }
+       mac_restrictions->entries = 0;
+       spin_unlock_bh(&mac_restrictions->lock);
+}
+
+
+static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev,
+                              u8 *mac)
+{
+       struct sta_info *sta;
+       u16 resp;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, mac);
+       if (sta) {
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -EINVAL;
+
+       resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH,
+                        (char *) &resp, 2, sta->addr, 0);
+
+       if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+               hostap_event_expired_sta(dev, sta);
+
+       ap_free_sta(ap, sta);
+
+       return 0;
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static void ap_control_kickall(struct ap_data *ap)
+{
+       struct list_head *ptr, *n;
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list;
+            ptr = n, n = ptr->next) {
+               sta = list_entry(ptr, struct sta_info, list);
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+                       hostap_event_expired_sta(sta->local->dev, sta);
+               ap_free_sta(ap, sta);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+#define PROC_LIMIT (PAGE_SIZE - 80)
+
+static int prism2_ap_proc_read(char *page, char **start, off_t off,
+                              int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ap_data *ap = (struct ap_data *) data;
+       struct list_head *ptr;
+       int i;
+
+       if (off > PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       p += sprintf(p, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n");
+       spin_lock_bh(&ap->sta_table_lock);
+       for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+
+               if (!sta->ap)
+                       continue;
+
+               p += sprintf(p, MACSTR " %d %d %d %d '", MAC2STR(sta->addr),
+                            sta->u.ap.channel, sta->last_rx_signal,
+                            sta->last_rx_silence, sta->last_rx_rate);
+               for (i = 0; i < sta->u.ap.ssid_len; i++)
+                       p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 &&
+                                         sta->u.ap.ssid[i] < 127) ?
+                                        "%c" : "<%02x>"),
+                                    sta->u.ap.ssid[i]);
+               p += sprintf(p, "'");
+               if (sta->capability & WLAN_CAPABILITY_ESS)
+                       p += sprintf(p, " [ESS]");
+               if (sta->capability & WLAN_CAPABILITY_IBSS)
+                       p += sprintf(p, " [IBSS]");
+               if (sta->capability & WLAN_CAPABILITY_PRIVACY)
+                       p += sprintf(p, " [WEP]");
+               p += sprintf(p, "\n");
+
+               if ((p - page) > PROC_LIMIT) {
+                       printk(KERN_DEBUG "hostap: ap proc did not fit\n");
+                       break;
+               }
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if ((p - page) <= off) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+
+       return (p - page - off);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver)
+{
+       if (!ap)
+               return;
+
+       if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) {
+               PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - "
+                      "firmware upgrade recommended\n");
+               ap->nullfunc_ack = 1;
+       } else
+               ap->nullfunc_ack = 0;
+
+       if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) {
+               printk(KERN_WARNING "%s: Warning: secondary station firmware "
+                      "version 1.4.2 does not seem to work in Host AP mode\n",
+                      ap->local->dev->name);
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       u16 fc;
+       struct ieee80211_hdr *hdr;
+
+       if (!ap->local->hostapd || !ap->local->apdev) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       /* Pass the TX callback frame to the hostapd; use 802.11 header version
+        * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */
+
+       fc &= ~IEEE80211_FCTL_VERS;
+       fc |= ok ? BIT(1) : BIT(0);
+       hdr->frame_ctl = cpu_to_le16(fc);
+
+       skb->dev = ap->local->apdev;
+       skb_pull(skb, hostap_80211_get_hdrlen(fc));
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = __constant_htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       struct net_device *dev = ap->local->dev;
+       struct ieee80211_hdr *hdr;
+       u16 fc, *pos, auth_alg, auth_transaction, status;
+       struct sta_info *sta = NULL;
+       char *txt = NULL;
+
+       if (ap->local->hostapd) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_MGMT ||
+           WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_AUTH ||
+           skb->len < IEEE80211_MGMT_HDR_LEN + 6) {
+               printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid "
+                      "frame\n", dev->name);
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       auth_alg = le16_to_cpu(*pos++);
+       auth_transaction = le16_to_cpu(*pos++);
+       status = le16_to_cpu(*pos++);
+
+       if (!ok) {
+               txt = "frame was not ACKed";
+               goto done;
+       }
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, hdr->addr1);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&ap->sta_table_lock);
+
+       if (!sta) {
+               txt = "STA not found";
+               goto done;
+       }
+
+       if (status == WLAN_STATUS_SUCCESS &&
+           ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+            (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+               txt = "STA authenticated";
+               sta->flags |= WLAN_STA_AUTH;
+               sta->last_auth = jiffies;
+       } else if (status != WLAN_STATUS_SUCCESS)
+               txt = "authentication failed";
+
+ done:
+       if (sta)
+               atomic_dec(&sta->users);
+       if (txt) {
+               PDEBUG(DEBUG_AP, "%s: " MACSTR " auth_cb - alg=%d trans#=%d "
+                      "status=%d - %s\n",
+                      dev->name, MAC2STR(hdr->addr1), auth_alg,
+                      auth_transaction, status, txt);
+       }
+       dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       struct net_device *dev = ap->local->dev;
+       struct ieee80211_hdr *hdr;
+       u16 fc, *pos, status;
+       struct sta_info *sta = NULL;
+       char *txt = NULL;
+
+       if (ap->local->hostapd) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_MGMT ||
+           (WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_ASSOC_RESP &&
+            WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_REASSOC_RESP) ||
+           skb->len < IEEE80211_MGMT_HDR_LEN + 4) {
+               printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid "
+                      "frame\n", dev->name);
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       if (!ok) {
+               txt = "frame was not ACKed";
+               goto done;
+       }
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, hdr->addr1);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&ap->sta_table_lock);
+
+       if (!sta) {
+               txt = "STA not found";
+               goto done;
+       }
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       pos++;
+       status = le16_to_cpu(*pos++);
+       if (status == WLAN_STATUS_SUCCESS) {
+               if (!(sta->flags & WLAN_STA_ASSOC))
+                       hostap_event_new_sta(dev, sta);
+               txt = "STA associated";
+               sta->flags |= WLAN_STA_ASSOC;
+               sta->last_assoc = jiffies;
+       } else
+               txt = "association failed";
+
+ done:
+       if (sta)
+               atomic_dec(&sta->users);
+       if (txt) {
+               PDEBUG(DEBUG_AP, "%s: " MACSTR " assoc_cb - %s\n",
+                      dev->name, MAC2STR(hdr->addr1), txt);
+       }
+       dev_kfree_skb(skb);
+}
+
+/* Called only as a tasklet (software IRQ); TX callback for poll frames used
+ * in verifying whether the STA is still present. */
+static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       struct ieee80211_hdr *hdr;
+       struct sta_info *sta;
+
+       if (skb->len < 24)
+               goto fail;
+       hdr = (struct ieee80211_hdr *) skb->data;
+       if (ok) {
+               spin_lock(&ap->sta_table_lock);
+               sta = ap_get_sta(ap, hdr->addr1);
+               if (sta)
+                       sta->flags &= ~WLAN_STA_PENDING_POLL;
+               spin_unlock(&ap->sta_table_lock);
+       } else {
+               PDEBUG(DEBUG_AP, "%s: STA " MACSTR " did not ACK activity "
+                      "poll frame\n", ap->local->dev->name,
+                      MAC2STR(hdr->addr1));
+       }
+
+ fail:
+       dev_kfree_skb(skb);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+void hostap_init_data(local_info_t *local)
+{
+       struct ap_data *ap = local->ap;
+
+       if (ap == NULL) {
+               printk(KERN_WARNING "hostap_init_data: ap == NULL\n");
+               return;
+       }
+       memset(ap, 0, sizeof(struct ap_data));
+       ap->local = local;
+
+       ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx);
+       ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx);
+       ap->max_inactivity =
+               GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ;
+       ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx);
+
+       spin_lock_init(&ap->sta_table_lock);
+       INIT_LIST_HEAD(&ap->sta_list);
+
+       /* Initialize task queue structure for AP management */
+       INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue, ap);
+
+       ap->tx_callback_idx =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb, ap);
+       if (ap->tx_callback_idx == 0)
+               printk(KERN_WARNING "%s: failed to register TX callback for "
+                      "AP\n", local->dev->name);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue, local);
+
+       ap->tx_callback_auth =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap);
+       ap->tx_callback_assoc =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap);
+       ap->tx_callback_poll =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap);
+       if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 ||
+               ap->tx_callback_poll == 0)
+               printk(KERN_WARNING "%s: failed to register TX callback for "
+                      "AP\n", local->dev->name);
+
+       spin_lock_init(&ap->mac_restrictions.lock);
+       INIT_LIST_HEAD(&ap->mac_restrictions.mac_list);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       ap->initialized = 1;
+}
+
+
+void hostap_init_ap_proc(local_info_t *local)
+{
+       struct ap_data *ap = local->ap;
+
+       ap->proc = local->proc;
+       if (ap->proc == NULL)
+               return;
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+       create_proc_read_entry("ap_debug", 0, ap->proc,
+                              ap_debug_proc_read, ap);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       create_proc_read_entry("ap_control", 0, ap->proc,
+                              ap_control_proc_read, ap);
+       create_proc_read_entry("ap", 0, ap->proc,
+                              prism2_ap_proc_read, ap);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+}
+
+
+void hostap_free_data(struct ap_data *ap)
+{
+       struct list_head *n, *ptr;
+
+       if (ap == NULL || !ap->initialized) {
+               printk(KERN_DEBUG "hostap_free_data: ap has not yet been "
+                      "initialized - skip resource freeing\n");
+               return;
+       }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (ap->crypt)
+               ap->crypt->deinit(ap->crypt_priv);
+       ap->crypt = ap->crypt_priv = NULL;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       list_for_each_safe(ptr, n, &ap->sta_list) {
+               struct sta_info *sta = list_entry(ptr, struct sta_info, list);
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+                       hostap_event_expired_sta(sta->local->dev, sta);
+               ap_free_sta(ap, sta);
+       }
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+       if (ap->proc != NULL) {
+               remove_proc_entry("ap_debug", ap->proc);
+       }
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (ap->proc != NULL) {
+         remove_proc_entry("ap", ap->proc);
+               remove_proc_entry("ap_control", ap->proc);
+       }
+       ap_control_flush_macs(&ap->mac_restrictions);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       ap->initialized = 0;
+}
+
+
+/* caller should have mutex for AP STA list handling */
+static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta)
+{
+       struct sta_info *s;
+
+       s = ap->sta_hash[STA_HASH(sta)];
+       while (s != NULL && memcmp(s->addr, sta, ETH_ALEN) != 0)
+               s = s->hnext;
+       return s;
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+/* Called from timer handler and from scheduled AP queue handlers */
+static void prism2_send_mgmt(struct net_device *dev,
+                            u16 type_subtype, char *body,
+                            int body_len, u8 *addr, u16 tx_cb_idx)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       struct sk_buff *skb;
+       struct hostap_skb_tx_data *meta;
+       int hdrlen;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       dev = local->dev; /* always use master radio device */
+       iface = netdev_priv(dev);
+
+       if (!(dev->flags & IFF_UP)) {
+               PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - "
+                      "cannot send frame\n", dev->name);
+               return;
+       }
+
+       skb = dev_alloc_skb(sizeof(*hdr) + body_len);
+       if (skb == NULL) {
+               PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate "
+                      "skb\n", dev->name);
+               return;
+       }
+
+       fc = type_subtype;
+       hdrlen = hostap_80211_get_hdrlen(fc);
+       hdr = (struct ieee80211_hdr *) skb_put(skb, hdrlen);
+       if (body)
+               memcpy(skb_put(skb, body_len), body, body_len);
+
+       memset(hdr, 0, hdrlen);
+
+       /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
+        * tx_control instead of using local->tx_control */
+
+
+       memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */
+       if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) {
+               fc |= IEEE80211_FCTL_FROMDS;
+               memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */
+               memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */
+       } else if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL) {
+               /* control:ACK does not have addr2 or addr3 */
+               memset(hdr->addr2, 0, ETH_ALEN);
+               memset(hdr->addr3, 0, ETH_ALEN);
+       } else {
+               memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */
+               memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */
+       }
+
+       hdr->frame_ctl = cpu_to_le16(fc);
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       memset(meta, 0, sizeof(*meta));
+       meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+       meta->iface = iface;
+       meta->tx_cb_idx = tx_cb_idx;
+
+       skb->dev = dev;
+       skb->mac.raw = skb->nh.raw = skb->data;
+       dev_queue_xmit(skb);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static int prism2_sta_proc_read(char *page, char **start, off_t off,
+                               int count, int *eof, void *data)
+{
+       char *p = page;
+       struct sta_info *sta = (struct sta_info *) data;
+       int i;
+
+       /* FIX: possible race condition.. the STA data could have just expired,
+        * but proc entry was still here so that the read could have started;
+        * some locking should be done here.. */
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       p += sprintf(p, "%s=" MACSTR "\nusers=%d\naid=%d\n"
+                    "flags=0x%04x%s%s%s%s%s%s%s\n"
+                    "capability=0x%02x\nlisten_interval=%d\nsupported_rates=",
+                    sta->ap ? "AP" : "STA",
+                    MAC2STR(sta->addr), atomic_read(&sta->users), sta->aid,
+                    sta->flags,
+                    sta->flags & WLAN_STA_AUTH ? " AUTH" : "",
+                    sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "",
+                    sta->flags & WLAN_STA_PS ? " PS" : "",
+                    sta->flags & WLAN_STA_TIM ? " TIM" : "",
+                    sta->flags & WLAN_STA_PERM ? " PERM" : "",
+                    sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "",
+                    sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "",
+                    sta->capability, sta->listen_interval);
+       /* supported_rates: 500 kbit/s units with msb ignored */
+       for (i = 0; i < sizeof(sta->supported_rates); i++)
+               if (sta->supported_rates[i] != 0)
+                       p += sprintf(p, "%d%sMbps ",
+                                    (sta->supported_rates[i] & 0x7f) / 2,
+                                    sta->supported_rates[i] & 1 ? ".5" : "");
+       p += sprintf(p, "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n"
+                    "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n"
+                    "tx_packets=%lu\n"
+                    "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n"
+                    "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n"
+                    "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n"
+                    "tx[11M]=%d\n"
+                    "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n",
+                    jiffies, sta->last_auth, sta->last_assoc, sta->last_rx,
+                    sta->last_tx,
+                    sta->rx_packets, sta->tx_packets, sta->rx_bytes,
+                    sta->tx_bytes, skb_queue_len(&sta->tx_buf),
+                    sta->last_rx_silence,
+                    sta->last_rx_signal, sta->last_rx_rate / 10,
+                    sta->last_rx_rate % 10 ? ".5" : "",
+                    sta->tx_rate, sta->tx_count[0], sta->tx_count[1],
+                    sta->tx_count[2], sta->tx_count[3],  sta->rx_count[0],
+                    sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]);
+       if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats)
+               p = sta->crypt->ops->print_stats(p, sta->crypt->priv);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (sta->ap) {
+               if (sta->u.ap.channel >= 0)
+                       p += sprintf(p, "channel=%d\n", sta->u.ap.channel);
+               p += sprintf(p, "ssid=");
+               for (i = 0; i < sta->u.ap.ssid_len; i++)
+                       p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 &&
+                                         sta->u.ap.ssid[i] < 127) ?
+                                        "%c" : "<%02x>"),
+                                    sta->u.ap.ssid[i]);
+               p += sprintf(p, "\n");
+       }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       return (p - page);
+}
+
+
+static void handle_add_proc_queue(void *data)
+{
+       struct ap_data *ap = (struct ap_data *) data;
+       struct sta_info *sta;
+       char name[20];
+       struct add_sta_proc_data *entry, *prev;
+
+       entry = ap->add_sta_proc_entries;
+       ap->add_sta_proc_entries = NULL;
+
+       while (entry) {
+               spin_lock_bh(&ap->sta_table_lock);
+               sta = ap_get_sta(ap, entry->addr);
+               if (sta)
+                       atomic_inc(&sta->users);
+               spin_unlock_bh(&ap->sta_table_lock);
+
+               if (sta) {
+                       sprintf(name, MACSTR, MAC2STR(sta->addr));
+                       sta->proc = create_proc_read_entry(
+                               name, 0, ap->proc,
+                               prism2_sta_proc_read, sta);
+
+                       atomic_dec(&sta->users);
+               }
+
+               prev = entry;
+               entry = entry->next;
+               kfree(prev);
+       }
+}
+
+
+static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr)
+{
+       struct sta_info *sta;
+
+       sta = (struct sta_info *)
+               kmalloc(sizeof(struct sta_info), GFP_ATOMIC);
+       if (sta == NULL) {
+               PDEBUG(DEBUG_AP, "AP: kmalloc failed\n");
+               return NULL;
+       }
+
+       /* initialize STA info data */
+       memset(sta, 0, sizeof(struct sta_info));
+       sta->local = ap->local;
+       skb_queue_head_init(&sta->tx_buf);
+       memcpy(sta->addr, addr, ETH_ALEN);
+
+       atomic_inc(&sta->users);
+       spin_lock_bh(&ap->sta_table_lock);
+       list_add(&sta->list, &ap->sta_list);
+       ap->num_sta++;
+       ap_sta_hash_add(ap, sta);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (ap->proc) {
+               struct add_sta_proc_data *entry;
+               /* schedule a non-interrupt context process to add a procfs
+                * entry for the STA since procfs code use GFP_KERNEL */
+               entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+               if (entry) {
+                       memcpy(entry->addr, sta->addr, ETH_ALEN);
+                       entry->next = ap->add_sta_proc_entries;
+                       ap->add_sta_proc_entries = entry;
+                       schedule_work(&ap->add_sta_proc_queue);
+               } else
+                       printk(KERN_DEBUG "Failed to add STA proc data\n");
+       }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       init_timer(&sta->timer);
+       sta->timer.expires = jiffies + ap->max_inactivity;
+       sta->timer.data = (unsigned long) sta;
+       sta->timer.function = ap_handle_timer;
+       if (!ap->local->hostapd)
+               add_timer(&sta->timer);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       return sta;
+}
+
+
+static int ap_tx_rate_ok(int rateidx, struct sta_info *sta,
+                        local_info_t *local)
+{
+       if (rateidx > sta->tx_max_rate ||
+           !(sta->tx_supp_rates & (1 << rateidx)))
+               return 0;
+
+       if (local->tx_rate_control != 0 &&
+           !(local->tx_rate_control & (1 << rateidx)))
+               return 0;
+
+       return 1;
+}
+
+
+static void prism2_check_tx_rates(struct sta_info *sta)
+{
+       int i;
+
+       sta->tx_supp_rates = 0;
+       for (i = 0; i < sizeof(sta->supported_rates); i++) {
+               if ((sta->supported_rates[i] & 0x7f) == 2)
+                       sta->tx_supp_rates |= WLAN_RATE_1M;
+               if ((sta->supported_rates[i] & 0x7f) == 4)
+                       sta->tx_supp_rates |= WLAN_RATE_2M;
+               if ((sta->supported_rates[i] & 0x7f) == 11)
+                       sta->tx_supp_rates |= WLAN_RATE_5M5;
+               if ((sta->supported_rates[i] & 0x7f) == 22)
+                       sta->tx_supp_rates |= WLAN_RATE_11M;
+       }
+       sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0;
+       if (sta->tx_supp_rates & WLAN_RATE_1M) {
+               sta->tx_max_rate = 0;
+               if (ap_tx_rate_ok(0, sta, sta->local)) {
+                       sta->tx_rate = 10;
+                       sta->tx_rate_idx = 0;
+               }
+       }
+       if (sta->tx_supp_rates & WLAN_RATE_2M) {
+               sta->tx_max_rate = 1;
+               if (ap_tx_rate_ok(1, sta, sta->local)) {
+                       sta->tx_rate = 20;
+                       sta->tx_rate_idx = 1;
+               }
+       }
+       if (sta->tx_supp_rates & WLAN_RATE_5M5) {
+               sta->tx_max_rate = 2;
+               if (ap_tx_rate_ok(2, sta, sta->local)) {
+                       sta->tx_rate = 55;
+                       sta->tx_rate_idx = 2;
+               }
+       }
+       if (sta->tx_supp_rates & WLAN_RATE_11M) {
+               sta->tx_max_rate = 3;
+               if (ap_tx_rate_ok(3, sta, sta->local)) {
+                       sta->tx_rate = 110;
+                       sta->tx_rate_idx = 3;
+               }
+       }
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void ap_crypt_init(struct ap_data *ap)
+{
+       ap->crypt = ieee80211_get_crypto_ops("WEP");
+
+       if (ap->crypt) {
+               if (ap->crypt->init) {
+                       ap->crypt_priv = ap->crypt->init(0);
+                       if (ap->crypt_priv == NULL)
+                               ap->crypt = NULL;
+                       else {
+                               u8 key[WEP_KEY_LEN];
+                               get_random_bytes(key, WEP_KEY_LEN);
+                               ap->crypt->set_key(key, WEP_KEY_LEN, NULL,
+                                                  ap->crypt_priv);
+                       }
+               }
+       }
+
+       if (ap->crypt == NULL) {
+               printk(KERN_WARNING "AP could not initialize WEP: load module "
+                      "ieee80211_crypt_wep.ko\n");
+       }
+}
+
+
+/* Generate challenge data for shared key authentication. IEEE 802.11 specifies
+ * that WEP algorithm is used for generating challange. This should be unique,
+ * but otherwise there is not really need for randomness etc. Initialize WEP
+ * with pseudo random key and then use increasing IV to get unique challenge
+ * streams.
+ *
+ * Called only as a scheduled task for pending AP frames.
+ */
+static char * ap_auth_make_challenge(struct ap_data *ap)
+{
+       char *tmpbuf;
+       struct sk_buff *skb;
+
+       if (ap->crypt == NULL) {
+               ap_crypt_init(ap);
+               if (ap->crypt == NULL)
+                       return NULL;
+       }
+
+       tmpbuf = (char *) kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
+       if (tmpbuf == NULL) {
+               PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n");
+               return NULL;
+       }
+
+       skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN +
+                           ap->crypt->extra_prefix_len +
+                           ap->crypt->extra_postfix_len);
+       if (skb == NULL) {
+               kfree(tmpbuf);
+               return NULL;
+       }
+
+       skb_reserve(skb, ap->crypt->extra_prefix_len);
+       memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0,
+              WLAN_AUTH_CHALLENGE_LEN);
+       if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) {
+               dev_kfree_skb(skb);
+               kfree(tmpbuf);
+               return NULL;
+       }
+
+       memcpy(tmpbuf, skb->data + ap->crypt->extra_prefix_len,
+              WLAN_AUTH_CHALLENGE_LEN);
+       dev_kfree_skb(skb);
+
+       return tmpbuf;
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_authen(local_info_t *local, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       size_t hdrlen;
+       struct ap_data *ap = local->ap;
+       char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL;
+       int len, olen;
+       u16 auth_alg, auth_transaction, status_code, *pos;
+       u16 resp = WLAN_STATUS_SUCCESS, fc;
+       struct sta_info *sta = NULL;
+       struct ieee80211_crypt_data *crypt;
+       char *txt = "";
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       hdrlen = hostap_80211_get_hdrlen(fc);
+
+       if (len < 6) {
+               PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload "
+                      "(len=%d) from " MACSTR "\n", dev->name, len,
+                      MAC2STR(hdr->addr2));
+               return;
+       }
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta && sta->crypt)
+               crypt = sta->crypt;
+       else {
+               int idx = 0;
+               if (skb->len >= hdrlen + 3)
+                       idx = skb->data[hdrlen + 3] >> 6;
+               crypt = local->crypt[idx];
+       }
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       auth_alg = __le16_to_cpu(*pos);
+       pos++;
+       auth_transaction = __le16_to_cpu(*pos);
+       pos++;
+       status_code = __le16_to_cpu(*pos);
+       pos++;
+
+       if (memcmp(dev->dev_addr, hdr->addr2, ETH_ALEN) == 0 ||
+           ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) {
+               txt = "authentication denied";
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto fail;
+       }
+
+       if (((local->auth_algs & PRISM2_AUTH_OPEN) &&
+            auth_alg == WLAN_AUTH_OPEN) ||
+           ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) &&
+            crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) {
+       } else {
+               txt = "unsupported algorithm";
+               resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+               goto fail;
+       }
+
+       if (len >= 8) {
+               u8 *u = (u8 *) pos;
+               if (*u == WLAN_EID_CHALLENGE) {
+                       if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) {
+                               txt = "invalid challenge len";
+                               resp = WLAN_STATUS_CHALLENGE_FAIL;
+                               goto fail;
+                       }
+                       if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) {
+                               txt = "challenge underflow";
+                               resp = WLAN_STATUS_CHALLENGE_FAIL;
+                               goto fail;
+                       }
+                       challenge = (char *) (u + 2);
+               }
+       }
+
+       if (sta && sta->ap) {
+               if (time_after(jiffies, sta->u.ap.last_beacon +
+                              (10 * sta->listen_interval * HZ) / 1024)) {
+                       PDEBUG(DEBUG_AP, "%s: no beacons received for a while,"
+                              " assuming AP " MACSTR " is now STA\n",
+                              dev->name, MAC2STR(sta->addr));
+                       sta->ap = 0;
+                       sta->flags = 0;
+                       sta->u.sta.challenge = NULL;
+               } else {
+                       txt = "AP trying to authenticate?";
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+       }
+
+       if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ||
+           (auth_alg == WLAN_AUTH_SHARED_KEY &&
+            (auth_transaction == 1 ||
+             (auth_transaction == 3 && sta != NULL &&
+              sta->u.sta.challenge != NULL)))) {
+       } else {
+               txt = "unknown authentication transaction number";
+               resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+               goto fail;
+       }
+
+       if (sta == NULL) {
+               txt = "new STA";
+
+               if (local->ap->num_sta >= MAX_STA_COUNT) {
+                       /* FIX: might try to remove some old STAs first? */
+                       txt = "no more room for new STAs";
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+
+               sta = ap_add_sta(local->ap, hdr->addr2);
+               if (sta == NULL) {
+                       txt = "ap_add_sta failed";
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+       }
+
+       switch (auth_alg) {
+       case WLAN_AUTH_OPEN:
+               txt = "authOK";
+               /* IEEE 802.11 standard is not completely clear about
+                * whether STA is considered authenticated after
+                * authentication OK frame has been send or after it
+                * has been ACKed. In order to reduce interoperability
+                * issues, mark the STA authenticated before ACK. */
+               sta->flags |= WLAN_STA_AUTH;
+               break;
+
+       case WLAN_AUTH_SHARED_KEY:
+               if (auth_transaction == 1) {
+                       if (sta->u.sta.challenge == NULL) {
+                               sta->u.sta.challenge =
+                                       ap_auth_make_challenge(local->ap);
+                               if (sta->u.sta.challenge == NULL) {
+                                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                                       goto fail;
+                               }
+                       }
+               } else {
+                       if (sta->u.sta.challenge == NULL ||
+                           challenge == NULL ||
+                           memcmp(sta->u.sta.challenge, challenge,
+                                  WLAN_AUTH_CHALLENGE_LEN) != 0 ||
+                           !(fc & IEEE80211_FCTL_PROTECTED)) {
+                               txt = "challenge response incorrect";
+                               resp = WLAN_STATUS_CHALLENGE_FAIL;
+                               goto fail;
+                       }
+
+                       txt = "challenge OK - authOK";
+                       /* IEEE 802.11 standard is not completely clear about
+                        * whether STA is considered authenticated after
+                        * authentication OK frame has been send or after it
+                        * has been ACKed. In order to reduce interoperability
+                        * issues, mark the STA authenticated before ACK. */
+                       sta->flags |= WLAN_STA_AUTH;
+                       kfree(sta->u.sta.challenge);
+                       sta->u.sta.challenge = NULL;
+               }
+               break;
+       }
+
+ fail:
+       pos = (u16 *) body;
+       *pos = cpu_to_le16(auth_alg);
+       pos++;
+       *pos = cpu_to_le16(auth_transaction + 1);
+       pos++;
+       *pos = cpu_to_le16(resp); /* status_code */
+       pos++;
+       olen = 6;
+
+       if (resp == WLAN_STATUS_SUCCESS && sta != NULL &&
+           sta->u.sta.challenge != NULL &&
+           auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) {
+               u8 *tmp = (u8 *) pos;
+               *tmp++ = WLAN_EID_CHALLENGE;
+               *tmp++ = WLAN_AUTH_CHALLENGE_LEN;
+               pos++;
+               memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN);
+               olen += 2 + WLAN_AUTH_CHALLENGE_LEN;
+       }
+
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH,
+                        body, olen, hdr->addr2, ap->tx_callback_auth);
+
+       if (sta) {
+               sta->last_rx = jiffies;
+               atomic_dec(&sta->users);
+       }
+
+       if (resp) {
+               PDEBUG(DEBUG_AP, "%s: " MACSTR " auth (alg=%d trans#=%d "
+                      "stat=%d len=%d fc=%04x) ==> %d (%s)\n",
+                      dev->name, MAC2STR(hdr->addr2), auth_alg,
+                      auth_transaction, status_code, len, fc, resp, txt);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_assoc(local_info_t *local, struct sk_buff *skb,
+                        struct hostap_80211_rx_status *rx_stats, int reassoc)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char body[12], *p, *lpos;
+       int len, left;
+       u16 *pos;
+       u16 resp = WLAN_STATUS_SUCCESS;
+       struct sta_info *sta = NULL;
+       int send_deauth = 0;
+       char *txt = "";
+       u8 prev_ap[ETH_ALEN];
+
+       left = len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < (reassoc ? 10 : 4)) {
+               PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload "
+                      "(len=%d, reassoc=%d) from " MACSTR "\n",
+                      dev->name, len, reassoc, MAC2STR(hdr->addr2));
+               return;
+       }
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+               spin_unlock_bh(&local->ap->sta_table_lock);
+               txt = "trying to associate before authentication";
+               send_deauth = 1;
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               sta = NULL; /* do not decrement sta->users */
+               goto fail;
+       }
+       atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       sta->capability = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+       sta->listen_interval = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+
+       if (reassoc) {
+               memcpy(prev_ap, pos, ETH_ALEN);
+               pos++; pos++; pos++; left -= 6;
+       } else
+               memset(prev_ap, 0, ETH_ALEN);
+
+       if (left >= 2) {
+               unsigned int ileft;
+               unsigned char *u = (unsigned char *) pos;
+
+               if (*u == WLAN_EID_SSID) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft > MAX_SSID_LEN) {
+                               txt = "SSID overflow";
+                               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto fail;
+                       }
+
+                       if (ileft != strlen(local->essid) ||
+                           memcmp(local->essid, u, ileft) != 0) {
+                               txt = "not our SSID";
+                               resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+                               goto fail;
+                       }
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (left >= 2 && *u == WLAN_EID_SUPP_RATES) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft == 0 ||
+                           ileft > WLAN_SUPP_RATES_MAX) {
+                               txt = "SUPP_RATES len error";
+                               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto fail;
+                       }
+
+                       memset(sta->supported_rates, 0,
+                              sizeof(sta->supported_rates));
+                       memcpy(sta->supported_rates, u, ileft);
+                       prism2_check_tx_rates(sta);
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (left > 0) {
+                       PDEBUG(DEBUG_AP, "%s: assoc from " MACSTR " with extra"
+                              " data (%d bytes) [",
+                              dev->name, MAC2STR(hdr->addr2), left);
+                       while (left > 0) {
+                               PDEBUG2(DEBUG_AP, "<%02x>", *u);
+                               u++; left--;
+                       }
+                       PDEBUG2(DEBUG_AP, "]\n");
+               }
+       } else {
+               txt = "frame underflow";
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto fail;
+       }
+
+       /* get a unique AID */
+       if (sta->aid > 0)
+               txt = "OK, old AID";
+       else {
+               spin_lock_bh(&local->ap->sta_table_lock);
+               for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
+                       if (local->ap->sta_aid[sta->aid - 1] == NULL)
+                               break;
+               if (sta->aid > MAX_AID_TABLE_SIZE) {
+                       sta->aid = 0;
+                       spin_unlock_bh(&local->ap->sta_table_lock);
+                       resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+                       txt = "no room for more AIDs";
+               } else {
+                       local->ap->sta_aid[sta->aid - 1] = sta;
+                       spin_unlock_bh(&local->ap->sta_table_lock);
+                       txt = "OK, new AID";
+               }
+       }
+
+ fail:
+       pos = (u16 *) body;
+
+       if (send_deauth) {
+               *pos = __constant_cpu_to_le16(
+                       WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH);
+               pos++;
+       } else {
+               /* FIX: CF-Pollable and CF-PollReq should be set to match the
+                * values in beacons/probe responses */
+               /* FIX: how about privacy and WEP? */
+               /* capability */
+               *pos = __constant_cpu_to_le16(WLAN_CAPABILITY_ESS);
+               pos++;
+
+               /* status_code */
+               *pos = __cpu_to_le16(resp);
+               pos++;
+
+               *pos = __cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) |
+                                    BIT(14) | BIT(15)); /* AID */
+               pos++;
+
+               /* Supported rates (Information element) */
+               p = (char *) pos;
+               *p++ = WLAN_EID_SUPP_RATES;
+               lpos = p;
+               *p++ = 0; /* len */
+               if (local->tx_rate_control & WLAN_RATE_1M) {
+                       *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02;
+                       (*lpos)++;
+               }
+               if (local->tx_rate_control & WLAN_RATE_2M) {
+                       *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04;
+                       (*lpos)++;
+               }
+               if (local->tx_rate_control & WLAN_RATE_5M5) {
+                       *p++ = local->basic_rates & WLAN_RATE_5M5 ?
+                               0x8b : 0x0b;
+                       (*lpos)++;
+               }
+               if (local->tx_rate_control & WLAN_RATE_11M) {
+                       *p++ = local->basic_rates & WLAN_RATE_11M ?
+                               0x96 : 0x16;
+                       (*lpos)++;
+               }
+               pos = (u16 *) p;
+       }
+
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+                        (send_deauth ? IEEE80211_STYPE_DEAUTH :
+                         (reassoc ? IEEE80211_STYPE_REASSOC_RESP :
+                          IEEE80211_STYPE_ASSOC_RESP)),
+                        body, (u8 *) pos - (u8 *) body,
+                        hdr->addr2,
+                        send_deauth ? 0 : local->ap->tx_callback_assoc);
+
+       if (sta) {
+               if (resp == WLAN_STATUS_SUCCESS) {
+                       sta->last_rx = jiffies;
+                       /* STA will be marked associated from TX callback, if
+                        * AssocResp is ACKed */
+               }
+               atomic_dec(&sta->users);
+       }
+
+#if 0
+       PDEBUG(DEBUG_AP, "%s: " MACSTR " %sassoc (len=%d prev_ap=" MACSTR
+              ") => %d(%d) (%s)\n",
+              dev->name, MAC2STR(hdr->addr2), reassoc ? "re" : "", len,
+              MAC2STR(prev_ap), resp, send_deauth, txt);
+#endif
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_deauth(local_info_t *local, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       int len;
+       u16 reason_code, *pos;
+       struct sta_info *sta = NULL;
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < 2) {
+               printk("handle_deauth - too short payload (len=%d)\n", len);
+               return;
+       }
+
+       pos = (u16 *) body;
+       reason_code = __le16_to_cpu(*pos);
+
+       PDEBUG(DEBUG_AP, "%s: deauthentication: " MACSTR " len=%d, "
+              "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len,
+              reason_code);
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta != NULL) {
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+                       hostap_event_expired_sta(local->dev, sta);
+               sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+       }
+       spin_unlock_bh(&local->ap->sta_table_lock);
+       if (sta == NULL) {
+               printk("%s: deauthentication from " MACSTR ", "
+              "reason_code=%d, but STA not authenticated\n", dev->name,
+                      MAC2STR(hdr->addr2), reason_code);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_disassoc(local_info_t *local, struct sk_buff *skb,
+                           struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
+       int len;
+       u16 reason_code, *pos;
+       struct sta_info *sta = NULL;
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < 2) {
+               printk("handle_disassoc - too short payload (len=%d)\n", len);
+               return;
+       }
+
+       pos = (u16 *) body;
+       reason_code = __le16_to_cpu(*pos);
+
+       PDEBUG(DEBUG_AP, "%s: disassociation: " MACSTR " len=%d, "
+              "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len,
+              reason_code);
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta != NULL) {
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+                       hostap_event_expired_sta(local->dev, sta);
+               sta->flags &= ~WLAN_STA_ASSOC;
+       }
+       spin_unlock_bh(&local->ap->sta_table_lock);
+       if (sta == NULL) {
+               printk("%s: disassociation from " MACSTR ", "
+                      "reason_code=%d, but STA not authenticated\n",
+                      dev->name, MAC2STR(hdr->addr2), reason_code);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void ap_handle_data_nullfunc(local_info_t *local,
+                                   struct ieee80211_hdr *hdr)
+{
+       struct net_device *dev = local->dev;
+
+       /* some STA f/w's seem to require control::ACK frame for
+        * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does
+        * not send this..
+        * send control::ACK for the data::nullfunc */
+
+       printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n");
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK,
+                        NULL, 0, hdr->addr2, 0);
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void ap_handle_dropped_data(local_info_t *local,
+                                  struct ieee80211_hdr *hdr)
+{
+       struct net_device *dev = local->dev;
+       struct sta_info *sta;
+       u16 reason;
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) {
+               PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n");
+               atomic_dec(&sta->users);
+               return;
+       }
+
+       reason = __constant_cpu_to_le16(
+               WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+                        ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ?
+                         IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC),
+                        (char *) &reason, sizeof(reason), hdr->addr2, 0);
+
+       if (sta)
+               atomic_dec(&sta->users);
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta,
+                                struct sk_buff *skb)
+{
+       struct hostap_skb_tx_data *meta;
+
+       if (!(sta->flags & WLAN_STA_PS)) {
+               /* Station has moved to non-PS mode, so send all buffered
+                * frames using normal device queue. */
+               dev_queue_xmit(skb);
+               return;
+       }
+
+       /* add a flag for hostap_handle_sta_tx() to know that this skb should
+        * be passed through even though STA is using PS */
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME;
+       if (!skb_queue_empty(&sta->tx_buf)) {
+               /* indicate to STA that more frames follow */
+               meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA;
+       }
+       dev_queue_xmit(skb);
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_pspoll(local_info_t *local,
+                         struct ieee80211_hdr *hdr,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct sta_info *sta;
+       u16 aid;
+       struct sk_buff *skb;
+
+       PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=" MACSTR ", TA=" MACSTR
+              " PWRMGT=%d\n",
+              MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+              !!(le16_to_cpu(hdr->frame_ctl) & IEEE80211_FCTL_PM));
+
+       if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+               PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=" MACSTR
+                      " not own MAC\n", MAC2STR(hdr->addr1));
+               return;
+       }
+
+       aid = __le16_to_cpu(hdr->duration_id);
+       if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) {
+               PDEBUG(DEBUG_PS, "   PSPOLL and AID[15:14] not set\n");
+               return;
+       }
+       aid &= ~BIT(15) & ~BIT(14);
+       if (aid == 0 || aid > MAX_AID_TABLE_SIZE) {
+               PDEBUG(DEBUG_PS, "   invalid aid=%d\n", aid);
+               return;
+       }
+       PDEBUG(DEBUG_PS2, "   aid=%d\n", aid);
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta == NULL) {
+               PDEBUG(DEBUG_PS, "   STA not found\n");
+               return;
+       }
+       if (sta->aid != aid) {
+               PDEBUG(DEBUG_PS, "   received aid=%i does not match with "
+                      "assoc.aid=%d\n", aid, sta->aid);
+               return;
+       }
+
+       /* FIX: todo:
+        * - add timeout for buffering (clear aid in TIM vector if buffer timed
+        *   out (expiry time must be longer than ListenInterval for
+        *   the corresponding STA; "8802-11: 11.2.1.9 AP aging function"
+        * - what to do, if buffered, pspolled, and sent frame is not ACKed by
+        *   sta; store buffer for later use and leave TIM aid bit set? use
+        *   TX event to check whether frame was ACKed?
+        */
+
+       while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) {
+               /* send buffered frame .. */
+               PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL"
+                      " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf));
+
+               pspoll_send_buffered(local, sta, skb);
+
+               if (sta->flags & WLAN_STA_PS) {
+                       /* send only one buffered packet per PS Poll */
+                       /* FIX: should ignore further PS Polls until the
+                        * buffered packet that was just sent is acknowledged
+                        * (Tx or TxExc event) */
+                       break;
+               }
+       }
+
+       if (skb_queue_empty(&sta->tx_buf)) {
+               /* try to clear aid from TIM */
+               if (!(sta->flags & WLAN_STA_TIM))
+                       PDEBUG(DEBUG_PS2,  "Re-unsetting TIM for aid %d\n",
+                              aid);
+               hostap_set_tim(local, aid, 0);
+               sta->flags &= ~WLAN_STA_TIM;
+       }
+
+       atomic_dec(&sta->users);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void handle_wds_oper_queue(void *data)
+{
+       local_info_t *local = data;
+       struct wds_oper_data *entry, *prev;
+
+       spin_lock_bh(&local->lock);
+       entry = local->ap->wds_oper_entries;
+       local->ap->wds_oper_entries = NULL;
+       spin_unlock_bh(&local->lock);
+
+       while (entry) {
+               PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection "
+                      "to AP " MACSTR "\n",
+                      local->dev->name,
+                      entry->type == WDS_ADD ? "adding" : "removing",
+                      MAC2STR(entry->addr));
+               if (entry->type == WDS_ADD)
+                       prism2_wds_add(local, entry->addr, 0);
+               else if (entry->type == WDS_DEL)
+                       prism2_wds_del(local, entry->addr, 0, 1);
+
+               prev = entry;
+               entry = entry->next;
+               kfree(prev);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_beacon(local_info_t *local, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
+       int len, left;
+       u16 *pos, beacon_int, capability;
+       char *ssid = NULL;
+       unsigned char *supp_rates = NULL;
+       int ssid_len = 0, supp_rates_len = 0;
+       struct sta_info *sta = NULL;
+       int new_sta = 0, channel = -1;
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < 8 + 2 + 2) {
+               printk(KERN_DEBUG "handle_beacon - too short payload "
+                      "(len=%d)\n", len);
+               return;
+       }
+
+       pos = (u16 *) body;
+       left = len;
+
+       /* Timestamp (8 octets) */
+       pos += 4; left -= 8;
+       /* Beacon interval (2 octets) */
+       beacon_int = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+       /* Capability information (2 octets) */
+       capability = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+
+       if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS &&
+           capability & WLAN_CAPABILITY_IBSS)
+               return;
+
+       if (left >= 2) {
+               unsigned int ileft;
+               unsigned char *u = (unsigned char *) pos;
+
+               if (*u == WLAN_EID_SSID) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft > MAX_SSID_LEN) {
+                               PDEBUG(DEBUG_AP, "SSID: overflow\n");
+                               return;
+                       }
+
+                       if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID &&
+                           (ileft != strlen(local->essid) ||
+                            memcmp(local->essid, u, ileft) != 0)) {
+                               /* not our SSID */
+                               return;
+                       }
+
+                       ssid = u;
+                       ssid_len = ileft;
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (*u == WLAN_EID_SUPP_RATES) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft == 0 || ileft > 8) {
+                               PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n");
+                               return;
+                       }
+
+                       supp_rates = u;
+                       supp_rates_len = ileft;
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (*u == WLAN_EID_DS_PARAMS) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft != 1) {
+                               PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n");
+                               return;
+                       }
+
+                       channel = *u;
+
+                       u += ileft;
+                       left -= ileft;
+               }
+       }
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta != NULL)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta == NULL) {
+               /* add new AP */
+               new_sta = 1;
+               sta = ap_add_sta(local->ap, hdr->addr2);
+               if (sta == NULL) {
+                       printk(KERN_INFO "prism2: kmalloc failed for AP "
+                              "data structure\n");
+                       return;
+               }
+               hostap_event_new_sta(local->dev, sta);
+
+               /* mark APs authentication and associated for pseudo ad-hoc
+                * style communication */
+               sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+
+               if (local->ap->autom_ap_wds) {
+                       hostap_wds_link_oper(local, sta->addr, WDS_ADD);
+               }
+       }
+
+       sta->ap = 1;
+       if (ssid) {
+               sta->u.ap.ssid_len = ssid_len;
+               memcpy(sta->u.ap.ssid, ssid, ssid_len);
+               sta->u.ap.ssid[ssid_len] = '\0';
+       } else {
+               sta->u.ap.ssid_len = 0;
+               sta->u.ap.ssid[0] = '\0';
+       }
+       sta->u.ap.channel = channel;
+       sta->rx_packets++;
+       sta->rx_bytes += len;
+       sta->u.ap.last_beacon = sta->last_rx = jiffies;
+       sta->capability = capability;
+       sta->listen_interval = beacon_int;
+
+       atomic_dec(&sta->users);
+
+       if (new_sta) {
+               memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+               memcpy(sta->supported_rates, supp_rates, supp_rates_len);
+               prism2_check_tx_rates(sta);
+       }
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+/* Called only as a tasklet. */
+static void handle_ap_item(local_info_t *local, struct sk_buff *skb,
+                          struct hostap_80211_rx_status *rx_stats)
+{
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       struct net_device *dev = local->dev;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+       u16 fc, type, stype;
+       struct ieee80211_hdr *hdr;
+
+       /* FIX: should give skb->len to handler functions and check that the
+        * buffer is long enough */
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (!local->hostapd && type == IEEE80211_FTYPE_DATA) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n");
+
+               if (!(fc & IEEE80211_FCTL_TODS) ||
+                   (fc & IEEE80211_FCTL_FROMDS)) {
+                       if (stype == IEEE80211_STYPE_NULLFUNC) {
+                               /* no ToDS nullfunc seems to be used to check
+                                * AP association; so send reject message to
+                                * speed up re-association */
+                               ap_handle_dropped_data(local, hdr);
+                               goto done;
+                       }
+                       PDEBUG(DEBUG_AP, "   not ToDS frame (fc=0x%04x)\n",
+                              fc);
+                       goto done;
+               }
+
+               if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+                       PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)="
+                              MACSTR " not own MAC\n",
+                              MAC2STR(hdr->addr1));
+                       goto done;
+               }
+
+               if (local->ap->nullfunc_ack &&
+                   stype == IEEE80211_STYPE_NULLFUNC)
+                       ap_handle_data_nullfunc(local, hdr);
+               else
+                       ap_handle_dropped_data(local, hdr);
+               goto done;
+       }
+
+       if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) {
+               handle_beacon(local, skb, rx_stats);
+               goto done;
+       }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) {
+               handle_pspoll(local, hdr, rx_stats);
+               goto done;
+       }
+
+       if (local->hostapd) {
+               PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x "
+                      "subtype=0x%02x\n", type, stype);
+               goto done;
+       }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (type != IEEE80211_FTYPE_MGMT) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n");
+               goto done;
+       }
+
+       if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=" MACSTR
+                      " not own MAC\n", MAC2STR(hdr->addr1));
+               goto done;
+       }
+
+       if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN)) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=" MACSTR
+                      " not own MAC\n", MAC2STR(hdr->addr3));
+               goto done;
+       }
+
+       switch (stype) {
+       case IEEE80211_STYPE_ASSOC_REQ:
+               handle_assoc(local, skb, rx_stats, 0);
+               break;
+       case IEEE80211_STYPE_ASSOC_RESP:
+               PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n");
+               break;
+       case IEEE80211_STYPE_REASSOC_REQ:
+               handle_assoc(local, skb, rx_stats, 1);
+               break;
+       case IEEE80211_STYPE_REASSOC_RESP:
+               PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n");
+               break;
+       case IEEE80211_STYPE_ATIM:
+               PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n");
+               break;
+       case IEEE80211_STYPE_DISASSOC:
+               handle_disassoc(local, skb, rx_stats);
+               break;
+       case IEEE80211_STYPE_AUTH:
+               handle_authen(local, skb, rx_stats);
+               break;
+       case IEEE80211_STYPE_DEAUTH:
+               handle_deauth(local, skb, rx_stats);
+               break;
+       default:
+               PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n",
+                      stype >> 4);
+               break;
+       }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ done:
+       dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+void hostap_rx(struct net_device *dev, struct sk_buff *skb,
+              struct hostap_80211_rx_status *rx_stats)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 fc;
+       struct ieee80211_hdr *hdr;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (skb->len < 16)
+               goto drop;
+
+       local->stats.rx_packets++;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL &&
+           WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_BEACON)
+               goto drop;
+
+       skb->protocol = __constant_htons(ETH_P_HOSTAP);
+       handle_ap_item(local, skb, rx_stats);
+       return;
+
+ drop:
+       dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void schedule_packet_send(local_info_t *local, struct sta_info *sta)
+{
+       struct sk_buff *skb;
+       struct ieee80211_hdr *hdr;
+       struct hostap_80211_rx_status rx_stats;
+
+       if (skb_queue_empty(&sta->tx_buf))
+               return;
+
+       skb = dev_alloc_skb(16);
+       if (skb == NULL) {
+               printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc "
+                      "failed\n", local->dev->name);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb_put(skb, 16);
+
+       /* Generate a fake pspoll frame to start packet delivery */
+       hdr->frame_ctl = __constant_cpu_to_le16(
+               IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
+       memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN);
+       memcpy(hdr->addr2, sta->addr, ETH_ALEN);
+       hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14));
+
+       PDEBUG(DEBUG_PS2, "%s: Scheduling buffered packet delivery for "
+              "STA " MACSTR "\n", local->dev->name, MAC2STR(sta->addr));
+
+       skb->dev = local->dev;
+
+       memset(&rx_stats, 0, sizeof(rx_stats));
+       hostap_rx(local->dev, skb, &rx_stats);
+}
+
+
+static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
+                                 struct iw_quality qual[], int buf_size,
+                                 int aplist)
+{
+       struct ap_data *ap = local->ap;
+       struct list_head *ptr;
+       int count = 0;
+
+       spin_lock_bh(&ap->sta_table_lock);
+
+       for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
+            ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+
+               if (aplist && !sta->ap)
+                       continue;
+               addr[count].sa_family = ARPHRD_ETHER;
+               memcpy(addr[count].sa_data, sta->addr, ETH_ALEN);
+               if (sta->last_rx_silence == 0)
+                       qual[count].qual = sta->last_rx_signal < 27 ?
+                               0 : (sta->last_rx_signal - 27) * 92 / 127;
+               else
+                       qual[count].qual = sta->last_rx_signal -
+                               sta->last_rx_silence - 35;
+               qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
+               qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
+               qual[count].updated = sta->last_rx_updated;
+
+               sta->last_rx_updated = 0;
+
+               count++;
+               if (count >= buf_size)
+                       break;
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       return count;
+}
+
+
+/* Translate our list of Access Points & Stations to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static int prism2_ap_translate_scan(struct net_device *dev, char *buffer)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct ap_data *ap;
+       struct list_head *ptr;
+       struct iw_event iwe;
+       char *current_ev = buffer;
+       char *end_buf = buffer + IW_SCAN_MAX_DATA;
+#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT)
+       char buf[64];
+#endif
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       ap = local->ap;
+
+       spin_lock_bh(&ap->sta_table_lock);
+
+       for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
+            ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+
+               /* First entry *MUST* be the AP MAC address */
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = SIOCGIWAP;
+               iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN);
+               iwe.len = IW_EV_ADDR_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_ADDR_LEN);
+
+               /* Use the mode to indicate if it's a station or
+                * an Access Point */
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = SIOCGIWMODE;
+               if (sta->ap)
+                       iwe.u.mode = IW_MODE_MASTER;
+               else
+                       iwe.u.mode = IW_MODE_INFRA;
+               iwe.len = IW_EV_UINT_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_UINT_LEN);
+
+               /* Some quality */
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVQUAL;
+               if (sta->last_rx_silence == 0)
+                       iwe.u.qual.qual = sta->last_rx_signal < 27 ?
+                               0 : (sta->last_rx_signal - 27) * 92 / 127;
+               else
+                       iwe.u.qual.qual = sta->last_rx_signal -
+                               sta->last_rx_silence - 35;
+               iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
+               iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
+               iwe.u.qual.updated = sta->last_rx_updated;
+               iwe.len = IW_EV_QUAL_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_QUAL_LEN);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+               if (sta->ap) {
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = SIOCGIWESSID;
+                       iwe.u.data.length = sta->u.ap.ssid_len;
+                       iwe.u.data.flags = 1;
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                                         &iwe,
+                                                         sta->u.ap.ssid);
+
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = SIOCGIWENCODE;
+                       if (sta->capability & WLAN_CAPABILITY_PRIVACY)
+                               iwe.u.data.flags =
+                                       IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+                       else
+                               iwe.u.data.flags = IW_ENCODE_DISABLED;
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                                         &iwe,
+                                                         sta->u.ap.ssid
+                                                         /* 0 byte memcpy */);
+
+                       if (sta->u.ap.channel > 0 &&
+                           sta->u.ap.channel <= FREQ_COUNT) {
+                               memset(&iwe, 0, sizeof(iwe));
+                               iwe.cmd = SIOCGIWFREQ;
+                               iwe.u.freq.m = freq_list[sta->u.ap.channel - 1]
+                                       * 100000;
+                               iwe.u.freq.e = 1;
+                               current_ev = iwe_stream_add_event(
+                                       current_ev, end_buf, &iwe,
+                                       IW_EV_FREQ_LEN);
+                       }
+
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = IWEVCUSTOM;
+                       sprintf(buf, "beacon_interval=%d",
+                               sta->listen_interval);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                                         &iwe, buf);
+               }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+               sta->last_rx_updated = 0;
+
+               /* To be continued, we should make good use of IWEVCUSTOM */
+       }
+
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       return current_ev - buffer;
+}
+
+
+static int prism2_hostapd_add_sta(struct ap_data *ap,
+                                 struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (sta == NULL) {
+               sta = ap_add_sta(ap, param->sta_addr);
+               if (sta == NULL)
+                       return -1;
+       }
+
+       if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+               hostap_event_new_sta(sta->local->dev, sta);
+
+       sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+       sta->last_rx = jiffies;
+       sta->aid = param->u.add_sta.aid;
+       sta->capability = param->u.add_sta.capability;
+       sta->tx_supp_rates = param->u.add_sta.tx_supp_rates;
+       if (sta->tx_supp_rates & WLAN_RATE_1M)
+               sta->supported_rates[0] = 2;
+       if (sta->tx_supp_rates & WLAN_RATE_2M)
+               sta->supported_rates[1] = 4;
+       if (sta->tx_supp_rates & WLAN_RATE_5M5)
+               sta->supported_rates[2] = 11;
+       if (sta->tx_supp_rates & WLAN_RATE_11M)
+               sta->supported_rates[3] = 22;
+       prism2_check_tx_rates(sta);
+       atomic_dec(&sta->users);
+       return 0;
+}
+
+
+static int prism2_hostapd_remove_sta(struct ap_data *ap,
+                                    struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta) {
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+               hostap_event_expired_sta(sta->local->dev, sta);
+       ap_free_sta(ap, sta);
+
+       return 0;
+}
+
+
+static int prism2_hostapd_get_info_sta(struct ap_data *ap,
+                                      struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ;
+
+       atomic_dec(&sta->users);
+
+       return 1;
+}
+
+
+static int prism2_hostapd_set_flags_sta(struct ap_data *ap,
+                                       struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta) {
+               sta->flags |= param->u.set_flags_sta.flags_or;
+               sta->flags &= param->u.set_flags_sta.flags_and;
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       return 0;
+}
+
+
+static int prism2_hostapd_sta_clear_stats(struct ap_data *ap,
+                                         struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+       int rate;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta) {
+               sta->rx_packets = sta->tx_packets = 0;
+               sta->rx_bytes = sta->tx_bytes = 0;
+               for (rate = 0; rate < WLAN_RATE_COUNT; rate++) {
+                       sta->tx_count[rate] = 0;
+                       sta->rx_count[rate] = 0;
+               }
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       return 0;
+}
+
+
+static int prism2_hostapd(struct ap_data *ap,
+                         struct prism2_hostapd_param *param)
+{
+       switch (param->cmd) {
+       case PRISM2_HOSTAPD_FLUSH:
+               ap_control_kickall(ap);
+               return 0;
+       case PRISM2_HOSTAPD_ADD_STA:
+               return prism2_hostapd_add_sta(ap, param);
+       case PRISM2_HOSTAPD_REMOVE_STA:
+               return prism2_hostapd_remove_sta(ap, param);
+       case PRISM2_HOSTAPD_GET_INFO_STA:
+               return prism2_hostapd_get_info_sta(ap, param);
+       case PRISM2_HOSTAPD_SET_FLAGS_STA:
+               return prism2_hostapd_set_flags_sta(ap, param);
+       case PRISM2_HOSTAPD_STA_CLEAR_STATS:
+               return prism2_hostapd_sta_clear_stats(ap, param);
+       default:
+               printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n",
+                      param->cmd);
+               return -EOPNOTSUPP;
+       }
+}
+
+
+/* Update station info for host-based TX rate control and return current
+ * TX rate */
+static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev)
+{
+       int ret = sta->tx_rate;
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       sta->tx_count[sta->tx_rate_idx]++;
+       sta->tx_since_last_failure++;
+       sta->tx_consecutive_exc = 0;
+       if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT &&
+           sta->tx_rate_idx < sta->tx_max_rate) {
+               /* use next higher rate */
+               int old_rate, new_rate;
+               old_rate = new_rate = sta->tx_rate_idx;
+               while (new_rate < sta->tx_max_rate) {
+                       new_rate++;
+                       if (ap_tx_rate_ok(new_rate, sta, local)) {
+                               sta->tx_rate_idx = new_rate;
+                               break;
+                       }
+               }
+               if (old_rate != sta->tx_rate_idx) {
+                       switch (sta->tx_rate_idx) {
+                       case 0: sta->tx_rate = 10; break;
+                       case 1: sta->tx_rate = 20; break;
+                       case 2: sta->tx_rate = 55; break;
+                       case 3: sta->tx_rate = 110; break;
+                       default: sta->tx_rate = 0; break;
+                       }
+                       PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate raised to"
+                              " %d\n", dev->name, MAC2STR(sta->addr),
+                              sta->tx_rate);
+               }
+               sta->tx_since_last_failure = 0;
+       }
+
+       return ret;
+}
+
+
+/* Called only from software IRQ. Called for each TX frame prior possible
+ * encryption and transmit. */
+ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
+{
+       struct sta_info *sta = NULL;
+       struct sk_buff *skb = tx->skb;
+       int set_tim, ret;
+       struct ieee80211_hdr *hdr;
+       struct hostap_skb_tx_data *meta;
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       ret = AP_TX_CONTINUE;
+       if (local->ap == NULL || skb->len < 10 ||
+           meta->iface->type == HOSTAP_INTERFACE_STA)
+               goto out;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       if (hdr->addr1[0] & 0x01) {
+               /* broadcast/multicast frame - no AP related processing */
+               goto out;
+       }
+
+       /* unicast packet - check whether destination STA is associated */
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr1);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (local->iw_mode == IW_MODE_MASTER && sta == NULL &&
+           !(meta->flags & HOSTAP_TX_FLAGS_WDS) &&
+           meta->iface->type != HOSTAP_INTERFACE_MASTER &&
+           meta->iface->type != HOSTAP_INTERFACE_AP) {
+#if 0
+               /* This can happen, e.g., when wlan0 is added to a bridge and
+                * bridging code does not know which port is the correct target
+                * for a unicast frame. In this case, the packet is send to all
+                * ports of the bridge. Since this is a valid scenario, do not
+                * print out any errors here. */
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "AP: drop packet to non-associated "
+                              "STA " MACSTR "\n", MAC2STR(hdr->addr1));
+               }
+#endif
+               local->ap->tx_drop_nonassoc++;
+               ret = AP_TX_DROP;
+               goto out;
+       }
+
+       if (sta == NULL)
+               goto out;
+
+       if (!(sta->flags & WLAN_STA_AUTHORIZED))
+               ret = AP_TX_CONTINUE_NOT_AUTHORIZED;
+
+       /* Set tx_rate if using host-based TX rate control */
+       if (!local->fw_tx_rate_control)
+               local->ap->last_tx_rate = meta->rate =
+                       ap_update_sta_tx_rate(sta, local->dev);
+
+       if (local->iw_mode != IW_MODE_MASTER)
+               goto out;
+
+       if (!(sta->flags & WLAN_STA_PS))
+               goto out;
+
+       if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) {
+               /* indicate to STA that more frames follow */
+               hdr->frame_ctl |=
+                       __constant_cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+       }
+
+       if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) {
+               /* packet was already buffered and now send due to
+                * PS poll, so do not rebuffer it */
+               goto out;
+       }
+
+       if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) {
+               PDEBUG(DEBUG_PS, "%s: No more space in STA (" MACSTR ")'s PS "
+                      "mode buffer\n", local->dev->name, MAC2STR(sta->addr));
+               /* Make sure that TIM is set for the station (it might not be
+                * after AP wlan hw reset). */
+               /* FIX: should fix hw reset to restore bits based on STA
+                * buffer state.. */
+               hostap_set_tim(local, sta->aid, 1);
+               sta->flags |= WLAN_STA_TIM;
+               ret = AP_TX_DROP;
+               goto out;
+       }
+
+       /* STA in PS mode, buffer frame for later delivery */
+       set_tim = skb_queue_empty(&sta->tx_buf);
+       skb_queue_tail(&sta->tx_buf, skb);
+       /* FIX: could save RX time to skb and expire buffered frames after
+        * some time if STA does not poll for them */
+
+       if (set_tim) {
+               if (sta->flags & WLAN_STA_TIM)
+                       PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n",
+                              sta->aid);
+               hostap_set_tim(local, sta->aid, 1);
+               sta->flags |= WLAN_STA_TIM;
+       }
+
+       ret = AP_TX_BUFFERED;
+
+ out:
+       if (sta != NULL) {
+               if (ret == AP_TX_CONTINUE ||
+                   ret == AP_TX_CONTINUE_NOT_AUTHORIZED) {
+                       sta->tx_packets++;
+                       sta->tx_bytes += skb->len;
+                       sta->last_tx = jiffies;
+               }
+
+               if ((ret == AP_TX_CONTINUE ||
+                    ret == AP_TX_CONTINUE_NOT_AUTHORIZED) &&
+                   sta->crypt && tx->host_encrypt) {
+                       tx->crypt = sta->crypt;
+                       tx->sta_ptr = sta; /* hostap_handle_sta_release() will
+                                           * be called to release sta info
+                                           * later */
+               } else
+                       atomic_dec(&sta->users);
+       }
+
+       return ret;
+}
+
+
+void hostap_handle_sta_release(void *ptr)
+{
+       struct sta_info *sta = ptr;
+       atomic_dec(&sta->users);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
+{
+       struct sta_info *sta;
+       struct ieee80211_hdr *hdr;
+       struct hostap_skb_tx_data *meta;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr1);
+       if (!sta) {
+               spin_unlock(&local->ap->sta_table_lock);
+               PDEBUG(DEBUG_AP, "%s: Could not find STA " MACSTR " for this "
+                      "TX error (@%lu)\n",
+                      local->dev->name, MAC2STR(hdr->addr1), jiffies);
+               return;
+       }
+
+       sta->tx_since_last_failure = 0;
+       sta->tx_consecutive_exc++;
+
+       if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD &&
+           sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) {
+               /* use next lower rate */
+               int old, rate;
+               old = rate = sta->tx_rate_idx;
+               while (rate > 0) {
+                       rate--;
+                       if (ap_tx_rate_ok(rate, sta, local)) {
+                               sta->tx_rate_idx = rate;
+                               break;
+                       }
+               }
+               if (old != sta->tx_rate_idx) {
+                       switch (sta->tx_rate_idx) {
+                       case 0: sta->tx_rate = 10; break;
+                       case 1: sta->tx_rate = 20; break;
+                       case 2: sta->tx_rate = 55; break;
+                       case 3: sta->tx_rate = 110; break;
+                       default: sta->tx_rate = 0; break;
+                       }
+                       PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate lowered "
+                              "to %d\n", local->dev->name, MAC2STR(sta->addr),
+                              sta->tx_rate);
+               }
+               sta->tx_consecutive_exc = 0;
+       }
+       spin_unlock(&local->ap->sta_table_lock);
+}
+
+
+static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta,
+                                 int pwrmgt, int type, int stype)
+{
+       if (pwrmgt && !(sta->flags & WLAN_STA_PS)) {
+               sta->flags |= WLAN_STA_PS;
+               PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to use PS "
+                      "mode (type=0x%02X, stype=0x%02X)\n",
+                      MAC2STR(sta->addr), type >> 2, stype >> 4);
+       } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) {
+               sta->flags &= ~WLAN_STA_PS;
+               PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to not use "
+                      "PS mode (type=0x%02X, stype=0x%02X)\n",
+                      MAC2STR(sta->addr), type >> 2, stype >> 4);
+               if (type != IEEE80211_FTYPE_CTL ||
+                   stype != IEEE80211_STYPE_PSPOLL)
+                       schedule_packet_send(local, sta);
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ). Called for each RX frame to update
+ * STA power saving state. pwrmgt is a flag from 802.11 frame_ctl field. */
+int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr)
+{
+       struct sta_info *sta;
+       u16 fc;
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (!sta)
+               return -1;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
+                             WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc));
+
+       atomic_dec(&sta->users);
+       return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ). Called for each RX frame after
+ * getting RX header and payload from hardware. */
+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 ret;
+       struct sta_info *sta;
+       u16 fc, type, stype;
+       struct ieee80211_hdr *hdr;
+
+       if (local->ap == NULL)
+               return AP_RX_CONTINUE;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (sta && !(sta->flags & WLAN_STA_AUTHORIZED))
+               ret = AP_RX_CONTINUE_NOT_AUTHORIZED;
+       else
+               ret = AP_RX_CONTINUE;
+
+
+       if (fc & IEEE80211_FCTL_TODS) {
+               if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+                       if (local->hostapd) {
+                               prism2_rx_80211(local->apdev, skb, rx_stats,
+                                               PRISM2_RX_NON_ASSOC);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+                       } else {
+                               printk(KERN_DEBUG "%s: dropped received packet"
+                                      " from non-associated STA " MACSTR
+                                      " (type=0x%02x, subtype=0x%02x)\n",
+                                      dev->name, MAC2STR(hdr->addr2),
+                                      type >> 2, stype >> 4);
+                               hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+                       }
+                       ret = AP_RX_EXIT;
+                       goto out;
+               }
+       } else if (fc & IEEE80211_FCTL_FROMDS) {
+               if (!wds) {
+                       /* FromDS frame - not for us; probably
+                        * broadcast/multicast in another BSS - drop */
+                       if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+                               printk(KERN_DEBUG "Odd.. FromDS packet "
+                                      "received with own BSSID\n");
+                               hostap_dump_rx_80211(dev->name, skb, rx_stats);
+                       }
+                       ret = AP_RX_DROP;
+                       goto out;
+               }
+       } else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL &&
+                  memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+
+               if (local->hostapd) {
+                       prism2_rx_80211(local->apdev, skb, rx_stats,
+                                       PRISM2_RX_NON_ASSOC);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+               } else {
+                       /* At least Lucent f/w seems to send data::nullfunc
+                        * frames with no ToDS flag when the current AP returns
+                        * after being unavailable for some time. Speed up
+                        * re-association by informing the station about it not
+                        * being associated. */
+                       printk(KERN_DEBUG "%s: rejected received nullfunc "
+                              "frame without ToDS from not associated STA "
+                              MACSTR "\n",
+                              dev->name, MAC2STR(hdr->addr2));
+                       hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+               }
+               ret = AP_RX_EXIT;
+               goto out;
+       } else if (stype == IEEE80211_STYPE_NULLFUNC) {
+               /* At least Lucent cards seem to send periodic nullfunc
+                * frames with ToDS. Let these through to update SQ
+                * stats and PS state. Nullfunc frames do not contain
+                * any data and they will be dropped below. */
+       } else {
+               /* If BSSID (Addr3) is foreign, this frame is a normal
+                * broadcast frame from an IBSS network. Drop it silently.
+                * If BSSID is own, report the dropping of this frame. */
+               if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+                       printk(KERN_DEBUG "%s: dropped received packet from "
+                              MACSTR " with no ToDS flag (type=0x%02x, "
+                              "subtype=0x%02x)\n", dev->name,
+                              MAC2STR(hdr->addr2), type >> 2, stype >> 4);
+                       hostap_dump_rx_80211(dev->name, skb, rx_stats);
+               }
+               ret = AP_RX_DROP;
+               goto out;
+       }
+
+       if (sta) {
+               hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
+                                     type, stype);
+
+               sta->rx_packets++;
+               sta->rx_bytes += skb->len;
+               sta->last_rx = jiffies;
+       }
+
+       if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC &&
+           fc & IEEE80211_FCTL_TODS) {
+               if (local->hostapd) {
+                       prism2_rx_80211(local->apdev, skb, rx_stats,
+                                       PRISM2_RX_NULLFUNC_ACK);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+               } else {
+                       /* some STA f/w's seem to require control::ACK frame
+                        * for data::nullfunc, but Prism2 f/w 0.8.0 (at least
+                        * from Compaq) does not send this.. Try to generate
+                        * ACK for these frames from the host driver to make
+                        * power saving work with, e.g., Lucent WaveLAN f/w */
+                       hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+               }
+               ret = AP_RX_EXIT;
+               goto out;
+       }
+
+ out:
+       if (sta)
+               atomic_dec(&sta->users);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_handle_sta_crypto(local_info_t *local,
+                            struct ieee80211_hdr *hdr,
+                            struct ieee80211_crypt_data **crypt,
+                            void **sta_ptr)
+{
+       struct sta_info *sta;
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (!sta)
+               return -1;
+
+       if (sta->crypt) {
+               *crypt = sta->crypt;
+               *sta_ptr = sta;
+               /* hostap_handle_sta_release() will be called to release STA
+                * info */
+       } else
+               atomic_dec(&sta->users);
+
+       return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr)
+{
+       struct sta_info *sta;
+       int ret = 0;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, sta_addr);
+       if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+               ret = 1;
+       spin_unlock(&ap->sta_table_lock);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr)
+{
+       struct sta_info *sta;
+       int ret = 0;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, sta_addr);
+       if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap &&
+           ((sta->flags & WLAN_STA_AUTHORIZED) ||
+            ap->local->ieee_802_1x == 0))
+               ret = 1;
+       spin_unlock(&ap->sta_table_lock);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_add_sta(struct ap_data *ap, u8 *sta_addr)
+{
+       struct sta_info *sta;
+       int ret = 1;
+
+       if (!ap)
+               return -1;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, sta_addr);
+       if (sta)
+               ret = 0;
+       spin_unlock(&ap->sta_table_lock);
+
+       if (ret == 1) {
+               sta = ap_add_sta(ap, sta_addr);
+               if (!sta)
+                       ret = -1;
+               sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+               sta->ap = 1;
+               memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+               /* No way of knowing which rates are supported since we did not
+                * get supported rates element from beacon/assoc req. Assume
+                * that remote end supports all 802.11b rates. */
+               sta->supported_rates[0] = 0x82;
+               sta->supported_rates[1] = 0x84;
+               sta->supported_rates[2] = 0x0b;
+               sta->supported_rates[3] = 0x16;
+               sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M |
+                       WLAN_RATE_5M5 | WLAN_RATE_11M;
+               sta->tx_rate = 110;
+               sta->tx_max_rate = sta->tx_rate_idx = 3;
+       }
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_update_rx_stats(struct ap_data *ap,
+                          struct ieee80211_hdr *hdr,
+                          struct hostap_80211_rx_status *rx_stats)
+{
+       struct sta_info *sta;
+
+       if (!ap)
+               return -1;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, hdr->addr2);
+       if (sta) {
+               sta->last_rx_silence = rx_stats->noise;
+               sta->last_rx_signal = rx_stats->signal;
+               sta->last_rx_rate = rx_stats->rate;
+               sta->last_rx_updated = 7;
+               if (rx_stats->rate == 10)
+                       sta->rx_count[0]++;
+               else if (rx_stats->rate == 20)
+                       sta->rx_count[1]++;
+               else if (rx_stats->rate == 55)
+                       sta->rx_count[2]++;
+               else if (rx_stats->rate == 110)
+                       sta->rx_count[3]++;
+       }
+       spin_unlock(&ap->sta_table_lock);
+
+       return sta ? 0 : -1;
+}
+
+
+void hostap_update_rates(local_info_t *local)
+{
+       struct list_head *ptr;
+       struct ap_data *ap = local->ap;
+
+       if (!ap)
+               return;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+               prism2_check_tx_rates(sta);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+}
+
+
+static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
+                               struct ieee80211_crypt_data ***crypt)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, addr);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta && permanent)
+               sta = ap_add_sta(ap, addr);
+
+       if (!sta)
+               return NULL;
+
+       if (permanent)
+               sta->flags |= WLAN_STA_PERM;
+
+       *crypt = &sta->crypt;
+
+       return sta;
+}
+
+
+void hostap_add_wds_links(local_info_t *local)
+{
+       struct ap_data *ap = local->ap;
+       struct list_head *ptr;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       list_for_each(ptr, &ap->sta_list) {
+               struct sta_info *sta = list_entry(ptr, struct sta_info, list);
+               if (sta->ap)
+                       hostap_wds_link_oper(local, sta->addr, WDS_ADD);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       schedule_work(&local->ap->wds_oper_queue);
+}
+
+
+void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type)
+{
+       struct wds_oper_data *entry;
+
+       entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+       if (!entry)
+               return;
+       memcpy(entry->addr, addr, ETH_ALEN);
+       entry->type = type;
+       spin_lock_bh(&local->lock);
+       entry->next = local->ap->wds_oper_entries;
+       local->ap->wds_oper_entries = entry;
+       spin_unlock_bh(&local->lock);
+
+       schedule_work(&local->ap->wds_oper_queue);
+}
+
+
+EXPORT_SYMBOL(hostap_init_data);
+EXPORT_SYMBOL(hostap_init_ap_proc);
+EXPORT_SYMBOL(hostap_free_data);
+EXPORT_SYMBOL(hostap_check_sta_fw_version);
+EXPORT_SYMBOL(hostap_handle_sta_tx);
+EXPORT_SYMBOL(hostap_handle_sta_release);
+EXPORT_SYMBOL(hostap_handle_sta_tx_exc);
+EXPORT_SYMBOL(hostap_update_sta_ps);
+EXPORT_SYMBOL(hostap_handle_sta_rx);
+EXPORT_SYMBOL(hostap_is_sta_assoc);
+EXPORT_SYMBOL(hostap_is_sta_authorized);
+EXPORT_SYMBOL(hostap_add_sta);
+EXPORT_SYMBOL(hostap_update_rates);
+EXPORT_SYMBOL(hostap_add_wds_links);
+EXPORT_SYMBOL(hostap_wds_link_oper);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+EXPORT_SYMBOL(hostap_deauth_all_stas);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
diff --git a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/hostap/hostap_ap.h
new file mode 100644 (file)
index 0000000..816a52b
--- /dev/null
@@ -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 */
diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h
new file mode 100644 (file)
index 0000000..6f4fa9d
--- /dev/null
@@ -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 */
diff --git a/drivers/net/wireless/hostap/hostap_config.h b/drivers/net/wireless/hostap/hostap_config.h
new file mode 100644 (file)
index 0000000..7ed3425
--- /dev/null
@@ -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 */
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
new file mode 100644 (file)
index 0000000..faa83ba
--- /dev/null
@@ -0,0 +1,1030 @@
+#define PRISM2_PCCARD
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <asm/io.h>
+
+#include "hostap_wlan.h"
+
+
+static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static dev_info_t dev_info = "hostap_cs";
+static dev_link_t *dev_list = NULL;
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
+                  "cards (PC Card).");
+MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+
+static int ignore_cis_vcc;
+module_param(ignore_cis_vcc, int, 0444);
+MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
+
+
+/* struct local_info::hw_priv */
+struct hostap_cs_priv {
+       dev_node_t node;
+       dev_link_t *link;
+       int sandisk_connectplus;
+};
+
+
+#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_detach(dev_link_t *link);
+static void prism2_release(u_long arg);
+static int prism2_event(event_t event, int priority,
+                       event_callback_args_t *args);
+
+
+static int prism2_pccard_card_present(local_info_t *local)
+{
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+       if (hw_priv != NULL && hw_priv->link != NULL &&
+           ((hw_priv->link->state & (DEV_PRESENT | DEV_CONFIG)) ==
+            (DEV_PRESENT | DEV_CONFIG)))
+               return 1;
+       return 0;
+}
+
+
+/*
+ * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0
+ * Document No. 20-10-00058, January 2004
+ * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf
+ */
+#define SANDISK_WLAN_ACTIVATION_OFF 0x40
+#define SANDISK_HCR_OFF 0x42
+
+
+static void sandisk_set_iobase(local_info_t *local)
+{
+       int res;
+       conf_reg_t reg;
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = 0x10; /* 0x3f0 IO base 1 */
+       reg.Value = hw_priv->link->io.BasePort1 & 0x00ff;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
+                      " res=%d\n", res);
+       }
+       udelay(10);
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = 0x12; /* 0x3f2 IO base 2 */
+       reg.Value = (hw_priv->link->io.BasePort1 & 0xff00) >> 8;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
+                      " res=%d\n", res);
+       }
+}
+
+
+static void sandisk_write_hcr(local_info_t *local, int hcr)
+{
+       struct net_device *dev = local->dev;
+       int i;
+
+       HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF);
+       udelay(50);
+       for (i = 0; i < 10; i++) {
+               HFA384X_OUTB(hcr, SANDISK_HCR_OFF);
+       }
+       udelay(55);
+       HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF);
+}
+
+
+static int sandisk_enable_wireless(struct net_device *dev)
+{
+       int res, ret = 0;
+       conf_reg_t reg;
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+       tuple_t tuple;
+       cisparse_t *parse = NULL;
+       u_char buf[64];
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       if (hw_priv->link->io.NumPorts1 < 0x42) {
+               /* Not enough ports to be SanDisk multi-function card */
+               ret = -ENODEV;
+               goto done;
+       }
+
+       parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
+       if (parse == NULL) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       tuple.DesiredTuple = CISTPL_MANFID;
+       tuple.Attributes = TUPLE_RETURN_COMMON;
+       tuple.TupleData = buf;
+       tuple.TupleDataMax = sizeof(buf);
+       tuple.TupleOffset = 0;
+       if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
+           pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
+           pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+           parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) {
+               /* No SanDisk manfid found */
+               ret = -ENODEV;
+               goto done;
+       }
+
+       tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
+       if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
+           pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
+           pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+               parse->longlink_mfc.nfn < 2) {
+               /* No multi-function links found */
+               ret = -ENODEV;
+               goto done;
+       }
+
+       printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected"
+              " - using vendor-specific initialization\n", dev->name);
+       hw_priv->sandisk_connectplus = 1;
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_COR;
+       reg.Value = COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+                      dev->name, res);
+               goto done;
+       }
+       mdelay(5);
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_COR;
+       /*
+        * Do not enable interrupts here to avoid some bogus events. Interrupts
+        * will be enabled during the first cor_sreset call.
+        */
+       reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+                      dev->name, res);
+               goto done;
+       }
+       mdelay(5);
+
+       sandisk_set_iobase(local);
+
+       HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF);
+       udelay(10);
+       HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF);
+       udelay(10);
+
+done:
+       kfree(parse);
+       return ret;
+}
+
+
+static void prism2_pccard_cor_sreset(local_info_t *local)
+{
+       int res;
+       conf_reg_t reg;
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       if (!prism2_pccard_card_present(local))
+              return;
+
+       reg.Function = 0;
+       reg.Action = CS_READ;
+       reg.Offset = CISREG_COR;
+       reg.Value = 0;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
+                      res);
+               return;
+       }
+       printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n",
+              reg.Value);
+
+       reg.Action = CS_WRITE;
+       reg.Value |= COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
+                      res);
+               return;
+       }
+
+       mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+       reg.Value &= ~COR_SOFT_RESET;
+       if (hw_priv->sandisk_connectplus)
+               reg.Value |= COR_IREQ_ENA;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
+                      res);
+               return;
+       }
+
+       mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+       if (hw_priv->sandisk_connectplus)
+               sandisk_set_iobase(local);
+}
+
+
+static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
+{
+       int res;
+       conf_reg_t reg;
+       int old_cor;
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       if (!prism2_pccard_card_present(local))
+              return;
+
+       if (hw_priv->sandisk_connectplus) {
+               sandisk_write_hcr(local, hcr);
+               return;
+       }
+
+       reg.Function = 0;
+       reg.Action = CS_READ;
+       reg.Offset = CISREG_COR;
+       reg.Value = 0;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 "
+                      "(%d)\n", res);
+               return;
+       }
+       printk(KERN_DEBUG "prism2_pccard_genesis_sreset: original COR %02x\n",
+              reg.Value);
+       old_cor = reg.Value;
+
+       reg.Action = CS_WRITE;
+       reg.Value |= COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 "
+                      "(%d)\n", res);
+               return;
+       }
+
+       mdelay(10);
+
+       /* Setup Genesis mode */
+       reg.Action = CS_WRITE;
+       reg.Value = hcr;
+       reg.Offset = CISREG_CCSR;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 "
+                      "(%d)\n", res);
+               return;
+       }
+       mdelay(10);
+
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_COR;
+       reg.Value = old_cor & ~COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 "
+                      "(%d)\n", res);
+               return;
+       }
+
+       mdelay(10);
+}
+
+
+static int prism2_pccard_dev_open(local_info_t *local)
+{
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+       hw_priv->link->open++;
+       return 0;
+}
+
+
+static int prism2_pccard_dev_close(local_info_t *local)
+{
+       struct hostap_cs_priv *hw_priv;
+
+       if (local == NULL || local->hw_priv == NULL)
+               return 1;
+       hw_priv = local->hw_priv;
+       if (hw_priv->link == NULL)
+               return 1;
+
+       if (!hw_priv->link->open) {
+               printk(KERN_WARNING "%s: prism2_pccard_dev_close(): "
+                      "link not open?!\n", local->dev->name);
+               return 1;
+       }
+
+       hw_priv->link->open--;
+
+       return 0;
+}
+
+
+static struct prism2_helper_functions prism2_pccard_funcs =
+{
+       .card_present   = prism2_pccard_card_present,
+       .cor_sreset     = prism2_pccard_cor_sreset,
+       .dev_open       = prism2_pccard_dev_open,
+       .dev_close      = prism2_pccard_dev_close,
+       .genesis_reset  = prism2_pccard_genesis_reset,
+       .hw_type        = HOSTAP_HW_PCCARD,
+};
+
+
+/* allocate local data and register with CardServices
+ * initialize dev_link structure, but do not configure the card yet */
+static dev_link_t *prism2_attach(void)
+{
+       dev_link_t *link;
+       client_reg_t client_reg;
+       int ret;
+
+       link = kmalloc(sizeof(dev_link_t), GFP_KERNEL);
+       if (link == NULL)
+               return NULL;
+
+       memset(link, 0, sizeof(dev_link_t));
+
+       PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
+       link->conf.Vcc = 33;
+       link->conf.IntType = INT_MEMORY_AND_IO;
+
+       /* register with CardServices */
+       link->next = dev_list;
+       dev_list = link;
+       client_reg.dev_info = &dev_info;
+       client_reg.Version = 0x0210;
+       client_reg.event_callback_args.client_data = link;
+       ret = pcmcia_register_client(&link->handle, &client_reg);
+       if (ret != CS_SUCCESS) {
+               cs_error(link->handle, RegisterClient, ret);
+               prism2_detach(link);
+               return NULL;
+       }
+       return link;
+}
+
+
+static void prism2_detach(dev_link_t *link)
+{
+       dev_link_t **linkp;
+
+       PDEBUG(DEBUG_FLOW, "prism2_detach\n");
+
+       for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+               if (*linkp == link)
+                       break;
+       if (*linkp == NULL) {
+               printk(KERN_WARNING "%s: Attempt to detach non-existing "
+                      "PCMCIA client\n", dev_info);
+               return;
+       }
+
+       if (link->state & DEV_CONFIG) {
+               prism2_release((u_long)link);
+       }
+
+       if (link->handle) {
+               int res = pcmcia_deregister_client(link->handle);
+               if (res) {
+                       printk("CardService(DeregisterClient) => %d\n", res);
+                       cs_error(link->handle, DeregisterClient, res);
+               }
+       }
+
+       *linkp = link->next;
+       /* release net devices */
+       if (link->priv) {
+               struct net_device *dev;
+               struct hostap_interface *iface;
+               dev = link->priv;
+               iface = netdev_priv(dev);
+               kfree(iface->local->hw_priv);
+               iface->local->hw_priv = NULL;
+               prism2_free_local_data(dev);
+       }
+       kfree(link);
+}
+
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+#define CFG_CHECK2(fn, retf) \
+do { int ret = (retf); \
+if (ret != 0) { \
+       PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \
+       cs_error(link->handle, fn, ret); \
+       goto next_entry; \
+} \
+} while (0)
+
+
+/* run after a CARD_INSERTION event is received to configure the PCMCIA
+ * socket and make the device available to the system */
+static int prism2_config(dev_link_t *link)
+{
+       struct net_device *dev;
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 1;
+       tuple_t tuple;
+       cisparse_t *parse;
+       int last_fn, last_ret;
+       u_char buf[64];
+       config_info_t conf;
+       cistpl_cftable_entry_t dflt = { 0 };
+       struct hostap_cs_priv *hw_priv;
+
+       PDEBUG(DEBUG_FLOW, "prism2_config()\n");
+
+       parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
+       hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
+       if (parse == NULL || hw_priv == NULL) {
+               kfree(parse);
+               kfree(hw_priv);
+               ret = -ENOMEM;
+               goto failed;
+       }
+       memset(hw_priv, 0, sizeof(*hw_priv));
+
+       tuple.DesiredTuple = CISTPL_CONFIG;
+       tuple.Attributes = 0;
+       tuple.TupleData = buf;
+       tuple.TupleDataMax = sizeof(buf);
+       tuple.TupleOffset = 0;
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, parse));
+       link->conf.ConfigBase = parse->config.base;
+       link->conf.Present = parse->config.rmask[0];
+
+       CS_CHECK(GetConfigurationInfo,
+                pcmcia_get_configuration_info(link->handle, &conf));
+       PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info,
+              ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc);
+       link->conf.Vcc = conf.Vcc;
+
+       /* Look for an appropriate configuration table entry in the CIS */
+       tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
+       for (;;) {
+               cistpl_cftable_entry_t *cfg = &(parse->cftable_entry);
+               CFG_CHECK2(GetTupleData,
+                          pcmcia_get_tuple_data(link->handle, &tuple));
+               CFG_CHECK2(ParseTuple,
+                          pcmcia_parse_tuple(link->handle, &tuple, parse));
+
+               if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+                       dflt = *cfg;
+               if (cfg->index == 0)
+                       goto next_entry;
+               link->conf.ConfigIndex = cfg->index;
+               PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X "
+                      "(default 0x%02X)\n", cfg->index, dflt.index);
+
+               /* Does this card need audio output? */
+               if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+                       link->conf.Attributes |= CONF_ENABLE_SPKR;
+                       link->conf.Status = CCSR_AUDIO_ENA;
+               }
+
+               /* Use power settings for Vcc and Vpp if present */
+               /*  Note that the CIS values need to be rescaled */
+               if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+                       if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] /
+                           10000 && !ignore_cis_vcc) {
+                               PDEBUG(DEBUG_EXTRA, "  Vcc mismatch - skipping"
+                                      " this entry\n");
+                               goto next_entry;
+                       }
+               } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
+                       if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] /
+                           10000 && !ignore_cis_vcc) {
+                               PDEBUG(DEBUG_EXTRA, "  Vcc (default) mismatch "
+                                      "- skipping this entry\n");
+                               goto next_entry;
+                       }
+               }
+
+               if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+                       link->conf.Vpp1 = link->conf.Vpp2 =
+                               cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+               else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
+                       link->conf.Vpp1 = link->conf.Vpp2 =
+                               dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+               /* Do we need to allocate an interrupt? */
+               if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+                       link->conf.Attributes |= CONF_ENABLE_IRQ;
+               else if (!(link->conf.Attributes & CONF_ENABLE_IRQ)) {
+                       /* At least Compaq WL200 does not have IRQInfo1 set,
+                        * but it does not work without interrupts.. */
+                       printk("Config has no IRQ info, but trying to enable "
+                              "IRQ anyway..\n");
+                       link->conf.Attributes |= CONF_ENABLE_IRQ;
+               }
+
+               /* IO window settings */
+               PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d "
+                      "dflt.io.nwin=%d\n",
+                      cfg->io.nwin, dflt.io.nwin);
+               link->io.NumPorts1 = link->io.NumPorts2 = 0;
+               if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+                       cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+                       link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+                       PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, "
+                              "io.base=0x%04x, len=%d\n", io->flags,
+                              io->win[0].base, io->win[0].len);
+                       if (!(io->flags & CISTPL_IO_8BIT))
+                               link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+                       if (!(io->flags & CISTPL_IO_16BIT))
+                               link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+                       link->io.IOAddrLines = io->flags &
+                               CISTPL_IO_LINES_MASK;
+                       link->io.BasePort1 = io->win[0].base;
+                       link->io.NumPorts1 = io->win[0].len;
+                       if (io->nwin > 1) {
+                               link->io.Attributes2 = link->io.Attributes1;
+                               link->io.BasePort2 = io->win[1].base;
+                               link->io.NumPorts2 = io->win[1].len;
+                       }
+               }
+
+               /* This reserves IO space but doesn't actually enable it */
+               CFG_CHECK2(RequestIO,
+                          pcmcia_request_io(link->handle, &link->io));
+
+               /* This configuration table entry is OK */
+               break;
+
+       next_entry:
+               CS_CHECK(GetNextTuple,
+                        pcmcia_get_next_tuple(link->handle, &tuple));
+       }
+
+       /* Need to allocate net_device before requesting IRQ handler */
+       dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
+                                    &handle_to_dev(link->handle));
+       if (dev == NULL)
+               goto failed;
+       link->priv = dev;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       local->hw_priv = hw_priv;
+       hw_priv->link = link;
+       strcpy(hw_priv->node.dev_name, dev->name);
+       link->dev = &hw_priv->node;
+
+       /*
+        * Allocate an interrupt line.  Note that this does not assign a
+        * handler to the interrupt, unless the 'Handler' member of the
+        * irq structure is initialized.
+        */
+       if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+               link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+               link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+               link->irq.Handler = prism2_interrupt;
+               link->irq.Instance = dev;
+               CS_CHECK(RequestIRQ,
+                        pcmcia_request_irq(link->handle, &link->irq));
+       }
+
+       /*
+        * This actually configures the PCMCIA socket -- setting up
+        * the I/O windows and the interrupt mapping, and putting the
+        * card and host interface into "Memory and IO" mode.
+        */
+       CS_CHECK(RequestConfiguration,
+                pcmcia_request_configuration(link->handle, &link->conf));
+
+       dev->irq = link->irq.AssignedIRQ;
+       dev->base_addr = link->io.BasePort1;
+
+       /* Finally, report what we've done */
+       printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+              dev_info, link->conf.ConfigIndex,
+              link->conf.Vcc / 10, link->conf.Vcc % 10);
+       if (link->conf.Vpp1)
+               printk(", Vpp %d.%d", link->conf.Vpp1 / 10,
+                      link->conf.Vpp1 % 10);
+       if (link->conf.Attributes & CONF_ENABLE_IRQ)
+               printk(", irq %d", link->irq.AssignedIRQ);
+       if (link->io.NumPorts1)
+               printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+                      link->io.BasePort1+link->io.NumPorts1-1);
+       if (link->io.NumPorts2)
+               printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+                      link->io.BasePort2+link->io.NumPorts2-1);
+       printk("\n");
+
+       link->state |= DEV_CONFIG;
+       link->state &= ~DEV_CONFIG_PENDING;
+
+       local->shutdown = 0;
+
+       sandisk_enable_wireless(dev);
+
+       ret = prism2_hw_config(dev, 1);
+       if (!ret) {
+               ret = hostap_hw_ready(dev);
+               if (ret == 0 && local->ddev)
+                       strcpy(hw_priv->node.dev_name, local->ddev->name);
+       }
+       kfree(parse);
+       return ret;
+
+ cs_failed:
+       cs_error(link->handle, last_fn, last_ret);
+
+ failed:
+       kfree(parse);
+       kfree(hw_priv);
+       prism2_release((u_long)link);
+       return ret;
+}
+
+
+static void prism2_release(u_long arg)
+{
+       dev_link_t *link = (dev_link_t *)arg;
+
+       PDEBUG(DEBUG_FLOW, "prism2_release\n");
+
+       if (link->priv) {
+               struct net_device *dev = link->priv;
+               struct hostap_interface *iface;
+
+               iface = netdev_priv(dev);
+               if (link->state & DEV_CONFIG)
+                       prism2_hw_shutdown(dev, 0);
+               iface->local->shutdown = 1;
+       }
+
+       if (link->win)
+               pcmcia_release_window(link->win);
+       pcmcia_release_configuration(link->handle);
+       if (link->io.NumPorts1)
+               pcmcia_release_io(link->handle, &link->io);
+       if (link->irq.AssignedIRQ)
+               pcmcia_release_irq(link->handle, &link->irq);
+
+       link->state &= ~DEV_CONFIG;
+
+       PDEBUG(DEBUG_FLOW, "release - done\n");
+}
+
+
+static int prism2_event(event_t event, int priority,
+                       event_callback_args_t *args)
+{
+       dev_link_t *link = args->client_data;
+       struct net_device *dev = (struct net_device *) link->priv;
+
+       switch (event) {
+       case CS_EVENT_CARD_INSERTION:
+               PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_INSERTION\n", dev_info);
+               link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+               if (prism2_config(link)) {
+                       PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
+               }
+               break;
+
+       case CS_EVENT_CARD_REMOVAL:
+               PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_REMOVAL\n", dev_info);
+               link->state &= ~DEV_PRESENT;
+               if (link->state & DEV_CONFIG) {
+                       netif_stop_queue(dev);
+                       netif_device_detach(dev);
+                       prism2_release((u_long) link);
+               }
+               break;
+
+       case CS_EVENT_PM_SUSPEND:
+               PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
+               link->state |= DEV_SUSPEND;
+               /* fall through */
+
+       case CS_EVENT_RESET_PHYSICAL:
+               PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_RESET_PHYSICAL\n", dev_info);
+               if (link->state & DEV_CONFIG) {
+                       if (link->open) {
+                               netif_stop_queue(dev);
+                               netif_device_detach(dev);
+                       }
+                       prism2_suspend(dev);
+                       pcmcia_release_configuration(link->handle);
+               }
+               break;
+
+       case CS_EVENT_PM_RESUME:
+               PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
+               link->state &= ~DEV_SUSPEND;
+               /* fall through */
+
+       case CS_EVENT_CARD_RESET:
+               PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_RESET\n", dev_info);
+               if (link->state & DEV_CONFIG) {
+                       pcmcia_request_configuration(link->handle,
+                                                    &link->conf);
+                       prism2_hw_shutdown(dev, 1);
+                       prism2_hw_config(dev, link->open ? 0 : 1);
+                       if (link->open) {
+                               netif_device_attach(dev);
+                               netif_start_queue(dev);
+                       }
+               }
+               break;
+
+       default:
+               PDEBUG(DEBUG_EXTRA, "%s: prism2_event() - unknown event %d\n",
+                      dev_info, event);
+               break;
+       }
+       return 0;
+}
+
+
+static struct pcmcia_device_id hostap_cs_ids[] = {
+       PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100),
+       PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300),
+       PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777),
+       PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000),
+       PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002),
+       PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002),
+       PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002),
+       PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b),
+       PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612),
+       PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613),
+       PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002),
+       PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002),
+       PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001),
+       PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001),
+       PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300),
+       PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000),
+       PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002),
+       PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005),
+       PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010),
+       PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus",
+                                   0x7a954bd9, 0x74be00c6),
+       PCMCIA_DEVICE_PROD_ID1234(
+               "Intersil", "PRISM 2_5 PCMCIA ADAPTER", "ISL37300P",
+               "Eval-RevA",
+               0x4b801a17, 0x6345a0bf, 0xc9049a39, 0xc23adc0e),
+       PCMCIA_DEVICE_PROD_ID123(
+               "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02",
+               0xe6ec52ce, 0x08649af2, 0x4b74baa0),
+       PCMCIA_DEVICE_PROD_ID123(
+               "D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02",
+               0x71b18589, 0xb6f1b0ab, 0x4b74baa0),
+       PCMCIA_DEVICE_PROD_ID123(
+               "Instant Wireless ", " Network PC CARD", "Version 01.02",
+               0x11d901af, 0x6e9bd926, 0x4b74baa0),
+       PCMCIA_DEVICE_PROD_ID123(
+               "SMC", "SMC2632W", "Version 01.02",
+               0xc4f8b18b, 0x474a1f2a, 0x4b74baa0),
+       PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 
+                               0x2decece3, 0x82067c18),
+       PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card",
+                               0x54f7c49c, 0x15a75e5b),
+       PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE",
+                               0x74c5e40d, 0xdb472a18),
+       PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card",
+                               0x0733cc81, 0x0c52f395),
+       PCMCIA_DEVICE_PROD_ID12(
+               "ZoomAir 11Mbps High", "Rate wireless Networking",
+               0x273fe3db, 0x32a1eaee),
+       PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
+
+
+static struct pcmcia_driver hostap_driver = {
+       .drv            = {
+               .name   = "hostap_cs",
+       },
+       .attach         = prism2_attach,
+       .detach         = prism2_detach,
+       .owner          = THIS_MODULE,
+       .event          = prism2_event,
+       .id_table       = hostap_cs_ids,
+};
+
+static int __init init_prism2_pccard(void)
+{
+       printk(KERN_INFO "%s: %s\n", dev_info, version);
+       return pcmcia_register_driver(&hostap_driver);
+}
+
+static void __exit exit_prism2_pccard(void)
+{
+       pcmcia_unregister_driver(&hostap_driver);
+       printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
+}
+
+
+module_init(init_prism2_pccard);
+module_exit(exit_prism2_pccard);
diff --git a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/hostap/hostap_download.c
new file mode 100644 (file)
index 0000000..ab26b52
--- /dev/null
@@ -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;
+}
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
new file mode 100644 (file)
index 0000000..e533a66
--- /dev/null
@@ -0,0 +1,3445 @@
+/*
+ * 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-2005, 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.
+ *
+ * FIX:
+ * - there is currently no way of associating TX packets to correct wds device
+ *   when TX Exc/OK event occurs, so all tx_packets and some
+ *   tx_errors/tx_dropped are added to the main netdevice; using sw_support
+ *   field in txdesc might be used to fix this (using Alloc event to increment
+ *   tx_packets would need some further info in txfid table)
+ *
+ * Buffer Access Path (BAP) usage:
+ *   Prism2 cards have two separate BAPs for accessing the card memory. These
+ *   should allow concurrent access to two different frames and the driver
+ *   previously used BAP0 for sending data and BAP1 for receiving data.
+ *   However, there seems to be number of issues with concurrent access and at
+ *   least one know hardware bug in using BAP0 and BAP1 concurrently with PCI
+ *   Prism2.5. Therefore, the driver now only uses BAP0 for moving data between
+ *   host and card memories. BAP0 accesses are protected with local->baplock
+ *   (spin_lock_bh) to prevent concurrent use.
+ */
+
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211_crypt.h>
+#include <asm/irq.h>
+
+#include "hostap_80211.h"
+#include "hostap.h"
+#include "hostap_ap.h"
+
+
+/* #define final_version */
+
+static int mtu = 1500;
+module_param(mtu, int, 0444);
+MODULE_PARM_DESC(mtu, "Maximum transfer unit");
+
+static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS };
+module_param_array(channel, int, NULL, 0444);
+MODULE_PARM_DESC(channel, "Initial channel");
+
+static char essid[33] = "test";
+module_param_string(essid, essid, sizeof(essid), 0444);
+MODULE_PARM_DESC(essid, "Host AP's ESSID");
+
+static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS };
+module_param_array(iw_mode, int, NULL, 0444);
+MODULE_PARM_DESC(iw_mode, "Initial operation mode");
+
+static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS };
+module_param_array(beacon_int, int, NULL, 0444);
+MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)");
+
+static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS };
+module_param_array(dtim_period, int, NULL, 0444);
+MODULE_PARM_DESC(dtim_period, "DTIM period");
+
+static char dev_template[16] = "wlan%d";
+module_param_string(dev_template, dev_template, sizeof(dev_template), 0444);
+MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: "
+                "wlan%d)");
+
+#ifdef final_version
+#define EXTRA_EVENTS_WTERR 0
+#else
+/* check WTERR events (Wait Time-out) in development versions */
+#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR
+#endif
+
+/* Events that will be using BAP0 */
+#define HFA384X_BAP0_EVENTS \
+       (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX)
+
+/* event mask, i.e., events that will result in an interrupt */
+#define HFA384X_EVENT_MASK \
+       (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \
+       HFA384X_EV_CMD | HFA384X_EV_TICK | \
+       EXTRA_EVENTS_WTERR)
+
+/* Default TX control flags: use 802.11 headers and request interrupt for
+ * failed transmits. Frames that request ACK callback, will add
+ * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy.
+ */
+#define HFA384X_TX_CTRL_FLAGS \
+       (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX)
+
+
+/* ca. 1 usec */
+#define HFA384X_CMD_BUSY_TIMEOUT 5000
+#define HFA384X_BAP_BUSY_TIMEOUT 50000
+
+/* ca. 10 usec */
+#define HFA384X_CMD_COMPL_TIMEOUT 20000
+#define HFA384X_DL_COMPL_TIMEOUT 1000000
+
+/* Wait times for initialization; yield to other processes to avoid busy
+ * waiting for long time. */
+#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */
+#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */
+
+
+static void prism2_hw_reset(struct net_device *dev);
+static void prism2_check_sta_fw_version(local_info_t *local);
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+/* hostap_download.c */
+static int prism2_download_aux_dump(struct net_device *dev,
+                                   unsigned int addr, int len, u8 *buf);
+static u8 * prism2_read_pda(struct net_device *dev);
+static int prism2_download(local_info_t *local,
+                          struct prism2_download_param *param);
+static void prism2_download_free_data(struct prism2_download_data *dl);
+static int prism2_download_volatile(local_info_t *local,
+                                   struct prism2_download_data *param);
+static int prism2_download_genesis(local_info_t *local,
+                                  struct prism2_download_data *param);
+static int prism2_get_ram_size(local_info_t *local);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+
+
+
+#ifndef final_version
+/* magic value written to SWSUPPORT0 reg. for detecting whether card is still
+ * present */
+#define HFA384X_MAGIC 0x8A32
+#endif
+
+
+static u16 hfa384x_read_reg(struct net_device *dev, u16 reg)
+{
+       return HFA384X_INW(reg);
+}
+
+
+static void hfa384x_read_regs(struct net_device *dev,
+                             struct hfa384x_regs *regs)
+{
+       regs->cmd = HFA384X_INW(HFA384X_CMD_OFF);
+       regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF);
+       regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF);
+       regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF);
+       regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF);
+}
+
+
+/**
+ * __hostap_cmd_queue_free - Free Prism2 command queue entry (private)
+ * @local: pointer to private Host AP driver data
+ * @entry: Prism2 command queue entry to be freed
+ * @del_req: request the entry to be removed
+ *
+ * Internal helper function for freeing Prism2 command queue entries.
+ * Caller must have acquired local->cmdlock before calling this function.
+ */
+static inline void __hostap_cmd_queue_free(local_info_t *local,
+                                          struct hostap_cmd_queue *entry,
+                                          int del_req)
+{
+       if (del_req) {
+               entry->del_req = 1;
+               if (!list_empty(&entry->list)) {
+                       list_del_init(&entry->list);
+                       local->cmd_queue_len--;
+               }
+       }
+
+       if (atomic_dec_and_test(&entry->usecnt) && entry->del_req)
+               kfree(entry);
+}
+
+
+/**
+ * hostap_cmd_queue_free - Free Prism2 command queue entry
+ * @local: pointer to private Host AP driver data
+ * @entry: Prism2 command queue entry to be freed
+ * @del_req: request the entry to be removed
+ *
+ * Free a Prism2 command queue entry.
+ */
+static inline void hostap_cmd_queue_free(local_info_t *local,
+                                        struct hostap_cmd_queue *entry,
+                                        int del_req)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->cmdlock, flags);
+       __hostap_cmd_queue_free(local, entry, del_req);
+       spin_unlock_irqrestore(&local->cmdlock, flags);
+}
+
+
+/**
+ * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries
+ * @local: pointer to private Host AP driver data
+ */
+static void prism2_clear_cmd_queue(local_info_t *local)
+{
+       struct list_head *ptr, *n;
+       unsigned long flags;
+       struct hostap_cmd_queue *entry;
+
+       spin_lock_irqsave(&local->cmdlock, flags);
+       list_for_each_safe(ptr, n, &local->cmd_queue) {
+               entry = list_entry(ptr, struct hostap_cmd_queue, list);
+               atomic_inc(&entry->usecnt);
+               printk(KERN_DEBUG "%s: removed pending cmd_queue entry "
+                      "(type=%d, cmd=0x%04x, param0=0x%04x)\n",
+                      local->dev->name, entry->type, entry->cmd,
+                      entry->param0);
+               __hostap_cmd_queue_free(local, entry, 1);
+       }
+       if (local->cmd_queue_len) {
+               /* This should not happen; print debug message and clear
+                * queue length. */
+               printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after "
+                      "flush\n", local->dev->name, local->cmd_queue_len);
+               local->cmd_queue_len = 0;
+       }
+       spin_unlock_irqrestore(&local->cmdlock, flags);
+}
+
+
+/**
+ * hfa384x_cmd_issue - Issue a Prism2 command to the hardware
+ * @dev: pointer to net_device
+ * @entry: Prism2 command queue entry to be issued
+ */
+static inline int hfa384x_cmd_issue(struct net_device *dev,
+                                   struct hostap_cmd_queue *entry)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int tries;
+       u16 reg;
+       unsigned long flags;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->card_present && !local->func->card_present(local))
+               return -ENODEV;
+
+       if (entry->issued) {
+               printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n",
+                      dev->name, entry);
+       }
+
+       /* wait until busy bit is clear; this should always be clear since the
+        * commands are serialized */
+       tries = HFA384X_CMD_BUSY_TIMEOUT;
+       while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
+               tries--;
+               udelay(1);
+       }
+#ifndef final_version
+       if (tries != HFA384X_CMD_BUSY_TIMEOUT) {
+               prism2_io_debug_error(dev, 1);
+               printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy "
+                      "for %d usec\n", dev->name,
+                      HFA384X_CMD_BUSY_TIMEOUT - tries);
+       }
+#endif
+       if (tries == 0) {
+               reg = HFA384X_INW(HFA384X_CMD_OFF);
+               prism2_io_debug_error(dev, 2);
+               printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - "
+                      "reg=0x%04x\n", dev->name, reg);
+               return -ETIMEDOUT;
+       }
+
+       /* write command */
+       spin_lock_irqsave(&local->cmdlock, flags);
+       HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF);
+       HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF);
+       HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF);
+       entry->issued = 1;
+       spin_unlock_irqrestore(&local->cmdlock, flags);
+
+       return 0;
+}
+
+
+/**
+ * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ * @param1: value for Param1 register (pointer; %NULL if not used)
+ * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed
+ *
+ * Issue given command (possibly after waiting in command queue) and sleep
+ * until the command is completed (or timed out or interrupted). This can be
+ * called only from user process context.
+ */
+static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0,
+                      u16 *param1, u16 *resp0)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int err, res, issue, issued = 0;
+       unsigned long flags;
+       struct hostap_cmd_queue *entry;
+       DECLARE_WAITQUEUE(wait, current);
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (in_interrupt()) {
+               printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt "
+                      "context\n", dev->name);
+               return -1;
+       }
+
+       if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) {
+               printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n",
+                      dev->name);
+               return -1;
+       }
+
+       if (signal_pending(current))
+               return -EINTR;
+
+       entry = (struct hostap_cmd_queue *)
+               kmalloc(sizeof(*entry), GFP_ATOMIC);
+       if (entry == NULL) {
+               printk(KERN_DEBUG "%s: hfa384x_cmd - kmalloc failed\n",
+                      dev->name);
+               return -ENOMEM;
+       }
+       memset(entry, 0, sizeof(*entry));
+       atomic_set(&entry->usecnt, 1);
+       entry->type = CMD_SLEEP;
+       entry->cmd = cmd;
+       entry->param0 = param0;
+       if (param1)
+               entry->param1 = *param1;
+       init_waitqueue_head(&entry->compl);
+
+       /* prepare to wait for command completion event, but do not sleep yet
+        */
+       add_wait_queue(&entry->compl, &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+
+       spin_lock_irqsave(&local->cmdlock, flags);
+       issue = list_empty(&local->cmd_queue);
+       if (issue)
+               entry->issuing = 1;
+       list_add_tail(&entry->list, &local->cmd_queue);
+       local->cmd_queue_len++;
+       spin_unlock_irqrestore(&local->cmdlock, flags);
+
+       err = 0;
+       if (!issue)
+               goto wait_completion;
+
+       if (signal_pending(current))
+               err = -EINTR;
+
+       if (!err) {
+               if (hfa384x_cmd_issue(dev, entry))
+                       err = -ETIMEDOUT;
+               else
+                       issued = 1;
+       }
+
+ wait_completion:
+       if (!err && entry->type != CMD_COMPLETED) {
+               /* sleep until command is completed or timed out */
+               res = schedule_timeout(2 * HZ);
+       } else
+               res = -1;
+
+       if (!err && signal_pending(current))
+               err = -EINTR;
+
+       if (err && issued) {
+               /* the command was issued, so a CmdCompl event should occur
+                * soon; however, there's a pending signal and
+                * schedule_timeout() would be interrupted; wait a short period
+                * of time to avoid removing entry from the list before
+                * CmdCompl event */
+               udelay(300);
+       }
+
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&entry->compl, &wait);
+
+       /* If entry->list is still in the list, it must be removed
+        * first and in this case prism2_cmd_ev() does not yet have
+        * local reference to it, and the data can be kfree()'d
+        * here. If the command completion event is still generated,
+        * it will be assigned to next (possibly) pending command, but
+        * the driver will reset the card anyway due to timeout
+        *
+        * If the entry is not in the list prism2_cmd_ev() has a local
+        * reference to it, but keeps cmdlock as long as the data is
+        * needed, so the data can be kfree()'d here. */
+
+       /* FIX: if the entry->list is in the list, it has not been completed
+        * yet, so removing it here is somewhat wrong.. this could cause
+        * references to freed memory and next list_del() causing NULL pointer
+        * dereference.. it would probably be better to leave the entry in the
+        * list and the list should be emptied during hw reset */
+
+       spin_lock_irqsave(&local->cmdlock, flags);
+       if (!list_empty(&entry->list)) {
+               printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? "
+                      "(entry=%p, type=%d, res=%d)\n", dev->name, entry,
+                      entry->type, res);
+               list_del_init(&entry->list);
+               local->cmd_queue_len--;
+       }
+       spin_unlock_irqrestore(&local->cmdlock, flags);
+
+       if (err) {
+               printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n",
+                      dev->name, err);
+               res = err;
+               goto done;
+       }
+
+       if (entry->type != CMD_COMPLETED) {
+               u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF);
+               printk(KERN_DEBUG "%s: hfa384x_cmd: command was not "
+                      "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, "
+                      "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name,
+                      res, entry, entry->type, entry->cmd, entry->param0, reg,
+                      HFA384X_INW(HFA384X_INTEN_OFF));
+               if (reg & HFA384X_EV_CMD) {
+                       /* Command completion event is pending, but the
+                        * interrupt was not delivered - probably an issue
+                        * with pcmcia-cs configuration. */
+                       printk(KERN_WARNING "%s: interrupt delivery does not "
+                              "seem to work\n", dev->name);
+               }
+               prism2_io_debug_error(dev, 3);
+               res = -ETIMEDOUT;
+               goto done;
+       }
+
+       if (resp0 != NULL)
+               *resp0 = entry->resp0;
+#ifndef final_version
+       if (entry->res) {
+               printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, "
+                      "resp0=0x%04x\n",
+                      dev->name, cmd, entry->res, entry->resp0);
+       }
+#endif /* final_version */
+
+       res = entry->res;
+ done:
+       hostap_cmd_queue_free(local, entry, 1);
+       return res;
+}
+
+
+/**
+ * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ * @callback: command completion callback function (%NULL = no callback)
+ * @context: context data to be given to the callback function
+ *
+ * Issue given command (possibly after waiting in command queue) and use
+ * callback function to indicate command completion. This can be called both
+ * from user and interrupt context. The callback function will be called in
+ * hardware IRQ context. It can be %NULL, when no function is called when
+ * command is completed.
+ */
+static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0,
+                               void (*callback)(struct net_device *dev,
+                                                long context, u16 resp0,
+                                                u16 status),
+                               long context)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int issue, ret;
+       unsigned long flags;
+       struct hostap_cmd_queue *entry;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) {
+               printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n",
+                      dev->name);
+               return -1;
+       }
+
+       entry = (struct hostap_cmd_queue *)
+               kmalloc(sizeof(*entry), GFP_ATOMIC);
+       if (entry == NULL) {
+               printk(KERN_DEBUG "%s: hfa384x_cmd_callback - kmalloc "
+                      "failed\n", dev->name);
+               return -ENOMEM;
+       }
+       memset(entry, 0, sizeof(*entry));
+       atomic_set(&entry->usecnt, 1);
+       entry->type = CMD_CALLBACK;
+       entry->cmd = cmd;
+       entry->param0 = param0;
+       entry->callback = callback;
+       entry->context = context;
+
+       spin_lock_irqsave(&local->cmdlock, flags);
+       issue = list_empty(&local->cmd_queue);
+       if (issue)
+               entry->issuing = 1;
+       list_add_tail(&entry->list, &local->cmd_queue);
+       local->cmd_queue_len++;
+       spin_unlock_irqrestore(&local->cmdlock, flags);
+
+       if (issue && hfa384x_cmd_issue(dev, entry))
+               ret = -ETIMEDOUT;
+       else
+               ret = 0;
+
+       hostap_cmd_queue_free(local, entry, ret);
+
+       return ret;
+}
+
+
+/**
+ * __hfa384x_cmd_no_wait - Issue a Prism2 command (private)
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ * @io_debug_num: I/O debug error number
+ *
+ * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait().
+ */
+static int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0,
+                                int io_debug_num)
+{
+       int tries;
+       u16 reg;
+
+       /* wait until busy bit is clear; this should always be clear since the
+        * commands are serialized */
+       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);
+               prism2_io_debug_error(dev, io_debug_num);
+               printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - "
+                      "reg=0x%04x\n", dev->name, io_debug_num, reg);
+               return -ETIMEDOUT;
+       }
+
+       /* write command */
+       HFA384X_OUTW(param0, HFA384X_PARAM0_OFF);
+       HFA384X_OUTW(cmd, HFA384X_CMD_OFF);
+
+       return 0;
+}
+
+
+/**
+ * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ */
+static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0)
+{
+       int res, tries;
+       u16 reg;
+
+       res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4);
+       if (res)
+               return res;
+
+        /* wait for command completion */
+       if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD)
+               tries = HFA384X_DL_COMPL_TIMEOUT;
+       else
+               tries = HFA384X_CMD_COMPL_TIMEOUT;
+
+        while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) &&
+               tries > 0) {
+                tries--;
+                udelay(10);
+        }
+        if (tries == 0) {
+                reg = HFA384X_INW(HFA384X_EVSTAT_OFF);
+               prism2_io_debug_error(dev, 5);
+                printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - "
+                      "reg=0x%04x\n", dev->name, reg);
+                return -ETIMEDOUT;
+        }
+
+        res = (HFA384X_INW(HFA384X_STATUS_OFF) &
+               (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) |
+                BIT(8))) >> 8;
+#ifndef final_version
+       if (res) {
+               printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n",
+                      dev->name, cmd, res);
+       }
+#endif
+
+       HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+
+       return res;
+}
+
+
+/**
+ * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ */
+static inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd,
+                                     u16 param0)
+{
+       return __hfa384x_cmd_no_wait(dev, cmd, param0, 6);
+}
+
+
+/**
+ * prism2_cmd_ev - Prism2 command completion event handler
+ * @dev: pointer to net_device
+ *
+ * Interrupt handler for command completion events. Called by the main
+ * interrupt handler in hardware IRQ context. Read Resp0 and status registers
+ * from the hardware and ACK the event. Depending on the issued command type
+ * either wake up the sleeping process that is waiting for command completion
+ * or call the callback function. Issue the next command, if one is pending.
+ */
+static void prism2_cmd_ev(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hostap_cmd_queue *entry = NULL;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       spin_lock(&local->cmdlock);
+       if (!list_empty(&local->cmd_queue)) {
+               entry = list_entry(local->cmd_queue.next,
+                                  struct hostap_cmd_queue, list);
+               atomic_inc(&entry->usecnt);
+               list_del_init(&entry->list);
+               local->cmd_queue_len--;
+
+               if (!entry->issued) {
+                       printk(KERN_DEBUG "%s: Command completion event, but "
+                              "cmd not issued\n", dev->name);
+                       __hostap_cmd_queue_free(local, entry, 1);
+                       entry = NULL;
+               }
+       }
+       spin_unlock(&local->cmdlock);
+
+       if (!entry) {
+               HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+               printk(KERN_DEBUG "%s: Command completion event, but no "
+                      "pending commands\n", dev->name);
+               return;
+       }
+
+       entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF);
+       entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) &
+                     (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) |
+                      BIT(9) | BIT(8))) >> 8;
+       HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+
+       /* TODO: rest of the CmdEv handling could be moved to tasklet */
+       if (entry->type == CMD_SLEEP) {
+               entry->type = CMD_COMPLETED;
+               wake_up_interruptible(&entry->compl);
+       } else if (entry->type == CMD_CALLBACK) {
+               if (entry->callback)
+                       entry->callback(dev, entry->context, entry->resp0,
+                                       entry->res);
+       } else {
+               printk(KERN_DEBUG "%s: Invalid command completion type %d\n",
+                      dev->name, entry->type);
+       }
+       hostap_cmd_queue_free(local, entry, 1);
+
+       /* issue next command, if pending */
+       entry = NULL;
+       spin_lock(&local->cmdlock);
+       if (!list_empty(&local->cmd_queue)) {
+               entry = list_entry(local->cmd_queue.next,
+                                  struct hostap_cmd_queue, list);
+               if (entry->issuing) {
+                       /* hfa384x_cmd() has already started issuing this
+                        * command, so do not start here */
+                       entry = NULL;
+               }
+               if (entry)
+                       atomic_inc(&entry->usecnt);
+       }
+       spin_unlock(&local->cmdlock);
+
+       if (entry) {
+               /* issue next command; if command issuing fails, remove the
+                * entry from cmd_queue */
+               int res = hfa384x_cmd_issue(dev, entry);
+               spin_lock(&local->cmdlock);
+               __hostap_cmd_queue_free(local, entry, res);
+               spin_unlock(&local->cmdlock);
+       }
+}
+
+
+static inline int hfa384x_wait_offset(struct net_device *dev, u16 o_off)
+{
+       int tries = HFA384X_BAP_BUSY_TIMEOUT;
+       int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY;
+
+       while (res && tries > 0) {
+               tries--;
+               udelay(1);
+               res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY;
+       }
+       return res;
+}
+
+
+/* Offset must be even */
+static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id,
+                            int offset)
+{
+       u16 o_off, s_off;
+       int ret = 0;
+
+       if (offset % 2 || bap > 1)
+               return -EINVAL;
+
+       if (bap == BAP1) {
+               o_off = HFA384X_OFFSET1_OFF;
+               s_off = HFA384X_SELECT1_OFF;
+       } else {
+               o_off = HFA384X_OFFSET0_OFF;
+               s_off = HFA384X_SELECT0_OFF;
+       }
+
+       if (hfa384x_wait_offset(dev, o_off)) {
+               prism2_io_debug_error(dev, 7);
+               printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n",
+                      dev->name);
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       HFA384X_OUTW(id, s_off);
+       HFA384X_OUTW(offset, o_off);
+
+       if (hfa384x_wait_offset(dev, o_off)) {
+               prism2_io_debug_error(dev, 8);
+               printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n",
+                      dev->name);
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+#ifndef final_version
+       if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) {
+               prism2_io_debug_error(dev, 9);
+               printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error "
+                      "(%d,0x04%x,%d); reg=0x%04x\n",
+                      dev->name, bap, id, offset, HFA384X_INW(o_off));
+               ret = -EINVAL;
+       }
+#endif
+
+ out:
+       return ret;
+}
+
+
+static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len,
+                          int exact_len)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int res, rlen = 0;
+       struct hfa384x_rid_hdr rec;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->no_pri) {
+               printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI "
+                      "f/w\n", dev->name, rid, len);
+               return -ENOTTY; /* Well.. not really correct, but return
+                                * something unique enough.. */
+       }
+
+       if ((local->func->card_present && !local->func->card_present(local)) ||
+           local->hw_downloading)
+               return -ENODEV;
+
+       res = down_interruptible(&local->rid_bap_sem);
+       if (res)
+               return res;
+
+       res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL);
+       if (res) {
+               printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed "
+                      "(res=%d, rid=%04x, len=%d)\n",
+                      dev->name, res, rid, len);
+               up(&local->rid_bap_sem);
+               return res;
+       }
+
+       spin_lock_bh(&local->baplock);
+
+       res = hfa384x_setup_bap(dev, BAP0, rid, 0);
+       if (!res)
+               res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec));
+
+       if (le16_to_cpu(rec.len) == 0) {
+               /* RID not available */
+               res = -ENODATA;
+       }
+
+       rlen = (le16_to_cpu(rec.len) - 1) * 2;
+       if (!res && exact_len && rlen != len) {
+               printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: "
+                      "rid=0x%04x, len=%d (expected %d)\n",
+                      dev->name, rid, rlen, len);
+               res = -ENODATA;
+       }
+
+       if (!res)
+               res = hfa384x_from_bap(dev, BAP0, buf, len);
+
+       spin_unlock_bh(&local->baplock);
+       up(&local->rid_bap_sem);
+
+       if (res) {
+               if (res != -ENODATA)
+                       printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, "
+                              "len=%d) - failed - res=%d\n", dev->name, rid,
+                              len, res);
+               if (res == -ETIMEDOUT)
+                       prism2_hw_reset(dev);
+               return res;
+       }
+
+       return rlen;
+}
+
+
+static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hfa384x_rid_hdr rec;
+       int res;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->no_pri) {
+               printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI "
+                      "f/w\n", dev->name, rid, len);
+               return -ENOTTY; /* Well.. not really correct, but return
+                                * something unique enough.. */
+       }
+
+       if ((local->func->card_present && !local->func->card_present(local)) ||
+           local->hw_downloading)
+               return -ENODEV;
+
+       rec.rid = cpu_to_le16(rid);
+       /* RID len in words and +1 for rec.rid */
+       rec.len = cpu_to_le16(len / 2 + len % 2 + 1);
+
+       res = down_interruptible(&local->rid_bap_sem);
+       if (res)
+               return res;
+
+       spin_lock_bh(&local->baplock);
+       res = hfa384x_setup_bap(dev, BAP0, rid, 0);
+       if (!res)
+               res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec));
+       if (!res)
+               res = hfa384x_to_bap(dev, BAP0, buf, len);
+       spin_unlock_bh(&local->baplock);
+
+       if (res) {
+               printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - "
+                      "failed - res=%d\n", dev->name, rid, len, res);
+               up(&local->rid_bap_sem);
+               return res;
+       }
+
+       res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL);
+       up(&local->rid_bap_sem);
+       if (res) {
+               printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE "
+                      "failed (res=%d, rid=%04x, len=%d)\n",
+                      dev->name, res, rid, len);
+               return res;
+       }
+
+       if (res == -ETIMEDOUT)
+               prism2_hw_reset(dev);
+
+       return res;
+}
+
+
+static void hfa384x_disable_interrupts(struct net_device *dev)
+{
+       /* disable interrupts and clear event status */
+       HFA384X_OUTW(0, HFA384X_INTEN_OFF);
+       HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+}
+
+
+static void hfa384x_enable_interrupts(struct net_device *dev)
+{
+       /* ack pending events and enable interrupts from selected events */
+       HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+       HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF);
+}
+
+
+static void hfa384x_events_no_bap0(struct net_device *dev)
+{
+       HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS,
+                    HFA384X_INTEN_OFF);
+}
+
+
+static void hfa384x_events_all(struct net_device *dev)
+{
+       HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF);
+}
+
+
+static void hfa384x_events_only_cmd(struct net_device *dev)
+{
+       HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF);
+}
+
+
+static u16 hfa384x_allocate_fid(struct net_device *dev, int len)
+{
+       u16 fid;
+       unsigned long delay;
+
+       /* FIX: this could be replace with hfa384x_cmd() if the Alloc event
+        * below would be handled like CmdCompl event (sleep here, wake up from
+        * interrupt handler */
+       if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) {
+               printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n",
+                      dev->name, len);
+               return 0xffff;
+       }
+
+       delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT;
+       while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) &&
+              time_before(jiffies, delay))
+               yield();
+       if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) {
+               printk("%s: fid allocate, len=%d - timeout\n", dev->name, len);
+               return 0xffff;
+       }
+
+       fid = HFA384X_INW(HFA384X_ALLOCFID_OFF);
+       HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF);
+
+       return fid;
+}
+
+
+static int prism2_reset_port(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int res;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (!local->dev_enabled)
+               return 0;
+
+       res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0,
+                         NULL, NULL);
+       if (res)
+               printk(KERN_DEBUG "%s: reset port failed to disable port\n",
+                      dev->name);
+       else {
+               res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0,
+                                 NULL, NULL);
+               if (res)
+                       printk(KERN_DEBUG "%s: reset port failed to enable "
+                              "port\n", dev->name);
+       }
+
+       /* It looks like at least some STA firmware versions reset
+        * fragmentation threshold back to 2346 after enable command. Restore
+        * the configured value, if it differs from this default. */
+       if (local->fragm_threshold != 2346 &&
+           hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
+                           local->fragm_threshold)) {
+               printk(KERN_DEBUG "%s: failed to restore fragmentation "
+                      "threshold (%d) after Port0 enable\n",
+                      dev->name, local->fragm_threshold);
+       }
+
+       return res;
+}
+
+
+static int prism2_get_version_info(struct net_device *dev, u16 rid,
+                                  const char *txt)
+{
+       struct hfa384x_comp_ident comp;
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->no_pri) {
+               /* PRI f/w not yet available - cannot read RIDs */
+               return -1;
+       }
+       if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) {
+               printk(KERN_DEBUG "Could not get RID for component %s\n", txt);
+               return -1;
+       }
+
+       printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt,
+              __le16_to_cpu(comp.id), __le16_to_cpu(comp.major),
+              __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant));
+       return 0;
+}
+
+
+static int prism2_setup_rids(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 tmp;
+       int ret = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000);
+
+       if (!local->fw_ap) {
+               tmp = hostap_get_porttype(local);
+               ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp);
+               if (ret) {
+                       printk("%s: Port type setting to %d failed\n",
+                              dev->name, tmp);
+                       goto fail;
+               }
+       }
+
+       /* Setting SSID to empty string seems to kill the card in Host AP mode
+        */
+       if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') {
+               ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID,
+                                       local->essid);
+               if (ret) {
+                       printk("%s: AP own SSID setting failed\n", dev->name);
+                       goto fail;
+               }
+       }
+
+       ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN,
+                             PRISM2_DATA_MAXLEN);
+       if (ret) {
+               printk("%s: MAC data length setting to %d failed\n",
+                      dev->name, PRISM2_DATA_MAXLEN);
+               goto fail;
+       }
+
+       if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) {
+               printk("%s: Channel list read failed\n", dev->name);
+               ret = -EINVAL;
+               goto fail;
+       }
+       local->channel_mask = __le16_to_cpu(tmp);
+
+       if (local->channel < 1 || local->channel > 14 ||
+           !(local->channel_mask & (1 << (local->channel - 1)))) {
+               printk(KERN_WARNING "%s: Channel setting out of range "
+                      "(%d)!\n", dev->name, local->channel);
+               ret = -EBUSY;
+               goto fail;
+       }
+
+       ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel);
+       if (ret) {
+               printk("%s: Channel setting to %d failed\n",
+                      dev->name, local->channel);
+               goto fail;
+       }
+
+       ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT,
+                             local->beacon_int);
+       if (ret) {
+               printk("%s: Beacon interval setting to %d failed\n",
+                      dev->name, local->beacon_int);
+               /* this may fail with Symbol/Lucent firmware */
+               if (ret == -ETIMEDOUT)
+                       goto fail;
+       }
+
+       ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD,
+                             local->dtim_period);
+       if (ret) {
+               printk("%s: DTIM period setting to %d failed\n",
+                      dev->name, local->dtim_period);
+               /* this may fail with Symbol/Lucent firmware */
+               if (ret == -ETIMEDOUT)
+                       goto fail;
+       }
+
+       ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE,
+                             local->is_promisc);
+       if (ret)
+               printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n",
+                      dev->name, local->is_promisc);
+
+       if (!local->fw_ap) {
+               ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID,
+                                       local->essid);
+               if (ret) {
+                       printk("%s: Desired SSID setting failed\n", dev->name);
+                       goto fail;
+               }
+       }
+
+       /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and
+        * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic
+        * rates */
+       if (local->tx_rate_control == 0) {
+               local->tx_rate_control =
+                       HFA384X_RATES_1MBPS |
+                       HFA384X_RATES_2MBPS |
+                       HFA384X_RATES_5MBPS |
+                       HFA384X_RATES_11MBPS;
+       }
+       if (local->basic_rates == 0)
+               local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS;
+
+       if (!local->fw_ap) {
+               ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL,
+                                     local->tx_rate_control);
+               if (ret) {
+                       printk("%s: TXRateControl setting to %d failed\n",
+                              dev->name, local->tx_rate_control);
+                       goto fail;
+               }
+
+               ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES,
+                                     local->tx_rate_control);
+               if (ret) {
+                       printk("%s: cnfSupportedRates setting to %d failed\n",
+                              dev->name, local->tx_rate_control);
+               }
+
+               ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES,
+                                     local->basic_rates);
+               if (ret) {
+                       printk("%s: cnfBasicRates setting to %d failed\n",
+                              dev->name, local->basic_rates);
+               }
+
+               ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1);
+               if (ret) {
+                       printk("%s: Create IBSS setting to 1 failed\n",
+                              dev->name);
+               }
+       }
+
+       if (local->name_set)
+               (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME,
+                                        local->name);
+
+       if (hostap_set_encryption(local)) {
+               printk(KERN_INFO "%s: could not configure encryption\n",
+                      dev->name);
+       }
+
+       (void) hostap_set_antsel(local);
+
+       if (hostap_set_roaming(local)) {
+               printk(KERN_INFO "%s: could not set host roaming\n",
+                      dev->name);
+       }
+
+       if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) &&
+           hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec))
+               printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n",
+                      dev->name, local->enh_sec);
+
+       /* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently
+        * not working correctly (last seven counters report bogus values).
+        * This has been fixed in 0.8.2, so enable 32-bit tallies only
+        * beginning with that firmware version. Another bug fix for 32-bit
+        * tallies in 1.4.0; should 16-bit tallies be used for some other
+        * versions, too? */
+       if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) {
+               if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) {
+                       printk(KERN_INFO "%s: cnfThirty2Tally setting "
+                              "failed\n", dev->name);
+                       local->tallies32 = 0;
+               } else
+                       local->tallies32 = 1;
+       } else
+               local->tallies32 = 0;
+
+       hostap_set_auth_algs(local);
+
+       if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
+                           local->fragm_threshold)) {
+               printk(KERN_INFO "%s: setting FragmentationThreshold to %d "
+                      "failed\n", dev->name, local->fragm_threshold);
+       }
+
+       if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD,
+                           local->rts_threshold)) {
+               printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n",
+                      dev->name, local->rts_threshold);
+       }
+
+       if (local->manual_retry_count >= 0 &&
+           hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT,
+                           local->manual_retry_count)) {
+               printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n",
+                      dev->name, local->manual_retry_count);
+       }
+
+       if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) &&
+           hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) {
+               local->rssi_to_dBm = le16_to_cpu(tmp);
+       }
+
+       if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa &&
+           hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) {
+               printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n",
+                      dev->name);
+       }
+
+       if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem &&
+           hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT,
+                           local->generic_elem, local->generic_elem_len)) {
+               printk(KERN_INFO "%s: setting genericElement failed\n",
+                      dev->name);
+       }
+
+ fail:
+       return ret;
+}
+
+
+static int prism2_hw_init(struct net_device *dev, int initial)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret, first = 1;
+       unsigned long start, delay;
+
+       PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n");
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits);
+
+ init:
+       /* initialize HFA 384x */
+       ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0);
+       if (ret) {
+               printk(KERN_INFO "%s: first command failed - assuming card "
+                      "does not have primary firmware\n", dev_info);
+       }
+
+       if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) {
+               /* EvStat has Cmd bit set in some cases, so retry once if no
+                * wait was needed */
+               HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+               printk(KERN_DEBUG "%s: init command completed too quickly - "
+                      "retrying\n", dev->name);
+               first = 0;
+               goto init;
+       }
+
+       start = jiffies;
+       delay = jiffies + HFA384X_INIT_TIMEOUT;
+       while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) &&
+              time_before(jiffies, delay))
+               yield();
+       if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) {
+               printk(KERN_DEBUG "%s: assuming no Primary image in "
+                      "flash - card initialization not completed\n",
+                      dev_info);
+               local->no_pri = 1;
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+                       if (local->sram_type == -1)
+                               local->sram_type = prism2_get_ram_size(local);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+               return 1;
+       }
+       local->no_pri = 0;
+       printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n",
+              (jiffies - start) * 1000 / HZ);
+       HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+       return 0;
+}
+
+
+static int prism2_hw_init2(struct net_device *dev, int initial)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int i;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+       kfree(local->pda);
+       if (local->no_pri)
+               local->pda = NULL;
+       else
+               local->pda = prism2_read_pda(dev);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+       hfa384x_disable_interrupts(dev);
+
+#ifndef final_version
+       HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF);
+       if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) {
+               printk("SWSUPPORT0 write/read failed: %04X != %04X\n",
+                      HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC);
+               goto failed;
+       }
+#endif
+
+       if (initial || local->pri_only) {
+               hfa384x_events_only_cmd(dev);
+               /* get card version information */
+               if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") ||
+                   prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) {
+                       hfa384x_disable_interrupts(dev);
+                       goto failed;
+               }
+
+               if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) {
+                       printk(KERN_DEBUG "%s: Failed to read STA f/w version "
+                              "- only Primary f/w present\n", dev->name);
+                       local->pri_only = 1;
+                       return 0;
+               }
+               local->pri_only = 0;
+               hfa384x_disable_interrupts(dev);
+       }
+
+       /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and
+        * enable interrupts before this. This would also require some sort of
+        * sleeping AllocEv waiting */
+
+       /* allocate TX FIDs */
+       local->txfid_len = PRISM2_TXFID_LEN;
+       for (i = 0; i < PRISM2_TXFID_COUNT; i++) {
+               local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len);
+               if (local->txfid[i] == 0xffff && local->txfid_len > 1600) {
+                       local->txfid[i] = hfa384x_allocate_fid(dev, 1600);
+                       if (local->txfid[i] != 0xffff) {
+                               printk(KERN_DEBUG "%s: Using shorter TX FID "
+                                      "(1600 bytes)\n", dev->name);
+                               local->txfid_len = 1600;
+                       }
+               }
+               if (local->txfid[i] == 0xffff)
+                       goto failed;
+               local->intransmitfid[i] = PRISM2_TXFID_EMPTY;
+       }
+
+       hfa384x_events_only_cmd(dev);
+
+       if (initial) {
+               struct list_head *ptr;
+               prism2_check_sta_fw_version(local);
+
+               if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR,
+                                   &dev->dev_addr, 6, 1) < 0) {
+                       printk("%s: could not get own MAC address\n",
+                              dev->name);
+               }
+               list_for_each(ptr, &local->hostap_interfaces) {
+                       iface = list_entry(ptr, struct hostap_interface, list);
+                       memcpy(iface->dev->dev_addr, dev->dev_addr, ETH_ALEN);
+               }
+       } else if (local->fw_ap)
+               prism2_check_sta_fw_version(local);
+
+       prism2_setup_rids(dev);
+
+       /* MAC is now configured, but port 0 is not yet enabled */
+       return 0;
+
+ failed:
+       if (!local->no_pri)
+               printk(KERN_WARNING "%s: Initialization failed\n", dev_info);
+       return 1;
+}
+
+
+static int prism2_hw_enable(struct net_device *dev, int initial)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int was_resetting;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       was_resetting = local->hw_resetting;
+
+       if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) {
+               printk("%s: MAC port 0 enabling failed\n", dev->name);
+               return 1;
+       }
+
+       local->hw_ready = 1;
+       local->hw_reset_tries = 0;
+       local->hw_resetting = 0;
+       hfa384x_enable_interrupts(dev);
+
+       /* at least D-Link DWL-650 seems to require additional port reset
+        * before it starts acting as an AP, so reset port automatically
+        * here just in case */
+       if (initial && prism2_reset_port(dev)) {
+               printk("%s: MAC port 0 reseting failed\n", dev->name);
+               return 1;
+       }
+
+       if (was_resetting && netif_queue_stopped(dev)) {
+               /* If hw_reset() was called during pending transmit, netif
+                * queue was stopped. Wake it up now since the wlan card has
+                * been resetted. */
+               netif_wake_queue(dev);
+       }
+
+       return 0;
+}
+
+
+static int prism2_hw_config(struct net_device *dev, int initial)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->hw_downloading)
+               return 1;
+
+       if (prism2_hw_init(dev, initial)) {
+               return local->no_pri ? 0 : 1;
+       }
+
+       if (prism2_hw_init2(dev, initial))
+               return 1;
+
+       /* Enable firmware if secondary image is loaded and at least one of the
+        * netdevices is up. */
+       if (!local->pri_only &&
+           (initial == 0 || (initial == 2 && local->num_dev_open > 0))) {
+               if (!local->dev_enabled)
+                       prism2_callback(local, PRISM2_CALLBACK_ENABLE);
+               local->dev_enabled = 1;
+               return prism2_hw_enable(dev, initial);
+       }
+
+       return 0;
+}
+
+
+static void prism2_hw_shutdown(struct net_device *dev, int no_disable)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       /* Allow only command completion events during disable */
+       hfa384x_events_only_cmd(dev);
+
+       local->hw_ready = 0;
+       if (local->dev_enabled)
+               prism2_callback(local, PRISM2_CALLBACK_DISABLE);
+       local->dev_enabled = 0;
+
+       if (local->func->card_present && !local->func->card_present(local)) {
+               printk(KERN_DEBUG "%s: card already removed or not configured "
+                      "during shutdown\n", dev->name);
+               return;
+       }
+
+       if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 &&
+           hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL))
+               printk(KERN_WARNING "%s: Shutdown failed\n", dev_info);
+
+       hfa384x_disable_interrupts(dev);
+
+       if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL)
+               hfa384x_events_only_cmd(dev);
+       else
+               prism2_clear_cmd_queue(local);
+}
+
+
+static void prism2_hw_reset(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+#if 0
+       static long last_reset = 0;
+
+       /* do not reset card more than once per second to avoid ending up in a
+        * busy loop reseting the card */
+       if (time_before_eq(jiffies, last_reset + HZ))
+               return;
+       last_reset = jiffies;
+#endif
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (in_interrupt()) {
+               printk(KERN_DEBUG "%s: driver bug - prism2_hw_reset() called "
+                      "in interrupt context\n", dev->name);
+               return;
+       }
+
+       if (local->hw_downloading)
+               return;
+
+       if (local->hw_resetting) {
+               printk(KERN_WARNING "%s: %s: already resetting card - "
+                      "ignoring reset request\n", dev_info, dev->name);
+               return;
+       }
+
+       local->hw_reset_tries++;
+       if (local->hw_reset_tries > 10) {
+               printk(KERN_WARNING "%s: too many reset tries, skipping\n",
+                      dev->name);
+               return;
+       }
+
+       printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name);
+       hfa384x_disable_interrupts(dev);
+       local->hw_resetting = 1;
+       if (local->func->cor_sreset) {
+               /* Host system seems to hang in some cases with high traffic
+                * load or shared interrupts during COR sreset. Disable shared
+                * interrupts during reset to avoid these crashes. COS sreset
+                * takes quite a long time, so it is unfortunate that this
+                * seems to be needed. Anyway, I do not know of any better way
+                * of avoiding the crash. */
+               disable_irq(dev->irq);
+               local->func->cor_sreset(local);
+               enable_irq(dev->irq);
+       }
+       prism2_hw_shutdown(dev, 1);
+       prism2_hw_config(dev, 0);
+       local->hw_resetting = 0;
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+       if (local->dl_pri) {
+               printk(KERN_DEBUG "%s: persistent download of primary "
+                      "firmware\n", dev->name);
+               if (prism2_download_genesis(local, local->dl_pri) < 0)
+                       printk(KERN_WARNING "%s: download (PRI) failed\n",
+                              dev->name);
+       }
+
+       if (local->dl_sec) {
+               printk(KERN_DEBUG "%s: persistent download of secondary "
+                      "firmware\n", dev->name);
+               if (prism2_download_volatile(local, local->dl_sec) < 0)
+                       printk(KERN_WARNING "%s: download (SEC) failed\n",
+                              dev->name);
+       }
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+       /* TODO: restore beacon TIM bits for STAs that have buffered frames */
+}
+
+
+static void prism2_schedule_reset(local_info_t *local)
+{
+       schedule_work(&local->reset_queue);
+}
+
+
+/* Called only as scheduled task after noticing card timeout in interrupt
+ * context */
+static void handle_reset_queue(void *data)
+{
+       local_info_t *local = (local_info_t *) data;
+
+       printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name);
+       prism2_hw_reset(local->dev);
+
+       if (netif_queue_stopped(local->dev)) {
+               int i;
+
+               for (i = 0; i < PRISM2_TXFID_COUNT; i++)
+                       if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) {
+                               PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: "
+                                      "wake up queue\n");
+                               netif_wake_queue(local->dev);
+                               break;
+                       }
+       }
+}
+
+
+static int prism2_get_txfid_idx(local_info_t *local)
+{
+       int idx, end;
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->txfidlock, flags);
+       end = idx = local->next_txfid;
+       do {
+               if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) {
+                       local->intransmitfid[idx] = PRISM2_TXFID_RESERVED;
+                       spin_unlock_irqrestore(&local->txfidlock, flags);
+                       return idx;
+               }
+               idx++;
+               if (idx >= PRISM2_TXFID_COUNT)
+                       idx = 0;
+       } while (idx != end);
+       spin_unlock_irqrestore(&local->txfidlock, flags);
+
+       PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: "
+              "packet dropped\n");
+       local->stats.tx_dropped++;
+
+       return -1;
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_transmit_cb(struct net_device *dev, long context,
+                              u16 resp0, u16 res)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int idx = (int) context;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (res) {
+               printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n",
+                      dev->name, res);
+               return;
+       }
+
+       if (idx < 0 || idx >= PRISM2_TXFID_COUNT) {
+               printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid "
+                      "idx=%d\n", dev->name, idx);
+               return;
+       }
+
+       if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
+               printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called "
+                      "with no pending transmit\n", dev->name);
+       }
+
+       if (netif_queue_stopped(dev)) {
+               /* ready for next TX, so wake up queue that was stopped in
+                * prism2_transmit() */
+               netif_wake_queue(dev);
+       }
+
+       spin_lock(&local->txfidlock);
+
+       /* With reclaim, Resp0 contains new txfid for transmit; the old txfid
+        * will be automatically allocated for the next TX frame */
+       local->intransmitfid[idx] = resp0;
+
+       PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, "
+              "resp0=0x%04x, transmit_txfid=0x%04x\n",
+              dev->name, idx, local->txfid[idx],
+              resp0, local->intransmitfid[local->next_txfid]);
+
+       idx++;
+       if (idx >= PRISM2_TXFID_COUNT)
+               idx = 0;
+       local->next_txfid = idx;
+
+       /* check if all TX buffers are occupied */
+       do {
+               if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) {
+                       spin_unlock(&local->txfidlock);
+                       return;
+               }
+               idx++;
+               if (idx >= PRISM2_TXFID_COUNT)
+                       idx = 0;
+       } while (idx != local->next_txfid);
+       spin_unlock(&local->txfidlock);
+
+       /* no empty TX buffers, stop queue */
+       netif_stop_queue(dev);
+}
+
+
+/* Called only from software IRQ if PCI bus master is not used (with bus master
+ * this can be called both from software and hardware IRQ) */
+static int prism2_transmit(struct net_device *dev, int idx)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int res;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       /* The driver tries to stop netif queue so that there would not be
+        * more than one attempt to transmit frames going on; check that this
+        * is really the case */
+
+       if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
+               printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called "
+                      "when previous TX was pending\n", dev->name);
+               return -1;
+       }
+
+       /* stop the queue for the time that transmit is pending */
+       netif_stop_queue(dev);
+
+       /* transmit packet */
+       res = hfa384x_cmd_callback(
+               dev,
+               HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM,
+               local->txfid[idx],
+               prism2_transmit_cb, (long) idx);
+
+       if (res) {
+               struct net_device_stats *stats;
+               printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT "
+                      "failed (res=%d)\n", dev->name, res);
+               stats = hostap_get_stats(dev);
+               stats->tx_dropped++;
+               netif_wake_queue(dev);
+               return -1;
+       }
+       dev->trans_start = jiffies;
+
+       /* Since we did not wait for command completion, the card continues
+        * to process on the background and we will finish handling when
+        * command completion event is handled (prism2_cmd_ev() function) */
+
+       return 0;
+}
+
+
+/* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and
+ * send the payload with this descriptor) */
+/* Called only from software IRQ */
+static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hfa384x_tx_frame txdesc;
+       struct hostap_skb_tx_data *meta;
+       int hdr_len, data_len, idx, res, ret = -1;
+       u16 tx_control, fc;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+
+       prism2_callback(local, PRISM2_CALLBACK_TX_START);
+
+       if ((local->func->card_present && !local->func->card_present(local)) ||
+           !local->hw_ready || local->hw_downloading || local->pri_only) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -"
+                              " skipping\n", dev->name);
+               }
+               goto fail;
+       }
+
+       memset(&txdesc, 0, sizeof(txdesc));
+
+       /* skb->data starts with txdesc->frame_control */
+       hdr_len = 24;
+       memcpy(&txdesc.frame_control, skb->data, hdr_len);
+       fc = le16_to_cpu(txdesc.frame_control);
+       if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+           (fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) &&
+           skb->len >= 30) {
+               /* Addr4 */
+               memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN);
+               hdr_len += ETH_ALEN;
+       }
+
+       tx_control = local->tx_control;
+       if (meta->tx_cb_idx) {
+               tx_control |= HFA384X_TX_CTRL_TX_OK;
+               txdesc.sw_support = cpu_to_le16(meta->tx_cb_idx);
+       }
+       txdesc.tx_control = cpu_to_le16(tx_control);
+       txdesc.tx_rate = meta->rate;
+
+       data_len = skb->len - hdr_len;
+       txdesc.data_len = cpu_to_le16(data_len);
+       txdesc.len = cpu_to_be16(data_len);
+
+       idx = prism2_get_txfid_idx(local);
+       if (idx < 0)
+               goto fail;
+
+       if (local->frame_dump & PRISM2_DUMP_TX_HDR)
+               hostap_dump_tx_header(dev->name, &txdesc);
+
+       spin_lock(&local->baplock);
+       res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0);
+
+       if (!res)
+               res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc));
+       if (!res)
+               res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len,
+                                    skb->len - hdr_len);
+       spin_unlock(&local->baplock);
+
+       if (!res)
+               res = prism2_transmit(dev, idx);
+       if (res) {
+               printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n",
+                      dev->name);
+               local->intransmitfid[idx] = PRISM2_TXFID_EMPTY;
+               schedule_work(&local->reset_queue);
+               goto fail;
+       }
+
+       ret = 0;
+
+fail:
+       prism2_callback(local, PRISM2_CALLBACK_TX_END);
+       return ret;
+}
+
+
+/* Some SMP systems have reported number of odd errors with hostap_pci. fid
+ * register has changed values between consecutive reads for an unknown reason.
+ * This should really not happen, so more debugging is needed. This test
+ * version is a big slower, but it will detect most of such register changes
+ * and will try to get the correct fid eventually. */
+#define EXTRA_FID_READ_TESTS
+
+static inline u16 prism2_read_fid_reg(struct net_device *dev, u16 reg)
+{
+#ifdef EXTRA_FID_READ_TESTS
+       u16 val, val2, val3;
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               val = HFA384X_INW(reg);
+               val2 = HFA384X_INW(reg);
+               val3 = HFA384X_INW(reg);
+
+               if (val == val2 && val == val3)
+                       return val;
+
+               printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):"
+                      " %04x %04x %04x\n",
+                      dev->name, i, reg, val, val2, val3);
+               if ((val == val2 || val == val3) && val != 0)
+                       return val;
+               if (val2 == val3 && val2 != 0)
+                       return val2;
+       }
+       printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg "
+              "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3);
+       return val;
+#else /* EXTRA_FID_READ_TESTS */
+       return HFA384X_INW(reg);
+#endif /* EXTRA_FID_READ_TESTS */
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_rx(local_info_t *local)
+{
+       struct net_device *dev = local->dev;
+       int res, rx_pending = 0;
+       u16 len, hdr_len, rxfid, status, macport;
+       struct net_device_stats *stats;
+       struct hfa384x_rx_frame rxdesc;
+       struct sk_buff *skb = NULL;
+
+       prism2_callback(local, PRISM2_CALLBACK_RX_START);
+       stats = hostap_get_stats(dev);
+
+       rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF);
+#ifndef final_version
+       if (rxfid == 0) {
+               rxfid = HFA384X_INW(HFA384X_RXFID_OFF);
+               printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n",
+                      rxfid);
+               if (rxfid == 0) {
+                       schedule_work(&local->reset_queue);
+                       goto rx_dropped;
+               }
+               /* try to continue with the new rxfid value */
+       }
+#endif
+
+       spin_lock(&local->baplock);
+       res = hfa384x_setup_bap(dev, BAP0, rxfid, 0);
+       if (!res)
+               res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc));
+
+       if (res) {
+               spin_unlock(&local->baplock);
+               printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name,
+                      res);
+               if (res == -ETIMEDOUT) {
+                       schedule_work(&local->reset_queue);
+               }
+               goto rx_dropped;
+       }
+
+       len = le16_to_cpu(rxdesc.data_len);
+       hdr_len = sizeof(rxdesc);
+       status = le16_to_cpu(rxdesc.status);
+       macport = (status >> 8) & 0x07;
+
+       /* Drop frames with too large reported payload length. Monitor mode
+        * seems to sometimes pass frames (e.g., ctrl::ack) with signed and
+        * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for
+        * macport 7 */
+       if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) {
+               if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) {
+                       if (len >= (u16) -14) {
+                               hdr_len -= 65535 - len;
+                               hdr_len--;
+                       }
+                       len = 0;
+               } else {
+                       spin_unlock(&local->baplock);
+                       printk(KERN_DEBUG "%s: Received frame with invalid "
+                              "length 0x%04x\n", dev->name, len);
+                       hostap_dump_rx_header(dev->name, &rxdesc);
+                       goto rx_dropped;
+               }
+       }
+
+       skb = dev_alloc_skb(len + hdr_len);
+       if (!skb) {
+               spin_unlock(&local->baplock);
+               printk(KERN_DEBUG "%s: RX failed to allocate skb\n",
+                      dev->name);
+               goto rx_dropped;
+       }
+       skb->dev = dev;
+       memcpy(skb_put(skb, hdr_len), &rxdesc, hdr_len);
+
+       if (len > 0)
+               res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), len);
+       spin_unlock(&local->baplock);
+       if (res) {
+               printk(KERN_DEBUG "%s: RX failed to read "
+                      "frame data\n", dev->name);
+               goto rx_dropped;
+       }
+
+       skb_queue_tail(&local->rx_list, skb);
+       tasklet_schedule(&local->rx_tasklet);
+
+ rx_exit:
+       prism2_callback(local, PRISM2_CALLBACK_RX_END);
+       if (!rx_pending) {
+               HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF);
+       }
+
+       return;
+
+ rx_dropped:
+       stats->rx_dropped++;
+       if (skb)
+               dev_kfree_skb(skb);
+       goto rx_exit;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb)
+{
+       struct hfa384x_rx_frame *rxdesc;
+       struct net_device *dev = skb->dev;
+       struct hostap_80211_rx_status stats;
+       int hdrlen, rx_hdrlen;
+
+       rx_hdrlen = sizeof(*rxdesc);
+       if (skb->len < sizeof(*rxdesc)) {
+               /* Allow monitor mode to receive shorter frames */
+               if (local->iw_mode == IW_MODE_MONITOR &&
+                   skb->len >= sizeof(*rxdesc) - 30) {
+                       rx_hdrlen = skb->len;
+               } else {
+                       dev_kfree_skb(skb);
+                       return;
+               }
+       }
+
+       rxdesc = (struct hfa384x_rx_frame *) skb->data;
+
+       if (local->frame_dump & PRISM2_DUMP_RX_HDR &&
+           skb->len >= sizeof(*rxdesc))
+               hostap_dump_rx_header(dev->name, rxdesc);
+
+       if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR &&
+           (!local->monitor_allow_fcserr ||
+            local->iw_mode != IW_MODE_MONITOR))
+               goto drop;
+
+       if (skb->len > PRISM2_DATA_MAXLEN) {
+               printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n",
+                      dev->name, skb->len, PRISM2_DATA_MAXLEN);
+               goto drop;
+       }
+
+       stats.mac_time = le32_to_cpu(rxdesc->time);
+       stats.signal = rxdesc->signal - local->rssi_to_dBm;
+       stats.noise = rxdesc->silence - local->rssi_to_dBm;
+       stats.rate = rxdesc->rate;
+
+       /* Convert Prism2 RX structure into IEEE 802.11 header */
+       hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(rxdesc->frame_control));
+       if (hdrlen > rx_hdrlen)
+               hdrlen = rx_hdrlen;
+
+       memmove(skb_pull(skb, rx_hdrlen - hdrlen),
+               &rxdesc->frame_control, hdrlen);
+
+       hostap_80211_rx(dev, skb, &stats);
+       return;
+
+ drop:
+       dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_rx_tasklet(unsigned long data)
+{
+       local_info_t *local = (local_info_t *) data;
+       struct sk_buff *skb;
+
+       while ((skb = skb_dequeue(&local->rx_list)) != NULL)
+               hostap_rx_skb(local, skb);
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_alloc_ev(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int idx;
+       u16 fid;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF);
+
+       PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid);
+
+       spin_lock(&local->txfidlock);
+       idx = local->next_alloc;
+
+       do {
+               if (local->txfid[idx] == fid) {
+                       PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n",
+                              idx);
+
+#ifndef final_version
+                       if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY)
+                               printk("Already released txfid found at idx "
+                                      "%d\n", idx);
+                       if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED)
+                               printk("Already reserved txfid found at idx "
+                                      "%d\n", idx);
+#endif
+                       local->intransmitfid[idx] = PRISM2_TXFID_EMPTY;
+                       idx++;
+                       local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 :
+                               idx;
+
+                       if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) &&
+                           netif_queue_stopped(dev))
+                               netif_wake_queue(dev);
+
+                       spin_unlock(&local->txfidlock);
+                       return;
+               }
+
+               idx++;
+               if (idx >= PRISM2_TXFID_COUNT)
+                       idx = 0;
+       } while (idx != local->next_alloc);
+
+       printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new "
+              "read 0x%04x) for alloc event\n", dev->name, fid,
+              HFA384X_INW(HFA384X_ALLOCFID_OFF));
+       printk(KERN_DEBUG "TXFIDs:");
+       for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++)
+               printk(" %04x[%04x]", local->txfid[idx],
+                      local->intransmitfid[idx]);
+       printk("\n");
+       spin_unlock(&local->txfidlock);
+
+       /* FIX: should probably schedule reset; reference to one txfid was lost
+        * completely.. Bad things will happen if we run out of txfids
+        * Actually, this will cause netdev watchdog to notice TX timeout and
+        * then card reset after all txfids have been leaked. */
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_tx_callback(local_info_t *local,
+                              struct hfa384x_tx_frame *txdesc, int ok,
+                              char *payload)
+{
+       u16 sw_support, hdrlen, len;
+       struct sk_buff *skb;
+       struct hostap_tx_callback_info *cb;
+
+       /* Make sure that frame was from us. */
+       if (memcmp(txdesc->addr2, local->dev->dev_addr, ETH_ALEN)) {
+               printk(KERN_DEBUG "%s: TX callback - foreign frame\n",
+                      local->dev->name);
+               return;
+       }
+
+       sw_support = le16_to_cpu(txdesc->sw_support);
+
+       spin_lock(&local->lock);
+       cb = local->tx_callback;
+       while (cb != NULL && cb->idx != sw_support)
+               cb = cb->next;
+       spin_unlock(&local->lock);
+
+       if (cb == NULL) {
+               printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n",
+                      local->dev->name, sw_support);
+               return;
+       }
+
+       hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(txdesc->frame_control));
+       len = le16_to_cpu(txdesc->data_len);
+       skb = dev_alloc_skb(hdrlen + len);
+       if (skb == NULL) {
+               printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate "
+                      "skb\n", local->dev->name);
+               return;
+       }
+
+       memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen);
+       if (payload)
+               memcpy(skb_put(skb, len), payload, len);
+
+       skb->dev = local->dev;
+       skb->mac.raw = skb->data;
+
+       cb->func(skb, ok, cb->data);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int hostap_tx_compl_read(local_info_t *local, int error,
+                               struct hfa384x_tx_frame *txdesc,
+                               char **payload)
+{
+       u16 fid, len;
+       int res, ret = 0;
+       struct net_device *dev = local->dev;
+
+       fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF);
+
+       PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error);
+
+       spin_lock(&local->baplock);
+       res = hfa384x_setup_bap(dev, BAP0, fid, 0);
+       if (!res)
+               res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc));
+       if (res) {
+               PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not "
+                      "read txdesc\n", dev->name, error, fid);
+               if (res == -ETIMEDOUT) {
+                       schedule_work(&local->reset_queue);
+               }
+               ret = -1;
+               goto fail;
+       }
+       if (txdesc->sw_support) {
+               len = le16_to_cpu(txdesc->data_len);
+               if (len < PRISM2_DATA_MAXLEN) {
+                       *payload = (char *) kmalloc(len, GFP_ATOMIC);
+                       if (*payload == NULL ||
+                           hfa384x_from_bap(dev, BAP0, *payload, len)) {
+                               PDEBUG(DEBUG_EXTRA, "%s: could not read TX "
+                                      "frame payload\n", dev->name);
+                               kfree(*payload);
+                               *payload = NULL;
+                               ret = -1;
+                               goto fail;
+                       }
+               }
+       }
+
+ fail:
+       spin_unlock(&local->baplock);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_tx_ev(local_info_t *local)
+{
+       struct net_device *dev = local->dev;
+       char *payload = NULL;
+       struct hfa384x_tx_frame txdesc;
+
+       if (hostap_tx_compl_read(local, 0, &txdesc, &payload))
+               goto fail;
+
+       if (local->frame_dump & PRISM2_DUMP_TX_HDR) {
+               PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x "
+                      "retry_count=%d tx_rate=%d seq_ctrl=%d "
+                      "duration_id=%d\n",
+                      dev->name, le16_to_cpu(txdesc.status),
+                      txdesc.retry_count, txdesc.tx_rate,
+                      le16_to_cpu(txdesc.seq_ctrl),
+                      le16_to_cpu(txdesc.duration_id));
+       }
+
+       if (txdesc.sw_support)
+               hostap_tx_callback(local, &txdesc, 1, payload);
+       kfree(payload);
+
+ fail:
+       HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_sta_tx_exc_tasklet(unsigned long data)
+{
+       local_info_t *local = (local_info_t *) data;
+       struct sk_buff *skb;
+
+       while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) {
+               struct hfa384x_tx_frame *txdesc =
+                       (struct hfa384x_tx_frame *) skb->data;
+
+               if (skb->len >= sizeof(*txdesc)) {
+                       /* Convert Prism2 RX structure into IEEE 802.11 header
+                        */
+                       u16 fc = le16_to_cpu(txdesc->frame_control);
+                       int hdrlen = hostap_80211_get_hdrlen(fc);
+                       memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen),
+                               &txdesc->frame_control, hdrlen);
+
+                       hostap_handle_sta_tx_exc(local, skb);
+               }
+               dev_kfree_skb(skb);
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_txexc(local_info_t *local)
+{
+       struct net_device *dev = local->dev;
+       u16 status, fc;
+       int show_dump, res;
+       char *payload = NULL;
+       struct hfa384x_tx_frame txdesc;
+
+       show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR;
+       local->stats.tx_errors++;
+
+       res = hostap_tx_compl_read(local, 1, &txdesc, &payload);
+       HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF);
+       if (res)
+               return;
+
+       status = le16_to_cpu(txdesc.status);
+
+       /* We produce a TXDROP event only for retry or lifetime
+        * exceeded, because that's the only status that really mean
+        * that this particular node went away.
+        * Other errors means that *we* screwed up. - Jean II */
+       if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR))
+       {
+               union iwreq_data wrqu;
+
+               /* Copy 802.11 dest address. */
+               memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN);
+               wrqu.addr.sa_family = ARPHRD_ETHER;
+               wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
+       } else
+               show_dump = 1;
+
+       if (local->iw_mode == IW_MODE_MASTER ||
+           local->iw_mode == IW_MODE_REPEAT ||
+           local->wds_type & HOSTAP_WDS_AP_CLIENT) {
+               struct sk_buff *skb;
+               skb = dev_alloc_skb(sizeof(txdesc));
+               if (skb) {
+                       memcpy(skb_put(skb, sizeof(txdesc)), &txdesc,
+                              sizeof(txdesc));
+                       skb_queue_tail(&local->sta_tx_exc_list, skb);
+                       tasklet_schedule(&local->sta_tx_exc_tasklet);
+               }
+       }
+
+       if (txdesc.sw_support)
+               hostap_tx_callback(local, &txdesc, 0, payload);
+       kfree(payload);
+
+       if (!show_dump)
+               return;
+
+       PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)"
+              " tx_control=%04x\n",
+              dev->name, status,
+              status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "",
+              status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "",
+              status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "",
+              status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "",
+              le16_to_cpu(txdesc.tx_control));
+
+       fc = le16_to_cpu(txdesc.frame_control);
+       PDEBUG(DEBUG_EXTRA, "   retry_count=%d tx_rate=%d fc=0x%04x "
+              "(%s%s%s::%d%s%s)\n",
+              txdesc.retry_count, txdesc.tx_rate, fc,
+              WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT ? "Mgmt" : "",
+              WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL ? "Ctrl" : "",
+              WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA ? "Data" : "",
+              WLAN_FC_GET_STYPE(fc) >> 4,
+              fc & IEEE80211_FCTL_TODS ? " ToDS" : "",
+              fc & IEEE80211_FCTL_FROMDS ? " FromDS" : "");
+       PDEBUG(DEBUG_EXTRA, "   A1=" MACSTR " A2=" MACSTR " A3="
+              MACSTR " A4=" MACSTR "\n",
+              MAC2STR(txdesc.addr1), MAC2STR(txdesc.addr2),
+              MAC2STR(txdesc.addr3), MAC2STR(txdesc.addr4));
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_info_tasklet(unsigned long data)
+{
+       local_info_t *local = (local_info_t *) data;
+       struct sk_buff *skb;
+
+       while ((skb = skb_dequeue(&local->info_list)) != NULL) {
+               hostap_info_process(local, skb);
+               dev_kfree_skb(skb);
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info(local_info_t *local)
+{
+       struct net_device *dev = local->dev;
+       u16 fid;
+       int res, left;
+       struct hfa384x_info_frame info;
+       struct sk_buff *skb;
+
+       fid = HFA384X_INW(HFA384X_INFOFID_OFF);
+
+       spin_lock(&local->baplock);
+       res = hfa384x_setup_bap(dev, BAP0, fid, 0);
+       if (!res)
+               res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info));
+       if (res) {
+               spin_unlock(&local->baplock);
+               printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n",
+                      fid);
+               if (res == -ETIMEDOUT) {
+                       schedule_work(&local->reset_queue);
+               }
+               goto out;
+       }
+
+       le16_to_cpus(&info.len);
+       le16_to_cpus(&info.type);
+       left = (info.len - 1) * 2;
+
+       if (info.len & 0x8000 || info.len == 0 || left > 2060) {
+               /* data register seems to give 0x8000 in some error cases even
+                * though busy bit is not set in offset register;
+                * in addition, length must be at least 1 due to type field */
+               spin_unlock(&local->baplock);
+               printk(KERN_DEBUG "%s: Received info frame with invalid "
+                      "length 0x%04x (type 0x%04x)\n", dev->name, info.len,
+                      info.type);
+               goto out;
+       }
+
+       skb = dev_alloc_skb(sizeof(info) + left);
+       if (skb == NULL) {
+               spin_unlock(&local->baplock);
+               printk(KERN_DEBUG "%s: Could not allocate skb for info "
+                      "frame\n", dev->name);
+               goto out;
+       }
+
+       memcpy(skb_put(skb, sizeof(info)), &info, sizeof(info));
+       if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left))
+       {
+               spin_unlock(&local->baplock);
+               printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, "
+                      "len=0x%04x, type=0x%04x\n",
+                      dev->name, fid, info.len, info.type);
+               dev_kfree_skb(skb);
+               goto out;
+       }
+       spin_unlock(&local->baplock);
+
+       skb_queue_tail(&local->info_list, skb);
+       tasklet_schedule(&local->info_tasklet);
+
+ out:
+       HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_bap_tasklet(unsigned long data)
+{
+       local_info_t *local = (local_info_t *) data;
+       struct net_device *dev = local->dev;
+       u16 ev;
+       int frames = 30;
+
+       if (local->func->card_present && !local->func->card_present(local))
+               return;
+
+       set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits);
+
+       /* Process all pending BAP events without generating new interrupts
+        * for them */
+       while (frames-- > 0) {
+               ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
+               if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS))
+                       break;
+               if (ev & HFA384X_EV_RX)
+                       prism2_rx(local);
+               if (ev & HFA384X_EV_INFO)
+                       prism2_info(local);
+               if (ev & HFA384X_EV_TX)
+                       prism2_tx_ev(local);
+               if (ev & HFA384X_EV_TXEXC)
+                       prism2_txexc(local);
+       }
+
+       set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits);
+       clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits);
+
+       /* Enable interrupts for new BAP events */
+       hfa384x_events_all(dev);
+       clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits);
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_infdrop(struct net_device *dev)
+{
+       static unsigned long last_inquire = 0;
+
+       PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name);
+
+       /* some firmware versions seem to get stuck with
+        * full CommTallies in high traffic load cases; every
+        * packet will then cause INFDROP event and CommTallies
+        * info frame will not be sent automatically. Try to
+        * get out of this state by inquiring CommTallies. */
+       if (!last_inquire || time_after(jiffies, last_inquire + HZ)) {
+               hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE,
+                                    HFA384X_INFO_COMMTALLIES, NULL, 0);
+               last_inquire = jiffies;
+       }
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_ev_tick(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 evstat, inten;
+       static int prev_stuck = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (time_after(jiffies, local->last_tick_timer + 5 * HZ) &&
+           local->last_tick_timer) {
+               evstat = HFA384X_INW(HFA384X_EVSTAT_OFF);
+               inten = HFA384X_INW(HFA384X_INTEN_OFF);
+               if (!prev_stuck) {
+                       printk(KERN_INFO "%s: SW TICK stuck? "
+                              "bits=0x%lx EvStat=%04x IntEn=%04x\n",
+                              dev->name, local->bits, evstat, inten);
+               }
+               local->sw_tick_stuck++;
+               if ((evstat & HFA384X_BAP0_EVENTS) &&
+                   (inten & HFA384X_BAP0_EVENTS)) {
+                       printk(KERN_INFO "%s: trying to recover from IRQ "
+                              "hang\n", dev->name);
+                       hfa384x_events_no_bap0(dev);
+               }
+               prev_stuck = 1;
+       } else
+               prev_stuck = 0;
+}
+
+
+/* Called only from hardware IRQ */
+static inline void prism2_check_magic(local_info_t *local)
+{
+       /* at least PCI Prism2.5 with bus mastering seems to sometimes
+        * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the
+        * register once or twice seems to get the correct value.. PCI cards
+        * cannot anyway be removed during normal operation, so there is not
+        * really any need for this verification with them. */
+
+#ifndef PRISM2_PCI
+#ifndef final_version
+       static unsigned long last_magic_err = 0;
+       struct net_device *dev = local->dev;
+
+       if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) {
+               if (!local->hw_ready)
+                       return;
+               HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+               if (time_after(jiffies, last_magic_err + 10 * HZ)) {
+                       printk("%s: Interrupt, but SWSUPPORT0 does not match: "
+                              "%04X != %04X - card removed?\n", dev->name,
+                              HFA384X_INW(HFA384X_SWSUPPORT0_OFF),
+                              HFA384X_MAGIC);
+                       last_magic_err = jiffies;
+               } else if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x "
+                              "MAGIC=%04x\n", dev->name,
+                              HFA384X_INW(HFA384X_SWSUPPORT0_OFF),
+                              HFA384X_MAGIC);
+               }
+               if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != 0xffff)
+                       schedule_work(&local->reset_queue);
+               return;
+       }
+#endif /* final_version */
+#endif /* !PRISM2_PCI */
+}
+
+
+/* Called only from hardware IRQ */
+static irqreturn_t prism2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = (struct net_device *) dev_id;
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int events = 0;
+       u16 ev;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0);
+
+       if (local->func->card_present && !local->func->card_present(local)) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n",
+                              dev->name);
+               }
+               return IRQ_HANDLED;
+       }
+
+       prism2_check_magic(local);
+
+       for (;;) {
+               ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
+               if (ev == 0xffff) {
+                       if (local->shutdown)
+                               return IRQ_HANDLED;
+                       HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+                       printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n",
+                              dev->name);
+                       return IRQ_HANDLED;
+               }
+
+               ev &= HFA384X_INW(HFA384X_INTEN_OFF);
+               if (ev == 0)
+                       break;
+
+               if (ev & HFA384X_EV_CMD) {
+                       prism2_cmd_ev(dev);
+               }
+
+               /* Above events are needed even before hw is ready, but other
+                * events should be skipped during initialization. This may
+                * change for AllocEv if allocate_fid is implemented without
+                * busy waiting. */
+               if (!local->hw_ready || local->hw_resetting ||
+                   !local->dev_enabled) {
+                       ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
+                       if (ev & HFA384X_EV_CMD)
+                               goto next_event;
+                       if ((ev & HFA384X_EVENT_MASK) == 0)
+                               return IRQ_HANDLED;
+                       if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) &&
+                           net_ratelimit()) {
+                               printk(KERN_DEBUG "%s: prism2_interrupt: hw "
+                                      "not ready; skipping events 0x%04x "
+                                      "(IntEn=0x%04x)%s%s%s\n",
+                                      dev->name, ev,
+                                      HFA384X_INW(HFA384X_INTEN_OFF),
+                                      !local->hw_ready ? " (!hw_ready)" : "",
+                                      local->hw_resetting ?
+                                      " (hw_resetting)" : "",
+                                      !local->dev_enabled ?
+                                      " (!dev_enabled)" : "");
+                       }
+                       HFA384X_OUTW(ev, HFA384X_EVACK_OFF);
+                       return IRQ_HANDLED;
+               }
+
+               if (ev & HFA384X_EV_TICK) {
+                       prism2_ev_tick(dev);
+                       HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF);
+               }
+
+               if (ev & HFA384X_EV_ALLOC) {
+                       prism2_alloc_ev(dev);
+                       HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF);
+               }
+
+               /* Reading data from the card is quite time consuming, so do it
+                * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed
+                * and unmasked after needed data has been read completely. */
+               if (ev & HFA384X_BAP0_EVENTS) {
+                       hfa384x_events_no_bap0(dev);
+                       tasklet_schedule(&local->bap_tasklet);
+               }
+
+#ifndef final_version
+               if (ev & HFA384X_EV_WTERR) {
+                       PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name);
+                       HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF);
+               }
+#endif /* final_version */
+
+               if (ev & HFA384X_EV_INFDROP) {
+                       prism2_infdrop(dev);
+                       HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF);
+               }
+
+       next_event:
+               events++;
+               if (events >= PRISM2_MAX_INTERRUPT_EVENTS) {
+                       PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events "
+                              "(EvStat=0x%04x)\n",
+                              PRISM2_MAX_INTERRUPT_EVENTS,
+                              HFA384X_INW(HFA384X_EVSTAT_OFF));
+                       break;
+               }
+       }
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1);
+       return IRQ_RETVAL(events);
+}
+
+
+static void prism2_check_sta_fw_version(local_info_t *local)
+{
+       struct hfa384x_comp_ident comp;
+       int id, variant, major, minor;
+
+       if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID,
+                           &comp, sizeof(comp), 1) < 0)
+               return;
+
+       local->fw_ap = 0;
+       id = le16_to_cpu(comp.id);
+       if (id != HFA384X_COMP_ID_STA) {
+               if (id == HFA384X_COMP_ID_FW_AP)
+                       local->fw_ap = 1;
+               return;
+       }
+
+       major = __le16_to_cpu(comp.major);
+       minor = __le16_to_cpu(comp.minor);
+       variant = __le16_to_cpu(comp.variant);
+       local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant);
+
+       /* Station firmware versions before 1.4.x seem to have a bug in
+        * firmware-based WEP encryption when using Host AP mode, so use
+        * host_encrypt as a default for them. Firmware version 1.4.9 is the
+        * first one that has been seen to produce correct encryption, but the
+        * bug might be fixed before that (although, at least 1.4.2 is broken).
+        */
+       local->fw_encrypt_ok = local->sta_fw_ver >= PRISM2_FW_VER(1,4,9);
+
+       if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt &&
+           !local->fw_encrypt_ok) {
+               printk(KERN_DEBUG "%s: defaulting to host-based encryption as "
+                      "a workaround for firmware bug in Host AP mode WEP\n",
+                      local->dev->name);
+               local->host_encrypt = 1;
+       }
+
+       /* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken
+        * in station firmware versions before 1.5.x. With these versions, the
+        * driver uses a workaround with bogus frame format (4th address after
+        * the payload). This is not compatible with other AP devices. Since
+        * the firmware bug is fixed in the latest station firmware versions,
+        * automatically enable standard compliant mode for cards using station
+        * firmware version 1.5.0 or newer. */
+       if (local->sta_fw_ver >= PRISM2_FW_VER(1,5,0))
+               local->wds_type |= HOSTAP_WDS_STANDARD_FRAME;
+       else {
+               printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a "
+                      "workaround for firmware bug in Host AP mode WDS\n",
+                      local->dev->name);
+       }
+
+       hostap_check_sta_fw_version(local->ap, local->sta_fw_ver);
+}
+
+
+static void prism2_crypt_deinit_entries(local_info_t *local, int force)
+{
+       struct list_head *ptr, *n;
+       struct ieee80211_crypt_data *entry;
+
+       for (ptr = local->crypt_deinit_list.next, n = ptr->next;
+            ptr != &local->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);
+               kfree(entry);
+       }
+}
+
+
+static void prism2_crypt_deinit_handler(unsigned long data)
+{
+       local_info_t *local = (local_info_t *) data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->lock, flags);
+       prism2_crypt_deinit_entries(local, 0);
+       if (!list_empty(&local->crypt_deinit_list)) {
+               printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
+                      "deletion list\n", local->dev->name);
+               local->crypt_deinit_timer.expires = jiffies + HZ;
+               add_timer(&local->crypt_deinit_timer);
+       }
+       spin_unlock_irqrestore(&local->lock, flags);
+
+}
+
+
+static void hostap_passive_scan(unsigned long data)
+{
+       local_info_t *local = (local_info_t *) data;
+       struct net_device *dev = local->dev;
+       u16 channel;
+
+       if (local->passive_scan_interval <= 0)
+               return;
+
+       if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) {
+               int max_tries = 16;
+
+               /* Even though host system does not really know when the WLAN
+                * MAC is sending frames, try to avoid changing channels for
+                * passive scanning when a host-generated frame is being
+                * transmitted */
+               if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
+                       printk(KERN_DEBUG "%s: passive scan detected pending "
+                              "TX - delaying\n", dev->name);
+                       local->passive_scan_timer.expires = jiffies + HZ / 10;
+                       add_timer(&local->passive_scan_timer);
+                       return;
+               }
+
+               do {
+                       local->passive_scan_channel++;
+                       if (local->passive_scan_channel > 14)
+                               local->passive_scan_channel = 1;
+                       max_tries--;
+               } while (!(local->channel_mask &
+                          (1 << (local->passive_scan_channel - 1))) &&
+                        max_tries > 0);
+
+               if (max_tries == 0) {
+                       printk(KERN_INFO "%s: no allowed passive scan channels"
+                              " found\n", dev->name);
+                       return;
+               }
+
+               printk(KERN_DEBUG "%s: passive scan channel %d\n",
+                      dev->name, local->passive_scan_channel);
+               channel = local->passive_scan_channel;
+               local->passive_scan_state = PASSIVE_SCAN_WAIT;
+               local->passive_scan_timer.expires = jiffies + HZ / 10;
+       } else {
+               channel = local->channel;
+               local->passive_scan_state = PASSIVE_SCAN_LISTEN;
+               local->passive_scan_timer.expires = jiffies +
+                       local->passive_scan_interval * HZ;
+       }
+
+       if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST |
+                                (HFA384X_TEST_CHANGE_CHANNEL << 8),
+                                channel, NULL, 0))
+               printk(KERN_ERR "%s: passive scan channel set %d "
+                      "failed\n", dev->name, channel);
+
+       add_timer(&local->passive_scan_timer);
+}
+
+
+/* Called only as a scheduled task when communications quality values should
+ * be updated. */
+static void handle_comms_qual_update(void *data)
+{
+       local_info_t *local = data;
+       prism2_update_comms_qual(local->dev);
+}
+
+
+/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is
+ * used to monitor that local->last_tick_timer is being updated. If not,
+ * interrupt busy-loop is assumed and driver tries to recover by masking out
+ * some events. */
+static void hostap_tick_timer(unsigned long data)
+{
+       static unsigned long last_inquire = 0;
+       local_info_t *local = (local_info_t *) data;
+       local->last_tick_timer = jiffies;
+
+       /* Inquire CommTallies every 10 seconds to keep the statistics updated
+        * more often during low load and when using 32-bit tallies. */
+       if ((!last_inquire || time_after(jiffies, last_inquire + 10 * HZ)) &&
+           !local->hw_downloading && local->hw_ready &&
+           !local->hw_resetting && local->dev_enabled) {
+               hfa384x_cmd_callback(local->dev, HFA384X_CMDCODE_INQUIRE,
+                                    HFA384X_INFO_COMMTALLIES, NULL, 0);
+               last_inquire = jiffies;
+       }
+
+       if ((local->last_comms_qual_update == 0 ||
+            time_after(jiffies, local->last_comms_qual_update + 10 * HZ)) &&
+           (local->iw_mode == IW_MODE_INFRA ||
+            local->iw_mode == IW_MODE_ADHOC)) {
+               schedule_work(&local->comms_qual_update);
+       }
+
+       local->tick_timer.expires = jiffies + 2 * HZ;
+       add_timer(&local->tick_timer);
+}
+
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+static int prism2_registers_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;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+#define SHOW_REG(n) \
+p += sprintf(p, #n "=%04x\n", hfa384x_read_reg(local->dev, HFA384X_##n##_OFF))
+
+       SHOW_REG(CMD);
+       SHOW_REG(PARAM0);
+       SHOW_REG(PARAM1);
+       SHOW_REG(PARAM2);
+       SHOW_REG(STATUS);
+       SHOW_REG(RESP0);
+       SHOW_REG(RESP1);
+       SHOW_REG(RESP2);
+       SHOW_REG(INFOFID);
+       SHOW_REG(CONTROL);
+       SHOW_REG(SELECT0);
+       SHOW_REG(SELECT1);
+       SHOW_REG(OFFSET0);
+       SHOW_REG(OFFSET1);
+       SHOW_REG(RXFID);
+       SHOW_REG(ALLOCFID);
+       SHOW_REG(TXCOMPLFID);
+       SHOW_REG(SWSUPPORT0);
+       SHOW_REG(SWSUPPORT1);
+       SHOW_REG(SWSUPPORT2);
+       SHOW_REG(EVSTAT);
+       SHOW_REG(INTEN);
+       SHOW_REG(EVACK);
+       /* Do not read data registers, because they change the state of the
+        * MAC (offset += 2) */
+       /* SHOW_REG(DATA0); */
+       /* SHOW_REG(DATA1); */
+       SHOW_REG(AUXPAGE);
+       SHOW_REG(AUXOFFSET);
+       /* SHOW_REG(AUXDATA); */
+#ifdef PRISM2_PCI
+       SHOW_REG(PCICOR);
+       SHOW_REG(PCIHCR);
+       SHOW_REG(PCI_M0_ADDRH);
+       SHOW_REG(PCI_M0_ADDRL);
+       SHOW_REG(PCI_M0_LEN);
+       SHOW_REG(PCI_M0_CTL);
+       SHOW_REG(PCI_STATUS);
+       SHOW_REG(PCI_M1_ADDRH);
+       SHOW_REG(PCI_M1_ADDRL);
+       SHOW_REG(PCI_M1_LEN);
+       SHOW_REG(PCI_M1_CTL);
+#endif /* PRISM2_PCI */
+
+       return (p - page);
+}
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+
+struct set_tim_data {
+       struct list_head list;
+       int aid;
+       int set;
+};
+
+static int prism2_set_tim(struct net_device *dev, int aid, int set)
+{
+       struct list_head *ptr;
+       struct set_tim_data *new_entry;
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       new_entry = (struct set_tim_data *)
+               kmalloc(sizeof(*new_entry), GFP_ATOMIC);
+       if (new_entry == NULL) {
+               printk(KERN_DEBUG "%s: prism2_set_tim: kmalloc failed\n",
+                      local->dev->name);
+               return -ENOMEM;
+       }
+       memset(new_entry, 0, sizeof(*new_entry));
+       new_entry->aid = aid;
+       new_entry->set = set;
+
+       spin_lock_bh(&local->set_tim_lock);
+       list_for_each(ptr, &local->set_tim_list) {
+               struct set_tim_data *entry =
+                       list_entry(ptr, struct set_tim_data, list);
+               if (entry->aid == aid) {
+                       PDEBUG(DEBUG_PS2, "%s: prism2_set_tim: aid=%d "
+                              "set=%d ==> %d\n",
+                              local->dev->name, aid, entry->set, set);
+                       entry->set = set;
+                       kfree(new_entry);
+                       new_entry = NULL;
+                       break;
+               }
+       }
+       if (new_entry)
+               list_add_tail(&new_entry->list, &local->set_tim_list);
+       spin_unlock_bh(&local->set_tim_lock);
+
+       schedule_work(&local->set_tim_queue);
+
+       return 0;
+}
+
+
+static void handle_set_tim_queue(void *data)
+{
+       local_info_t *local = (local_info_t *) data;
+       struct set_tim_data *entry;
+       u16 val;
+
+       for (;;) {
+               entry = NULL;
+               spin_lock_bh(&local->set_tim_lock);
+               if (!list_empty(&local->set_tim_list)) {
+                       entry = list_entry(local->set_tim_list.next,
+                                          struct set_tim_data, list);
+                       list_del(&entry->list);
+               }
+               spin_unlock_bh(&local->set_tim_lock);
+               if (!entry)
+                       break;
+
+               PDEBUG(DEBUG_PS2, "%s: handle_set_tim_queue: aid=%d set=%d\n",
+                      local->dev->name, entry->aid, entry->set);
+
+               val = entry->aid;
+               if (entry->set)
+                       val |= 0x8000;
+               if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) {
+                       printk(KERN_DEBUG "%s: set_tim failed (aid=%d "
+                              "set=%d)\n",
+                              local->dev->name, entry->aid, entry->set);
+               }
+
+               kfree(entry);
+       }
+}
+
+
+static void prism2_clear_set_tim_queue(local_info_t *local)
+{
+       struct list_head *ptr, *n;
+
+       list_for_each_safe(ptr, n, &local->set_tim_list) {
+               struct set_tim_data *entry;
+               entry = list_entry(ptr, struct set_tim_data, list);
+               list_del(&entry->list);
+               kfree(entry);
+       }
+}
+
+
+static struct net_device *
+prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx,
+                      struct device *sdev)
+{
+       struct net_device *dev;
+       struct hostap_interface *iface;
+       struct local_info *local;
+       int len, i, ret;
+
+       if (funcs == NULL)
+               return NULL;
+
+       len = strlen(dev_template);
+       if (len >= IFNAMSIZ || strstr(dev_template, "%d") == NULL) {
+               printk(KERN_WARNING "hostap: Invalid dev_template='%s'\n",
+                      dev_template);
+               return NULL;
+       }
+
+       len = sizeof(struct hostap_interface) +
+               3 + sizeof(struct local_info) +
+               3 + sizeof(struct ap_data);
+
+       dev = alloc_etherdev(len);
+       if (dev == NULL)
+               return NULL;
+
+       iface = netdev_priv(dev);
+       local = (struct local_info *) ((((long) (iface + 1)) + 3) & ~3);
+       local->ap = (struct ap_data *) ((((long) (local + 1)) + 3) & ~3);
+       local->dev = iface->dev = dev;
+       iface->local = local;
+       iface->type = HOSTAP_INTERFACE_MASTER;
+       INIT_LIST_HEAD(&local->hostap_interfaces);
+
+       local->hw_module = THIS_MODULE;
+
+#ifdef PRISM2_IO_DEBUG
+       local->io_debug_enabled = 1;
+#endif /* PRISM2_IO_DEBUG */
+
+       local->func = funcs;
+       local->func->cmd = hfa384x_cmd;
+       local->func->read_regs = hfa384x_read_regs;
+       local->func->get_rid = hfa384x_get_rid;
+       local->func->set_rid = hfa384x_set_rid;
+       local->func->hw_enable = prism2_hw_enable;
+       local->func->hw_config = prism2_hw_config;
+       local->func->hw_reset = prism2_hw_reset;
+       local->func->hw_shutdown = prism2_hw_shutdown;
+       local->func->reset_port = prism2_reset_port;
+       local->func->schedule_reset = prism2_schedule_reset;
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+       local->func->read_aux = prism2_download_aux_dump;
+       local->func->download = prism2_download;
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+       local->func->tx = prism2_tx_80211;
+       local->func->set_tim = prism2_set_tim;
+       local->func->need_tx_headroom = 0; /* no need to add txdesc in
+                                           * skb->data (FIX: maybe for DMA bus
+                                           * mastering? */
+
+       local->mtu = mtu;
+
+       rwlock_init(&local->iface_lock);
+       spin_lock_init(&local->txfidlock);
+       spin_lock_init(&local->cmdlock);
+       spin_lock_init(&local->baplock);
+       spin_lock_init(&local->lock);
+       init_MUTEX(&local->rid_bap_sem);
+
+       if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES)
+               card_idx = 0;
+       local->card_idx = card_idx;
+
+       len = strlen(essid);
+       memcpy(local->essid, essid,
+              len > MAX_SSID_LEN ? MAX_SSID_LEN : len);
+       local->essid[MAX_SSID_LEN] = '\0';
+       i = GET_INT_PARM(iw_mode, card_idx);
+       if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) ||
+           i == IW_MODE_MONITOR) {
+               local->iw_mode = i;
+       } else {
+               printk(KERN_WARNING "prism2: Unknown iw_mode %d; using "
+                      "IW_MODE_MASTER\n", i);
+               local->iw_mode = IW_MODE_MASTER;
+       }
+       local->channel = GET_INT_PARM(channel, card_idx);
+       local->beacon_int = GET_INT_PARM(beacon_int, card_idx);
+       local->dtim_period = GET_INT_PARM(dtim_period, card_idx);
+       local->wds_max_connections = 16;
+       local->tx_control = HFA384X_TX_CTRL_FLAGS;
+       local->manual_retry_count = -1;
+       local->rts_threshold = 2347;
+       local->fragm_threshold = 2346;
+       local->rssi_to_dBm = 100; /* default; to be overriden by
+                                  * cnfDbmAdjust, if available */
+       local->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY;
+       local->sram_type = -1;
+       local->scan_channel_mask = 0xffff;
+
+       /* Initialize task queue structures */
+       INIT_WORK(&local->reset_queue, handle_reset_queue, local);
+       INIT_WORK(&local->set_multicast_list_queue,
+                 hostap_set_multicast_list_queue, local->dev);
+
+       INIT_WORK(&local->set_tim_queue, handle_set_tim_queue, local);
+       INIT_LIST_HEAD(&local->set_tim_list);
+       spin_lock_init(&local->set_tim_lock);
+
+       INIT_WORK(&local->comms_qual_update, handle_comms_qual_update, local);
+
+       /* Initialize tasklets for handling hardware IRQ related operations
+        * outside hw IRQ handler */
+#define HOSTAP_TASKLET_INIT(q, f, d) \
+do { memset((q), 0, sizeof(*(q))); (q)->func = (f); (q)->data = (d); } \
+while (0)
+       HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet,
+                           (unsigned long) local);
+
+       HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet,
+                           (unsigned long) local);
+       hostap_info_init(local);
+
+       HOSTAP_TASKLET_INIT(&local->rx_tasklet,
+                           hostap_rx_tasklet, (unsigned long) local);
+       skb_queue_head_init(&local->rx_list);
+
+       HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet,
+                           hostap_sta_tx_exc_tasklet, (unsigned long) local);
+       skb_queue_head_init(&local->sta_tx_exc_list);
+
+       INIT_LIST_HEAD(&local->cmd_queue);
+       init_waitqueue_head(&local->hostscan_wq);
+       INIT_LIST_HEAD(&local->crypt_deinit_list);
+       init_timer(&local->crypt_deinit_timer);
+       local->crypt_deinit_timer.data = (unsigned long) local;
+       local->crypt_deinit_timer.function = prism2_crypt_deinit_handler;
+
+       init_timer(&local->passive_scan_timer);
+       local->passive_scan_timer.data = (unsigned long) local;
+       local->passive_scan_timer.function = hostap_passive_scan;
+
+       init_timer(&local->tick_timer);
+       local->tick_timer.data = (unsigned long) local;
+       local->tick_timer.function = hostap_tick_timer;
+       local->tick_timer.expires = jiffies + 2 * HZ;
+       add_timer(&local->tick_timer);
+
+       INIT_LIST_HEAD(&local->bss_list);
+
+       hostap_setup_dev(dev, local, 1);
+       local->saved_eth_header_parse = dev->hard_header_parse;
+
+       dev->hard_start_xmit = hostap_master_start_xmit;
+       dev->type = ARPHRD_IEEE80211;
+       dev->hard_header_parse = hostap_80211_header_parse;
+
+       rtnl_lock();
+       ret = dev_alloc_name(dev, "wifi%d");
+       SET_NETDEV_DEV(dev, sdev);
+       if (ret >= 0)
+               ret = register_netdevice(dev);
+       rtnl_unlock();
+       if (ret < 0) {
+               printk(KERN_WARNING "%s: register netdevice failed!\n",
+                      dev_info);
+               goto fail;
+       }
+       printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name);
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+       create_proc_read_entry("registers", 0, local->proc,
+                              prism2_registers_proc_read, local);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+       hostap_init_data(local);
+       return dev;
+
+ fail:
+       free_netdev(dev);
+       return NULL;
+}
+
+
+static int hostap_hw_ready(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       struct local_info *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       local->ddev = hostap_add_interface(local, HOSTAP_INTERFACE_MAIN, 0,
+                                          "", dev_template);
+
+       if (local->ddev) {
+               if (local->iw_mode == IW_MODE_INFRA ||
+                   local->iw_mode == IW_MODE_ADHOC) {
+                       netif_carrier_off(local->dev);
+                       netif_carrier_off(local->ddev);
+               }
+               hostap_init_proc(local);
+               hostap_init_ap_proc(local);
+               return 0;
+       }
+
+       return -1;
+}
+
+
+static void prism2_free_local_data(struct net_device *dev)
+{
+       struct hostap_tx_callback_info *tx_cb, *tx_cb_prev;
+       int i;
+       struct hostap_interface *iface;
+       struct local_info *local;
+       struct list_head *ptr, *n;
+
+       if (dev == NULL)
+               return;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       flush_scheduled_work();
+
+       if (timer_pending(&local->crypt_deinit_timer))
+               del_timer(&local->crypt_deinit_timer);
+       prism2_crypt_deinit_entries(local, 1);
+
+       if (timer_pending(&local->passive_scan_timer))
+               del_timer(&local->passive_scan_timer);
+
+       if (timer_pending(&local->tick_timer))
+               del_timer(&local->tick_timer);
+
+       prism2_clear_cmd_queue(local);
+
+       skb_queue_purge(&local->info_list);
+       skb_queue_purge(&local->rx_list);
+       skb_queue_purge(&local->sta_tx_exc_list);
+
+       if (local->dev_enabled)
+               prism2_callback(local, PRISM2_CALLBACK_DISABLE);
+
+       for (i = 0; i < WEP_KEYS; i++) {
+               struct ieee80211_crypt_data *crypt = local->crypt[i];
+               if (crypt) {
+                       if (crypt->ops)
+                               crypt->ops->deinit(crypt->priv);
+                       kfree(crypt);
+                       local->crypt[i] = NULL;
+               }
+       }
+
+       if (local->ap != NULL)
+               hostap_free_data(local->ap);
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+       if (local->proc != NULL)
+               remove_proc_entry("registers", local->proc);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+       hostap_remove_proc(local);
+
+       tx_cb = local->tx_callback;
+       while (tx_cb != NULL) {
+               tx_cb_prev = tx_cb;
+               tx_cb = tx_cb->next;
+               kfree(tx_cb_prev);
+       }
+
+       hostap_set_hostapd(local, 0, 0);
+       hostap_set_hostapd_sta(local, 0, 0);
+
+       for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) {
+               if (local->frag_cache[i].skb != NULL)
+                       dev_kfree_skb(local->frag_cache[i].skb);
+       }
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+       prism2_download_free_data(local->dl_pri);
+       prism2_download_free_data(local->dl_sec);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+       list_for_each_safe(ptr, n, &local->hostap_interfaces) {
+               iface = list_entry(ptr, struct hostap_interface, list);
+               if (iface->type == HOSTAP_INTERFACE_MASTER) {
+                       /* special handling for this interface below */
+                       continue;
+               }
+               hostap_remove_interface(iface->dev, 0, 1);
+       }
+
+       prism2_clear_set_tim_queue(local);
+
+       list_for_each_safe(ptr, n, &local->bss_list) {
+               struct hostap_bss_info *bss =
+                       list_entry(ptr, struct hostap_bss_info, list);
+               kfree(bss);
+       }
+
+       kfree(local->pda);
+       kfree(local->last_scan_results);
+       kfree(local->generic_elem);
+
+       unregister_netdev(local->dev);
+       free_netdev(local->dev);
+}
+
+
+#ifndef PRISM2_PLX
+static void prism2_suspend(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       struct local_info *local;
+       union iwreq_data wrqu;
+
+       iface = dev->priv;
+       local = iface->local;
+
+       /* Send disconnect event, e.g., to trigger reassociation after resume
+        * if wpa_supplicant is used. */
+       memset(&wrqu, 0, sizeof(wrqu));
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
+
+       /* Disable hardware and firmware */
+       prism2_hw_shutdown(dev, 0);
+}
+#endif /* PRISM2_PLX */
+
+
+/* These might at some point be compiled separately and used as separate
+ * kernel modules or linked into one */
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+#include "hostap_download.c"
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+#ifdef PRISM2_CALLBACK
+/* External hostap_callback.c file can be used to, e.g., blink activity led.
+ * This can use platform specific code and must define prism2_callback()
+ * function (if PRISM2_CALLBACK is not defined, these function calls are not
+ * used. */
+#include "hostap_callback.c"
+#endif /* PRISM2_CALLBACK */
diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c
new file mode 100644 (file)
index 0000000..5aa998f
--- /dev/null
@@ -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);
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
new file mode 100644 (file)
index 0000000..e720369
--- /dev/null
@@ -0,0 +1,4102 @@
+/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */
+
+#ifdef in_atomic
+/* Get kernel_locked() for in_atomic() */
+#include <linux/smp_lock.h>
+#endif
+#include <linux/ethtool.h>
+
+
+static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct iw_statistics *wstats;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       /* Why are we doing that ? Jean II */
+       if (iface->type != HOSTAP_INTERFACE_MAIN)
+               return NULL;
+
+       wstats = &local->wstats;
+
+       wstats->status = 0;
+       wstats->discard.code =
+               local->comm_tallies.rx_discards_wep_undecryptable;
+       wstats->discard.misc =
+               local->comm_tallies.rx_fcs_errors +
+               local->comm_tallies.rx_discards_no_buffer +
+               local->comm_tallies.tx_discards_wrong_sa;
+
+       wstats->discard.retries =
+               local->comm_tallies.tx_retry_limit_exceeded;
+       wstats->discard.fragment =
+               local->comm_tallies.rx_message_in_bad_msg_fragments;
+
+       if (local->iw_mode != IW_MODE_MASTER &&
+           local->iw_mode != IW_MODE_REPEAT) {
+               int update = 1;
+#ifdef in_atomic
+               /* RID reading might sleep and it must not be called in
+                * interrupt context or while atomic. However, this
+                * function seems to be called while atomic (at least in Linux
+                * 2.5.59). Update signal quality values only if in suitable
+                * context. Otherwise, previous values read from tick timer
+                * will be used. */
+               if (in_atomic())
+                       update = 0;
+#endif /* in_atomic */
+
+               if (update && prism2_update_comms_qual(dev) == 0)
+                       wstats->qual.updated = 7;
+
+               wstats->qual.qual = local->comms_qual;
+               wstats->qual.level = local->avg_signal;
+               wstats->qual.noise = local->avg_noise;
+       } else {
+               wstats->qual.qual = 0;
+               wstats->qual.level = 0;
+               wstats->qual.noise = 0;
+               wstats->qual.updated = 0;
+       }
+
+       return wstats;
+}
+
+
+static int prism2_get_datarates(struct net_device *dev, u8 *rates)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u8 buf[12];
+       int len;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf,
+                                  sizeof(buf), 0);
+       if (len < 2)
+               return 0;
+
+       val = le16_to_cpu(*(u16 *) buf); /* string length */
+
+       if (len - 2 < val || val > 10)
+               return 0;
+
+       memcpy(rates, buf + 2, val);
+       return val;
+}
+
+
+static int prism2_get_name(struct net_device *dev,
+                          struct iw_request_info *info,
+                          char *name, char *extra)
+{
+       u8 rates[10];
+       int len, i, over2 = 0;
+
+       len = prism2_get_datarates(dev, rates);
+
+       for (i = 0; i < len; i++) {
+               if (rates[i] == 0x0b || rates[i] == 0x16) {
+                       over2 = 1;
+                       break;
+               }
+       }
+
+       strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS");
+
+       return 0;
+}
+
+
+static void prism2_crypt_delayed_deinit(local_info_t *local,
+                                       struct ieee80211_crypt_data **crypt)
+{
+       struct ieee80211_crypt_data *tmp;
+       unsigned long flags;
+
+       tmp = *crypt;
+       *crypt = NULL;
+
+       if (tmp == NULL)
+               return;
+
+       /* 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(&local->lock, flags);
+       list_add(&tmp->list, &local->crypt_deinit_list);
+       if (!timer_pending(&local->crypt_deinit_timer)) {
+               local->crypt_deinit_timer.expires = jiffies + HZ;
+               add_timer(&local->crypt_deinit_timer);
+       }
+       spin_unlock_irqrestore(&local->lock, flags);
+}
+
+
+static int prism2_ioctl_siwencode(struct net_device *dev,
+                                 struct iw_request_info *info,
+                                 struct iw_point *erq, char *keybuf)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int i;
+       struct ieee80211_crypt_data **crypt;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       i = erq->flags & IW_ENCODE_INDEX;
+       if (i < 1 || i > 4)
+               i = local->tx_keyidx;
+       else
+               i--;
+       if (i < 0 || i >= WEP_KEYS)
+               return -EINVAL;
+
+       crypt = &local->crypt[i];
+
+       if (erq->flags & IW_ENCODE_DISABLED) {
+               if (*crypt)
+                       prism2_crypt_delayed_deinit(local, crypt);
+               goto done;
+       }
+
+       if (*crypt != NULL && (*crypt)->ops != NULL &&
+           strcmp((*crypt)->ops->name, "WEP") != 0) {
+               /* changing to use WEP; deinit previously used algorithm */
+               prism2_crypt_delayed_deinit(local, crypt);
+       }
+
+       if (*crypt == NULL) {
+               struct ieee80211_crypt_data *new_crypt;
+
+               /* take WEP into use */
+               new_crypt = (struct ieee80211_crypt_data *)
+                       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)
+                       new_crypt->priv = new_crypt->ops->init(i);
+               if (!new_crypt->ops || !new_crypt->priv) {
+                       kfree(new_crypt);
+                       new_crypt = NULL;
+
+                       printk(KERN_WARNING "%s: could not initialize WEP: "
+                              "load module hostap_crypt_wep.o\n",
+                              dev->name);
+                       return -EOPNOTSUPP;
+               }
+               *crypt = new_crypt;
+       }
+
+       if (erq->length > 0) {
+               int len = erq->length <= 5 ? 5 : 13;
+               int first = 1, j;
+               if (len > erq->length)
+                       memset(keybuf + erq->length, 0, len - erq->length);
+               (*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv);
+               for (j = 0; j < WEP_KEYS; j++) {
+                       if (j != i && local->crypt[j]) {
+                               first = 0;
+                               break;
+                       }
+               }
+               if (first)
+                       local->tx_keyidx = i;
+       } else {
+               /* No key data - just set the default TX key index */
+               local->tx_keyidx = i;
+       }
+
+ done:
+       local->open_wep = erq->flags & IW_ENCODE_OPEN;
+
+       if (hostap_set_encryption(local)) {
+               printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name);
+               return -EINVAL;
+       }
+
+       /* Do not reset port0 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. Prism2 documentation seem to require port reset
+        * after WEP configuration. However, keys are apparently changed at
+        * least in Managed mode. */
+       if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) {
+               printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+static int prism2_ioctl_giwencode(struct net_device *dev,
+                                 struct iw_request_info *info,
+                                 struct iw_point *erq, char *key)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int i, len;
+       u16 val;
+       struct ieee80211_crypt_data *crypt;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       i = erq->flags & IW_ENCODE_INDEX;
+       if (i < 1 || i > 4)
+               i = local->tx_keyidx;
+       else
+               i--;
+       if (i < 0 || i >= WEP_KEYS)
+               return -EINVAL;
+
+       crypt = local->crypt[i];
+       erq->flags = i + 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;
+       }
+
+       /* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show
+        * the keys from driver buffer */
+       len = crypt->ops->get_key(key, WEP_KEY_LEN, NULL, crypt->priv);
+       erq->length = (len >= 0 ? len : 0);
+
+       if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0)
+       {
+               printk("CNFWEPFLAGS reading failed\n");
+               return -EOPNOTSUPP;
+       }
+       le16_to_cpus(&val);
+       if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED)
+               erq->flags |= IW_ENCODE_ENABLED;
+       else
+               erq->flags |= IW_ENCODE_DISABLED;
+       if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED)
+               erq->flags |= IW_ENCODE_RESTRICTED;
+       else
+               erq->flags |= IW_ENCODE_OPEN;
+
+       return 0;
+}
+
+
+static int hostap_set_rate(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret, basic_rates;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       basic_rates = local->basic_rates & local->tx_rate_control;
+       if (!basic_rates || basic_rates != local->basic_rates) {
+               printk(KERN_INFO "%s: updating basic rate set automatically "
+                      "to match with the new supported rate set\n",
+                      dev->name);
+               if (!basic_rates)
+                       basic_rates = local->tx_rate_control;
+
+               local->basic_rates = basic_rates;
+               if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES,
+                                   basic_rates))
+                       printk(KERN_WARNING "%s: failed to set "
+                              "cnfBasicRates\n", dev->name);
+       }
+
+       ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL,
+                              local->tx_rate_control) ||
+              hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES,
+                              local->tx_rate_control) ||
+              local->func->reset_port(dev));
+
+       if (ret) {
+               printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates "
+                      "setting to 0x%x failed\n",
+                      dev->name, local->tx_rate_control);
+       }
+
+       /* Update TX rate configuration for all STAs based on new operational
+        * rate set. */
+       hostap_update_rates(local);
+
+       return ret;
+}
+
+
+static int prism2_ioctl_siwrate(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *rrq, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (rrq->fixed) {
+               switch (rrq->value) {
+               case 11000000:
+                       local->tx_rate_control = HFA384X_RATES_11MBPS;
+                       break;
+               case 5500000:
+                       local->tx_rate_control = HFA384X_RATES_5MBPS;
+                       break;
+               case 2000000:
+                       local->tx_rate_control = HFA384X_RATES_2MBPS;
+                       break;
+               case 1000000:
+                       local->tx_rate_control = HFA384X_RATES_1MBPS;
+                       break;
+               default:
+                       local->tx_rate_control = HFA384X_RATES_1MBPS |
+                               HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS |
+                               HFA384X_RATES_11MBPS;
+                       break;
+               }
+       } else {
+               switch (rrq->value) {
+               case 11000000:
+                       local->tx_rate_control = HFA384X_RATES_1MBPS |
+                               HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS |
+                               HFA384X_RATES_11MBPS;
+                       break;
+               case 5500000:
+                       local->tx_rate_control = HFA384X_RATES_1MBPS |
+                               HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS;
+                       break;
+               case 2000000:
+                       local->tx_rate_control = HFA384X_RATES_1MBPS |
+                               HFA384X_RATES_2MBPS;
+                       break;
+               case 1000000:
+                       local->tx_rate_control = HFA384X_RATES_1MBPS;
+                       break;
+               default:
+                       local->tx_rate_control = HFA384X_RATES_1MBPS |
+                               HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS |
+                               HFA384X_RATES_11MBPS;
+                       break;
+               }
+       }
+
+       return hostap_set_rate(dev);
+}
+
+
+static int prism2_ioctl_giwrate(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *rrq, char *extra)
+{
+       u16 val;
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) <
+           0)
+               return -EINVAL;
+
+       if ((val & 0x1) && (val > 1))
+               rrq->fixed = 0;
+       else
+               rrq->fixed = 1;
+
+       if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL &&
+           !local->fw_tx_rate_control) {
+               /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in
+                * Host AP mode, so use the recorded TX rate of the last sent
+                * frame */
+               rrq->value = local->ap->last_tx_rate > 0 ?
+                       local->ap->last_tx_rate * 100000 : 11000000;
+               return 0;
+       }
+
+       if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) <
+           0)
+               return -EINVAL;
+
+       switch (val) {
+       case HFA384X_RATES_1MBPS:
+               rrq->value = 1000000;
+               break;
+       case HFA384X_RATES_2MBPS:
+               rrq->value = 2000000;
+               break;
+       case HFA384X_RATES_5MBPS:
+               rrq->value = 5500000;
+               break;
+       case HFA384X_RATES_11MBPS:
+               rrq->value = 11000000;
+               break;
+       default:
+               /* should not happen */
+               rrq->value = 11000000;
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+
+static int prism2_ioctl_siwsens(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *sens, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       /* Set the desired AP density */
+       if (sens->value < 1 || sens->value > 3)
+               return -EINVAL;
+
+       if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) ||
+           local->func->reset_port(dev))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int prism2_ioctl_giwsens(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *sens, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       /* Get the current AP density */
+       if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) <
+           0)
+               return -EINVAL;
+
+       sens->value = __le16_to_cpu(val);
+       sens->fixed = 1;
+
+       return 0;
+}
+
+
+/* Deprecated in new wireless extension API */
+static int prism2_ioctl_giwaplist(struct net_device *dev,
+                                 struct iw_request_info *info,
+                                 struct iw_point *data, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct sockaddr *addr;
+       struct iw_quality *qual;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->iw_mode != IW_MODE_MASTER) {
+               printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported "
+                      "in Host AP mode\n");
+               data->length = 0;
+               return -EOPNOTSUPP;
+       }
+
+       addr = kmalloc(sizeof(struct sockaddr) * IW_MAX_AP, GFP_KERNEL);
+       qual = kmalloc(sizeof(struct iw_quality) * IW_MAX_AP, GFP_KERNEL);
+       if (addr == NULL || qual == NULL) {
+               kfree(addr);
+               kfree(qual);
+               data->length = 0;
+               return -ENOMEM;
+       }
+
+       data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1);
+
+       memcpy(extra, &addr, sizeof(struct sockaddr) * data->length);
+       data->flags = 1; /* has quality information */
+       memcpy(extra + sizeof(struct sockaddr) * data->length, &qual,
+              sizeof(struct iw_quality) * data->length);
+
+       kfree(addr);
+       kfree(qual);
+
+       return 0;
+}
+
+
+static int prism2_ioctl_siwrts(struct net_device *dev,
+                              struct iw_request_info *info,
+                              struct iw_param *rts, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (rts->disabled)
+               val = __constant_cpu_to_le16(2347);
+       else if (rts->value < 0 || rts->value > 2347)
+               return -EINVAL;
+       else
+               val = __cpu_to_le16(rts->value);
+
+       if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) ||
+           local->func->reset_port(dev))
+               return -EINVAL;
+
+       local->rts_threshold = rts->value;
+
+       return 0;
+}
+
+static int prism2_ioctl_giwrts(struct net_device *dev,
+                              struct iw_request_info *info,
+                              struct iw_param *rts, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) <
+           0)
+               return -EINVAL;
+
+       rts->value = __le16_to_cpu(val);
+       rts->disabled = (rts->value == 2347);
+       rts->fixed = 1;
+
+       return 0;
+}
+
+
+static int prism2_ioctl_siwfrag(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *rts, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (rts->disabled)
+               val = __constant_cpu_to_le16(2346);
+       else if (rts->value < 256 || rts->value > 2346)
+               return -EINVAL;
+       else
+               val = __cpu_to_le16(rts->value & ~0x1); /* even numbers only */
+
+       local->fragm_threshold = rts->value & ~0x1;
+       if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val,
+                                2)
+           || local->func->reset_port(dev))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int prism2_ioctl_giwfrag(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *rts, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
+                                &val, 2, 1) < 0)
+               return -EINVAL;
+
+       rts->value = __le16_to_cpu(val);
+       rts->disabled = (rts->value == 2346);
+       rts->fixed = 1;
+
+       return 0;
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+static int hostap_join_ap(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hfa384x_join_request req;
+       unsigned long flags;
+       int i;
+       struct hfa384x_hostscan_result *entry;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       memcpy(req.bssid, local->preferred_ap, ETH_ALEN);
+       req.channel = 0;
+
+       spin_lock_irqsave(&local->lock, flags);
+       for (i = 0; i < local->last_scan_results_count; i++) {
+               if (!local->last_scan_results)
+                       break;
+               entry = &local->last_scan_results[i];
+               if (memcmp(local->preferred_ap, entry->bssid, ETH_ALEN) == 0) {
+                       req.channel = entry->chid;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&local->lock, flags);
+
+       if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
+                                sizeof(req))) {
+               printk(KERN_DEBUG "%s: JoinRequest " MACSTR
+                      " failed\n",
+                      dev->name, MAC2STR(local->preferred_ap));
+               return -1;
+       }
+
+       printk(KERN_DEBUG "%s: Trying to join BSSID " MACSTR "\n",
+              dev->name, MAC2STR(local->preferred_ap));
+
+       return 0;
+}
+#endif /* PRISM2_NO_STATION_MODES */
+
+
+static int prism2_ioctl_siwap(struct net_device *dev,
+                             struct iw_request_info *info,
+                             struct sockaddr *ap_addr, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+       return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN);
+
+       if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) {
+               struct hfa384x_scan_request scan_req;
+               memset(&scan_req, 0, sizeof(scan_req));
+               scan_req.channel_list = __constant_cpu_to_le16(0x3fff);
+               scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS);
+               if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST,
+                                        &scan_req, sizeof(scan_req))) {
+                       printk(KERN_DEBUG "%s: ScanResults request failed - "
+                              "preferred AP delayed to next unsolicited "
+                              "scan\n", dev->name);
+               }
+       } else if (local->host_roaming == 2 &&
+                  local->iw_mode == IW_MODE_INFRA) {
+               if (hostap_join_ap(dev))
+                       return -EINVAL;
+       } else {
+               printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only "
+                      "in Managed mode when host_roaming is enabled\n",
+                      dev->name);
+       }
+
+       return 0;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+static int prism2_ioctl_giwap(struct net_device *dev,
+                             struct iw_request_info *info,
+                             struct sockaddr *ap_addr, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       ap_addr->sa_family = ARPHRD_ETHER;
+       switch (iface->type) {
+       case HOSTAP_INTERFACE_AP:
+               memcpy(&ap_addr->sa_data, dev->dev_addr, ETH_ALEN);
+               break;
+       case HOSTAP_INTERFACE_STA:
+               memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN);
+               break;
+       case HOSTAP_INTERFACE_WDS:
+               memcpy(&ap_addr->sa_data, iface->u.wds.remote_addr, ETH_ALEN);
+               break;
+       default:
+               if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID,
+                                        &ap_addr->sa_data, ETH_ALEN, 1) < 0)
+                       return -EOPNOTSUPP;
+
+               /* local->bssid is also updated in LinkStatus handler when in
+                * station mode */
+               memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN);
+               break;
+       }
+
+       return 0;
+}
+
+
+static int prism2_ioctl_siwnickn(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *data, char *nickname)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       memset(local->name, 0, sizeof(local->name));
+       memcpy(local->name, nickname, data->length);
+       local->name_set = 1;
+
+       if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) ||
+           local->func->reset_port(dev))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int prism2_ioctl_giwnickn(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *data, char *nickname)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int len;
+       char name[MAX_NAME_LEN + 3];
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME,
+                                  &name, MAX_NAME_LEN + 2, 0);
+       val = __le16_to_cpu(*(u16 *) name);
+       if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN)
+               return -EOPNOTSUPP;
+
+       name[val + 2] = '\0';
+       data->length = val + 1;
+       memcpy(nickname, name + 2, val + 1);
+
+       return 0;
+}
+
+
+static int prism2_ioctl_siwfreq(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_freq *freq, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       /* freq => chan. */
+       if (freq->e == 1 &&
+           freq->m / 100000 >= freq_list[0] &&
+           freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) {
+               int ch;
+               int fr = freq->m / 100000;
+               for (ch = 0; ch < FREQ_COUNT; ch++) {
+                       if (fr == freq_list[ch]) {
+                               freq->e = 0;
+                               freq->m = ch + 1;
+                               break;
+                       }
+               }
+       }
+
+       if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT ||
+           !(local->channel_mask & (1 << (freq->m - 1))))
+               return -EINVAL;
+
+       local->channel = freq->m; /* channel is used in prism2_setup_rids() */
+       if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) ||
+           local->func->reset_port(dev))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int prism2_ioctl_giwfreq(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_freq *freq, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) <
+           0)
+               return -EINVAL;
+
+       le16_to_cpus(&val);
+       if (val < 1 || val > FREQ_COUNT)
+               return -EINVAL;
+
+       freq->m = freq_list[val - 1] * 100000;
+       freq->e = 1;
+
+       return 0;
+}
+
+
+static void hostap_monitor_set_type(local_info_t *local)
+{
+       struct net_device *dev = local->ddev;
+
+       if (dev == NULL)
+               return;
+
+       if (local->monitor_type == PRISM2_MONITOR_PRISM ||
+           local->monitor_type == PRISM2_MONITOR_CAPHDR) {
+               dev->type = ARPHRD_IEEE80211_PRISM;
+               dev->hard_header_parse =
+                       hostap_80211_prism_header_parse;
+       } else {
+               dev->type = ARPHRD_IEEE80211;
+               dev->hard_header_parse = hostap_80211_header_parse;
+       }
+}
+
+
+static int prism2_ioctl_siwessid(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *data, char *ssid)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (iface->type == HOSTAP_INTERFACE_WDS)
+               return -EOPNOTSUPP;
+
+       if (data->flags == 0)
+               ssid[0] = '\0'; /* ANY */
+
+       if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') {
+               /* Setting SSID to empty string seems to kill the card in
+                * Host AP mode */
+               printk(KERN_DEBUG "%s: Host AP mode does not support "
+                      "'Any' essid\n", dev->name);
+               return -EINVAL;
+       }
+
+       memcpy(local->essid, ssid, data->length);
+       local->essid[data->length] = '\0';
+
+       if ((!local->fw_ap &&
+            hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid))
+           || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) ||
+           local->func->reset_port(dev))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int prism2_ioctl_giwessid(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *data, char *essid)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (iface->type == HOSTAP_INTERFACE_WDS)
+               return -EOPNOTSUPP;
+
+       data->flags = 1; /* active */
+       if (local->iw_mode == IW_MODE_MASTER) {
+               data->length = strlen(local->essid);
+               memcpy(essid, local->essid, IW_ESSID_MAX_SIZE);
+       } else {
+               int len;
+               char ssid[MAX_SSID_LEN + 2];
+               memset(ssid, 0, sizeof(ssid));
+               len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID,
+                                          &ssid, MAX_SSID_LEN + 2, 0);
+               val = __le16_to_cpu(*(u16 *) ssid);
+               if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) {
+                       return -EOPNOTSUPP;
+               }
+               data->length = val;
+               memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE);
+       }
+
+       return 0;
+}
+
+
+static int prism2_ioctl_giwrange(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *data, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct iw_range *range = (struct iw_range *) extra;
+       u8 rates[10];
+       u16 val;
+       int i, len, over2;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       data->length = sizeof(struct iw_range);
+       memset(range, 0, sizeof(struct iw_range));
+
+       /* TODO: could fill num_txpower and txpower array with
+        * something; however, there are 128 different values.. */
+
+       range->txpower_capa = IW_TXPOW_DBM;
+
+       if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC)
+       {
+               range->min_pmp = 1 * 1024;
+               range->max_pmp = 65535 * 1024;
+               range->min_pmt = 1 * 1024;
+               range->max_pmt = 1000 * 1024;
+               range->pmp_flags = IW_POWER_PERIOD;
+               range->pmt_flags = IW_POWER_TIMEOUT;
+               range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT |
+                       IW_POWER_UNICAST_R | IW_POWER_ALL_R;
+       }
+
+       range->we_version_compiled = WIRELESS_EXT;
+       range->we_version_source = 18;
+
+       range->retry_capa = IW_RETRY_LIMIT;
+       range->retry_flags = IW_RETRY_LIMIT;
+       range->min_retry = 0;
+       range->max_retry = 255;
+
+       range->num_channels = FREQ_COUNT;
+
+       val = 0;
+       for (i = 0; i < FREQ_COUNT; i++) {
+               if (local->channel_mask & (1 << i)) {
+                       range->freq[val].i = i + 1;
+                       range->freq[val].m = freq_list[i] * 100000;
+                       range->freq[val].e = 1;
+                       val++;
+               }
+               if (val == IW_MAX_FREQUENCIES)
+                       break;
+       }
+       range->num_frequency = val;
+
+       if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) {
+               range->max_qual.qual = 70; /* what is correct max? This was not
+                                           * documented exactly. At least
+                                           * 69 has been observed. */
+               range->max_qual.level = 0; /* dB */
+               range->max_qual.noise = 0; /* dB */
+
+               /* What would be suitable values for "average/typical" qual? */
+               range->avg_qual.qual = 20;
+               range->avg_qual.level = -60;
+               range->avg_qual.noise = -95;
+       } else {
+               range->max_qual.qual = 92; /* 0 .. 92 */
+               range->max_qual.level = 154; /* 27 .. 154 */
+               range->max_qual.noise = 154; /* 27 .. 154 */
+       }
+       range->sensitivity = 3;
+
+       range->max_encoding_tokens = WEP_KEYS;
+       range->num_encoding_sizes = 2;
+       range->encoding_size[0] = 5;
+       range->encoding_size[1] = 13;
+
+       over2 = 0;
+       len = prism2_get_datarates(dev, rates);
+       range->num_bitrates = 0;
+       for (i = 0; i < len; i++) {
+               if (range->num_bitrates < IW_MAX_BITRATES) {
+                       range->bitrate[range->num_bitrates] =
+                               rates[i] * 500000;
+                       range->num_bitrates++;
+               }
+               if (rates[i] == 0x0b || rates[i] == 0x16)
+                       over2 = 1;
+       }
+       /* estimated maximum TCP throughput values (bps) */
+       range->throughput = over2 ? 5500000 : 1500000;
+
+       range->min_rts = 0;
+       range->max_rts = 2347;
+       range->min_frag = 256;
+       range->max_frag = 2346;
+
+       /* Event capability (kernel + driver) */
+       range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+                               IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+                               IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+                               IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+       range->event_capa[1] = IW_EVENT_CAPA_K_1;
+       range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) |
+                               IW_EVENT_CAPA_MASK(IWEVCUSTOM) |
+                               IW_EVENT_CAPA_MASK(IWEVREGISTERED) |
+                               IW_EVENT_CAPA_MASK(IWEVEXPIRED));
+
+       range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+               IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+       return 0;
+}
+
+
+static int hostap_monitor_mode_enable(local_info_t *local)
+{
+       struct net_device *dev = local->dev;
+
+       printk(KERN_DEBUG "Enabling monitor mode\n");
+       hostap_monitor_set_type(local);
+
+       if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+                           HFA384X_PORTTYPE_PSEUDO_IBSS)) {
+               printk(KERN_DEBUG "Port type setting for monitor mode "
+                      "failed\n");
+               return -EOPNOTSUPP;
+       }
+
+       /* Host decrypt is needed to get the IV and ICV fields;
+        * however, monitor mode seems to remove WEP flag from frame
+        * control field */
+       if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS,
+                           HFA384X_WEPFLAGS_HOSTENCRYPT |
+                           HFA384X_WEPFLAGS_HOSTDECRYPT)) {
+               printk(KERN_DEBUG "WEP flags setting failed\n");
+               return -EOPNOTSUPP;
+       }
+
+       if (local->func->reset_port(dev) ||
+           local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+                            (HFA384X_TEST_MONITOR << 8),
+                            0, NULL, NULL)) {
+               printk(KERN_DEBUG "Setting monitor mode failed\n");
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+
+static int hostap_monitor_mode_disable(local_info_t *local)
+{
+       struct net_device *dev = local->ddev;
+
+       if (dev == NULL)
+               return -1;
+
+       printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name);
+       dev->type = ARPHRD_ETHER;
+       dev->hard_header_parse = local->saved_eth_header_parse;
+       if (local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+                            (HFA384X_TEST_STOP << 8),
+                            0, NULL, NULL))
+               return -1;
+       return hostap_set_encryption(local);
+}
+
+
+static int prism2_ioctl_siwmode(struct net_device *dev,
+                               struct iw_request_info *info,
+                               __u32 *mode, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int double_reset = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA &&
+           *mode != IW_MODE_MASTER && *mode != IW_MODE_REPEAT &&
+           *mode != IW_MODE_MONITOR)
+               return -EOPNOTSUPP;
+
+#ifdef PRISM2_NO_STATION_MODES
+       if (*mode == IW_MODE_ADHOC || *mode == IW_MODE_INFRA)
+               return -EOPNOTSUPP;
+#endif /* PRISM2_NO_STATION_MODES */
+
+       if (*mode == local->iw_mode)
+               return 0;
+
+       if (*mode == IW_MODE_MASTER && local->essid[0] == '\0') {
+               printk(KERN_WARNING "%s: empty SSID not allowed in Master "
+                      "mode\n", dev->name);
+               return -EINVAL;
+       }
+
+       if (local->iw_mode == IW_MODE_MONITOR)
+               hostap_monitor_mode_disable(local);
+
+       if ((local->iw_mode == IW_MODE_ADHOC ||
+            local->iw_mode == IW_MODE_MONITOR) && *mode == IW_MODE_MASTER) {
+               /* There seems to be a firmware bug in at least STA f/w v1.5.6
+                * that leaves beacon frames to use IBSS type when moving from
+                * IBSS to Host AP mode. Doing double Port0 reset seems to be
+                * enough to workaround this. */
+               double_reset = 1;
+       }
+
+       printk(KERN_DEBUG "prism2: %s: operating mode changed "
+              "%d -> %d\n", dev->name, local->iw_mode, *mode);
+       local->iw_mode = *mode;
+
+       if (local->iw_mode == IW_MODE_MONITOR)
+               hostap_monitor_mode_enable(local);
+       else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt &&
+                !local->fw_encrypt_ok) {
+               printk(KERN_DEBUG "%s: defaulting to host-based encryption as "
+                      "a workaround for firmware bug in Host AP mode WEP\n",
+                      dev->name);
+               local->host_encrypt = 1;
+       }
+
+       if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+                           hostap_get_porttype(local)))
+               return -EOPNOTSUPP;
+
+       if (local->func->reset_port(dev))
+               return -EINVAL;
+       if (double_reset && local->func->reset_port(dev))
+               return -EINVAL;
+
+       if (local->iw_mode != IW_MODE_INFRA && local->iw_mode != IW_MODE_ADHOC)
+       {
+               /* netif_carrier is used only in client modes for now, so make
+                * sure carrier is on when moving to non-client modes. */
+               netif_carrier_on(local->dev);
+               netif_carrier_on(local->ddev);
+       }
+       return 0;
+}
+
+
+static int prism2_ioctl_giwmode(struct net_device *dev,
+                               struct iw_request_info *info,
+                               __u32 *mode, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       switch (iface->type) {
+       case HOSTAP_INTERFACE_STA:
+               *mode = IW_MODE_INFRA;
+               break;
+       case HOSTAP_INTERFACE_WDS:
+               *mode = IW_MODE_REPEAT;
+               break;
+       default:
+               *mode = local->iw_mode;
+               break;
+       }
+       return 0;
+}
+
+
+static int prism2_ioctl_siwpower(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_param *wrq, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+       return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+       int ret = 0;
+
+       if (wrq->disabled)
+               return hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 0);
+
+       switch (wrq->flags & IW_POWER_MODE) {
+       case IW_POWER_UNICAST_R:
+               ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 0);
+               if (ret)
+                       return ret;
+               ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+               if (ret)
+                       return ret;
+               break;
+       case IW_POWER_ALL_R:
+               ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 1);
+               if (ret)
+                       return ret;
+               ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+               if (ret)
+                       return ret;
+               break;
+       case IW_POWER_ON:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (wrq->flags & IW_POWER_TIMEOUT) {
+               ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+               if (ret)
+                       return ret;
+               ret = hostap_set_word(dev, HFA384X_RID_CNFPMHOLDOVERDURATION,
+                                     wrq->value / 1024);
+               if (ret)
+                       return ret;
+       }
+       if (wrq->flags & IW_POWER_PERIOD) {
+               ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+               if (ret)
+                       return ret;
+               ret = hostap_set_word(dev, HFA384X_RID_CNFMAXSLEEPDURATION,
+                                     wrq->value / 1024);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_giwpower(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_param *rrq, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+       return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 enable, mcast;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->get_rid(dev, HFA384X_RID_CNFPMENABLED, &enable, 2, 1)
+           < 0)
+               return -EINVAL;
+
+       if (!__le16_to_cpu(enable)) {
+               rrq->disabled = 1;
+               return 0;
+       }
+
+       rrq->disabled = 0;
+
+       if ((rrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+               u16 timeout;
+               if (local->func->get_rid(dev,
+                                        HFA384X_RID_CNFPMHOLDOVERDURATION,
+                                        &timeout, 2, 1) < 0)
+                       return -EINVAL;
+
+               rrq->flags = IW_POWER_TIMEOUT;
+               rrq->value = __le16_to_cpu(timeout) * 1024;
+       } else {
+               u16 period;
+               if (local->func->get_rid(dev, HFA384X_RID_CNFMAXSLEEPDURATION,
+                                        &period, 2, 1) < 0)
+                       return -EINVAL;
+
+               rrq->flags = IW_POWER_PERIOD;
+               rrq->value = __le16_to_cpu(period) * 1024;
+       }
+
+       if (local->func->get_rid(dev, HFA384X_RID_CNFMULTICASTRECEIVE, &mcast,
+                                2, 1) < 0)
+               return -EINVAL;
+
+       if (__le16_to_cpu(mcast))
+               rrq->flags |= IW_POWER_ALL_R;
+       else
+               rrq->flags |= IW_POWER_UNICAST_R;
+
+       return 0;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_siwretry(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_param *rrq, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (rrq->disabled)
+               return -EINVAL;
+
+       /* setting retry limits is not supported with the current station
+        * firmware code; simulate this with alternative retry count for now */
+       if (rrq->flags == IW_RETRY_LIMIT) {
+               if (rrq->value < 0) {
+                       /* disable manual retry count setting and use firmware
+                        * defaults */
+                       local->manual_retry_count = -1;
+                       local->tx_control &= ~HFA384X_TX_CTRL_ALT_RTRY;
+               } else {
+                       if (hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT,
+                                           rrq->value)) {
+                               printk(KERN_DEBUG "%s: Alternate retry count "
+                                      "setting to %d failed\n",
+                                      dev->name, rrq->value);
+                               return -EOPNOTSUPP;
+                       }
+
+                       local->manual_retry_count = rrq->value;
+                       local->tx_control |= HFA384X_TX_CTRL_ALT_RTRY;
+               }
+               return 0;
+       }
+
+       return -EOPNOTSUPP;
+
+#if 0
+       /* what could be done, if firmware would support this.. */
+
+       if (rrq->flags & IW_RETRY_LIMIT) {
+               if (rrq->flags & IW_RETRY_MAX)
+                       HFA384X_RID_LONGRETRYLIMIT = rrq->value;
+               else if (rrq->flags & IW_RETRY_MIN)
+                       HFA384X_RID_SHORTRETRYLIMIT = rrq->value;
+               else {
+                       HFA384X_RID_LONGRETRYLIMIT = rrq->value;
+                       HFA384X_RID_SHORTRETRYLIMIT = rrq->value;
+               }
+
+       }
+
+       if (rrq->flags & IW_RETRY_LIFETIME) {
+               HFA384X_RID_MAXTRANSMITLIFETIME = rrq->value / 1024;
+       }
+
+       return 0;
+#endif /* 0 */
+}
+
+static int prism2_ioctl_giwretry(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_param *rrq, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 shortretry, longretry, lifetime, altretry;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->get_rid(dev, HFA384X_RID_SHORTRETRYLIMIT, &shortretry,
+                                2, 1) < 0 ||
+           local->func->get_rid(dev, HFA384X_RID_LONGRETRYLIMIT, &longretry,
+                                2, 1) < 0 ||
+           local->func->get_rid(dev, HFA384X_RID_MAXTRANSMITLIFETIME,
+                                &lifetime, 2, 1) < 0)
+               return -EINVAL;
+
+       le16_to_cpus(&shortretry);
+       le16_to_cpus(&longretry);
+       le16_to_cpus(&lifetime);
+
+       rrq->disabled = 0;
+
+       if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+               rrq->flags = IW_RETRY_LIFETIME;
+               rrq->value = lifetime * 1024;
+       } else {
+               if (local->manual_retry_count >= 0) {
+                       rrq->flags = IW_RETRY_LIMIT;
+                       if (local->func->get_rid(dev,
+                                                HFA384X_RID_CNFALTRETRYCOUNT,
+                                                &altretry, 2, 1) >= 0)
+                               rrq->value = le16_to_cpu(altretry);
+                       else
+                               rrq->value = local->manual_retry_count;
+               } else if ((rrq->flags & IW_RETRY_MAX)) {
+                       rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+                       rrq->value = longretry;
+               } else {
+                       rrq->flags = IW_RETRY_LIMIT;
+                       rrq->value = shortretry;
+                       if (shortretry != longretry)
+                               rrq->flags |= IW_RETRY_MIN;
+               }
+       }
+       return 0;
+}
+
+
+/* Note! This TX power controlling is experimental and should not be used in
+ * production use. It just sets raw power register and does not use any kind of
+ * feedback information from the measured TX power (CR58). This is now
+ * commented out to make sure that it is not used by accident. TX power
+ * configuration will be enabled again after proper algorithm using feedback
+ * has been implemented. */
+
+#ifdef RAW_TXPOWER_SETTING
+/* Map HFA386x's CR31 to and from dBm with some sort of ad hoc mapping..
+ * This version assumes following mapping:
+ * CR31 is 7-bit value with -64 to +63 range.
+ * -64 is mapped into +20dBm and +63 into -43dBm.
+ * This is certainly not an exact mapping for every card, but at least
+ * increasing dBm value should correspond to increasing TX power.
+ */
+
+static int prism2_txpower_hfa386x_to_dBm(u16 val)
+{
+       signed char tmp;
+
+       if (val > 255)
+               val = 255;
+
+       tmp = val;
+       tmp >>= 2;
+
+       return -12 - tmp;
+}
+
+static u16 prism2_txpower_dBm_to_hfa386x(int val)
+{
+       signed char tmp;
+
+       if (val > 20)
+               return 128;
+       else if (val < -43)
+               return 127;
+
+       tmp = val;
+       tmp = -12 - tmp;
+       tmp <<= 2;
+
+       return (unsigned char) tmp;
+}
+#endif /* RAW_TXPOWER_SETTING */
+
+
+static int prism2_ioctl_siwtxpow(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_param *rrq, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+#ifdef RAW_TXPOWER_SETTING
+       char *tmp;
+#endif
+       u16 val;
+       int ret = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (rrq->disabled) {
+               if (local->txpower_type != PRISM2_TXPOWER_OFF) {
+                       val = 0xff; /* use all standby and sleep modes */
+                       ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+                                              HFA386X_CR_A_D_TEST_MODES2,
+                                              &val, NULL);
+                       printk(KERN_DEBUG "%s: Turning radio off: %s\n",
+                              dev->name, ret ? "failed" : "OK");
+                       local->txpower_type = PRISM2_TXPOWER_OFF;
+               }
+               return (ret ? -EOPNOTSUPP : 0);
+       }
+
+       if (local->txpower_type == PRISM2_TXPOWER_OFF) {
+               val = 0; /* disable all standby and sleep modes */
+               ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+                                      HFA386X_CR_A_D_TEST_MODES2, &val, NULL);
+               printk(KERN_DEBUG "%s: Turning radio on: %s\n",
+                      dev->name, ret ? "failed" : "OK");
+               local->txpower_type = PRISM2_TXPOWER_UNKNOWN;
+       }
+
+#ifdef RAW_TXPOWER_SETTING
+       if (!rrq->fixed && local->txpower_type != PRISM2_TXPOWER_AUTO) {
+               printk(KERN_DEBUG "Setting ALC on\n");
+               val = HFA384X_TEST_CFG_BIT_ALC;
+               local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+                                (HFA384X_TEST_CFG_BITS << 8), 1, &val, NULL);
+               local->txpower_type = PRISM2_TXPOWER_AUTO;
+               return 0;
+       }
+
+       if (local->txpower_type != PRISM2_TXPOWER_FIXED) {
+               printk(KERN_DEBUG "Setting ALC off\n");
+               val = HFA384X_TEST_CFG_BIT_ALC;
+               local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+                                (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL);
+                       local->txpower_type = PRISM2_TXPOWER_FIXED;
+       }
+
+       if (rrq->flags == IW_TXPOW_DBM)
+               tmp = "dBm";
+       else if (rrq->flags == IW_TXPOW_MWATT)
+               tmp = "mW";
+       else
+               tmp = "UNKNOWN";
+       printk(KERN_DEBUG "Setting TX power to %d %s\n", rrq->value, tmp);
+
+       if (rrq->flags != IW_TXPOW_DBM) {
+               printk("SIOCSIWTXPOW with mW is not supported; use dBm\n");
+               return -EOPNOTSUPP;
+       }
+
+       local->txpower = rrq->value;
+       val = prism2_txpower_dBm_to_hfa386x(local->txpower);
+       if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+                            HFA386X_CR_MANUAL_TX_POWER, &val, NULL))
+               ret = -EOPNOTSUPP;
+#else /* RAW_TXPOWER_SETTING */
+       if (rrq->fixed)
+               ret = -EOPNOTSUPP;
+#endif /* RAW_TXPOWER_SETTING */
+
+       return ret;
+}
+
+static int prism2_ioctl_giwtxpow(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_param *rrq, char *extra)
+{
+#ifdef RAW_TXPOWER_SETTING
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 resp0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       rrq->flags = IW_TXPOW_DBM;
+       rrq->disabled = 0;
+       rrq->fixed = 0;
+
+       if (local->txpower_type == PRISM2_TXPOWER_AUTO) {
+               if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF,
+                                    HFA386X_CR_MANUAL_TX_POWER,
+                                    NULL, &resp0) == 0) {
+                       rrq->value = prism2_txpower_hfa386x_to_dBm(resp0);
+               } else {
+                       /* Could not get real txpower; guess 15 dBm */
+                       rrq->value = 15;
+               }
+       } else if (local->txpower_type == PRISM2_TXPOWER_OFF) {
+               rrq->value = 0;
+               rrq->disabled = 1;
+       } else if (local->txpower_type == PRISM2_TXPOWER_FIXED) {
+               rrq->value = local->txpower;
+               rrq->fixed = 1;
+       } else {
+               printk("SIOCGIWTXPOW - unknown txpower_type=%d\n",
+                      local->txpower_type);
+       }
+       return 0;
+#else /* RAW_TXPOWER_SETTING */
+       return -EOPNOTSUPP;
+#endif /* RAW_TXPOWER_SETTING */
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+
+/* HostScan request works with and without host_roaming mode. In addition, it
+ * does not break current association. However, it requires newer station
+ * firmware version (>= 1.3.1) than scan request. */
+static int prism2_request_hostscan(struct net_device *dev,
+                                  u8 *ssid, u8 ssid_len)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hfa384x_hostscan_request scan_req;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       memset(&scan_req, 0, sizeof(scan_req));
+       scan_req.channel_list = cpu_to_le16(local->channel_mask &
+                                           local->scan_channel_mask);
+       scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS);
+       if (ssid) {
+               if (ssid_len > 32)
+                       return -EINVAL;
+               scan_req.target_ssid_len = cpu_to_le16(ssid_len);
+               memcpy(scan_req.target_ssid, ssid, ssid_len);
+       }
+
+       if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req,
+                                sizeof(scan_req))) {
+               printk(KERN_DEBUG "%s: HOSTSCAN failed\n", dev->name);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+
+static int prism2_request_scan(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hfa384x_scan_request scan_req;
+       int ret = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       memset(&scan_req, 0, sizeof(scan_req));
+       scan_req.channel_list = cpu_to_le16(local->channel_mask &
+                                           local->scan_channel_mask);
+       scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS);
+
+       /* FIX:
+        * It seems to be enough to set roaming mode for a short moment to
+        * host-based and then setup scanrequest data and return the mode to
+        * firmware-based.
+        *
+        * Master mode would need to drop to Managed mode for a short while
+        * to make scanning work.. Or sweep through the different channels and
+        * use passive scan based on beacons. */
+
+       if (!local->host_roaming)
+               hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE,
+                               HFA384X_ROAMING_HOST);
+
+       if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, &scan_req,
+                                sizeof(scan_req))) {
+               printk(KERN_DEBUG "SCANREQUEST failed\n");
+               ret = -EINVAL;
+       }
+
+       if (!local->host_roaming)
+               hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE,
+                               HFA384X_ROAMING_FIRMWARE);
+
+       return 0;
+}
+
+#else /* !PRISM2_NO_STATION_MODES */
+
+static inline int prism2_request_hostscan(struct net_device *dev,
+                                         u8 *ssid, u8 ssid_len)
+{
+       return -EOPNOTSUPP;
+}
+
+
+static inline int prism2_request_scan(struct net_device *dev)
+{
+       return -EOPNOTSUPP;
+}
+
+#endif /* !PRISM2_NO_STATION_MODES */
+
+
+static int prism2_ioctl_siwscan(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_point *data, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret;
+       u8 *ssid = NULL, ssid_len = 0;
+       struct iw_scan_req *req = (struct iw_scan_req *) extra;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (data->length < sizeof(struct iw_scan_req))
+               req = NULL;
+
+       if (local->iw_mode == IW_MODE_MASTER) {
+               /* In master mode, we just return the results of our local
+                * tables, so we don't need to start anything...
+                * Jean II */
+               data->length = 0;
+               return 0;
+       }
+
+       if (!local->dev_enabled)
+               return -ENETDOWN;
+
+       if (req && data->flags & IW_SCAN_THIS_ESSID) {
+               ssid = req->essid;
+               ssid_len = req->essid_len;
+
+               if (ssid_len &&
+                   ((local->iw_mode != IW_MODE_INFRA &&
+                     local->iw_mode != IW_MODE_ADHOC) ||
+                    (local->sta_fw_ver < PRISM2_FW_VER(1,3,1))))
+                       return -EOPNOTSUPP;
+       }
+
+       if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1))
+               ret = prism2_request_hostscan(dev, ssid, ssid_len);
+       else
+               ret = prism2_request_scan(dev);
+
+       if (ret == 0)
+               local->scan_timestamp = jiffies;
+
+       /* Could inquire F101, F103 or wait for SIOCGIWSCAN and read RID */
+
+       return ret;
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+static char * __prism2_translate_scan(local_info_t *local,
+                                     struct hfa384x_hostscan_result *scan,
+                                     struct hostap_bss_info *bss,
+                                     char *current_ev, char *end_buf)
+{
+       int i, chan;
+       struct iw_event iwe;
+       char *current_val;
+       u16 capabilities;
+       u8 *pos;
+       u8 *ssid, *bssid;
+       size_t ssid_len;
+       char *buf;
+
+       if (bss) {
+               ssid = bss->ssid;
+               ssid_len = bss->ssid_len;
+               bssid = bss->bssid;
+       } else {
+               ssid = scan->ssid;
+               ssid_len = le16_to_cpu(scan->ssid_len);
+               bssid = scan->bssid;
+       }
+       if (ssid_len > 32)
+               ssid_len = 32;
+
+       /* First entry *MUST* be the AP MAC address */
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWAP;
+       iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+       memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN);
+       /* FIX:
+        * I do not know how this is possible, but iwe_stream_add_event
+        * seems to re-order memcpy execution so that len is set only
+        * after copying.. Pre-setting len here "fixes" this, but real
+        * problems should be solved (after which these iwe.len
+        * settings could be removed from this function). */
+       iwe.len = IW_EV_ADDR_LEN;
+       current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                         IW_EV_ADDR_LEN);
+
+       /* Other entries will be displayed in the order we give them */
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWESSID;
+       iwe.u.data.length = ssid_len;
+       iwe.u.data.flags = 1;
+       iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+       current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid);
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWMODE;
+       if (bss) {
+               capabilities = bss->capab_info;
+       } else {
+               capabilities = le16_to_cpu(scan->capability);
+       }
+       if (capabilities & (WLAN_CAPABILITY_ESS |
+                           WLAN_CAPABILITY_IBSS)) {
+               if (capabilities & WLAN_CAPABILITY_ESS)
+                       iwe.u.mode = IW_MODE_MASTER;
+               else
+                       iwe.u.mode = IW_MODE_ADHOC;
+               iwe.len = IW_EV_UINT_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_UINT_LEN);
+       }
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWFREQ;
+       if (scan) {
+               chan = scan->chid;
+       } else if (bss) {
+               chan = bss->chan;
+       } else {
+               chan = 0;
+       }
+
+       if (chan > 0) {
+               iwe.u.freq.m = freq_list[le16_to_cpu(chan - 1)] * 100000;
+               iwe.u.freq.e = 1;
+               iwe.len = IW_EV_FREQ_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_FREQ_LEN);
+       }
+
+       if (scan) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVQUAL;
+               if (local->last_scan_type == PRISM2_HOSTSCAN) {
+                       iwe.u.qual.level = le16_to_cpu(scan->sl);
+                       iwe.u.qual.noise = le16_to_cpu(scan->anl);
+               } else {
+                       iwe.u.qual.level =
+                               HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->sl));
+                       iwe.u.qual.noise =
+                               HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->anl));
+               }
+               iwe.len = IW_EV_QUAL_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_QUAL_LEN);
+       }
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWENCODE;
+       if (capabilities & 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;
+       iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+       current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
+
+       /* TODO: add SuppRates into BSS table */
+       if (scan) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = SIOCGIWRATE;
+               current_val = current_ev + IW_EV_LCP_LEN;
+               pos = scan->sup_rates;
+               for (i = 0; i < sizeof(scan->sup_rates); i++) {
+                       if (pos[i] == 0)
+                               break;
+                       /* Bit rate given in 500 kb/s units (+ 0x80) */
+                       iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000);
+                       current_val = iwe_stream_add_value(
+                               current_ev, current_val, end_buf, &iwe,
+                               IW_EV_PARAM_LEN);
+               }
+               /* Check if we added any event */
+               if ((current_val - current_ev) > IW_EV_LCP_LEN)
+                       current_ev = current_val;
+       }
+
+       /* TODO: add BeaconInt,resp_rate,atim into BSS table */
+       buf = kmalloc(MAX_WPA_IE_LEN * 2 + 30, GFP_KERNEL);
+       if (buf && scan) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVCUSTOM;
+               sprintf(buf, "bcn_int=%d", le16_to_cpu(scan->beacon_interval));
+               iwe.u.data.length = strlen(buf);
+               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+                                                 buf);
+
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVCUSTOM;
+               sprintf(buf, "resp_rate=%d", le16_to_cpu(scan->rate));
+               iwe.u.data.length = strlen(buf);
+               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+                                                 buf);
+
+               if (local->last_scan_type == PRISM2_HOSTSCAN &&
+                   (capabilities & WLAN_CAPABILITY_IBSS)) {
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = IWEVCUSTOM;
+                       sprintf(buf, "atim=%d", le16_to_cpu(scan->atim));
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                                         &iwe, buf);
+               }
+       }
+       kfree(buf);
+
+       if (bss && bss->wpa_ie_len > 0 && bss->wpa_ie_len <= MAX_WPA_IE_LEN) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = bss->wpa_ie_len;
+               current_ev = iwe_stream_add_point(
+                       current_ev, end_buf, &iwe, bss->wpa_ie);
+       }
+
+       if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = bss->rsn_ie_len;
+               current_ev = iwe_stream_add_point(
+                       current_ev, end_buf, &iwe, bss->rsn_ie);
+       }
+
+       return current_ev;
+}
+
+
+/* Translate scan data returned from the card to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static inline int prism2_translate_scan(local_info_t *local,
+                                       char *buffer, int buflen)
+{
+       struct hfa384x_hostscan_result *scan;
+       int entry, hostscan;
+       char *current_ev = buffer;
+       char *end_buf = buffer + buflen;
+       struct list_head *ptr;
+
+       spin_lock_bh(&local->lock);
+
+       list_for_each(ptr, &local->bss_list) {
+               struct hostap_bss_info *bss;
+               bss = list_entry(ptr, struct hostap_bss_info, list);
+               bss->included = 0;
+       }
+
+       hostscan = local->last_scan_type == PRISM2_HOSTSCAN;
+       for (entry = 0; entry < local->last_scan_results_count; entry++) {
+               int found = 0;
+               scan = &local->last_scan_results[entry];
+
+               /* Report every SSID if the AP is using multiple SSIDs. If no
+                * BSS record is found (e.g., when WPA mode is disabled),
+                * report the AP once. */
+               list_for_each(ptr, &local->bss_list) {
+                       struct hostap_bss_info *bss;
+                       bss = list_entry(ptr, struct hostap_bss_info, list);
+                       if (memcmp(bss->bssid, scan->bssid, ETH_ALEN) == 0) {
+                               bss->included = 1;
+                               current_ev = __prism2_translate_scan(
+                                       local, scan, bss, current_ev, end_buf);
+                               found++;
+                       }
+               }
+               if (!found) {
+                       current_ev = __prism2_translate_scan(
+                               local, scan, NULL, current_ev, end_buf);
+               }
+               /* Check if there is space for one more entry */
+               if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) {
+                       /* Ask user space to try again with a bigger buffer */
+                       spin_unlock_bh(&local->lock);
+                       return -E2BIG;
+               }
+       }
+
+       /* Prism2 firmware has limits (32 at least in some versions) for number
+        * of BSSes in scan results. Extend this limit by using local BSS list.
+        */
+       list_for_each(ptr, &local->bss_list) {
+               struct hostap_bss_info *bss;
+               bss = list_entry(ptr, struct hostap_bss_info, list);
+               if (bss->included)
+                       continue;
+               current_ev = __prism2_translate_scan(local, NULL, bss,
+                                                    current_ev, end_buf);
+               /* Check if there is space for one more entry */
+               if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) {
+                       /* Ask user space to try again with a bigger buffer */
+                       spin_unlock_bh(&local->lock);
+                       return -E2BIG;
+               }
+       }
+
+       spin_unlock_bh(&local->lock);
+
+       return current_ev - buffer;
+}
+#endif /* PRISM2_NO_STATION_MODES */
+
+
+static inline int prism2_ioctl_giwscan_sta(struct net_device *dev,
+                                          struct iw_request_info *info,
+                                          struct iw_point *data, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+       return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int res;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       /* Wait until the scan is finished. We can probably do better
+        * than that - Jean II */
+       if (local->scan_timestamp &&
+           time_before(jiffies, local->scan_timestamp + 3 * HZ)) {
+               /* Important note : we don't want to block the caller
+                * until results are ready for various reasons.
+                * First, managing wait queues is complex and racy
+                * (there may be multiple simultaneous callers).
+                * Second, we grab some rtnetlink lock before comming
+                * here (in dev_ioctl()).
+                * Third, the caller can wait on the Wireless Event
+                * - Jean II */
+               return -EAGAIN;
+       }
+       local->scan_timestamp = 0;
+
+       res = prism2_translate_scan(local, extra, data->length);
+
+       if (res >= 0) {
+               data->length = res;
+               return 0;
+       } else {
+               data->length = 0;
+               return res;
+       }
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_giwscan(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_point *data, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int res;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->iw_mode == IW_MODE_MASTER) {
+               /* In MASTER mode, it doesn't make sense to go around
+                * scanning the frequencies and make the stations we serve
+                * wait when what the user is really interested about is the
+                * list of stations and access points we are talking to.
+                * So, just extract results from our cache...
+                * Jean II */
+
+               /* Translate to WE format */
+               res = prism2_ap_translate_scan(dev, extra);
+               if (res >= 0) {
+                       printk(KERN_DEBUG "Scan result translation succeeded "
+                              "(length=%d)\n", res);
+                       data->length = res;
+                       return 0;
+               } else {
+                       printk(KERN_DEBUG
+                              "Scan result translation failed (res=%d)\n",
+                              res);
+                       data->length = 0;
+                       return res;
+               }
+       } else {
+               /* Station mode */
+               return prism2_ioctl_giwscan_sta(dev, info, data, extra);
+       }
+}
+
+
+static const struct iw_priv_args prism2_priv[] = {
+       { PRISM2_IOCTL_MONITOR,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor" },
+       { PRISM2_IOCTL_READMIF,
+         IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+         IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "readmif" },
+       { PRISM2_IOCTL_WRITEMIF,
+         IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, 0, "writemif" },
+       { PRISM2_IOCTL_RESET,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" },
+       { PRISM2_IOCTL_INQUIRE,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inquire" },
+       { PRISM2_IOCTL_SET_RID_WORD,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_rid_word" },
+       { PRISM2_IOCTL_MACCMD,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" },
+       { PRISM2_IOCTL_WDS_ADD,
+         IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_add" },
+       { PRISM2_IOCTL_WDS_DEL,
+         IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_del" },
+       { PRISM2_IOCTL_ADDMAC,
+         IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "addmac" },
+       { PRISM2_IOCTL_DELMAC,
+         IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "delmac" },
+       { PRISM2_IOCTL_KICKMAC,
+         IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac" },
+       /* --- raw access to sub-ioctls --- */
+       { PRISM2_IOCTL_PRISM2_PARAM,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "prism2_param" },
+       { PRISM2_IOCTL_GET_PRISM2_PARAM,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprism2_param" },
+       /* --- sub-ioctls handlers --- */
+       { PRISM2_IOCTL_PRISM2_PARAM,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" },
+       { PRISM2_IOCTL_GET_PRISM2_PARAM,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" },
+       /* --- sub-ioctls definitions --- */
+       { PRISM2_PARAM_TXRATECTRL,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txratectrl" },
+       { PRISM2_PARAM_TXRATECTRL,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettxratectrl" },
+       { PRISM2_PARAM_BEACON_INT,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_int" },
+       { PRISM2_PARAM_BEACON_INT,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbeacon_int" },
+#ifndef PRISM2_NO_STATION_MODES
+       { PRISM2_PARAM_PSEUDO_IBSS,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pseudo_ibss" },
+       { PRISM2_PARAM_PSEUDO_IBSS,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getpseudo_ibss" },
+#endif /* PRISM2_NO_STATION_MODES */
+       { PRISM2_PARAM_ALC,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" },
+       { PRISM2_PARAM_ALC,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getalc" },
+       { PRISM2_PARAM_DUMP,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" },
+       { PRISM2_PARAM_DUMP,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdump" },
+       { PRISM2_PARAM_OTHER_AP_POLICY,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "other_ap_policy" },
+       { PRISM2_PARAM_OTHER_AP_POLICY,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getother_ap_pol" },
+       { PRISM2_PARAM_AP_MAX_INACTIVITY,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_inactivity" },
+       { PRISM2_PARAM_AP_MAX_INACTIVITY,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_inactivi" },
+       { PRISM2_PARAM_AP_BRIDGE_PACKETS,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bridge_packets" },
+       { PRISM2_PARAM_AP_BRIDGE_PACKETS,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbridge_packe" },
+       { PRISM2_PARAM_DTIM_PERIOD,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" },
+       { PRISM2_PARAM_DTIM_PERIOD,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdtim_period" },
+       { PRISM2_PARAM_AP_NULLFUNC_ACK,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nullfunc_ack" },
+       { PRISM2_PARAM_AP_NULLFUNC_ACK,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getnullfunc_ack" },
+       { PRISM2_PARAM_MAX_WDS,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_wds" },
+       { PRISM2_PARAM_MAX_WDS,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_wds" },
+       { PRISM2_PARAM_AP_AUTOM_AP_WDS,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autom_ap_wds" },
+       { PRISM2_PARAM_AP_AUTOM_AP_WDS,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getautom_ap_wds" },
+       { PRISM2_PARAM_AP_AUTH_ALGS,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_auth_algs" },
+       { PRISM2_PARAM_AP_AUTH_ALGS,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_auth_algs" },
+       { PRISM2_PARAM_MONITOR_ALLOW_FCSERR,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "allow_fcserr" },
+       { PRISM2_PARAM_MONITOR_ALLOW_FCSERR,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getallow_fcserr" },
+       { PRISM2_PARAM_HOST_ENCRYPT,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_encrypt" },
+       { PRISM2_PARAM_HOST_ENCRYPT,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_encrypt" },
+       { PRISM2_PARAM_HOST_DECRYPT,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_decrypt" },
+       { PRISM2_PARAM_HOST_DECRYPT,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_decrypt" },
+#ifndef PRISM2_NO_STATION_MODES
+       { PRISM2_PARAM_HOST_ROAMING,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_roaming" },
+       { PRISM2_PARAM_HOST_ROAMING,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_roaming" },
+#endif /* PRISM2_NO_STATION_MODES */
+       { PRISM2_PARAM_BCRX_STA_KEY,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcrx_sta_key" },
+       { PRISM2_PARAM_BCRX_STA_KEY,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbcrx_sta_key" },
+       { PRISM2_PARAM_IEEE_802_1X,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ieee_802_1x" },
+       { PRISM2_PARAM_IEEE_802_1X,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getieee_802_1x" },
+       { PRISM2_PARAM_ANTSEL_TX,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_tx" },
+       { PRISM2_PARAM_ANTSEL_TX,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_tx" },
+       { PRISM2_PARAM_ANTSEL_RX,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_rx" },
+       { PRISM2_PARAM_ANTSEL_RX,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_rx" },
+       { PRISM2_PARAM_MONITOR_TYPE,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor_type" },
+       { PRISM2_PARAM_MONITOR_TYPE,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmonitor_type" },
+       { PRISM2_PARAM_WDS_TYPE,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds_type" },
+       { PRISM2_PARAM_WDS_TYPE,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwds_type" },
+       { PRISM2_PARAM_HOSTSCAN,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostscan" },
+       { PRISM2_PARAM_HOSTSCAN,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostscan" },
+       { PRISM2_PARAM_AP_SCAN,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_scan" },
+       { PRISM2_PARAM_AP_SCAN,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_scan" },
+       { PRISM2_PARAM_ENH_SEC,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enh_sec" },
+       { PRISM2_PARAM_ENH_SEC,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getenh_sec" },
+#ifdef PRISM2_IO_DEBUG
+       { PRISM2_PARAM_IO_DEBUG,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "io_debug" },
+       { PRISM2_PARAM_IO_DEBUG,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getio_debug" },
+#endif /* PRISM2_IO_DEBUG */
+       { PRISM2_PARAM_BASIC_RATES,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "basic_rates" },
+       { PRISM2_PARAM_BASIC_RATES,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbasic_rates" },
+       { PRISM2_PARAM_OPER_RATES,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oper_rates" },
+       { PRISM2_PARAM_OPER_RATES,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getoper_rates" },
+       { PRISM2_PARAM_HOSTAPD,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd" },
+       { PRISM2_PARAM_HOSTAPD,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd" },
+       { PRISM2_PARAM_HOSTAPD_STA,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd_sta" },
+       { PRISM2_PARAM_HOSTAPD_STA,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd_sta" },
+       { PRISM2_PARAM_WPA,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" },
+       { PRISM2_PARAM_WPA,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwpa" },
+       { PRISM2_PARAM_PRIVACY_INVOKED,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy_invoked" },
+       { PRISM2_PARAM_PRIVACY_INVOKED,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprivacy_invo" },
+       { PRISM2_PARAM_TKIP_COUNTERMEASURES,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tkip_countermea" },
+       { PRISM2_PARAM_TKIP_COUNTERMEASURES,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettkip_counter" },
+       { PRISM2_PARAM_DROP_UNENCRYPTED,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "drop_unencrypte" },
+       { PRISM2_PARAM_DROP_UNENCRYPTED,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdrop_unencry" },
+       { PRISM2_PARAM_SCAN_CHANNEL_MASK,
+         IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_channels" },
+       { PRISM2_PARAM_SCAN_CHANNEL_MASK,
+         0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getscan_channel" },
+};
+
+
+static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->cmd(dev, HFA384X_CMDCODE_INQUIRE, *i, NULL, NULL))
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+
+static int prism2_ioctl_priv_prism2_param(struct net_device *dev,
+                                         struct iw_request_info *info,
+                                         void *wrqu, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int *i = (int *) extra;
+       int param = *i;
+       int value = *(i + 1);
+       int ret = 0;
+       u16 val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       switch (param) {
+       case PRISM2_PARAM_TXRATECTRL:
+               local->fw_tx_rate_control = value;
+               break;
+
+       case PRISM2_PARAM_BEACON_INT:
+               if (hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, value) ||
+                   local->func->reset_port(dev))
+                       ret = -EINVAL;
+               else
+                       local->beacon_int = value;
+               break;
+
+#ifndef PRISM2_NO_STATION_MODES
+       case PRISM2_PARAM_PSEUDO_IBSS:
+               if (value == local->pseudo_adhoc)
+                       break;
+
+               if (value != 0 && value != 1) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               printk(KERN_DEBUG "prism2: %s: pseudo IBSS change %d -> %d\n",
+                      dev->name, local->pseudo_adhoc, value);
+               local->pseudo_adhoc = value;
+               if (local->iw_mode != IW_MODE_ADHOC)
+                       break;
+
+               if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+                                   hostap_get_porttype(local))) {
+                       ret = -EOPNOTSUPP;
+                       break;
+               }
+
+               if (local->func->reset_port(dev))
+                       ret = -EINVAL;
+               break;
+#endif /* PRISM2_NO_STATION_MODES */
+
+       case PRISM2_PARAM_ALC:
+               printk(KERN_DEBUG "%s: %s ALC\n", dev->name,
+                      value == 0 ? "Disabling" : "Enabling");
+               val = HFA384X_TEST_CFG_BIT_ALC;
+               local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+                                (HFA384X_TEST_CFG_BITS << 8),
+                                value == 0 ? 0 : 1, &val, NULL);
+               break;
+
+       case PRISM2_PARAM_DUMP:
+               local->frame_dump = value;
+               break;
+
+       case PRISM2_PARAM_OTHER_AP_POLICY:
+               if (value < 0 || value > 3) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (local->ap != NULL)
+                       local->ap->ap_policy = value;
+               break;
+
+       case PRISM2_PARAM_AP_MAX_INACTIVITY:
+               if (value < 0 || value > 7 * 24 * 60 * 60) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (local->ap != NULL)
+                       local->ap->max_inactivity = value * HZ;
+               break;
+
+       case PRISM2_PARAM_AP_BRIDGE_PACKETS:
+               if (local->ap != NULL)
+                       local->ap->bridge_packets = value;
+               break;
+
+       case PRISM2_PARAM_DTIM_PERIOD:
+               if (value < 0 || value > 65535) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, value)
+                   || local->func->reset_port(dev))
+                       ret = -EINVAL;
+               else
+                       local->dtim_period = value;
+               break;
+
+       case PRISM2_PARAM_AP_NULLFUNC_ACK:
+               if (local->ap != NULL)
+                       local->ap->nullfunc_ack = value;
+               break;
+
+       case PRISM2_PARAM_MAX_WDS:
+               local->wds_max_connections = value;
+               break;
+
+       case PRISM2_PARAM_AP_AUTOM_AP_WDS:
+               if (local->ap != NULL) {
+                       if (!local->ap->autom_ap_wds && value) {
+                               /* add WDS link to all APs in STA table */
+                               hostap_add_wds_links(local);
+                       }
+                       local->ap->autom_ap_wds = value;
+               }
+               break;
+
+       case PRISM2_PARAM_AP_AUTH_ALGS:
+               local->auth_algs = value;
+               if (hostap_set_auth_algs(local))
+                       ret = -EINVAL;
+               break;
+
+       case PRISM2_PARAM_MONITOR_ALLOW_FCSERR:
+               local->monitor_allow_fcserr = value;
+               break;
+
+       case PRISM2_PARAM_HOST_ENCRYPT:
+               local->host_encrypt = value;
+               if (hostap_set_encryption(local) ||
+                   local->func->reset_port(dev))
+                       ret = -EINVAL;
+               break;
+
+       case PRISM2_PARAM_HOST_DECRYPT:
+               local->host_decrypt = value;
+               if (hostap_set_encryption(local) ||
+                   local->func->reset_port(dev))
+                       ret = -EINVAL;
+               break;
+
+#ifndef PRISM2_NO_STATION_MODES
+       case PRISM2_PARAM_HOST_ROAMING:
+               if (value < 0 || value > 2) {
+                       ret = -EINVAL;
+                       break;
+               }
+               local->host_roaming = value;
+               if (hostap_set_roaming(local) || local->func->reset_port(dev))
+                       ret = -EINVAL;
+               break;
+#endif /* PRISM2_NO_STATION_MODES */
+
+       case PRISM2_PARAM_BCRX_STA_KEY:
+               local->bcrx_sta_key = value;
+               break;
+
+       case PRISM2_PARAM_IEEE_802_1X:
+               local->ieee_802_1x = value;
+               break;
+
+       case PRISM2_PARAM_ANTSEL_TX:
+               if (value < 0 || value > HOSTAP_ANTSEL_HIGH) {
+                       ret = -EINVAL;
+                       break;
+               }
+               local->antsel_tx = value;
+               hostap_set_antsel(local);
+               break;
+
+       case PRISM2_PARAM_ANTSEL_RX:
+               if (value < 0 || value > HOSTAP_ANTSEL_HIGH) {
+                       ret = -EINVAL;
+                       break;
+               }
+               local->antsel_rx = value;
+               hostap_set_antsel(local);
+               break;
+
+       case PRISM2_PARAM_MONITOR_TYPE:
+               if (value != PRISM2_MONITOR_80211 &&
+                   value != PRISM2_MONITOR_CAPHDR &&
+                   value != PRISM2_MONITOR_PRISM) {
+                       ret = -EINVAL;
+                       break;
+               }
+               local->monitor_type = value;
+               if (local->iw_mode == IW_MODE_MONITOR)
+                       hostap_monitor_set_type(local);
+               break;
+
+       case PRISM2_PARAM_WDS_TYPE:
+               local->wds_type = value;
+               break;
+
+       case PRISM2_PARAM_HOSTSCAN:
+       {
+               struct hfa384x_hostscan_request scan_req;
+               u16 rate;
+
+               memset(&scan_req, 0, sizeof(scan_req));
+               scan_req.channel_list = __constant_cpu_to_le16(0x3fff);
+               switch (value) {
+               case 1: rate = HFA384X_RATES_1MBPS; break;
+               case 2: rate = HFA384X_RATES_2MBPS; break;
+               case 3: rate = HFA384X_RATES_5MBPS; break;
+               case 4: rate = HFA384X_RATES_11MBPS; break;
+               default: rate = HFA384X_RATES_1MBPS; break;
+               }
+               scan_req.txrate = cpu_to_le16(rate);
+               /* leave SSID empty to accept all SSIDs */
+
+               if (local->iw_mode == IW_MODE_MASTER) {
+                       if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+                                           HFA384X_PORTTYPE_BSS) ||
+                           local->func->reset_port(dev))
+                               printk(KERN_DEBUG "Leaving Host AP mode "
+                                      "for HostScan failed\n");
+               }
+
+               if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req,
+                                        sizeof(scan_req))) {
+                       printk(KERN_DEBUG "HOSTSCAN failed\n");
+                       ret = -EINVAL;
+               }
+               if (local->iw_mode == IW_MODE_MASTER) {
+                       wait_queue_t __wait;
+                       init_waitqueue_entry(&__wait, current);
+                       add_wait_queue(&local->hostscan_wq, &__wait);
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(HZ);
+                       if (signal_pending(current))
+                               ret = -EINTR;
+                       set_current_state(TASK_RUNNING);
+                       remove_wait_queue(&local->hostscan_wq, &__wait);
+
+                       if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+                                           HFA384X_PORTTYPE_HOSTAP) ||
+                           local->func->reset_port(dev))
+                               printk(KERN_DEBUG "Returning to Host AP mode "
+                                      "after HostScan failed\n");
+               }
+               break;
+       }
+
+       case PRISM2_PARAM_AP_SCAN:
+               local->passive_scan_interval = value;
+               if (timer_pending(&local->passive_scan_timer))
+                       del_timer(&local->passive_scan_timer);
+               if (value > 0) {
+                       local->passive_scan_timer.expires = jiffies +
+                               local->passive_scan_interval * HZ;
+                       add_timer(&local->passive_scan_timer);
+               }
+               break;
+
+       case PRISM2_PARAM_ENH_SEC:
+               if (value < 0 || value > 3) {
+                       ret = -EINVAL;
+                       break;
+               }
+               local->enh_sec = value;
+               if (hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY,
+                                   local->enh_sec) ||
+                   local->func->reset_port(dev)) {
+                       printk(KERN_INFO "%s: cnfEnhSecurity requires STA f/w "
+                              "1.6.3 or newer\n", dev->name);
+                       ret = -EOPNOTSUPP;
+               }
+               break;
+
+#ifdef PRISM2_IO_DEBUG
+       case PRISM2_PARAM_IO_DEBUG:
+               local->io_debug_enabled = value;
+               break;
+#endif /* PRISM2_IO_DEBUG */
+
+       case PRISM2_PARAM_BASIC_RATES:
+               if ((value & local->tx_rate_control) != value || value == 0) {
+                       printk(KERN_INFO "%s: invalid basic rate set - basic "
+                              "rates must be in supported rate set\n",
+                              dev->name);
+                       ret = -EINVAL;
+                       break;
+               }
+               local->basic_rates = value;
+               if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES,
+                                   local->basic_rates) ||
+                   local->func->reset_port(dev))
+                       ret = -EINVAL;
+               break;
+
+       case PRISM2_PARAM_OPER_RATES:
+               local->tx_rate_control = value;
+               if (hostap_set_rate(dev))
+                       ret = -EINVAL;
+               break;
+
+       case PRISM2_PARAM_HOSTAPD:
+               ret = hostap_set_hostapd(local, value, 1);
+               break;
+
+       case PRISM2_PARAM_HOSTAPD_STA:
+               ret = hostap_set_hostapd_sta(local, value, 1);
+               break;
+
+       case PRISM2_PARAM_WPA:
+               local->wpa = value;
+               if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+                       ret = -EOPNOTSUPP;
+               else if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE,
+                                        value ? 1 : 0))
+                       ret = -EINVAL;
+               break;
+
+       case PRISM2_PARAM_PRIVACY_INVOKED:
+               local->privacy_invoked = value;
+               if (hostap_set_encryption(local) ||
+                   local->func->reset_port(dev))
+                       ret = -EINVAL;
+               break;
+
+       case PRISM2_PARAM_TKIP_COUNTERMEASURES:
+               local->tkip_countermeasures = value;
+               break;
+
+       case PRISM2_PARAM_DROP_UNENCRYPTED:
+               local->drop_unencrypted = value;
+               break;
+
+       case PRISM2_PARAM_SCAN_CHANNEL_MASK:
+               local->scan_channel_mask = value;
+               break;
+
+       default:
+               printk(KERN_DEBUG "%s: prism2_param: unknown param %d\n",
+                      dev->name, param);
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
+
+
+static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev,
+                                             struct iw_request_info *info,
+                                             void *wrqu, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int *param = (int *) extra;
+       int ret = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       switch (*param) {
+       case PRISM2_PARAM_TXRATECTRL:
+               *param = local->fw_tx_rate_control;
+               break;
+
+       case PRISM2_PARAM_BEACON_INT:
+               *param = local->beacon_int;
+               break;
+
+       case PRISM2_PARAM_PSEUDO_IBSS:
+               *param = local->pseudo_adhoc;
+               break;
+
+       case PRISM2_PARAM_ALC:
+               ret = -EOPNOTSUPP; /* FIX */
+               break;
+
+       case PRISM2_PARAM_DUMP:
+               *param = local->frame_dump;
+               break;
+
+       case PRISM2_PARAM_OTHER_AP_POLICY:
+               if (local->ap != NULL)
+                       *param = local->ap->ap_policy;
+               else
+                       ret = -EOPNOTSUPP;
+               break;
+
+       case PRISM2_PARAM_AP_MAX_INACTIVITY:
+               if (local->ap != NULL)
+                       *param = local->ap->max_inactivity / HZ;
+               else
+                       ret = -EOPNOTSUPP;
+               break;
+
+       case PRISM2_PARAM_AP_BRIDGE_PACKETS:
+               if (local->ap != NULL)
+                       *param = local->ap->bridge_packets;
+               else
+                       ret = -EOPNOTSUPP;
+               break;
+
+       case PRISM2_PARAM_DTIM_PERIOD:
+               *param = local->dtim_period;
+               break;
+
+       case PRISM2_PARAM_AP_NULLFUNC_ACK:
+               if (local->ap != NULL)
+                       *param = local->ap->nullfunc_ack;
+               else
+                       ret = -EOPNOTSUPP;
+               break;
+
+       case PRISM2_PARAM_MAX_WDS:
+               *param = local->wds_max_connections;
+               break;
+
+       case PRISM2_PARAM_AP_AUTOM_AP_WDS:
+               if (local->ap != NULL)
+                       *param = local->ap->autom_ap_wds;
+               else
+                       ret = -EOPNOTSUPP;
+               break;
+
+       case PRISM2_PARAM_AP_AUTH_ALGS:
+               *param = local->auth_algs;
+               break;
+
+       case PRISM2_PARAM_MONITOR_ALLOW_FCSERR:
+               *param = local->monitor_allow_fcserr;
+               break;
+
+       case PRISM2_PARAM_HOST_ENCRYPT:
+               *param = local->host_encrypt;
+               break;
+
+       case PRISM2_PARAM_HOST_DECRYPT:
+               *param = local->host_decrypt;
+               break;
+
+       case PRISM2_PARAM_HOST_ROAMING:
+               *param = local->host_roaming;
+               break;
+
+       case PRISM2_PARAM_BCRX_STA_KEY:
+               *param = local->bcrx_sta_key;
+               break;
+
+       case PRISM2_PARAM_IEEE_802_1X:
+               *param = local->ieee_802_1x;
+               break;
+
+       case PRISM2_PARAM_ANTSEL_TX:
+               *param = local->antsel_tx;
+               break;
+
+       case PRISM2_PARAM_ANTSEL_RX:
+               *param = local->antsel_rx;
+               break;
+
+       case PRISM2_PARAM_MONITOR_TYPE:
+               *param = local->monitor_type;
+               break;
+
+       case PRISM2_PARAM_WDS_TYPE:
+               *param = local->wds_type;
+               break;
+
+       case PRISM2_PARAM_HOSTSCAN:
+               ret = -EOPNOTSUPP;
+               break;
+
+       case PRISM2_PARAM_AP_SCAN:
+               *param = local->passive_scan_interval;
+               break;
+
+       case PRISM2_PARAM_ENH_SEC:
+               *param = local->enh_sec;
+               break;
+
+#ifdef PRISM2_IO_DEBUG
+       case PRISM2_PARAM_IO_DEBUG:
+               *param = local->io_debug_enabled;
+               break;
+#endif /* PRISM2_IO_DEBUG */
+
+       case PRISM2_PARAM_BASIC_RATES:
+               *param = local->basic_rates;
+               break;
+
+       case PRISM2_PARAM_OPER_RATES:
+               *param = local->tx_rate_control;
+               break;
+
+       case PRISM2_PARAM_HOSTAPD:
+               *param = local->hostapd;
+               break;
+
+       case PRISM2_PARAM_HOSTAPD_STA:
+               *param = local->hostapd_sta;
+               break;
+
+       case PRISM2_PARAM_WPA:
+               if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+                       ret = -EOPNOTSUPP;
+               *param = local->wpa;
+               break;
+
+       case PRISM2_PARAM_PRIVACY_INVOKED:
+               *param = local->privacy_invoked;
+               break;
+
+       case PRISM2_PARAM_TKIP_COUNTERMEASURES:
+               *param = local->tkip_countermeasures;
+               break;
+
+       case PRISM2_PARAM_DROP_UNENCRYPTED:
+               *param = local->drop_unencrypted;
+               break;
+
+       case PRISM2_PARAM_SCAN_CHANNEL_MASK:
+               *param = local->scan_channel_mask;
+               break;
+
+       default:
+               printk(KERN_DEBUG "%s: get_prism2_param: unknown param %d\n",
+                      dev->name, *param);
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
+
+
+static int prism2_ioctl_priv_readmif(struct net_device *dev,
+                                    struct iw_request_info *info,
+                                    void *wrqu, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 resp0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, *extra, NULL,
+                            &resp0))
+               return -EOPNOTSUPP;
+       else
+               *extra = resp0;
+
+       return 0;
+}
+
+
+static int prism2_ioctl_priv_writemif(struct net_device *dev,
+                                     struct iw_request_info *info,
+                                     void *wrqu, char *extra)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 cr, val;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       cr = *extra;
+       val = *(extra + 1);
+       if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, cr, &val, NULL))
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+
+static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 0;
+       u32 mode;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       printk(KERN_DEBUG "%s: process %d (%s) used deprecated iwpriv monitor "
+              "- update software to use iwconfig mode monitor\n",
+              dev->name, current->pid, current->comm);
+
+       /* Backward compatibility code - this can be removed at some point */
+
+       if (*i == 0) {
+               /* Disable monitor mode - old mode was not saved, so go to
+                * Master mode */
+               mode = IW_MODE_MASTER;
+               ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL);
+       } else if (*i == 1) {
+               /* netlink socket mode is not supported anymore since it did
+                * not separate different devices from each other and was not
+                * best method for delivering large amount of packets to
+                * user space */
+               ret = -EOPNOTSUPP;
+       } else if (*i == 2 || *i == 3) {
+               switch (*i) {
+               case 2:
+                       local->monitor_type = PRISM2_MONITOR_80211;
+                       break;
+               case 3:
+                       local->monitor_type = PRISM2_MONITOR_PRISM;
+                       break;
+               }
+               mode = IW_MODE_MONITOR;
+               ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL);
+               hostap_monitor_mode_enable(local);
+       } else
+               ret = -EINVAL;
+
+       return ret;
+}
+
+
+static int prism2_ioctl_priv_reset(struct net_device *dev, int *i)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       printk(KERN_DEBUG "%s: manual reset request(%d)\n", dev->name, *i);
+       switch (*i) {
+       case 0:
+               /* Disable and enable card */
+               local->func->hw_shutdown(dev, 1);
+               local->func->hw_config(dev, 0);
+               break;
+
+       case 1:
+               /* COR sreset */
+               local->func->hw_reset(dev);
+               break;
+
+       case 2:
+               /* Disable and enable port 0 */
+               local->func->reset_port(dev);
+               break;
+
+       case 3:
+               prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING);
+               if (local->func->cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL,
+                                    NULL))
+                       return -EINVAL;
+               break;
+
+       case 4:
+               if (local->func->cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL,
+                                    NULL))
+                       return -EINVAL;
+               break;
+
+       default:
+               printk(KERN_DEBUG "Unknown reset request %d\n", *i);
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+
+static int prism2_ioctl_priv_set_rid_word(struct net_device *dev, int *i)
+{
+       int rid = *i;
+       int value = *(i + 1);
+
+       printk(KERN_DEBUG "%s: Set RID[0x%X] = %d\n", dev->name, rid, value);
+
+       if (hostap_set_word(dev, rid, value))
+               return -EINVAL;
+
+       return 0;
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static int ap_mac_cmd_ioctl(local_info_t *local, int *cmd)
+{
+       int ret = 0;
+
+       switch (*cmd) {
+       case AP_MAC_CMD_POLICY_OPEN:
+               local->ap->mac_restrictions.policy = MAC_POLICY_OPEN;
+               break;
+       case AP_MAC_CMD_POLICY_ALLOW:
+               local->ap->mac_restrictions.policy = MAC_POLICY_ALLOW;
+               break;
+       case AP_MAC_CMD_POLICY_DENY:
+               local->ap->mac_restrictions.policy = MAC_POLICY_DENY;
+               break;
+       case AP_MAC_CMD_FLUSH:
+               ap_control_flush_macs(&local->ap->mac_restrictions);
+               break;
+       case AP_MAC_CMD_KICKALL:
+               ap_control_kickall(local->ap);
+               hostap_deauth_all_stas(local->dev, local->ap, 0);
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+static int prism2_ioctl_priv_download(local_info_t *local, struct iw_point *p)
+{
+       struct prism2_download_param *param;
+       int ret = 0;
+
+       if (p->length < sizeof(struct prism2_download_param) ||
+           p->length > 1024 || !p->pointer)
+               return -EINVAL;
+
+       param = (struct prism2_download_param *)
+               kmalloc(p->length, GFP_KERNEL);
+       if (param == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(param, p->pointer, p->length)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       if (p->length < sizeof(struct prism2_download_param) +
+           param->num_areas * sizeof(struct prism2_download_area)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = local->func->download(local, param);
+
+ out:
+       if (param != NULL)
+               kfree(param);
+
+       return ret;
+}
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+
+static int prism2_set_genericelement(struct net_device *dev, u8 *elem,
+                                    size_t len)
+{
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+       u8 *buf;
+
+       /*
+        * Add 16-bit length in the beginning of the buffer because Prism2 RID
+        * includes it.
+        */
+       buf = kmalloc(len + 2, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       *((u16 *) buf) = cpu_to_le16(len);
+       memcpy(buf + 2, elem, len);
+
+       kfree(local->generic_elem);
+       local->generic_elem = buf;
+       local->generic_elem_len = len + 2;
+
+       return local->func->set_rid(local->dev, HFA384X_RID_GENERICELEMENT,
+                                   buf, len + 2);
+}
+
+
+static int prism2_ioctl_siwauth(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *data, char *extra)
+{
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+
+       switch (data->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_WPA_VERSION:
+       case IW_AUTH_CIPHER_PAIRWISE:
+       case IW_AUTH_CIPHER_GROUP:
+       case IW_AUTH_KEY_MGMT:
+               /*
+                * Host AP driver does not use these parameters and allows
+                * wpa_supplicant to control them internally.
+                */
+               break;
+       case IW_AUTH_TKIP_COUNTERMEASURES:
+               local->tkip_countermeasures = data->value;
+               break;
+       case IW_AUTH_DROP_UNENCRYPTED:
+               local->drop_unencrypted = data->value;
+               break;
+       case IW_AUTH_80211_AUTH_ALG:
+               local->auth_algs = data->value;
+               break;
+       case IW_AUTH_WPA_ENABLED:
+               if (data->value == 0) {
+                       local->wpa = 0;
+                       if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+                               break;
+                       prism2_set_genericelement(dev, "", 0);
+                       local->host_roaming = 0;
+                       local->privacy_invoked = 0;
+                       if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE,
+                                           0) ||
+                           hostap_set_roaming(local) ||
+                           hostap_set_encryption(local) ||
+                           local->func->reset_port(dev))
+                               return -EINVAL;
+                       break;
+               }
+               if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+                       return -EOPNOTSUPP;
+               local->host_roaming = 2;
+               local->privacy_invoked = 1;
+               local->wpa = 1;
+               if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1) ||
+                   hostap_set_roaming(local) ||
+                   hostap_set_encryption(local) ||
+                   local->func->reset_port(dev))
+                       return -EINVAL;
+               break;
+       case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+               local->ieee_802_1x = data->value;
+               break;
+       case IW_AUTH_PRIVACY_INVOKED:
+               local->privacy_invoked = data->value;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+
+static int prism2_ioctl_giwauth(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_param *data, char *extra)
+{
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+
+       switch (data->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_WPA_VERSION:
+       case IW_AUTH_CIPHER_PAIRWISE:
+       case IW_AUTH_CIPHER_GROUP:
+       case IW_AUTH_KEY_MGMT:
+               /*
+                * Host AP driver does not use these parameters and allows
+                * wpa_supplicant to control them internally.
+                */
+               return -EOPNOTSUPP;
+       case IW_AUTH_TKIP_COUNTERMEASURES:
+               data->value = local->tkip_countermeasures;
+               break;
+       case IW_AUTH_DROP_UNENCRYPTED:
+               data->value = local->drop_unencrypted;
+               break;
+       case IW_AUTH_80211_AUTH_ALG:
+               data->value = local->auth_algs;
+               break;
+       case IW_AUTH_WPA_ENABLED:
+               data->value = local->wpa;
+               break;
+       case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+               data->value = local->ieee_802_1x;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+
+static int prism2_ioctl_siwencodeext(struct net_device *dev,
+                                    struct iw_request_info *info,
+                                    struct iw_point *erq, char *extra)
+{
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
+       int i, ret = 0;
+       struct ieee80211_crypto_ops *ops;
+       struct ieee80211_crypt_data **crypt;
+       void *sta_ptr;
+       u8 *addr;
+       const char *alg, *module;
+
+       i = erq->flags & IW_ENCODE_INDEX;
+       if (i > WEP_KEYS)
+               return -EINVAL;
+       if (i < 1 || i > WEP_KEYS)
+               i = local->tx_keyidx;
+       else
+               i--;
+       if (i < 0 || i >= WEP_KEYS)
+               return -EINVAL;
+
+       addr = ext->addr.sa_data;
+       if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
+           addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
+               sta_ptr = NULL;
+               crypt = &local->crypt[i];
+       } else {
+               if (i != 0)
+                       return -EINVAL;
+               sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt);
+               if (sta_ptr == NULL) {
+                       if (local->iw_mode == IW_MODE_INFRA) {
+                               /*
+                                * TODO: add STA entry for the current AP so
+                                * that unicast key can be used. For now, this
+                                * is emulated by using default key idx 0.
+                                */
+                               i = 0;
+                               crypt = &local->crypt[i];
+                       } else
+                               return -EINVAL;
+               }
+       }
+
+       if ((erq->flags & IW_ENCODE_DISABLED) ||
+           ext->alg == IW_ENCODE_ALG_NONE) {
+               if (*crypt)
+                       prism2_crypt_delayed_deinit(local, crypt);
+               goto done;
+       }
+
+       switch (ext->alg) {
+       case IW_ENCODE_ALG_WEP:
+               alg = "WEP";
+               module = "ieee80211_crypt_wep";
+               break;
+       case IW_ENCODE_ALG_TKIP:
+               alg = "TKIP";
+               module = "ieee80211_crypt_tkip";
+               break;
+       case IW_ENCODE_ALG_CCMP:
+               alg = "CCMP";
+               module = "ieee80211_crypt_ccmp";
+               break;
+       default:
+               printk(KERN_DEBUG "%s: unsupported algorithm %d\n",
+                      local->dev->name, ext->alg);
+               ret = -EOPNOTSUPP;
+               goto done;
+       }
+
+       ops = ieee80211_get_crypto_ops(alg);
+       if (ops == NULL) {
+               request_module(module);
+               ops = ieee80211_get_crypto_ops(alg);
+       }
+       if (ops == NULL) {
+               printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n",
+                      local->dev->name, alg);
+               ret = -EOPNOTSUPP;
+               goto done;
+       }
+
+       if (sta_ptr || ext->alg != IW_ENCODE_ALG_WEP) {
+               /*
+                * Per station encryption and other than WEP algorithms
+                * require host-based encryption, so force them on
+                * automatically.
+                */
+               local->host_decrypt = local->host_encrypt = 1;
+       }
+
+       if (*crypt == NULL || (*crypt)->ops != ops) {
+               struct ieee80211_crypt_data *new_crypt;
+
+               prism2_crypt_delayed_deinit(local, crypt);
+
+               new_crypt = (struct ieee80211_crypt_data *)
+                       kmalloc(sizeof(struct ieee80211_crypt_data),
+                               GFP_KERNEL);
+               if (new_crypt == NULL) {
+                       ret = -ENOMEM;
+                       goto done;
+               }
+               memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
+               new_crypt->ops = ops;
+               new_crypt->priv = new_crypt->ops->init(i);
+               if (new_crypt->priv == NULL) {
+                       kfree(new_crypt);
+                       ret = -EINVAL;
+                       goto done;
+               }
+
+               *crypt = new_crypt;
+       }
+
+       /*
+        * TODO: if ext_flags does not have IW_ENCODE_EXT_RX_SEQ_VALID, the
+        * existing seq# should not be changed.
+        * TODO: if ext_flags has IW_ENCODE_EXT_TX_SEQ_VALID, next TX seq#
+        * should be changed to something else than zero.
+        */
+       if ((!(ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) || ext->key_len > 0)
+           && (*crypt)->ops->set_key &&
+           (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
+                                  (*crypt)->priv) < 0) {
+               printk(KERN_DEBUG "%s: key setting failed\n",
+                      local->dev->name);
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+               if (!sta_ptr)
+                       local->tx_keyidx = i;
+               else if (i) {
+                       ret = -EINVAL;
+                       goto done;
+               }
+       }
+
+
+       if (sta_ptr == NULL && ext->key_len > 0) {
+               int first = 1, j;
+               for (j = 0; j < WEP_KEYS; j++) {
+                       if (j != i && local->crypt[j]) {
+                               first = 0;
+                               break;
+                       }
+               }
+               if (first)
+                       local->tx_keyidx = i;
+       }
+
+ done:
+       if (sta_ptr)
+               hostap_handle_sta_release(sta_ptr);
+
+       local->open_wep = erq->flags & IW_ENCODE_OPEN;
+
+       /*
+        * Do not reset port0 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. Prism2 documentation seem to require port reset
+        * after WEP configuration. However, keys are apparently changed at
+        * least in Managed mode.
+        */
+       if (ret == 0 &&
+           (hostap_set_encryption(local) ||
+            (local->iw_mode != IW_MODE_INFRA &&
+             local->func->reset_port(local->dev))))
+               ret = -EINVAL;
+
+       return ret;
+}
+
+
+static int prism2_ioctl_giwencodeext(struct net_device *dev,
+                                    struct iw_request_info *info,
+                                    struct iw_point *erq, char *extra)
+{
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+       struct ieee80211_crypt_data **crypt;
+       void *sta_ptr;
+       int max_key_len, i;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
+       u8 *addr;
+
+       max_key_len = erq->length - sizeof(*ext);
+       if (max_key_len < 0)
+               return -EINVAL;
+
+       i = erq->flags & IW_ENCODE_INDEX;
+       if (i < 1 || i > WEP_KEYS)
+               i = local->tx_keyidx;
+       else
+               i--;
+
+       addr = ext->addr.sa_data;
+       if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
+           addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
+               sta_ptr = NULL;
+               crypt = &local->crypt[i];
+       } else {
+               i = 0;
+               sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt);
+               if (sta_ptr == NULL)
+                       return -EINVAL;
+       }
+       erq->flags = i + 1;
+       memset(ext, 0, sizeof(*ext));
+
+       if (*crypt == NULL || (*crypt)->ops == NULL) {
+               ext->alg = IW_ENCODE_ALG_NONE;
+               ext->key_len = 0;
+               erq->flags |= IW_ENCODE_DISABLED;
+       } else {
+               if (strcmp((*crypt)->ops->name, "WEP") == 0)
+                       ext->alg = IW_ENCODE_ALG_WEP;
+               else if (strcmp((*crypt)->ops->name, "TKIP") == 0)
+                       ext->alg = IW_ENCODE_ALG_TKIP;
+               else if (strcmp((*crypt)->ops->name, "CCMP") == 0)
+                       ext->alg = IW_ENCODE_ALG_CCMP;
+               else
+                       return -EINVAL;
+
+               if ((*crypt)->ops->get_key) {
+                       ext->key_len =
+                               (*crypt)->ops->get_key(ext->key,
+                                                      max_key_len,
+                                                      ext->tx_seq,
+                                                      (*crypt)->priv);
+                       if (ext->key_len &&
+                           (ext->alg == IW_ENCODE_ALG_TKIP ||
+                            ext->alg == IW_ENCODE_ALG_CCMP))
+                               ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
+               }
+       }
+
+       if (sta_ptr)
+               hostap_handle_sta_release(sta_ptr);
+
+       return 0;
+}
+
+
+static int prism2_ioctl_set_encryption(local_info_t *local,
+                                      struct prism2_hostapd_param *param,
+                                      int param_len)
+{
+       int ret = 0;
+       struct ieee80211_crypto_ops *ops;
+       struct ieee80211_crypt_data **crypt;
+       void *sta_ptr;
+
+       param->u.crypt.err = 0;
+       param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0';
+
+       if (param_len !=
+           (int) ((char *) param->u.crypt.key - (char *) param) +
+           param->u.crypt.key_len)
+               return -EINVAL;
+
+       if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+           param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+           param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+               if (param->u.crypt.idx >= WEP_KEYS)
+                       return -EINVAL;
+               sta_ptr = NULL;
+               crypt = &local->crypt[param->u.crypt.idx];
+       } else {
+               if (param->u.crypt.idx)
+                       return -EINVAL;
+               sta_ptr = ap_crypt_get_ptrs(
+                       local->ap, param->sta_addr,
+                       (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_PERMANENT),
+                       &crypt);
+
+               if (sta_ptr == NULL) {
+                       param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR;
+                       return -EINVAL;
+               }
+       }
+
+       if (strcmp(param->u.crypt.alg, "none") == 0) {
+               if (crypt)
+                       prism2_crypt_delayed_deinit(local, crypt);
+               goto done;
+       }
+
+       ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
+               request_module("ieee80211_crypt_wep");
+               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
+               request_module("ieee80211_crypt_tkip");
+               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
+               request_module("ieee80211_crypt_ccmp");
+               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       }
+       if (ops == NULL) {
+               printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n",
+                      local->dev->name, param->u.crypt.alg);
+               param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG;
+               ret = -EINVAL;
+               goto done;
+       }
+
+       /* station based encryption and other than WEP algorithms require
+        * host-based encryption, so force them on automatically */
+       local->host_decrypt = local->host_encrypt = 1;
+
+       if (*crypt == NULL || (*crypt)->ops != ops) {
+               struct ieee80211_crypt_data *new_crypt;
+
+               prism2_crypt_delayed_deinit(local, crypt);
+
+               new_crypt = (struct ieee80211_crypt_data *)
+                       kmalloc(sizeof(struct ieee80211_crypt_data),
+                               GFP_KERNEL);
+               if (new_crypt == NULL) {
+                       ret = -ENOMEM;
+                       goto done;
+               }
+               memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
+               new_crypt->ops = ops;
+               new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx);
+               if (new_crypt->priv == NULL) {
+                       kfree(new_crypt);
+                       param->u.crypt.err =
+                               HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED;
+                       ret = -EINVAL;
+                       goto done;
+               }
+
+               *crypt = new_crypt;
+       }
+
+       if ((!(param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) ||
+            param->u.crypt.key_len > 0) && (*crypt)->ops->set_key &&
+           (*crypt)->ops->set_key(param->u.crypt.key,
+                                  param->u.crypt.key_len, param->u.crypt.seq,
+                                  (*crypt)->priv) < 0) {
+               printk(KERN_DEBUG "%s: key setting failed\n",
+                      local->dev->name);
+               param->u.crypt.err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED;
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) {
+               if (!sta_ptr)
+                       local->tx_keyidx = param->u.crypt.idx;
+               else if (param->u.crypt.idx) {
+                       printk(KERN_DEBUG "%s: TX key idx setting failed\n",
+                              local->dev->name);
+                       param->u.crypt.err =
+                               HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED;
+                       ret = -EINVAL;
+                       goto done;
+               }
+       }
+
+ done:
+       if (sta_ptr)
+               hostap_handle_sta_release(sta_ptr);
+
+       /* Do not reset port0 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. Prism2 documentation seem to require port reset
+        * after WEP configuration. However, keys are apparently changed at
+        * least in Managed mode. */
+       if (ret == 0 &&
+           (hostap_set_encryption(local) ||
+            (local->iw_mode != IW_MODE_INFRA &&
+             local->func->reset_port(local->dev)))) {
+               param->u.crypt.err = HOSTAP_CRYPT_ERR_CARD_CONF_FAILED;
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+
+static int prism2_ioctl_get_encryption(local_info_t *local,
+                                      struct prism2_hostapd_param *param,
+                                      int param_len)
+{
+       struct ieee80211_crypt_data **crypt;
+       void *sta_ptr;
+       int max_key_len;
+
+       param->u.crypt.err = 0;
+
+       max_key_len = param_len -
+               (int) ((char *) param->u.crypt.key - (char *) param);
+       if (max_key_len < 0)
+               return -EINVAL;
+
+       if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+           param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+           param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+               sta_ptr = NULL;
+               if (param->u.crypt.idx >= WEP_KEYS)
+                       param->u.crypt.idx = local->tx_keyidx;
+               crypt = &local->crypt[param->u.crypt.idx];
+       } else {
+               param->u.crypt.idx = 0;
+               sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0,
+                                           &crypt);
+
+               if (sta_ptr == NULL) {
+                       param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR;
+                       return -EINVAL;
+               }
+       }
+
+       if (*crypt == NULL || (*crypt)->ops == NULL) {
+               memcpy(param->u.crypt.alg, "none", 5);
+               param->u.crypt.key_len = 0;
+               param->u.crypt.idx = 0xff;
+       } else {
+               strncpy(param->u.crypt.alg, (*crypt)->ops->name,
+                       HOSTAP_CRYPT_ALG_NAME_LEN);
+               param->u.crypt.key_len = 0;
+
+               memset(param->u.crypt.seq, 0, 8);
+               if ((*crypt)->ops->get_key) {
+                       param->u.crypt.key_len =
+                               (*crypt)->ops->get_key(param->u.crypt.key,
+                                                      max_key_len,
+                                                      param->u.crypt.seq,
+                                                      (*crypt)->priv);
+               }
+       }
+
+       if (sta_ptr)
+               hostap_handle_sta_release(sta_ptr);
+
+       return 0;
+}
+
+
+static int prism2_ioctl_get_rid(local_info_t *local,
+                               struct prism2_hostapd_param *param,
+                               int param_len)
+{
+       int max_len, res;
+
+       max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN;
+       if (max_len < 0)
+               return -EINVAL;
+
+       res = local->func->get_rid(local->dev, param->u.rid.rid,
+                                  param->u.rid.data, param->u.rid.len, 0);
+       if (res >= 0) {
+               param->u.rid.len = res;
+               return 0;
+       }
+
+       return res;
+}
+
+
+static int prism2_ioctl_set_rid(local_info_t *local,
+                               struct prism2_hostapd_param *param,
+                               int param_len)
+{
+       int max_len;
+
+       max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN;
+       if (max_len < 0 || max_len < param->u.rid.len)
+               return -EINVAL;
+
+       return local->func->set_rid(local->dev, param->u.rid.rid,
+                                   param->u.rid.data, param->u.rid.len);
+}
+
+
+static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local,
+                                         struct prism2_hostapd_param *param,
+                                         int param_len)
+{
+       printk(KERN_DEBUG "%ssta: associated as client with AP " MACSTR "\n",
+              local->dev->name, MAC2STR(param->sta_addr));
+       memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int prism2_ioctl_siwgenie(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *data, char *extra)
+{
+       return prism2_set_genericelement(dev, extra, data->length);
+}
+
+
+static int prism2_ioctl_giwgenie(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *data, char *extra)
+{
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+       int len = local->generic_elem_len - 2;
+
+       if (len <= 0 || local->generic_elem == NULL) {
+               data->length = 0;
+               return 0;
+       }
+
+       if (data->length < len)
+               return -E2BIG;
+
+       data->length = len;
+       memcpy(extra, local->generic_elem + 2, len);
+
+       return 0;
+}
+
+
+static int prism2_ioctl_set_generic_element(local_info_t *local,
+                                           struct prism2_hostapd_param *param,
+                                           int param_len)
+{
+       int max_len, len;
+
+       len = param->u.generic_elem.len;
+       max_len = param_len - PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN;
+       if (max_len < 0 || max_len < len)
+               return -EINVAL;
+
+       return prism2_set_genericelement(local->dev,
+                                        param->u.generic_elem.data, len);
+}
+
+
+static int prism2_ioctl_siwmlme(struct net_device *dev,
+                               struct iw_request_info *info,
+                               struct iw_point *data, char *extra)
+{
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+       struct iw_mlme *mlme = (struct iw_mlme *) extra;
+       u16 reason;
+
+       reason = cpu_to_le16(mlme->reason_code);
+
+       switch (mlme->cmd) {
+       case IW_MLME_DEAUTH:
+               return prism2_sta_send_mgmt(local, mlme->addr.sa_data,
+                                           IEEE80211_STYPE_DEAUTH,
+                                           (u8 *) &reason, 2);
+       case IW_MLME_DISASSOC:
+               return prism2_sta_send_mgmt(local, mlme->addr.sa_data,
+                                           IEEE80211_STYPE_DISASSOC,
+                                           (u8 *) &reason, 2);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+
+static int prism2_ioctl_mlme(local_info_t *local,
+                            struct prism2_hostapd_param *param)
+{
+       u16 reason;
+
+       reason = cpu_to_le16(param->u.mlme.reason_code);
+       switch (param->u.mlme.cmd) {
+       case MLME_STA_DEAUTH:
+               return prism2_sta_send_mgmt(local, param->sta_addr,
+                                           IEEE80211_STYPE_DEAUTH,
+                                           (u8 *) &reason, 2);
+       case MLME_STA_DISASSOC:
+               return prism2_sta_send_mgmt(local, param->sta_addr,
+                                           IEEE80211_STYPE_DISASSOC,
+                                           (u8 *) &reason, 2);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+
+static int prism2_ioctl_scan_req(local_info_t *local,
+                                struct prism2_hostapd_param *param)
+{
+#ifndef PRISM2_NO_STATION_MODES
+       if ((local->iw_mode != IW_MODE_INFRA &&
+            local->iw_mode != IW_MODE_ADHOC) ||
+           (local->sta_fw_ver < PRISM2_FW_VER(1,3,1)))
+               return -EOPNOTSUPP;
+
+       if (!local->dev_enabled)
+               return -ENETDOWN;
+
+       return prism2_request_hostscan(local->dev, param->u.scan_req.ssid,
+                                      param->u.scan_req.ssid_len);
+#else /* PRISM2_NO_STATION_MODES */
+       return -EOPNOTSUPP;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p)
+{
+       struct prism2_hostapd_param *param;
+       int ret = 0;
+       int ap_ioctl = 0;
+
+       if (p->length < sizeof(struct prism2_hostapd_param) ||
+           p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer)
+               return -EINVAL;
+
+       param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL);
+       if (param == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(param, p->pointer, p->length)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       switch (param->cmd) {
+       case PRISM2_SET_ENCRYPTION:
+               ret = prism2_ioctl_set_encryption(local, param, p->length);
+               break;
+       case PRISM2_GET_ENCRYPTION:
+               ret = prism2_ioctl_get_encryption(local, param, p->length);
+               break;
+       case PRISM2_HOSTAPD_GET_RID:
+               ret = prism2_ioctl_get_rid(local, param, p->length);
+               break;
+       case PRISM2_HOSTAPD_SET_RID:
+               ret = prism2_ioctl_set_rid(local, param, p->length);
+               break;
+       case PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR:
+               ret = prism2_ioctl_set_assoc_ap_addr(local, param, p->length);
+               break;
+       case PRISM2_HOSTAPD_SET_GENERIC_ELEMENT:
+               ret = prism2_ioctl_set_generic_element(local, param,
+                                                      p->length);
+               break;
+       case PRISM2_HOSTAPD_MLME:
+               ret = prism2_ioctl_mlme(local, param);
+               break;
+       case PRISM2_HOSTAPD_SCAN_REQ:
+               ret = prism2_ioctl_scan_req(local, param);
+               break;
+       default:
+               ret = prism2_hostapd(local->ap, param);
+               ap_ioctl = 1;
+               break;
+       }
+
+       if (ret == 1 || !ap_ioctl) {
+               if (copy_to_user(p->pointer, param, p->length)) {
+                       ret = -EFAULT;
+                       goto out;
+               } else if (ap_ioctl)
+                       ret = 0;
+       }
+
+ out:
+       if (param != NULL)
+               kfree(param);
+
+       return ret;
+}
+
+
+static void prism2_get_drvinfo(struct net_device *dev,
+                              struct ethtool_drvinfo *info)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       strncpy(info->driver, "hostap", sizeof(info->driver) - 1);
+       strncpy(info->version, PRISM2_VERSION,
+               sizeof(info->version) - 1);
+       snprintf(info->fw_version, sizeof(info->fw_version) - 1,
+                "%d.%d.%d", (local->sta_fw_ver >> 16) & 0xff,
+                (local->sta_fw_ver >> 8) & 0xff,
+                local->sta_fw_ver & 0xff);
+}
+
+static struct ethtool_ops prism2_ethtool_ops = {
+       .get_drvinfo = prism2_get_drvinfo
+};
+
+
+/* Structures to export the Wireless Handlers */
+
+static const iw_handler prism2_handler[] =
+{
+       (iw_handler) NULL,                              /* SIOCSIWCOMMIT */
+       (iw_handler) prism2_get_name,                   /* SIOCGIWNAME */
+       (iw_handler) NULL,                              /* SIOCSIWNWID */
+       (iw_handler) NULL,                              /* SIOCGIWNWID */
+       (iw_handler) prism2_ioctl_siwfreq,              /* SIOCSIWFREQ */
+       (iw_handler) prism2_ioctl_giwfreq,              /* SIOCGIWFREQ */
+       (iw_handler) prism2_ioctl_siwmode,              /* SIOCSIWMODE */
+       (iw_handler) prism2_ioctl_giwmode,              /* SIOCGIWMODE */
+       (iw_handler) prism2_ioctl_siwsens,              /* SIOCSIWSENS */
+       (iw_handler) prism2_ioctl_giwsens,              /* SIOCGIWSENS */
+       (iw_handler) NULL /* not used */,               /* SIOCSIWRANGE */
+       (iw_handler) prism2_ioctl_giwrange,             /* SIOCGIWRANGE */
+       (iw_handler) NULL /* not used */,               /* SIOCSIWPRIV */
+       (iw_handler) NULL /* kernel code */,            /* SIOCGIWPRIV */
+       (iw_handler) NULL /* not used */,               /* SIOCSIWSTATS */
+       (iw_handler) NULL /* kernel code */,            /* SIOCGIWSTATS */
+       iw_handler_set_spy,                             /* SIOCSIWSPY */
+       iw_handler_get_spy,                             /* SIOCGIWSPY */
+       iw_handler_set_thrspy,                          /* SIOCSIWTHRSPY */
+       iw_handler_get_thrspy,                          /* SIOCGIWTHRSPY */
+       (iw_handler) prism2_ioctl_siwap,                /* SIOCSIWAP */
+       (iw_handler) prism2_ioctl_giwap,                /* SIOCGIWAP */
+       (iw_handler) prism2_ioctl_siwmlme,              /* SIOCSIWMLME */
+       (iw_handler) prism2_ioctl_giwaplist,            /* SIOCGIWAPLIST */
+       (iw_handler) prism2_ioctl_siwscan,              /* SIOCSIWSCAN */
+       (iw_handler) prism2_ioctl_giwscan,              /* SIOCGIWSCAN */
+       (iw_handler) prism2_ioctl_siwessid,             /* SIOCSIWESSID */
+       (iw_handler) prism2_ioctl_giwessid,             /* SIOCGIWESSID */
+       (iw_handler) prism2_ioctl_siwnickn,             /* SIOCSIWNICKN */
+       (iw_handler) prism2_ioctl_giwnickn,             /* SIOCGIWNICKN */
+       (iw_handler) NULL,                              /* -- hole -- */
+       (iw_handler) NULL,                              /* -- hole -- */
+       (iw_handler) prism2_ioctl_siwrate,              /* SIOCSIWRATE */
+       (iw_handler) prism2_ioctl_giwrate,              /* SIOCGIWRATE */
+       (iw_handler) prism2_ioctl_siwrts,               /* SIOCSIWRTS */
+       (iw_handler) prism2_ioctl_giwrts,               /* SIOCGIWRTS */
+       (iw_handler) prism2_ioctl_siwfrag,              /* SIOCSIWFRAG */
+       (iw_handler) prism2_ioctl_giwfrag,              /* SIOCGIWFRAG */
+       (iw_handler) prism2_ioctl_siwtxpow,             /* SIOCSIWTXPOW */
+       (iw_handler) prism2_ioctl_giwtxpow,             /* SIOCGIWTXPOW */
+       (iw_handler) prism2_ioctl_siwretry,             /* SIOCSIWRETRY */
+       (iw_handler) prism2_ioctl_giwretry,             /* SIOCGIWRETRY */
+       (iw_handler) prism2_ioctl_siwencode,            /* SIOCSIWENCODE */
+       (iw_handler) prism2_ioctl_giwencode,            /* SIOCGIWENCODE */
+       (iw_handler) prism2_ioctl_siwpower,             /* SIOCSIWPOWER */
+       (iw_handler) prism2_ioctl_giwpower,             /* SIOCGIWPOWER */
+       (iw_handler) NULL,                              /* -- hole -- */
+       (iw_handler) NULL,                              /* -- hole -- */
+       (iw_handler) prism2_ioctl_siwgenie,             /* SIOCSIWGENIE */
+       (iw_handler) prism2_ioctl_giwgenie,             /* SIOCGIWGENIE */
+       (iw_handler) prism2_ioctl_siwauth,              /* SIOCSIWAUTH */
+       (iw_handler) prism2_ioctl_giwauth,              /* SIOCGIWAUTH */
+       (iw_handler) prism2_ioctl_siwencodeext,         /* SIOCSIWENCODEEXT */
+       (iw_handler) prism2_ioctl_giwencodeext,         /* SIOCGIWENCODEEXT */
+       (iw_handler) NULL,                              /* SIOCSIWPMKSA */
+       (iw_handler) NULL,                              /* -- hole -- */
+};
+
+static const iw_handler prism2_private_handler[] =
+{                                                      /* SIOCIWFIRSTPRIV + */
+       (iw_handler) prism2_ioctl_priv_prism2_param,    /* 0 */
+       (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */
+       (iw_handler) prism2_ioctl_priv_writemif,        /* 2 */
+       (iw_handler) prism2_ioctl_priv_readmif,         /* 3 */
+};
+
+static const struct iw_handler_def hostap_iw_handler_def =
+{
+       .num_standard   = sizeof(prism2_handler) / sizeof(iw_handler),
+       .num_private    = sizeof(prism2_private_handler) / sizeof(iw_handler),
+       .num_private_args = sizeof(prism2_priv) / sizeof(struct iw_priv_args),
+       .standard       = (iw_handler *) prism2_handler,
+       .private        = (iw_handler *) prism2_private_handler,
+       .private_args   = (struct iw_priv_args *) prism2_priv,
+       .get_wireless_stats = hostap_get_wireless_stats,
+};
+
+
+int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct iwreq *wrq = (struct iwreq *) ifr;
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       switch (cmd) {
+               /* Private ioctls (iwpriv) that have not yet been converted
+                * into new wireless extensions API */
+
+       case PRISM2_IOCTL_INQUIRE:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_ioctl_priv_inquire(dev, (int *) wrq->u.name);
+               break;
+
+       case PRISM2_IOCTL_MONITOR:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_ioctl_priv_monitor(dev, (int *) wrq->u.name);
+               break;
+
+       case PRISM2_IOCTL_RESET:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_ioctl_priv_reset(dev, (int *) wrq->u.name);
+               break;
+
+       case PRISM2_IOCTL_WDS_ADD:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_wds_add(local, wrq->u.ap_addr.sa_data, 1);
+               break;
+
+       case PRISM2_IOCTL_WDS_DEL:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_wds_del(local, wrq->u.ap_addr.sa_data, 1, 0);
+               break;
+
+       case PRISM2_IOCTL_SET_RID_WORD:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_ioctl_priv_set_rid_word(dev,
+                                                         (int *) wrq->u.name);
+               break;
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       case PRISM2_IOCTL_MACCMD:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = ap_mac_cmd_ioctl(local, (int *) wrq->u.name);
+               break;
+
+       case PRISM2_IOCTL_ADDMAC:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = ap_control_add_mac(&local->ap->mac_restrictions,
+                                             wrq->u.ap_addr.sa_data);
+               break;
+       case PRISM2_IOCTL_DELMAC:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = ap_control_del_mac(&local->ap->mac_restrictions,
+                                             wrq->u.ap_addr.sa_data);
+               break;
+       case PRISM2_IOCTL_KICKMAC:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = ap_control_kick_mac(local->ap, local->dev,
+                                              wrq->u.ap_addr.sa_data);
+               break;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+               /* Private ioctls that are not used with iwpriv;
+                * in SIOCDEVPRIVATE range */
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+       case PRISM2_IOCTL_DOWNLOAD:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_ioctl_priv_download(local, &wrq->u.data);
+               break;
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+       case PRISM2_IOCTL_HOSTAPD:
+               if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+               else ret = prism2_ioctl_priv_hostapd(local, &wrq->u.data);
+               break;
+
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c
new file mode 100644 (file)
index 0000000..4f567ef
--- /dev/null
@@ -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);
diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c
new file mode 100644 (file)
index 0000000..474ef83
--- /dev/null
@@ -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);
diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c
new file mode 100644 (file)
index 0000000..a0a4cbd
--- /dev/null
@@ -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);
diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h
new file mode 100644 (file)
index 0000000..cc061e1
--- /dev/null
@@ -0,0 +1,1033 @@
+#ifndef HOSTAP_WLAN_H
+#define HOSTAP_WLAN_H
+
+#include "hostap_config.h"
+#include "hostap_common.h"
+
+#define MAX_PARM_DEVICES 8
+#define PARM_MIN_MAX "1-" __MODULE_STRING(MAX_PARM_DEVICES)
+#define DEF_INTS -1, -1, -1, -1, -1, -1, -1
+#define GET_INT_PARM(var,idx) var[var[idx] < 0 ? 0 : idx]
+
+
+/* Specific skb->protocol value that indicates that the packet already contains
+ * txdesc header.
+ * FIX: This might need own value that would be allocated especially for Prism2
+ * txdesc; ETH_P_CONTROL is commented as "Card specific control frames".
+ * However, these skb's should have only minimal path in the kernel side since
+ * prism2_send_mgmt() sends these with dev_queue_xmit() to prism2_tx(). */
+#define ETH_P_HOSTAP ETH_P_CONTROL
+
+/* ARPHRD_IEEE80211_PRISM uses a bloated version of Prism2 RX frame header
+ * (from linux-wlan-ng) */
+struct linux_wlan_ng_val {
+       u32 did;
+       u16 status, len;
+       u32 data;
+} __attribute__ ((packed));
+
+struct linux_wlan_ng_prism_hdr {
+       u32 msgcode, msglen;
+       char devname[16];
+       struct linux_wlan_ng_val hosttime, mactime, channel, rssi, sq, signal,
+               noise, rate, istx, frmlen;
+} __attribute__ ((packed));
+
+struct linux_wlan_ng_cap_hdr {
+       u32 version;
+       u32 length;
+       u64 mactime;
+       u64 hosttime;
+       u32 phytype;
+       u32 channel;
+       u32 datarate;
+       u32 antenna;
+       u32 priority;
+       u32 ssi_type;
+       s32 ssi_signal;
+       s32 ssi_noise;
+       u32 preamble;
+       u32 encoding;
+} __attribute__ ((packed));
+
+#define LWNG_CAP_DID_BASE   (4 | (1 << 6)) /* section 4, group 1 */
+#define LWNG_CAPHDR_VERSION 0x80211001
+
+struct hfa384x_rx_frame {
+       /* HFA384X RX frame descriptor */
+       u16 status; /* HFA384X_RX_STATUS_ flags */
+       u32 time; /* timestamp, 1 microsecond resolution */
+       u8 silence; /* 27 .. 154; seems to be 0 */
+       u8 signal; /* 27 .. 154 */
+       u8 rate; /* 10, 20, 55, or 110 */
+       u8 rxflow;
+       u32 reserved;
+
+       /* 802.11 */
+       u16 frame_control;
+       u16 duration_id;
+       u8 addr1[6];
+       u8 addr2[6];
+       u8 addr3[6];
+       u16 seq_ctrl;
+       u8 addr4[6];
+       u16 data_len;
+
+       /* 802.3 */
+       u8 dst_addr[6];
+       u8 src_addr[6];
+       u16 len;
+
+       /* followed by frame data; max 2304 bytes */
+} __attribute__ ((packed));
+
+
+struct hfa384x_tx_frame {
+       /* HFA384X TX frame descriptor */
+       u16 status; /* HFA384X_TX_STATUS_ flags */
+       u16 reserved1;
+       u16 reserved2;
+       u32 sw_support;
+       u8 retry_count; /* not yet implemented */
+       u8 tx_rate; /* Host AP only; 0 = firmware, or 10, 20, 55, 110 */
+       u16 tx_control; /* HFA384X_TX_CTRL_ flags */
+
+       /* 802.11 */
+       u16 frame_control; /* parts not used */
+       u16 duration_id;
+       u8 addr1[6];
+       u8 addr2[6]; /* filled by firmware */
+       u8 addr3[6];
+       u16 seq_ctrl; /* filled by firmware */
+       u8 addr4[6];
+       u16 data_len;
+
+       /* 802.3 */
+       u8 dst_addr[6];
+       u8 src_addr[6];
+       u16 len;
+
+       /* followed by frame data; max 2304 bytes */
+} __attribute__ ((packed));
+
+
+struct hfa384x_rid_hdr
+{
+       u16 len;
+       u16 rid;
+} __attribute__ ((packed));
+
+
+/* Macro for converting signal levels (range 27 .. 154) to wireless ext
+ * dBm value with some accuracy */
+#define HFA384X_LEVEL_TO_dBm(v) 0x100 + (v) * 100 / 255 - 100
+
+#define HFA384X_LEVEL_TO_dBm_sign(v) (v) * 100 / 255 - 100
+
+struct hfa384x_scan_request {
+       u16 channel_list;
+       u16 txrate; /* HFA384X_RATES_* */
+} __attribute__ ((packed));
+
+struct hfa384x_hostscan_request {
+       u16 channel_list;
+       u16 txrate;
+       u16 target_ssid_len;
+       u8 target_ssid[32];
+} __attribute__ ((packed));
+
+struct hfa384x_join_request {
+       u8 bssid[6];
+       u16 channel;
+} __attribute__ ((packed));
+
+struct hfa384x_info_frame {
+       u16 len;
+       u16 type;
+} __attribute__ ((packed));
+
+struct hfa384x_comm_tallies {
+       u16 tx_unicast_frames;
+       u16 tx_multicast_frames;
+       u16 tx_fragments;
+       u16 tx_unicast_octets;
+       u16 tx_multicast_octets;
+       u16 tx_deferred_transmissions;
+       u16 tx_single_retry_frames;
+       u16 tx_multiple_retry_frames;
+       u16 tx_retry_limit_exceeded;
+       u16 tx_discards;
+       u16 rx_unicast_frames;
+       u16 rx_multicast_frames;
+       u16 rx_fragments;
+       u16 rx_unicast_octets;
+       u16 rx_multicast_octets;
+       u16 rx_fcs_errors;
+       u16 rx_discards_no_buffer;
+       u16 tx_discards_wrong_sa;
+       u16 rx_discards_wep_undecryptable;
+       u16 rx_message_in_msg_fragments;
+       u16 rx_message_in_bad_msg_fragments;
+} __attribute__ ((packed));
+
+struct hfa384x_comm_tallies32 {
+       u32 tx_unicast_frames;
+       u32 tx_multicast_frames;
+       u32 tx_fragments;
+       u32 tx_unicast_octets;
+       u32 tx_multicast_octets;
+       u32 tx_deferred_transmissions;
+       u32 tx_single_retry_frames;
+       u32 tx_multiple_retry_frames;
+       u32 tx_retry_limit_exceeded;
+       u32 tx_discards;
+       u32 rx_unicast_frames;
+       u32 rx_multicast_frames;
+       u32 rx_fragments;
+       u32 rx_unicast_octets;
+       u32 rx_multicast_octets;
+       u32 rx_fcs_errors;
+       u32 rx_discards_no_buffer;
+       u32 tx_discards_wrong_sa;
+       u32 rx_discards_wep_undecryptable;
+       u32 rx_message_in_msg_fragments;
+       u32 rx_message_in_bad_msg_fragments;
+} __attribute__ ((packed));
+
+struct hfa384x_scan_result_hdr {
+       u16 reserved;
+       u16 scan_reason;
+#define HFA384X_SCAN_IN_PROGRESS 0 /* no results available yet */
+#define HFA384X_SCAN_HOST_INITIATED 1
+#define HFA384X_SCAN_FIRMWARE_INITIATED 2
+#define HFA384X_SCAN_INQUIRY_FROM_HOST 3
+} __attribute__ ((packed));
+
+#define HFA384X_SCAN_MAX_RESULTS 32
+
+struct hfa384x_scan_result {
+       u16 chid;
+       u16 anl;
+       u16 sl;
+       u8 bssid[6];
+       u16 beacon_interval;
+       u16 capability;
+       u16 ssid_len;
+       u8 ssid[32];
+       u8 sup_rates[10];
+       u16 rate;
+} __attribute__ ((packed));
+
+struct hfa384x_hostscan_result {
+       u16 chid;
+       u16 anl;
+       u16 sl;
+       u8 bssid[6];
+       u16 beacon_interval;
+       u16 capability;
+       u16 ssid_len;
+       u8 ssid[32];
+       u8 sup_rates[10];
+       u16 rate;
+       u16 atim;
+} __attribute__ ((packed));
+
+struct comm_tallies_sums {
+       unsigned int tx_unicast_frames;
+       unsigned int tx_multicast_frames;
+       unsigned int tx_fragments;
+       unsigned int tx_unicast_octets;
+       unsigned int tx_multicast_octets;
+       unsigned int tx_deferred_transmissions;
+       unsigned int tx_single_retry_frames;
+       unsigned int tx_multiple_retry_frames;
+       unsigned int tx_retry_limit_exceeded;
+       unsigned int tx_discards;
+       unsigned int rx_unicast_frames;
+       unsigned int rx_multicast_frames;
+       unsigned int rx_fragments;
+       unsigned int rx_unicast_octets;
+       unsigned int rx_multicast_octets;
+       unsigned int rx_fcs_errors;
+       unsigned int rx_discards_no_buffer;
+       unsigned int tx_discards_wrong_sa;
+       unsigned int rx_discards_wep_undecryptable;
+       unsigned int rx_message_in_msg_fragments;
+       unsigned int rx_message_in_bad_msg_fragments;
+};
+
+
+struct hfa384x_regs {
+       u16 cmd;
+       u16 evstat;
+       u16 offset0;
+       u16 offset1;
+       u16 swsupport0;
+};
+
+
+#if defined(PRISM2_PCCARD) || defined(PRISM2_PLX)
+/* I/O ports for HFA384X Controller access */
+#define HFA384X_CMD_OFF 0x00
+#define HFA384X_PARAM0_OFF 0x02
+#define HFA384X_PARAM1_OFF 0x04
+#define HFA384X_PARAM2_OFF 0x06
+#define HFA384X_STATUS_OFF 0x08
+#define HFA384X_RESP0_OFF 0x0A
+#define HFA384X_RESP1_OFF 0x0C
+#define HFA384X_RESP2_OFF 0x0E
+#define HFA384X_INFOFID_OFF 0x10
+#define HFA384X_CONTROL_OFF 0x14
+#define HFA384X_SELECT0_OFF 0x18
+#define HFA384X_SELECT1_OFF 0x1A
+#define HFA384X_OFFSET0_OFF 0x1C
+#define HFA384X_OFFSET1_OFF 0x1E
+#define HFA384X_RXFID_OFF 0x20
+#define HFA384X_ALLOCFID_OFF 0x22
+#define HFA384X_TXCOMPLFID_OFF 0x24
+#define HFA384X_SWSUPPORT0_OFF 0x28
+#define HFA384X_SWSUPPORT1_OFF 0x2A
+#define HFA384X_SWSUPPORT2_OFF 0x2C
+#define HFA384X_EVSTAT_OFF 0x30
+#define HFA384X_INTEN_OFF 0x32
+#define HFA384X_EVACK_OFF 0x34
+#define HFA384X_DATA0_OFF 0x36
+#define HFA384X_DATA1_OFF 0x38
+#define HFA384X_AUXPAGE_OFF 0x3A
+#define HFA384X_AUXOFFSET_OFF 0x3C
+#define HFA384X_AUXDATA_OFF 0x3E
+#endif /* PRISM2_PCCARD || PRISM2_PLX */
+
+#ifdef PRISM2_PCI
+/* Memory addresses for ISL3874 controller access */
+#define HFA384X_CMD_OFF 0x00
+#define HFA384X_PARAM0_OFF 0x04
+#define HFA384X_PARAM1_OFF 0x08
+#define HFA384X_PARAM2_OFF 0x0C
+#define HFA384X_STATUS_OFF 0x10
+#define HFA384X_RESP0_OFF 0x14
+#define HFA384X_RESP1_OFF 0x18
+#define HFA384X_RESP2_OFF 0x1C
+#define HFA384X_INFOFID_OFF 0x20
+#define HFA384X_CONTROL_OFF 0x28
+#define HFA384X_SELECT0_OFF 0x30
+#define HFA384X_SELECT1_OFF 0x34
+#define HFA384X_OFFSET0_OFF 0x38
+#define HFA384X_OFFSET1_OFF 0x3C
+#define HFA384X_RXFID_OFF 0x40
+#define HFA384X_ALLOCFID_OFF 0x44
+#define HFA384X_TXCOMPLFID_OFF 0x48
+#define HFA384X_PCICOR_OFF 0x4C
+#define HFA384X_SWSUPPORT0_OFF 0x50
+#define HFA384X_SWSUPPORT1_OFF 0x54
+#define HFA384X_SWSUPPORT2_OFF 0x58
+#define HFA384X_PCIHCR_OFF 0x5C
+#define HFA384X_EVSTAT_OFF 0x60
+#define HFA384X_INTEN_OFF 0x64
+#define HFA384X_EVACK_OFF 0x68
+#define HFA384X_DATA0_OFF 0x6C
+#define HFA384X_DATA1_OFF 0x70
+#define HFA384X_AUXPAGE_OFF 0x74
+#define HFA384X_AUXOFFSET_OFF 0x78
+#define HFA384X_AUXDATA_OFF 0x7C
+#define HFA384X_PCI_M0_ADDRH_OFF 0x80
+#define HFA384X_PCI_M0_ADDRL_OFF 0x84
+#define HFA384X_PCI_M0_LEN_OFF 0x88
+#define HFA384X_PCI_M0_CTL_OFF 0x8C
+#define HFA384X_PCI_STATUS_OFF 0x98
+#define HFA384X_PCI_M1_ADDRH_OFF 0xA0
+#define HFA384X_PCI_M1_ADDRL_OFF 0xA4
+#define HFA384X_PCI_M1_LEN_OFF 0xA8
+#define HFA384X_PCI_M1_CTL_OFF 0xAC
+
+/* PCI bus master control bits (these are undocumented; based on guessing and
+ * experimenting..) */
+#define HFA384X_PCI_CTL_FROM_BAP (BIT(5) | BIT(1) | BIT(0))
+#define HFA384X_PCI_CTL_TO_BAP (BIT(5) | BIT(0))
+
+#endif /* PRISM2_PCI */
+
+
+/* Command codes for CMD reg. */
+#define HFA384X_CMDCODE_INIT 0x00
+#define HFA384X_CMDCODE_ENABLE 0x01
+#define HFA384X_CMDCODE_DISABLE 0x02
+#define HFA384X_CMDCODE_ALLOC 0x0A
+#define HFA384X_CMDCODE_TRANSMIT 0x0B
+#define HFA384X_CMDCODE_INQUIRE 0x11
+#define HFA384X_CMDCODE_ACCESS 0x21
+#define HFA384X_CMDCODE_ACCESS_WRITE (0x21 | BIT(8))
+#define HFA384X_CMDCODE_DOWNLOAD 0x22
+#define HFA384X_CMDCODE_READMIF 0x30
+#define HFA384X_CMDCODE_WRITEMIF 0x31
+#define HFA384X_CMDCODE_TEST 0x38
+
+#define HFA384X_CMDCODE_MASK 0x3F
+
+/* Test mode operations */
+#define HFA384X_TEST_CHANGE_CHANNEL 0x08
+#define HFA384X_TEST_MONITOR 0x0B
+#define HFA384X_TEST_STOP 0x0F
+#define HFA384X_TEST_CFG_BITS 0x15
+#define HFA384X_TEST_CFG_BIT_ALC BIT(3)
+
+#define HFA384X_CMD_BUSY BIT(15)
+
+#define HFA384X_CMD_TX_RECLAIM BIT(8)
+
+#define HFA384X_OFFSET_ERR BIT(14)
+#define HFA384X_OFFSET_BUSY BIT(15)
+
+
+/* ProgMode for download command */
+#define HFA384X_PROGMODE_DISABLE 0
+#define HFA384X_PROGMODE_ENABLE_VOLATILE 1
+#define HFA384X_PROGMODE_ENABLE_NON_VOLATILE 2
+#define HFA384X_PROGMODE_PROGRAM_NON_VOLATILE 3
+
+#define HFA384X_AUX_MAGIC0 0xfe01
+#define HFA384X_AUX_MAGIC1 0xdc23
+#define HFA384X_AUX_MAGIC2 0xba45
+
+#define HFA384X_AUX_PORT_DISABLED 0
+#define HFA384X_AUX_PORT_DISABLE BIT(14)
+#define HFA384X_AUX_PORT_ENABLE BIT(15)
+#define HFA384X_AUX_PORT_ENABLED (BIT(14) | BIT(15))
+#define HFA384X_AUX_PORT_MASK (BIT(14) | BIT(15))
+
+#define PRISM2_PDA_SIZE 1024
+
+
+/* Events; EvStat, Interrupt mask (IntEn), and acknowledge bits (EvAck) */
+#define HFA384X_EV_TICK BIT(15)
+#define HFA384X_EV_WTERR BIT(14)
+#define HFA384X_EV_INFDROP BIT(13)
+#ifdef PRISM2_PCI
+#define HFA384X_EV_PCI_M1 BIT(9)
+#define HFA384X_EV_PCI_M0 BIT(8)
+#endif /* PRISM2_PCI */
+#define HFA384X_EV_INFO BIT(7)
+#define HFA384X_EV_DTIM BIT(5)
+#define HFA384X_EV_CMD BIT(4)
+#define HFA384X_EV_ALLOC BIT(3)
+#define HFA384X_EV_TXEXC BIT(2)
+#define HFA384X_EV_TX BIT(1)
+#define HFA384X_EV_RX BIT(0)
+
+
+/* HFA384X Information frames */
+#define HFA384X_INFO_HANDOVERADDR 0xF000 /* AP f/w ? */
+#define HFA384X_INFO_HANDOVERDEAUTHADDR 0xF001 /* AP f/w 1.3.7 */
+#define HFA384X_INFO_COMMTALLIES 0xF100
+#define HFA384X_INFO_SCANRESULTS 0xF101
+#define HFA384X_INFO_CHANNELINFORESULTS 0xF102 /* AP f/w only */
+#define HFA384X_INFO_HOSTSCANRESULTS 0xF103
+#define HFA384X_INFO_LINKSTATUS 0xF200
+#define HFA384X_INFO_ASSOCSTATUS 0xF201 /* ? */
+#define HFA384X_INFO_AUTHREQ 0xF202 /* ? */
+#define HFA384X_INFO_PSUSERCNT 0xF203 /* ? */
+#define HFA384X_INFO_KEYIDCHANGED 0xF204 /* ? */
+
+enum { HFA384X_LINKSTATUS_CONNECTED = 1,
+       HFA384X_LINKSTATUS_DISCONNECTED = 2,
+       HFA384X_LINKSTATUS_AP_CHANGE = 3,
+       HFA384X_LINKSTATUS_AP_OUT_OF_RANGE = 4,
+       HFA384X_LINKSTATUS_AP_IN_RANGE = 5,
+       HFA384X_LINKSTATUS_ASSOC_FAILED = 6 };
+
+enum { HFA384X_PORTTYPE_BSS = 1, HFA384X_PORTTYPE_WDS = 2,
+       HFA384X_PORTTYPE_PSEUDO_IBSS = 3, HFA384X_PORTTYPE_IBSS = 0,
+       HFA384X_PORTTYPE_HOSTAP = 6 };
+
+#define HFA384X_RATES_1MBPS BIT(0)
+#define HFA384X_RATES_2MBPS BIT(1)
+#define HFA384X_RATES_5MBPS BIT(2)
+#define HFA384X_RATES_11MBPS BIT(3)
+
+#define HFA384X_ROAMING_FIRMWARE 1
+#define HFA384X_ROAMING_HOST 2
+#define HFA384X_ROAMING_DISABLED 3
+
+#define HFA384X_WEPFLAGS_PRIVACYINVOKED BIT(0)
+#define HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED BIT(1)
+#define HFA384X_WEPFLAGS_HOSTENCRYPT BIT(4)
+#define HFA384X_WEPFLAGS_HOSTDECRYPT BIT(7)
+
+#define HFA384X_RX_STATUS_MSGTYPE (BIT(15) | BIT(14) | BIT(13))
+#define HFA384X_RX_STATUS_PCF BIT(12)
+#define HFA384X_RX_STATUS_MACPORT (BIT(10) | BIT(9) | BIT(8))
+#define HFA384X_RX_STATUS_UNDECR BIT(1)
+#define HFA384X_RX_STATUS_FCSERR BIT(0)
+
+#define HFA384X_RX_STATUS_GET_MSGTYPE(s) \
+(((s) & HFA384X_RX_STATUS_MSGTYPE) >> 13)
+#define HFA384X_RX_STATUS_GET_MACPORT(s) \
+(((s) & HFA384X_RX_STATUS_MACPORT) >> 8)
+
+enum { HFA384X_RX_MSGTYPE_NORMAL = 0, HFA384X_RX_MSGTYPE_RFC1042 = 1,
+       HFA384X_RX_MSGTYPE_BRIDGETUNNEL = 2, HFA384X_RX_MSGTYPE_MGMT = 4 };
+
+
+#define HFA384X_TX_CTRL_ALT_RTRY BIT(5)
+#define HFA384X_TX_CTRL_802_11 BIT(3)
+#define HFA384X_TX_CTRL_802_3 0
+#define HFA384X_TX_CTRL_TX_EX BIT(2)
+#define HFA384X_TX_CTRL_TX_OK BIT(1)
+
+#define HFA384X_TX_STATUS_RETRYERR BIT(0)
+#define HFA384X_TX_STATUS_AGEDERR BIT(1)
+#define HFA384X_TX_STATUS_DISCON BIT(2)
+#define HFA384X_TX_STATUS_FORMERR BIT(3)
+
+/* HFA3861/3863 (BBP) Control Registers */
+#define HFA386X_CR_TX_CONFIGURE 0x12 /* CR9 */
+#define HFA386X_CR_RX_CONFIGURE 0x14 /* CR10 */
+#define HFA386X_CR_A_D_TEST_MODES2 0x1A /* CR13 */
+#define HFA386X_CR_MANUAL_TX_POWER 0x3E /* CR31 */
+#define HFA386X_CR_MEASURED_TX_POWER 0x74 /* CR58 */
+
+
+#ifdef __KERNEL__
+
+#define PRISM2_TXFID_COUNT 8
+#define PRISM2_DATA_MAXLEN 2304
+#define PRISM2_TXFID_LEN (PRISM2_DATA_MAXLEN + sizeof(struct hfa384x_tx_frame))
+#define PRISM2_TXFID_EMPTY 0xffff
+#define PRISM2_TXFID_RESERVED 0xfffe
+#define PRISM2_DUMMY_FID 0xffff
+#define MAX_SSID_LEN 32
+#define MAX_NAME_LEN 32 /* this is assumed to be equal to MAX_SSID_LEN */
+
+#define PRISM2_DUMP_RX_HDR BIT(0)
+#define PRISM2_DUMP_TX_HDR BIT(1)
+#define PRISM2_DUMP_TXEXC_HDR BIT(2)
+
+struct hostap_tx_callback_info {
+       u16 idx;
+       void (*func)(struct sk_buff *, int ok, void *);
+       void *data;
+       struct hostap_tx_callback_info *next;
+};
+
+
+/* IEEE 802.11 requires that STA supports concurrent reception of at least
+ * three fragmented frames. This define can be increased to support more
+ * concurrent frames, but it should be noted that each entry can consume about
+ * 2 kB of RAM and increasing cache size will slow down frame reassembly. */
+#define PRISM2_FRAG_CACHE_LEN 4
+
+struct prism2_frag_entry {
+       unsigned long first_frag_time;
+       unsigned int seq;
+       unsigned int last_frag;
+       struct sk_buff *skb;
+       u8 src_addr[ETH_ALEN];
+       u8 dst_addr[ETH_ALEN];
+};
+
+
+struct hostap_cmd_queue {
+       struct list_head list;
+       wait_queue_head_t compl;
+       volatile enum { CMD_SLEEP, CMD_CALLBACK, CMD_COMPLETED } type;
+       void (*callback)(struct net_device *dev, long context, u16 resp0,
+                        u16 res);
+       long context;
+       u16 cmd, param0, param1;
+       u16 resp0, res;
+       volatile int issued, issuing;
+
+       atomic_t usecnt;
+       int del_req;
+};
+
+/* options for hw_shutdown */
+#define HOSTAP_HW_NO_DISABLE BIT(0)
+#define HOSTAP_HW_ENABLE_CMDCOMPL BIT(1)
+
+typedef struct local_info local_info_t;
+
+struct prism2_helper_functions {
+       /* these functions are defined in hardware model specific files
+        * (hostap_{cs,plx,pci}.c */
+       int (*card_present)(local_info_t *local);
+       void (*cor_sreset)(local_info_t *local);
+       int (*dev_open)(local_info_t *local);
+       int (*dev_close)(local_info_t *local);
+       void (*genesis_reset)(local_info_t *local, int hcr);
+
+       /* the following functions are from hostap_hw.c, but they may have some
+        * hardware model specific code */
+
+       /* FIX: low-level commands like cmd might disappear at some point to
+        * make it easier to change them if needed (e.g., cmd would be replaced
+        * with write_mif/read_mif/testcmd/inquire); at least get_rid and
+        * set_rid might move to hostap_{cs,plx,pci}.c */
+       int (*cmd)(struct net_device *dev, u16 cmd, u16 param0, u16 *param1,
+                  u16 *resp0);
+       void (*read_regs)(struct net_device *dev, struct hfa384x_regs *regs);
+       int (*get_rid)(struct net_device *dev, u16 rid, void *buf, int len,
+                      int exact_len);
+       int (*set_rid)(struct net_device *dev, u16 rid, void *buf, int len);
+       int (*hw_enable)(struct net_device *dev, int initial);
+       int (*hw_config)(struct net_device *dev, int initial);
+       void (*hw_reset)(struct net_device *dev);
+       void (*hw_shutdown)(struct net_device *dev, int no_disable);
+       int (*reset_port)(struct net_device *dev);
+       void (*schedule_reset)(local_info_t *local);
+       int (*download)(local_info_t *local,
+                       struct prism2_download_param *param);
+       int (*tx)(struct sk_buff *skb, struct net_device *dev);
+       int (*set_tim)(struct net_device *dev, int aid, int set);
+       int (*read_aux)(struct net_device *dev, unsigned addr, int len,
+                       u8 *buf);
+
+       int need_tx_headroom; /* number of bytes of headroom needed before
+                              * IEEE 802.11 header */
+       enum { HOSTAP_HW_PCCARD, HOSTAP_HW_PLX, HOSTAP_HW_PCI } hw_type;
+};
+
+
+struct prism2_download_data {
+       u32 dl_cmd;
+       u32 start_addr;
+       u32 num_areas;
+       struct prism2_download_data_area {
+               u32 addr; /* wlan card address */
+               u32 len;
+               u8 *data; /* allocated data */
+       } data[0];
+};
+
+
+#define HOSTAP_MAX_BSS_COUNT 64
+#define MAX_WPA_IE_LEN 64
+
+struct hostap_bss_info {
+       struct list_head list;
+       unsigned long last_update;
+       unsigned int count;
+       u8 bssid[ETH_ALEN];
+       u16 capab_info;
+       u8 ssid[32];
+       size_t ssid_len;
+       u8 wpa_ie[MAX_WPA_IE_LEN];
+       size_t wpa_ie_len;
+       u8 rsn_ie[MAX_WPA_IE_LEN];
+       size_t rsn_ie_len;
+       int chan;
+       int included;
+};
+
+
+/* Per radio private Host AP data - shared by all net devices interfaces used
+ * by each radio (wlan#, wlan#ap, wlan#sta, WDS).
+ * ((struct hostap_interface *) netdev_priv(dev))->local points to this
+ * structure. */
+struct local_info {
+       struct module *hw_module;
+       int card_idx;
+       int dev_enabled;
+       int master_dev_auto_open; /* was master device opened automatically */
+       int num_dev_open; /* number of open devices */
+       struct net_device *dev; /* master radio device */
+       struct net_device *ddev; /* main data device */
+       struct list_head hostap_interfaces; /* Host AP interface list (contains
+                                            * struct hostap_interface entries)
+                                            */
+       rwlock_t iface_lock; /* hostap_interfaces read lock; use write lock
+                             * when removing entries from the list.
+                             * TX and RX paths can use read lock. */
+       spinlock_t cmdlock, baplock, lock;
+       struct semaphore rid_bap_sem;
+       u16 infofid; /* MAC buffer id for info frame */
+       /* txfid, intransmitfid, next_txtid, and next_alloc are protected by
+        * txfidlock */
+       spinlock_t txfidlock;
+       int txfid_len; /* length of allocated TX buffers */
+       u16 txfid[PRISM2_TXFID_COUNT]; /* buffer IDs for TX frames */
+       /* buffer IDs for intransmit frames or PRISM2_TXFID_EMPTY if
+        * corresponding txfid is free for next TX frame */
+       u16 intransmitfid[PRISM2_TXFID_COUNT];
+       int next_txfid; /* index to the next txfid to be checked for
+                        * availability */
+       int next_alloc; /* index to the next intransmitfid to be checked for
+                        * allocation events */
+
+       /* bitfield for atomic bitops */
+#define HOSTAP_BITS_TRANSMIT 0
+#define HOSTAP_BITS_BAP_TASKLET 1
+#define HOSTAP_BITS_BAP_TASKLET2 2
+       long bits;
+
+       struct ap_data *ap;
+
+       char essid[MAX_SSID_LEN + 1];
+       char name[MAX_NAME_LEN + 1];
+       int name_set;
+       u16 channel_mask; /* mask of allowed channels */
+       u16 scan_channel_mask; /* mask of channels to be scanned */
+       struct comm_tallies_sums comm_tallies;
+       struct net_device_stats stats;
+       struct proc_dir_entry *proc;
+       int iw_mode; /* operating mode (IW_MODE_*) */
+       int pseudo_adhoc; /* 0: IW_MODE_ADHOC is real 802.11 compliant IBSS
+                          * 1: IW_MODE_ADHOC is "pseudo IBSS" */
+       char bssid[ETH_ALEN];
+       int channel;
+       int beacon_int;
+       int dtim_period;
+       int mtu;
+       int frame_dump; /* dump RX/TX frame headers, PRISM2_DUMP_ flags */
+       int fw_tx_rate_control;
+       u16 tx_rate_control;
+       u16 basic_rates;
+       int hw_resetting;
+       int hw_ready;
+       int hw_reset_tries; /* how many times reset has been tried */
+       int hw_downloading;
+       int shutdown;
+       int pri_only;
+       int no_pri; /* no PRI f/w present */
+       int sram_type; /* 8 = x8 SRAM, 16 = x16 SRAM, -1 = unknown */
+
+       enum {
+               PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF,
+               PRISM2_TXPOWER_FIXED, PRISM2_TXPOWER_UNKNOWN
+       } txpower_type;
+       int txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */
+
+       /* command queue for hfa384x_cmd(); protected with cmdlock */
+       struct list_head cmd_queue;
+       /* max_len for cmd_queue; in addition, cmd_callback can use two
+        * additional entries to prevent sleeping commands from stopping
+        * transmits */
+#define HOSTAP_CMD_QUEUE_MAX_LEN 16
+       int cmd_queue_len; /* number of entries in cmd_queue */
+
+       /* if card timeout is detected in interrupt context, reset_queue is
+        * used to schedule card reseting to be done in user context */
+       struct work_struct reset_queue;
+
+       /* For scheduling a change of the promiscuous mode RID */
+       int is_promisc;
+       struct work_struct set_multicast_list_queue;
+
+       struct work_struct set_tim_queue;
+       struct list_head set_tim_list;
+       spinlock_t set_tim_lock;
+
+       int wds_max_connections;
+       int wds_connections;
+#define HOSTAP_WDS_BROADCAST_RA BIT(0)
+#define HOSTAP_WDS_AP_CLIENT BIT(1)
+#define HOSTAP_WDS_STANDARD_FRAME BIT(2)
+       u32 wds_type;
+       u16 tx_control; /* flags to be used in TX description */
+       int manual_retry_count; /* -1 = use f/w default; otherwise retry count
+                                * to be used with all frames */
+
+       struct iw_statistics wstats;
+       unsigned long scan_timestamp; /* Time started to scan */
+       enum {
+               PRISM2_MONITOR_80211 = 0, PRISM2_MONITOR_PRISM = 1,
+               PRISM2_MONITOR_CAPHDR = 2
+       } monitor_type;
+       int (*saved_eth_header_parse)(struct sk_buff *skb,
+                                     unsigned char *haddr);
+       int monitor_allow_fcserr;
+
+       int hostapd; /* whether user space daemon, hostapd, is used for AP
+                     * management */
+       int hostapd_sta; /* whether hostapd is used with an extra STA interface
+                         */
+       struct net_device *apdev;
+       struct net_device_stats apdevstats;
+
+       char assoc_ap_addr[ETH_ALEN];
+       struct net_device *stadev;
+       struct net_device_stats stadevstats;
+
+#define WEP_KEYS 4
+#define WEP_KEY_LEN 13
+       struct ieee80211_crypt_data *crypt[WEP_KEYS];
+       int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
+       struct timer_list crypt_deinit_timer;
+       struct list_head crypt_deinit_list;
+
+       int open_wep; /* allow unencrypted frames */
+       int host_encrypt;
+       int host_decrypt;
+       int privacy_invoked; /* force privacy invoked flag even if no keys are
+                             * configured */
+       int fw_encrypt_ok; /* whether firmware-based WEP encrypt is working
+                           * in Host AP mode (STA f/w 1.4.9 or newer) */
+       int bcrx_sta_key; /* use individual keys to override default keys even
+                          * with RX of broad/multicast frames */
+
+       struct prism2_frag_entry frag_cache[PRISM2_FRAG_CACHE_LEN];
+       unsigned int frag_next_idx;
+
+       int ieee_802_1x; /* is IEEE 802.1X used */
+
+       int antsel_tx, antsel_rx;
+       int rts_threshold; /* dot11RTSThreshold */
+       int fragm_threshold; /* dot11FragmentationThreshold */
+       int auth_algs; /* PRISM2_AUTH_ flags */
+
+       int enh_sec; /* cnfEnhSecurity options (broadcast SSID hide/ignore) */
+       int tallies32; /* 32-bit tallies in use */
+
+       struct prism2_helper_functions *func;
+
+       u8 *pda;
+       int fw_ap;
+#define PRISM2_FW_VER(major, minor, variant) \
+(((major) << 16) | ((minor) << 8) | variant)
+       u32 sta_fw_ver;
+
+       /* Tasklets for handling hardware IRQ related operations outside hw IRQ
+        * handler */
+       struct tasklet_struct bap_tasklet;
+
+       struct tasklet_struct info_tasklet;
+       struct sk_buff_head info_list; /* info frames as skb's for
+                                       * info_tasklet */
+
+       struct hostap_tx_callback_info *tx_callback; /* registered TX callbacks
+                                                     */
+
+       struct tasklet_struct rx_tasklet;
+       struct sk_buff_head rx_list;
+
+       struct tasklet_struct sta_tx_exc_tasklet;
+       struct sk_buff_head sta_tx_exc_list;
+
+       int host_roaming;
+       unsigned long last_join_time; /* time of last JoinRequest */
+       struct hfa384x_hostscan_result *last_scan_results;
+       int last_scan_results_count;
+       enum { PRISM2_SCAN, PRISM2_HOSTSCAN } last_scan_type;
+       struct work_struct info_queue;
+       long pending_info; /* bit field of pending info_queue items */
+#define PRISM2_INFO_PENDING_LINKSTATUS 0
+#define PRISM2_INFO_PENDING_SCANRESULTS 1
+       int prev_link_status; /* previous received LinkStatus info */
+       int prev_linkstatus_connected;
+       u8 preferred_ap[6]; /* use this AP if possible */
+
+#ifdef PRISM2_CALLBACK
+       void *callback_data; /* Can be used in callbacks; e.g., allocate
+                             * on enable event and free on disable event.
+                             * Host AP driver code does not touch this. */
+#endif /* PRISM2_CALLBACK */
+
+       wait_queue_head_t hostscan_wq;
+
+       /* Passive scan in Host AP mode */
+       struct timer_list passive_scan_timer;
+       int passive_scan_interval; /* in seconds, 0 = disabled */
+       int passive_scan_channel;
+       enum { PASSIVE_SCAN_WAIT, PASSIVE_SCAN_LISTEN } passive_scan_state;
+
+       struct timer_list tick_timer;
+       unsigned long last_tick_timer;
+       unsigned int sw_tick_stuck;
+
+       /* commsQuality / dBmCommsQuality data from periodic polling; only
+        * valid for Managed and Ad-hoc modes */
+       unsigned long last_comms_qual_update;
+       int comms_qual; /* in some odd unit.. */
+       int avg_signal; /* in dB (note: negative) */
+       int avg_noise; /* in dB (note: negative) */
+       struct work_struct comms_qual_update;
+
+       /* RSSI to dBm adjustment (for RX descriptor fields) */
+       int rssi_to_dBm; /* substract from RSSI to get approximate dBm value */
+
+       /* BSS list / protected by local->lock */
+       struct list_head bss_list;
+       int num_bss_info;
+       int wpa; /* WPA support enabled */
+       int tkip_countermeasures;
+       int drop_unencrypted;
+       /* Generic IEEE 802.11 info element to be added to
+        * ProbeResp/Beacon/(Re)AssocReq */
+       u8 *generic_elem;
+       size_t generic_elem_len;
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+       /* Persistent volatile download data */
+       struct prism2_download_data *dl_pri;
+       struct prism2_download_data *dl_sec;
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+#ifdef PRISM2_IO_DEBUG
+#define PRISM2_IO_DEBUG_SIZE 10000
+       u32 io_debug[PRISM2_IO_DEBUG_SIZE];
+       int io_debug_head;
+       int io_debug_enabled;
+#endif /* PRISM2_IO_DEBUG */
+
+       /* Pointer to hardware model specific (cs,pci,plx) private data. */
+       void *hw_priv;
+};
+
+
+/* Per interface private Host AP data
+ * Allocated for each net device that Host AP uses (wlan#, wlan#ap, wlan#sta,
+ * WDS) and netdev_priv(dev) points to this structure. */
+struct hostap_interface {
+       struct list_head list; /* list entry in Host AP interface list */
+       struct net_device *dev; /* pointer to this device */
+       struct local_info *local; /* pointer to shared private data */
+       struct net_device_stats stats;
+       struct iw_spy_data spy_data; /* iwspy support */
+       struct iw_public_data wireless_data;
+
+       enum {
+               HOSTAP_INTERFACE_MASTER,
+               HOSTAP_INTERFACE_MAIN,
+               HOSTAP_INTERFACE_AP,
+               HOSTAP_INTERFACE_STA,
+               HOSTAP_INTERFACE_WDS,
+       } type;
+
+       union {
+               struct hostap_interface_wds {
+                       u8 remote_addr[ETH_ALEN];
+               } wds;
+       } u;
+};
+
+
+#define HOSTAP_SKB_TX_DATA_MAGIC 0xf08a36a2
+
+/*
+ * TX meta data - stored in skb->cb buffer, so this must not be increased over
+ * the 40-byte limit
+ */
+struct hostap_skb_tx_data {
+       u32 magic; /* HOSTAP_SKB_TX_DATA_MAGIC */
+       u8 rate; /* transmit rate */
+#define HOSTAP_TX_FLAGS_WDS BIT(0)
+#define HOSTAP_TX_FLAGS_BUFFERED_FRAME BIT(1)
+#define HOSTAP_TX_FLAGS_ADD_MOREDATA BIT(2)
+       u8 flags; /* HOSTAP_TX_FLAGS_* */
+       u16 tx_cb_idx;
+       struct hostap_interface *iface;
+       unsigned long jiffies; /* queueing timestamp */
+       unsigned short ethertype;
+};
+
+
+#ifndef PRISM2_NO_DEBUG
+
+#define DEBUG_FID BIT(0)
+#define DEBUG_PS BIT(1)
+#define DEBUG_FLOW BIT(2)
+#define DEBUG_AP BIT(3)
+#define DEBUG_HW BIT(4)
+#define DEBUG_EXTRA BIT(5)
+#define DEBUG_EXTRA2 BIT(6)
+#define DEBUG_PS2 BIT(7)
+#define DEBUG_MASK (DEBUG_PS | DEBUG_AP | DEBUG_HW | DEBUG_EXTRA)
+#define PDEBUG(n, args...) \
+do { if ((n) & DEBUG_MASK) printk(KERN_DEBUG args); } while (0)
+#define PDEBUG2(n, args...) \
+do { if ((n) & DEBUG_MASK) printk(args); } while (0)
+
+#else /* PRISM2_NO_DEBUG */
+
+#define PDEBUG(n, args...)
+#define PDEBUG2(n, args...)
+
+#endif /* PRISM2_NO_DEBUG */
+
+enum { BAP0 = 0, BAP1 = 1 };
+
+#define PRISM2_IO_DEBUG_CMD_INB 0
+#define PRISM2_IO_DEBUG_CMD_INW 1
+#define PRISM2_IO_DEBUG_CMD_INSW 2
+#define PRISM2_IO_DEBUG_CMD_OUTB 3
+#define PRISM2_IO_DEBUG_CMD_OUTW 4
+#define PRISM2_IO_DEBUG_CMD_OUTSW 5
+#define PRISM2_IO_DEBUG_CMD_ERROR 6
+#define PRISM2_IO_DEBUG_CMD_INTERRUPT 7
+
+#ifdef PRISM2_IO_DEBUG
+
+#define PRISM2_IO_DEBUG_ENTRY(cmd, reg, value) \
+(((cmd) << 24) | ((reg) << 16) | value)
+
+static inline void prism2_io_debug_add(struct net_device *dev, int cmd,
+                                      int reg, int value)
+{
+       struct hostap_interface *iface = netdev_priv(dev);
+       local_info_t *local = iface->local;
+
+       if (!local->io_debug_enabled)
+               return;
+
+       local->io_debug[local->io_debug_head] = jiffies & 0xffffffff;
+       if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE)
+               local->io_debug_head = 0;
+       local->io_debug[local->io_debug_head] =
+               PRISM2_IO_DEBUG_ENTRY(cmd, reg, value);
+       if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE)
+               local->io_debug_head = 0;
+}
+
+
+static inline void prism2_io_debug_error(struct net_device *dev, int err)
+{
+       struct hostap_interface *iface = netdev_priv(dev);
+       local_info_t *local = iface->local;
+       unsigned long flags;
+
+       if (!local->io_debug_enabled)
+               return;
+
+       spin_lock_irqsave(&local->lock, flags);
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_ERROR, 0, err);
+       if (local->io_debug_enabled == 1) {
+               local->io_debug_enabled = 0;
+               printk(KERN_DEBUG "%s: I/O debug stopped\n", dev->name);
+       }
+       spin_unlock_irqrestore(&local->lock, flags);
+}
+
+#else /* PRISM2_IO_DEBUG */
+
+static inline void prism2_io_debug_add(struct net_device *dev, int cmd,
+                                      int reg, int value)
+{
+}
+
+static inline void prism2_io_debug_error(struct net_device *dev, int err)
+{
+}
+
+#endif /* PRISM2_IO_DEBUG */
+
+
+#ifdef PRISM2_CALLBACK
+enum {
+       /* Called when card is enabled */
+       PRISM2_CALLBACK_ENABLE,
+
+       /* Called when card is disabled */
+       PRISM2_CALLBACK_DISABLE,
+
+       /* Called when RX/TX starts/ends */
+       PRISM2_CALLBACK_RX_START, PRISM2_CALLBACK_RX_END,
+       PRISM2_CALLBACK_TX_START, PRISM2_CALLBACK_TX_END
+};
+void prism2_callback(local_info_t *local, int event);
+#else /* PRISM2_CALLBACK */
+#define prism2_callback(d, e) do { } while (0)
+#endif /* PRISM2_CALLBACK */
+
+#endif /* __KERNEL__ */
+
+#endif /* HOSTAP_WLAN_H */
diff --git a/drivers/net/wireless/ieee802_11.h b/drivers/net/wireless/ieee802_11.h
deleted file mode 100644 (file)
index 53dd524..0000000
+++ /dev/null
@@ -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 */
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
new file mode 100644 (file)
index 0000000..a47fce4
--- /dev/null
@@ -0,0 +1,8679 @@
+/******************************************************************************
+
+  Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+
+  Contact Information:
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+  Portions of this file are based on the sample_* files provided by Wireless
+  Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes
+  <jt@hpl.hp.com>
+
+  Portions of this file are based on the Host AP project,
+  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>
+
+  Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
+  ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
+  available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox
+
+******************************************************************************/
+/*
+
+ Initial driver on which this is based was developed by Janusz Gorycki,
+ Maciej Urbaniak, and Maciej Sosnowski.
+
+ Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak.
+
+Theory of Operation
+
+Tx - Commands and Data
+
+Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs)
+Each TBD contains a pointer to the physical (dma_addr_t) address of data being
+sent to the firmware as well as the length of the data.
+
+The host writes to the TBD queue at the WRITE index.  The WRITE index points
+to the _next_ packet to be written and is advanced when after the TBD has been
+filled.
+
+The firmware pulls from the TBD queue at the READ index.  The READ index points
+to the currently being read entry, and is advanced once the firmware is
+done with a packet.
+
+When data is sent to the firmware, the first TBD is used to indicate to the
+firmware if a Command or Data is being sent.  If it is Command, all of the
+command information is contained within the physical address referred to by the
+TBD.  If it is Data, the first TBD indicates the type of data packet, number
+of fragments, etc.  The next TBD then referrs to the actual packet location.
+
+The Tx flow cycle is as follows:
+
+1) ipw2100_tx() is called by kernel with SKB to transmit
+2) Packet is move from the tx_free_list and appended to the transmit pending
+   list (tx_pend_list)
+3) work is scheduled to move pending packets into the shared circular queue.
+4) when placing packet in the circular queue, the incoming SKB is DMA mapped
+   to a physical address.  That address is entered into a TBD.  Two TBDs are
+   filled out.  The first indicating a data packet, the second referring to the
+   actual payload data.
+5) the packet is removed from tx_pend_list and placed on the end of the
+   firmware pending list (fw_pend_list)
+6) firmware is notified that the WRITE index has
+7) Once the firmware has processed the TBD, INTA is triggered.
+8) For each Tx interrupt received from the firmware, the READ index is checked
+   to see which TBDs are done being processed.
+9) For each TBD that has been processed, the ISR pulls the oldest packet
+   from the fw_pend_list.
+10)The packet structure contained in the fw_pend_list is then used
+   to unmap the DMA address and to free the SKB originally passed to the driver
+   from the kernel.
+11)The packet structure is placed onto the tx_free_list
+
+The above steps are the same for commands, only the msg_free_list/msg_pend_list
+are used instead of tx_free_list/tx_pend_list
+
+...
+
+Critical Sections / Locking :
+
+There are two locks utilized.  The first is the low level lock (priv->low_lock)
+that protects the following:
+
+- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows:
+
+  tx_free_list : Holds pre-allocated Tx buffers.
+    TAIL modified in __ipw2100_tx_process()
+    HEAD modified in ipw2100_tx()
+
+  tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring
+    TAIL modified ipw2100_tx()
+    HEAD modified by ipw2100_tx_send_data()
+
+  msg_free_list : Holds pre-allocated Msg (Command) buffers
+    TAIL modified in __ipw2100_tx_process()
+    HEAD modified in ipw2100_hw_send_command()
+
+  msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring
+    TAIL modified in ipw2100_hw_send_command()
+    HEAD modified in ipw2100_tx_send_commands()
+
+  The flow of data on the TX side is as follows:
+
+  MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST
+  TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST
+
+  The methods that work on the TBD ring are protected via priv->low_lock.
+
+- The internal data state of the device itself
+- Access to the firmware read/write indexes for the BD queues
+  and associated logic
+
+All external entry functions are locked with the priv->action_lock to ensure
+that only one external action is invoked at a time.
+
+
+*/
+
+#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/kmod.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <linux/stringify.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/time.h>
+#include <linux/firmware.h>
+#include <linux/acpi.h>
+#include <linux/ctype.h>
+
+#include "ipw2100.h"
+
+#define IPW2100_VERSION "1.1.0"
+
+#define DRV_NAME       "ipw2100"
+#define DRV_VERSION    IPW2100_VERSION
+#define DRV_DESCRIPTION        "Intel(R) PRO/Wireless 2100 Network Driver"
+#define DRV_COPYRIGHT  "Copyright(c) 2003-2004 Intel Corporation"
+
+
+/* Debugging stuff */
+#ifdef CONFIG_IPW_DEBUG
+#define CONFIG_IPW2100_RX_DEBUG   /* Reception debugging */
+#endif
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+static int mode = 0;
+static int channel = 0;
+static int associate = 1;
+static int disable = 0;
+#ifdef CONFIG_PM
+static struct ipw2100_fw ipw2100_firmware;
+#endif
+
+#include <linux/moduleparam.h>
+module_param(debug, int, 0444);
+module_param(mode, int, 0444);
+module_param(channel, int, 0444);
+module_param(associate, int, 0444);
+module_param(disable, int, 0444);
+
+MODULE_PARM_DESC(debug, "debug level");
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
+MODULE_PARM_DESC(channel, "channel");
+MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
+
+static u32 ipw2100_debug_level = IPW_DL_NONE;
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW_DEBUG(level, message...) \
+do { \
+       if (ipw2100_debug_level & (level)) { \
+               printk(KERN_DEBUG "ipw2100: %c %s ", \
+                       in_interrupt() ? 'I' : 'U',  __FUNCTION__); \
+               printk(message); \
+       } \
+} while (0)
+#else
+#define IPW_DEBUG(level, message...) do {} while (0)
+#endif /* CONFIG_IPW_DEBUG */
+
+#ifdef CONFIG_IPW_DEBUG
+static const char *command_types[] = {
+       "undefined",
+       "unused", /* HOST_ATTENTION */
+       "HOST_COMPLETE",
+       "unused", /* SLEEP */
+       "unused", /* HOST_POWER_DOWN */
+       "unused",
+       "SYSTEM_CONFIG",
+       "unused", /* SET_IMR */
+       "SSID",
+       "MANDATORY_BSSID",
+       "AUTHENTICATION_TYPE",
+       "ADAPTER_ADDRESS",
+       "PORT_TYPE",
+       "INTERNATIONAL_MODE",
+       "CHANNEL",
+       "RTS_THRESHOLD",
+       "FRAG_THRESHOLD",
+       "POWER_MODE",
+       "TX_RATES",
+       "BASIC_TX_RATES",
+       "WEP_KEY_INFO",
+       "unused",
+       "unused",
+       "unused",
+       "unused",
+       "WEP_KEY_INDEX",
+       "WEP_FLAGS",
+       "ADD_MULTICAST",
+       "CLEAR_ALL_MULTICAST",
+       "BEACON_INTERVAL",
+       "ATIM_WINDOW",
+       "CLEAR_STATISTICS",
+       "undefined",
+       "undefined",
+       "undefined",
+       "undefined",
+       "TX_POWER_INDEX",
+       "undefined",
+       "undefined",
+       "undefined",
+       "undefined",
+       "undefined",
+       "undefined",
+       "BROADCAST_SCAN",
+       "CARD_DISABLE",
+       "PREFERRED_BSSID",
+       "SET_SCAN_OPTIONS",
+       "SCAN_DWELL_TIME",
+       "SWEEP_TABLE",
+       "AP_OR_STATION_TABLE",
+       "GROUP_ORDINALS",
+       "SHORT_RETRY_LIMIT",
+       "LONG_RETRY_LIMIT",
+       "unused", /* SAVE_CALIBRATION */
+       "unused", /* RESTORE_CALIBRATION */
+       "undefined",
+       "undefined",
+       "undefined",
+       "HOST_PRE_POWER_DOWN",
+       "unused", /* HOST_INTERRUPT_COALESCING */
+       "undefined",
+       "CARD_DISABLE_PHY_OFF",
+       "MSDU_TX_RATES"
+       "undefined",
+       "undefined",
+       "SET_STATION_STAT_BITS",
+       "CLEAR_STATIONS_STAT_BITS",
+       "LEAP_ROGUE_MODE",
+       "SET_SECURITY_INFORMATION",
+       "DISASSOCIATION_BSSID",
+       "SET_WPA_ASS_IE"
+};
+#endif
+
+
+/* Pre-decl until we get the code solid and then we can clean it up */
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv);
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv);
+
+static void ipw2100_queues_initialize(struct ipw2100_priv *priv);
+static void ipw2100_queues_free(struct ipw2100_priv *priv);
+static int ipw2100_queues_allocate(struct ipw2100_priv *priv);
+
+static int ipw2100_fw_download(struct ipw2100_priv *priv,
+                              struct ipw2100_fw *fw);
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+                               struct ipw2100_fw *fw);
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+                                size_t max);
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+                                   size_t max);
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+                                    struct ipw2100_fw *fw);
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+                                 struct ipw2100_fw *fw);
+static void ipw2100_wx_event_work(struct ipw2100_priv *priv);
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device * dev);
+static struct iw_handler_def ipw2100_wx_handler_def;
+
+
+static inline void read_register(struct net_device *dev, u32 reg, u32 *val)
+{
+       *val = readl((void *)(dev->base_addr + reg));
+       IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val);
+}
+
+static inline void write_register(struct net_device *dev, u32 reg, u32 val)
+{
+       writel(val, (void *)(dev->base_addr + reg));
+       IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val);
+}
+
+static inline void read_register_word(struct net_device *dev, u32 reg, u16 *val)
+{
+       *val = readw((void *)(dev->base_addr + reg));
+       IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val);
+}
+
+static inline void read_register_byte(struct net_device *dev, u32 reg, u8 *val)
+{
+       *val = readb((void *)(dev->base_addr + reg));
+       IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val);
+}
+
+static inline void write_register_word(struct net_device *dev, u32 reg, u16 val)
+{
+       writew(val, (void *)(dev->base_addr + reg));
+       IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val);
+}
+
+
+static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val)
+{
+       writeb(val, (void *)(dev->base_addr + reg));
+       IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val);
+}
+
+static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 *val)
+{
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                      addr & IPW_REG_INDIRECT_ADDR_MASK);
+       read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val)
+{
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                      addr & IPW_REG_INDIRECT_ADDR_MASK);
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_word(struct net_device *dev, u32 addr, u16 *val)
+{
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                      addr & IPW_REG_INDIRECT_ADDR_MASK);
+       read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val)
+{
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                      addr & IPW_REG_INDIRECT_ADDR_MASK);
+       write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 *val)
+{
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                      addr & IPW_REG_INDIRECT_ADDR_MASK);
+       read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val)
+{
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                      addr & IPW_REG_INDIRECT_ADDR_MASK);
+       write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr)
+{
+       write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+                      addr & IPW_REG_INDIRECT_ADDR_MASK);
+}
+
+static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val)
+{
+       write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val);
+}
+
+static inline void write_nic_memory(struct net_device *dev, u32 addr, u32 len,
+                                   const u8 *buf)
+{
+       u32 aligned_addr;
+       u32 aligned_len;
+       u32 dif_len;
+       u32 i;
+
+       /* read first nibble byte by byte */
+       aligned_addr = addr & (~0x3);
+       dif_len = addr - aligned_addr;
+       if (dif_len) {
+               /* Start reading at aligned_addr + dif_len */
+               write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                              aligned_addr);
+               for (i = dif_len; i < 4; i++, buf++)
+                       write_register_byte(
+                               dev, IPW_REG_INDIRECT_ACCESS_DATA + i,
+                               *buf);
+
+               len -= dif_len;
+               aligned_addr += 4;
+       }
+
+       /* read DWs through autoincrement registers */
+       write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+                      aligned_addr);
+       aligned_len = len & (~0x3);
+       for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+               write_register(
+                       dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *)buf);
+
+       /* copy the last nibble */
+       dif_len = len - aligned_len;
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
+       for (i = 0; i < dif_len; i++, buf++)
+               write_register_byte(
+                       dev, IPW_REG_INDIRECT_ACCESS_DATA + i, *buf);
+}
+
+static inline void read_nic_memory(struct net_device *dev, u32 addr, u32 len,
+                                  u8 *buf)
+{
+       u32 aligned_addr;
+       u32 aligned_len;
+       u32 dif_len;
+       u32 i;
+
+       /* read first nibble byte by byte */
+       aligned_addr = addr & (~0x3);
+       dif_len = addr - aligned_addr;
+       if (dif_len) {
+               /* Start reading at aligned_addr + dif_len */
+               write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                              aligned_addr);
+               for (i = dif_len; i < 4; i++, buf++)
+                       read_register_byte(
+                               dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf);
+
+               len -= dif_len;
+               aligned_addr += 4;
+       }
+
+       /* read DWs through autoincrement registers */
+       write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+                      aligned_addr);
+       aligned_len = len & (~0x3);
+       for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+               read_register(dev, IPW_REG_AUTOINCREMENT_DATA,
+                             (u32 *)buf);
+
+       /* copy the last nibble */
+       dif_len = len - aligned_len;
+       write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+                      aligned_addr);
+       for (i = 0; i < dif_len; i++, buf++)
+               read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA +
+                                  i, buf);
+}
+
+static inline int ipw2100_hw_is_adapter_in_system(struct net_device *dev)
+{
+       return (dev->base_addr &&
+               (readl((void *)(dev->base_addr + IPW_REG_DOA_DEBUG_AREA_START))
+                == IPW_DATA_DOA_DEBUG_VALUE));
+}
+
+static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
+                              void *val, u32 *len)
+{
+       struct ipw2100_ordinals *ordinals = &priv->ordinals;
+       u32 addr;
+       u32 field_info;
+       u16 field_len;
+       u16 field_count;
+       u32 total_length;
+
+       if (ordinals->table1_addr == 0) {
+               printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals "
+                      "before they have been loaded.\n");
+               return -EINVAL;
+       }
+
+       if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+               if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) {
+                       *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+                       printk(KERN_WARNING DRV_NAME
+                              ": ordinal buffer length too small, need %zd\n",
+                              IPW_ORD_TAB_1_ENTRY_SIZE);
+
+                       return -EINVAL;
+               }
+
+               read_nic_dword(priv->net_dev, ordinals->table1_addr + (ord << 2),
+                              &addr);
+               read_nic_dword(priv->net_dev, addr, val);
+
+               *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+               return 0;
+       }
+
+       if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) {
+
+               ord -= IPW_START_ORD_TAB_2;
+
+               /* get the address of statistic */
+               read_nic_dword(priv->net_dev, ordinals->table2_addr + (ord << 3),
+                              &addr);
+
+               /* get the second DW of statistics ;
+                * two 16-bit words - first is length, second is count */
+               read_nic_dword(priv->net_dev,
+                              ordinals->table2_addr + (ord << 3) + sizeof(u32),
+                              &field_info);
+
+               /* get each entry length */
+               field_len = *((u16 *)&field_info);
+
+               /* get number of entries */
+               field_count = *(((u16 *)&field_info) + 1);
+
+               /* abort if no enought memory */
+               total_length = field_len * field_count;
+               if (total_length > *len) {
+                       *len = total_length;
+                       return -EINVAL;
+               }
+
+               *len = total_length;
+               if (!total_length)
+                       return 0;
+
+               /* read the ordinal data from the SRAM */
+               read_nic_memory(priv->net_dev, addr, total_length, val);
+
+               return 0;
+       }
+
+       printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor "
+              "in table 2\n", ord);
+
+       return -EINVAL;
+}
+
+static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 *val,
+                              u32 *len)
+{
+       struct ipw2100_ordinals *ordinals = &priv->ordinals;
+       u32 addr;
+
+       if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+               if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) {
+                       *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+                       IPW_DEBUG_INFO("wrong size\n");
+                       return -EINVAL;
+               }
+
+               read_nic_dword(priv->net_dev, ordinals->table1_addr + (ord << 2),
+                              &addr);
+
+               write_nic_dword(priv->net_dev, addr, *val);
+
+               *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+               return 0;
+       }
+
+       IPW_DEBUG_INFO("wrong table\n");
+       if (IS_ORDINAL_TABLE_TWO(ordinals, ord))
+               return -EINVAL;
+
+       return -EINVAL;
+}
+
+static char *snprint_line(char *buf, size_t count,
+                         const u8 *data, u32 len, u32 ofs)
+{
+       int out, i, j, l;
+       char c;
+
+       out = snprintf(buf, count, "%08X", ofs);
+
+       for (l = 0, i = 0; i < 2; i++) {
+               out += snprintf(buf + out, count - out, " ");
+               for (j = 0; j < 8 && l < len; j++, l++)
+                       out += snprintf(buf + out, count - out, "%02X ",
+                                       data[(i * 8 + j)]);
+               for (; j < 8; j++)
+                       out += snprintf(buf + out, count - out, "   ");
+       }
+
+       out += snprintf(buf + out, count - out, " ");
+       for (l = 0, i = 0; i < 2; i++) {
+               out += snprintf(buf + out, count - out, " ");
+               for (j = 0; j < 8 && l < len; j++, l++) {
+                       c = data[(i * 8 + j)];
+                       if (!isascii(c) || !isprint(c))
+                               c = '.';
+
+                       out += snprintf(buf + out, count - out, "%c", c);
+               }
+
+               for (; j < 8; j++)
+                       out += snprintf(buf + out, count - out, " ");
+       }
+
+       return buf;
+}
+
+static void printk_buf(int level, const u8 *data, u32 len)
+{
+       char line[81];
+       u32 ofs = 0;
+       if (!(ipw2100_debug_level & level))
+               return;
+
+       while (len) {
+               printk(KERN_DEBUG "%s\n",
+                      snprint_line(line, sizeof(line), &data[ofs],
+                                   min(len, 16U), ofs));
+               ofs += 16;
+               len -= min(len, 16U);
+       }
+}
+
+
+
+#define MAX_RESET_BACKOFF 10
+
+static inline void schedule_reset(struct ipw2100_priv *priv)
+{
+       unsigned long now = get_seconds();
+
+       /* If we haven't received a reset request within the backoff period,
+        * then we can reset the backoff interval so this reset occurs
+        * immediately */
+       if (priv->reset_backoff &&
+           (now - priv->last_reset > priv->reset_backoff))
+               priv->reset_backoff = 0;
+
+       priv->last_reset = get_seconds();
+
+       if (!(priv->status & STATUS_RESET_PENDING)) {
+               IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n",
+                              priv->net_dev->name, priv->reset_backoff);
+               netif_carrier_off(priv->net_dev);
+               netif_stop_queue(priv->net_dev);
+               priv->status |= STATUS_RESET_PENDING;
+               if (priv->reset_backoff)
+                       queue_delayed_work(priv->workqueue, &priv->reset_work,
+                                          priv->reset_backoff * HZ);
+               else
+                       queue_work(priv->workqueue, &priv->reset_work);
+
+               if (priv->reset_backoff < MAX_RESET_BACKOFF)
+                       priv->reset_backoff++;
+
+               wake_up_interruptible(&priv->wait_command_queue);
+       } else
+               IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n",
+                              priv->net_dev->name);
+
+}
+
+#define HOST_COMPLETE_TIMEOUT (2 * HZ)
+static int ipw2100_hw_send_command(struct ipw2100_priv *priv,
+                                  struct host_command * cmd)
+{
+       struct list_head *element;
+       struct ipw2100_tx_packet *packet;
+       unsigned long flags;
+       int err = 0;
+
+       IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
+                    command_types[cmd->host_command], cmd->host_command,
+                    cmd->host_command_length);
+       printk_buf(IPW_DL_HC, (u8*)cmd->host_command_parameters,
+                  cmd->host_command_length);
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+
+       if (priv->fatal_error) {
+               IPW_DEBUG_INFO("Attempt to send command while hardware in fatal error condition.\n");
+               err = -EIO;
+               goto fail_unlock;
+       }
+
+       if (!(priv->status & STATUS_RUNNING)) {
+               IPW_DEBUG_INFO("Attempt to send command while hardware is not running.\n");
+               err = -EIO;
+               goto fail_unlock;
+       }
+
+       if (priv->status & STATUS_CMD_ACTIVE) {
+               IPW_DEBUG_INFO("Attempt to send command while another command is pending.\n");
+               err = -EBUSY;
+               goto fail_unlock;
+       }
+
+       if (list_empty(&priv->msg_free_list)) {
+               IPW_DEBUG_INFO("no available msg buffers\n");
+               goto fail_unlock;
+       }
+
+       priv->status |= STATUS_CMD_ACTIVE;
+       priv->messages_sent++;
+
+       element = priv->msg_free_list.next;
+
+       packet = list_entry(element, struct ipw2100_tx_packet, list);
+       packet->jiffy_start = jiffies;
+
+       /* initialize the firmware command packet */
+       packet->info.c_struct.cmd->host_command_reg = cmd->host_command;
+       packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1;
+       packet->info.c_struct.cmd->host_command_len_reg = cmd->host_command_length;
+       packet->info.c_struct.cmd->sequence = cmd->host_command_sequence;
+
+       memcpy(packet->info.c_struct.cmd->host_command_params_reg,
+              cmd->host_command_parameters,
+              sizeof(packet->info.c_struct.cmd->host_command_params_reg));
+
+       list_del(element);
+       DEC_STAT(&priv->msg_free_stat);
+
+       list_add_tail(element, &priv->msg_pend_list);
+       INC_STAT(&priv->msg_pend_stat);
+
+       ipw2100_tx_send_commands(priv);
+       ipw2100_tx_send_data(priv);
+
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       /*
+        * We must wait for this command to complete before another
+        * command can be sent...  but if we wait more than 3 seconds
+        * then there is a problem.
+        */
+
+       err = wait_event_interruptible_timeout(
+               priv->wait_command_queue, !(priv->status & STATUS_CMD_ACTIVE),
+               HOST_COMPLETE_TIMEOUT);
+
+       if (err == 0) {
+               IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
+                              HOST_COMPLETE_TIMEOUT / (HZ / 100));
+               priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT;
+               priv->status &= ~STATUS_CMD_ACTIVE;
+               schedule_reset(priv);
+               return -EIO;
+       }
+
+       if (priv->fatal_error) {
+               printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n",
+                      priv->net_dev->name);
+               return -EIO;
+       }
+
+       /* !!!!! HACK TEST !!!!!
+        * When lots of debug trace statements are enabled, the driver
+        * doesn't seem to have as many firmware restart cycles...
+        *
+        * As a test, we're sticking in a 1/100s delay here */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(HZ / 100);
+
+       return 0;
+
+ fail_unlock:
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       return err;
+}
+
+
+/*
+ * Verify the values and data access of the hardware
+ * No locks needed or used.  No functions called.
+ */
+static int ipw2100_verify(struct ipw2100_priv *priv)
+{
+       u32 data1, data2;
+       u32 address;
+
+       u32 val1 = 0x76543210;
+       u32 val2 = 0xFEDCBA98;
+
+       /* Domain 0 check - all values should be DOA_DEBUG */
+       for (address = IPW_REG_DOA_DEBUG_AREA_START;
+            address < IPW_REG_DOA_DEBUG_AREA_END;
+            address += sizeof(u32)) {
+               read_register(priv->net_dev, address, &data1);
+               if (data1 != IPW_DATA_DOA_DEBUG_VALUE)
+                       return -EIO;
+       }
+
+       /* Domain 1 check - use arbitrary read/write compare  */
+       for (address = 0; address < 5; address++) {
+               /* The memory area is not used now */
+               write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+                              val1);
+               write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+                              val2);
+               read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+                             &data1);
+               read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+                             &data2);
+               if (val1 == data1 && val2 == data2)
+                       return 0;
+       }
+
+       return -EIO;
+}
+
+/*
+ *
+ * Loop until the CARD_DISABLED bit is the same value as the
+ * supplied parameter
+ *
+ * TODO: See if it would be more efficient to do a wait/wake
+ *       cycle and have the completion event trigger the wakeup
+ *
+ */
+#define IPW_CARD_DISABLE_COMPLETE_WAIT             100 // 100 milli
+static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state)
+{
+       int i;
+       u32 card_state;
+       u32 len = sizeof(card_state);
+       int err;
+
+       for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) {
+               err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED,
+                                         &card_state, &len);
+               if (err) {
+                       IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal "
+                                      "failed.\n");
+                       return 0;
+               }
+
+               /* We'll break out if either the HW state says it is
+                * in the state we want, or if HOST_COMPLETE command
+                * finishes */
+               if ((card_state == state) ||
+                   ((priv->status & STATUS_ENABLED) ?
+                    IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) {
+                       if (state == IPW_HW_STATE_ENABLED)
+                               priv->status |= STATUS_ENABLED;
+                       else
+                               priv->status &= ~STATUS_ENABLED;
+
+                       return 0;
+               }
+
+               udelay(50);
+       }
+
+       IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n",
+                      state ? "DISABLED" : "ENABLED");
+       return -EIO;
+}
+
+
+/*********************************************************************
+    Procedure   :   sw_reset_and_clock
+    Purpose     :   Asserts s/w reset, asserts clock initialization
+                    and waits for clock stabilization
+ ********************************************************************/
+static int sw_reset_and_clock(struct ipw2100_priv *priv)
+{
+       int i;
+       u32 r;
+
+       // assert s/w reset
+       write_register(priv->net_dev, IPW_REG_RESET_REG,
+                      IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+       // wait for clock stabilization
+       for (i = 0; i < 1000; i++) {
+               udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY);
+
+               // check clock ready bit
+               read_register(priv->net_dev, IPW_REG_RESET_REG, &r);
+               if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET)
+                       break;
+       }
+
+       if (i == 1000)
+               return -EIO;    // TODO: better error value
+
+       /* set "initialization complete" bit to move adapter to
+        * D0 state */
+       write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+                      IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE);
+
+       /* wait for clock stabilization */
+       for (i = 0; i < 10000; i++) {
+               udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4);
+
+               /* check clock ready bit */
+               read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+               if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY)
+                       break;
+       }
+
+       if (i == 10000)
+               return -EIO;    /* TODO: better error value */
+
+       /* set D0 standby bit */
+       read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+       write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+                      r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
+
+       return 0;
+}
+
+/*********************************************************************
+    Procedure   :   ipw2100_download_firmware
+    Purpose     :   Initiaze adapter after power on.
+                    The sequence is:
+                    1. assert s/w reset first!
+                    2. awake clocks & wait for clock stabilization
+                    3. hold ARC (don't ask me why...)
+                    4. load Dino ucode and reset/clock init again
+                    5. zero-out shared mem
+                    6. download f/w
+ *******************************************************************/
+static int ipw2100_download_firmware(struct ipw2100_priv *priv)
+{
+       u32 address;
+       int err;
+
+#ifndef CONFIG_PM
+       /* Fetch the firmware and microcode */
+       struct ipw2100_fw ipw2100_firmware;
+#endif
+
+       if (priv->fatal_error) {
+               IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after "
+                      "fatal error %d.  Interface must be brought down.\n",
+                      priv->net_dev->name, priv->fatal_error);
+               return -EINVAL;
+       }
+
+#ifdef CONFIG_PM
+       if (!ipw2100_firmware.version) {
+               err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+               if (err) {
+                       IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+                              priv->net_dev->name, err);
+                       priv->fatal_error = IPW2100_ERR_FW_LOAD;
+                       goto fail;
+               }
+       }
+#else
+       err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+       if (err) {
+               IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+                      priv->net_dev->name, err);
+               priv->fatal_error = IPW2100_ERR_FW_LOAD;
+               goto fail;
+       }
+#endif
+       priv->firmware_version = ipw2100_firmware.version;
+
+       /* s/w reset and clock stabilization */
+       err = sw_reset_and_clock(priv);
+       if (err) {
+               IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n",
+                      priv->net_dev->name, err);
+               goto fail;
+       }
+
+       err = ipw2100_verify(priv);
+       if (err) {
+               IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n",
+                      priv->net_dev->name, err);
+               goto fail;
+       }
+
+       /* Hold ARC */
+       write_nic_dword(priv->net_dev,
+                       IPW_INTERNAL_REGISTER_HALT_AND_RESET,
+                       0x80000000);
+
+       /* allow ARC to run */
+       write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+       /* load microcode */
+       err = ipw2100_ucode_download(priv, &ipw2100_firmware);
+       if (err) {
+               printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n",
+                      priv->net_dev->name, err);
+               goto fail;
+       }
+
+       /* release ARC */
+       write_nic_dword(priv->net_dev,
+                       IPW_INTERNAL_REGISTER_HALT_AND_RESET,
+                       0x00000000);
+
+       /* s/w reset and clock stabilization (again!!!) */
+       err = sw_reset_and_clock(priv);
+       if (err) {
+               printk(KERN_ERR DRV_NAME ": %s: sw_reset_and_clock failed: %d\n",
+                      priv->net_dev->name, err);
+               goto fail;
+       }
+
+       /* load f/w */
+       err = ipw2100_fw_download(priv, &ipw2100_firmware);
+       if (err) {
+               IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n",
+                      priv->net_dev->name, err);
+               goto fail;
+       }
+
+#ifndef CONFIG_PM
+       /*
+        * When the .resume method of the driver is called, the other
+        * part of the system, i.e. the ide driver could still stay in
+        * the suspend stage. This prevents us from loading the firmware
+        * from the disk.  --YZ
+        */
+
+       /* free any storage allocated for firmware image */
+       ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+
+       /* zero out Domain 1 area indirectly (Si requirement) */
+       for (address = IPW_HOST_FW_SHARED_AREA0;
+            address < IPW_HOST_FW_SHARED_AREA0_END; address += 4)
+               write_nic_dword(priv->net_dev, address, 0);
+       for (address = IPW_HOST_FW_SHARED_AREA1;
+            address < IPW_HOST_FW_SHARED_AREA1_END; address += 4)
+               write_nic_dword(priv->net_dev, address, 0);
+       for (address = IPW_HOST_FW_SHARED_AREA2;
+            address < IPW_HOST_FW_SHARED_AREA2_END; address += 4)
+               write_nic_dword(priv->net_dev, address, 0);
+       for (address = IPW_HOST_FW_SHARED_AREA3;
+            address < IPW_HOST_FW_SHARED_AREA3_END; address += 4)
+               write_nic_dword(priv->net_dev, address, 0);
+       for (address = IPW_HOST_FW_INTERRUPT_AREA;
+            address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4)
+               write_nic_dword(priv->net_dev, address, 0);
+
+       return 0;
+
+ fail:
+       ipw2100_release_firmware(priv, &ipw2100_firmware);
+       return err;
+}
+
+static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv)
+{
+       if (priv->status & STATUS_INT_ENABLED)
+               return;
+       priv->status |= STATUS_INT_ENABLED;
+       write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK);
+}
+
+static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv)
+{
+       if (!(priv->status & STATUS_INT_ENABLED))
+               return;
+       priv->status &= ~STATUS_INT_ENABLED;
+       write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0);
+}
+
+
+static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv)
+{
+       struct ipw2100_ordinals *ord = &priv->ordinals;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1,
+                     &ord->table1_addr);
+
+       read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2,
+                     &ord->table2_addr);
+
+       read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size);
+       read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size);
+
+       ord->table2_size &= 0x0000FFFF;
+
+       IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size);
+       IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size);
+       IPW_DEBUG_INFO("exit\n");
+}
+
+static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv)
+{
+       u32 reg = 0;
+       /*
+        * Set GPIO 3 writable by FW; GPIO 1 writable
+        * by driver and enable clock
+        */
+       reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE |
+              IPW_BIT_GPIO_LED_OFF);
+       write_register(priv->net_dev, IPW_REG_GPIO, reg);
+}
+
+static inline int rf_kill_active(struct ipw2100_priv *priv)
+{
+#define MAX_RF_KILL_CHECKS 5
+#define RF_KILL_CHECK_DELAY 40
+
+       unsigned short value = 0;
+       u32 reg = 0;
+       int i;
+
+       if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
+               priv->status &= ~STATUS_RF_KILL_HW;
+               return 0;
+       }
+
+       for (i = 0; i < MAX_RF_KILL_CHECKS; i++) {
+               udelay(RF_KILL_CHECK_DELAY);
+               read_register(priv->net_dev, IPW_REG_GPIO, &reg);
+               value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
+       }
+
+       if (value == 0)
+               priv->status |= STATUS_RF_KILL_HW;
+       else
+               priv->status &= ~STATUS_RF_KILL_HW;
+
+       return (value == 0);
+}
+
+static int ipw2100_get_hw_features(struct ipw2100_priv *priv)
+{
+       u32 addr, len;
+       u32 val;
+
+       /*
+        * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1
+        */
+       len = sizeof(addr);
+       if (ipw2100_get_ordinal(
+                   priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS,
+                   &addr, &len)) {
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                      __LINE__);
+               return -EIO;
+       }
+
+       IPW_DEBUG_INFO("EEPROM address: %08X\n", addr);
+
+       /*
+        * EEPROM version is the byte at offset 0xfd in firmware
+        * We read 4 bytes, then shift out the byte we actually want */
+       read_nic_dword(priv->net_dev, addr + 0xFC, &val);
+       priv->eeprom_version = (val >> 24) & 0xFF;
+       IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version);
+
+        /*
+        *  HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware
+        *
+        *  notice that the EEPROM bit is reverse polarity, i.e.
+        *     bit = 0  signifies HW RF kill switch is supported
+        *     bit = 1  signifies HW RF kill switch is NOT supported
+        */
+       read_nic_dword(priv->net_dev, addr + 0x20, &val);
+       if (!((val >> 24) & 0x01))
+               priv->hw_features |= HW_FEATURE_RFKILL;
+
+       IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n",
+                          (priv->hw_features & HW_FEATURE_RFKILL) ?
+                          "" : "not ");
+
+       return 0;
+}
+
+/*
+ * Start firmware execution after power on and intialization
+ * The sequence is:
+ *  1. Release ARC
+ *  2. Wait for f/w initialization completes;
+ */
+static int ipw2100_start_adapter(struct ipw2100_priv *priv)
+{
+       int i;
+       u32 inta, inta_mask, gpio;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       if (priv->status & STATUS_RUNNING)
+               return 0;
+
+       /*
+        * Initialize the hw - drive adapter to DO state by setting
+        * init_done bit. Wait for clk_ready bit and Download
+        * fw & dino ucode
+        */
+       if (ipw2100_download_firmware(priv)) {
+               printk(KERN_ERR DRV_NAME ": %s: Failed to power on the adapter.\n",
+                      priv->net_dev->name);
+               return -EIO;
+       }
+
+       /* Clear the Tx, Rx and Msg queues and the r/w indexes
+        * in the firmware RBD and TBD ring queue */
+       ipw2100_queues_initialize(priv);
+
+       ipw2100_hw_set_gpio(priv);
+
+       /* TODO -- Look at disabling interrupts here to make sure none
+        * get fired during FW initialization */
+
+       /* Release ARC - clear reset bit */
+       write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+       /* wait for f/w intialization complete */
+       IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n");
+       i = 5000;
+       do {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(40 * HZ / 1000);
+               /* Todo... wait for sync command ... */
+
+               read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+               /* check "init done" bit */
+               if (inta & IPW2100_INTA_FW_INIT_DONE) {
+                       /* reset "init done" bit */
+                       write_register(priv->net_dev, IPW_REG_INTA,
+                                      IPW2100_INTA_FW_INIT_DONE);
+                       break;
+               }
+
+               /* check error conditions : we check these after the firmware
+                * check so that if there is an error, the interrupt handler
+                * will see it and the adapter will be reset */
+               if (inta &
+                   (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) {
+                       /* clear error conditions */
+                       write_register(priv->net_dev, IPW_REG_INTA,
+                                      IPW2100_INTA_FATAL_ERROR |
+                                      IPW2100_INTA_PARITY_ERROR);
+               }
+       } while (i--);
+
+       /* Clear out any pending INTAs since we aren't supposed to have
+        * interrupts enabled at this point... */
+       read_register(priv->net_dev, IPW_REG_INTA, &inta);
+       read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+       inta &= IPW_INTERRUPT_MASK;
+       /* Clear out any pending interrupts */
+       if (inta & inta_mask)
+               write_register(priv->net_dev, IPW_REG_INTA, inta);
+
+       IPW_DEBUG_FW("f/w initialization complete: %s\n",
+                    i ? "SUCCESS" : "FAILED");
+
+       if (!i) {
+               printk(KERN_WARNING DRV_NAME ": %s: Firmware did not initialize.\n",
+                      priv->net_dev->name);
+               return -EIO;
+       }
+
+       /* allow firmware to write to GPIO1 & GPIO3 */
+       read_register(priv->net_dev, IPW_REG_GPIO, &gpio);
+
+       gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK);
+
+       write_register(priv->net_dev, IPW_REG_GPIO, gpio);
+
+       /* Ready to receive commands */
+       priv->status |= STATUS_RUNNING;
+
+       /* The adapter has been reset; we are not associated */
+       priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
+
+       IPW_DEBUG_INFO("exit\n");
+
+       return 0;
+}
+
+static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv)
+{
+       if (!priv->fatal_error)
+               return;
+
+       priv->fatal_errors[priv->fatal_index++] = priv->fatal_error;
+       priv->fatal_index %= IPW2100_ERROR_QUEUE;
+       priv->fatal_error = 0;
+}
+
+
+/* NOTE: Our interrupt is disabled when this method is called */
+static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv)
+{
+       u32 reg;
+       int i;
+
+       IPW_DEBUG_INFO("Power cycling the hardware.\n");
+
+       ipw2100_hw_set_gpio(priv);
+
+       /* Step 1. Stop Master Assert */
+       write_register(priv->net_dev, IPW_REG_RESET_REG,
+                      IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+       /* Step 2. Wait for stop Master Assert
+        *         (not more then 50us, otherwise ret error */
+       i = 5;
+       do {
+               udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+               read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+               if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+                       break;
+       }  while(i--);
+
+       priv->status &= ~STATUS_RESET_PENDING;
+
+       if (!i) {
+               IPW_DEBUG_INFO("exit - waited too long for master assert stop\n");
+               return -EIO;
+       }
+
+       write_register(priv->net_dev, IPW_REG_RESET_REG,
+                      IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+
+       /* Reset any fatal_error conditions */
+       ipw2100_reset_fatalerror(priv);
+
+       /* At this point, the adapter is now stopped and disabled */
+       priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING |
+                         STATUS_ASSOCIATED | STATUS_ENABLED);
+
+       return 0;
+}
+
+/*
+ * Send the CARD_DISABLE_PHY_OFF comamnd to the card to disable it
+ *
+ * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent.
+ *
+ * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of
+ * if STATUS_ASSN_LOST is sent.
+ */
+static int ipw2100_hw_phy_off(struct ipw2100_priv *priv)
+{
+
+#define HW_PHY_OFF_LOOP_DELAY (HZ / 5000)
+
+       struct host_command cmd = {
+               .host_command = CARD_DISABLE_PHY_OFF,
+               .host_command_sequence = 0,
+               .host_command_length = 0,
+       };
+       int err, i;
+       u32 val1, val2;
+
+       IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n");
+
+       /* Turn off the radio */
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err)
+               return err;
+
+       for (i = 0; i < 2500; i++) {
+               read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1);
+               read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2);
+
+               if ((val1 & IPW2100_CONTROL_PHY_OFF) &&
+                   (val2 & IPW2100_COMMAND_PHY_OFF))
+                       return 0;
+
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(HW_PHY_OFF_LOOP_DELAY);
+       }
+
+       return -EIO;
+}
+
+
+static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
+{
+       struct host_command cmd = {
+               .host_command = HOST_COMPLETE,
+               .host_command_sequence = 0,
+               .host_command_length = 0
+       };
+       int err = 0;
+
+       IPW_DEBUG_HC("HOST_COMPLETE\n");
+
+       if (priv->status & STATUS_ENABLED)
+               return 0;
+
+       down(&priv->adapter_sem);
+
+       if (rf_kill_active(priv)) {
+               IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
+               goto fail_up;
+       }
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err) {
+               IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n");
+               goto fail_up;
+       }
+
+       err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED);
+       if (err) {
+               IPW_DEBUG_INFO(
+                      "%s: card not responding to init command.\n",
+                      priv->net_dev->name);
+               goto fail_up;
+       }
+
+       if (priv->stop_hang_check) {
+               priv->stop_hang_check = 0;
+               queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
+       }
+
+fail_up:
+       up(&priv->adapter_sem);
+       return err;
+}
+
+static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
+{
+#define HW_POWER_DOWN_DELAY (HZ / 10)
+
+       struct host_command cmd = {
+               .host_command = HOST_PRE_POWER_DOWN,
+               .host_command_sequence = 0,
+               .host_command_length = 0,
+       };
+       int err, i;
+       u32 reg;
+
+       if (!(priv->status & STATUS_RUNNING))
+               return 0;
+
+       priv->status |= STATUS_STOPPING;
+
+       /* We can only shut down the card if the firmware is operational.  So,
+        * if we haven't reset since a fatal_error, then we can not send the
+        * shutdown commands. */
+       if (!priv->fatal_error) {
+               /* First, make sure the adapter is enabled so that the PHY_OFF
+                * command can shut it down */
+               ipw2100_enable_adapter(priv);
+
+               err = ipw2100_hw_phy_off(priv);
+               if (err)
+                       printk(KERN_WARNING DRV_NAME ": Error disabling radio %d\n", err);
+
+               /*
+                * If in D0-standby mode going directly to D3 may cause a
+                * PCI bus violation.  Therefore we must change out of the D0
+                * state.
+                *
+                * Sending the PREPARE_FOR_POWER_DOWN will restrict the
+                * hardware from going into standby mode and will transition
+                * out of D0-standy if it is already in that state.
+                *
+                * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
+                * driver upon completion.  Once received, the driver can
+                * proceed to the D3 state.
+                *
+                * Prepare for power down command to fw.  This command would
+                * take HW out of D0-standby and prepare it for D3 state.
+                *
+                * Currently FW does not support event notification for this
+                * event. Therefore, skip waiting for it.  Just wait a fixed
+                * 100ms
+                */
+               IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n");
+
+               err = ipw2100_hw_send_command(priv, &cmd);
+               if (err)
+                       printk(KERN_WARNING DRV_NAME ": "
+                              "%s: Power down command failed: Error %d\n",
+                              priv->net_dev->name, err);
+               else {
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout(HW_POWER_DOWN_DELAY);
+               }
+       }
+
+       priv->status &= ~STATUS_ENABLED;
+
+       /*
+        * Set GPIO 3 writable by FW; GPIO 1 writable
+        * by driver and enable clock
+        */
+       ipw2100_hw_set_gpio(priv);
+
+       /*
+        * Power down adapter.  Sequence:
+        * 1. Stop master assert (RESET_REG[9]=1)
+        * 2. Wait for stop master (RESET_REG[8]==1)
+        * 3. S/w reset assert (RESET_REG[7] = 1)
+        */
+
+       /* Stop master assert */
+       write_register(priv->net_dev, IPW_REG_RESET_REG,
+                      IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+       /* wait stop master not more than 50 usec.
+        * Otherwise return error. */
+       for (i = 5; i > 0; i--) {
+               udelay(10);
+
+               /* Check master stop bit */
+               read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+               if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+                       break;
+       }
+
+       if (i == 0)
+               printk(KERN_WARNING DRV_NAME
+                      ": %s: Could now power down adapter.\n",
+                      priv->net_dev->name);
+
+       /* assert s/w reset */
+       write_register(priv->net_dev, IPW_REG_RESET_REG,
+                      IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+       priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING);
+
+       return 0;
+}
+
+
+static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
+{
+       struct host_command cmd = {
+               .host_command = CARD_DISABLE,
+               .host_command_sequence = 0,
+               .host_command_length = 0
+       };
+       int err = 0;
+
+       IPW_DEBUG_HC("CARD_DISABLE\n");
+
+       if (!(priv->status & STATUS_ENABLED))
+               return 0;
+
+       /* Make sure we clear the associated state */
+       priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+       if (!priv->stop_hang_check) {
+               priv->stop_hang_check = 1;
+               cancel_delayed_work(&priv->hang_check);
+       }
+
+       down(&priv->adapter_sem);
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME ": exit - failed to send CARD_DISABLE command\n");
+               goto fail_up;
+       }
+
+       err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME ": exit - card failed to change to DISABLED\n");
+               goto fail_up;
+       }
+
+       IPW_DEBUG_INFO("TODO: implement scan state machine\n");
+
+fail_up:
+       up(&priv->adapter_sem);
+       return err;
+}
+
+static int ipw2100_set_scan_options(struct ipw2100_priv *priv)
+{
+       struct host_command cmd = {
+               .host_command = SET_SCAN_OPTIONS,
+               .host_command_sequence = 0,
+               .host_command_length = 8
+       };
+       int err;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       IPW_DEBUG_SCAN("setting scan options\n");
+
+       cmd.host_command_parameters[0] = 0;
+
+       if (!(priv->config & CFG_ASSOCIATE))
+               cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE;
+       if ((priv->sec.flags & SEC_ENABLED) && priv->sec.enabled)
+               cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL;
+       if (priv->config & CFG_PASSIVE_SCAN)
+               cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE;
+
+       cmd.host_command_parameters[1] = priv->channel_mask;
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n",
+                    cmd.host_command_parameters[0]);
+
+       return err;
+}
+
+static int ipw2100_start_scan(struct ipw2100_priv *priv)
+{
+       struct host_command cmd = {
+               .host_command = BROADCAST_SCAN,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       IPW_DEBUG_HC("START_SCAN\n");
+
+       cmd.host_command_parameters[0] = 0;
+
+       /* No scanning if in monitor mode */
+       if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+               return 1;
+
+       if (priv->status & STATUS_SCANNING) {
+               IPW_DEBUG_SCAN("Scan requested while already in scan...\n");
+               return 0;
+       }
+
+       IPW_DEBUG_INFO("enter\n");
+
+       /* Not clearing here; doing so makes iwlist always return nothing...
+        *
+        * We should modify the table logic to use aging tables vs. clearing
+        * the table on each scan start.
+        */
+       IPW_DEBUG_SCAN("starting scan\n");
+
+       priv->status |= STATUS_SCANNING;
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err)
+               priv->status &= ~STATUS_SCANNING;
+
+       IPW_DEBUG_INFO("exit\n");
+
+       return err;
+}
+
+static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
+{
+       unsigned long flags;
+       int rc = 0;
+       u32 lock;
+       u32 ord_len = sizeof(lock);
+
+       /* Quite if manually disabled. */
+       if (priv->status & STATUS_RF_KILL_SW) {
+               IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable "
+                              "switch\n", priv->net_dev->name);
+               return 0;
+       }
+
+       /* If the interrupt is enabled, turn it off... */
+       spin_lock_irqsave(&priv->low_lock, flags);
+       ipw2100_disable_interrupts(priv);
+
+       /* Reset any fatal_error conditions */
+       ipw2100_reset_fatalerror(priv);
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       if (priv->status & STATUS_POWERED ||
+           (priv->status & STATUS_RESET_PENDING)) {
+               /* Power cycle the card ... */
+               if (ipw2100_power_cycle_adapter(priv)) {
+                       printk(KERN_WARNING DRV_NAME ": %s: Could not cycle adapter.\n",
+                                         priv->net_dev->name);
+                       rc = 1;
+                       goto exit;
+               }
+       } else
+               priv->status |= STATUS_POWERED;
+
+       /* Load the firmware, start the clocks, etc. */
+       if (ipw2100_start_adapter(priv)) {
+               printk(KERN_ERR DRV_NAME ": %s: Failed to start the firmware.\n",
+                               priv->net_dev->name);
+               rc = 1;
+               goto exit;
+       }
+
+       ipw2100_initialize_ordinals(priv);
+
+       /* Determine capabilities of this particular HW configuration */
+       if (ipw2100_get_hw_features(priv)) {
+               printk(KERN_ERR DRV_NAME ": %s: Failed to determine HW features.\n",
+                               priv->net_dev->name);
+               rc = 1;
+               goto exit;
+       }
+
+       lock = LOCK_NONE;
+       if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) {
+               printk(KERN_ERR DRV_NAME ": %s: Failed to clear ordinal lock.\n",
+                               priv->net_dev->name);
+               rc = 1;
+               goto exit;
+       }
+
+       priv->status &= ~STATUS_SCANNING;
+
+       if (rf_kill_active(priv)) {
+               printk(KERN_INFO "%s: Radio is disabled by RF switch.\n",
+                      priv->net_dev->name);
+
+               if (priv->stop_rf_kill) {
+                       priv->stop_rf_kill = 0;
+                       queue_delayed_work(priv->workqueue, &priv->rf_kill, HZ);
+               }
+
+               deferred = 1;
+       }
+
+       /* Turn on the interrupt so that commands can be processed */
+       ipw2100_enable_interrupts(priv);
+
+       /* Send all of the commands that must be sent prior to
+        * HOST_COMPLETE */
+       if (ipw2100_adapter_setup(priv)) {
+               printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n",
+                               priv->net_dev->name);
+               rc = 1;
+               goto exit;
+       }
+
+       if (!deferred) {
+               /* Enable the adapter - sends HOST_COMPLETE */
+               if (ipw2100_enable_adapter(priv)) {
+                       printk(KERN_ERR DRV_NAME ": "
+                               "%s: failed in call to enable adapter.\n",
+                               priv->net_dev->name);
+                       ipw2100_hw_stop_adapter(priv);
+                       rc = 1;
+                       goto exit;
+               }
+
+
+               /* Start a scan . . . */
+               ipw2100_set_scan_options(priv);
+               ipw2100_start_scan(priv);
+       }
+
+ exit:
+       return rc;
+}
+
+/* Called by register_netdev() */
+static int ipw2100_net_init(struct net_device *dev)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       return ipw2100_up(priv, 1);
+}
+
+static void ipw2100_down(struct ipw2100_priv *priv)
+{
+       unsigned long flags;
+       union iwreq_data wrqu = {
+               .ap_addr = {
+                       .sa_family = ARPHRD_ETHER
+               }
+       };
+       int associated = priv->status & STATUS_ASSOCIATED;
+
+       /* Kill the RF switch timer */
+       if (!priv->stop_rf_kill) {
+               priv->stop_rf_kill = 1;
+               cancel_delayed_work(&priv->rf_kill);
+       }
+
+       /* Kill the firmare hang check timer */
+       if (!priv->stop_hang_check) {
+               priv->stop_hang_check = 1;
+               cancel_delayed_work(&priv->hang_check);
+       }
+
+       /* Kill any pending resets */
+       if (priv->status & STATUS_RESET_PENDING)
+               cancel_delayed_work(&priv->reset_work);
+
+       /* Make sure the interrupt is on so that FW commands will be
+        * processed correctly */
+       spin_lock_irqsave(&priv->low_lock, flags);
+       ipw2100_enable_interrupts(priv);
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       if (ipw2100_hw_stop_adapter(priv))
+               printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n",
+                      priv->net_dev->name);
+
+       /* Do not disable the interrupt until _after_ we disable
+        * the adaptor.  Otherwise the CARD_DISABLE command will never
+        * be ack'd by the firmware */
+       spin_lock_irqsave(&priv->low_lock, flags);
+       ipw2100_disable_interrupts(priv);
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+       if (priv->config & CFG_C3_DISABLED) {
+               IPW_DEBUG_INFO(DRV_NAME ": Resetting C3 transitions.\n");
+               acpi_set_cstate_limit(priv->cstate_limit);
+               priv->config &= ~CFG_C3_DISABLED;
+       }
+#endif
+
+       /* We have to signal any supplicant if we are disassociating */
+       if (associated)
+               wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+       priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+       netif_carrier_off(priv->net_dev);
+       netif_stop_queue(priv->net_dev);
+}
+
+static void ipw2100_reset_adapter(struct ipw2100_priv *priv)
+{
+       unsigned long flags;
+       union iwreq_data wrqu = {
+               .ap_addr = {
+                       .sa_family = ARPHRD_ETHER
+               }
+       };
+       int associated = priv->status & STATUS_ASSOCIATED;
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+       IPW_DEBUG_INFO(DRV_NAME ": %s: Restarting adapter.\n",
+                      priv->net_dev->name);
+       priv->resets++;
+       priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+       priv->status |= STATUS_SECURITY_UPDATED;
+
+       /* Force a power cycle even if interface hasn't been opened
+        * yet */
+       cancel_delayed_work(&priv->reset_work);
+       priv->status |= STATUS_RESET_PENDING;
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       down(&priv->action_sem);
+       /* stop timed checks so that they don't interfere with reset */
+       priv->stop_hang_check = 1;
+       cancel_delayed_work(&priv->hang_check);
+
+       /* We have to signal any supplicant if we are disassociating */
+       if (associated)
+               wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+       ipw2100_up(priv, 0);
+       up(&priv->action_sem);
+
+}
+
+
+static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
+{
+
+#define MAC_ASSOCIATION_READ_DELAY (HZ)
+       int ret, len, essid_len;
+       char essid[IW_ESSID_MAX_SIZE];
+       u32 txrate;
+       u32 chan;
+       char *txratename;
+       u8 bssid[ETH_ALEN];
+
+       /*
+        * TBD: BSSID is usually 00:00:00:00:00:00 here and not
+        *      an actual MAC of the AP. Seems like FW sets this
+        *      address too late. Read it later and expose through
+        *      /proc or schedule a later task to query and update
+        */
+
+       essid_len = IW_ESSID_MAX_SIZE;
+       ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID,
+                                 essid, &essid_len);
+       if (ret) {
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                                  __LINE__);
+               return;
+       }
+
+       len = sizeof(u32);
+       ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE,
+                                 &txrate, &len);
+       if (ret) {
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                                  __LINE__);
+               return;
+       }
+
+       len = sizeof(u32);
+       ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len);
+       if (ret) {
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                                  __LINE__);
+               return;
+       }
+       len = ETH_ALEN;
+        ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, &bssid,  &len);
+       if (ret) {
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                                  __LINE__);
+               return;
+       }
+       memcpy(priv->ieee->bssid, bssid, ETH_ALEN);
+
+
+       switch (txrate) {
+       case TX_RATE_1_MBIT:
+               txratename = "1Mbps";
+               break;
+       case TX_RATE_2_MBIT:
+               txratename = "2Mbsp";
+               break;
+       case TX_RATE_5_5_MBIT:
+               txratename = "5.5Mbps";
+               break;
+       case TX_RATE_11_MBIT:
+               txratename = "11Mbps";
+               break;
+       default:
+               IPW_DEBUG_INFO("Unknown rate: %d\n", txrate);
+               txratename = "unknown rate";
+               break;
+       }
+
+       IPW_DEBUG_INFO("%s: Associated with '%s' at %s, channel %d (BSSID="
+                      MAC_FMT ")\n",
+                      priv->net_dev->name, escape_essid(essid, essid_len),
+                      txratename, chan, MAC_ARG(bssid));
+
+       /* now we copy read ssid into dev */
+       if (!(priv->config & CFG_STATIC_ESSID)) {
+               priv->essid_len = min((u8)essid_len, (u8)IW_ESSID_MAX_SIZE);
+               memcpy(priv->essid, essid, priv->essid_len);
+       }
+       priv->channel = chan;
+       memcpy(priv->bssid, bssid, ETH_ALEN);
+
+       priv->status |= STATUS_ASSOCIATING;
+       priv->connect_start = get_seconds();
+
+       queue_delayed_work(priv->workqueue, &priv->wx_event_work, HZ / 10);
+}
+
+
+static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid,
+                            int length, int batch_mode)
+{
+       int ssid_len = min(length, IW_ESSID_MAX_SIZE);
+       struct host_command cmd = {
+               .host_command = SSID,
+               .host_command_sequence = 0,
+               .host_command_length = ssid_len
+       };
+       int err;
+
+       IPW_DEBUG_HC("SSID: '%s'\n", escape_essid(essid, ssid_len));
+
+       if (ssid_len)
+               memcpy((char*)cmd.host_command_parameters,
+                      essid, ssid_len);
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to
+        * disable auto association -- so we cheat by setting a bogus SSID */
+       if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) {
+               int i;
+               u8 *bogus = (u8*)cmd.host_command_parameters;
+               for (i = 0; i < IW_ESSID_MAX_SIZE; i++)
+                       bogus[i] = 0x18 + i;
+               cmd.host_command_length = IW_ESSID_MAX_SIZE;
+       }
+
+       /* NOTE:  We always send the SSID command even if the provided ESSID is
+        * the same as what we currently think is set. */
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (!err) {
+               memset(priv->essid + ssid_len, 0,
+                      IW_ESSID_MAX_SIZE - ssid_len);
+               memcpy(priv->essid, essid, ssid_len);
+               priv->essid_len = ssid_len;
+       }
+
+       if (!batch_mode) {
+               if (ipw2100_enable_adapter(priv))
+                       err = -EIO;
+       }
+
+       return err;
+}
+
+static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
+{
+       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                 "disassociated: '%s' " MAC_FMT " \n",
+                 escape_essid(priv->essid, priv->essid_len),
+                 MAC_ARG(priv->bssid));
+
+       priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+       if (priv->status & STATUS_STOPPING) {
+               IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n");
+               return;
+       }
+
+       memset(priv->bssid, 0, ETH_ALEN);
+       memset(priv->ieee->bssid, 0, ETH_ALEN);
+
+       netif_carrier_off(priv->net_dev);
+       netif_stop_queue(priv->net_dev);
+
+       if (!(priv->status & STATUS_RUNNING))
+               return;
+
+       if (priv->status & STATUS_SECURITY_UPDATED)
+               queue_work(priv->workqueue, &priv->security_work);
+
+       queue_work(priv->workqueue, &priv->wx_event_work);
+}
+
+static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
+{
+       IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n",
+              priv->net_dev->name);
+
+       /* RF_KILL is now enabled (else we wouldn't be here) */
+       priv->status |= STATUS_RF_KILL_HW;
+
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+       if (priv->config & CFG_C3_DISABLED) {
+               IPW_DEBUG_INFO(DRV_NAME ": Resetting C3 transitions.\n");
+               acpi_set_cstate_limit(priv->cstate_limit);
+               priv->config &= ~CFG_C3_DISABLED;
+       }
+#endif
+
+       /* Make sure the RF Kill check timer is running */
+       priv->stop_rf_kill = 0;
+       cancel_delayed_work(&priv->rf_kill);
+       queue_delayed_work(priv->workqueue, &priv->rf_kill, HZ);
+}
+
+static void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
+{
+       IPW_DEBUG_SCAN("scan complete\n");
+       /* Age the scan results... */
+       priv->ieee->scans++;
+       priv->status &= ~STATUS_SCANNING;
+}
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW2100_HANDLER(v, f) { v, f, # v }
+struct ipw2100_status_indicator {
+       int status;
+       void (*cb)(struct ipw2100_priv *priv, u32 status);
+       char *name;
+};
+#else
+#define IPW2100_HANDLER(v, f) { v, f }
+struct ipw2100_status_indicator {
+       int status;
+       void (*cb)(struct ipw2100_priv *priv, u32 status);
+};
+#endif /* CONFIG_IPW_DEBUG */
+
+static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status)
+{
+       IPW_DEBUG_SCAN("Scanning...\n");
+       priv->status |= STATUS_SCANNING;
+}
+
+static const struct ipw2100_status_indicator status_handlers[] = {
+       IPW2100_HANDLER(IPW_STATE_INITIALIZED, 0),
+       IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, 0),
+       IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated),
+       IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost),
+       IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, 0),
+       IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete),
+       IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, 0),
+       IPW2100_HANDLER(IPW_STATE_LEFT_PSP, 0),
+       IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill),
+       IPW2100_HANDLER(IPW_STATE_DISABLED, 0),
+       IPW2100_HANDLER(IPW_STATE_POWER_DOWN, 0),
+       IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning),
+       IPW2100_HANDLER(-1, 0)
+};
+
+
+static void isr_status_change(struct ipw2100_priv *priv, int status)
+{
+       int i;
+
+       if (status == IPW_STATE_SCANNING &&
+           priv->status & STATUS_ASSOCIATED &&
+           !(priv->status & STATUS_SCANNING)) {
+               IPW_DEBUG_INFO("Scan detected while associated, with "
+                              "no scan request.  Restarting firmware.\n");
+
+               /* Wake up any sleeping jobs */
+               schedule_reset(priv);
+       }
+
+       for (i = 0; status_handlers[i].status != -1; i++) {
+               if (status == status_handlers[i].status) {
+                       IPW_DEBUG_NOTIF("Status change: %s\n",
+                                        status_handlers[i].name);
+                       if (status_handlers[i].cb)
+                               status_handlers[i].cb(priv, status);
+                       priv->wstats.status = status;
+                       return;
+               }
+       }
+
+       IPW_DEBUG_NOTIF("unknown status received: %04x\n", status);
+}
+
+static void isr_rx_complete_command(
+       struct ipw2100_priv *priv,
+       struct ipw2100_cmd_header *cmd)
+{
+#ifdef CONFIG_IPW_DEBUG
+       if (cmd->host_command_reg < ARRAY_SIZE(command_types)) {
+               IPW_DEBUG_HC("Command completed '%s (%d)'\n",
+                            command_types[cmd->host_command_reg],
+                            cmd->host_command_reg);
+       }
+#endif
+       if (cmd->host_command_reg == HOST_COMPLETE)
+               priv->status |= STATUS_ENABLED;
+
+       if (cmd->host_command_reg == CARD_DISABLE)
+               priv->status &= ~STATUS_ENABLED;
+
+       priv->status &= ~STATUS_CMD_ACTIVE;
+
+       wake_up_interruptible(&priv->wait_command_queue);
+}
+
+#ifdef CONFIG_IPW_DEBUG
+static const char *frame_types[] = {
+       "COMMAND_STATUS_VAL",
+       "STATUS_CHANGE_VAL",
+       "P80211_DATA_VAL",
+       "P8023_DATA_VAL",
+       "HOST_NOTIFICATION_VAL"
+};
+#endif
+
+
+static inline int ipw2100_alloc_skb(
+       struct ipw2100_priv *priv,
+       struct ipw2100_rx_packet *packet)
+{
+       packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx));
+       if (!packet->skb)
+               return -ENOMEM;
+
+       packet->rxp = (struct ipw2100_rx *)packet->skb->data;
+       packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data,
+                                         sizeof(struct ipw2100_rx),
+                                         PCI_DMA_FROMDEVICE);
+       /* NOTE: pci_map_single does not return an error code, and 0 is a valid
+        *       dma_addr */
+
+       return 0;
+}
+
+
+#define SEARCH_ERROR   0xffffffff
+#define SEARCH_FAIL    0xfffffffe
+#define SEARCH_SUCCESS 0xfffffff0
+#define SEARCH_DISCARD 0
+#define SEARCH_SNAPSHOT 1
+
+#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff))
+static inline int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
+{
+       int i;
+       if (priv->snapshot[0])
+               return 1;
+       for (i = 0; i < 0x30; i++) {
+               priv->snapshot[i] = (u8*)kmalloc(0x1000, GFP_ATOMIC);
+               if (!priv->snapshot[i]) {
+                       IPW_DEBUG_INFO("%s: Error allocating snapshot "
+                              "buffer %d\n", priv->net_dev->name, i);
+                       while (i > 0)
+                               kfree(priv->snapshot[--i]);
+                       priv->snapshot[0] = NULL;
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static inline void ipw2100_snapshot_free(struct ipw2100_priv *priv)
+{
+       int i;
+       if (!priv->snapshot[0])
+               return;
+       for (i = 0; i < 0x30; i++)
+               kfree(priv->snapshot[i]);
+       priv->snapshot[0] = NULL;
+}
+
+static inline u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 *in_buf,
+                                   size_t len, int mode)
+{
+       u32 i, j;
+       u32 tmp;
+       u8 *s, *d;
+       u32 ret;
+
+       s = in_buf;
+       if (mode == SEARCH_SNAPSHOT) {
+               if (!ipw2100_snapshot_alloc(priv))
+                       mode = SEARCH_DISCARD;
+       }
+
+       for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) {
+               read_nic_dword(priv->net_dev, i, &tmp);
+               if (mode == SEARCH_SNAPSHOT)
+                       *(u32 *)SNAPSHOT_ADDR(i) = tmp;
+               if (ret == SEARCH_FAIL) {
+                       d = (u8*)&tmp;
+                       for (j = 0; j < 4; j++) {
+                               if (*s != *d) {
+                                       s = in_buf;
+                                       continue;
+                               }
+
+                               s++;
+                               d++;
+
+                               if ((s - in_buf) == len)
+                                       ret = (i + j) - len + 1;
+                       }
+               } else if (mode == SEARCH_DISCARD)
+                       return ret;
+       }
+
+       return ret;
+}
+
+/*
+ *
+ * 0) Disconnect the SKB from the firmware (just unmap)
+ * 1) Pack the ETH header into the SKB
+ * 2) Pass the SKB to the network stack
+ *
+ * When packet is provided by the firmware, it contains the following:
+ *
+ * .  ieee80211_hdr
+ * .  ieee80211_snap_hdr
+ *
+ * The size of the constructed ethernet
+ *
+ */
+#ifdef CONFIG_IPW2100_RX_DEBUG
+static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH];
+#endif
+
+static inline void ipw2100_corruption_detected(struct ipw2100_priv *priv,
+                                              int i)
+{
+#ifdef CONFIG_IPW_DEBUG_C3
+       struct ipw2100_status *status = &priv->status_queue.drv[i];
+       u32 match, reg;
+       int j;
+#endif
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+       int limit;
+#endif
+
+       IPW_DEBUG_INFO(DRV_NAME ": PCI latency error detected at "
+                      "0x%04zX.\n", i * sizeof(struct ipw2100_status));
+
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+       IPW_DEBUG_INFO(DRV_NAME ": Disabling C3 transitions.\n");
+       limit = acpi_get_cstate_limit();
+       if (limit > 2) {
+               priv->cstate_limit = limit;
+               acpi_set_cstate_limit(2);
+               priv->config |= CFG_C3_DISABLED;
+       }
+#endif
+
+#ifdef CONFIG_IPW_DEBUG_C3
+       /* Halt the fimrware so we can get a good image */
+       write_register(priv->net_dev, IPW_REG_RESET_REG,
+                      IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+       j = 5;
+       do {
+               udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+               read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+               if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+                       break;
+       }  while (j--);
+
+       match = ipw2100_match_buf(priv, (u8*)status,
+                                 sizeof(struct ipw2100_status),
+                                 SEARCH_SNAPSHOT);
+       if (match < SEARCH_SUCCESS)
+               IPW_DEBUG_INFO("%s: DMA status match in Firmware at "
+                              "offset 0x%06X, length %d:\n",
+                              priv->net_dev->name, match,
+                              sizeof(struct ipw2100_status));
+       else
+               IPW_DEBUG_INFO("%s: No DMA status match in "
+                              "Firmware.\n", priv->net_dev->name);
+
+       printk_buf((u8*)priv->status_queue.drv,
+                  sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH);
+#endif
+
+       priv->fatal_error = IPW2100_ERR_C3_CORRUPTION;
+       priv->ieee->stats.rx_errors++;
+       schedule_reset(priv);
+}
+
+static inline void isr_rx(struct ipw2100_priv *priv, int i,
+                         struct ieee80211_rx_stats *stats)
+{
+       struct ipw2100_status *status = &priv->status_queue.drv[i];
+       struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+       IPW_DEBUG_RX("Handler...\n");
+
+       if (unlikely(status->frame_size > skb_tailroom(packet->skb))) {
+               IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+                              "  Dropping.\n",
+                              priv->net_dev->name,
+                              status->frame_size, skb_tailroom(packet->skb));
+               priv->ieee->stats.rx_errors++;
+               return;
+       }
+
+       if (unlikely(!netif_running(priv->net_dev))) {
+               priv->ieee->stats.rx_errors++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+               return;
+       }
+
+       if (unlikely(priv->ieee->iw_mode == IW_MODE_MONITOR &&
+                    status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
+               IPW_DEBUG_RX("CRC error in packet.  Dropping.\n");
+               priv->ieee->stats.rx_errors++;
+               return;
+       }
+
+       if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
+               !(priv->status & STATUS_ASSOCIATED))) {
+               IPW_DEBUG_DROP("Dropping packet while not associated.\n");
+               priv->wstats.discard.misc++;
+               return;
+       }
+
+
+       pci_unmap_single(priv->pci_dev,
+                        packet->dma_addr,
+                        sizeof(struct ipw2100_rx),
+                        PCI_DMA_FROMDEVICE);
+
+       skb_put(packet->skb, status->frame_size);
+
+#ifdef CONFIG_IPW2100_RX_DEBUG
+       /* Make a copy of the frame so we can dump it to the logs if
+        * ieee80211_rx fails */
+       memcpy(packet_data, packet->skb->data,
+              min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH));
+#endif
+
+       if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
+#ifdef CONFIG_IPW2100_RX_DEBUG
+               IPW_DEBUG_DROP("%s: Non consumed packet:\n",
+                              priv->net_dev->name);
+               printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
+#endif
+               priv->ieee->stats.rx_errors++;
+
+               /* ieee80211_rx failed, so it didn't free the SKB */
+               dev_kfree_skb_any(packet->skb);
+               packet->skb = NULL;
+       }
+
+       /* We need to allocate a new SKB and attach it to the RDB. */
+       if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+               printk(KERN_WARNING DRV_NAME ": "
+                       "%s: Unable to allocate SKB onto RBD ring - disabling "
+                       "adapter.\n", priv->net_dev->name);
+               /* TODO: schedule adapter shutdown */
+               IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+       }
+
+       /* Update the RDB entry */
+       priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+static inline int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
+{
+       struct ipw2100_status *status = &priv->status_queue.drv[i];
+       struct ipw2100_rx *u = priv->rx_buffers[i].rxp;
+       u16 frame_type = status->status_fields & STATUS_TYPE_MASK;
+
+       switch (frame_type) {
+       case COMMAND_STATUS_VAL:
+               return (status->frame_size != sizeof(u->rx_data.command));
+       case STATUS_CHANGE_VAL:
+               return (status->frame_size != sizeof(u->rx_data.status));
+       case HOST_NOTIFICATION_VAL:
+               return (status->frame_size < sizeof(u->rx_data.notification));
+       case P80211_DATA_VAL:
+       case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+               return 0;
+#else
+               switch (WLAN_FC_GET_TYPE(u->rx_data.header.frame_ctl)) {
+               case IEEE80211_FTYPE_MGMT:
+               case IEEE80211_FTYPE_CTL:
+                       return 0;
+               case IEEE80211_FTYPE_DATA:
+                       return (status->frame_size >
+                               IPW_MAX_802_11_PAYLOAD_LENGTH);
+               }
+#endif
+       }
+
+       return 1;
+}
+
+/*
+ * ipw2100 interrupts are disabled at this point, and the ISR
+ * is the only code that calls this method.  So, we do not need
+ * to play with any locks.
+ *
+ * RX Queue works as follows:
+ *
+ * Read index - firmware places packet in entry identified by the
+ *              Read index and advances Read index.  In this manner,
+ *              Read index will always point to the next packet to
+ *              be filled--but not yet valid.
+ *
+ * Write index - driver fills this entry with an unused RBD entry.
+ *               This entry has not filled by the firmware yet.
+ *
+ * In between the W and R indexes are the RBDs that have been received
+ * but not yet processed.
+ *
+ * The process of handling packets will start at WRITE + 1 and advance
+ * until it reaches the READ index.
+ *
+ * The WRITE index is cached in the variable 'priv->rx_queue.next'.
+ *
+ */
+static inline void __ipw2100_rx_process(struct ipw2100_priv *priv)
+{
+       struct ipw2100_bd_queue *rxq = &priv->rx_queue;
+       struct ipw2100_status_queue *sq = &priv->status_queue;
+       struct ipw2100_rx_packet *packet;
+       u16 frame_type;
+       u32 r, w, i, s;
+       struct ipw2100_rx *u;
+       struct ieee80211_rx_stats stats = {
+               .mac_time = jiffies,
+       };
+
+       read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r);
+       read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w);
+
+       if (r >= rxq->entries) {
+               IPW_DEBUG_RX("exit - bad read index\n");
+               return;
+       }
+
+       i = (rxq->next + 1) % rxq->entries;
+       s = i;
+       while (i != r) {
+               /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n",
+                  r, rxq->next, i); */
+
+               packet = &priv->rx_buffers[i];
+
+               /* Sync the DMA for the STATUS buffer so CPU is sure to get
+                * the correct values */
+               pci_dma_sync_single_for_cpu(
+                       priv->pci_dev,
+                       sq->nic + sizeof(struct ipw2100_status) * i,
+                       sizeof(struct ipw2100_status),
+                       PCI_DMA_FROMDEVICE);
+
+               /* Sync the DMA for the RX buffer so CPU is sure to get
+                * the correct values */
+               pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr,
+                                           sizeof(struct ipw2100_rx),
+                                           PCI_DMA_FROMDEVICE);
+
+               if (unlikely(ipw2100_corruption_check(priv, i))) {
+                       ipw2100_corruption_detected(priv, i);
+                       goto increment;
+               }
+
+               u = packet->rxp;
+               frame_type = sq->drv[i].status_fields &
+                       STATUS_TYPE_MASK;
+               stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM;
+               stats.len = sq->drv[i].frame_size;
+
+               stats.mask = 0;
+               if (stats.rssi != 0)
+                       stats.mask |= IEEE80211_STATMASK_RSSI;
+               stats.freq = IEEE80211_24GHZ_BAND;
+
+               IPW_DEBUG_RX(
+                       "%s: '%s' frame type received (%d).\n",
+                       priv->net_dev->name, frame_types[frame_type],
+                       stats.len);
+
+               switch (frame_type) {
+               case COMMAND_STATUS_VAL:
+                       /* Reset Rx watchdog */
+                       isr_rx_complete_command(
+                               priv, &u->rx_data.command);
+                       break;
+
+               case STATUS_CHANGE_VAL:
+                       isr_status_change(priv, u->rx_data.status);
+                       break;
+
+               case P80211_DATA_VAL:
+               case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+                       if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+                               isr_rx(priv, i, &stats);
+                               break;
+                       }
+#endif
+                       if (stats.len < sizeof(u->rx_data.header))
+                               break;
+                       switch (WLAN_FC_GET_TYPE(u->rx_data.header.
+                                                frame_ctl)) {
+                       case IEEE80211_FTYPE_MGMT:
+                               ieee80211_rx_mgt(priv->ieee,
+                                                &u->rx_data.header,
+                                                &stats);
+                               break;
+
+                       case IEEE80211_FTYPE_CTL:
+                               break;
+
+                       case IEEE80211_FTYPE_DATA:
+                               isr_rx(priv, i, &stats);
+                               break;
+
+                       }
+                       break;
+               }
+
+       increment:
+               /* clear status field associated with this RBD */
+               rxq->drv[i].status.info.field = 0;
+
+               i = (i + 1) % rxq->entries;
+       }
+
+       if (i != s) {
+               /* backtrack one entry, wrapping to end if at 0 */
+               rxq->next = (i ? i : rxq->entries) - 1;
+
+               write_register(priv->net_dev,
+                              IPW_MEM_HOST_SHARED_RX_WRITE_INDEX,
+                              rxq->next);
+       }
+}
+
+
+/*
+ * __ipw2100_tx_process
+ *
+ * This routine will determine whether the next packet on
+ * the fw_pend_list has been processed by the firmware yet.
+ *
+ * If not, then it does nothing and returns.
+ *
+ * If so, then it removes the item from the fw_pend_list, frees
+ * any associated storage, and places the item back on the
+ * free list of its source (either msg_free_list or tx_free_list)
+ *
+ * TX Queue works as follows:
+ *
+ * Read index - points to the next TBD that the firmware will
+ *              process.  The firmware will read the data, and once
+ *              done processing, it will advance the Read index.
+ *
+ * Write index - driver fills this entry with an constructed TBD
+ *               entry.  The Write index is not advanced until the
+ *               packet has been configured.
+ *
+ * In between the W and R indexes are the TBDs that have NOT been
+ * processed.  Lagging behind the R index are packets that have
+ * been processed but have not been freed by the driver.
+ *
+ * In order to free old storage, an internal index will be maintained
+ * that points to the next packet to be freed.  When all used
+ * packets have been freed, the oldest index will be the same as the
+ * firmware's read index.
+ *
+ * The OLDEST index is cached in the variable 'priv->tx_queue.oldest'
+ *
+ * Because the TBD structure can not contain arbitrary data, the
+ * driver must keep an internal queue of cached allocations such that
+ * it can put that data back into the tx_free_list and msg_free_list
+ * for use by future command and data packets.
+ *
+ */
+static inline int __ipw2100_tx_process(struct ipw2100_priv *priv)
+{
+       struct ipw2100_bd_queue *txq = &priv->tx_queue;
+        struct ipw2100_bd *tbd;
+       struct list_head *element;
+       struct ipw2100_tx_packet *packet;
+       int descriptors_used;
+       int e, i;
+       u32 r, w, frag_num = 0;
+
+       if (list_empty(&priv->fw_pend_list))
+               return 0;
+
+       element = priv->fw_pend_list.next;
+
+       packet = list_entry(element, struct ipw2100_tx_packet, list);
+        tbd = &txq->drv[packet->index];
+
+       /* Determine how many TBD entries must be finished... */
+       switch (packet->type) {
+       case COMMAND:
+               /* COMMAND uses only one slot; don't advance */
+               descriptors_used = 1;
+               e = txq->oldest;
+               break;
+
+       case DATA:
+               /* DATA uses two slots; advance and loop position. */
+               descriptors_used = tbd->num_fragments;
+                frag_num = tbd->num_fragments - 1;
+               e = txq->oldest + frag_num;
+               e %= txq->entries;
+               break;
+
+       default:
+               printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n",
+                                  priv->net_dev->name);
+               return 0;
+       }
+
+       /* if the last TBD is not done by NIC yet, then packet is
+        * not ready to be released.
+        *
+        */
+       read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+                     &r);
+       read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+                     &w);
+       if (w != txq->next)
+               printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n",
+                      priv->net_dev->name);
+
+        /*
+        * txq->next is the index of the last packet written txq->oldest is
+        * the index of the r is the index of the next packet to be read by
+        * firmware
+        */
+
+
+       /*
+        * Quick graphic to help you visualize the following
+        * if / else statement
+        *
+        * ===>|                     s---->|===============
+        *                               e>|
+        * | a | b | c | d | e | f | g | h | i | j | k | l
+        *       r---->|
+        *               w
+        *
+        * w - updated by driver
+        * r - updated by firmware
+        * s - start of oldest BD entry (txq->oldest)
+        * e - end of oldest BD entry
+        *
+        */
+       if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) {
+               IPW_DEBUG_TX("exit - no processed packets ready to release.\n");
+               return 0;
+       }
+
+       list_del(element);
+       DEC_STAT(&priv->fw_pend_stat);
+
+#ifdef CONFIG_IPW_DEBUG
+       {
+               int i = txq->oldest;
+               IPW_DEBUG_TX(
+                       "TX%d V=%p P=%04X T=%04X L=%d\n", i,
+                       &txq->drv[i],
+                       (u32)(txq->nic + i * sizeof(struct ipw2100_bd)),
+                       txq->drv[i].host_addr,
+                       txq->drv[i].buf_length);
+
+               if (packet->type == DATA) {
+                       i = (i + 1) % txq->entries;
+
+                       IPW_DEBUG_TX(
+                               "TX%d V=%p P=%04X T=%04X L=%d\n", i,
+                               &txq->drv[i],
+                               (u32)(txq->nic + i *
+                               sizeof(struct ipw2100_bd)),
+                               (u32)txq->drv[i].host_addr,
+                               txq->drv[i].buf_length);
+               }
+       }
+#endif
+
+       switch (packet->type) {
+       case DATA:
+               if (txq->drv[txq->oldest].status.info.fields.txType != 0)
+                       printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch.  "
+                              "Expecting DATA TBD but pulled "
+                              "something else: ids %d=%d.\n",
+                              priv->net_dev->name, txq->oldest, packet->index);
+
+               /* DATA packet; we have to unmap and free the SKB */
+               priv->ieee->stats.tx_packets++;
+               for (i = 0; i < frag_num; i++) {
+                       tbd = &txq->drv[(packet->index + 1 + i) %
+                                       txq->entries];
+
+                       IPW_DEBUG_TX(
+                               "TX%d P=%08x L=%d\n",
+                               (packet->index + 1 + i) % txq->entries,
+                               tbd->host_addr, tbd->buf_length);
+
+                       pci_unmap_single(priv->pci_dev,
+                                        tbd->host_addr,
+                                        tbd->buf_length,
+                                        PCI_DMA_TODEVICE);
+               }
+
+               priv->ieee->stats.tx_bytes += packet->info.d_struct.txb->payload_size;
+               ieee80211_txb_free(packet->info.d_struct.txb);
+               packet->info.d_struct.txb = NULL;
+
+               list_add_tail(element, &priv->tx_free_list);
+               INC_STAT(&priv->tx_free_stat);
+
+               /* We have a free slot in the Tx queue, so wake up the
+                * transmit layer if it is stopped. */
+               if (priv->status & STATUS_ASSOCIATED &&
+                   netif_queue_stopped(priv->net_dev)) {
+                       IPW_DEBUG_INFO(KERN_INFO
+                                          "%s: Waking net queue.\n",
+                                          priv->net_dev->name);
+                       netif_wake_queue(priv->net_dev);
+               }
+
+               /* A packet was processed by the hardware, so update the
+                * watchdog */
+               priv->net_dev->trans_start = jiffies;
+
+               break;
+
+       case COMMAND:
+               if (txq->drv[txq->oldest].status.info.fields.txType != 1)
+                       printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch.  "
+                              "Expecting COMMAND TBD but pulled "
+                              "something else: ids %d=%d.\n",
+                              priv->net_dev->name, txq->oldest, packet->index);
+
+#ifdef CONFIG_IPW_DEBUG
+               if (packet->info.c_struct.cmd->host_command_reg <
+                   sizeof(command_types) / sizeof(*command_types))
+                       IPW_DEBUG_TX(
+                               "Command '%s (%d)' processed: %d.\n",
+                               command_types[packet->info.c_struct.cmd->host_command_reg],
+                               packet->info.c_struct.cmd->host_command_reg,
+                               packet->info.c_struct.cmd->cmd_status_reg);
+#endif
+
+               list_add_tail(element, &priv->msg_free_list);
+               INC_STAT(&priv->msg_free_stat);
+               break;
+       }
+
+       /* advance oldest used TBD pointer to start of next entry */
+       txq->oldest = (e + 1) % txq->entries;
+       /* increase available TBDs number */
+       txq->available += descriptors_used;
+       SET_STAT(&priv->txq_stat, txq->available);
+
+       IPW_DEBUG_TX("packet latency (send to process)  %ld jiffies\n",
+                        jiffies - packet->jiffy_start);
+
+       return (!list_empty(&priv->fw_pend_list));
+}
+
+
+static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv)
+{
+       int i = 0;
+
+       while (__ipw2100_tx_process(priv) && i < 200) i++;
+
+       if (i == 200) {
+               printk(KERN_WARNING DRV_NAME ": "
+                      "%s: Driver is running slow (%d iters).\n",
+                      priv->net_dev->name, i);
+       }
+}
+
+
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
+{
+       struct list_head *element;
+       struct ipw2100_tx_packet *packet;
+       struct ipw2100_bd_queue *txq = &priv->tx_queue;
+       struct ipw2100_bd *tbd;
+       int next = txq->next;
+
+       while (!list_empty(&priv->msg_pend_list)) {
+               /* if there isn't enough space in TBD queue, then
+                * don't stuff a new one in.
+                * NOTE: 3 are needed as a command will take one,
+                *       and there is a minimum of 2 that must be
+                *       maintained between the r and w indexes
+                */
+               if (txq->available <= 3) {
+                       IPW_DEBUG_TX("no room in tx_queue\n");
+                       break;
+               }
+
+               element = priv->msg_pend_list.next;
+               list_del(element);
+               DEC_STAT(&priv->msg_pend_stat);
+
+               packet = list_entry(element,
+                                   struct ipw2100_tx_packet, list);
+
+               IPW_DEBUG_TX("using TBD at virt=%p, phys=%p\n",
+                                &txq->drv[txq->next],
+                                (void*)(txq->nic + txq->next *
+                                        sizeof(struct ipw2100_bd)));
+
+               packet->index = txq->next;
+
+               tbd = &txq->drv[txq->next];
+
+               /* initialize TBD */
+               tbd->host_addr = packet->info.c_struct.cmd_phys;
+               tbd->buf_length = sizeof(struct ipw2100_cmd_header);
+               /* not marking number of fragments causes problems
+                * with f/w debug version */
+               tbd->num_fragments = 1;
+               tbd->status.info.field =
+                       IPW_BD_STATUS_TX_FRAME_COMMAND |
+                       IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+
+               /* update TBD queue counters */
+               txq->next++;
+               txq->next %= txq->entries;
+               txq->available--;
+               DEC_STAT(&priv->txq_stat);
+
+               list_add_tail(element, &priv->fw_pend_list);
+               INC_STAT(&priv->fw_pend_stat);
+       }
+
+       if (txq->next != next) {
+               /* kick off the DMA by notifying firmware the
+                * write index has moved; make sure TBD stores are sync'd */
+               wmb();
+               write_register(priv->net_dev,
+                              IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+                              txq->next);
+       }
+}
+
+
+/*
+ * ipw2100_tx_send_data
+ *
+ */
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
+{
+       struct list_head *element;
+       struct ipw2100_tx_packet *packet;
+       struct ipw2100_bd_queue *txq = &priv->tx_queue;
+       struct ipw2100_bd *tbd;
+       int next = txq->next;
+        int i = 0;
+       struct ipw2100_data_header *ipw_hdr;
+       struct ieee80211_hdr *hdr;
+
+       while (!list_empty(&priv->tx_pend_list)) {
+               /* if there isn't enough space in TBD queue, then
+                * don't stuff a new one in.
+                * NOTE: 4 are needed as a data will take two,
+                *       and there is a minimum of 2 that must be
+                *       maintained between the r and w indexes
+                */
+               element = priv->tx_pend_list.next;
+                packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+               if (unlikely(1 + packet->info.d_struct.txb->nr_frags >
+                            IPW_MAX_BDS)) {
+                       /* TODO: Support merging buffers if more than
+                        * IPW_MAX_BDS are used */
+                       IPW_DEBUG_INFO(
+                              "%s: Maximum BD theshold exceeded.  "
+                              "Increase fragmentation level.\n",
+                              priv->net_dev->name);
+               }
+
+               if (txq->available <= 3 +
+                   packet->info.d_struct.txb->nr_frags) {
+                       IPW_DEBUG_TX("no room in tx_queue\n");
+                       break;
+               }
+
+               list_del(element);
+               DEC_STAT(&priv->tx_pend_stat);
+
+               tbd = &txq->drv[txq->next];
+
+               packet->index = txq->next;
+
+               ipw_hdr = packet->info.d_struct.data;
+               hdr = (struct ieee80211_hdr *)packet->info.d_struct.txb->
+                       fragments[0]->data;
+
+               if (priv->ieee->iw_mode == IW_MODE_INFRA) {
+                       /* To DS: Addr1 = BSSID, Addr2 = SA,
+                          Addr3 = DA */
+                       memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+                       memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN);
+               } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+                       /* not From/To DS: Addr1 = DA, Addr2 = SA,
+                          Addr3 = BSSID */
+                       memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+                       memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN);
+               }
+
+               ipw_hdr->host_command_reg = SEND;
+               ipw_hdr->host_command_reg1 = 0;
+
+               /* For now we only support host based encryption */
+               ipw_hdr->needs_encryption = 0;
+               ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted;
+               if (packet->info.d_struct.txb->nr_frags > 1)
+                       ipw_hdr->fragment_size =
+                               packet->info.d_struct.txb->frag_size - IEEE80211_3ADDR_LEN;
+               else
+                       ipw_hdr->fragment_size = 0;
+
+               tbd->host_addr = packet->info.d_struct.data_phys;
+               tbd->buf_length = sizeof(struct ipw2100_data_header);
+               tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags;
+               tbd->status.info.field =
+                       IPW_BD_STATUS_TX_FRAME_802_3 |
+                       IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+               txq->next++;
+               txq->next %= txq->entries;
+
+               IPW_DEBUG_TX(
+                       "data header tbd TX%d P=%08x L=%d\n",
+                       packet->index, tbd->host_addr,
+                       tbd->buf_length);
+#ifdef CONFIG_IPW_DEBUG
+               if (packet->info.d_struct.txb->nr_frags > 1)
+                       IPW_DEBUG_FRAG("fragment Tx: %d frames\n",
+                                      packet->info.d_struct.txb->nr_frags);
+#endif
+
+                for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) {
+                       tbd = &txq->drv[txq->next];
+                       if (i == packet->info.d_struct.txb->nr_frags - 1)
+                               tbd->status.info.field =
+                                       IPW_BD_STATUS_TX_FRAME_802_3 |
+                                       IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+                       else
+                               tbd->status.info.field =
+                                       IPW_BD_STATUS_TX_FRAME_802_3 |
+                                       IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+
+                       tbd->buf_length = packet->info.d_struct.txb->
+                               fragments[i]->len - IEEE80211_3ADDR_LEN;
+
+                        tbd->host_addr = pci_map_single(
+                               priv->pci_dev,
+                               packet->info.d_struct.txb->fragments[i]->data +
+                               IEEE80211_3ADDR_LEN,
+                               tbd->buf_length,
+                               PCI_DMA_TODEVICE);
+
+                       IPW_DEBUG_TX(
+                               "data frag tbd TX%d P=%08x L=%d\n",
+                               txq->next, tbd->host_addr, tbd->buf_length);
+
+                       pci_dma_sync_single_for_device(
+                               priv->pci_dev, tbd->host_addr,
+                               tbd->buf_length,
+                               PCI_DMA_TODEVICE);
+
+                       txq->next++;
+                       txq->next %= txq->entries;
+                }
+
+               txq->available -= 1 + packet->info.d_struct.txb->nr_frags;
+               SET_STAT(&priv->txq_stat, txq->available);
+
+               list_add_tail(element, &priv->fw_pend_list);
+               INC_STAT(&priv->fw_pend_stat);
+       }
+
+       if (txq->next != next) {
+               /* kick off the DMA by notifying firmware the
+                * write index has moved; make sure TBD stores are sync'd */
+               write_register(priv->net_dev,
+                              IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+                              txq->next);
+       }
+        return;
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv)
+{
+       struct net_device *dev = priv->net_dev;
+       unsigned long flags;
+       u32 inta, tmp;
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+       ipw2100_disable_interrupts(priv);
+
+       read_register(dev, IPW_REG_INTA, &inta);
+
+       IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n",
+                     (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+       priv->in_isr++;
+       priv->interrupts++;
+
+       /* We do not loop and keep polling for more interrupts as this
+        * is frowned upon and doesn't play nicely with other potentially
+        * chained IRQs */
+       IPW_DEBUG_ISR("INTA: 0x%08lX\n",
+                     (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+       if (inta & IPW2100_INTA_FATAL_ERROR) {
+               printk(KERN_WARNING DRV_NAME
+                                 ": Fatal interrupt. Scheduling firmware restart.\n");
+               priv->inta_other++;
+               write_register(
+                       dev, IPW_REG_INTA,
+                       IPW2100_INTA_FATAL_ERROR);
+
+               read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error);
+               IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n",
+                              priv->net_dev->name, priv->fatal_error);
+
+               read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp);
+               IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n",
+                              priv->net_dev->name, tmp);
+
+               /* Wake up any sleeping jobs */
+               schedule_reset(priv);
+       }
+
+       if (inta & IPW2100_INTA_PARITY_ERROR) {
+               printk(KERN_ERR DRV_NAME ": ***** PARITY ERROR INTERRUPT !!!! \n");
+               priv->inta_other++;
+               write_register(
+                       dev, IPW_REG_INTA,
+                       IPW2100_INTA_PARITY_ERROR);
+       }
+
+       if (inta & IPW2100_INTA_RX_TRANSFER) {
+               IPW_DEBUG_ISR("RX interrupt\n");
+
+               priv->rx_interrupts++;
+
+               write_register(
+                       dev, IPW_REG_INTA,
+                       IPW2100_INTA_RX_TRANSFER);
+
+               __ipw2100_rx_process(priv);
+               __ipw2100_tx_complete(priv);
+       }
+
+       if (inta & IPW2100_INTA_TX_TRANSFER) {
+               IPW_DEBUG_ISR("TX interrupt\n");
+
+               priv->tx_interrupts++;
+
+               write_register(dev, IPW_REG_INTA,
+                              IPW2100_INTA_TX_TRANSFER);
+
+               __ipw2100_tx_complete(priv);
+               ipw2100_tx_send_commands(priv);
+               ipw2100_tx_send_data(priv);
+       }
+
+       if (inta & IPW2100_INTA_TX_COMPLETE) {
+               IPW_DEBUG_ISR("TX complete\n");
+               priv->inta_other++;
+               write_register(
+                       dev, IPW_REG_INTA,
+                       IPW2100_INTA_TX_COMPLETE);
+
+               __ipw2100_tx_complete(priv);
+       }
+
+       if (inta & IPW2100_INTA_EVENT_INTERRUPT) {
+               /* ipw2100_handle_event(dev); */
+               priv->inta_other++;
+               write_register(
+                       dev, IPW_REG_INTA,
+                       IPW2100_INTA_EVENT_INTERRUPT);
+       }
+
+       if (inta & IPW2100_INTA_FW_INIT_DONE) {
+               IPW_DEBUG_ISR("FW init done interrupt\n");
+               priv->inta_other++;
+
+               read_register(dev, IPW_REG_INTA, &tmp);
+               if (tmp & (IPW2100_INTA_FATAL_ERROR |
+                          IPW2100_INTA_PARITY_ERROR)) {
+                       write_register(
+                               dev, IPW_REG_INTA,
+                               IPW2100_INTA_FATAL_ERROR |
+                               IPW2100_INTA_PARITY_ERROR);
+               }
+
+               write_register(dev, IPW_REG_INTA,
+                              IPW2100_INTA_FW_INIT_DONE);
+       }
+
+       if (inta & IPW2100_INTA_STATUS_CHANGE) {
+               IPW_DEBUG_ISR("Status change interrupt\n");
+               priv->inta_other++;
+               write_register(
+                       dev, IPW_REG_INTA,
+                       IPW2100_INTA_STATUS_CHANGE);
+       }
+
+       if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) {
+               IPW_DEBUG_ISR("slave host mode interrupt\n");
+               priv->inta_other++;
+               write_register(
+                       dev, IPW_REG_INTA,
+                       IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE);
+       }
+
+       priv->in_isr--;
+       ipw2100_enable_interrupts(priv);
+
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       IPW_DEBUG_ISR("exit\n");
+}
+
+
+static irqreturn_t ipw2100_interrupt(int irq, void *data,
+                                    struct pt_regs *regs)
+{
+       struct ipw2100_priv *priv = data;
+       u32 inta, inta_mask;
+
+       if (!data)
+               return IRQ_NONE;
+
+       spin_lock(&priv->low_lock);
+
+       /* We check to see if we should be ignoring interrupts before
+        * we touch the hardware.  During ucode load if we try and handle
+        * an interrupt we can cause keyboard problems as well as cause
+        * the ucode to fail to initialize */
+       if (!(priv->status & STATUS_INT_ENABLED)) {
+               /* Shared IRQ */
+               goto none;
+       }
+
+       read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+       read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+       if (inta == 0xFFFFFFFF) {
+               /* Hardware disappeared */
+               printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n");
+               goto none;
+       }
+
+       inta &= IPW_INTERRUPT_MASK;
+
+       if (!(inta & inta_mask)) {
+               /* Shared interrupt */
+               goto none;
+       }
+
+       /* We disable the hardware interrupt here just to prevent unneeded
+        * calls to be made.  We disable this again within the actual
+        * work tasklet, so if another part of the code re-enables the
+        * interrupt, that is fine */
+       ipw2100_disable_interrupts(priv);
+
+       tasklet_schedule(&priv->irq_tasklet);
+       spin_unlock(&priv->low_lock);
+
+       return IRQ_HANDLED;
+ none:
+       spin_unlock(&priv->low_lock);
+       return IRQ_NONE;
+}
+
+static int ipw2100_tx(struct ieee80211_txb *txb, struct net_device *dev)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       struct list_head *element;
+       struct ipw2100_tx_packet *packet;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+
+       if (!(priv->status & STATUS_ASSOCIATED)) {
+               IPW_DEBUG_INFO("Can not transmit when not connected.\n");
+               priv->ieee->stats.tx_carrier_errors++;
+               netif_stop_queue(dev);
+               goto fail_unlock;
+       }
+
+       if (list_empty(&priv->tx_free_list))
+               goto fail_unlock;
+
+       element = priv->tx_free_list.next;
+       packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+       packet->info.d_struct.txb = txb;
+
+       IPW_DEBUG_TX("Sending fragment (%d bytes):\n",
+                        txb->fragments[0]->len);
+       printk_buf(IPW_DL_TX, txb->fragments[0]->data,
+                  txb->fragments[0]->len);
+
+       packet->jiffy_start = jiffies;
+
+       list_del(element);
+       DEC_STAT(&priv->tx_free_stat);
+
+       list_add_tail(element, &priv->tx_pend_list);
+       INC_STAT(&priv->tx_pend_stat);
+
+       ipw2100_tx_send_data(priv);
+
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+       return 0;
+
+ fail_unlock:
+       netif_stop_queue(dev);
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+       return 1;
+}
+
+
+static int ipw2100_msg_allocate(struct ipw2100_priv *priv)
+{
+       int i, j, err = -EINVAL;
+       void *v;
+       dma_addr_t p;
+
+       priv->msg_buffers = (struct ipw2100_tx_packet *)kmalloc(
+               IPW_COMMAND_POOL_SIZE * sizeof(struct ipw2100_tx_packet),
+               GFP_KERNEL);
+       if (!priv->msg_buffers) {
+               printk(KERN_ERR DRV_NAME ": %s: PCI alloc failed for msg "
+                      "buffers.\n", priv->net_dev->name);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+               v = pci_alloc_consistent(
+                       priv->pci_dev,
+                       sizeof(struct ipw2100_cmd_header),
+                       &p);
+               if (!v) {
+                       printk(KERN_ERR DRV_NAME ": "
+                              "%s: PCI alloc failed for msg "
+                              "buffers.\n",
+                              priv->net_dev->name);
+                       err = -ENOMEM;
+                       break;
+               }
+
+               memset(v, 0, sizeof(struct ipw2100_cmd_header));
+
+               priv->msg_buffers[i].type = COMMAND;
+               priv->msg_buffers[i].info.c_struct.cmd =
+                       (struct ipw2100_cmd_header*)v;
+               priv->msg_buffers[i].info.c_struct.cmd_phys = p;
+       }
+
+       if (i == IPW_COMMAND_POOL_SIZE)
+               return 0;
+
+       for (j = 0; j < i; j++) {
+               pci_free_consistent(
+                       priv->pci_dev,
+                       sizeof(struct ipw2100_cmd_header),
+                       priv->msg_buffers[j].info.c_struct.cmd,
+                       priv->msg_buffers[j].info.c_struct.cmd_phys);
+       }
+
+       kfree(priv->msg_buffers);
+       priv->msg_buffers = NULL;
+
+       return err;
+}
+
+static int ipw2100_msg_initialize(struct ipw2100_priv *priv)
+{
+       int i;
+
+       INIT_LIST_HEAD(&priv->msg_free_list);
+       INIT_LIST_HEAD(&priv->msg_pend_list);
+
+       for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++)
+               list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list);
+       SET_STAT(&priv->msg_free_stat, i);
+
+       return 0;
+}
+
+static void ipw2100_msg_free(struct ipw2100_priv *priv)
+{
+       int i;
+
+       if (!priv->msg_buffers)
+               return;
+
+       for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+               pci_free_consistent(priv->pci_dev,
+                                   sizeof(struct ipw2100_cmd_header),
+                                   priv->msg_buffers[i].info.c_struct.cmd,
+                                   priv->msg_buffers[i].info.c_struct.cmd_phys);
+       }
+
+       kfree(priv->msg_buffers);
+       priv->msg_buffers = NULL;
+}
+
+static ssize_t show_pci(struct device *d, struct device_attribute *attr,
+                       char *buf)
+{
+       struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev);
+       char *out = buf;
+       int i, j;
+       u32 val;
+
+       for (i = 0; i < 16; i++) {
+               out += sprintf(out, "[%08X] ", i * 16);
+               for (j = 0; j < 16; j += 4) {
+                       pci_read_config_dword(pci_dev, i * 16 + j, &val);
+                       out += sprintf(out, "%08X ", val);
+               }
+               out += sprintf(out, "\n");
+       }
+
+       return out - buf;
+}
+static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL);
+
+static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
+                       char *buf)
+{
+       struct ipw2100_priv *p = d->driver_data;
+       return sprintf(buf, "0x%08x\n", (int)p->config);
+}
+static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+
+static ssize_t show_status(struct device *d, struct device_attribute *attr,
+                       char *buf)
+{
+       struct ipw2100_priv *p = d->driver_data;
+       return sprintf(buf, "0x%08x\n", (int)p->status);
+}
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_capability(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *p = d->driver_data;
+       return sprintf(buf, "0x%08x\n", (int)p->capability);
+}
+static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL);
+
+
+#define IPW2100_REG(x) { IPW_ ##x, #x }
+static const struct {
+       u32 addr;
+       const char *name;
+} hw_data[] = {
+       IPW2100_REG(REG_GP_CNTRL),
+       IPW2100_REG(REG_GPIO),
+       IPW2100_REG(REG_INTA),
+       IPW2100_REG(REG_INTA_MASK),
+       IPW2100_REG(REG_RESET_REG),
+};
+#define IPW2100_NIC(x, s) { x, #x, s }
+static const struct {
+       u32 addr;
+       const char *name;
+       size_t size;
+} nic_data[] = {
+       IPW2100_NIC(IPW2100_CONTROL_REG, 2),
+       IPW2100_NIC(0x210014, 1),
+       IPW2100_NIC(0x210000, 1),
+};
+#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d }
+static const struct {
+       u8 index;
+       const char *name;
+       const char *desc;
+} ord_data[] = {
+       IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
+       IPW2100_ORD(STAT_TX_HOST_COMPLETE, "successful Host Tx's (MSDU)"),
+       IPW2100_ORD(STAT_TX_DIR_DATA,      "successful Directed Tx's (MSDU)"),
+       IPW2100_ORD(STAT_TX_DIR_DATA1,     "successful Directed Tx's (MSDU) @ 1MB"),
+       IPW2100_ORD(STAT_TX_DIR_DATA2,     "successful Directed Tx's (MSDU) @ 2MB"),
+       IPW2100_ORD(STAT_TX_DIR_DATA5_5,   "successful Directed Tx's (MSDU) @ 5_5MB"),
+       IPW2100_ORD(STAT_TX_DIR_DATA11,    "successful Directed Tx's (MSDU) @ 11MB"),
+       IPW2100_ORD(STAT_TX_NODIR_DATA1,   "successful Non_Directed Tx's (MSDU) @ 1MB"),
+       IPW2100_ORD(STAT_TX_NODIR_DATA2,   "successful Non_Directed Tx's (MSDU) @ 2MB"),
+       IPW2100_ORD(STAT_TX_NODIR_DATA5_5, "successful Non_Directed Tx's (MSDU) @ 5.5MB"),
+       IPW2100_ORD(STAT_TX_NODIR_DATA11,  "successful Non_Directed Tx's (MSDU) @ 11MB"),
+       IPW2100_ORD(STAT_NULL_DATA,        "successful NULL data Tx's"),
+       IPW2100_ORD(STAT_TX_RTS,           "successful Tx RTS"),
+       IPW2100_ORD(STAT_TX_CTS,           "successful Tx CTS"),
+       IPW2100_ORD(STAT_TX_ACK,           "successful Tx ACK"),
+       IPW2100_ORD(STAT_TX_ASSN,          "successful Association Tx's"),
+       IPW2100_ORD(STAT_TX_ASSN_RESP,     "successful Association response Tx's"),
+       IPW2100_ORD(STAT_TX_REASSN,        "successful Reassociation Tx's"),
+       IPW2100_ORD(STAT_TX_REASSN_RESP,   "successful Reassociation response Tx's"),
+       IPW2100_ORD(STAT_TX_PROBE,         "probes successfully transmitted"),
+       IPW2100_ORD(STAT_TX_PROBE_RESP,    "probe responses successfully transmitted"),
+       IPW2100_ORD(STAT_TX_BEACON,        "tx beacon"),
+       IPW2100_ORD(STAT_TX_ATIM,          "Tx ATIM"),
+       IPW2100_ORD(STAT_TX_DISASSN,       "successful Disassociation TX"),
+       IPW2100_ORD(STAT_TX_AUTH,          "successful Authentication Tx"),
+       IPW2100_ORD(STAT_TX_DEAUTH,        "successful Deauthentication TX"),
+       IPW2100_ORD(STAT_TX_TOTAL_BYTES,   "Total successful Tx data bytes"),
+       IPW2100_ORD(STAT_TX_RETRIES,       "Tx retries"),
+       IPW2100_ORD(STAT_TX_RETRY1,        "Tx retries at 1MBPS"),
+       IPW2100_ORD(STAT_TX_RETRY2,        "Tx retries at 2MBPS"),
+       IPW2100_ORD(STAT_TX_RETRY5_5,      "Tx retries at 5.5MBPS"),
+       IPW2100_ORD(STAT_TX_RETRY11,       "Tx retries at 11MBPS"),
+       IPW2100_ORD(STAT_TX_FAILURES,      "Tx Failures"),
+       IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP,"times max tries in a hop failed"),
+       IPW2100_ORD(STAT_TX_DISASSN_FAIL,       "times disassociation failed"),
+       IPW2100_ORD(STAT_TX_ERR_CTS,         "missed/bad CTS frames"),
+       IPW2100_ORD(STAT_TX_ERR_ACK,    "tx err due to acks"),
+       IPW2100_ORD(STAT_RX_HOST,       "packets passed to host"),
+       IPW2100_ORD(STAT_RX_DIR_DATA,   "directed packets"),
+       IPW2100_ORD(STAT_RX_DIR_DATA1,  "directed packets at 1MB"),
+       IPW2100_ORD(STAT_RX_DIR_DATA2,  "directed packets at 2MB"),
+       IPW2100_ORD(STAT_RX_DIR_DATA5_5,        "directed packets at 5.5MB"),
+       IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"),
+       IPW2100_ORD(STAT_RX_NODIR_DATA,"nondirected packets"),
+       IPW2100_ORD(STAT_RX_NODIR_DATA1,        "nondirected packets at 1MB"),
+       IPW2100_ORD(STAT_RX_NODIR_DATA2,        "nondirected packets at 2MB"),
+       IPW2100_ORD(STAT_RX_NODIR_DATA5_5,      "nondirected packets at 5.5MB"),
+       IPW2100_ORD(STAT_RX_NODIR_DATA11,       "nondirected packets at 11MB"),
+       IPW2100_ORD(STAT_RX_NULL_DATA,  "null data rx's"),
+       IPW2100_ORD(STAT_RX_RTS,        "Rx RTS"),
+       IPW2100_ORD(STAT_RX_CTS,        "Rx CTS"),
+       IPW2100_ORD(STAT_RX_ACK,        "Rx ACK"),
+       IPW2100_ORD(STAT_RX_CFEND,      "Rx CF End"),
+       IPW2100_ORD(STAT_RX_CFEND_ACK,  "Rx CF End + CF Ack"),
+       IPW2100_ORD(STAT_RX_ASSN,       "Association Rx's"),
+       IPW2100_ORD(STAT_RX_ASSN_RESP,  "Association response Rx's"),
+       IPW2100_ORD(STAT_RX_REASSN,     "Reassociation Rx's"),
+       IPW2100_ORD(STAT_RX_REASSN_RESP,        "Reassociation response Rx's"),
+       IPW2100_ORD(STAT_RX_PROBE,      "probe Rx's"),
+       IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"),
+       IPW2100_ORD(STAT_RX_BEACON,     "Rx beacon"),
+       IPW2100_ORD(STAT_RX_ATIM,       "Rx ATIM"),
+       IPW2100_ORD(STAT_RX_DISASSN,    "disassociation Rx"),
+       IPW2100_ORD(STAT_RX_AUTH,       "authentication Rx"),
+       IPW2100_ORD(STAT_RX_DEAUTH,     "deauthentication Rx"),
+       IPW2100_ORD(STAT_RX_TOTAL_BYTES,"Total rx data bytes received"),
+       IPW2100_ORD(STAT_RX_ERR_CRC,     "packets with Rx CRC error"),
+       IPW2100_ORD(STAT_RX_ERR_CRC1,    "Rx CRC errors at 1MB"),
+       IPW2100_ORD(STAT_RX_ERR_CRC2,    "Rx CRC errors at 2MB"),
+       IPW2100_ORD(STAT_RX_ERR_CRC5_5,  "Rx CRC errors at 5.5MB"),
+       IPW2100_ORD(STAT_RX_ERR_CRC11,   "Rx CRC errors at 11MB"),
+       IPW2100_ORD(STAT_RX_DUPLICATE1, "duplicate rx packets at 1MB"),
+       IPW2100_ORD(STAT_RX_DUPLICATE2,  "duplicate rx packets at 2MB"),
+       IPW2100_ORD(STAT_RX_DUPLICATE5_5,        "duplicate rx packets at 5.5MB"),
+       IPW2100_ORD(STAT_RX_DUPLICATE11,         "duplicate rx packets at 11MB"),
+       IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"),
+       IPW2100_ORD(PERS_DB_LOCK,       "locking fw permanent  db"),
+       IPW2100_ORD(PERS_DB_SIZE,       "size of fw permanent  db"),
+       IPW2100_ORD(PERS_DB_ADDR,       "address of fw permanent  db"),
+       IPW2100_ORD(STAT_RX_INVALID_PROTOCOL,   "rx frames with invalid protocol"),
+       IPW2100_ORD(SYS_BOOT_TIME,      "Boot time"),
+       IPW2100_ORD(STAT_RX_NO_BUFFER,  "rx frames rejected due to no buffer"),
+       IPW2100_ORD(STAT_RX_MISSING_FRAG,       "rx frames dropped due to missing fragment"),
+       IPW2100_ORD(STAT_RX_ORPHAN_FRAG,        "rx frames dropped due to non-sequential fragment"),
+       IPW2100_ORD(STAT_RX_ORPHAN_FRAME,       "rx frames dropped due to unmatched 1st frame"),
+       IPW2100_ORD(STAT_RX_FRAG_AGEOUT,        "rx frames dropped due to uncompleted frame"),
+       IPW2100_ORD(STAT_RX_ICV_ERRORS, "ICV errors during decryption"),
+       IPW2100_ORD(STAT_PSP_SUSPENSION,"times adapter suspended"),
+       IPW2100_ORD(STAT_PSP_BCN_TIMEOUT,       "beacon timeout"),
+       IPW2100_ORD(STAT_PSP_POLL_TIMEOUT,      "poll response timeouts"),
+       IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT, "timeouts waiting for last {broad,multi}cast pkt"),
+       IPW2100_ORD(STAT_PSP_RX_DTIMS,  "PSP DTIMs received"),
+       IPW2100_ORD(STAT_PSP_RX_TIMS,   "PSP TIMs received"),
+       IPW2100_ORD(STAT_PSP_STATION_ID,"PSP Station ID"),
+       IPW2100_ORD(LAST_ASSN_TIME,     "RTC time of last association"),
+       IPW2100_ORD(STAT_PERCENT_MISSED_BCNS,"current calculation of % missed beacons"),
+       IPW2100_ORD(STAT_PERCENT_RETRIES,"current calculation of % missed tx retries"),
+       IPW2100_ORD(ASSOCIATED_AP_PTR,  "0 if not associated, else pointer to AP table entry"),
+       IPW2100_ORD(AVAILABLE_AP_CNT,   "AP's decsribed in the AP table"),
+       IPW2100_ORD(AP_LIST_PTR,        "Ptr to list of available APs"),
+       IPW2100_ORD(STAT_AP_ASSNS,      "associations"),
+       IPW2100_ORD(STAT_ASSN_FAIL,     "association failures"),
+       IPW2100_ORD(STAT_ASSN_RESP_FAIL,"failures due to response fail"),
+       IPW2100_ORD(STAT_FULL_SCANS,    "full scans"),
+       IPW2100_ORD(CARD_DISABLED,      "Card Disabled"),
+       IPW2100_ORD(STAT_ROAM_INHIBIT,  "times roaming was inhibited due to activity"),
+       IPW2100_ORD(RSSI_AT_ASSN,       "RSSI of associated AP at time of association"),
+       IPW2100_ORD(STAT_ASSN_CAUSE1,   "reassociation: no probe response or TX on hop"),
+       IPW2100_ORD(STAT_ASSN_CAUSE2,   "reassociation: poor tx/rx quality"),
+       IPW2100_ORD(STAT_ASSN_CAUSE3,   "reassociation: tx/rx quality (excessive AP load"),
+       IPW2100_ORD(STAT_ASSN_CAUSE4,   "reassociation: AP RSSI level"),
+       IPW2100_ORD(STAT_ASSN_CAUSE5,   "reassociations due to load leveling"),
+       IPW2100_ORD(STAT_AUTH_FAIL,     "times authentication failed"),
+       IPW2100_ORD(STAT_AUTH_RESP_FAIL,"times authentication response failed"),
+       IPW2100_ORD(STATION_TABLE_CNT,  "entries in association table"),
+       IPW2100_ORD(RSSI_AVG_CURR,      "Current avg RSSI"),
+       IPW2100_ORD(POWER_MGMT_MODE,    "Power mode - 0=CAM, 1=PSP"),
+       IPW2100_ORD(COUNTRY_CODE,       "IEEE country code as recv'd from beacon"),
+       IPW2100_ORD(COUNTRY_CHANNELS,   "channels suported by country"),
+       IPW2100_ORD(RESET_CNT,  "adapter resets (warm)"),
+       IPW2100_ORD(BEACON_INTERVAL,    "Beacon interval"),
+       IPW2100_ORD(ANTENNA_DIVERSITY,  "TRUE if antenna diversity is disabled"),
+       IPW2100_ORD(DTIM_PERIOD,        "beacon intervals between DTIMs"),
+       IPW2100_ORD(OUR_FREQ,   "current radio freq lower digits - channel ID"),
+       IPW2100_ORD(RTC_TIME,   "current RTC time"),
+       IPW2100_ORD(PORT_TYPE,  "operating mode"),
+       IPW2100_ORD(CURRENT_TX_RATE,    "current tx rate"),
+       IPW2100_ORD(SUPPORTED_RATES,    "supported tx rates"),
+       IPW2100_ORD(ATIM_WINDOW,        "current ATIM Window"),
+       IPW2100_ORD(BASIC_RATES,        "basic tx rates"),
+       IPW2100_ORD(NIC_HIGHEST_RATE,   "NIC highest tx rate"),
+       IPW2100_ORD(AP_HIGHEST_RATE,    "AP highest tx rate"),
+       IPW2100_ORD(CAPABILITIES,       "Management frame capability field"),
+       IPW2100_ORD(AUTH_TYPE,  "Type of authentication"),
+       IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"),
+       IPW2100_ORD(RTS_THRESHOLD,      "Min packet length for RTS handshaking"),
+       IPW2100_ORD(INT_MODE,   "International mode"),
+       IPW2100_ORD(FRAGMENTATION_THRESHOLD,    "protocol frag threshold"),
+       IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS, "EEPROM offset in SRAM"),
+       IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE,  "EEPROM size in SRAM"),
+       IPW2100_ORD(EEPROM_SKU_CAPABILITY,      "EEPROM SKU Capability"),
+       IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS,   "EEPROM IBSS 11b channel set"),
+       IPW2100_ORD(MAC_VERSION,        "MAC Version"),
+       IPW2100_ORD(MAC_REVISION,       "MAC Revision"),
+       IPW2100_ORD(RADIO_VERSION,      "Radio Version"),
+       IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"),
+       IPW2100_ORD(UCODE_VERSION,      "Ucode Version"),
+};
+
+
+static ssize_t show_registers(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       int i;
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       struct net_device *dev = priv->net_dev;
+       char * out = buf;
+       u32 val = 0;
+
+       out += sprintf(out, "%30s [Address ] : Hex\n", "Register");
+
+       for (i = 0; i < (sizeof(hw_data) / sizeof(*hw_data)); i++) {
+               read_register(dev, hw_data[i].addr, &val);
+               out += sprintf(out, "%30s [%08X] : %08X\n",
+                              hw_data[i].name, hw_data[i].addr, val);
+       }
+
+       return out - buf;
+}
+static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
+
+
+static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       struct net_device *dev = priv->net_dev;
+       char * out = buf;
+       int i;
+
+       out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry");
+
+       for (i = 0; i < (sizeof(nic_data) / sizeof(*nic_data)); i++) {
+               u8 tmp8;
+               u16 tmp16;
+               u32 tmp32;
+
+               switch (nic_data[i].size) {
+               case 1:
+                       read_nic_byte(dev, nic_data[i].addr, &tmp8);
+                       out += sprintf(out, "%30s [%08X] : %02X\n",
+                                      nic_data[i].name, nic_data[i].addr,
+                                      tmp8);
+                       break;
+               case 2:
+                       read_nic_word(dev, nic_data[i].addr, &tmp16);
+                       out += sprintf(out, "%30s [%08X] : %04X\n",
+                                      nic_data[i].name, nic_data[i].addr,
+                                      tmp16);
+                       break;
+               case 4:
+                       read_nic_dword(dev, nic_data[i].addr, &tmp32);
+                       out += sprintf(out, "%30s [%08X] : %08X\n",
+                                      nic_data[i].name, nic_data[i].addr,
+                                      tmp32);
+                       break;
+               }
+       }
+       return out - buf;
+}
+static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+
+
+static ssize_t show_memory(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       struct net_device *dev = priv->net_dev;
+       static unsigned long loop = 0;
+       int len = 0;
+       u32 buffer[4];
+       int i;
+       char line[81];
+
+       if (loop >= 0x30000)
+               loop = 0;
+
+       /* sysfs provides us PAGE_SIZE buffer */
+       while (len < PAGE_SIZE - 128 && loop < 0x30000) {
+
+               if (priv->snapshot[0]) for (i = 0; i < 4; i++)
+                       buffer[i] = *(u32 *)SNAPSHOT_ADDR(loop + i * 4);
+               else for (i = 0; i < 4; i++)
+                       read_nic_dword(dev, loop + i * 4, &buffer[i]);
+
+               if (priv->dump_raw)
+                       len += sprintf(buf + len,
+                                      "%c%c%c%c"
+                                      "%c%c%c%c"
+                                      "%c%c%c%c"
+                                      "%c%c%c%c",
+                                      ((u8*)buffer)[0x0],
+                                      ((u8*)buffer)[0x1],
+                                      ((u8*)buffer)[0x2],
+                                      ((u8*)buffer)[0x3],
+                                      ((u8*)buffer)[0x4],
+                                      ((u8*)buffer)[0x5],
+                                      ((u8*)buffer)[0x6],
+                                      ((u8*)buffer)[0x7],
+                                      ((u8*)buffer)[0x8],
+                                      ((u8*)buffer)[0x9],
+                                      ((u8*)buffer)[0xa],
+                                      ((u8*)buffer)[0xb],
+                                      ((u8*)buffer)[0xc],
+                                      ((u8*)buffer)[0xd],
+                                      ((u8*)buffer)[0xe],
+                                      ((u8*)buffer)[0xf]);
+               else
+                       len += sprintf(buf + len, "%s\n",
+                                      snprint_line(line, sizeof(line),
+                                                   (u8*)buffer, 16, loop));
+               loop += 16;
+       }
+
+       return len;
+}
+
+static ssize_t store_memory(struct device *d, struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       struct net_device *dev = priv->net_dev;
+       const char *p = buf;
+
+       if (count < 1)
+               return count;
+
+       if (p[0] == '1' ||
+           (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) {
+               IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n",
+                      dev->name);
+               priv->dump_raw = 1;
+
+       } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' &&
+                                 tolower(p[1]) == 'f')) {
+               IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n",
+                      dev->name);
+               priv->dump_raw = 0;
+
+       } else if (tolower(p[0]) == 'r') {
+               IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n",
+                      dev->name);
+               ipw2100_snapshot_free(priv);
+
+       } else
+               IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, "
+                      "reset = clear memory snapshot\n",
+                      dev->name);
+
+       return count;
+}
+static DEVICE_ATTR(memory, S_IWUSR|S_IRUGO, show_memory, store_memory);
+
+
+static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       u32 val = 0;
+       int len = 0;
+       u32 val_len;
+       static int loop = 0;
+
+       if (loop >= sizeof(ord_data) / sizeof(*ord_data))
+               loop = 0;
+
+       /* sysfs provides us PAGE_SIZE buffer */
+       while (len < PAGE_SIZE - 128 &&
+              loop < (sizeof(ord_data) / sizeof(*ord_data))) {
+
+               val_len = sizeof(u32);
+
+               if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val,
+                                       &val_len))
+                       len += sprintf(buf + len, "[0x%02X] = ERROR    %s\n",
+                                      ord_data[loop].index,
+                                      ord_data[loop].desc);
+               else
+                       len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n",
+                                      ord_data[loop].index, val,
+                                      ord_data[loop].desc);
+               loop++;
+       }
+
+       return len;
+}
+static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL);
+
+
+static ssize_t show_stats(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       char * out = buf;
+
+       out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n",
+                      priv->interrupts, priv->tx_interrupts,
+                      priv->rx_interrupts, priv->inta_other);
+       out += sprintf(out, "firmware resets: %d\n", priv->resets);
+       out += sprintf(out, "firmware hangs: %d\n", priv->hangs);
+#ifdef CONFIG_IPW_DEBUG
+       out += sprintf(out, "packet mismatch image: %s\n",
+                      priv->snapshot[0] ? "YES" : "NO");
+#endif
+
+       return out - buf;
+}
+static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
+
+
+static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
+{
+       int err;
+
+       if (mode == priv->ieee->iw_mode)
+               return 0;
+
+       err = ipw2100_disable_adapter(priv);
+       if (err) {
+               printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+                      priv->net_dev->name, err);
+               return err;
+       }
+
+       switch (mode) {
+       case IW_MODE_INFRA:
+               priv->net_dev->type = ARPHRD_ETHER;
+               break;
+       case IW_MODE_ADHOC:
+               priv->net_dev->type = ARPHRD_ETHER;
+               break;
+#ifdef CONFIG_IPW2100_MONITOR
+       case IW_MODE_MONITOR:
+               priv->last_mode = priv->ieee->iw_mode;
+               priv->net_dev->type = ARPHRD_IEEE80211;
+               break;
+#endif /* CONFIG_IPW2100_MONITOR */
+       }
+
+       priv->ieee->iw_mode = mode;
+
+#ifdef CONFIG_PM
+        /* Indicate ipw2100_download_firmware download firmware
+        * from disk instead of memory. */
+       ipw2100_firmware.version = 0;
+#endif
+
+       printk(KERN_INFO "%s: Reseting on mode change.\n",
+               priv->net_dev->name);
+       priv->reset_backoff = 0;
+       schedule_reset(priv);
+
+       return 0;
+}
+
+static ssize_t show_internals(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       int len = 0;
+
+#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" # y "\n", priv-> x)
+
+       if (priv->status & STATUS_ASSOCIATED)
+               len += sprintf(buf + len, "connected: %lu\n",
+                              get_seconds() - priv->connect_start);
+       else
+               len += sprintf(buf + len, "not connected\n");
+
+       DUMP_VAR(ieee->crypt[priv->ieee->tx_keyidx], p);
+       DUMP_VAR(status, 08lx);
+       DUMP_VAR(config, 08lx);
+       DUMP_VAR(capability, 08lx);
+
+       len += sprintf(buf + len, "last_rtc: %lu\n", (unsigned long)priv->last_rtc);
+
+       DUMP_VAR(fatal_error, d);
+       DUMP_VAR(stop_hang_check, d);
+       DUMP_VAR(stop_rf_kill, d);
+       DUMP_VAR(messages_sent, d);
+
+       DUMP_VAR(tx_pend_stat.value, d);
+       DUMP_VAR(tx_pend_stat.hi, d);
+
+       DUMP_VAR(tx_free_stat.value, d);
+       DUMP_VAR(tx_free_stat.lo, d);
+
+       DUMP_VAR(msg_free_stat.value, d);
+       DUMP_VAR(msg_free_stat.lo, d);
+
+       DUMP_VAR(msg_pend_stat.value, d);
+       DUMP_VAR(msg_pend_stat.hi, d);
+
+       DUMP_VAR(fw_pend_stat.value, d);
+       DUMP_VAR(fw_pend_stat.hi, d);
+
+       DUMP_VAR(txq_stat.value, d);
+       DUMP_VAR(txq_stat.lo, d);
+
+       DUMP_VAR(ieee->scans, d);
+       DUMP_VAR(reset_backoff, d);
+
+       return len;
+}
+static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL);
+
+
+static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       char essid[IW_ESSID_MAX_SIZE + 1];
+       u8 bssid[ETH_ALEN];
+       u32 chan = 0;
+       char * out = buf;
+       int length;
+       int ret;
+
+       memset(essid, 0, sizeof(essid));
+       memset(bssid, 0, sizeof(bssid));
+
+       length = IW_ESSID_MAX_SIZE;
+       ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length);
+       if (ret)
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                              __LINE__);
+
+       length = sizeof(bssid);
+       ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+                                 bssid, &length);
+       if (ret)
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                              __LINE__);
+
+       length = sizeof(u32);
+       ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length);
+       if (ret)
+               IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+                              __LINE__);
+
+       out += sprintf(out, "ESSID: %s\n", essid);
+       out += sprintf(out, "BSSID:   %02x:%02x:%02x:%02x:%02x:%02x\n",
+                      bssid[0], bssid[1], bssid[2],
+                      bssid[3], bssid[4], bssid[5]);
+       out += sprintf(out, "Channel: %d\n", chan);
+
+       return out - buf;
+}
+static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL);
+
+
+#ifdef CONFIG_IPW_DEBUG
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
+{
+       return sprintf(buf, "0x%08X\n", ipw2100_debug_level);
+}
+
+static ssize_t store_debug_level(struct device_driver *d, const char *buf,
+                                size_t count)
+{
+       char *p = (char *)buf;
+       u32 val;
+
+       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)
+               IPW_DEBUG_INFO(DRV_NAME
+                      ": %s is not in hex or decimal form.\n", buf);
+       else
+               ipw2100_debug_level = val;
+
+       return strnlen(buf, count);
+}
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level,
+                  store_debug_level);
+#endif /* CONFIG_IPW_DEBUG */
+
+
+static ssize_t show_fatal_error(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       char *out = buf;
+       int i;
+
+       if (priv->fatal_error)
+               out += sprintf(out, "0x%08X\n",
+                              priv->fatal_error);
+       else
+               out += sprintf(out, "0\n");
+
+       for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) {
+               if (!priv->fatal_errors[(priv->fatal_index - i) %
+                                       IPW2100_ERROR_QUEUE])
+                       continue;
+
+               out += sprintf(out, "%d. 0x%08X\n", i,
+                              priv->fatal_errors[(priv->fatal_index - i) %
+                                                 IPW2100_ERROR_QUEUE]);
+       }
+
+       return out - buf;
+}
+
+static ssize_t store_fatal_error(struct device *d,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       schedule_reset(priv);
+       return count;
+}
+static DEVICE_ATTR(fatal_error, S_IWUSR|S_IRUGO, show_fatal_error, store_fatal_error);
+
+
+static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       return sprintf(buf, "%d\n", priv->ieee->scan_age);
+}
+
+static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       struct net_device *dev = priv->net_dev;
+       char buffer[] = "00000000";
+       unsigned long len =
+           (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
+       unsigned long val;
+       char *p = buffer;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       strncpy(buffer, buf, len);
+       buffer[len] = 0;
+
+       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 == buffer) {
+               IPW_DEBUG_INFO("%s: user supplied invalid value.\n",
+                      dev->name);
+       } else {
+               priv->ieee->scan_age = val;
+               IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
+       }
+
+       IPW_DEBUG_INFO("exit\n");
+       return len;
+}
+static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
+
+
+static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       /* 0 - RF kill not enabled
+          1 - SW based RF kill active (sysfs)
+          2 - HW based RF kill active
+          3 - Both HW and SW baed RF kill active */
+       struct ipw2100_priv *priv = (struct ipw2100_priv *)d->driver_data;
+       int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
+               (rf_kill_active(priv) ? 0x2 : 0x0);
+       return sprintf(buf, "%i\n", val);
+}
+
+static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
+{
+       if ((disable_radio ? 1 : 0) ==
+           (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
+               return 0 ;
+
+       IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO  %s\n",
+                         disable_radio ? "OFF" : "ON");
+
+       down(&priv->action_sem);
+
+       if (disable_radio) {
+               priv->status |= STATUS_RF_KILL_SW;
+               ipw2100_down(priv);
+       } else {
+               priv->status &= ~STATUS_RF_KILL_SW;
+               if (rf_kill_active(priv)) {
+                       IPW_DEBUG_RF_KILL("Can not turn radio back on - "
+                                         "disabled by HW switch\n");
+                       /* Make sure the RF_KILL check timer is running */
+                       priv->stop_rf_kill = 0;
+                       cancel_delayed_work(&priv->rf_kill);
+                       queue_delayed_work(priv->workqueue, &priv->rf_kill,
+                                          HZ);
+               } else
+                       schedule_reset(priv);
+       }
+
+       up(&priv->action_sem);
+       return 1;
+}
+
+static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct ipw2100_priv *priv = dev_get_drvdata(d);
+       ipw_radio_kill_sw(priv, buf[0] == '1');
+       return count;
+}
+static DEVICE_ATTR(rf_kill, S_IWUSR|S_IRUGO, show_rf_kill, store_rf_kill);
+
+
+static struct attribute *ipw2100_sysfs_entries[] = {
+       &dev_attr_hardware.attr,
+       &dev_attr_registers.attr,
+       &dev_attr_ordinals.attr,
+       &dev_attr_pci.attr,
+       &dev_attr_stats.attr,
+       &dev_attr_internals.attr,
+       &dev_attr_bssinfo.attr,
+       &dev_attr_memory.attr,
+       &dev_attr_scan_age.attr,
+       &dev_attr_fatal_error.attr,
+       &dev_attr_rf_kill.attr,
+       &dev_attr_cfg.attr,
+       &dev_attr_status.attr,
+       &dev_attr_capability.attr,
+       NULL,
+};
+
+static struct attribute_group ipw2100_attribute_group = {
+       .attrs = ipw2100_sysfs_entries,
+};
+
+
+static int status_queue_allocate(struct ipw2100_priv *priv, int entries)
+{
+       struct ipw2100_status_queue *q = &priv->status_queue;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       q->size = entries * sizeof(struct ipw2100_status);
+       q->drv = (struct ipw2100_status *)pci_alloc_consistent(
+               priv->pci_dev, q->size, &q->nic);
+       if (!q->drv) {
+               IPW_DEBUG_WARNING(
+                      "Can not allocate status queue.\n");
+               return -ENOMEM;
+       }
+
+       memset(q->drv, 0, q->size);
+
+       IPW_DEBUG_INFO("exit\n");
+
+       return 0;
+}
+
+static void status_queue_free(struct ipw2100_priv *priv)
+{
+       IPW_DEBUG_INFO("enter\n");
+
+       if (priv->status_queue.drv) {
+               pci_free_consistent(
+                       priv->pci_dev, priv->status_queue.size,
+                       priv->status_queue.drv, priv->status_queue.nic);
+               priv->status_queue.drv = NULL;
+       }
+
+       IPW_DEBUG_INFO("exit\n");
+}
+
+static int bd_queue_allocate(struct ipw2100_priv *priv,
+                            struct ipw2100_bd_queue *q, int entries)
+{
+       IPW_DEBUG_INFO("enter\n");
+
+       memset(q, 0, sizeof(struct ipw2100_bd_queue));
+
+       q->entries = entries;
+       q->size = entries * sizeof(struct ipw2100_bd);
+       q->drv = pci_alloc_consistent(priv->pci_dev, q->size, &q->nic);
+       if (!q->drv) {
+               IPW_DEBUG_INFO("can't allocate shared memory for buffer descriptors\n");
+               return -ENOMEM;
+       }
+       memset(q->drv, 0, q->size);
+
+       IPW_DEBUG_INFO("exit\n");
+
+       return 0;
+}
+
+static void bd_queue_free(struct ipw2100_priv *priv,
+                         struct ipw2100_bd_queue *q)
+{
+       IPW_DEBUG_INFO("enter\n");
+
+       if (!q)
+               return;
+
+       if (q->drv) {
+               pci_free_consistent(priv->pci_dev,
+                                   q->size, q->drv, q->nic);
+               q->drv = NULL;
+       }
+
+       IPW_DEBUG_INFO("exit\n");
+}
+
+static void bd_queue_initialize(
+       struct ipw2100_priv *priv, struct ipw2100_bd_queue * q,
+       u32 base, u32 size, u32 r, u32 w)
+{
+       IPW_DEBUG_INFO("enter\n");
+
+       IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv, (u32)q->nic);
+
+       write_register(priv->net_dev, base, q->nic);
+       write_register(priv->net_dev, size, q->entries);
+       write_register(priv->net_dev, r, q->oldest);
+       write_register(priv->net_dev, w, q->next);
+
+       IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_kill_workqueue(struct ipw2100_priv *priv)
+{
+       if (priv->workqueue) {
+               priv->stop_rf_kill = 1;
+               priv->stop_hang_check = 1;
+               cancel_delayed_work(&priv->reset_work);
+               cancel_delayed_work(&priv->security_work);
+               cancel_delayed_work(&priv->wx_event_work);
+               cancel_delayed_work(&priv->hang_check);
+               cancel_delayed_work(&priv->rf_kill);
+               destroy_workqueue(priv->workqueue);
+               priv->workqueue = NULL;
+       }
+}
+
+static int ipw2100_tx_allocate(struct ipw2100_priv *priv)
+{
+       int i, j, err = -EINVAL;
+       void *v;
+       dma_addr_t p;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH);
+       if (err) {
+               IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n",
+                      priv->net_dev->name);
+               return err;
+       }
+
+       priv->tx_buffers = (struct ipw2100_tx_packet *)kmalloc(
+               TX_PENDED_QUEUE_LENGTH * sizeof(struct ipw2100_tx_packet),
+               GFP_ATOMIC);
+       if (!priv->tx_buffers) {
+               printk(KERN_ERR DRV_NAME ": %s: alloc failed form tx buffers.\n",
+                      priv->net_dev->name);
+               bd_queue_free(priv, &priv->tx_queue);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+               v = pci_alloc_consistent(
+                       priv->pci_dev, sizeof(struct ipw2100_data_header), &p);
+               if (!v) {
+                       printk(KERN_ERR DRV_NAME ": %s: PCI alloc failed for tx "
+                              "buffers.\n", priv->net_dev->name);
+                       err = -ENOMEM;
+                       break;
+               }
+
+               priv->tx_buffers[i].type = DATA;
+               priv->tx_buffers[i].info.d_struct.data = (struct ipw2100_data_header*)v;
+               priv->tx_buffers[i].info.d_struct.data_phys = p;
+               priv->tx_buffers[i].info.d_struct.txb = NULL;
+       }
+
+       if (i == TX_PENDED_QUEUE_LENGTH)
+               return 0;
+
+       for (j = 0; j < i; j++) {
+               pci_free_consistent(
+                       priv->pci_dev,
+                       sizeof(struct ipw2100_data_header),
+                       priv->tx_buffers[j].info.d_struct.data,
+                       priv->tx_buffers[j].info.d_struct.data_phys);
+       }
+
+       kfree(priv->tx_buffers);
+       priv->tx_buffers = NULL;
+
+       return err;
+}
+
+static void ipw2100_tx_initialize(struct ipw2100_priv *priv)
+{
+       int i;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       /*
+        * reinitialize packet info lists
+        */
+       INIT_LIST_HEAD(&priv->fw_pend_list);
+       INIT_STAT(&priv->fw_pend_stat);
+
+       /*
+        * reinitialize lists
+        */
+       INIT_LIST_HEAD(&priv->tx_pend_list);
+       INIT_LIST_HEAD(&priv->tx_free_list);
+       INIT_STAT(&priv->tx_pend_stat);
+       INIT_STAT(&priv->tx_free_stat);
+
+       for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+               /* We simply drop any SKBs that have been queued for
+                * transmit */
+               if (priv->tx_buffers[i].info.d_struct.txb) {
+                       ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.txb);
+                       priv->tx_buffers[i].info.d_struct.txb = NULL;
+               }
+
+               list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list);
+       }
+
+       SET_STAT(&priv->tx_free_stat, i);
+
+       priv->tx_queue.oldest = 0;
+       priv->tx_queue.available = priv->tx_queue.entries;
+       priv->tx_queue.next = 0;
+       INIT_STAT(&priv->txq_stat);
+       SET_STAT(&priv->txq_stat, priv->tx_queue.available);
+
+       bd_queue_initialize(priv, &priv->tx_queue,
+                           IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE,
+                           IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE,
+                           IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+                           IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX);
+
+       IPW_DEBUG_INFO("exit\n");
+
+}
+
+static void ipw2100_tx_free(struct ipw2100_priv *priv)
+{
+       int i;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       bd_queue_free(priv, &priv->tx_queue);
+
+       if (!priv->tx_buffers)
+               return;
+
+       for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+               if (priv->tx_buffers[i].info.d_struct.txb) {
+                       ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.txb);
+                       priv->tx_buffers[i].info.d_struct.txb = NULL;
+               }
+               if (priv->tx_buffers[i].info.d_struct.data)
+                       pci_free_consistent(
+                               priv->pci_dev,
+                               sizeof(struct ipw2100_data_header),
+                               priv->tx_buffers[i].info.d_struct.data,
+                               priv->tx_buffers[i].info.d_struct.data_phys);
+       }
+
+       kfree(priv->tx_buffers);
+       priv->tx_buffers = NULL;
+
+       IPW_DEBUG_INFO("exit\n");
+}
+
+
+
+static int ipw2100_rx_allocate(struct ipw2100_priv *priv)
+{
+       int i, j, err = -EINVAL;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH);
+       if (err) {
+               IPW_DEBUG_INFO("failed bd_queue_allocate\n");
+               return err;
+       }
+
+       err = status_queue_allocate(priv, RX_QUEUE_LENGTH);
+       if (err) {
+               IPW_DEBUG_INFO("failed status_queue_allocate\n");
+               bd_queue_free(priv, &priv->rx_queue);
+               return err;
+       }
+
+       /*
+        * allocate packets
+        */
+       priv->rx_buffers = (struct ipw2100_rx_packet *)
+           kmalloc(RX_QUEUE_LENGTH * sizeof(struct ipw2100_rx_packet),
+                   GFP_KERNEL);
+       if (!priv->rx_buffers) {
+               IPW_DEBUG_INFO("can't allocate rx packet buffer table\n");
+
+               bd_queue_free(priv, &priv->rx_queue);
+
+               status_queue_free(priv);
+
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+               struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+               err = ipw2100_alloc_skb(priv, packet);
+               if (unlikely(err)) {
+                       err = -ENOMEM;
+                       break;
+               }
+
+               /* The BD holds the cache aligned address */
+               priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+               priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH;
+               priv->status_queue.drv[i].status_fields = 0;
+       }
+
+       if (i == RX_QUEUE_LENGTH)
+               return 0;
+
+       for (j = 0; j < i; j++) {
+               pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr,
+                                sizeof(struct ipw2100_rx_packet),
+                                PCI_DMA_FROMDEVICE);
+               dev_kfree_skb(priv->rx_buffers[j].skb);
+       }
+
+       kfree(priv->rx_buffers);
+       priv->rx_buffers = NULL;
+
+       bd_queue_free(priv, &priv->rx_queue);
+
+       status_queue_free(priv);
+
+       return err;
+}
+
+static void ipw2100_rx_initialize(struct ipw2100_priv *priv)
+{
+       IPW_DEBUG_INFO("enter\n");
+
+       priv->rx_queue.oldest = 0;
+       priv->rx_queue.available = priv->rx_queue.entries - 1;
+       priv->rx_queue.next = priv->rx_queue.entries - 1;
+
+       INIT_STAT(&priv->rxq_stat);
+       SET_STAT(&priv->rxq_stat, priv->rx_queue.available);
+
+       bd_queue_initialize(priv, &priv->rx_queue,
+                           IPW_MEM_HOST_SHARED_RX_BD_BASE,
+                           IPW_MEM_HOST_SHARED_RX_BD_SIZE,
+                           IPW_MEM_HOST_SHARED_RX_READ_INDEX,
+                           IPW_MEM_HOST_SHARED_RX_WRITE_INDEX);
+
+       /* set up the status queue */
+       write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE,
+                      priv->status_queue.nic);
+
+       IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_rx_free(struct ipw2100_priv *priv)
+{
+       int i;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       bd_queue_free(priv, &priv->rx_queue);
+       status_queue_free(priv);
+
+       if (!priv->rx_buffers)
+               return;
+
+       for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+               if (priv->rx_buffers[i].rxp) {
+                       pci_unmap_single(priv->pci_dev,
+                                        priv->rx_buffers[i].dma_addr,
+                                        sizeof(struct ipw2100_rx),
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb(priv->rx_buffers[i].skb);
+               }
+       }
+
+       kfree(priv->rx_buffers);
+       priv->rx_buffers = NULL;
+
+       IPW_DEBUG_INFO("exit\n");
+}
+
+static int ipw2100_read_mac_address(struct ipw2100_priv *priv)
+{
+       u32 length = ETH_ALEN;
+       u8 mac[ETH_ALEN];
+
+       int err;
+
+       err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC,
+                                 mac, &length);
+       if (err) {
+               IPW_DEBUG_INFO("MAC address read failed\n");
+               return -EIO;
+       }
+       IPW_DEBUG_INFO("card MAC is %02X:%02X:%02X:%02X:%02X:%02X\n",
+              mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+       memcpy(priv->net_dev->dev_addr, mac, ETH_ALEN);
+
+       return 0;
+}
+
+/********************************************************************
+ *
+ * Firmware Commands
+ *
+ ********************************************************************/
+
+static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = ADAPTER_ADDRESS,
+               .host_command_sequence = 0,
+               .host_command_length = ETH_ALEN
+       };
+       int err;
+
+       IPW_DEBUG_HC("SET_MAC_ADDRESS\n");
+
+       IPW_DEBUG_INFO("enter\n");
+
+       if (priv->config & CFG_CUSTOM_MAC) {
+               memcpy(cmd.host_command_parameters, priv->mac_addr,
+                      ETH_ALEN);
+               memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+       } else
+               memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr,
+                      ETH_ALEN);
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       IPW_DEBUG_INFO("exit\n");
+       return err;
+}
+
+static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type,
+                                int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = PORT_TYPE,
+               .host_command_sequence = 0,
+               .host_command_length = sizeof(u32)
+       };
+       int err;
+
+       switch (port_type) {
+       case IW_MODE_INFRA:
+               cmd.host_command_parameters[0] = IPW_BSS;
+               break;
+       case IW_MODE_ADHOC:
+               cmd.host_command_parameters[0] = IPW_IBSS;
+               break;
+       }
+
+       IPW_DEBUG_HC("PORT_TYPE: %s\n",
+                    port_type == IPW_IBSS ? "Ad-Hoc" : "Managed");
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err) {
+                       printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+                              priv->net_dev->name, err);
+                       return err;
+               }
+       }
+
+       /* send cmd to firmware */
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode)
+               ipw2100_enable_adapter(priv);
+
+       return err;
+}
+
+
+static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel,
+                              int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = CHANNEL,
+               .host_command_sequence = 0,
+               .host_command_length = sizeof(u32)
+       };
+       int err;
+
+       cmd.host_command_parameters[0] = channel;
+
+       IPW_DEBUG_HC("CHANNEL: %d\n", channel);
+
+       /* If BSS then we don't support channel selection */
+       if (priv->ieee->iw_mode == IW_MODE_INFRA)
+               return 0;
+
+       if ((channel != 0) &&
+           ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL)))
+               return -EINVAL;
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err) {
+               IPW_DEBUG_INFO("Failed to set channel to %d",
+                              channel);
+               return err;
+       }
+
+       if (channel)
+               priv->config |= CFG_STATIC_CHANNEL;
+       else
+               priv->config &= ~CFG_STATIC_CHANNEL;
+
+       priv->channel = channel;
+
+       if (!batch_mode) {
+               err = ipw2100_enable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = SYSTEM_CONFIG,
+               .host_command_sequence = 0,
+               .host_command_length = 12,
+       };
+       u32 ibss_mask, len = sizeof(u32);
+       int err;
+
+       /* Set system configuration */
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+               cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START;
+
+       cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK |
+               IPW_CFG_BSS_MASK |
+               IPW_CFG_802_1x_ENABLE;
+
+       if (!(priv->config & CFG_LONG_PREAMBLE))
+               cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO;
+
+       err = ipw2100_get_ordinal(priv,
+                                 IPW_ORD_EEPROM_IBSS_11B_CHANNELS,
+                                 &ibss_mask,  &len);
+       if (err)
+               ibss_mask = IPW_IBSS_11B_DEFAULT_MASK;
+
+       cmd.host_command_parameters[1] = REG_CHANNEL_MASK;
+       cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask;
+
+       /* 11b only */
+       /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A;*/
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err)
+               return err;
+
+/* If IPv6 is configured in the kernel then we don't want to filter out all
+ * of the multicast packets as IPv6 needs some. */
+#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
+       cmd.host_command = ADD_MULTICAST;
+       cmd.host_command_sequence = 0;
+       cmd.host_command_length = 0;
+
+       ipw2100_hw_send_command(priv, &cmd);
+#endif
+       if (!batch_mode) {
+               err = ipw2100_enable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate,
+                               int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = BASIC_TX_RATES,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       cmd.host_command_parameters[0] = rate & TX_RATE_MASK;
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       /* Set BASIC TX Rate first */
+       ipw2100_hw_send_command(priv, &cmd);
+
+       /* Set TX Rate */
+       cmd.host_command = TX_RATES;
+       ipw2100_hw_send_command(priv, &cmd);
+
+       /* Set MSDU TX Rate */
+       cmd.host_command = MSDU_TX_RATES;
+       ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode) {
+               err = ipw2100_enable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       priv->tx_rates = rate;
+
+       return 0;
+}
+
+static int ipw2100_set_power_mode(struct ipw2100_priv *priv,
+                                 int power_level)
+{
+       struct host_command cmd = {
+               .host_command = POWER_MODE,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       cmd.host_command_parameters[0] = power_level;
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err)
+               return err;
+
+       if (power_level == IPW_POWER_MODE_CAM)
+               priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+       else
+               priv->power_mode = IPW_POWER_ENABLED | power_level;
+
+#ifdef CONFIG_IPW2100_TX_POWER
+       if (priv->port_type == IBSS &&
+           priv->adhoc_power != DFTL_IBSS_TX_POWER) {
+               /* Set beacon interval */
+               cmd.host_command = TX_POWER_INDEX;
+               cmd.host_command_parameters[0] = (u32)priv->adhoc_power;
+
+               err = ipw2100_hw_send_command(priv, &cmd);
+               if (err)
+                       return err;
+       }
+#endif
+
+       return 0;
+}
+
+
+static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold)
+{
+       struct host_command cmd = {
+               .host_command = RTS_THRESHOLD,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       if (threshold & RTS_DISABLED)
+               cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD;
+       else
+               cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED;
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err)
+               return err;
+
+       priv->rts_threshold = threshold;
+
+       return 0;
+}
+
+#if 0
+int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv,
+                                       u32 threshold, int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = FRAG_THRESHOLD,
+               .host_command_sequence = 0,
+               .host_command_length = 4,
+               .host_command_parameters[0] = 0,
+       };
+       int err;
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       if (threshold == 0)
+               threshold = DEFAULT_FRAG_THRESHOLD;
+       else {
+               threshold = max(threshold, MIN_FRAG_THRESHOLD);
+               threshold = min(threshold, MAX_FRAG_THRESHOLD);
+       }
+
+       cmd.host_command_parameters[0] = threshold;
+
+       IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold);
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode)
+               ipw2100_enable_adapter(priv);
+
+       if (!err)
+               priv->frag_threshold = threshold;
+
+       return err;
+}
+#endif
+
+static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry)
+{
+       struct host_command cmd = {
+               .host_command = SHORT_RETRY_LIMIT,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       cmd.host_command_parameters[0] = retry;
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err)
+               return err;
+
+       priv->short_retry_limit = retry;
+
+       return 0;
+}
+
+static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry)
+{
+       struct host_command cmd = {
+               .host_command = LONG_RETRY_LIMIT,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       cmd.host_command_parameters[0] = retry;
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+       if (err)
+               return err;
+
+       priv->long_retry_limit = retry;
+
+       return 0;
+}
+
+
+static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 *bssid,
+                                      int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = MANDATORY_BSSID,
+               .host_command_sequence = 0,
+               .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN
+       };
+       int err;
+
+#ifdef CONFIG_IPW_DEBUG
+       if (bssid != NULL)
+               IPW_DEBUG_HC(
+                       "MANDATORY_BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n",
+                       bssid[0], bssid[1], bssid[2], bssid[3], bssid[4],
+                       bssid[5]);
+       else
+               IPW_DEBUG_HC("MANDATORY_BSSID: <clear>\n");
+#endif
+       /* if BSSID is empty then we disable mandatory bssid mode */
+       if (bssid != NULL)
+               memcpy((u8 *)cmd.host_command_parameters, bssid, ETH_ALEN);
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode)
+               ipw2100_enable_adapter(priv);
+
+       return err;
+}
+
+#ifdef CONFIG_IEEE80211_WPA
+static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv)
+{
+       struct host_command cmd = {
+               .host_command = DISASSOCIATION_BSSID,
+               .host_command_sequence = 0,
+               .host_command_length = ETH_ALEN
+       };
+       int err;
+       int len;
+
+       IPW_DEBUG_HC("DISASSOCIATION_BSSID\n");
+
+       len = ETH_ALEN;
+       /* The Firmware currently ignores the BSSID and just disassociates from
+        * the currently associated AP -- but in the off chance that a future
+        * firmware does use the BSSID provided here, we go ahead and try and
+        * set it to the currently associated AP's BSSID */
+       memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN);
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       return err;
+}
+#endif
+
+/*
+ * Pseudo code for setting up wpa_frame:
+ */
+#if 0
+void x(struct ieee80211_assoc_frame *wpa_assoc)
+{
+       struct ipw2100_wpa_assoc_frame frame;
+       frame->fixed_ie_mask = IPW_WPA_CAPABILTIES |
+               IPW_WPA_LISTENINTERVAL |
+               IPW_WPA_AP_ADDRESS;
+       frame->capab_info = wpa_assoc->capab_info;
+       frame->lisen_interval = wpa_assoc->listent_interval;
+       memcpy(frame->current_ap, wpa_assoc->current_ap, ETH_ALEN);
+
+       /* UNKNOWN -- I'm not postivive about this part; don't have any WPA
+        * setup here to test it with.
+        *
+        * Walk the IEs in the wpa_assoc and figure out the total size of all
+        * that data.  Stick that into frame->var_ie_len.  Then memcpy() all of
+        * the IEs from wpa_frame into frame.
+        */
+       frame->var_ie_len = calculate_ie_len(wpa_assoc);
+       memcpy(frame->var_ie,  wpa_assoc->variable, frame->var_ie_len);
+
+       ipw2100_set_wpa_ie(priv, &frame, 0);
+}
+#endif
+
+
+
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *,
+                             struct ipw2100_wpa_assoc_frame *, int)
+__attribute__ ((unused));
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv,
+                             struct ipw2100_wpa_assoc_frame *wpa_frame,
+                             int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = SET_WPA_IE,
+               .host_command_sequence = 0,
+               .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame),
+       };
+       int err;
+
+       IPW_DEBUG_HC("SET_WPA_IE\n");
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       memcpy(cmd.host_command_parameters, wpa_frame,
+              sizeof(struct ipw2100_wpa_assoc_frame));
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode) {
+               if (ipw2100_enable_adapter(priv))
+                       err = -EIO;
+       }
+
+       return err;
+}
+
+struct security_info_params {
+       u32 allowed_ciphers;
+       u16 version;
+       u8 auth_mode;
+       u8 replay_counters_number;
+       u8 unicast_using_group;
+} __attribute__ ((packed));
+
+static int ipw2100_set_security_information(struct ipw2100_priv *priv,
+                                           int auth_mode,
+                                           int security_level,
+                                           int unicast_using_group,
+                                           int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = SET_SECURITY_INFORMATION,
+               .host_command_sequence = 0,
+               .host_command_length = sizeof(struct security_info_params)
+       };
+       struct security_info_params *security =
+               (struct security_info_params *)&cmd.host_command_parameters;
+       int err;
+       memset(security, 0, sizeof(*security));
+
+       /* If shared key AP authentication is turned on, then we need to
+        * configure the firmware to try and use it.
+        *
+        * Actual data encryption/decryption is handled by the host. */
+       security->auth_mode = auth_mode;
+       security->unicast_using_group = unicast_using_group;
+
+       switch (security_level) {
+       default:
+       case SEC_LEVEL_0:
+               security->allowed_ciphers = IPW_NONE_CIPHER;
+               break;
+       case SEC_LEVEL_1:
+               security->allowed_ciphers = IPW_WEP40_CIPHER |
+                       IPW_WEP104_CIPHER;
+               break;
+       case SEC_LEVEL_2:
+               security->allowed_ciphers = IPW_WEP40_CIPHER |
+                       IPW_WEP104_CIPHER | IPW_TKIP_CIPHER;
+               break;
+       case SEC_LEVEL_2_CKIP:
+               security->allowed_ciphers = IPW_WEP40_CIPHER |
+                       IPW_WEP104_CIPHER | IPW_CKIP_CIPHER;
+               break;
+       case SEC_LEVEL_3:
+               security->allowed_ciphers = IPW_WEP40_CIPHER |
+                       IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER;
+               break;
+       }
+
+       IPW_DEBUG_HC(
+               "SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n",
+               security->auth_mode, security->allowed_ciphers, security_level);
+
+       security->replay_counters_number = 0;
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode)
+               ipw2100_enable_adapter(priv);
+
+       return err;
+}
+
+static int ipw2100_set_tx_power(struct ipw2100_priv *priv,
+                               u32 tx_power)
+{
+       struct host_command cmd = {
+               .host_command = TX_POWER_INDEX,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err = 0;
+
+       cmd.host_command_parameters[0] = tx_power;
+
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+               err = ipw2100_hw_send_command(priv, &cmd);
+       if (!err)
+               priv->tx_power = tx_power;
+
+       return 0;
+}
+
+static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv,
+                                           u32 interval, int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = BEACON_INTERVAL,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       cmd.host_command_parameters[0] = interval;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+               if (!batch_mode) {
+                       err = ipw2100_disable_adapter(priv);
+                       if (err)
+                               return err;
+               }
+
+               ipw2100_hw_send_command(priv, &cmd);
+
+               if (!batch_mode) {
+                       err = ipw2100_enable_adapter(priv);
+                       if (err)
+                               return err;
+               }
+       }
+
+       IPW_DEBUG_INFO("exit\n");
+
+       return 0;
+}
+
+
+void ipw2100_queues_initialize(struct ipw2100_priv *priv)
+{
+       ipw2100_tx_initialize(priv);
+       ipw2100_rx_initialize(priv);
+       ipw2100_msg_initialize(priv);
+}
+
+void ipw2100_queues_free(struct ipw2100_priv *priv)
+{
+       ipw2100_tx_free(priv);
+       ipw2100_rx_free(priv);
+       ipw2100_msg_free(priv);
+}
+
+int ipw2100_queues_allocate(struct ipw2100_priv *priv)
+{
+       if (ipw2100_tx_allocate(priv) ||
+           ipw2100_rx_allocate(priv) ||
+           ipw2100_msg_allocate(priv))
+               goto fail;
+
+       return 0;
+
+ fail:
+       ipw2100_tx_free(priv);
+       ipw2100_rx_free(priv);
+       ipw2100_msg_free(priv);
+       return -ENOMEM;
+}
+
+#define IPW_PRIVACY_CAPABLE 0x0008
+
+static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags,
+                                int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = WEP_FLAGS,
+               .host_command_sequence = 0,
+               .host_command_length = 4
+       };
+       int err;
+
+       cmd.host_command_parameters[0] = flags;
+
+       IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags);
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err) {
+                       printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+                              priv->net_dev->name, err);
+                       return err;
+               }
+       }
+
+       /* send cmd to firmware */
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode)
+               ipw2100_enable_adapter(priv);
+
+       return err;
+}
+
+struct ipw2100_wep_key {
+       u8 idx;
+       u8 len;
+       u8 key[13];
+};
+
+/* Macros to ease up priting WEP keys */
+#define WEP_FMT_64  "%02X%02X%02X%02X-%02X"
+#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X"
+#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4]
+#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10]
+
+
+/**
+ * Set a the wep key
+ *
+ * @priv: struct to work on
+ * @idx: index of the key we want to set
+ * @key: ptr to the key data to set
+ * @len: length of the buffer at @key
+ * @batch_mode: FIXME perform the operation in batch mode, not
+ *              disabling the device.
+ *
+ * @returns 0 if OK, < 0 errno code on error.
+ *
+ * Fill out a command structure with the new wep key, length an
+ * index and send it down the wire.
+ */
+static int ipw2100_set_key(struct ipw2100_priv *priv,
+                          int idx, char *key, int len, int batch_mode)
+{
+       int keylen = len ? (len <= 5 ? 5 : 13) : 0;
+       struct host_command cmd = {
+               .host_command = WEP_KEY_INFO,
+               .host_command_sequence = 0,
+               .host_command_length = sizeof(struct ipw2100_wep_key),
+       };
+       struct ipw2100_wep_key *wep_key = (void*)cmd.host_command_parameters;
+       int err;
+
+       IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n",
+                                idx, keylen, len);
+
+       /* NOTE: We don't check cached values in case the firmware was reset
+        * or some other problem is occuring.  If the user is setting the key,
+        * then we push the change */
+
+       wep_key->idx = idx;
+       wep_key->len = keylen;
+
+       if (keylen) {
+               memcpy(wep_key->key, key, len);
+               memset(wep_key->key + len, 0, keylen - len);
+       }
+
+       /* Will be optimized out on debug not being configured in */
+       if (keylen == 0)
+               IPW_DEBUG_WEP("%s: Clearing key %d\n",
+                                 priv->net_dev->name, wep_key->idx);
+       else if (keylen == 5)
+               IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n",
+                                 priv->net_dev->name, wep_key->idx, wep_key->len,
+                                 WEP_STR_64(wep_key->key));
+       else
+               IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128
+                                 "\n",
+                                 priv->net_dev->name, wep_key->idx, wep_key->len,
+                                 WEP_STR_128(wep_key->key));
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */
+               if (err) {
+                       printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+                              priv->net_dev->name, err);
+                       return err;
+               }
+       }
+
+       /* send cmd to firmware */
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode) {
+               int err2 = ipw2100_enable_adapter(priv);
+               if (err == 0)
+                       err = err2;
+       }
+       return err;
+}
+
+static int ipw2100_set_key_index(struct ipw2100_priv *priv,
+                                int idx, int batch_mode)
+{
+       struct host_command cmd = {
+               .host_command = WEP_KEY_INDEX,
+               .host_command_sequence = 0,
+               .host_command_length = 4,
+               .host_command_parameters = { idx },
+       };
+       int err;
+
+       IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx);
+
+       if (idx < 0 || idx > 3)
+               return -EINVAL;
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err) {
+                       printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+                              priv->net_dev->name, err);
+                       return err;
+               }
+       }
+
+       /* send cmd to firmware */
+       err = ipw2100_hw_send_command(priv, &cmd);
+
+       if (!batch_mode)
+               ipw2100_enable_adapter(priv);
+
+       return err;
+}
+
+
+static int ipw2100_configure_security(struct ipw2100_priv *priv,
+                                     int batch_mode)
+{
+       int i, err, auth_mode, sec_level, use_group;
+
+       if (!(priv->status & STATUS_RUNNING))
+               return 0;
+
+       if (!batch_mode) {
+               err = ipw2100_disable_adapter(priv);
+               if (err)
+                       return err;
+       }
+
+       if (!priv->sec.enabled) {
+               err = ipw2100_set_security_information(
+                       priv, IPW_AUTH_OPEN, SEC_LEVEL_0, 0, 1);
+       } else {
+               auth_mode = IPW_AUTH_OPEN;
+               if ((priv->sec.flags & SEC_AUTH_MODE) &&
+                   (priv->sec.auth_mode == WLAN_AUTH_SHARED_KEY))
+                       auth_mode = IPW_AUTH_SHARED;
+
+               sec_level = SEC_LEVEL_0;
+               if (priv->sec.flags & SEC_LEVEL)
+                       sec_level = priv->sec.level;
+
+               use_group = 0;
+               if (priv->sec.flags & SEC_UNICAST_GROUP)
+                       use_group = priv->sec.unicast_uses_group;
+
+               err = ipw2100_set_security_information(
+                           priv, auth_mode, sec_level, use_group, 1);
+       }
+
+       if (err)
+               goto exit;
+
+       if (priv->sec.enabled) {
+               for (i = 0; i < 4; i++) {
+                       if (!(priv->sec.flags & (1 << i))) {
+                               memset(priv->sec.keys[i], 0, WEP_KEY_LEN);
+                               priv->sec.key_sizes[i] = 0;
+                       } else {
+                               err = ipw2100_set_key(priv, i,
+                                                     priv->sec.keys[i],
+                                                     priv->sec.key_sizes[i],
+                                                     1);
+                               if (err)
+                                       goto exit;
+                       }
+               }
+
+               ipw2100_set_key_index(priv, priv->ieee->tx_keyidx, 1);
+       }
+
+       /* Always enable privacy so the Host can filter WEP packets if
+        * encrypted data is sent up */
+       err = ipw2100_set_wep_flags(
+               priv, priv->sec.enabled ? IPW_PRIVACY_CAPABLE : 0, 1);
+       if (err)
+               goto exit;
+
+       priv->status &= ~STATUS_SECURITY_UPDATED;
+
+ exit:
+       if (!batch_mode)
+               ipw2100_enable_adapter(priv);
+
+       return err;
+}
+
+static void ipw2100_security_work(struct ipw2100_priv *priv)
+{
+       /* If we happen to have reconnected before we get a chance to
+        * process this, then update the security settings--which causes
+        * a disassociation to occur */
+       if (!(priv->status & STATUS_ASSOCIATED) &&
+           priv->status & STATUS_SECURITY_UPDATED)
+               ipw2100_configure_security(priv, 0);
+}
+
+static void shim__set_security(struct net_device *dev,
+                              struct ieee80211_security *sec)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int i, force_update = 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED))
+               goto done;
+
+       for (i = 0; i < 4; i++) {
+               if (sec->flags & (1 << i)) {
+                       priv->sec.key_sizes[i] = sec->key_sizes[i];
+                       if (sec->key_sizes[i] == 0)
+                               priv->sec.flags &= ~(1 << i);
+                       else
+                               memcpy(priv->sec.keys[i], sec->keys[i],
+                                      sec->key_sizes[i]);
+                       priv->sec.flags |= (1 << i);
+                       priv->status |= STATUS_SECURITY_UPDATED;
+               }
+       }
+
+       if ((sec->flags & SEC_ACTIVE_KEY) &&
+           priv->sec.active_key != sec->active_key) {
+               if (sec->active_key <= 3) {
+                       priv->sec.active_key = sec->active_key;
+                       priv->sec.flags |= SEC_ACTIVE_KEY;
+               } else
+                       priv->sec.flags &= ~SEC_ACTIVE_KEY;
+
+               priv->status |= STATUS_SECURITY_UPDATED;
+       }
+
+       if ((sec->flags & SEC_AUTH_MODE) &&
+           (priv->sec.auth_mode != sec->auth_mode)) {
+               priv->sec.auth_mode = sec->auth_mode;
+               priv->sec.flags |= SEC_AUTH_MODE;
+               priv->status |= STATUS_SECURITY_UPDATED;
+       }
+
+       if (sec->flags & SEC_ENABLED &&
+           priv->sec.enabled != sec->enabled) {
+               priv->sec.flags |= SEC_ENABLED;
+               priv->sec.enabled = sec->enabled;
+               priv->status |= STATUS_SECURITY_UPDATED;
+               force_update = 1;
+       }
+
+       if (sec->flags & SEC_LEVEL &&
+           priv->sec.level != sec->level) {
+               priv->sec.level = sec->level;
+               priv->sec.flags |= SEC_LEVEL;
+               priv->status |= STATUS_SECURITY_UPDATED;
+       }
+
+       IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n",
+                         priv->sec.flags & (1<<8) ? '1' : '0',
+                         priv->sec.flags & (1<<7) ? '1' : '0',
+                         priv->sec.flags & (1<<6) ? '1' : '0',
+                         priv->sec.flags & (1<<5) ? '1' : '0',
+                         priv->sec.flags & (1<<4) ? '1' : '0',
+                         priv->sec.flags & (1<<3) ? '1' : '0',
+                         priv->sec.flags & (1<<2) ? '1' : '0',
+                         priv->sec.flags & (1<<1) ? '1' : '0',
+                         priv->sec.flags & (1<<0) ? '1' : '0');
+
+/* As a temporary work around to enable WPA until we figure out why
+ * wpa_supplicant toggles the security capability of the driver, which
+ * forces a disassocation with force_update...
+ *
+ *     if (force_update || !(priv->status & STATUS_ASSOCIATED))*/
+       if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
+               ipw2100_configure_security(priv, 0);
+done:
+       up(&priv->action_sem);
+}
+
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv)
+{
+       int err;
+       int batch_mode = 1;
+       u8 *bssid;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       err = ipw2100_disable_adapter(priv);
+       if (err)
+               return err;
+#ifdef CONFIG_IPW2100_MONITOR
+       if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+               err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+               if (err)
+                       return err;
+
+               IPW_DEBUG_INFO("exit\n");
+
+               return 0;
+       }
+#endif /* CONFIG_IPW2100_MONITOR */
+
+       err = ipw2100_read_mac_address(priv);
+       if (err)
+               return -EIO;
+
+       err = ipw2100_set_mac_address(priv, batch_mode);
+       if (err)
+               return err;
+
+       err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode);
+       if (err)
+               return err;
+
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+               err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+               if (err)
+                       return err;
+       }
+
+       err  = ipw2100_system_config(priv, batch_mode);
+       if (err)
+               return err;
+
+       err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode);
+       if (err)
+               return err;
+
+       /* Default to power mode OFF */
+       err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+       if (err)
+               return err;
+
+       err = ipw2100_set_rts_threshold(priv, priv->rts_threshold);
+       if (err)
+               return err;
+
+       if (priv->config & CFG_STATIC_BSSID)
+               bssid = priv->bssid;
+       else
+               bssid = NULL;
+       err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode);
+       if (err)
+               return err;
+
+       if (priv->config & CFG_STATIC_ESSID)
+               err = ipw2100_set_essid(priv, priv->essid, priv->essid_len,
+                                       batch_mode);
+       else
+               err = ipw2100_set_essid(priv, NULL, 0, batch_mode);
+       if (err)
+               return err;
+
+       err = ipw2100_configure_security(priv, batch_mode);
+       if (err)
+               return err;
+
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+               err = ipw2100_set_ibss_beacon_interval(
+                       priv, priv->beacon_interval, batch_mode);
+               if (err)
+                       return err;
+
+               err = ipw2100_set_tx_power(priv, priv->tx_power);
+               if (err)
+                       return err;
+       }
+
+       /*
+         err = ipw2100_set_fragmentation_threshold(
+         priv, priv->frag_threshold, batch_mode);
+         if (err)
+         return err;
+       */
+
+       IPW_DEBUG_INFO("exit\n");
+
+       return 0;
+}
+
+
+/*************************************************************************
+ *
+ * EXTERNALLY CALLED METHODS
+ *
+ *************************************************************************/
+
+/* This method is called by the network layer -- not to be confused with
+ * ipw2100_set_mac_address() declared above called by this driver (and this
+ * method as well) to talk to the firmware */
+static int ipw2100_set_address(struct net_device *dev, void *p)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       struct sockaddr *addr = p;
+       int err = 0;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       down(&priv->action_sem);
+
+       priv->config |= CFG_CUSTOM_MAC;
+       memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
+
+       err = ipw2100_set_mac_address(priv, 0);
+       if (err)
+               goto done;
+
+       priv->reset_backoff = 0;
+       up(&priv->action_sem);
+       ipw2100_reset_adapter(priv);
+       return 0;
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_open(struct net_device *dev)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       unsigned long flags;
+       IPW_DEBUG_INFO("dev->open\n");
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+       if (priv->status & STATUS_ASSOCIATED) {
+               netif_carrier_on(dev);
+               netif_start_queue(dev);
+       }
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       return 0;
+}
+
+static int ipw2100_close(struct net_device *dev)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       unsigned long flags;
+       struct list_head *element;
+       struct ipw2100_tx_packet *packet;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+
+       if (priv->status & STATUS_ASSOCIATED)
+               netif_carrier_off(dev);
+       netif_stop_queue(dev);
+
+       /* Flush the TX queue ... */
+       while (!list_empty(&priv->tx_pend_list)) {
+               element = priv->tx_pend_list.next;
+                packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+               list_del(element);
+               DEC_STAT(&priv->tx_pend_stat);
+
+               ieee80211_txb_free(packet->info.d_struct.txb);
+               packet->info.d_struct.txb = NULL;
+
+               list_add_tail(element, &priv->tx_free_list);
+               INC_STAT(&priv->tx_free_stat);
+       }
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+
+       IPW_DEBUG_INFO("exit\n");
+
+       return 0;
+}
+
+
+
+/*
+ * TODO:  Fix this function... its just wrong
+ */
+static void ipw2100_tx_timeout(struct net_device *dev)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       priv->ieee->stats.tx_errors++;
+
+#ifdef CONFIG_IPW2100_MONITOR
+       if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+               return;
+#endif
+
+       IPW_DEBUG_INFO("%s: TX timed out.  Scheduling firmware restart.\n",
+                      dev->name);
+       schedule_reset(priv);
+}
+
+
+/*
+ * TODO: reimplement it so that it reads statistics
+ *       from the adapter using ordinal tables
+ *       instead of/in addition to collecting them
+ *       in the driver
+ */
+static struct net_device_stats *ipw2100_stats(struct net_device *dev)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       return &priv->ieee->stats;
+}
+
+/* Support for wpa_supplicant. Will be replaced with WEXT once
+ * they get WPA support. */
+#ifdef CONFIG_IEEE80211_WPA
+
+/* following definitions must match definitions in driver_ipw2100.c */
+
+#define IPW2100_IOCTL_WPA_SUPPLICANT           SIOCIWFIRSTPRIV+30
+
+#define IPW2100_CMD_SET_WPA_PARAM              1
+#define        IPW2100_CMD_SET_WPA_IE                  2
+#define IPW2100_CMD_SET_ENCRYPTION             3
+#define IPW2100_CMD_MLME                       4
+
+#define IPW2100_PARAM_WPA_ENABLED              1
+#define IPW2100_PARAM_TKIP_COUNTERMEASURES     2
+#define IPW2100_PARAM_DROP_UNENCRYPTED         3
+#define IPW2100_PARAM_PRIVACY_INVOKED          4
+#define IPW2100_PARAM_AUTH_ALGS                        5
+#define IPW2100_PARAM_IEEE_802_1X              6
+
+#define IPW2100_MLME_STA_DEAUTH                        1
+#define IPW2100_MLME_STA_DISASSOC              2
+
+#define IPW2100_CRYPT_ERR_UNKNOWN_ALG          2
+#define IPW2100_CRYPT_ERR_UNKNOWN_ADDR         3
+#define IPW2100_CRYPT_ERR_CRYPT_INIT_FAILED    4
+#define IPW2100_CRYPT_ERR_KEY_SET_FAILED       5
+#define IPW2100_CRYPT_ERR_TX_KEY_SET_FAILED    6
+#define IPW2100_CRYPT_ERR_CARD_CONF_FAILED     7
+
+#define        IPW2100_CRYPT_ALG_NAME_LEN              16
+
+struct ipw2100_param {
+       u32 cmd;
+       u8 sta_addr[ETH_ALEN];
+        union {
+               struct {
+                       u8 name;
+                       u32 value;
+               } wpa_param;
+               struct {
+                       u32 len;
+                       u8 *data;
+               } wpa_ie;
+               struct{
+                       int command;
+                       int reason_code;
+               } mlme;
+               struct {
+                       u8 alg[IPW2100_CRYPT_ALG_NAME_LEN];
+                       u8 set_tx;
+                       u32 err;
+                       u8 idx;
+                       u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+                       u16 key_len;
+                       u8 key[0];
+               } crypt;
+
+       } u;
+};
+
+/* end of driver_ipw2100.c code */
+
+static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value){
+
+       struct ieee80211_device *ieee = priv->ieee;
+       struct ieee80211_security sec = {
+               .flags = SEC_LEVEL | SEC_ENABLED,
+       };
+       int ret = 0;
+
+       ieee->wpa_enabled = value;
+
+       if (value){
+               sec.level = SEC_LEVEL_3;
+               sec.enabled = 1;
+       } else {
+               sec.level = SEC_LEVEL_0;
+               sec.enabled = 0;
+       }
+
+       if (ieee->set_security)
+               ieee->set_security(ieee->dev, &sec);
+       else
+               ret = -EOPNOTSUPP;
+
+       return ret;
+}
+
+#define AUTH_ALG_OPEN_SYSTEM                   0x1
+#define AUTH_ALG_SHARED_KEY                    0x2
+
+static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value){
+
+       struct ieee80211_device *ieee = priv->ieee;
+       struct ieee80211_security sec = {
+               .flags = SEC_AUTH_MODE,
+       };
+       int ret = 0;
+
+       if (value & AUTH_ALG_SHARED_KEY){
+               sec.auth_mode = WLAN_AUTH_SHARED_KEY;
+               ieee->open_wep = 0;
+       } else {
+               sec.auth_mode = WLAN_AUTH_OPEN;
+               ieee->open_wep = 1;
+       }
+
+       if (ieee->set_security)
+               ieee->set_security(ieee->dev, &sec);
+       else
+               ret = -EOPNOTSUPP;
+
+       return ret;
+}
+
+
+static int ipw2100_wpa_set_param(struct net_device *dev, u8 name, u32 value){
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int ret=0;
+
+       switch(name){
+               case IPW2100_PARAM_WPA_ENABLED:
+                       ret = ipw2100_wpa_enable(priv, value);
+                       break;
+
+               case IPW2100_PARAM_TKIP_COUNTERMEASURES:
+                       priv->ieee->tkip_countermeasures=value;
+                       break;
+
+               case IPW2100_PARAM_DROP_UNENCRYPTED:
+                       priv->ieee->drop_unencrypted=value;
+                       break;
+
+               case IPW2100_PARAM_PRIVACY_INVOKED:
+                       priv->ieee->privacy_invoked=value;
+                       break;
+
+               case IPW2100_PARAM_AUTH_ALGS:
+                       ret = ipw2100_wpa_set_auth_algs(priv, value);
+                       break;
+
+               case IPW2100_PARAM_IEEE_802_1X:
+                       priv->ieee->ieee802_1x=value;
+                       break;
+
+               default:
+                       printk(KERN_ERR DRV_NAME ": %s: Unknown WPA param: %d\n",
+                                           dev->name, name);
+                       ret = -EOPNOTSUPP;
+       }
+
+       return ret;
+}
+
+static int ipw2100_wpa_mlme(struct net_device *dev, int command, int reason){
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int ret=0;
+
+       switch(command){
+               case IPW2100_MLME_STA_DEAUTH:
+                       // silently ignore
+                       break;
+
+               case IPW2100_MLME_STA_DISASSOC:
+                       ipw2100_disassociate_bssid(priv);
+                       break;
+
+               default:
+                       printk(KERN_ERR DRV_NAME ": %s: Unknown MLME request: %d\n",
+                                           dev->name, command);
+                       ret = -EOPNOTSUPP;
+       }
+
+       return ret;
+}
+
+
+void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
+                            char *wpa_ie, int wpa_ie_len){
+
+       struct ipw2100_wpa_assoc_frame frame;
+
+       frame.fixed_ie_mask = 0;
+
+       /* copy WPA IE */
+       memcpy(frame.var_ie, wpa_ie, wpa_ie_len);
+       frame.var_ie_len = wpa_ie_len;
+
+       /* make sure WPA is enabled */
+       ipw2100_wpa_enable(priv, 1);
+       ipw2100_set_wpa_ie(priv, &frame, 0);
+}
+
+
+static int ipw2100_wpa_set_wpa_ie(struct net_device *dev,
+                               struct ipw2100_param *param, int plen){
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       struct ieee80211_device *ieee = priv->ieee;
+       u8 *buf;
+
+       if (! ieee->wpa_enabled)
+           return -EOPNOTSUPP;
+
+       if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
+          (param->u.wpa_ie.len &&
+               param->u.wpa_ie.data==NULL))
+               return -EINVAL;
+
+       if (param->u.wpa_ie.len){
+               buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL);
+               if (buf == NULL)
+                       return -ENOMEM;
+
+               memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len);
+
+               kfree(ieee->wpa_ie);
+               ieee->wpa_ie = buf;
+               ieee->wpa_ie_len = param->u.wpa_ie.len;
+
+       } else {
+               kfree(ieee->wpa_ie);
+               ieee->wpa_ie = NULL;
+               ieee->wpa_ie_len = 0;
+       }
+
+       ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
+
+       return 0;
+}
+
+/* implementation borrowed from hostap driver */
+
+static int ipw2100_wpa_set_encryption(struct net_device *dev,
+                               struct ipw2100_param *param, int param_len){
+
+       int ret = 0;
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       struct ieee80211_device *ieee = priv->ieee;
+       struct ieee80211_crypto_ops *ops;
+       struct ieee80211_crypt_data **crypt;
+
+       struct ieee80211_security sec = {
+               .flags = 0,
+       };
+
+       param->u.crypt.err = 0;
+       param->u.crypt.alg[IPW2100_CRYPT_ALG_NAME_LEN - 1] = '\0';
+
+       if (param_len !=
+           (int) ((char *) param->u.crypt.key - (char *) param) +
+           param->u.crypt.key_len){
+               IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len, param->u.crypt.key_len);
+               return -EINVAL;
+       }
+       if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+           param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+           param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+               if (param->u.crypt.idx >= WEP_KEYS)
+                       return -EINVAL;
+               crypt = &ieee->crypt[param->u.crypt.idx];
+       } else {
+               return -EINVAL;
+       }
+
+       if (strcmp(param->u.crypt.alg, "none") == 0) {
+               if (crypt){
+                       sec.enabled = 0;
+                       sec.level = SEC_LEVEL_0;
+                       sec.flags |= SEC_ENABLED | SEC_LEVEL;
+                       ieee80211_crypt_delayed_deinit(ieee, crypt);
+               }
+               goto done;
+       }
+       sec.enabled = 1;
+       sec.flags |= SEC_ENABLED;
+
+       ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
+               request_module("ieee80211_crypt_wep");
+               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
+               request_module("ieee80211_crypt_tkip");
+               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
+               request_module("ieee80211_crypt_ccmp");
+               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+       }
+       if (ops == NULL) {
+               IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n",
+                      dev->name, param->u.crypt.alg);
+               param->u.crypt.err = IPW2100_CRYPT_ERR_UNKNOWN_ALG;
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if (*crypt == NULL || (*crypt)->ops != ops) {
+               struct ieee80211_crypt_data *new_crypt;
+
+               ieee80211_crypt_delayed_deinit(ieee, crypt);
+
+               new_crypt = (struct ieee80211_crypt_data *)
+                       kmalloc(sizeof(struct ieee80211_crypt_data), GFP_KERNEL);
+               if (new_crypt == NULL) {
+                       ret = -ENOMEM;
+                       goto done;
+               }
+               memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
+               new_crypt->ops = ops;
+               if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+                       new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx);
+
+               if (new_crypt->priv == NULL) {
+                       kfree(new_crypt);
+                       param->u.crypt.err =
+                               IPW2100_CRYPT_ERR_CRYPT_INIT_FAILED;
+                       ret = -EINVAL;
+                       goto done;
+               }
+
+               *crypt = new_crypt;
+       }
+
+       if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
+           (*crypt)->ops->set_key(param->u.crypt.key,
+                                  param->u.crypt.key_len, param->u.crypt.seq,
+                                  (*crypt)->priv) < 0) {
+               IPW_DEBUG_INFO("%s: key setting failed\n",
+                      dev->name);
+               param->u.crypt.err = IPW2100_CRYPT_ERR_KEY_SET_FAILED;
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if (param->u.crypt.set_tx){
+               ieee->tx_keyidx = param->u.crypt.idx;
+               sec.active_key = param->u.crypt.idx;
+               sec.flags |= SEC_ACTIVE_KEY;
+       }
+
+       if (ops->name != NULL){
+
+               if (strcmp(ops->name, "WEP") == 0) {
+                       memcpy(sec.keys[param->u.crypt.idx], param->u.crypt.key, param->u.crypt.key_len);
+                       sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
+                       sec.flags |= (1 << param->u.crypt.idx);
+                       sec.flags |= SEC_LEVEL;
+                       sec.level = SEC_LEVEL_1;
+               } else if (strcmp(ops->name, "TKIP") == 0) {
+                       sec.flags |= SEC_LEVEL;
+                       sec.level = SEC_LEVEL_2;
+               } else if (strcmp(ops->name, "CCMP") == 0) {
+                       sec.flags |= SEC_LEVEL;
+                       sec.level = SEC_LEVEL_3;
+               }
+       }
+ done:
+       if (ieee->set_security)
+               ieee->set_security(ieee->dev, &sec);
+
+       /* Do not reset port if card is in Managed mode since resetting will
+        * generate new IEEE 802.11 authentication which may end up in looping
+        * with IEEE 802.1X.  If your hardware requires a reset after WEP
+        * configuration (for example... Prism2), implement the reset_port in
+        * the callbacks structures used to initialize the 802.11 stack. */
+       if (ieee->reset_on_keychange &&
+           ieee->iw_mode != IW_MODE_INFRA &&
+           ieee->reset_port &&
+           ieee->reset_port(dev)) {
+               IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name);
+               param->u.crypt.err = IPW2100_CRYPT_ERR_CARD_CONF_FAILED;
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+
+static int ipw2100_wpa_supplicant(struct net_device *dev, struct iw_point *p){
+
+       struct ipw2100_param *param;
+       int ret=0;
+
+       IPW_DEBUG_IOCTL("wpa_supplicant: len=%d\n", p->length);
+
+       if (p->length < sizeof(struct ipw2100_param) || !p->pointer)
+               return -EINVAL;
+
+       param = (struct ipw2100_param *)kmalloc(p->length, GFP_KERNEL);
+       if (param == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(param, p->pointer, p->length)){
+               kfree(param);
+               return -EFAULT;
+       }
+
+       switch (param->cmd){
+
+       case IPW2100_CMD_SET_WPA_PARAM:
+               ret = ipw2100_wpa_set_param(dev, param->u.wpa_param.name,
+                                           param->u.wpa_param.value);
+               break;
+
+       case IPW2100_CMD_SET_WPA_IE:
+               ret = ipw2100_wpa_set_wpa_ie(dev, param, p->length);
+               break;
+
+       case IPW2100_CMD_SET_ENCRYPTION:
+               ret = ipw2100_wpa_set_encryption(dev, param, p->length);
+               break;
+
+       case IPW2100_CMD_MLME:
+               ret = ipw2100_wpa_mlme(dev, param->u.mlme.command,
+                                      param->u.mlme.reason_code);
+               break;
+
+       default:
+               printk(KERN_ERR DRV_NAME ": %s: Unknown WPA supplicant request: %d\n",
+                               dev->name, param->cmd);
+               ret = -EOPNOTSUPP;
+
+       }
+
+       if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+               ret = -EFAULT;
+
+       kfree(param);
+       return ret;
+}
+#endif /* CONFIG_IEEE80211_WPA */
+
+static int ipw2100_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+#ifdef CONFIG_IEEE80211_WPA
+       struct iwreq *wrq = (struct iwreq *) rq;
+       int ret=-1;
+       switch (cmd){
+           case IPW2100_IOCTL_WPA_SUPPLICANT:
+               ret = ipw2100_wpa_supplicant(dev, &wrq->u.data);
+               return ret;
+
+           default:
+               return -EOPNOTSUPP;
+       }
+
+#endif /* CONFIG_IEEE80211_WPA */
+
+       return -EOPNOTSUPP;
+}
+
+
+static void ipw_ethtool_get_drvinfo(struct net_device *dev,
+                                   struct ethtool_drvinfo *info)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       char fw_ver[64], ucode_ver[64];
+
+       strcpy(info->driver, DRV_NAME);
+       strcpy(info->version, DRV_VERSION);
+
+       ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver));
+       ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver));
+
+       snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s",
+                fw_ver, priv->eeprom_version, ucode_ver);
+
+       strcpy(info->bus_info, pci_name(priv->pci_dev));
+}
+
+static u32 ipw2100_ethtool_get_link(struct net_device *dev)
+{
+    struct ipw2100_priv *priv = ieee80211_priv(dev);
+    return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
+}
+
+
+static struct ethtool_ops ipw2100_ethtool_ops = {
+    .get_link        = ipw2100_ethtool_get_link,
+    .get_drvinfo     = ipw_ethtool_get_drvinfo,
+};
+
+static void ipw2100_hang_check(void *adapter)
+{
+       struct ipw2100_priv *priv = adapter;
+       unsigned long flags;
+       u32 rtc = 0xa5a5a5a5;
+       u32 len = sizeof(rtc);
+       int restart = 0;
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+
+       if (priv->fatal_error != 0) {
+               /* If fatal_error is set then we need to restart */
+               IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n",
+                              priv->net_dev->name);
+
+               restart = 1;
+       } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) ||
+                  (rtc == priv->last_rtc)) {
+               /* Check if firmware is hung */
+               IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n",
+                              priv->net_dev->name);
+
+               restart = 1;
+       }
+
+       if (restart) {
+               /* Kill timer */
+               priv->stop_hang_check = 1;
+               priv->hangs++;
+
+               /* Restart the NIC */
+               schedule_reset(priv);
+       }
+
+       priv->last_rtc = rtc;
+
+       if (!priv->stop_hang_check)
+               queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
+
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+
+static void ipw2100_rf_kill(void *adapter)
+{
+       struct ipw2100_priv *priv = adapter;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->low_lock, flags);
+
+       if (rf_kill_active(priv)) {
+               IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
+               if (!priv->stop_rf_kill)
+                       queue_delayed_work(priv->workqueue, &priv->rf_kill, HZ);
+               goto exit_unlock;
+       }
+
+       /* RF Kill is now disabled, so bring the device back up */
+
+       if (!(priv->status & STATUS_RF_KILL_MASK)) {
+               IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
+                                 "device\n");
+               schedule_reset(priv);
+       } else
+               IPW_DEBUG_RF_KILL("HW RF Kill deactivated.  SW RF Kill still "
+                                 "enabled\n");
+
+ exit_unlock:
+       spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv);
+
+/* Look into using netdev destructor to shutdown ieee80211? */
+
+static struct net_device *ipw2100_alloc_device(
+       struct pci_dev *pci_dev,
+       char *base_addr,
+       unsigned long mem_start,
+       unsigned long mem_len)
+{
+       struct ipw2100_priv *priv;
+       struct net_device *dev;
+
+       dev = alloc_ieee80211(sizeof(struct ipw2100_priv));
+       if (!dev)
+               return NULL;
+       priv = ieee80211_priv(dev);
+       priv->ieee = netdev_priv(dev);
+       priv->pci_dev = pci_dev;
+       priv->net_dev = dev;
+
+       priv->ieee->hard_start_xmit = ipw2100_tx;
+       priv->ieee->set_security = shim__set_security;
+
+       dev->open = ipw2100_open;
+       dev->stop = ipw2100_close;
+       dev->init = ipw2100_net_init;
+       dev->do_ioctl = ipw2100_ioctl;
+       dev->get_stats = ipw2100_stats;
+       dev->ethtool_ops = &ipw2100_ethtool_ops;
+       dev->tx_timeout = ipw2100_tx_timeout;
+       dev->wireless_handlers = &ipw2100_wx_handler_def;
+       dev->get_wireless_stats = ipw2100_wx_wireless_stats;
+       dev->set_mac_address = ipw2100_set_address;
+       dev->watchdog_timeo = 3*HZ;
+       dev->irq = 0;
+
+       dev->base_addr = (unsigned long)base_addr;
+       dev->mem_start = mem_start;
+       dev->mem_end = dev->mem_start + mem_len - 1;
+
+       /* NOTE: We don't use the wireless_handlers hook
+        * in dev as the system will start throwing WX requests
+        * to us before we're actually initialized and it just
+        * ends up causing problems.  So, we just handle
+        * the WX extensions through the ipw2100_ioctl interface */
+
+
+       /* memset() puts everything to 0, so we only have explicitely set
+        * those values that need to be something else */
+
+       /* If power management is turned on, default to AUTO mode */
+       priv->power_mode = IPW_POWER_AUTO;
+
+
+
+#ifdef CONFIG_IEEE80211_WPA
+       priv->ieee->wpa_enabled = 0;
+       priv->ieee->tkip_countermeasures = 0;
+       priv->ieee->drop_unencrypted = 0;
+       priv->ieee->privacy_invoked = 0;
+       priv->ieee->ieee802_1x = 1;
+#endif /* CONFIG_IEEE80211_WPA */
+
+       /* Set module parameters */
+       switch (mode) {
+       case 1:
+               priv->ieee->iw_mode = IW_MODE_ADHOC;
+               break;
+#ifdef CONFIG_IPW2100_MONITOR
+       case 2:
+               priv->ieee->iw_mode = IW_MODE_MONITOR;
+               break;
+#endif
+       default:
+       case 0:
+               priv->ieee->iw_mode = IW_MODE_INFRA;
+               break;
+       }
+
+       if (disable == 1)
+               priv->status |= STATUS_RF_KILL_SW;
+
+       if (channel != 0 &&
+           ((channel >= REG_MIN_CHANNEL) &&
+            (channel <= REG_MAX_CHANNEL))) {
+               priv->config |= CFG_STATIC_CHANNEL;
+               priv->channel = channel;
+       }
+
+       if (associate)
+               priv->config |= CFG_ASSOCIATE;
+
+       priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
+       priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
+       priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
+       priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED;
+       priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED;
+       priv->tx_power = IPW_TX_POWER_DEFAULT;
+       priv->tx_rates = DEFAULT_TX_RATES;
+
+       strcpy(priv->nick, "ipw2100");
+
+       spin_lock_init(&priv->low_lock);
+       sema_init(&priv->action_sem, 1);
+       sema_init(&priv->adapter_sem, 1);
+
+       init_waitqueue_head(&priv->wait_command_queue);
+
+       netif_carrier_off(dev);
+
+       INIT_LIST_HEAD(&priv->msg_free_list);
+       INIT_LIST_HEAD(&priv->msg_pend_list);
+       INIT_STAT(&priv->msg_free_stat);
+       INIT_STAT(&priv->msg_pend_stat);
+
+       INIT_LIST_HEAD(&priv->tx_free_list);
+       INIT_LIST_HEAD(&priv->tx_pend_list);
+       INIT_STAT(&priv->tx_free_stat);
+       INIT_STAT(&priv->tx_pend_stat);
+
+       INIT_LIST_HEAD(&priv->fw_pend_list);
+       INIT_STAT(&priv->fw_pend_stat);
+
+
+#ifdef CONFIG_SOFTWARE_SUSPEND2
+       priv->workqueue = create_workqueue(DRV_NAME, 0);
+#else
+       priv->workqueue = create_workqueue(DRV_NAME);
+#endif
+       INIT_WORK(&priv->reset_work,
+                 (void (*)(void *))ipw2100_reset_adapter, priv);
+       INIT_WORK(&priv->security_work,
+                 (void (*)(void *))ipw2100_security_work, priv);
+       INIT_WORK(&priv->wx_event_work,
+                 (void (*)(void *))ipw2100_wx_event_work, priv);
+       INIT_WORK(&priv->hang_check, ipw2100_hang_check, priv);
+       INIT_WORK(&priv->rf_kill, ipw2100_rf_kill, priv);
+
+       tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+                    ipw2100_irq_tasklet, (unsigned long)priv);
+
+       /* NOTE:  We do not start the deferred work for status checks yet */
+       priv->stop_rf_kill = 1;
+       priv->stop_hang_check = 1;
+
+       return dev;
+}
+
+static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
+                               const struct pci_device_id *ent)
+{
+       unsigned long mem_start, mem_len, mem_flags;
+       char *base_addr = NULL;
+       struct net_device *dev = NULL;
+       struct ipw2100_priv *priv = NULL;
+       int err = 0;
+       int registered = 0;
+       u32 val;
+
+       IPW_DEBUG_INFO("enter\n");
+
+       mem_start = pci_resource_start(pci_dev, 0);
+       mem_len = pci_resource_len(pci_dev, 0);
+       mem_flags = pci_resource_flags(pci_dev, 0);
+
+       if ((mem_flags & IORESOURCE_MEM) != IORESOURCE_MEM) {
+               IPW_DEBUG_INFO("weird - resource type is not memory\n");
+               err = -ENODEV;
+               goto fail;
+       }
+
+       base_addr = ioremap_nocache(mem_start, mem_len);
+       if (!base_addr) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calling ioremap_nocache.\n");
+               err = -EIO;
+               goto fail;
+       }
+
+       /* allocate and initialize our net_device */
+       dev = ipw2100_alloc_device(pci_dev, base_addr, mem_start, mem_len);
+       if (!dev) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calling ipw2100_alloc_device.\n");
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       /* set up PCI mappings for device */
+       err = pci_enable_device(pci_dev);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calling pci_enable_device.\n");
+               return err;
+       }
+
+       priv = ieee80211_priv(dev);
+
+       pci_set_master(pci_dev);
+       pci_set_drvdata(pci_dev, priv);
+
+       err = pci_set_dma_mask(pci_dev, DMA_32BIT_MASK);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calling pci_set_dma_mask.\n");
+               pci_disable_device(pci_dev);
+               return err;
+       }
+
+       err = pci_request_regions(pci_dev, DRV_NAME);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calling pci_request_regions.\n");
+               pci_disable_device(pci_dev);
+               return err;
+       }
+
+        /* We disable the RETRY_TIMEOUT register (0x41) to keep
+        * PCI Tx retries from interfering with C3 CPU state */
+       pci_read_config_dword(pci_dev, 0x40, &val);
+       if ((val & 0x0000ff00) != 0)
+               pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+       pci_set_power_state(pci_dev, PCI_D0);
+
+       if (!ipw2100_hw_is_adapter_in_system(dev)) {
+               printk(KERN_WARNING DRV_NAME
+                      "Device not found via register read.\n");
+               err = -ENODEV;
+               goto fail;
+       }
+
+       SET_NETDEV_DEV(dev, &pci_dev->dev);
+
+       /* Force interrupts to be shut off on the device */
+       priv->status |= STATUS_INT_ENABLED;
+       ipw2100_disable_interrupts(priv);
+
+       /* Allocate and initialize the Tx/Rx queues and lists */
+       if (ipw2100_queues_allocate(priv)) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calilng ipw2100_queues_allocate.\n");
+               err = -ENOMEM;
+               goto fail;
+       }
+       ipw2100_queues_initialize(priv);
+
+       err = request_irq(pci_dev->irq,
+                         ipw2100_interrupt, SA_SHIRQ,
+                         dev->name, priv);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calling request_irq: %d.\n",
+                      pci_dev->irq);
+               goto fail;
+       }
+       dev->irq = pci_dev->irq;
+
+       IPW_DEBUG_INFO("Attempting to register device...\n");
+
+       SET_MODULE_OWNER(dev);
+
+       printk(KERN_INFO DRV_NAME
+              ": Detected Intel PRO/Wireless 2100 Network Connection\n");
+
+       /* Bring up the interface.  Pre 0.46, after we registered the
+        * network device we would call ipw2100_up.  This introduced a race
+        * condition with newer hotplug configurations (network was coming
+        * up and making calls before the device was initialized).
+        *
+        * If we called ipw2100_up before we registered the device, then the
+        * device name wasn't registered.  So, we instead use the net_dev->init
+        * member to call a function that then just turns and calls ipw2100_up.
+        * net_dev->init is called after name allocation but before the
+        * notifier chain is called */
+       down(&priv->action_sem);
+       err = register_netdev(dev);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME
+                      "Error calling register_netdev.\n");
+               goto fail_unlock;
+       }
+       registered = 1;
+
+       IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
+
+       /* perform this after register_netdev so that dev->name is set */
+       sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+       netif_carrier_off(dev);
+
+       /* If the RF Kill switch is disabled, go ahead and complete the
+        * startup sequence */
+       if (!(priv->status & STATUS_RF_KILL_MASK)) {
+               /* Enable the adapter - sends HOST_COMPLETE */
+               if (ipw2100_enable_adapter(priv)) {
+                       printk(KERN_WARNING DRV_NAME
+                              ": %s: failed in call to enable adapter.\n",
+                              priv->net_dev->name);
+                       ipw2100_hw_stop_adapter(priv);
+                       err = -EIO;
+                       goto fail_unlock;
+               }
+
+               /* Start a scan . . . */
+               ipw2100_set_scan_options(priv);
+               ipw2100_start_scan(priv);
+       }
+
+       IPW_DEBUG_INFO("exit\n");
+
+       priv->status |= STATUS_INITIALIZED;
+
+       up(&priv->action_sem);
+
+       return 0;
+
+ fail_unlock:
+       up(&priv->action_sem);
+
+ fail:
+       if (dev) {
+               if (registered)
+                       unregister_netdev(dev);
+
+               ipw2100_hw_stop_adapter(priv);
+
+               ipw2100_disable_interrupts(priv);
+
+               if (dev->irq)
+                       free_irq(dev->irq, priv);
+
+               ipw2100_kill_workqueue(priv);
+
+               /* These are safe to call even if they weren't allocated */
+               ipw2100_queues_free(priv);
+               sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+
+               free_ieee80211(dev);
+               pci_set_drvdata(pci_dev, NULL);
+       }
+
+       if (base_addr)
+               iounmap((char*)base_addr);
+
+       pci_release_regions(pci_dev);
+       pci_disable_device(pci_dev);
+
+       return err;
+}
+
+static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
+{
+       struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+       struct net_device *dev;
+
+       if (priv) {
+               down(&priv->action_sem);
+
+               priv->status &= ~STATUS_INITIALIZED;
+
+               dev = priv->net_dev;
+               sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+
+#ifdef CONFIG_PM
+               if (ipw2100_firmware.version)
+                       ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+               /* Take down the hardware */
+               ipw2100_down(priv);
+
+               /* Release the semaphore so that the network subsystem can
+                * complete any needed calls into the driver... */
+               up(&priv->action_sem);
+
+               /* Unregister the device first - this results in close()
+                * being called if the device is open.  If we free storage
+                * first, then close() will crash. */
+               unregister_netdev(dev);
+
+               /* ipw2100_down will ensure that there is no more pending work
+                * in the workqueue's, so we can safely remove them now. */
+               ipw2100_kill_workqueue(priv);
+
+               ipw2100_queues_free(priv);
+
+               /* Free potential debugging firmware snapshot */
+               ipw2100_snapshot_free(priv);
+
+               if (dev->irq)
+                       free_irq(dev->irq, priv);
+
+               if (dev->base_addr)
+                       iounmap((unsigned char *)dev->base_addr);
+
+               free_ieee80211(dev);
+       }
+
+       pci_release_regions(pci_dev);
+       pci_disable_device(pci_dev);
+
+       IPW_DEBUG_INFO("exit\n");
+}
+
+
+#ifdef CONFIG_PM
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int ipw2100_suspend(struct pci_dev *pci_dev, u32 state)
+#else
+static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
+#endif
+{
+       struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+       struct net_device *dev = priv->net_dev;
+
+       IPW_DEBUG_INFO("%s: Going into suspend...\n",
+              dev->name);
+
+       down(&priv->action_sem);
+       if (priv->status & STATUS_INITIALIZED) {
+               /* Take down the device; powers it off, etc. */
+               ipw2100_down(priv);
+       }
+
+       /* Remove the PRESENT state of the device */
+       netif_device_detach(dev);
+
+       pci_save_state(pci_dev);
+       pci_disable_device (pci_dev);
+       pci_set_power_state(pci_dev, PCI_D3hot);
+
+       up(&priv->action_sem);
+
+       return 0;
+}
+
+static int ipw2100_resume(struct pci_dev *pci_dev)
+{
+       struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+       struct net_device *dev = priv->net_dev;
+       u32 val;
+
+       if (IPW2100_PM_DISABLED)
+               return 0;
+
+       down(&priv->action_sem);
+
+       IPW_DEBUG_INFO("%s: Coming out of suspend...\n",
+              dev->name);
+
+       pci_set_power_state(pci_dev, PCI_D0);
+       pci_enable_device(pci_dev);
+       pci_restore_state(pci_dev);
+
+       /*
+        * Suspend/Resume resets the PCI configuration space, so we have to
+        * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
+        * from interfering with C3 CPU state. pci_restore_state won't help
+        * here since it only restores the first 64 bytes pci config header.
+        */
+       pci_read_config_dword(pci_dev, 0x40, &val);
+       if ((val & 0x0000ff00) != 0)
+               pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+       /* Set the device back into the PRESENT state; this will also wake
+        * the queue of needed */
+       netif_device_attach(dev);
+
+        /* Bring the device back up */
+        if (!(priv->status & STATUS_RF_KILL_SW))
+                ipw2100_up(priv, 0);
+
+       up(&priv->action_sem);
+
+       return 0;
+}
+#endif
+
+
+#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
+
+static struct pci_device_id ipw2100_pci_id_table[] __devinitdata = {
+       IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */
+       IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */
+       IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */
+       IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */
+       IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */
+       IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */
+
+       IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */
+
+       IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */
+       IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */
+       IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */
+       IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */
+       IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */
+       IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */
+       IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */
+
+       IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */
+
+       IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */
+
+       IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */
+       IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */
+       IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */
+
+       IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */
+       {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table);
+
+static struct pci_driver ipw2100_pci_driver = {
+       .name = DRV_NAME,
+       .id_table = ipw2100_pci_id_table,
+       .probe = ipw2100_pci_init_one,
+       .remove = __devexit_p(ipw2100_pci_remove_one),
+#ifdef CONFIG_PM
+       .suspend = ipw2100_suspend,
+       .resume = ipw2100_resume,
+#endif
+};
+
+
+/**
+ * Initialize the ipw2100 driver/module
+ *
+ * @returns 0 if ok, < 0 errno node con error.
+ *
+ * Note: we cannot init the /proc stuff until the PCI driver is there,
+ * or we risk an unlikely race condition on someone accessing
+ * uninitialized data in the PCI dev struct through /proc.
+ */
+static int __init ipw2100_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
+       printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
+
+#ifdef CONFIG_IEEE80211_NOWEP
+       IPW_DEBUG_INFO(DRV_NAME ": Compiled with WEP disabled.\n");
+#endif
+
+       ret = pci_module_init(&ipw2100_pci_driver);
+
+#ifdef CONFIG_IPW_DEBUG
+       ipw2100_debug_level = debug;
+       driver_create_file(&ipw2100_pci_driver.driver,
+                          &driver_attr_debug_level);
+#endif
+
+       return ret;
+}
+
+
+/**
+ * Cleanup ipw2100 driver registration
+ */
+static void __exit ipw2100_exit(void)
+{
+       /* FIXME: IPG: check that we have no instances of the devices open */
+#ifdef CONFIG_IPW_DEBUG
+       driver_remove_file(&ipw2100_pci_driver.driver,
+                          &driver_attr_debug_level);
+#endif
+       pci_unregister_driver(&ipw2100_pci_driver);
+}
+
+module_init(ipw2100_init);
+module_exit(ipw2100_exit);
+
+#define WEXT_USECHANNELS 1
+
+static const long ipw2100_frequencies[] = {
+       2412, 2417, 2422, 2427,
+       2432, 2437, 2442, 2447,
+       2452, 2457, 2462, 2467,
+       2472, 2484
+};
+
+#define FREQ_COUNT (sizeof(ipw2100_frequencies) / \
+                    sizeof(ipw2100_frequencies[0]))
+
+static const long ipw2100_rates_11b[] = {
+       1000000,
+       2000000,
+       5500000,
+       11000000
+};
+
+#define RATE_COUNT (sizeof(ipw2100_rates_11b) / sizeof(ipw2100_rates_11b[0]))
+
+static int ipw2100_wx_get_name(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       if (!(priv->status & STATUS_ASSOCIATED))
+               strcpy(wrqu->name, "unassociated");
+       else
+               snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
+
+       IPW_DEBUG_WX("Name: %s\n", wrqu->name);
+       return 0;
+}
+
+
+static int ipw2100_wx_set_freq(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       struct iw_freq *fwrq = &wrqu->freq;
+       int err = 0;
+
+       if (priv->ieee->iw_mode == IW_MODE_INFRA)
+               return -EOPNOTSUPP;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       /* if setting by freq convert to channel */
+       if (fwrq->e == 1) {
+               if ((fwrq->m >= (int) 2.412e8 &&
+                    fwrq->m <= (int) 2.487e8)) {
+                       int f = fwrq->m / 100000;
+                       int c = 0;
+
+                       while ((c < REG_MAX_CHANNEL) &&
+                              (f != ipw2100_frequencies[c]))
+                               c++;
+
+                       /* hack to fall through */
+                       fwrq->e = 0;
+                       fwrq->m = c + 1;
+               }
+       }
+
+       if (fwrq->e > 0 || fwrq->m > 1000)
+               return -EOPNOTSUPP;
+       else { /* Set the channel */
+               IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
+               err = ipw2100_set_channel(priv, fwrq->m, 0);
+       }
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+
+static int ipw2100_wx_get_freq(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       wrqu->freq.e = 0;
+
+       /* If we are associated, trying to associate, or have a statically
+        * configured CHANNEL then return that; otherwise return ANY */
+       if (priv->config & CFG_STATIC_CHANNEL ||
+           priv->status & STATUS_ASSOCIATED)
+               wrqu->freq.m = priv->channel;
+       else
+               wrqu->freq.m = 0;
+
+       IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
+       return 0;
+
+}
+
+static int ipw2100_wx_set_mode(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err = 0;
+
+       IPW_DEBUG_WX("SET Mode -> %d \n", wrqu->mode);
+
+       if (wrqu->mode == priv->ieee->iw_mode)
+               return 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       switch (wrqu->mode) {
+#ifdef CONFIG_IPW2100_MONITOR
+       case IW_MODE_MONITOR:
+               err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+               break;
+#endif /* CONFIG_IPW2100_MONITOR */
+       case IW_MODE_ADHOC:
+               err = ipw2100_switch_mode(priv, IW_MODE_ADHOC);
+               break;
+       case IW_MODE_INFRA:
+       case IW_MODE_AUTO:
+       default:
+               err = ipw2100_switch_mode(priv, IW_MODE_INFRA);
+               break;
+       }
+
+done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_mode(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       wrqu->mode = priv->ieee->iw_mode;
+       IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode);
+
+       return 0;
+}
+
+
+#define POWER_MODES 5
+
+/* Values are in microsecond */
+static const s32 timeout_duration[POWER_MODES] = {
+       350000,
+       250000,
+       75000,
+       37000,
+       25000,
+};
+
+static const s32 period_duration[POWER_MODES] = {
+       400000,
+       700000,
+       1000000,
+       1000000,
+       1000000
+};
+
+static int ipw2100_wx_get_range(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       struct iw_range *range = (struct iw_range *)extra;
+       u16 val;
+       int i, level;
+
+       wrqu->data.length = sizeof(*range);
+       memset(range, 0, sizeof(*range));
+
+       /* Let's try to keep this struct in the same order as in
+        * linux/include/wireless.h
+        */
+
+       /* TODO: See what values we can set, and remove the ones we can't
+        * set, or fill them with some default data.
+        */
+
+       /* ~5 Mb/s real (802.11b) */
+       range->throughput = 5 * 1000 * 1000;
+
+//     range->sensitivity;     /* signal level threshold range */
+
+       range->max_qual.qual = 100;
+       /* TODO: Find real max RSSI and stick here */
+       range->max_qual.level = 0;
+       range->max_qual.noise = 0;
+       range->max_qual.updated = 7; /* Updated all three */
+
+       range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
+       /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+       range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
+       range->avg_qual.noise = 0;
+       range->avg_qual.updated = 7; /* Updated all three */
+
+       range->num_bitrates = RATE_COUNT;
+
+       for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) {
+               range->bitrate[i] = ipw2100_rates_11b[i];
+       }
+
+       range->min_rts = MIN_RTS_THRESHOLD;
+       range->max_rts = MAX_RTS_THRESHOLD;
+       range->min_frag = MIN_FRAG_THRESHOLD;
+       range->max_frag = MAX_FRAG_THRESHOLD;
+
+       range->min_pmp = period_duration[0];    /* Minimal PM period */
+       range->max_pmp = period_duration[POWER_MODES-1];/* Maximal PM period */
+       range->min_pmt = timeout_duration[POWER_MODES-1];       /* Minimal PM timeout */
+       range->max_pmt = timeout_duration[0];/* Maximal PM timeout */
+
+        /* How to decode max/min PM period */
+       range->pmp_flags = IW_POWER_PERIOD;
+        /* How to decode max/min PM period */
+       range->pmt_flags = IW_POWER_TIMEOUT;
+       /* What PM options are supported */
+       range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD;
+
+       range->encoding_size[0] = 5;
+       range->encoding_size[1] = 13;           /* Different token sizes */
+       range->num_encoding_sizes = 2;          /* Number of entry in the list */
+       range->max_encoding_tokens = WEP_KEYS;  /* Max number of tokens */
+//     range->encoding_login_index;            /* token index for login token */
+
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+               range->txpower_capa = IW_TXPOW_DBM;
+               range->num_txpower = IW_MAX_TXPOWER;
+               for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16); i < IW_MAX_TXPOWER;
+                    i++, level -= ((IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM) * 16) /
+                            (IW_MAX_TXPOWER - 1))
+                       range->txpower[i] = level / 16;
+       } else {
+               range->txpower_capa = 0;
+               range->num_txpower = 0;
+       }
+
+
+       /* Set the Wireless Extension versions */
+       range->we_version_compiled = WIRELESS_EXT;
+       range->we_version_source = 16;
+
+//     range->retry_capa;      /* What retry options are supported */
+//     range->retry_flags;     /* How to decode max/min retry limit */
+//     range->r_time_flags;    /* How to decode max/min retry life */
+//     range->min_retry;       /* Minimal number of retries */
+//     range->max_retry;       /* Maximal number of retries */
+//     range->min_r_time;      /* Minimal retry lifetime */
+//     range->max_r_time;      /* Maximal retry lifetime */
+
+        range->num_channels = FREQ_COUNT;
+
+       val = 0;
+       for (i = 0; i < FREQ_COUNT; i++) {
+               // TODO: Include only legal frequencies for some countries
+//             if (local->channel_mask & (1 << i)) {
+                       range->freq[val].i = i + 1;
+                       range->freq[val].m = ipw2100_frequencies[i] * 100000;
+                       range->freq[val].e = 1;
+                       val++;
+//             }
+               if (val == IW_MAX_FREQUENCIES)
+               break;
+       }
+       range->num_frequency = val;
+
+       IPW_DEBUG_WX("GET Range\n");
+
+       return 0;
+}
+
+static int ipw2100_wx_set_wap(struct net_device *dev,
+                             struct iw_request_info *info,
+                             union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err = 0;
+
+       static const unsigned char any[] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+       };
+       static const unsigned char off[] = {
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+
+       // sanity checks
+       if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+               return -EINVAL;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
+           !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+               /* we disable mandatory BSSID association */
+               IPW_DEBUG_WX("exit - disable mandatory BSSID\n");
+               priv->config &= ~CFG_STATIC_BSSID;
+               err = ipw2100_set_mandatory_bssid(priv, NULL, 0);
+               goto done;
+       }
+
+       priv->config |= CFG_STATIC_BSSID;
+       memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+       err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0);
+
+       IPW_DEBUG_WX("SET BSSID -> %02X:%02X:%02X:%02X:%02X:%02X\n",
+                    wrqu->ap_addr.sa_data[0] & 0xff,
+                    wrqu->ap_addr.sa_data[1] & 0xff,
+                    wrqu->ap_addr.sa_data[2] & 0xff,
+                    wrqu->ap_addr.sa_data[3] & 0xff,
+                    wrqu->ap_addr.sa_data[4] & 0xff,
+                    wrqu->ap_addr.sa_data[5] & 0xff);
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_wap(struct net_device *dev,
+                             struct iw_request_info *info,
+                             union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       /* If we are associated, trying to associate, or have a statically
+        * configured BSSID then return that; otherwise return ANY */
+       if (priv->config & CFG_STATIC_BSSID ||
+           priv->status & STATUS_ASSOCIATED) {
+               wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(wrqu->ap_addr.sa_data, &priv->bssid, ETH_ALEN);
+       } else
+               memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+
+       IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
+                    MAC_ARG(wrqu->ap_addr.sa_data));
+       return 0;
+}
+
+static int ipw2100_wx_set_essid(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       char *essid = ""; /* ANY */
+       int length = 0;
+       int err = 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (wrqu->essid.flags && wrqu->essid.length) {
+               length = wrqu->essid.length - 1;
+               essid = extra;
+       }
+
+       if (length == 0) {
+               IPW_DEBUG_WX("Setting ESSID to ANY\n");
+               priv->config &= ~CFG_STATIC_ESSID;
+               err = ipw2100_set_essid(priv, NULL, 0, 0);
+               goto done;
+       }
+
+       length = min(length, IW_ESSID_MAX_SIZE);
+
+       priv->config |= CFG_STATIC_ESSID;
+
+       if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
+               IPW_DEBUG_WX("ESSID set to current ESSID.\n");
+               err = 0;
+               goto done;
+       }
+
+       IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
+                    length);
+
+       priv->essid_len = length;
+       memcpy(priv->essid, essid, priv->essid_len);
+
+       err = ipw2100_set_essid(priv, essid, length, 0);
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_essid(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       /* If we are associated, trying to associate, or have a statically
+        * configured ESSID then return that; otherwise return ANY */
+       if (priv->config & CFG_STATIC_ESSID ||
+           priv->status & STATUS_ASSOCIATED) {
+               IPW_DEBUG_WX("Getting essid: '%s'\n",
+                            escape_essid(priv->essid, priv->essid_len));
+               memcpy(extra, priv->essid, priv->essid_len);
+               wrqu->essid.length = priv->essid_len;
+               wrqu->essid.flags = 1; /* active */
+       } else {
+               IPW_DEBUG_WX("Getting essid: ANY\n");
+               wrqu->essid.length = 0;
+               wrqu->essid.flags = 0; /* active */
+       }
+
+       return 0;
+}
+
+static int ipw2100_wx_set_nick(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+               return -E2BIG;
+
+       wrqu->data.length = min((size_t)wrqu->data.length, sizeof(priv->nick));
+       memset(priv->nick, 0, sizeof(priv->nick));
+       memcpy(priv->nick, extra,  wrqu->data.length);
+
+       IPW_DEBUG_WX("SET Nickname -> %s \n", priv->nick);
+
+       return 0;
+}
+
+static int ipw2100_wx_get_nick(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       wrqu->data.length = strlen(priv->nick) + 1;
+       memcpy(extra, priv->nick, wrqu->data.length);
+       wrqu->data.flags = 1; /* active */
+
+       IPW_DEBUG_WX("GET Nickname -> %s \n", extra);
+
+       return 0;
+}
+
+static int ipw2100_wx_set_rate(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       u32 target_rate = wrqu->bitrate.value;
+       u32 rate;
+       int err = 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       rate = 0;
+
+       if (target_rate == 1000000 ||
+           (!wrqu->bitrate.fixed && target_rate > 1000000))
+               rate |= TX_RATE_1_MBIT;
+       if (target_rate == 2000000 ||
+           (!wrqu->bitrate.fixed && target_rate > 2000000))
+               rate |= TX_RATE_2_MBIT;
+       if (target_rate == 5500000 ||
+           (!wrqu->bitrate.fixed && target_rate > 5500000))
+               rate |= TX_RATE_5_5_MBIT;
+       if (target_rate == 11000000 ||
+           (!wrqu->bitrate.fixed && target_rate > 11000000))
+               rate |= TX_RATE_11_MBIT;
+       if (rate == 0)
+               rate = DEFAULT_TX_RATES;
+
+       err = ipw2100_set_tx_rates(priv, rate, 0);
+
+       IPW_DEBUG_WX("SET Rate -> %04X \n", rate);
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+
+static int ipw2100_wx_get_rate(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int val;
+       int len = sizeof(val);
+       int err = 0;
+
+       if (!(priv->status & STATUS_ENABLED) ||
+           priv->status & STATUS_RF_KILL_MASK ||
+           !(priv->status & STATUS_ASSOCIATED)) {
+               wrqu->bitrate.value = 0;
+               return 0;
+       }
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len);
+       if (err) {
+               IPW_DEBUG_WX("failed querying ordinals.\n");
+               return err;
+       }
+
+       switch (val & TX_RATE_MASK) {
+       case TX_RATE_1_MBIT:
+               wrqu->bitrate.value = 1000000;
+               break;
+       case TX_RATE_2_MBIT:
+               wrqu->bitrate.value = 2000000;
+               break;
+       case TX_RATE_5_5_MBIT:
+               wrqu->bitrate.value = 5500000;
+               break;
+       case TX_RATE_11_MBIT:
+               wrqu->bitrate.value = 11000000;
+               break;
+       default:
+               wrqu->bitrate.value = 0;
+       }
+
+       IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_set_rts(struct net_device *dev,
+                             struct iw_request_info *info,
+                             union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int value, err;
+
+       /* Auto RTS not yet supported */
+       if (wrqu->rts.fixed == 0)
+               return -EINVAL;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (wrqu->rts.disabled)
+               value = priv->rts_threshold | RTS_DISABLED;
+       else {
+               if (wrqu->rts.value < 1 ||
+                   wrqu->rts.value > 2304) {
+                       err = -EINVAL;
+                       goto done;
+               }
+               value = wrqu->rts.value;
+       }
+
+       err = ipw2100_set_rts_threshold(priv, value);
+
+       IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X \n", value);
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_rts(struct net_device *dev,
+                             struct iw_request_info *info,
+                             union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED;
+       wrqu->rts.fixed = 1; /* no auto select */
+
+       /* If RTS is set to the default value, then it is disabled */
+       wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0;
+
+       IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X \n", wrqu->rts.value);
+
+       return 0;
+}
+
+static int ipw2100_wx_set_txpow(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err = 0, value;
+
+       if (priv->ieee->iw_mode != IW_MODE_ADHOC)
+               return -EINVAL;
+
+       if (wrqu->txpower.disabled == 1 || wrqu->txpower.fixed == 0)
+               value = IPW_TX_POWER_DEFAULT;
+       else {
+               if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM ||
+                   wrqu->txpower.value > IPW_TX_POWER_MAX_DBM)
+                       return -EINVAL;
+
+               value = (wrqu->txpower.value - IPW_TX_POWER_MIN_DBM) * 16 /
+                       (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
+       }
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       err = ipw2100_set_tx_power(priv, value);
+
+       IPW_DEBUG_WX("SET TX Power -> %d \n", value);
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_txpow(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       if (priv->ieee->iw_mode != IW_MODE_ADHOC) {
+               wrqu->power.disabled = 1;
+               return 0;
+       }
+
+       if (priv->tx_power == IPW_TX_POWER_DEFAULT) {
+               wrqu->power.fixed = 0;
+               wrqu->power.value = IPW_TX_POWER_MAX_DBM;
+               wrqu->power.disabled = 1;
+       } else {
+               wrqu->power.disabled = 0;
+               wrqu->power.fixed = 1;
+               wrqu->power.value =
+                       (priv->tx_power *
+                        (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM)) /
+                       (IPW_TX_POWER_MAX - IPW_TX_POWER_MIN) +
+                       IPW_TX_POWER_MIN_DBM;
+       }
+
+       wrqu->power.flags = IW_TXPOW_DBM;
+
+       IPW_DEBUG_WX("GET TX Power -> %d \n", wrqu->power.value);
+
+       return 0;
+}
+
+static int ipw2100_wx_set_frag(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       if (!wrqu->frag.fixed)
+               return -EINVAL;
+
+       if (wrqu->frag.disabled) {
+               priv->frag_threshold |= FRAG_DISABLED;
+               priv->ieee->fts = DEFAULT_FTS;
+       } else {
+               if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+                   wrqu->frag.value > MAX_FRAG_THRESHOLD)
+                       return -EINVAL;
+
+               priv->ieee->fts = wrqu->frag.value & ~0x1;
+               priv->frag_threshold = priv->ieee->fts;
+       }
+
+       IPW_DEBUG_WX("SET Frag Threshold -> %d \n", priv->ieee->fts);
+
+       return 0;
+}
+
+static int ipw2100_wx_get_frag(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED;
+       wrqu->frag.fixed = 0;   /* no auto select */
+       wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0;
+
+       IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
+
+       return 0;
+}
+
+static int ipw2100_wx_set_retry(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err = 0;
+
+       if (wrqu->retry.flags & IW_RETRY_LIFETIME ||
+           wrqu->retry.disabled)
+               return -EINVAL;
+
+       if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
+               return 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (wrqu->retry.flags & IW_RETRY_MIN) {
+               err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+               IPW_DEBUG_WX("SET Short Retry Limit -> %d \n",
+                      wrqu->retry.value);
+               goto done;
+       }
+
+       if (wrqu->retry.flags & IW_RETRY_MAX) {
+               err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+               IPW_DEBUG_WX("SET Long Retry Limit -> %d \n",
+                      wrqu->retry.value);
+               goto done;
+       }
+
+       err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+       if (!err)
+               err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+
+       IPW_DEBUG_WX("SET Both Retry Limits -> %d \n", wrqu->retry.value);
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_retry(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       wrqu->retry.disabled = 0; /* can't be disabled */
+
+       if ((wrqu->retry.flags & IW_RETRY_TYPE) ==
+           IW_RETRY_LIFETIME)
+               return -EINVAL;
+
+       if (wrqu->retry.flags & IW_RETRY_MAX) {
+               wrqu->retry.flags = IW_RETRY_LIMIT & IW_RETRY_MAX;
+               wrqu->retry.value = priv->long_retry_limit;
+       } else {
+               wrqu->retry.flags =
+                   (priv->short_retry_limit !=
+                    priv->long_retry_limit) ?
+                   IW_RETRY_LIMIT & IW_RETRY_MIN : IW_RETRY_LIMIT;
+
+               wrqu->retry.value = priv->short_retry_limit;
+       }
+
+       IPW_DEBUG_WX("GET Retry -> %d \n", wrqu->retry.value);
+
+       return 0;
+}
+
+static int ipw2100_wx_set_scan(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err = 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       IPW_DEBUG_WX("Initiating scan...\n");
+       if (ipw2100_set_scan_options(priv) ||
+           ipw2100_start_scan(priv)) {
+               IPW_DEBUG_WX("Start scan failed.\n");
+
+               /* TODO: Mark a scan as pending so when hardware initialized
+                *       a scan starts */
+       }
+
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_scan(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
+}
+
+
+/*
+ * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c
+ */
+static int ipw2100_wx_set_encode(struct net_device *dev,
+                                struct iw_request_info *info,
+                                union iwreq_data *wrqu, char *key)
+{
+       /*
+        * No check of STATUS_INITIALIZED required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_get_encode(struct net_device *dev,
+                                struct iw_request_info *info,
+                                union iwreq_data *wrqu, char *key)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_set_power(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err = 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (wrqu->power.disabled) {
+               priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+               err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+               IPW_DEBUG_WX("SET Power Management Mode -> off\n");
+               goto done;
+       }
+
+       switch (wrqu->power.flags & IW_POWER_MODE) {
+       case IW_POWER_ON:    /* If not specified */
+       case IW_POWER_MODE:  /* If set all mask */
+       case IW_POWER_ALL_R: /* If explicitely state all */
+               break;
+       default: /* Otherwise we don't support it */
+               IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
+                            wrqu->power.flags);
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
+       /* If the user hasn't specified a power management mode yet, default
+        * to BATTERY */
+       priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+       err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
+
+       IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n",
+                    priv->power_mode);
+
+ done:
+       up(&priv->action_sem);
+       return err;
+
+}
+
+static int ipw2100_wx_get_power(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+               wrqu->power.disabled = 1;
+       } else {
+               wrqu->power.disabled = 0;
+               wrqu->power.flags = 0;
+       }
+
+       IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
+
+       return 0;
+}
+
+
+/*
+ *
+ * IWPRIV handlers
+ *
+ */
+#ifdef CONFIG_IPW2100_MONITOR
+static int ipw2100_wx_set_promisc(struct net_device *dev,
+                                 struct iw_request_info *info,
+                                 union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int *parms = (int *)extra;
+       int enable = (parms[0] > 0);
+       int err = 0;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (enable) {
+               if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+                       err = ipw2100_set_channel(priv, parms[1], 0);
+                       goto done;
+               }
+               priv->channel = parms[1];
+               err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+       } else {
+               if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+                       err = ipw2100_switch_mode(priv, priv->last_mode);
+       }
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_reset(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       if (priv->status & STATUS_INITIALIZED)
+               schedule_reset(priv);
+       return 0;
+}
+
+#endif
+
+static int ipw2100_wx_set_powermode(struct net_device *dev,
+                                   struct iw_request_info *info,
+                                   union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err = 0, mode = *(int *)extra;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if ((mode < 1) || (mode > POWER_MODES))
+               mode = IPW_POWER_AUTO;
+
+       if (priv->power_mode != mode)
+               err = ipw2100_set_power_mode(priv, mode);
+ done:
+       up(&priv->action_sem);
+       return err;
+}
+
+#define MAX_POWER_STRING 80
+static int ipw2100_wx_get_powermode(struct net_device *dev,
+                                   struct iw_request_info *info,
+                                   union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int level = IPW_POWER_LEVEL(priv->power_mode);
+       s32 timeout, period;
+
+       if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+               snprintf(extra, MAX_POWER_STRING,
+                        "Power save level: %d (Off)", level);
+       } else {
+               switch (level) {
+               case IPW_POWER_MODE_CAM:
+                       snprintf(extra, MAX_POWER_STRING,
+                                "Power save level: %d (None)", level);
+                       break;
+               case IPW_POWER_AUTO:
+               snprintf(extra, MAX_POWER_STRING,
+                        "Power save level: %d (Auto)", 0);
+                       break;
+               default:
+                       timeout = timeout_duration[level - 1] / 1000;
+                       period = period_duration[level - 1] / 1000;
+                       snprintf(extra, MAX_POWER_STRING,
+                                "Power save level: %d "
+                                "(Timeout %dms, Period %dms)",
+                                level, timeout, period);
+               }
+       }
+
+       wrqu->data.length = strlen(extra) + 1;
+
+       return 0;
+}
+
+
+static int ipw2100_wx_set_preamble(struct net_device *dev,
+                                  struct iw_request_info *info,
+                                  union iwreq_data *wrqu, char *extra)
+{
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       int err, mode = *(int *)extra;
+
+       down(&priv->action_sem);
+       if (!(priv->status & STATUS_INITIALIZED)) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (mode == 1)
+               priv->config |= CFG_LONG_PREAMBLE;
+       else if (mode == 0)
+               priv->config &= ~CFG_LONG_PREAMBLE;
+       else {
+               err = -EINVAL;
+               goto done;
+       }
+
+       err = ipw2100_system_config(priv, 0);
+
+done:
+       up(&priv->action_sem);
+       return err;
+}
+
+static int ipw2100_wx_get_preamble(struct net_device *dev,
+                                   struct iw_request_info *info,
+                                   union iwreq_data *wrqu, char *extra)
+{
+       /*
+        * This can be called at any time.  No action lock required
+        */
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+       if (priv->config & CFG_LONG_PREAMBLE)
+               snprintf(wrqu->name, IFNAMSIZ, "long (1)");
+       else
+               snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
+
+       return 0;
+}
+
+static iw_handler ipw2100_wx_handlers[] =
+{
+        NULL,                     /* SIOCSIWCOMMIT */
+        ipw2100_wx_get_name,      /* SIOCGIWNAME */
+        NULL,                     /* SIOCSIWNWID */
+        NULL,                     /* SIOCGIWNWID */
+        ipw2100_wx_set_freq,      /* SIOCSIWFREQ */
+        ipw2100_wx_get_freq,      /* SIOCGIWFREQ */
+        ipw2100_wx_set_mode,      /* SIOCSIWMODE */
+        ipw2100_wx_get_mode,      /* SIOCGIWMODE */
+        NULL,                     /* SIOCSIWSENS */
+        NULL,                     /* SIOCGIWSENS */
+        NULL,                     /* SIOCSIWRANGE */
+        ipw2100_wx_get_range,     /* SIOCGIWRANGE */
+        NULL,                     /* SIOCSIWPRIV */
+        NULL,                     /* SIOCGIWPRIV */
+        NULL,                     /* SIOCSIWSTATS */
+        NULL,                     /* SIOCGIWSTATS */
+        NULL,                     /* SIOCSIWSPY */
+        NULL,                     /* SIOCGIWSPY */
+        NULL,                     /* SIOCGIWTHRSPY */
+        NULL,                     /* SIOCWIWTHRSPY */
+        ipw2100_wx_set_wap,       /* SIOCSIWAP */
+        ipw2100_wx_get_wap,       /* SIOCGIWAP */
+        NULL,                     /* -- hole -- */
+        NULL,                     /* SIOCGIWAPLIST -- deprecated */
+        ipw2100_wx_set_scan,      /* SIOCSIWSCAN */
+        ipw2100_wx_get_scan,      /* SIOCGIWSCAN */
+        ipw2100_wx_set_essid,     /* SIOCSIWESSID */
+        ipw2100_wx_get_essid,     /* SIOCGIWESSID */
+        ipw2100_wx_set_nick,      /* SIOCSIWNICKN */
+        ipw2100_wx_get_nick,      /* SIOCGIWNICKN */
+        NULL,                     /* -- hole -- */
+        NULL,                     /* -- hole -- */
+        ipw2100_wx_set_rate,      /* SIOCSIWRATE */
+        ipw2100_wx_get_rate,      /* SIOCGIWRATE */
+        ipw2100_wx_set_rts,       /* SIOCSIWRTS */
+        ipw2100_wx_get_rts,       /* SIOCGIWRTS */
+        ipw2100_wx_set_frag,      /* SIOCSIWFRAG */
+        ipw2100_wx_get_frag,      /* SIOCGIWFRAG */
+        ipw2100_wx_set_txpow,     /* SIOCSIWTXPOW */
+        ipw2100_wx_get_txpow,     /* SIOCGIWTXPOW */
+        ipw2100_wx_set_retry,     /* SIOCSIWRETRY */
+        ipw2100_wx_get_retry,     /* SIOCGIWRETRY */
+        ipw2100_wx_set_encode,    /* SIOCSIWENCODE */
+        ipw2100_wx_get_encode,    /* SIOCGIWENCODE */
+        ipw2100_wx_set_power,     /* SIOCSIWPOWER */
+        ipw2100_wx_get_power,     /* SIOCGIWPOWER */
+};
+
+#define IPW2100_PRIV_SET_MONITOR       SIOCIWFIRSTPRIV
+#define IPW2100_PRIV_RESET             SIOCIWFIRSTPRIV+1
+#define IPW2100_PRIV_SET_POWER         SIOCIWFIRSTPRIV+2
+#define IPW2100_PRIV_GET_POWER         SIOCIWFIRSTPRIV+3
+#define IPW2100_PRIV_SET_LONGPREAMBLE  SIOCIWFIRSTPRIV+4
+#define IPW2100_PRIV_GET_LONGPREAMBLE  SIOCIWFIRSTPRIV+5
+
+static const struct iw_priv_args ipw2100_private_args[] = {
+
+#ifdef CONFIG_IPW2100_MONITOR
+       {
+               IPW2100_PRIV_SET_MONITOR,
+               IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"
+       },
+       {
+               IPW2100_PRIV_RESET,
+               IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"
+       },
+#endif /* CONFIG_IPW2100_MONITOR */
+
+       {
+               IPW2100_PRIV_SET_POWER,
+               IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"
+       },
+       {
+               IPW2100_PRIV_GET_POWER,
+               0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING, "get_power"
+       },
+       {
+               IPW2100_PRIV_SET_LONGPREAMBLE,
+               IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"
+       },
+       {
+               IPW2100_PRIV_GET_LONGPREAMBLE,
+               0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"
+       },
+};
+
+static iw_handler ipw2100_private_handler[] = {
+#ifdef CONFIG_IPW2100_MONITOR
+       ipw2100_wx_set_promisc,
+       ipw2100_wx_reset,
+#else /* CONFIG_IPW2100_MONITOR */
+       NULL,
+       NULL,
+#endif /* CONFIG_IPW2100_MONITOR */
+       ipw2100_wx_set_powermode,
+       ipw2100_wx_get_powermode,
+       ipw2100_wx_set_preamble,
+       ipw2100_wx_get_preamble,
+};
+
+static struct iw_handler_def ipw2100_wx_handler_def =
+{
+       .standard = ipw2100_wx_handlers,
+       .num_standard = sizeof(ipw2100_wx_handlers) / sizeof(iw_handler),
+       .num_private = sizeof(ipw2100_private_handler) / sizeof(iw_handler),
+       .num_private_args = sizeof(ipw2100_private_args) /
+       sizeof(struct iw_priv_args),
+       .private = (iw_handler *)ipw2100_private_handler,
+       .private_args = (struct iw_priv_args *)ipw2100_private_args,
+};
+
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ * Also called by SIOCGIWSTATS
+ */
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device * dev)
+{
+       enum {
+               POOR = 30,
+               FAIR = 60,
+               GOOD = 80,
+               VERY_GOOD = 90,
+               EXCELLENT = 95,
+               PERFECT = 100
+       };
+       int rssi_qual;
+       int tx_qual;
+       int beacon_qual;
+
+       struct ipw2100_priv *priv = ieee80211_priv(dev);
+       struct iw_statistics *wstats;
+       u32 rssi, quality, tx_retries, missed_beacons, tx_failures;
+       u32 ord_len = sizeof(u32);
+
+       if (!priv)
+               return (struct iw_statistics *) NULL;
+
+       wstats = &priv->wstats;
+
+       /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
+        * ipw2100_wx_wireless_stats seems to be called before fw is
+        * initialized.  STATUS_ASSOCIATED will only be set if the hw is up
+        * and associated; if not associcated, the values are all meaningless
+        * anyway, so set them all to NULL and INVALID */
+       if (!(priv->status & STATUS_ASSOCIATED)) {
+               wstats->miss.beacon = 0;
+               wstats->discard.retries = 0;
+               wstats->qual.qual = 0;
+               wstats->qual.level = 0;
+               wstats->qual.noise = 0;
+               wstats->qual.updated = 7;
+               wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+                       IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+               return wstats;
+       }
+
+       if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS,
+                               &missed_beacons, &ord_len))
+               goto fail_get_ordinal;
+
+        /* If we don't have a connection the quality and level is 0*/
+       if (!(priv->status & STATUS_ASSOCIATED)) {
+               wstats->qual.qual = 0;
+               wstats->qual.level = 0;
+       } else {
+               if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR,
+                                       &rssi, &ord_len))
+                       goto fail_get_ordinal;
+               wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+               if (rssi < 10)
+                       rssi_qual = rssi * POOR / 10;
+               else if (rssi < 15)
+                       rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR;
+               else if (rssi < 20)
+                       rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR;
+               else if (rssi < 30)
+                       rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) /
+                               10 + GOOD;
+               else
+                       rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) /
+                               10 + VERY_GOOD;
+
+               if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES,
+                                       &tx_retries, &ord_len))
+                       goto fail_get_ordinal;
+
+               if (tx_retries > 75)
+                       tx_qual = (90 - tx_retries) * POOR / 15;
+               else if (tx_retries > 70)
+                       tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
+               else if (tx_retries > 65)
+                       tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
+               else if (tx_retries > 50)
+                       tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
+                               15 + GOOD;
+               else
+                       tx_qual = (50 - tx_retries) *
+                               (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
+
+               if (missed_beacons > 50)
+                       beacon_qual = (60 - missed_beacons) * POOR / 10;
+               else if (missed_beacons > 40)
+                       beacon_qual = (50 - missed_beacons) * (FAIR - POOR) /
+                               10 + POOR;
+               else if (missed_beacons > 32)
+                       beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) /
+                               18 + FAIR;
+               else if (missed_beacons > 20)
+                       beacon_qual = (32 - missed_beacons) *
+                               (VERY_GOOD - GOOD) / 20 + GOOD;
+               else
+                       beacon_qual = (20 - missed_beacons) *
+                               (PERFECT - VERY_GOOD) / 20 + VERY_GOOD;
+
+               quality = min(beacon_qual, min(tx_qual, rssi_qual));
+
+#ifdef CONFIG_IPW_DEBUG
+               if (beacon_qual == quality)
+                       IPW_DEBUG_WX("Quality clamped by Missed Beacons\n");
+               else if (tx_qual == quality)
+                       IPW_DEBUG_WX("Quality clamped by Tx Retries\n");
+               else if (quality != 100)
+                       IPW_DEBUG_WX("Quality clamped by Signal Strength\n");
+               else
+                       IPW_DEBUG_WX("Quality not clamped.\n");
+#endif
+
+               wstats->qual.qual = quality;
+               wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+       }
+
+       wstats->qual.noise = 0;
+       wstats->qual.updated = 7;
+       wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
+
+        /* FIXME: this is percent and not a # */
+       wstats->miss.beacon = missed_beacons;
+
+       if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES,
+                               &tx_failures, &ord_len))
+               goto fail_get_ordinal;
+       wstats->discard.retries = tx_failures;
+
+       return wstats;
+
+ fail_get_ordinal:
+       IPW_DEBUG_WX("failed querying ordinals.\n");
+
+       return (struct iw_statistics *) NULL;
+}
+
+static void ipw2100_wx_event_work(struct ipw2100_priv *priv)
+{
+       union iwreq_data wrqu;
+       int len = ETH_ALEN;
+
+       if (priv->status & STATUS_STOPPING)
+               return;
+
+       down(&priv->action_sem);
+
+       IPW_DEBUG_WX("enter\n");
+
+       up(&priv->action_sem);
+
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+       /* Fetch BSSID from the hardware */
+       if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) ||
+           priv->status & STATUS_RF_KILL_MASK ||
+           ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+                               &priv->bssid,  &len)) {
+               memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+       } else {
+               /* We now have the BSSID, so can finish setting to the full
+                * associated state */
+               memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
+               memcpy(&priv->ieee->bssid, priv->bssid, ETH_ALEN);
+               priv->status &= ~STATUS_ASSOCIATING;
+               priv->status |= STATUS_ASSOCIATED;
+               netif_carrier_on(priv->net_dev);
+               if (netif_queue_stopped(priv->net_dev)) {
+                       IPW_DEBUG_INFO("Waking net queue.\n");
+                       netif_wake_queue(priv->net_dev);
+               } else {
+                       IPW_DEBUG_INFO("Starting net queue.\n");
+                       netif_start_queue(priv->net_dev);
+               }
+       }
+
+       if (!(priv->status & STATUS_ASSOCIATED)) {
+               IPW_DEBUG_WX("Configuring ESSID\n");
+               down(&priv->action_sem);
+               /* This is a disassociation event, so kick the firmware to
+                * look for another AP */
+               if (priv->config & CFG_STATIC_ESSID)
+                       ipw2100_set_essid(priv, priv->essid, priv->essid_len, 0);
+               else
+                       ipw2100_set_essid(priv, NULL, 0, 0);
+               up(&priv->action_sem);
+       }
+
+       wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+#define IPW2100_FW_MAJOR_VERSION 1
+#define IPW2100_FW_MINOR_VERSION 3
+
+#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8)
+#define IPW2100_FW_MAJOR(x) (x & 0xff)
+
+#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \
+                             IPW2100_FW_MAJOR_VERSION)
+
+#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \
+"." __stringify(IPW2100_FW_MINOR_VERSION)
+
+#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw"
+
+
+/*
+
+BINARY FIRMWARE HEADER FORMAT
+
+offset      length   desc
+0           2        version
+2           2        mode == 0:BSS,1:IBSS,2:MONITOR
+4           4        fw_len
+8           4        uc_len
+C           fw_len   firmware data
+12 + fw_len uc_len   microcode data
+
+*/
+
+struct ipw2100_fw_header {
+       short version;
+       short mode;
+       unsigned int fw_size;
+       unsigned int uc_size;
+} __attribute__ ((packed));
+
+
+
+static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
+{
+       struct ipw2100_fw_header *h =
+               (struct ipw2100_fw_header *)fw->fw_entry->data;
+
+       if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
+               printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
+                      "(detected version id of %u). "
+                      "See Documentation/networking/README.ipw2100\n",
+                      h->version);
+               return 1;
+       }
+
+       fw->version = h->version;
+       fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header);
+       fw->fw.size = h->fw_size;
+       fw->uc.data = fw->fw.data + h->fw_size;
+       fw->uc.size = h->uc_size;
+
+       return 0;
+}
+
+
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+                               struct ipw2100_fw *fw)
+{
+       char *fw_name;
+       int rc;
+
+       IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n",
+              priv->net_dev->name);
+
+       switch (priv->ieee->iw_mode) {
+       case IW_MODE_ADHOC:
+               fw_name = IPW2100_FW_NAME("-i");
+               break;
+#ifdef CONFIG_IPW2100_MONITOR
+       case IW_MODE_MONITOR:
+               fw_name = IPW2100_FW_NAME("-p");
+               break;
+#endif
+       case IW_MODE_INFRA:
+       default:
+               fw_name = IPW2100_FW_NAME("");
+               break;
+       }
+
+       rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev);
+
+       if (rc < 0) {
+               printk(KERN_ERR DRV_NAME ": "
+                      "%s: Firmware '%s' not available or load failed.\n",
+                      priv->net_dev->name, fw_name);
+               return rc;
+       }
+       IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data,
+                          fw->fw_entry->size);
+
+       ipw2100_mod_firmware_load(fw);
+
+       return 0;
+}
+
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+                                    struct ipw2100_fw *fw)
+{
+       fw->version = 0;
+       if (fw->fw_entry)
+               release_firmware(fw->fw_entry);
+       fw->fw_entry = NULL;
+}
+
+
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+                                size_t max)
+{
+       char ver[MAX_FW_VERSION_LEN];
+       u32 len = MAX_FW_VERSION_LEN;
+       u32 tmp;
+       int i;
+       /* firmware version is an ascii string (max len of 14) */
+       if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM,
+                               ver, &len))
+               return -EIO;
+       tmp = max;
+       if (len >= max)
+               len = max - 1;
+       for (i = 0; i < len; i++)
+               buf[i] = ver[i];
+       buf[i] = '\0';
+       return tmp;
+}
+
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+                                   size_t max)
+{
+       u32 ver;
+       u32 len = sizeof(ver);
+       /* microcode version is a 32 bit integer */
+       if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION,
+                               &ver, &len))
+               return -EIO;
+       return snprintf(buf, max, "%08X", ver);
+}
+
+/*
+ * On exit, the firmware will have been freed from the fw list
+ */
+static int ipw2100_fw_download(struct ipw2100_priv *priv,
+                              struct ipw2100_fw *fw)
+{
+       /* firmware is constructed of N contiguous entries, each entry is
+        * structured as:
+        *
+        * offset    sie         desc
+        * 0         4           address to write to
+        * 4         2           length of data run
+         * 6         length      data
+        */
+       unsigned int addr;
+       unsigned short len;
+
+       const unsigned char *firmware_data = fw->fw.data;
+       unsigned int firmware_data_left = fw->fw.size;
+
+       while (firmware_data_left > 0) {
+               addr = *(u32 *)(firmware_data);
+               firmware_data      += 4;
+               firmware_data_left -= 4;
+
+               len = *(u16 *)(firmware_data);
+               firmware_data      += 2;
+               firmware_data_left -= 2;
+
+               if (len > 32) {
+                       printk(KERN_ERR DRV_NAME ": "
+                              "Invalid firmware run-length of %d bytes\n",
+                              len);
+                       return -EINVAL;
+               }
+
+               write_nic_memory(priv->net_dev, addr, len, firmware_data);
+               firmware_data      += len;
+               firmware_data_left -= len;
+       }
+
+       return 0;
+}
+
+struct symbol_alive_response {
+       u8 cmd_id;
+       u8 seq_num;
+       u8 ucode_rev;
+       u8 eeprom_valid;
+       u16 valid_flags;
+       u8 IEEE_addr[6];
+       u16 flags;
+       u16 pcb_rev;
+       u16 clock_settle_time;  // 1us LSB
+       u16 powerup_settle_time;        // 1us LSB
+       u16 hop_settle_time;    // 1us LSB
+       u8 date[3];             // month, day, year
+       u8 time[2];             // hours, minutes
+       u8 ucode_valid;
+};
+
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+                                 struct ipw2100_fw *fw)
+{
+       struct net_device *dev = priv->net_dev;
+       const unsigned char *microcode_data = fw->uc.data;
+       unsigned int microcode_data_left = fw->uc.size;
+
+       struct symbol_alive_response response;
+       int i, j;
+       u8 data;
+
+       /* Symbol control */
+       write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+       readl((void *)(dev->base_addr));
+       write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+       readl((void *)(dev->base_addr));
+
+       /* HW config */
+       write_nic_byte(dev, 0x210014, 0x72);    /* fifo width =16 */
+       readl((void *)(dev->base_addr));
+       write_nic_byte(dev, 0x210014, 0x72);    /* fifo width =16 */
+       readl((void *)(dev->base_addr));
+
+       /* EN_CS_ACCESS bit to reset control store pointer */
+       write_nic_byte(dev, 0x210000, 0x40);
+       readl((void *)(dev->base_addr));
+       write_nic_byte(dev, 0x210000, 0x0);
+       readl((void *)(dev->base_addr));
+       write_nic_byte(dev, 0x210000, 0x40);
+       readl((void *)(dev->base_addr));
+
+       /* copy microcode from buffer into Symbol */
+
+       while (microcode_data_left > 0) {
+               write_nic_byte(dev, 0x210010, *microcode_data++);
+               write_nic_byte(dev, 0x210010, *microcode_data++);
+               microcode_data_left -= 2;
+       }
+
+       /* EN_CS_ACCESS bit to reset the control store pointer */
+       write_nic_byte(dev, 0x210000, 0x0);
+       readl((void *)(dev->base_addr));
+
+       /* Enable System (Reg 0)
+        * first enable causes garbage in RX FIFO */
+       write_nic_byte(dev, 0x210000, 0x0);
+       readl((void *)(dev->base_addr));
+       write_nic_byte(dev, 0x210000, 0x80);
+       readl((void *)(dev->base_addr));
+
+       /* Reset External Baseband Reg */
+       write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+       readl((void *)(dev->base_addr));
+       write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+       readl((void *)(dev->base_addr));
+
+       /* HW Config (Reg 5) */
+       write_nic_byte(dev, 0x210014, 0x72);    // fifo width =16
+       readl((void *)(dev->base_addr));
+       write_nic_byte(dev, 0x210014, 0x72);    // fifo width =16
+       readl((void *)(dev->base_addr));
+
+       /* Enable System (Reg 0)
+        * second enable should be OK */
+       write_nic_byte(dev, 0x210000, 0x00);    // clear enable system
+       readl((void *)(dev->base_addr));
+       write_nic_byte(dev, 0x210000, 0x80);    // set enable system
+
+       /* check Symbol is enabled - upped this from 5 as it wasn't always
+        * catching the update */
+       for (i = 0; i < 10; i++) {
+               udelay(10);
+
+               /* check Dino is enabled bit */
+               read_nic_byte(dev, 0x210000, &data);
+               if (data & 0x1)
+                       break;
+       }
+
+       if (i == 10) {
+               printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n",
+                      dev->name);
+               return -EIO;
+       }
+
+       /* Get Symbol alive response */
+       for (i = 0; i < 30; i++) {
+               /* Read alive response structure */
+               for (j = 0;
+                    j < (sizeof(struct symbol_alive_response) >> 1);
+                    j++)
+                       read_nic_word(dev, 0x210004,
+                                     ((u16 *)&response) + j);
+
+               if ((response.cmd_id == 1) &&
+                   (response.ucode_valid == 0x1))
+                       break;
+               udelay(10);
+       }
+
+       if (i == 30) {
+               printk(KERN_ERR DRV_NAME ": %s: No response from Symbol - hw not alive\n",
+                      dev->name);
+               printk_buf(IPW_DL_ERROR, (u8*)&response, sizeof(response));
+               return -EIO;
+       }
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ipw2100.h b/drivers/net/wireless/ipw2100.h
new file mode 100644 (file)
index 0000000..2a3cdbd
--- /dev/null
@@ -0,0 +1,1167 @@
+/******************************************************************************
+
+  Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+
+  Contact Information:
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+#ifndef _IPW2100_H
+#define _IPW2100_H
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/version.h>
+#include <net/iw_handler.h>    // new driver API
+
+#include <net/ieee80211.h>
+
+#include <linux/workqueue.h>
+
+struct ipw2100_priv;
+struct ipw2100_tx_packet;
+struct ipw2100_rx_packet;
+
+#define IPW_DL_UNINIT    0x80000000
+#define IPW_DL_NONE      0x00000000
+#define IPW_DL_ALL       0x7FFFFFFF
+
+/*
+ * 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:
+ *
+ * #define IPW_DL_xxxx VALUE
+ *
+ * shifting value to the left one bit from the previous entry.  xxxx should be
+ * the name of the classification (for example, WEP)
+ *
+ * You then need to either add a IPW2100_xxxx_DEBUG() macro definition for your
+ * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * To add your debug level to the list of levels seen when you perform
+ *
+ * % cat /proc/net/ipw2100/debug_level
+ *
+ * you simply need to add your entry to the ipw2100_debug_levels array.
+ *
+ * If you do not see debug_level in /proc/net/ipw2100 then you do not have
+ * CONFIG_IPW_DEBUG defined in your kernel configuration
+ *
+ */
+
+#define IPW_DL_ERROR         (1<<0)
+#define IPW_DL_WARNING       (1<<1)
+#define IPW_DL_INFO          (1<<2)
+#define IPW_DL_WX            (1<<3)
+#define IPW_DL_HC            (1<<5)
+#define IPW_DL_STATE         (1<<6)
+
+#define IPW_DL_NOTIF         (1<<10)
+#define IPW_DL_SCAN          (1<<11)
+#define IPW_DL_ASSOC         (1<<12)
+#define IPW_DL_DROP          (1<<13)
+
+#define IPW_DL_IOCTL         (1<<14)
+#define IPW_DL_RF_KILL       (1<<17)
+
+
+#define IPW_DL_MANAGE        (1<<15)
+#define IPW_DL_FW            (1<<16)
+
+#define IPW_DL_FRAG          (1<<21)
+#define IPW_DL_WEP           (1<<22)
+#define IPW_DL_TX            (1<<23)
+#define IPW_DL_RX            (1<<24)
+#define IPW_DL_ISR           (1<<25)
+#define IPW_DL_IO            (1<<26)
+#define IPW_DL_TRACE         (1<<28)
+
+#define IPW_DEBUG_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a)
+#define IPW_DEBUG_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a)
+#define IPW_DEBUG_INFO(f...)    IPW_DEBUG(IPW_DL_INFO, ## f)
+#define IPW_DEBUG_WX(f...)     IPW_DEBUG(IPW_DL_WX, ## f)
+#define IPW_DEBUG_SCAN(f...)   IPW_DEBUG(IPW_DL_SCAN, ## f)
+#define IPW_DEBUG_NOTIF(f...) IPW_DEBUG(IPW_DL_NOTIF, ## f)
+#define IPW_DEBUG_TRACE(f...)  IPW_DEBUG(IPW_DL_TRACE, ## f)
+#define IPW_DEBUG_RX(f...)     IPW_DEBUG(IPW_DL_RX, ## f)
+#define IPW_DEBUG_TX(f...)     IPW_DEBUG(IPW_DL_TX, ## f)
+#define IPW_DEBUG_ISR(f...)    IPW_DEBUG(IPW_DL_ISR, ## f)
+#define IPW_DEBUG_MANAGEMENT(f...) IPW_DEBUG(IPW_DL_MANAGE, ## f)
+#define IPW_DEBUG_WEP(f...)    IPW_DEBUG(IPW_DL_WEP, ## f)
+#define IPW_DEBUG_HC(f...) IPW_DEBUG(IPW_DL_HC, ## f)
+#define IPW_DEBUG_FRAG(f...) IPW_DEBUG(IPW_DL_FRAG, ## f)
+#define IPW_DEBUG_FW(f...) IPW_DEBUG(IPW_DL_FW, ## f)
+#define IPW_DEBUG_RF_KILL(f...) IPW_DEBUG(IPW_DL_RF_KILL, ## f)
+#define IPW_DEBUG_DROP(f...) IPW_DEBUG(IPW_DL_DROP, ## f)
+#define IPW_DEBUG_IO(f...) IPW_DEBUG(IPW_DL_IO, ## f)
+#define IPW_DEBUG_IOCTL(f...) IPW_DEBUG(IPW_DL_IOCTL, ## f)
+#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+
+enum {
+       IPW_HW_STATE_DISABLED = 1,
+       IPW_HW_STATE_ENABLED = 0
+};
+
+struct ssid_context {
+       char ssid[IW_ESSID_MAX_SIZE + 1];
+       int ssid_len;
+       unsigned char bssid[ETH_ALEN];
+       int port_type;
+       int channel;
+
+};
+
+extern const char *port_type_str[];
+extern const char *band_str[];
+
+#define NUMBER_OF_BD_PER_COMMAND_PACKET                1
+#define NUMBER_OF_BD_PER_DATA_PACKET           2
+
+#define IPW_MAX_BDS 6
+#define NUMBER_OF_OVERHEAD_BDS_PER_PACKETR     2
+#define NUMBER_OF_BDS_TO_LEAVE_FOR_COMMANDS    1
+
+#define REQUIRED_SPACE_IN_RING_FOR_COMMAND_PACKET \
+    (IPW_BD_QUEUE_W_R_MIN_SPARE + NUMBER_OF_BD_PER_COMMAND_PACKET)
+
+struct bd_status {
+       union {
+               struct { u8 nlf:1, txType:2, intEnabled:1, reserved:4;} fields;
+               u8 field;
+       } info;
+} __attribute__ ((packed));
+
+struct ipw2100_bd {
+       u32 host_addr;
+       u32 buf_length;
+       struct bd_status status;
+        /* number of fragments for frame (should be set only for
+        * 1st TBD) */
+       u8 num_fragments;
+       u8 reserved[6];
+} __attribute__ ((packed));
+
+#define IPW_BD_QUEUE_LENGTH(n) (1<<n)
+#define IPW_BD_ALIGNMENT(L)    (L*sizeof(struct ipw2100_bd))
+
+#define IPW_BD_STATUS_TX_FRAME_802_3             0x00
+#define IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT 0x01
+#define IPW_BD_STATUS_TX_FRAME_COMMAND          0x02
+#define IPW_BD_STATUS_TX_FRAME_802_11           0x04
+#define IPW_BD_STATUS_TX_INTERRUPT_ENABLE       0x08
+
+struct ipw2100_bd_queue {
+       /* driver (virtual) pointer to queue */
+       struct ipw2100_bd *drv;
+
+       /* firmware (physical) pointer to queue */
+       dma_addr_t nic;
+
+       /* Length of phy memory allocated for BDs */
+       u32 size;
+
+       /* Number of BDs in queue (and in array) */
+       u32 entries;
+
+       /* Number of available BDs (invalid for NIC BDs) */
+       u32 available;
+
+       /* Offset of oldest used BD in array (next one to
+        * check for completion) */
+       u32 oldest;
+
+       /* Offset of next available (unused) BD */
+       u32 next;
+};
+
+#define RX_QUEUE_LENGTH 256
+#define TX_QUEUE_LENGTH 256
+#define HW_QUEUE_LENGTH 256
+
+#define TX_PENDED_QUEUE_LENGTH (TX_QUEUE_LENGTH / NUMBER_OF_BD_PER_DATA_PACKET)
+
+#define STATUS_TYPE_MASK       0x0000000f
+#define COMMAND_STATUS_VAL     0
+#define STATUS_CHANGE_VAL      1
+#define P80211_DATA_VAL        2
+#define P8023_DATA_VAL         3
+#define HOST_NOTIFICATION_VAL  4
+
+#define IPW2100_RSSI_TO_DBM (-98)
+
+struct ipw2100_status {
+       u32 frame_size;
+       u16 status_fields;
+       u8 flags;
+#define IPW_STATUS_FLAG_DECRYPTED      (1<<0)
+#define IPW_STATUS_FLAG_WEP_ENCRYPTED  (1<<1)
+#define IPW_STATUS_FLAG_CRC_ERROR       (1<<2)
+       u8 rssi;
+} __attribute__ ((packed));
+
+struct ipw2100_status_queue {
+       /* driver (virtual) pointer to queue */
+       struct ipw2100_status *drv;
+
+       /* firmware (physical) pointer to queue */
+       dma_addr_t nic;
+
+       /* Length of phy memory allocated for BDs */
+       u32 size;
+};
+
+#define HOST_COMMAND_PARAMS_REG_LEN    100
+#define CMD_STATUS_PARAMS_REG_LEN      3
+
+#define IPW_WPA_CAPABILITIES   0x1
+#define IPW_WPA_LISTENINTERVAL 0x2
+#define IPW_WPA_AP_ADDRESS     0x4
+
+#define IPW_MAX_VAR_IE_LEN ((HOST_COMMAND_PARAMS_REG_LEN - 4) * sizeof(u32))
+
+struct ipw2100_wpa_assoc_frame {
+       u16 fixed_ie_mask;
+       struct {
+               u16 capab_info;
+               u16 listen_interval;
+               u8 current_ap[ETH_ALEN];
+       } fixed_ies;
+       u32 var_ie_len;
+       u8 var_ie[IPW_MAX_VAR_IE_LEN];
+};
+
+#define IPW_BSS     1
+#define IPW_MONITOR 2
+#define IPW_IBSS    3
+
+/**
+ * @struct _tx_cmd - HWCommand
+ * @brief H/W command structure.
+ */
+struct ipw2100_cmd_header {
+       u32 host_command_reg;
+       u32 host_command_reg1;
+       u32 sequence;
+       u32 host_command_len_reg;
+       u32 host_command_params_reg[HOST_COMMAND_PARAMS_REG_LEN];
+       u32 cmd_status_reg;
+       u32 cmd_status_params_reg[CMD_STATUS_PARAMS_REG_LEN];
+       u32 rxq_base_ptr;
+       u32 rxq_next_ptr;
+       u32 rxq_host_ptr;
+       u32 txq_base_ptr;
+       u32 txq_next_ptr;
+       u32 txq_host_ptr;
+       u32 tx_status_reg;
+       u32 reserved;
+       u32 status_change_reg;
+       u32 reserved1[3];
+       u32 *ordinal1_ptr;
+       u32 *ordinal2_ptr;
+} __attribute__ ((packed));
+
+struct ipw2100_data_header {
+       u32 host_command_reg;
+       u32 host_command_reg1;
+       u8 encrypted;   // BOOLEAN in win! TRUE if frame is enc by driver
+       u8 needs_encryption;    // BOOLEAN in win! TRUE if frma need to be enc in NIC
+       u8 wep_index;           // 0 no key, 1-4 key index, 0xff immediate key
+       u8 key_size;    // 0 no imm key, 0x5 64bit encr, 0xd 128bit encr, 0x10 128bit encr and 128bit IV
+       u8 key[16];
+       u8 reserved[10];        // f/w reserved
+       u8 src_addr[ETH_ALEN];
+       u8 dst_addr[ETH_ALEN];
+       u16 fragment_size;
+} __attribute__ ((packed));
+
+/* Host command data structure */
+struct host_command {
+       u32 host_command;               // COMMAND ID
+       u32 host_command1;              // COMMAND ID
+       u32 host_command_sequence;      // UNIQUE COMMAND NUMBER (ID)
+       u32 host_command_length;        // LENGTH
+       u32 host_command_parameters[HOST_COMMAND_PARAMS_REG_LEN];       // COMMAND PARAMETERS
+} __attribute__ ((packed));
+
+
+typedef enum {
+       POWER_ON_RESET,
+       EXIT_POWER_DOWN_RESET,
+       SW_RESET,
+       EEPROM_RW,
+       SW_RE_INIT
+} ipw2100_reset_event;
+
+enum {
+       COMMAND = 0xCAFE,
+       DATA,
+       RX
+};
+
+
+struct ipw2100_tx_packet {
+       int type;
+       int index;
+       union {
+               struct { /* COMMAND */
+                       struct ipw2100_cmd_header* cmd;
+                       dma_addr_t cmd_phys;
+               } c_struct;
+               struct { /* DATA */
+                       struct ipw2100_data_header* data;
+                       dma_addr_t data_phys;
+                       struct ieee80211_txb *txb;
+               } d_struct;
+       } info;
+       int jiffy_start;
+
+       struct list_head list;
+};
+
+
+struct ipw2100_rx_packet {
+       struct ipw2100_rx *rxp;
+       dma_addr_t dma_addr;
+       int jiffy_start;
+       struct sk_buff *skb;
+       struct list_head list;
+};
+
+#define FRAG_DISABLED             (1<<31)
+#define RTS_DISABLED              (1<<31)
+#define MAX_RTS_THRESHOLD         2304U
+#define MIN_RTS_THRESHOLD         1U
+#define DEFAULT_RTS_THRESHOLD     1000U
+
+#define DEFAULT_BEACON_INTERVAL   100U
+#define        DEFAULT_SHORT_RETRY_LIMIT 7U
+#define        DEFAULT_LONG_RETRY_LIMIT  4U
+
+struct ipw2100_ordinals {
+       u32 table1_addr;
+       u32 table2_addr;
+       u32 table1_size;
+       u32 table2_size;
+};
+
+/* Host Notification header */
+struct ipw2100_notification {
+       u32 hnhdr_subtype;      /* type of host notification */
+       u32 hnhdr_size;         /* size in bytes of data
+                                  or number of entries, if table.
+                                  Does NOT include header */
+} __attribute__ ((packed));
+
+#define MAX_KEY_SIZE   16
+#define        MAX_KEYS        8
+
+#define IPW2100_WEP_ENABLE     (1<<1)
+#define IPW2100_WEP_DROP_CLEAR (1<<2)
+
+#define IPW_NONE_CIPHER   (1<<0)
+#define IPW_WEP40_CIPHER  (1<<1)
+#define IPW_TKIP_CIPHER   (1<<2)
+#define IPW_CCMP_CIPHER   (1<<4)
+#define IPW_WEP104_CIPHER (1<<5)
+#define IPW_CKIP_CIPHER   (1<<6)
+
+#define        IPW_AUTH_OPEN     0
+#define        IPW_AUTH_SHARED   1
+
+struct statistic {
+       int value;
+       int hi;
+       int lo;
+};
+
+#define INIT_STAT(x) do {  \
+  (x)->value = (x)->hi = 0; \
+  (x)->lo = 0x7fffffff; \
+} while (0)
+#define SET_STAT(x,y) do { \
+  (x)->value = y; \
+  if ((x)->value > (x)->hi) (x)->hi = (x)->value; \
+  if ((x)->value < (x)->lo) (x)->lo = (x)->value; \
+} while (0)
+#define INC_STAT(x) do { if (++(x)->value > (x)->hi) (x)->hi = (x)->value; } \
+while (0)
+#define DEC_STAT(x) do { if (--(x)->value < (x)->lo) (x)->lo = (x)->value; } \
+while (0)
+
+#define IPW2100_ERROR_QUEUE 5
+
+/* Power management code: enable or disable? */
+enum {
+#ifdef CONFIG_PM
+       IPW2100_PM_DISABLED = 0,
+       PM_STATE_SIZE = 16,
+#else
+       IPW2100_PM_DISABLED = 1,
+       PM_STATE_SIZE = 0,
+#endif
+};
+
+#define STATUS_POWERED          (1<<0)
+#define STATUS_CMD_ACTIVE       (1<<1)  /**< host command in progress */
+#define STATUS_RUNNING          (1<<2)  /* Card initialized, but not enabled */
+#define STATUS_ENABLED          (1<<3)  /* Card enabled -- can scan,Tx,Rx */
+#define STATUS_STOPPING         (1<<4)  /* Card is in shutdown phase */
+#define STATUS_INITIALIZED      (1<<5)  /* Card is ready for external calls */
+#define STATUS_ASSOCIATING      (1<<9)  /* Associated, but no BSSID yet */
+#define STATUS_ASSOCIATED       (1<<10) /* Associated and BSSID valid */
+#define STATUS_INT_ENABLED      (1<<11)
+#define STATUS_RF_KILL_HW       (1<<12)
+#define STATUS_RF_KILL_SW       (1<<13)
+#define STATUS_RF_KILL_MASK     (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW)
+#define STATUS_EXIT_PENDING     (1<<14)
+
+#define STATUS_SCAN_PENDING     (1<<23)
+#define STATUS_SCANNING         (1<<24)
+#define STATUS_SCAN_ABORTING    (1<<25)
+#define STATUS_SCAN_COMPLETE    (1<<26)
+#define STATUS_WX_EVENT_PENDING (1<<27)
+#define STATUS_RESET_PENDING    (1<<29)
+#define STATUS_SECURITY_UPDATED (1<<30) /* Security sync needed */
+
+
+
+/* Internal NIC states */
+#define IPW_STATE_INITIALIZED  (1<<0)
+#define IPW_STATE_COUNTRY_FOUND        (1<<1)
+#define IPW_STATE_ASSOCIATED    (1<<2)
+#define IPW_STATE_ASSN_LOST    (1<<3)
+#define IPW_STATE_ASSN_CHANGED         (1<<4)
+#define IPW_STATE_SCAN_COMPLETE        (1<<5)
+#define IPW_STATE_ENTERED_PSP  (1<<6)
+#define IPW_STATE_LEFT_PSP     (1<<7)
+#define IPW_STATE_RF_KILL       (1<<8)
+#define IPW_STATE_DISABLED     (1<<9)
+#define IPW_STATE_POWER_DOWN   (1<<10)
+#define IPW_STATE_SCANNING      (1<<11)
+
+
+
+#define CFG_STATIC_CHANNEL      (1<<0) /* Restrict assoc. to single channel */
+#define CFG_STATIC_ESSID        (1<<1) /* Restrict assoc. to single SSID */
+#define CFG_STATIC_BSSID        (1<<2) /* Restrict assoc. to single BSSID */
+#define CFG_CUSTOM_MAC          (1<<3)
+#define CFG_LONG_PREAMBLE       (1<<4)
+#define CFG_ASSOCIATE           (1<<6)
+#define CFG_FIXED_RATE          (1<<7)
+#define CFG_ADHOC_CREATE        (1<<8)
+#define CFG_C3_DISABLED         (1<<9)
+#define CFG_PASSIVE_SCAN        (1<<10)
+
+#define CAP_SHARED_KEY          (1<<0) /* Off = OPEN */
+#define CAP_PRIVACY_ON          (1<<1) /* Off = No privacy */
+
+struct ipw2100_priv {
+
+       int stop_hang_check; /* Set 1 when shutting down to kill hang_check */
+       int stop_rf_kill; /* Set 1 when shutting down to kill rf_kill */
+
+       struct ieee80211_device *ieee;
+       unsigned long status;
+       unsigned long config;
+       unsigned long capability;
+
+       /* Statistics */
+       int resets;
+       int reset_backoff;
+
+       /* Context */
+       u8 essid[IW_ESSID_MAX_SIZE];
+       u8 essid_len;
+       u8 bssid[ETH_ALEN];
+       u8 channel;
+       int last_mode;
+       int cstate_limit;
+
+       unsigned long connect_start;
+       unsigned long last_reset;
+
+       u32 channel_mask;
+       u32 fatal_error;
+       u32 fatal_errors[IPW2100_ERROR_QUEUE];
+       u32 fatal_index;
+       int eeprom_version;
+       int firmware_version;
+       unsigned long hw_features;
+       int hangs;
+       u32 last_rtc;
+       int dump_raw; /* 1 to dump raw bytes in /sys/.../memory */
+       u8* snapshot[0x30];
+
+       u8 mandatory_bssid_mac[ETH_ALEN];
+       u8 mac_addr[ETH_ALEN];
+
+       int power_mode;
+
+       /* WEP data */
+       struct ieee80211_security sec;
+       int messages_sent;
+
+
+       int short_retry_limit;
+       int long_retry_limit;
+
+       u32 rts_threshold;
+       u32 frag_threshold;
+
+       int in_isr;
+
+       u32 tx_rates;
+       int tx_power;
+       u32 beacon_interval;
+
+       char nick[IW_ESSID_MAX_SIZE + 1];
+
+       struct ipw2100_status_queue status_queue;
+
+       struct statistic txq_stat;
+       struct statistic rxq_stat;
+       struct ipw2100_bd_queue rx_queue;
+       struct ipw2100_bd_queue tx_queue;
+       struct ipw2100_rx_packet *rx_buffers;
+
+       struct statistic fw_pend_stat;
+       struct list_head fw_pend_list;
+
+       struct statistic msg_free_stat;
+       struct statistic msg_pend_stat;
+       struct list_head msg_free_list;
+       struct list_head msg_pend_list;
+       struct ipw2100_tx_packet *msg_buffers;
+
+       struct statistic tx_free_stat;
+       struct statistic tx_pend_stat;
+       struct list_head tx_free_list;
+       struct list_head tx_pend_list;
+       struct ipw2100_tx_packet *tx_buffers;
+
+       struct ipw2100_ordinals ordinals;
+
+       struct pci_dev *pci_dev;
+
+       struct proc_dir_entry *dir_dev;
+
+       struct net_device *net_dev;
+       struct iw_statistics wstats;
+
+       struct tasklet_struct irq_tasklet;
+
+       struct workqueue_struct *workqueue;
+       struct work_struct reset_work;
+       struct work_struct security_work;
+       struct work_struct wx_event_work;
+       struct work_struct hang_check;
+       struct work_struct rf_kill;
+
+       u32 interrupts;
+       int tx_interrupts;
+       int rx_interrupts;
+       int inta_other;
+
+       spinlock_t low_lock;
+       struct semaphore action_sem;
+       struct semaphore adapter_sem;
+
+       wait_queue_head_t wait_command_queue;
+};
+
+
+/*********************************************************
+ * Host Command -> From Driver to FW
+ *********************************************************/
+
+/**
+ * Host command identifiers
+ */
+#define HOST_COMPLETE           2
+#define SYSTEM_CONFIG           6
+#define SSID                    8
+#define MANDATORY_BSSID         9
+#define AUTHENTICATION_TYPE    10
+#define ADAPTER_ADDRESS        11
+#define PORT_TYPE              12
+#define INTERNATIONAL_MODE     13
+#define CHANNEL                14
+#define RTS_THRESHOLD          15
+#define FRAG_THRESHOLD         16
+#define POWER_MODE             17
+#define TX_RATES               18
+#define BASIC_TX_RATES         19
+#define WEP_KEY_INFO           20
+#define WEP_KEY_INDEX          25
+#define WEP_FLAGS              26
+#define ADD_MULTICAST          27
+#define CLEAR_ALL_MULTICAST    28
+#define BEACON_INTERVAL        29
+#define ATIM_WINDOW            30
+#define CLEAR_STATISTICS       31
+#define SEND                  33
+#define TX_POWER_INDEX         36
+#define BROADCAST_SCAN         43
+#define CARD_DISABLE           44
+#define PREFERRED_BSSID        45
+#define SET_SCAN_OPTIONS       46
+#define SCAN_DWELL_TIME        47
+#define SWEEP_TABLE            48
+#define AP_OR_STATION_TABLE    49
+#define GROUP_ORDINALS         50
+#define SHORT_RETRY_LIMIT      51
+#define LONG_RETRY_LIMIT       52
+
+#define HOST_PRE_POWER_DOWN    58
+#define CARD_DISABLE_PHY_OFF   61
+#define MSDU_TX_RATES          62
+
+
+/* Rogue AP Detection */
+#define SET_STATION_STAT_BITS      64
+#define CLEAR_STATIONS_STAT_BITS   65
+#define LEAP_ROGUE_MODE            66  //TODO tbw replaced by CFG_LEAP_ROGUE_AP
+#define SET_SECURITY_INFORMATION   67
+#define DISASSOCIATION_BSSID      68
+#define SET_WPA_IE                 69
+
+
+
+/* system configuration bit mask: */
+#define IPW_CFG_MONITOR               0x00004
+#define IPW_CFG_PREAMBLE_AUTO        0x00010
+#define IPW_CFG_IBSS_AUTO_START     0x00020
+#define IPW_CFG_LOOPBACK            0x00100
+#define IPW_CFG_ANSWER_BCSSID_PROBE 0x00800
+#define IPW_CFG_BT_SIDEBAND_SIGNAL     0x02000
+#define IPW_CFG_802_1x_ENABLE       0x04000
+#define IPW_CFG_BSS_MASK               0x08000
+#define IPW_CFG_IBSS_MASK              0x10000
+
+#define IPW_SCAN_NOASSOCIATE (1<<0)
+#define IPW_SCAN_MIXED_CELL (1<<1)
+/* RESERVED (1<<2) */
+#define IPW_SCAN_PASSIVE (1<<3)
+
+#define IPW_NIC_FATAL_ERROR 0x2A7F0
+#define IPW_ERROR_ADDR(x) (x & 0x3FFFF)
+#define IPW_ERROR_CODE(x) ((x & 0xFF000000) >> 24)
+#define IPW2100_ERR_C3_CORRUPTION (0x10 << 24)
+#define IPW2100_ERR_MSG_TIMEOUT   (0x11 << 24)
+#define IPW2100_ERR_FW_LOAD       (0x12 << 24)
+
+#define IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND                   0x200
+#define IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND   IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0D80
+
+#define IPW_MEM_HOST_SHARED_RX_BD_BASE                  (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x40)
+#define IPW_MEM_HOST_SHARED_RX_STATUS_BASE              (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x44)
+#define IPW_MEM_HOST_SHARED_RX_BD_SIZE                  (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x48)
+#define IPW_MEM_HOST_SHARED_RX_READ_INDEX               (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0xa0)
+
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE          (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x00)
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE          (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x04)
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX       (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x80)
+
+#define IPW_MEM_HOST_SHARED_RX_WRITE_INDEX \
+    (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x20)
+
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX \
+    (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND)
+
+#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1   (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x180)
+#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2   (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x184)
+
+#define IPW2100_INTA_TX_TRANSFER               (0x00000001)    // Bit 0 (LSB)
+#define IPW2100_INTA_RX_TRANSFER               (0x00000002)    // Bit 1
+#define IPW2100_INTA_TX_COMPLETE              (0x00000004)     // Bit 2
+#define IPW2100_INTA_EVENT_INTERRUPT           (0x00000008)     // Bit 3
+#define IPW2100_INTA_STATUS_CHANGE             (0x00000010)    // Bit 4
+#define IPW2100_INTA_BEACON_PERIOD_EXPIRED     (0x00000020)    // Bit 5
+#define IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE  (0x00010000)        // Bit 16
+#define IPW2100_INTA_FW_INIT_DONE              (0x01000000)    // Bit 24
+#define IPW2100_INTA_FW_CALIBRATION_CALC       (0x02000000)    // Bit 25
+#define IPW2100_INTA_FATAL_ERROR               (0x40000000)    // Bit 30
+#define IPW2100_INTA_PARITY_ERROR              (0x80000000)    // Bit 31 (MSB)
+
+#define IPW_AUX_HOST_RESET_REG_PRINCETON_RESET              (0x00000001)
+#define IPW_AUX_HOST_RESET_REG_FORCE_NMI                    (0x00000002)
+#define IPW_AUX_HOST_RESET_REG_PCI_HOST_CLUSTER_FATAL_NMI   (0x00000004)
+#define IPW_AUX_HOST_RESET_REG_CORE_FATAL_NMI               (0x00000008)
+#define IPW_AUX_HOST_RESET_REG_SW_RESET                     (0x00000080)
+#define IPW_AUX_HOST_RESET_REG_MASTER_DISABLED              (0x00000100)
+#define IPW_AUX_HOST_RESET_REG_STOP_MASTER                  (0x00000200)
+
+#define IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY           (0x00000001)   // Bit 0 (LSB)
+#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY   (0x00000002)   // Bit 1
+#define IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE             (0x00000004)   // Bit 2
+#define IPW_AUX_HOST_GP_CNTRL_BITS_SYS_CONFIG           (0x000007c0)   // Bits 6-10
+#define IPW_AUX_HOST_GP_CNTRL_BIT_BUS_TYPE              (0x00000200)   // Bit 9
+#define IPW_AUX_HOST_GP_CNTRL_BIT_BAR0_BLOCK_SIZE       (0x00000400)   // Bit 10
+#define IPW_AUX_HOST_GP_CNTRL_BIT_USB_MODE              (0x20000000)   // Bit 29
+#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_FORCES_SYS_CLK   (0x40000000)   // Bit 30
+#define IPW_AUX_HOST_GP_CNTRL_BIT_FW_FORCES_SYS_CLK     (0x80000000)   // Bit 31 (MSB)
+
+#define IPW_BIT_GPIO_GPIO1_MASK         0x0000000C
+#define IPW_BIT_GPIO_GPIO3_MASK         0x000000C0
+#define IPW_BIT_GPIO_GPIO1_ENABLE       0x00000008
+#define IPW_BIT_GPIO_RF_KILL            0x00010000
+
+#define IPW_BIT_GPIO_LED_OFF            0x00002000     // Bit 13 = 1
+
+#define IPW_REG_DOMAIN_0_OFFSET        0x0000
+#define IPW_REG_DOMAIN_1_OFFSET        IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND
+
+#define IPW_REG_INTA                   IPW_REG_DOMAIN_0_OFFSET + 0x0008
+#define IPW_REG_INTA_MASK              IPW_REG_DOMAIN_0_OFFSET + 0x000C
+#define IPW_REG_INDIRECT_ACCESS_ADDRESS        IPW_REG_DOMAIN_0_OFFSET + 0x0010
+#define IPW_REG_INDIRECT_ACCESS_DATA   IPW_REG_DOMAIN_0_OFFSET + 0x0014
+#define IPW_REG_AUTOINCREMENT_ADDRESS  IPW_REG_DOMAIN_0_OFFSET + 0x0018
+#define IPW_REG_AUTOINCREMENT_DATA     IPW_REG_DOMAIN_0_OFFSET + 0x001C
+#define IPW_REG_RESET_REG              IPW_REG_DOMAIN_0_OFFSET + 0x0020
+#define IPW_REG_GP_CNTRL               IPW_REG_DOMAIN_0_OFFSET + 0x0024
+#define IPW_REG_GPIO                   IPW_REG_DOMAIN_0_OFFSET + 0x0030
+#define IPW_REG_FW_TYPE                 IPW_REG_DOMAIN_1_OFFSET + 0x0188
+#define IPW_REG_FW_VERSION             IPW_REG_DOMAIN_1_OFFSET + 0x018C
+#define IPW_REG_FW_COMPATABILITY_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x0190
+
+#define IPW_REG_INDIRECT_ADDR_MASK     0x00FFFFFC
+
+#define IPW_INTERRUPT_MASK             0xC1010013
+
+#define IPW2100_CONTROL_REG             0x220000
+#define IPW2100_CONTROL_PHY_OFF         0x8
+
+#define IPW2100_COMMAND                        0x00300004
+#define IPW2100_COMMAND_PHY_ON         0x0
+#define IPW2100_COMMAND_PHY_OFF                0x1
+
+/* in DEBUG_AREA, values of memory always 0xd55555d5 */
+#define IPW_REG_DOA_DEBUG_AREA_START    IPW_REG_DOMAIN_0_OFFSET + 0x0090
+#define IPW_REG_DOA_DEBUG_AREA_END      IPW_REG_DOMAIN_0_OFFSET + 0x00FF
+#define IPW_DATA_DOA_DEBUG_VALUE        0xd55555d5
+
+#define IPW_INTERNAL_REGISTER_HALT_AND_RESET   0x003000e0
+
+#define IPW_WAIT_CLOCK_STABILIZATION_DELAY         50  // micro seconds
+#define IPW_WAIT_RESET_ARC_COMPLETE_DELAY          10  // micro seconds
+#define IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY 10 // micro seconds
+
+// BD ring queue read/write difference
+#define IPW_BD_QUEUE_W_R_MIN_SPARE 2
+
+#define IPW_CACHE_LINE_LENGTH_DEFAULT              0x80
+
+#define IPW_CARD_DISABLE_PHY_OFF_COMPLETE_WAIT     100 // 100 milli
+#define IPW_PREPARE_POWER_DOWN_COMPLETE_WAIT       100 // 100 milli
+
+
+
+
+#define IPW_HEADER_802_11_SIZE          sizeof(struct ieee80211_hdr_3addr)
+#define IPW_MAX_80211_PAYLOAD_SIZE              2304U
+#define IPW_MAX_802_11_PAYLOAD_LENGTH          2312
+#define IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH     1536
+#define IPW_MIN_ACCEPTABLE_RX_FRAME_LENGTH     60
+#define IPW_MAX_ACCEPTABLE_RX_FRAME_LENGTH \
+       (IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH + IPW_HEADER_802_11_SIZE - \
+        sizeof(struct ethhdr))
+
+#define IPW_802_11_FCS_LENGTH 4
+#define IPW_RX_NIC_BUFFER_LENGTH \
+        (IPW_MAX_802_11_PAYLOAD_LENGTH + IPW_HEADER_802_11_SIZE + \
+               IPW_802_11_FCS_LENGTH)
+
+#define IPW_802_11_PAYLOAD_OFFSET \
+        (sizeof(struct ieee80211_hdr_3addr) + \
+         sizeof(struct ieee80211_snap_hdr))
+
+struct ipw2100_rx {
+       union {
+               unsigned char payload[IPW_RX_NIC_BUFFER_LENGTH];
+               struct ieee80211_hdr header;
+               u32 status;
+               struct ipw2100_notification notification;
+               struct ipw2100_cmd_header command;
+       } rx_data;
+} __attribute__ ((packed));
+
+/* Bit 0-7 are for 802.11b tx rates - .  Bit 5-7 are reserved */
+#define TX_RATE_1_MBIT              0x0001
+#define TX_RATE_2_MBIT              0x0002
+#define TX_RATE_5_5_MBIT            0x0004
+#define TX_RATE_11_MBIT             0x0008
+#define TX_RATE_MASK                0x000F
+#define DEFAULT_TX_RATES            0x000F
+
+#define IPW_POWER_MODE_CAM           0x00      //(always on)
+#define IPW_POWER_INDEX_1            0x01
+#define IPW_POWER_INDEX_2            0x02
+#define IPW_POWER_INDEX_3            0x03
+#define IPW_POWER_INDEX_4            0x04
+#define IPW_POWER_INDEX_5            0x05
+#define IPW_POWER_AUTO               0x06
+#define IPW_POWER_MASK               0x0F
+#define IPW_POWER_ENABLED            0x10
+#define IPW_POWER_LEVEL(x)           ((x) & IPW_POWER_MASK)
+
+#define IPW_TX_POWER_AUTO            0
+#define IPW_TX_POWER_ENHANCED        1
+
+#define IPW_TX_POWER_DEFAULT         32
+#define IPW_TX_POWER_MIN             0
+#define IPW_TX_POWER_MAX             16
+#define IPW_TX_POWER_MIN_DBM         (-12)
+#define IPW_TX_POWER_MAX_DBM         16
+
+#define FW_SCAN_DONOT_ASSOCIATE     0x0001 // Dont Attempt to Associate after Scan
+#define FW_SCAN_PASSIVE             0x0008 // Force PASSSIVE Scan
+
+#define REG_MIN_CHANNEL             0
+#define REG_MAX_CHANNEL             14
+
+#define REG_CHANNEL_MASK            0x00003FFF
+#define IPW_IBSS_11B_DEFAULT_MASK   0x87ff
+
+#define DIVERSITY_EITHER            0  // Use both antennas
+#define DIVERSITY_ANTENNA_A         1  // Use antenna A
+#define DIVERSITY_ANTENNA_B         2  // Use antenna B
+
+
+#define HOST_COMMAND_WAIT 0
+#define HOST_COMMAND_NO_WAIT 1
+
+#define LOCK_NONE 0
+#define LOCK_DRIVER 1
+#define LOCK_FW 2
+
+#define TYPE_SWEEP_ORD                  0x000D
+#define TYPE_IBSS_STTN_ORD              0x000E
+#define TYPE_BSS_AP_ORD                 0x000F
+#define TYPE_RAW_BEACON_ENTRY           0x0010
+#define TYPE_CALIBRATION_DATA           0x0011
+#define TYPE_ROGUE_AP_DATA              0x0012
+#define TYPE_ASSOCIATION_REQUEST       0x0013
+#define TYPE_REASSOCIATION_REQUEST     0x0014
+
+
+#define HW_FEATURE_RFKILL (0x0001)
+#define RF_KILLSWITCH_OFF (1)
+#define RF_KILLSWITCH_ON  (0)
+
+#define IPW_COMMAND_POOL_SIZE        40
+
+#define IPW_START_ORD_TAB_1                    1
+#define IPW_START_ORD_TAB_2                    1000
+
+#define IPW_ORD_TAB_1_ENTRY_SIZE               sizeof(u32)
+
+#define IS_ORDINAL_TABLE_ONE(mgr,id) \
+    ((id >= IPW_START_ORD_TAB_1) && (id < mgr->table1_size))
+#define IS_ORDINAL_TABLE_TWO(mgr,id) \
+    ((id >= IPW_START_ORD_TAB_2) && (id < (mgr->table2_size + IPW_START_ORD_TAB_2)))
+
+#define BSS_ID_LENGTH               6
+
+// Fixed size data: Ordinal Table 1
+typedef enum _ORDINAL_TABLE_1 {        // NS - means Not Supported by FW
+// Transmit statistics
+       IPW_ORD_STAT_TX_HOST_REQUESTS = 1,// # of requested Host Tx's (MSDU)
+       IPW_ORD_STAT_TX_HOST_COMPLETE,  // # of successful Host Tx's (MSDU)
+       IPW_ORD_STAT_TX_DIR_DATA,       // # of successful Directed Tx's (MSDU)
+
+       IPW_ORD_STAT_TX_DIR_DATA1 = 4,  // # of successful Directed Tx's (MSDU) @ 1MB
+       IPW_ORD_STAT_TX_DIR_DATA2,      // # of successful Directed Tx's (MSDU) @ 2MB
+       IPW_ORD_STAT_TX_DIR_DATA5_5,    // # of successful Directed Tx's (MSDU) @ 5_5MB
+       IPW_ORD_STAT_TX_DIR_DATA11,     // # of successful Directed Tx's (MSDU) @ 11MB
+       IPW_ORD_STAT_TX_DIR_DATA22,     // # of successful Directed Tx's (MSDU) @ 22MB
+
+       IPW_ORD_STAT_TX_NODIR_DATA1 = 13,// # of successful Non_Directed Tx's (MSDU) @ 1MB
+       IPW_ORD_STAT_TX_NODIR_DATA2,    // # of successful Non_Directed Tx's (MSDU) @ 2MB
+       IPW_ORD_STAT_TX_NODIR_DATA5_5,  // # of successful Non_Directed Tx's (MSDU) @ 5.5MB
+       IPW_ORD_STAT_TX_NODIR_DATA11,   // # of successful Non_Directed Tx's (MSDU) @ 11MB
+
+       IPW_ORD_STAT_NULL_DATA = 21,    // # of successful NULL data Tx's
+       IPW_ORD_STAT_TX_RTS,            // # of successful Tx RTS
+       IPW_ORD_STAT_TX_CTS,            // # of successful Tx CTS
+       IPW_ORD_STAT_TX_ACK,            // # of successful Tx ACK
+       IPW_ORD_STAT_TX_ASSN,           // # of successful Association Tx's
+       IPW_ORD_STAT_TX_ASSN_RESP,      // # of successful Association response Tx's
+       IPW_ORD_STAT_TX_REASSN,         // # of successful Reassociation Tx's
+       IPW_ORD_STAT_TX_REASSN_RESP,    // # of successful Reassociation response Tx's
+       IPW_ORD_STAT_TX_PROBE,          // # of probes successfully transmitted
+       IPW_ORD_STAT_TX_PROBE_RESP,     // # of probe responses successfully transmitted
+       IPW_ORD_STAT_TX_BEACON,         // # of tx beacon
+       IPW_ORD_STAT_TX_ATIM,           // # of Tx ATIM
+       IPW_ORD_STAT_TX_DISASSN,        // # of successful Disassociation TX
+       IPW_ORD_STAT_TX_AUTH,           // # of successful Authentication Tx
+       IPW_ORD_STAT_TX_DEAUTH,         // # of successful Deauthentication TX
+
+       IPW_ORD_STAT_TX_TOTAL_BYTES = 41,// Total successful Tx data bytes
+       IPW_ORD_STAT_TX_RETRIES,         // # of Tx retries
+       IPW_ORD_STAT_TX_RETRY1,          // # of Tx retries at 1MBPS
+       IPW_ORD_STAT_TX_RETRY2,          // # of Tx retries at 2MBPS
+       IPW_ORD_STAT_TX_RETRY5_5,        // # of Tx retries at 5.5MBPS
+       IPW_ORD_STAT_TX_RETRY11,         // # of Tx retries at 11MBPS
+
+       IPW_ORD_STAT_TX_FAILURES = 51,  // # of Tx Failures
+       IPW_ORD_STAT_TX_ABORT_AT_HOP,   //NS // # of Tx's aborted at hop time
+       IPW_ORD_STAT_TX_MAX_TRIES_IN_HOP,// # of times max tries in a hop failed
+       IPW_ORD_STAT_TX_ABORT_LATE_DMA, //NS // # of times tx aborted due to late dma setup
+       IPW_ORD_STAT_TX_ABORT_STX,      //NS // # of times backoff aborted
+       IPW_ORD_STAT_TX_DISASSN_FAIL,   // # of times disassociation failed
+       IPW_ORD_STAT_TX_ERR_CTS,         // # of missed/bad CTS frames
+       IPW_ORD_STAT_TX_BPDU,           //NS // # of spanning tree BPDUs sent
+       IPW_ORD_STAT_TX_ERR_ACK,        // # of tx err due to acks
+
+       // Receive statistics
+       IPW_ORD_STAT_RX_HOST = 61,      // # of packets passed to host
+       IPW_ORD_STAT_RX_DIR_DATA,       // # of directed packets
+       IPW_ORD_STAT_RX_DIR_DATA1,      // # of directed packets at 1MB
+       IPW_ORD_STAT_RX_DIR_DATA2,      // # of directed packets at 2MB
+       IPW_ORD_STAT_RX_DIR_DATA5_5,    // # of directed packets at 5.5MB
+       IPW_ORD_STAT_RX_DIR_DATA11,     // # of directed packets at 11MB
+       IPW_ORD_STAT_RX_DIR_DATA22,     // # of directed packets at 22MB
+
+       IPW_ORD_STAT_RX_NODIR_DATA = 71,// # of nondirected packets
+       IPW_ORD_STAT_RX_NODIR_DATA1,    // # of nondirected packets at 1MB
+       IPW_ORD_STAT_RX_NODIR_DATA2,    // # of nondirected packets at 2MB
+       IPW_ORD_STAT_RX_NODIR_DATA5_5,  // # of nondirected packets at 5.5MB
+       IPW_ORD_STAT_RX_NODIR_DATA11,   // # of nondirected packets at 11MB
+
+       IPW_ORD_STAT_RX_NULL_DATA = 80, // # of null data rx's
+       IPW_ORD_STAT_RX_POLL,   //NS // # of poll rx
+       IPW_ORD_STAT_RX_RTS,    // # of Rx RTS
+       IPW_ORD_STAT_RX_CTS,    // # of Rx CTS
+       IPW_ORD_STAT_RX_ACK,    // # of Rx ACK
+       IPW_ORD_STAT_RX_CFEND,  // # of Rx CF End
+       IPW_ORD_STAT_RX_CFEND_ACK,      // # of Rx CF End + CF Ack
+       IPW_ORD_STAT_RX_ASSN,   // # of Association Rx's
+       IPW_ORD_STAT_RX_ASSN_RESP,      // # of Association response Rx's
+       IPW_ORD_STAT_RX_REASSN, // # of Reassociation Rx's
+       IPW_ORD_STAT_RX_REASSN_RESP,    // # of Reassociation response Rx's
+       IPW_ORD_STAT_RX_PROBE,  // # of probe Rx's
+       IPW_ORD_STAT_RX_PROBE_RESP,     // # of probe response Rx's
+       IPW_ORD_STAT_RX_BEACON, // # of Rx beacon
+       IPW_ORD_STAT_RX_ATIM,   // # of Rx ATIM
+       IPW_ORD_STAT_RX_DISASSN,        // # of disassociation Rx
+       IPW_ORD_STAT_RX_AUTH,   // # of authentication Rx
+       IPW_ORD_STAT_RX_DEAUTH, // # of deauthentication Rx
+
+       IPW_ORD_STAT_RX_TOTAL_BYTES = 101,// Total rx data bytes received
+       IPW_ORD_STAT_RX_ERR_CRC,         // # of packets with Rx CRC error
+       IPW_ORD_STAT_RX_ERR_CRC1,        // # of Rx CRC errors at 1MB
+       IPW_ORD_STAT_RX_ERR_CRC2,        // # of Rx CRC errors at 2MB
+       IPW_ORD_STAT_RX_ERR_CRC5_5,      // # of Rx CRC errors at 5.5MB
+       IPW_ORD_STAT_RX_ERR_CRC11,       // # of Rx CRC errors at 11MB
+
+       IPW_ORD_STAT_RX_DUPLICATE1 = 112, // # of duplicate rx packets at 1MB
+       IPW_ORD_STAT_RX_DUPLICATE2,      // # of duplicate rx packets at 2MB
+       IPW_ORD_STAT_RX_DUPLICATE5_5,    // # of duplicate rx packets at 5.5MB
+       IPW_ORD_STAT_RX_DUPLICATE11,     // # of duplicate rx packets at 11MB
+       IPW_ORD_STAT_RX_DUPLICATE = 119, // # of duplicate rx packets
+
+       IPW_ORD_PERS_DB_LOCK = 120,     // # locking fw permanent  db
+       IPW_ORD_PERS_DB_SIZE,   // # size of fw permanent  db
+       IPW_ORD_PERS_DB_ADDR,   // # address of fw permanent  db
+       IPW_ORD_STAT_RX_INVALID_PROTOCOL,       // # of rx frames with invalid protocol
+       IPW_ORD_SYS_BOOT_TIME,  // # Boot time
+       IPW_ORD_STAT_RX_NO_BUFFER,      // # of rx frames rejected due to no buffer
+       IPW_ORD_STAT_RX_ABORT_LATE_DMA, //NS // # of rx frames rejected due to dma setup too late
+       IPW_ORD_STAT_RX_ABORT_AT_HOP,   //NS // # of rx frames aborted due to hop
+       IPW_ORD_STAT_RX_MISSING_FRAG,   // # of rx frames dropped due to missing fragment
+       IPW_ORD_STAT_RX_ORPHAN_FRAG,    // # of rx frames dropped due to non-sequential fragment
+       IPW_ORD_STAT_RX_ORPHAN_FRAME,   // # of rx frames dropped due to unmatched 1st frame
+       IPW_ORD_STAT_RX_FRAG_AGEOUT,    // # of rx frames dropped due to uncompleted frame
+       IPW_ORD_STAT_RX_BAD_SSID,       //NS // Bad SSID (unused)
+       IPW_ORD_STAT_RX_ICV_ERRORS,     // # of ICV errors during decryption
+
+// PSP Statistics
+       IPW_ORD_STAT_PSP_SUSPENSION = 137,// # of times adapter suspended
+       IPW_ORD_STAT_PSP_BCN_TIMEOUT,   // # of beacon timeout
+       IPW_ORD_STAT_PSP_POLL_TIMEOUT,  // # of poll response timeouts
+       IPW_ORD_STAT_PSP_NONDIR_TIMEOUT,// # of timeouts waiting for last broadcast/muticast pkt
+       IPW_ORD_STAT_PSP_RX_DTIMS,      // # of PSP DTIMs received
+       IPW_ORD_STAT_PSP_RX_TIMS,       // # of PSP TIMs received
+       IPW_ORD_STAT_PSP_STATION_ID,    // PSP Station ID
+
+// Association and roaming
+       IPW_ORD_LAST_ASSN_TIME = 147,   // RTC time of last association
+       IPW_ORD_STAT_PERCENT_MISSED_BCNS,// current calculation of % missed beacons
+       IPW_ORD_STAT_PERCENT_RETRIES,   // current calculation of % missed tx retries
+       IPW_ORD_ASSOCIATED_AP_PTR,      // If associated, this is ptr to the associated
+       // AP table entry. set to 0 if not associated
+       IPW_ORD_AVAILABLE_AP_CNT,       // # of AP's decsribed in the AP table
+       IPW_ORD_AP_LIST_PTR,    // Ptr to list of available APs
+       IPW_ORD_STAT_AP_ASSNS,  // # of associations
+       IPW_ORD_STAT_ASSN_FAIL, // # of association failures
+       IPW_ORD_STAT_ASSN_RESP_FAIL,    // # of failuresdue to response fail
+       IPW_ORD_STAT_FULL_SCANS,        // # of full scans
+
+       IPW_ORD_CARD_DISABLED,  // # Card Disabled
+       IPW_ORD_STAT_ROAM_INHIBIT,      // # of times roaming was inhibited due to ongoing activity
+       IPW_FILLER_40,
+       IPW_ORD_RSSI_AT_ASSN = 160,     // RSSI of associated AP at time of association
+       IPW_ORD_STAT_ASSN_CAUSE1,       // # of reassociations due to no tx from AP in last N
+       // hops or no prob_ responses in last 3 minutes
+       IPW_ORD_STAT_ASSN_CAUSE2,       // # of reassociations due to poor tx/rx quality
+       IPW_ORD_STAT_ASSN_CAUSE3,       // # of reassociations due to tx/rx quality with excessive
+       // load at the AP
+       IPW_ORD_STAT_ASSN_CAUSE4,       // # of reassociations due to AP RSSI level fell below
+       // eligible group
+       IPW_ORD_STAT_ASSN_CAUSE5,       // # of reassociations due to load leveling
+       IPW_ORD_STAT_ASSN_CAUSE6,       //NS // # of reassociations due to dropped by Ap
+       IPW_FILLER_41,
+       IPW_FILLER_42,
+       IPW_FILLER_43,
+       IPW_ORD_STAT_AUTH_FAIL, // # of times authentication failed
+       IPW_ORD_STAT_AUTH_RESP_FAIL,    // # of times authentication response failed
+       IPW_ORD_STATION_TABLE_CNT,      // # of entries in association table
+
+// Other statistics
+       IPW_ORD_RSSI_AVG_CURR = 173,    // Current avg RSSI
+       IPW_ORD_STEST_RESULTS_CURR,     //NS // Current self test results word
+       IPW_ORD_STEST_RESULTS_CUM,      //NS // Cummulative self test results word
+       IPW_ORD_SELF_TEST_STATUS,       //NS //
+       IPW_ORD_POWER_MGMT_MODE,        // Power mode - 0=CAM, 1=PSP
+       IPW_ORD_POWER_MGMT_INDEX,       //NS //
+       IPW_ORD_COUNTRY_CODE,   // IEEE country code as recv'd from beacon
+       IPW_ORD_COUNTRY_CHANNELS,       // channels suported by country
+// IPW_ORD_COUNTRY_CHANNELS:
+// For 11b the lower 2-byte are used for channels from 1-14
+//   and the higher 2-byte are not used.
+       IPW_ORD_RESET_CNT,      // # of adapter resets (warm)
+       IPW_ORD_BEACON_INTERVAL,        // Beacon interval
+
+       IPW_ORD_PRINCETON_VERSION = 184,        //NS // Princeton Version
+       IPW_ORD_ANTENNA_DIVERSITY,      // TRUE if antenna diversity is disabled
+       IPW_ORD_CCA_RSSI,       //NS // CCA RSSI value (factory programmed)
+       IPW_ORD_STAT_EEPROM_UPDATE,     //NS // # of times config EEPROM updated
+       IPW_ORD_DTIM_PERIOD,    // # of beacon intervals between DTIMs
+       IPW_ORD_OUR_FREQ,       // current radio freq lower digits - channel ID
+
+       IPW_ORD_RTC_TIME = 190, // current RTC time
+       IPW_ORD_PORT_TYPE,      // operating mode
+       IPW_ORD_CURRENT_TX_RATE,        // current tx rate
+       IPW_ORD_SUPPORTED_RATES,        // Bitmap of supported tx rates
+       IPW_ORD_ATIM_WINDOW,    // current ATIM Window
+       IPW_ORD_BASIC_RATES,    // bitmap of basic tx rates
+       IPW_ORD_NIC_HIGHEST_RATE,       // bitmap of basic tx rates
+       IPW_ORD_AP_HIGHEST_RATE,        // bitmap of basic tx rates
+       IPW_ORD_CAPABILITIES,   // Management frame capability field
+       IPW_ORD_AUTH_TYPE,      // Type of authentication
+       IPW_ORD_RADIO_TYPE,     // Adapter card platform type
+       IPW_ORD_RTS_THRESHOLD = 201,    // Min length of packet after which RTS handshaking is used
+       IPW_ORD_INT_MODE,       // International mode
+       IPW_ORD_FRAGMENTATION_THRESHOLD,        // protocol frag threshold
+       IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS,     // EEPROM offset in SRAM
+       IPW_ORD_EEPROM_SRAM_DB_BLOCK_SIZE,      // EEPROM size in SRAM
+       IPW_ORD_EEPROM_SKU_CAPABILITY,  // EEPROM SKU Capability    206 =
+       IPW_ORD_EEPROM_IBSS_11B_CHANNELS,       // EEPROM IBSS 11b channel set
+
+       IPW_ORD_MAC_VERSION = 209,      // MAC Version
+       IPW_ORD_MAC_REVISION,   // MAC Revision
+       IPW_ORD_RADIO_VERSION,  // Radio Version
+       IPW_ORD_NIC_MANF_DATE_TIME,     // MANF Date/Time STAMP
+       IPW_ORD_UCODE_VERSION,  // Ucode Version
+       IPW_ORD_HW_RF_SWITCH_STATE = 214,       // HW RF Kill Switch State
+} ORDINALTABLE1;
+
+// ordinal table 2
+// Variable length data:
+#define IPW_FIRST_VARIABLE_LENGTH_ORDINAL   1001
+
+typedef enum _ORDINAL_TABLE_2 {        // NS - means Not Supported by FW
+       IPW_ORD_STAT_BASE = 1000,       // contains number of variable ORDs
+       IPW_ORD_STAT_ADAPTER_MAC = 1001,        // 6 bytes: our adapter MAC address
+       IPW_ORD_STAT_PREFERRED_BSSID = 1002,    // 6 bytes: BSSID of the preferred AP
+       IPW_ORD_STAT_MANDATORY_BSSID = 1003,    // 6 bytes: BSSID of the mandatory AP
+       IPW_FILL_1,             //NS //
+       IPW_ORD_STAT_COUNTRY_TEXT = 1005,       // 36 bytes: Country name text, First two bytes are Country code
+       IPW_ORD_STAT_ASSN_SSID = 1006,  // 32 bytes: ESSID String
+       IPW_ORD_STATION_TABLE = 1007,   // ? bytes: Station/AP table (via Direct SSID Scans)
+       IPW_ORD_STAT_SWEEP_TABLE = 1008,        // ? bytes: Sweep/Host Table table (via Broadcast Scans)
+       IPW_ORD_STAT_ROAM_LOG = 1009,   // ? bytes: Roaming log
+       IPW_ORD_STAT_RATE_LOG = 1010,   //NS // 0 bytes: Rate log
+       IPW_ORD_STAT_FIFO = 1011,       //NS // 0 bytes: Fifo buffer data structures
+       IPW_ORD_STAT_FW_VER_NUM = 1012, // 14 bytes: fw version ID string as in (a.bb.ccc; "0.08.011")
+       IPW_ORD_STAT_FW_DATE = 1013,    // 14 bytes: fw date string (mmm dd yyyy; "Mar 13 2002")
+       IPW_ORD_STAT_ASSN_AP_BSSID = 1014,      // 6 bytes: MAC address of associated AP
+       IPW_ORD_STAT_DEBUG = 1015,      //NS // ? bytes:
+       IPW_ORD_STAT_NIC_BPA_NUM = 1016,        // 11 bytes: NIC BPA number in ASCII
+       IPW_ORD_STAT_UCODE_DATE = 1017, // 5 bytes: uCode date
+       IPW_ORD_SECURITY_NGOTIATION_RESULT = 1018,
+} ORDINALTABLE2;               // NS - means Not Supported by FW
+
+#define IPW_LAST_VARIABLE_LENGTH_ORDINAL   1018
+
+#ifndef WIRELESS_SPY
+#define WIRELESS_SPY           // enable iwspy support
+#endif
+
+#define IPW_HOST_FW_SHARED_AREA0       0x0002f200
+#define IPW_HOST_FW_SHARED_AREA0_END   0x0002f510      // 0x310 bytes
+
+#define IPW_HOST_FW_SHARED_AREA1       0x0002f610
+#define IPW_HOST_FW_SHARED_AREA1_END   0x0002f630      // 0x20 bytes
+
+#define IPW_HOST_FW_SHARED_AREA2       0x0002fa00
+#define IPW_HOST_FW_SHARED_AREA2_END   0x0002fa20      // 0x20 bytes
+
+#define IPW_HOST_FW_SHARED_AREA3       0x0002fc00
+#define IPW_HOST_FW_SHARED_AREA3_END   0x0002fc10      // 0x10 bytes
+
+#define IPW_HOST_FW_INTERRUPT_AREA     0x0002ff80
+#define IPW_HOST_FW_INTERRUPT_AREA_END         0x00030000      // 0x80 bytes
+
+struct ipw2100_fw_chunk {
+       unsigned char *buf;
+       long len;
+       long pos;
+       struct list_head list;
+};
+
+struct ipw2100_fw_chunk_set {
+       const void *data;
+       unsigned long size;
+};
+
+struct ipw2100_fw {
+       int version;
+       struct ipw2100_fw_chunk_set fw;
+       struct ipw2100_fw_chunk_set uc;
+       const struct firmware *fw_entry;
+};
+
+#define MAX_FW_VERSION_LEN 14
+
+#endif /* _IPW2100_H */
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
new file mode 100644 (file)
index 0000000..6d0b6b1
--- /dev/null
@@ -0,0 +1,7353 @@
+/******************************************************************************
+
+  Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
+
+  802.11 status code portion of this file from ethereal-0.10.6:
+    Copyright 2000, Axis Communications AB
+    Ethereal - Network traffic analyzer
+    By Gerald Combs <gerald@ethereal.com>
+    Copyright 1998 Gerald Combs
+
+  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 "ipw2200.h"
+
+#define IPW2200_VERSION "1.0.0"
+#define DRV_DESCRIPTION        "Intel(R) PRO/Wireless 2200/2915 Network Driver"
+#define DRV_COPYRIGHT  "Copyright(c) 2003-2004 Intel Corporation"
+#define DRV_VERSION     IPW2200_VERSION
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+static int channel = 0;
+static char *ifname;
+static int mode = 0;
+
+static u32 ipw_debug_level;
+static int associate = 1;
+static int auto_create = 1;
+static int disable = 0;
+static const char ipw_modes[] = {
+       'a', 'b', 'g', '?'
+};
+
+static void ipw_rx(struct ipw_priv *priv);
+static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
+                               struct clx2_tx_queue *txq, int qindex);
+static int ipw_queue_reset(struct ipw_priv *priv);
+
+static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
+                            int len, int sync);
+
+static void ipw_tx_queue_free(struct ipw_priv *);
+
+static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
+static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
+static void ipw_rx_queue_replenish(void *);
+
+static int ipw_up(struct ipw_priv *);
+static void ipw_down(struct ipw_priv *);
+static int ipw_config(struct ipw_priv *);
+static int init_supported_rates(struct ipw_priv *priv, struct ipw_supported_rates *prates);
+
+static u8 band_b_active_channel[MAX_B_CHANNELS] = {
+       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0
+};
+static u8 band_a_active_channel[MAX_A_CHANNELS] = {
+       36, 40, 44, 48, 149, 153, 157, 161, 165, 52, 56, 60, 64, 0
+};
+
+static int is_valid_channel(int mode_mask, int channel)
+{
+       int i;
+
+       if (!channel)
+               return 0;
+
+       if (mode_mask & IEEE_A)
+               for (i = 0; i < MAX_A_CHANNELS; i++)
+                       if (band_a_active_channel[i] == channel)
+                               return IEEE_A;
+
+       if (mode_mask & (IEEE_B | IEEE_G))
+               for (i = 0; i < MAX_B_CHANNELS; i++)
+                       if (band_b_active_channel[i] == channel)
+                               return mode_mask & (IEEE_B | IEEE_G);
+
+       return 0;
+}
+
+static char *snprint_line(char *buf, size_t count,
+                         const u8 *data, u32 len, u32 ofs)
+{
+       int out, i, j, l;
+       char c;
+
+       out = snprintf(buf, count, "%08X", ofs);
+
+       for (l = 0, i = 0; i < 2; i++) {
+               out += snprintf(buf + out, count - out, " ");
+               for (j = 0; j < 8 && l < len; j++, l++)
+                       out += snprintf(buf + out, count - out, "%02X ",
+                                       data[(i * 8 + j)]);
+               for (; j < 8; j++)
+                       out += snprintf(buf + out, count - out, "   ");
+       }
+
+       out += snprintf(buf + out, count - out, " ");
+       for (l = 0, i = 0; i < 2; i++) {
+               out += snprintf(buf + out, count - out, " ");
+               for (j = 0; j < 8 && l < len; j++, l++) {
+                       c = data[(i * 8 + j)];
+                       if (!isascii(c) || !isprint(c))
+                               c = '.';
+
+                       out += snprintf(buf + out, count - out, "%c", c);
+               }
+
+               for (; j < 8; j++)
+                       out += snprintf(buf + out, count - out, " ");
+       }
+
+       return buf;
+}
+
+static void printk_buf(int level, const u8 *data, u32 len)
+{
+       char line[81];
+       u32 ofs = 0;
+       if (!(ipw_debug_level & level))
+               return;
+
+       while (len) {
+               printk(KERN_DEBUG "%s\n",
+                      snprint_line(line, sizeof(line), &data[ofs],
+                                   min(len, 16U), ofs));
+               ofs += 16;
+               len -= min(len, 16U);
+       }
+}
+
+static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
+#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
+
+static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
+#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
+
+static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
+static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
+{
+       IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(b), (u32)(c));
+       _ipw_write_reg8(a, b, c);
+}
+
+static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
+static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
+{
+       IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(b), (u32)(c));
+       _ipw_write_reg16(a, b, c);
+}
+
+static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
+static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
+{
+       IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(b), (u32)(c));
+       _ipw_write_reg32(a, b, c);
+}
+
+#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
+#define ipw_write8(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write8(ipw, ofs, val)
+
+#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
+#define ipw_write16(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write16(ipw, ofs, val)
+
+#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
+#define ipw_write32(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write32(ipw, ofs, val)
+
+#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
+static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs) {
+       IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32)(ofs));
+       return _ipw_read8(ipw, ofs);
+}
+#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
+
+#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
+static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs) {
+       IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32)(ofs));
+       return _ipw_read16(ipw, ofs);
+}
+#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
+
+#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
+static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs) {
+       IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32)(ofs));
+       return _ipw_read32(ipw, ofs);
+}
+#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
+
+static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
+#define ipw_read_indirect(a, b, c, d) \
+       IPW_DEBUG_IO("%s %d: read_inddirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
+       _ipw_read_indirect(a, b, c, d)
+
+static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 *data, int num);
+#define ipw_write_indirect(a, b, c, d) \
+       IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
+        _ipw_write_indirect(a, b, c, d)
+
+/* indirect write s */
+static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg,
+                            u32 value)
+{
+       IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n",
+                    priv, reg, value);
+       _ipw_write32(priv, CX2_INDIRECT_ADDR, reg);
+       _ipw_write32(priv, CX2_INDIRECT_DATA, value);
+}
+
+
+static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
+{
+       IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
+       _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
+       _ipw_write8(priv, CX2_INDIRECT_DATA, value);
+       IPW_DEBUG_IO(" reg = 0x%8lX : value = 0x%8X\n",
+                    (unsigned long)(priv->hw_base + CX2_INDIRECT_DATA),
+                    value);
+}
+
+static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg,
+                            u16 value)
+{
+       IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
+       _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
+       _ipw_write16(priv, CX2_INDIRECT_DATA, value);
+}
+
+/* indirect read s */
+
+static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
+{
+       u32 word;
+       _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
+       IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
+       word = _ipw_read32(priv, CX2_INDIRECT_DATA);
+       return (word >> ((reg & 0x3)*8)) & 0xff;
+}
+
+static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
+{
+       u32 value;
+
+       IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
+
+       _ipw_write32(priv, CX2_INDIRECT_ADDR, reg);
+       value = _ipw_read32(priv, CX2_INDIRECT_DATA);
+       IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
+       return value;
+}
+
+/* iterative/auto-increment 32 bit reads and writes */
+static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
+                              int num)
+{
+       u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK;
+       u32 dif_len = addr - aligned_addr;
+       u32 aligned_len;
+       u32 i;
+
+       IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
+
+       /* Read the first nibble byte by byte */
+       if (unlikely(dif_len)) {
+               /* Start reading at aligned_addr + dif_len */
+               _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+               for (i = dif_len; i < 4; i++, buf++)
+                       *buf = _ipw_read8(priv, CX2_INDIRECT_DATA + i);
+               num -= dif_len;
+               aligned_addr += 4;
+       }
+
+       /* Read DWs through autoinc register */
+       _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
+       aligned_len = num & CX2_INDIRECT_ADDR_MASK;
+       for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+               *(u32*)buf = ipw_read32(priv, CX2_AUTOINC_DATA);
+
+       /* Copy the last nibble */
+       dif_len = num - aligned_len;
+       _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+       for (i = 0; i < dif_len; i++, buf++)
+               *buf = ipw_read8(priv, CX2_INDIRECT_DATA + i);
+}
+
+static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 *buf,
+                               int num)
+{
+       u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK;
+       u32 dif_len = addr - aligned_addr;
+       u32 aligned_len;
+       u32 i;
+
+       IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
+
+       /* Write the first nibble byte by byte */
+       if (unlikely(dif_len)) {
+               /* Start writing at aligned_addr + dif_len */
+               _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+               for (i = dif_len; i < 4; i++, buf++)
+                       _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
+               num -= dif_len;
+               aligned_addr += 4;
+       }
+
+       /* Write DWs through autoinc register */
+       _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
+       aligned_len = num & CX2_INDIRECT_ADDR_MASK;
+       for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+               _ipw_write32(priv, CX2_AUTOINC_DATA, *(u32*)buf);
+
+       /* Copy the last nibble */
+       dif_len = num - aligned_len;
+       _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+       for (i = 0; i < dif_len; i++, buf++)
+               _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
+}
+
+static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
+                            int num)
+{
+       memcpy_toio((priv->hw_base + addr), buf, num);
+}
+
+static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
+{
+       ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
+}
+
+static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
+{
+       ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
+}
+
+static inline void ipw_enable_interrupts(struct ipw_priv *priv)
+{
+       if (priv->status & STATUS_INT_ENABLED)
+               return;
+       priv->status |= STATUS_INT_ENABLED;
+       ipw_write32(priv, CX2_INTA_MASK_R, CX2_INTA_MASK_ALL);
+}
+
+static inline void ipw_disable_interrupts(struct ipw_priv *priv)
+{
+       if (!(priv->status & STATUS_INT_ENABLED))
+               return;
+       priv->status &= ~STATUS_INT_ENABLED;
+       ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
+}
+
+static char *ipw_error_desc(u32 val)
+{
+       switch (val) {
+       case IPW_FW_ERROR_OK:
+               return "ERROR_OK";
+       case IPW_FW_ERROR_FAIL:
+               return "ERROR_FAIL";
+       case IPW_FW_ERROR_MEMORY_UNDERFLOW:
+               return "MEMORY_UNDERFLOW";
+       case IPW_FW_ERROR_MEMORY_OVERFLOW:
+               return "MEMORY_OVERFLOW";
+       case IPW_FW_ERROR_BAD_PARAM:
+               return "ERROR_BAD_PARAM";
+       case IPW_FW_ERROR_BAD_CHECKSUM:
+               return "ERROR_BAD_CHECKSUM";
+       case IPW_FW_ERROR_NMI_INTERRUPT:
+               return "ERROR_NMI_INTERRUPT";
+       case IPW_FW_ERROR_BAD_DATABASE:
+               return "ERROR_BAD_DATABASE";
+       case IPW_FW_ERROR_ALLOC_FAIL:
+               return "ERROR_ALLOC_FAIL";
+       case IPW_FW_ERROR_DMA_UNDERRUN:
+               return "ERROR_DMA_UNDERRUN";
+       case IPW_FW_ERROR_DMA_STATUS:
+               return "ERROR_DMA_STATUS";
+       case IPW_FW_ERROR_DINOSTATUS_ERROR:
+               return "ERROR_DINOSTATUS_ERROR";
+       case IPW_FW_ERROR_EEPROMSTATUS_ERROR:
+               return "ERROR_EEPROMSTATUS_ERROR";
+       case IPW_FW_ERROR_SYSASSERT:
+               return "ERROR_SYSASSERT";
+       case IPW_FW_ERROR_FATAL_ERROR:
+               return "ERROR_FATALSTATUS_ERROR";
+       default:
+               return "UNKNOWNSTATUS_ERROR";
+       }
+}
+
+static void ipw_dump_nic_error_log(struct ipw_priv *priv)
+{
+       u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base;
+
+       base = ipw_read32(priv, IPWSTATUS_ERROR_LOG);
+       count = ipw_read_reg32(priv, base);
+
+       if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
+               IPW_ERROR("Start IPW Error Log Dump:\n");
+               IPW_ERROR("Status: 0x%08X, Config: %08X\n",
+                         priv->status, priv->config);
+       }
+
+       for (i = ERROR_START_OFFSET;
+            i <= count * ERROR_ELEM_SIZE;
+            i += ERROR_ELEM_SIZE) {
+               desc   = ipw_read_reg32(priv, base + i);
+               time   = ipw_read_reg32(priv, base + i + 1*sizeof(u32));
+               blink1 = ipw_read_reg32(priv, base + i + 2*sizeof(u32));
+               blink2 = ipw_read_reg32(priv, base + i + 3*sizeof(u32));
+               ilink1 = ipw_read_reg32(priv, base + i + 4*sizeof(u32));
+               ilink2 = ipw_read_reg32(priv, base + i + 5*sizeof(u32));
+               idata =  ipw_read_reg32(priv, base + i + 6*sizeof(u32));
+
+               IPW_ERROR(
+                       "%s %i 0x%08x  0x%08x  0x%08x  0x%08x  0x%08x\n",
+                       ipw_error_desc(desc), time, blink1, blink2,
+                       ilink1, ilink2, idata);
+       }
+}
+
+static void ipw_dump_nic_event_log(struct ipw_priv *priv)
+{
+       u32 ev, time, data, i, count, base;
+
+       base = ipw_read32(priv, IPW_EVENT_LOG);
+       count = ipw_read_reg32(priv, base);
+
+       if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE)
+               IPW_ERROR("Start IPW Event Log Dump:\n");
+
+       for (i = EVENT_START_OFFSET;
+            i <= count * EVENT_ELEM_SIZE;
+            i += EVENT_ELEM_SIZE) {
+               ev = ipw_read_reg32(priv, base + i);
+               time  = ipw_read_reg32(priv, base + i + 1*sizeof(u32));
+               data  = ipw_read_reg32(priv, base + i + 2*sizeof(u32));
+
+#ifdef CONFIG_IPW_DEBUG
+               IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);
+#endif
+       }
+}
+
+static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val,
+                          u32 *len)
+{
+       u32 addr, field_info, field_len, field_count, total_len;
+
+       IPW_DEBUG_ORD("ordinal = %i\n", ord);
+
+       if (!priv || !val || !len) {
+               IPW_DEBUG_ORD("Invalid argument\n");
+               return -EINVAL;
+       }
+
+       /* verify device ordinal tables have been initialized */
+       if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
+               IPW_DEBUG_ORD("Access ordinals before initialization\n");
+               return -EINVAL;
+       }
+
+       switch (IPW_ORD_TABLE_ID_MASK & ord) {
+       case IPW_ORD_TABLE_0_MASK:
+               /*
+                * TABLE 0: Direct access to a table of 32 bit values
+                *
+                * This is a very simple table with the data directly
+                * read from the table
+                */
+
+               /* remove the table id from the ordinal */
+               ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+               /* boundary check */
+               if (ord > priv->table0_len) {
+                       IPW_DEBUG_ORD("ordinal value (%i) longer then "
+                                     "max (%i)\n", ord, priv->table0_len);
+                       return -EINVAL;
+               }
+
+               /* verify we have enough room to store the value */
+               if (*len < sizeof(u32)) {
+                       IPW_DEBUG_ORD("ordinal buffer length too small, "
+                                     "need %zd\n", sizeof(u32));
+                       return -EINVAL;
+               }
+
+               IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
+                             ord, priv->table0_addr + (ord  << 2));
+
+               *len = sizeof(u32);
+               ord <<= 2;
+               *((u32 *)val) = ipw_read32(priv, priv->table0_addr + ord);
+               break;
+
+       case IPW_ORD_TABLE_1_MASK:
+               /*
+                * TABLE 1: Indirect access to a table of 32 bit values
+                *
+                * This is a fairly large table of u32 values each
+                * representing starting addr for the data (which is
+                * also a u32)
+                */
+
+               /* remove the table id from the ordinal */
+               ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+               /* boundary check */
+               if (ord > priv->table1_len) {
+                       IPW_DEBUG_ORD("ordinal value too long\n");
+                       return -EINVAL;
+               }
+
+               /* verify we have enough room to store the value */
+               if (*len < sizeof(u32)) {
+                       IPW_DEBUG_ORD("ordinal buffer length too small, "
+                                     "need %zd\n", sizeof(u32));
+                       return -EINVAL;
+               }
+
+               *((u32 *)val) = ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
+               *len = sizeof(u32);
+               break;
+
+       case IPW_ORD_TABLE_2_MASK:
+               /*
+                * TABLE 2: Indirect access to a table of variable sized values
+                *
+                * This table consist of six values, each containing
+                *     - dword containing the starting offset of the data
+                *     - dword containing the lengh in the first 16bits
+                *       and the count in the second 16bits
+                */
+
+               /* remove the table id from the ordinal */
+               ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+               /* boundary check */
+               if (ord > priv->table2_len) {
+                       IPW_DEBUG_ORD("ordinal value too long\n");
+                       return -EINVAL;
+               }
+
+               /* get the address of statistic */
+               addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
+
+               /* get the second DW of statistics ;
+                * two 16-bit words - first is length, second is count */
+               field_info = ipw_read_reg32(priv, priv->table2_addr + (ord << 3) + sizeof(u32));
+
+               /* get each entry length */
+               field_len = *((u16 *)&field_info);
+
+               /* get number of entries */
+               field_count = *(((u16 *)&field_info) + 1);
+
+               /* abort if not enought memory */
+               total_len = field_len * field_count;
+               if (total_len > *len) {
+                       *len = total_len;
+                       return -EINVAL;
+               }
+
+               *len = total_len;
+               if (!total_len)
+                       return 0;
+
+               IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
+                             "field_info = 0x%08x\n",
+                             addr, total_len, field_info);
+               ipw_read_indirect(priv, addr, val, total_len);
+               break;
+
+       default:
+               IPW_DEBUG_ORD("Invalid ordinal!\n");
+               return -EINVAL;
+
+       }
+
+
+       return 0;
+}
+
+static void ipw_init_ordinals(struct ipw_priv *priv)
+{
+       priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
+       priv->table0_len = ipw_read32(priv, priv->table0_addr);
+
+       IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
+                     priv->table0_addr, priv->table0_len);
+
+       priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
+       priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
+
+       IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
+                     priv->table1_addr, priv->table1_len);
+
+       priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
+       priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
+       priv->table2_len &= 0x0000ffff; /* use first two bytes */
+
+       IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
+                     priv->table2_addr, priv->table2_len);
+
+}
+
+/*
+ * The following adds a new attribute to the sysfs representation
+ * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
+ * used for controling the debug level.
+ *
+ * See the level definitions in ipw for details.
+ */
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
+{
+       return sprintf(buf, "0x%08X\n", ipw_debug_level);
+}
+static ssize_t store_debug_level(struct device_driver *d,
+                               const char *buf, size_t count)
+{
+       char *p = (char *)buf;
+       u32 val;
+
+       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
+               ipw_debug_level = val;
+
+       return strnlen(buf, count);
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
+                  show_debug_level, store_debug_level);
+
+static ssize_t show_status(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       struct ipw_priv *p = d->driver_data;
+       return sprintf(buf, "0x%08x\n", (int)p->status);
+}
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
+                       char *buf)
+{
+       struct ipw_priv *p = d->driver_data;
+       return sprintf(buf, "0x%08x\n", (int)p->config);
+}
+static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+
+static ssize_t show_nic_type(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       struct ipw_priv *p = d->driver_data;
+       u8 type = p->eeprom[EEPROM_NIC_TYPE];
+
+       switch (type) {
+       case EEPROM_NIC_TYPE_STANDARD:
+               return sprintf(buf, "STANDARD\n");
+       case EEPROM_NIC_TYPE_DELL:
+               return sprintf(buf, "DELL\n");
+       case EEPROM_NIC_TYPE_FUJITSU:
+               return sprintf(buf, "FUJITSU\n");
+       case EEPROM_NIC_TYPE_IBM:
+               return sprintf(buf, "IBM\n");
+       case EEPROM_NIC_TYPE_HP:
+               return sprintf(buf, "HP\n");
+       }
+
+       return sprintf(buf, "UNKNOWN\n");
+}
+static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
+
+static ssize_t dump_error_log(struct device *d,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       char *p = (char *)buf;
+
+       if (p[0] == '1')
+               ipw_dump_nic_error_log((struct ipw_priv*)d->driver_data);
+
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
+
+static ssize_t dump_event_log(struct device *d,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       char *p = (char *)buf;
+
+       if (p[0] == '1')
+               ipw_dump_nic_event_log((struct ipw_priv*)d->driver_data);
+
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
+
+static ssize_t show_ucode_version(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 len = sizeof(u32), tmp = 0;
+       struct ipw_priv *p = d->driver_data;
+
+       if(ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
+               return 0;
+
+       return sprintf(buf, "0x%08x\n", tmp);
+}
+static DEVICE_ATTR(ucode_version, S_IWUSR|S_IRUGO, show_ucode_version, NULL);
+
+static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
+                       char *buf)
+{
+       u32 len = sizeof(u32), tmp = 0;
+       struct ipw_priv *p = d->driver_data;
+
+       if(ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
+               return 0;
+
+       return sprintf(buf, "0x%08x\n", tmp);
+}
+static DEVICE_ATTR(rtc, S_IWUSR|S_IRUGO, show_rtc, NULL);
+
+/*
+ * Add a device attribute to view/control the delay between eeprom
+ * operations.
+ */
+static ssize_t show_eeprom_delay(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       int n = ((struct ipw_priv*)d->driver_data)->eeprom_delay;
+       return sprintf(buf, "%i\n", n);
+}
+static ssize_t store_eeprom_delay(struct device *d,
+                       struct device_attribute *attr, const char *buf,
+                       size_t count)
+{
+       struct ipw_priv *p = d->driver_data;
+       sscanf(buf, "%i", &p->eeprom_delay);
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(eeprom_delay, S_IWUSR|S_IRUGO,
+                  show_eeprom_delay,store_eeprom_delay);
+
+static ssize_t show_command_event_reg(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 reg = 0;
+       struct ipw_priv *p = d->driver_data;
+
+       reg = ipw_read_reg32(p, CX2_INTERNAL_CMD_EVENT);
+       return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_command_event_reg(struct device *d,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       u32 reg;
+       struct ipw_priv *p = d->driver_data;
+
+       sscanf(buf, "%x", &reg);
+       ipw_write_reg32(p, CX2_INTERNAL_CMD_EVENT, reg);
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(command_event_reg, S_IWUSR|S_IRUGO,
+                  show_command_event_reg,store_command_event_reg);
+
+static ssize_t show_mem_gpio_reg(struct device *d,
+                               struct device_attribute *attr, char *buf)
+{
+       u32 reg = 0;
+       struct ipw_priv *p = d->driver_data;
+
+       reg = ipw_read_reg32(p, 0x301100);
+       return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_mem_gpio_reg(struct device *d,
+                       struct device_attribute *attr, const char *buf,
+                       size_t count)
+{
+       u32 reg;
+       struct ipw_priv *p = d->driver_data;
+
+       sscanf(buf, "%x", &reg);
+       ipw_write_reg32(p, 0x301100, reg);
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(mem_gpio_reg, S_IWUSR|S_IRUGO,
+                  show_mem_gpio_reg,store_mem_gpio_reg);
+
+static ssize_t show_indirect_dword(struct device *d,
+                               struct device_attribute *attr, char *buf)
+{
+       u32 reg = 0;
+       struct ipw_priv *priv = d->driver_data;
+       if (priv->status & STATUS_INDIRECT_DWORD)
+               reg = ipw_read_reg32(priv, priv->indirect_dword);
+       else
+               reg = 0;
+
+       return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_indirect_dword(struct device *d,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       struct ipw_priv *priv = d->driver_data;
+
+       sscanf(buf, "%x", &priv->indirect_dword);
+       priv->status |= STATUS_INDIRECT_DWORD;
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(indirect_dword, S_IWUSR|S_IRUGO,
+                  show_indirect_dword,store_indirect_dword);
+
+static ssize_t show_indirect_byte(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       u8 reg = 0;
+       struct ipw_priv *priv = d->driver_data;
+       if (priv->status & STATUS_INDIRECT_BYTE)
+               reg = ipw_read_reg8(priv, priv->indirect_byte);
+       else
+               reg = 0;
+
+       return sprintf(buf, "0x%02x\n", reg);
+}
+static ssize_t store_indirect_byte(struct device *d,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       struct ipw_priv *priv = d->driver_data;
+
+       sscanf(buf, "%x", &priv->indirect_byte);
+       priv->status |= STATUS_INDIRECT_BYTE;
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(indirect_byte, S_IWUSR|S_IRUGO,
+                  show_indirect_byte, store_indirect_byte);
+
+static ssize_t show_direct_dword(struct device *d,
+                               struct device_attribute *attr, char *buf)
+{
+       u32 reg = 0;
+       struct ipw_priv *priv = d->driver_data;
+
+       if (priv->status & STATUS_DIRECT_DWORD)
+               reg = ipw_read32(priv, priv->direct_dword);
+       else
+               reg = 0;
+
+       return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_direct_dword(struct device *d,
+                       struct device_attribute *attr, const char *buf,
+                       size_t count)
+{
+       struct ipw_priv *priv = d->driver_data;
+
+       sscanf(buf, "%x", &priv->direct_dword);
+       priv->status |= STATUS_DIRECT_DWORD;
+       return strnlen(buf, count);
+}
+static DEVICE_ATTR(direct_dword, S_IWUSR|S_IRUGO,
+                  show_direct_dword,store_direct_dword);
+
+
+static inline int rf_kill_active(struct ipw_priv *priv)
+{
+       if (0 == (ipw_read32(priv, 0x30) & 0x10000))
+               priv->status |= STATUS_RF_KILL_HW;
+       else
+               priv->status &= ~STATUS_RF_KILL_HW;
+
+       return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
+}
+
+static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
+                               char *buf)
+{
+       /* 0 - RF kill not enabled
+          1 - SW based RF kill active (sysfs)
+          2 - HW based RF kill active
+          3 - Both HW and SW baed RF kill active */
+       struct ipw_priv *priv = d->driver_data;
+       int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
+               (rf_kill_active(priv) ? 0x2 : 0x0);
+       return sprintf(buf, "%i\n", val);
+}
+
+static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
+{
+       if ((disable_radio ? 1 : 0) ==
+           (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
+               return 0 ;
+
+       IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO  %s\n",
+                         disable_radio ? "OFF" : "ON");
+
+       if (disable_radio) {
+               priv->status |= STATUS_RF_KILL_SW;
+
+               if (priv->workqueue) {
+                       cancel_delayed_work(&priv->request_scan);
+               }
+               wake_up_interruptible(&priv->wait_command_queue);
+               queue_work(priv->workqueue, &priv->down);
+       } else {
+               priv->status &= ~STATUS_RF_KILL_SW;
+               if (rf_kill_active(priv)) {
+                       IPW_DEBUG_RF_KILL("Can not turn radio back on - "
+                                         "disabled by HW switch\n");
+                       /* Make sure the RF_KILL check timer is running */
+                       cancel_delayed_work(&priv->rf_kill);
+                       queue_delayed_work(priv->workqueue, &priv->rf_kill,
+                                          2 * HZ);
+               } else
+                       queue_work(priv->workqueue, &priv->up);
+       }
+
+       return 1;
+}
+
+static ssize_t store_rf_kill(struct device *d,  struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct ipw_priv *priv = d->driver_data;
+
+       ipw_radio_kill_sw(priv, buf[0] == '1');
+
+       return count;
+}
+static DEVICE_ATTR(rf_kill, S_IWUSR|S_IRUGO, show_rf_kill, store_rf_kill);
+
+static void ipw_irq_tasklet(struct ipw_priv *priv)
+{
+       u32 inta, inta_mask, handled = 0;
+       unsigned long flags;
+       int rc = 0;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       inta = ipw_read32(priv, CX2_INTA_RW);
+       inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
+       inta &= (CX2_INTA_MASK_ALL & inta_mask);
+
+       /* Add any cached INTA values that need to be handled */
+       inta |= priv->isr_inta;
+
+       /* handle all the justifications for the interrupt */
+       if (inta & CX2_INTA_BIT_RX_TRANSFER) {
+               ipw_rx(priv);
+               handled |= CX2_INTA_BIT_RX_TRANSFER;
+       }
+
+       if (inta & CX2_INTA_BIT_TX_CMD_QUEUE) {
+               IPW_DEBUG_HC("Command completed.\n");
+               rc = ipw_queue_tx_reclaim( priv, &priv->txq_cmd, -1);
+               priv->status &= ~STATUS_HCMD_ACTIVE;
+               wake_up_interruptible(&priv->wait_command_queue);
+               handled |= CX2_INTA_BIT_TX_CMD_QUEUE;
+       }
+
+       if (inta & CX2_INTA_BIT_TX_QUEUE_1) {
+               IPW_DEBUG_TX("TX_QUEUE_1\n");
+               rc = ipw_queue_tx_reclaim( priv, &priv->txq[0], 0);
+               handled |= CX2_INTA_BIT_TX_QUEUE_1;
+       }
+
+       if (inta & CX2_INTA_BIT_TX_QUEUE_2) {
+               IPW_DEBUG_TX("TX_QUEUE_2\n");
+               rc = ipw_queue_tx_reclaim( priv, &priv->txq[1], 1);
+               handled |= CX2_INTA_BIT_TX_QUEUE_2;
+       }
+
+       if (inta & CX2_INTA_BIT_TX_QUEUE_3) {
+               IPW_DEBUG_TX("TX_QUEUE_3\n");
+               rc = ipw_queue_tx_reclaim( priv, &priv->txq[2], 2);
+               handled |= CX2_INTA_BIT_TX_QUEUE_3;
+       }
+
+       if (inta & CX2_INTA_BIT_TX_QUEUE_4) {
+               IPW_DEBUG_TX("TX_QUEUE_4\n");
+               rc = ipw_queue_tx_reclaim( priv, &priv->txq[3], 3);
+               handled |= CX2_INTA_BIT_TX_QUEUE_4;
+       }
+
+       if (inta & CX2_INTA_BIT_STATUS_CHANGE) {
+               IPW_WARNING("STATUS_CHANGE\n");
+               handled |= CX2_INTA_BIT_STATUS_CHANGE;
+       }
+
+       if (inta & CX2_INTA_BIT_BEACON_PERIOD_EXPIRED) {
+               IPW_WARNING("TX_PERIOD_EXPIRED\n");
+               handled |= CX2_INTA_BIT_BEACON_PERIOD_EXPIRED;
+       }
+
+       if (inta & CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
+               IPW_WARNING("HOST_CMD_DONE\n");
+               handled |= CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
+       }
+
+       if (inta & CX2_INTA_BIT_FW_INITIALIZATION_DONE) {
+               IPW_WARNING("FW_INITIALIZATION_DONE\n");
+               handled |= CX2_INTA_BIT_FW_INITIALIZATION_DONE;
+       }
+
+       if (inta & CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
+               IPW_WARNING("PHY_OFF_DONE\n");
+               handled |= CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
+       }
+
+       if (inta & CX2_INTA_BIT_RF_KILL_DONE) {
+               IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
+               priv->status |= STATUS_RF_KILL_HW;
+               wake_up_interruptible(&priv->wait_command_queue);
+               netif_carrier_off(priv->net_dev);
+               netif_stop_queue(priv->net_dev);
+               cancel_delayed_work(&priv->request_scan);
+               queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
+               handled |= CX2_INTA_BIT_RF_KILL_DONE;
+       }
+
+       if (inta & CX2_INTA_BIT_FATAL_ERROR) {
+               IPW_ERROR("Firmware error detected.  Restarting.\n");
+#ifdef CONFIG_IPW_DEBUG
+               if (ipw_debug_level & IPW_DL_FW_ERRORS) {
+                       ipw_dump_nic_error_log(priv);
+                       ipw_dump_nic_event_log(priv);
+               }
+#endif
+               queue_work(priv->workqueue, &priv->adapter_restart);
+               handled |= CX2_INTA_BIT_FATAL_ERROR;
+       }
+
+       if (inta & CX2_INTA_BIT_PARITY_ERROR) {
+               IPW_ERROR("Parity error\n");
+               handled |= CX2_INTA_BIT_PARITY_ERROR;
+       }
+
+       if (handled != inta) {
+               IPW_ERROR("Unhandled INTA bits 0x%08x\n",
+                               inta & ~handled);
+       }
+
+       /* enable all interrupts */
+       ipw_enable_interrupts(priv);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
+static char *get_cmd_string(u8 cmd)
+{
+       switch (cmd) {
+               IPW_CMD(HOST_COMPLETE);
+               IPW_CMD(POWER_DOWN);
+               IPW_CMD(SYSTEM_CONFIG);
+               IPW_CMD(MULTICAST_ADDRESS);
+               IPW_CMD(SSID);
+               IPW_CMD(ADAPTER_ADDRESS);
+               IPW_CMD(PORT_TYPE);
+               IPW_CMD(RTS_THRESHOLD);
+               IPW_CMD(FRAG_THRESHOLD);
+               IPW_CMD(POWER_MODE);
+               IPW_CMD(WEP_KEY);
+               IPW_CMD(TGI_TX_KEY);
+               IPW_CMD(SCAN_REQUEST);
+               IPW_CMD(SCAN_REQUEST_EXT);
+               IPW_CMD(ASSOCIATE);
+               IPW_CMD(SUPPORTED_RATES);
+               IPW_CMD(SCAN_ABORT);
+               IPW_CMD(TX_FLUSH);
+               IPW_CMD(QOS_PARAMETERS);
+               IPW_CMD(DINO_CONFIG);
+               IPW_CMD(RSN_CAPABILITIES);
+               IPW_CMD(RX_KEY);
+               IPW_CMD(CARD_DISABLE);
+               IPW_CMD(SEED_NUMBER);
+               IPW_CMD(TX_POWER);
+               IPW_CMD(COUNTRY_INFO);
+               IPW_CMD(AIRONET_INFO);
+               IPW_CMD(AP_TX_POWER);
+               IPW_CMD(CCKM_INFO);
+               IPW_CMD(CCX_VER_INFO);
+               IPW_CMD(SET_CALIBRATION);
+               IPW_CMD(SENSITIVITY_CALIB);
+               IPW_CMD(RETRY_LIMIT);
+               IPW_CMD(IPW_PRE_POWER_DOWN);
+               IPW_CMD(VAP_BEACON_TEMPLATE);
+               IPW_CMD(VAP_DTIM_PERIOD);
+               IPW_CMD(EXT_SUPPORTED_RATES);
+               IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
+               IPW_CMD(VAP_QUIET_INTERVALS);
+               IPW_CMD(VAP_CHANNEL_SWITCH);
+               IPW_CMD(VAP_MANDATORY_CHANNELS);
+               IPW_CMD(VAP_CELL_PWR_LIMIT);
+               IPW_CMD(VAP_CF_PARAM_SET);
+               IPW_CMD(VAP_SET_BEACONING_STATE);
+               IPW_CMD(MEASUREMENT);
+               IPW_CMD(POWER_CAPABILITY);
+               IPW_CMD(SUPPORTED_CHANNELS);
+               IPW_CMD(TPC_REPORT);
+               IPW_CMD(WME_INFO);
+               IPW_CMD(PRODUCTION_COMMAND);
+       default:
+               return "UNKNOWN";
+       }
+}
+#endif /* CONFIG_IPW_DEBUG */
+
+#define HOST_COMPLETE_TIMEOUT HZ
+static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
+{
+       int rc = 0;
+
+       if (priv->status & STATUS_HCMD_ACTIVE) {
+               IPW_ERROR("Already sending a command\n");
+               return -1;
+       }
+
+       priv->status |= STATUS_HCMD_ACTIVE;
+
+       IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
+                    get_cmd_string(cmd->cmd), cmd->cmd, cmd->len);
+       printk_buf(IPW_DL_HOST_COMMAND, (u8*)cmd->param, cmd->len);
+
+       rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
+       if (rc)
+               return rc;
+
+       rc = wait_event_interruptible_timeout(
+               priv->wait_command_queue, !(priv->status & STATUS_HCMD_ACTIVE),
+               HOST_COMPLETE_TIMEOUT);
+       if (rc == 0) {
+               IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
+                              jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
+               priv->status &= ~STATUS_HCMD_ACTIVE;
+               return -EIO;
+       }
+       if (priv->status & STATUS_RF_KILL_MASK) {
+               IPW_DEBUG_INFO("Command aborted due to RF Kill Switch\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int ipw_send_host_complete(struct ipw_priv *priv)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_HOST_COMPLETE,
+               .len = 0
+       };
+
+       if (!priv) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send HOST_COMPLETE command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_system_config(struct ipw_priv *priv,
+                                 struct ipw_sys_config *config)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_SYSTEM_CONFIG,
+               .len = sizeof(*config)
+       };
+
+       if (!priv || !config) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param,config,sizeof(*config));
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send SYSTEM_CONFIG command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_ssid(struct ipw_priv *priv, u8 *ssid, int len)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_SSID,
+               .len = min(len, IW_ESSID_MAX_SIZE)
+       };
+
+       if (!priv || !ssid) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param, ssid, cmd.len);
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send SSID command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_adapter_address(struct ipw_priv *priv, u8 *mac)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_ADAPTER_ADDRESS,
+               .len = ETH_ALEN
+       };
+
+       if (!priv || !mac) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
+                      priv->net_dev->name, MAC_ARG(mac));
+
+       memcpy(&cmd.param, mac, ETH_ALEN);
+
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send ADAPTER_ADDRESS command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static void ipw_adapter_restart(void *adapter)
+{
+       struct ipw_priv *priv = adapter;
+
+       if (priv->status & STATUS_RF_KILL_MASK)
+               return;
+
+       ipw_down(priv);
+       if (ipw_up(priv)) {
+               IPW_ERROR("Failed to up device\n");
+               return;
+       }
+}
+
+
+
+
+#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
+
+static void ipw_scan_check(void *data)
+{
+       struct ipw_priv *priv = data;
+       if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
+               IPW_DEBUG_SCAN("Scan completion watchdog resetting "
+                              "adapter (%dms).\n",
+                              IPW_SCAN_CHECK_WATCHDOG / 100);
+               ipw_adapter_restart(priv);
+       }
+}
+
+static int ipw_send_scan_request_ext(struct ipw_priv *priv,
+                                    struct ipw_scan_request_ext *request)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_SCAN_REQUEST_EXT,
+               .len = sizeof(*request)
+       };
+
+       if (!priv || !request) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param,request,sizeof(*request));
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send SCAN_REQUEST_EXT command\n");
+               return -1;
+       }
+
+       queue_delayed_work(priv->workqueue, &priv->scan_check,
+                          IPW_SCAN_CHECK_WATCHDOG);
+       return 0;
+}
+
+static int ipw_send_scan_abort(struct ipw_priv *priv)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_SCAN_ABORT,
+               .len = 0
+       };
+
+       if (!priv) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send SCAN_ABORT command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_SENSITIVITY_CALIB,
+               .len = sizeof(struct ipw_sensitivity_calib)
+       };
+       struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
+               &cmd.param;
+       calib->beacon_rssi_raw = sens;
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send SENSITIVITY CALIB command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_associate(struct ipw_priv *priv,
+                             struct ipw_associate *associate)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_ASSOCIATE,
+               .len = sizeof(*associate)
+       };
+
+       if (!priv || !associate) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param,associate,sizeof(*associate));
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send ASSOCIATE command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_supported_rates(struct ipw_priv *priv,
+                                   struct ipw_supported_rates *rates)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_SUPPORTED_RATES,
+               .len = sizeof(*rates)
+       };
+
+       if (!priv || !rates) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param,rates,sizeof(*rates));
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send SUPPORTED_RATES command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_set_random_seed(struct ipw_priv *priv)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_SEED_NUMBER,
+               .len = sizeof(u32)
+       };
+
+       if (!priv) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       get_random_bytes(&cmd.param, sizeof(u32));
+
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send SEED_NUMBER command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+#if 0
+static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_CARD_DISABLE,
+               .len = sizeof(u32)
+       };
+
+       if (!priv) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       *((u32*)&cmd.param) = phy_off;
+
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send CARD_DISABLE command\n");
+               return -1;
+       }
+
+       return 0;
+}
+#endif
+
+static int ipw_send_tx_power(struct ipw_priv *priv,
+                            struct ipw_tx_power *power)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_TX_POWER,
+               .len = sizeof(*power)
+       };
+
+       if (!priv || !power) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param,power,sizeof(*power));
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send TX_POWER command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
+{
+       struct ipw_rts_threshold rts_threshold = {
+               .rts_threshold = rts,
+       };
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_RTS_THRESHOLD,
+               .len = sizeof(rts_threshold)
+       };
+
+       if (!priv) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param, &rts_threshold, sizeof(rts_threshold));
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send RTS_THRESHOLD command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
+{
+       struct ipw_frag_threshold frag_threshold = {
+               .frag_threshold = frag,
+       };
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_FRAG_THRESHOLD,
+               .len = sizeof(frag_threshold)
+       };
+
+       if (!priv) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       memcpy(&cmd.param, &frag_threshold, sizeof(frag_threshold));
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send FRAG_THRESHOLD command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
+{
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_POWER_MODE,
+               .len = sizeof(u32)
+       };
+       u32 *param = (u32*)(&cmd.param);
+
+       if (!priv) {
+               IPW_ERROR("Invalid args\n");
+               return -1;
+       }
+
+       /* If on battery, set to 3, if AC set to CAM, else user
+        * level */
+       switch (mode) {
+       case IPW_POWER_BATTERY:
+               *param = IPW_POWER_INDEX_3;
+               break;
+       case IPW_POWER_AC:
+               *param = IPW_POWER_MODE_CAM;
+               break;
+       default:
+               *param = mode;
+               break;
+       }
+
+       if (ipw_send_cmd(priv, &cmd)) {
+               IPW_ERROR("failed to send POWER_MODE command\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * The IPW device contains a Microwire compatible EEPROM that stores
+ * various data like the MAC address.  Usually the firmware has exclusive
+ * access to the eeprom, but during device initialization (before the
+ * device driver has sent the HostComplete command to the firmware) the
+ * device driver has read access to the EEPROM by way of indirect addressing
+ * through a couple of memory mapped registers.
+ *
+ * The following is a simplified implementation for pulling data out of the
+ * the eeprom, along with some helper functions to find information in
+ * the per device private data's copy of the eeprom.
+ *
+ * NOTE: To better understand how these functions work (i.e what is a chip
+ *       select and why do have to keep driving the eeprom clock?), read
+ *       just about any data sheet for a Microwire compatible EEPROM.
+ */
+
+/* write a 32 bit value into the indirect accessor register */
+static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
+{
+       ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
+
+       /* the eeprom requires some time to complete the operation */
+       udelay(p->eeprom_delay);
+
+       return;
+}
+
+/* perform a chip select operation */
+static inline void eeprom_cs(struct ipw_priv* priv)
+{
+       eeprom_write_reg(priv,0);
+       eeprom_write_reg(priv,EEPROM_BIT_CS);
+       eeprom_write_reg(priv,EEPROM_BIT_CS|EEPROM_BIT_SK);
+       eeprom_write_reg(priv,EEPROM_BIT_CS);
+}
+
+/* perform a chip select operation */
+static inline void eeprom_disable_cs(struct ipw_priv* priv)
+{
+       eeprom_write_reg(priv,EEPROM_BIT_CS);
+       eeprom_write_reg(priv,0);
+       eeprom_write_reg(priv,EEPROM_BIT_SK);
+}
+
+/* push a single bit down to the eeprom */
+static inline void eeprom_write_bit(struct ipw_priv *p,u8 bit)
+{
+       int d = ( bit ? EEPROM_BIT_DI : 0);
+       eeprom_write_reg(p,EEPROM_BIT_CS|d);
+       eeprom_write_reg(p,EEPROM_BIT_CS|d|EEPROM_BIT_SK);
+}
+
+/* push an opcode followed by an address down to the eeprom */
+static void eeprom_op(struct ipw_priv* priv, u8 op, u8 addr)
+{
+       int i;
+
+       eeprom_cs(priv);
+       eeprom_write_bit(priv,1);
+       eeprom_write_bit(priv,op&2);
+       eeprom_write_bit(priv,op&1);
+       for ( i=7; i>=0; i-- ) {
+               eeprom_write_bit(priv,addr&(1<<i));
+       }
+}
+
+/* pull 16 bits off the eeprom, one bit at a time */
+static u16 eeprom_read_u16(struct ipw_priv* priv, u8 addr)
+{
+       int i;
+       u16 r=0;
+
+       /* Send READ Opcode */
+       eeprom_op(priv,EEPROM_CMD_READ,addr);
+
+       /* Send dummy bit */
+       eeprom_write_reg(priv,EEPROM_BIT_CS);
+
+       /* Read the byte off the eeprom one bit at a time */
+       for ( i=0; i<16; i++ ) {
+               u32 data = 0;
+               eeprom_write_reg(priv,EEPROM_BIT_CS|EEPROM_BIT_SK);
+               eeprom_write_reg(priv,EEPROM_BIT_CS);
+               data = ipw_read_reg32(priv,FW_MEM_REG_EEPROM_ACCESS);
+               r = (r<<1) | ((data & EEPROM_BIT_DO)?1:0);
+       }
+
+       /* Send another dummy bit */
+       eeprom_write_reg(priv,0);
+       eeprom_disable_cs(priv);
+
+       return r;
+}
+
+/* helper function for pulling the mac address out of the private */
+/* data's copy of the eeprom data                                 */
+static void eeprom_parse_mac(struct ipw_priv* priv, u8* mac)
+{
+       u8* ee = (u8*)priv->eeprom;
+       memcpy(mac, &ee[EEPROM_MAC_ADDRESS], 6);
+}
+
+/*
+ * Either the device driver (i.e. the host) or the firmware can
+ * load eeprom data into the designated region in SRAM.  If neither
+ * happens then the FW will shutdown with a fatal error.
+ *
+ * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
+ * bit needs region of shared SRAM needs to be non-zero.
+ */
+static void ipw_eeprom_init_sram(struct ipw_priv *priv)
+{
+       int i;
+       u16 *eeprom = (u16 *)priv->eeprom;
+
+       IPW_DEBUG_TRACE(">>\n");
+
+       /* read entire contents of eeprom into private buffer */
+       for ( i=0; i<128; i++ )
+               eeprom[i] = eeprom_read_u16(priv,(u8)i);
+
+       /*
+          If the data looks correct, then copy it to our private
+          copy.  Otherwise let the firmware know to perform the operation
+          on it's own
+       */
+       if ((priv->eeprom + EEPROM_VERSION) != 0) {
+               IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
+
+               /* write the eeprom data to sram */
+               for( i=0; i<CX2_EEPROM_IMAGE_SIZE; i++ )
+                       ipw_write8(priv, IPW_EEPROM_DATA + i,
+                                  priv->eeprom[i]);
+
+               /* Do not load eeprom data on fatal error or suspend */
+               ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
+       } else {
+               IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
+
+               /* Load eeprom data on fatal error or suspend */
+               ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
+       }
+
+       IPW_DEBUG_TRACE("<<\n");
+}
+
+
+static inline void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
+{
+       count >>= 2;
+       if (!count) return;
+       _ipw_write32(priv, CX2_AUTOINC_ADDR, start);
+       while (count--)
+               _ipw_write32(priv, CX2_AUTOINC_DATA, 0);
+}
+
+static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
+{
+       ipw_zero_memory(priv, CX2_SHARED_SRAM_DMA_CONTROL,
+                       CB_NUMBER_OF_ELEMENTS_SMALL *
+                       sizeof(struct command_block));
+}
+
+static int ipw_fw_dma_enable(struct ipw_priv *priv)
+{ /* start dma engine but no transfers yet*/
+
+       IPW_DEBUG_FW(">> : \n");
+
+       /* Start the dma */
+       ipw_fw_dma_reset_command_blocks(priv);
+
+       /* Write CB base address */
+       ipw_write_reg32(priv, CX2_DMA_I_CB_BASE, CX2_SHARED_SRAM_DMA_CONTROL);
+
+       IPW_DEBUG_FW("<< : \n");
+       return 0;
+}
+
+static void ipw_fw_dma_abort(struct ipw_priv *priv)
+{
+       u32 control = 0;
+
+       IPW_DEBUG_FW(">> :\n");
+
+       //set the Stop and Abort bit
+       control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
+       ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
+       priv->sram_desc.last_cb_index = 0;
+
+       IPW_DEBUG_FW("<< \n");
+}
+
+static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index, struct command_block *cb)
+{
+       u32 address = CX2_SHARED_SRAM_DMA_CONTROL + (sizeof(struct command_block) * index);
+       IPW_DEBUG_FW(">> :\n");
+
+       ipw_write_indirect(priv, address, (u8*)cb, (int)sizeof(struct command_block));
+
+       IPW_DEBUG_FW("<< :\n");
+       return 0;
+
+}
+
+static int ipw_fw_dma_kick(struct ipw_priv *priv)
+{
+       u32 control = 0;
+       u32 index=0;
+
+       IPW_DEBUG_FW(">> :\n");
+
+       for (index = 0; index < priv->sram_desc.last_cb_index; index++)
+               ipw_fw_dma_write_command_block(priv, index, &priv->sram_desc.cb_list[index]);
+
+       /* Enable the DMA in the CSR register */
+       ipw_clear_bit(priv, CX2_RESET_REG,CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER);
+
+        /* Set the Start bit. */
+       control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
+       ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
+
+       IPW_DEBUG_FW("<< :\n");
+       return 0;
+}
+
+static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
+{
+       u32 address;
+       u32 register_value=0;
+       u32 cb_fields_address=0;
+
+       IPW_DEBUG_FW(">> :\n");
+       address = ipw_read_reg32(priv,CX2_DMA_I_CURRENT_CB);
+       IPW_DEBUG_FW_INFO("Current CB is 0x%x \n",address);
+
+       /* Read the DMA Controlor register */
+       register_value = ipw_read_reg32(priv, CX2_DMA_I_DMA_CONTROL);
+       IPW_DEBUG_FW_INFO("CX2_DMA_I_DMA_CONTROL is 0x%x \n",register_value);
+
+       /* Print the CB values*/
+       cb_fields_address = address;
+       register_value = ipw_read_reg32(priv, cb_fields_address);
+       IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n",register_value);
+
+       cb_fields_address += sizeof(u32);
+       register_value = ipw_read_reg32(priv, cb_fields_address);
+       IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n",register_value);
+
+       cb_fields_address += sizeof(u32);
+       register_value = ipw_read_reg32(priv, cb_fields_address);
+       IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
+                         register_value);
+
+       cb_fields_address += sizeof(u32);
+       register_value = ipw_read_reg32(priv, cb_fields_address);
+       IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n",register_value);
+
+       IPW_DEBUG_FW(">> :\n");
+}
+
+static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
+{
+       u32 current_cb_address = 0;
+       u32 current_cb_index = 0;
+
+       IPW_DEBUG_FW("<< :\n");
+       current_cb_address= ipw_read_reg32(priv, CX2_DMA_I_CURRENT_CB);
+
+       current_cb_index = (current_cb_address - CX2_SHARED_SRAM_DMA_CONTROL )/
+               sizeof (struct command_block);
+
+       IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
+                         current_cb_index, current_cb_address );
+
+       IPW_DEBUG_FW(">> :\n");
+       return current_cb_index;
+
+}
+
+static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
+                                       u32 src_address,
+                                       u32 dest_address,
+                                       u32 length,
+                                       int interrupt_enabled,
+                                       int is_last)
+{
+
+       u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
+               CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
+               CB_DEST_SIZE_LONG;
+       struct command_block *cb;
+       u32 last_cb_element=0;
+
+       IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
+                         src_address, dest_address, length);
+
+       if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
+               return -1;
+
+       last_cb_element = priv->sram_desc.last_cb_index;
+       cb = &priv->sram_desc.cb_list[last_cb_element];
+       priv->sram_desc.last_cb_index++;
+
+       /* Calculate the new CB control word */
+       if (interrupt_enabled )
+               control |= CB_INT_ENABLED;
+
+       if (is_last)
+               control |= CB_LAST_VALID;
+
+       control |= length;
+
+       /* Calculate the CB Element's checksum value */
+       cb->status = control ^src_address ^dest_address;
+
+       /* Copy the Source and Destination addresses */
+       cb->dest_addr = dest_address;
+       cb->source_addr = src_address;
+
+       /* Copy the Control Word last */
+       cb->control = control;
+
+       return 0;
+}
+
+static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
+                                u32 src_phys,
+                                u32 dest_address,
+                                u32 length)
+{
+       u32 bytes_left = length;
+       u32 src_offset=0;
+       u32 dest_offset=0;
+       int status = 0;
+       IPW_DEBUG_FW(">> \n");
+       IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
+                         src_phys, dest_address, length);
+       while (bytes_left > CB_MAX_LENGTH) {
+               status = ipw_fw_dma_add_command_block( priv,
+                                                      src_phys + src_offset,
+                                                      dest_address + dest_offset,
+                                                      CB_MAX_LENGTH, 0, 0);
+               if (status) {
+                       IPW_DEBUG_FW_INFO(": Failed\n");
+                       return -1;
+               } else
+                       IPW_DEBUG_FW_INFO(": Added new cb\n");
+
+               src_offset += CB_MAX_LENGTH;
+               dest_offset += CB_MAX_LENGTH;
+               bytes_left -= CB_MAX_LENGTH;
+       }
+
+       /* add the buffer tail */
+       if (bytes_left > 0) {
+               status = ipw_fw_dma_add_command_block(
+                       priv, src_phys + src_offset,
+                       dest_address + dest_offset,
+                       bytes_left, 0, 0);
+               if (status) {
+                       IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
+                       return -1;
+               } else
+                       IPW_DEBUG_FW_INFO(": Adding new cb - the buffer tail\n");
+       }
+
+
+       IPW_DEBUG_FW("<< \n");
+       return 0;
+}
+
+static int ipw_fw_dma_wait(struct ipw_priv *priv)
+{
+       u32 current_index = 0;
+       u32 watchdog = 0;
+
+       IPW_DEBUG_FW(">> : \n");
+
+       current_index = ipw_fw_dma_command_block_index(priv);
+       IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
+                         (int) priv->sram_desc.last_cb_index);
+
+       while (current_index < priv->sram_desc.last_cb_index) {
+               udelay(50);
+               current_index = ipw_fw_dma_command_block_index(priv);
+
+               watchdog++;
+
+               if (watchdog > 400) {
+                       IPW_DEBUG_FW_INFO("Timeout\n");
+                       ipw_fw_dma_dump_command_block(priv);
+                       ipw_fw_dma_abort(priv);
+                       return -1;
+               }
+       }
+
+       ipw_fw_dma_abort(priv);
+
+       /*Disable the DMA in the CSR register*/
+       ipw_set_bit(priv, CX2_RESET_REG,
+                   CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER);
+
+       IPW_DEBUG_FW("<< dmaWaitSync \n");
+       return 0;
+}
+
+static void ipw_remove_current_network(struct ipw_priv *priv)
+{
+       struct list_head *element, *safe;
+       struct ieee80211_network *network = NULL;
+       list_for_each_safe(element, safe, &priv->ieee->network_list) {
+               network = list_entry(element, struct ieee80211_network, list);
+               if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
+                       list_del(element);
+                       list_add_tail(&network->list,
+                                     &priv->ieee->network_free_list);
+               }
+       }
+}
+
+/**
+ * Check that card is still alive.
+ * Reads debug register from domain0.
+ * If card is present, pre-defined value should
+ * be found there.
+ *
+ * @param priv
+ * @return 1 if card is present, 0 otherwise
+ */
+static inline int ipw_alive(struct ipw_priv *priv)
+{
+       return ipw_read32(priv, 0x90) == 0xd55555d5;
+}
+
+static inline int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
+                              int timeout)
+{
+       int i = 0;
+
+       do {
+               if ((ipw_read32(priv, addr) & mask) == mask)
+                       return i;
+               mdelay(10);
+               i += 10;
+       } while (i < timeout);
+
+       return -ETIME;
+}
+
+/* These functions load the firmware and micro code for the operation of
+ * the ipw hardware.  It assumes the buffer has all the bits for the
+ * image and the caller is handling the memory allocation and clean up.
+ */
+
+
+static int ipw_stop_master(struct ipw_priv * priv)
+{
+       int rc;
+
+       IPW_DEBUG_TRACE(">> \n");
+       /* stop master. typical delay - 0 */
+       ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
+
+       rc = ipw_poll_bit(priv, CX2_RESET_REG,
+                         CX2_RESET_REG_MASTER_DISABLED, 100);
+       if (rc < 0) {
+               IPW_ERROR("stop master failed in 10ms\n");
+               return -1;
+       }
+
+       IPW_DEBUG_INFO("stop master %dms\n", rc);
+
+       return rc;
+}
+
+static void ipw_arc_release(struct ipw_priv *priv)
+{
+       IPW_DEBUG_TRACE(">> \n");
+       mdelay(5);
+
+       ipw_clear_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
+
+       /* no one knows timing, for safety add some delay */
+       mdelay(5);
+}
+
+struct fw_header {
+       u32 version;
+       u32 mode;
+};
+
+struct fw_chunk {
+       u32 address;
+       u32 length;
+};
+
+#define IPW_FW_MAJOR_VERSION 2
+#define IPW_FW_MINOR_VERSION 2
+
+#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
+#define IPW_FW_MAJOR(x) (x & 0xff)
+
+#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | \
+                         IPW_FW_MAJOR_VERSION)
+
+#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
+"." __stringify(IPW_FW_MINOR_VERSION) "-"
+
+#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
+#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
+#else
+#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
+#endif
+
+static int ipw_load_ucode(struct ipw_priv *priv, u8 * data,
+                         size_t len)
+{
+       int rc = 0, i, addr;
+       u8 cr = 0;
+       u16 *image;
+
+       image = (u16 *)data;
+
+       IPW_DEBUG_TRACE(">> \n");
+
+       rc = ipw_stop_master(priv);
+
+       if (rc < 0)
+               return rc;
+
+//     spin_lock_irqsave(&priv->lock, flags);
+
+       for (addr = CX2_SHARED_LOWER_BOUND;
+            addr < CX2_REGISTER_DOMAIN1_END; addr += 4) {
+               ipw_write32(priv, addr, 0);
+       }
+
+       /* no ucode (yet) */
+       memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
+       /* destroy DMA queues */
+       /* reset sequence */
+
+       ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET ,CX2_BIT_HALT_RESET_ON);
+       ipw_arc_release(priv);
+       ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET, CX2_BIT_HALT_RESET_OFF);
+       mdelay(1);
+
+       /* reset PHY */
+       ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, CX2_BASEBAND_POWER_DOWN);
+       mdelay(1);
+
+       ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, 0);
+       mdelay(1);
+
+       /* enable ucode store */
+       ipw_write_reg8(priv, DINO_CONTROL_REG, 0x0);
+       ipw_write_reg8(priv, DINO_CONTROL_REG, DINO_ENABLE_CS);
+       mdelay(1);
+
+       /* write ucode */
+       /**
+        * @bug
+        * Do NOT set indirect address register once and then
+        * store data to indirect data register in the loop.
+        * It seems very reasonable, but in this case DINO do not
+        * accept ucode. It is essential to set address each time.
+        */
+       /* load new ipw uCode */
+       for (i = 0; i < len / 2; i++)
+               ipw_write_reg16(priv, CX2_BASEBAND_CONTROL_STORE, image[i]);
+
+
+       /* enable DINO */
+       ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
+       ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS,
+                      DINO_ENABLE_SYSTEM );
+
+       /* this is where the igx / win driver deveates from the VAP driver.*/
+
+       /* wait for alive response */
+       for (i = 0; i < 100; i++) {
+               /* poll for incoming data */
+               cr = ipw_read_reg8(priv, CX2_BASEBAND_CONTROL_STATUS);
+               if (cr & DINO_RXFIFO_DATA)
+                       break;
+               mdelay(1);
+       }
+
+       if (cr & DINO_RXFIFO_DATA) {
+               /* alive_command_responce size is NOT multiple of 4 */
+               u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
+
+               for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
+                       response_buffer[i] =
+                               ipw_read_reg32(priv,
+                                              CX2_BASEBAND_RX_FIFO_READ);
+               memcpy(&priv->dino_alive, response_buffer,
+                      sizeof(priv->dino_alive));
+               if (priv->dino_alive.alive_command == 1
+                   && priv->dino_alive.ucode_valid == 1) {
+                       rc = 0;
+                       IPW_DEBUG_INFO(
+                               "Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
+                               "of %02d/%02d/%02d %02d:%02d\n",
+                               priv->dino_alive.software_revision,
+                               priv->dino_alive.software_revision,
+                               priv->dino_alive.device_identifier,
+                               priv->dino_alive.device_identifier,
+                               priv->dino_alive.time_stamp[0],
+                               priv->dino_alive.time_stamp[1],
+                               priv->dino_alive.time_stamp[2],
+                               priv->dino_alive.time_stamp[3],
+                               priv->dino_alive.time_stamp[4]);
+               } else {
+                       IPW_DEBUG_INFO("Microcode is not alive\n");
+                       rc = -EINVAL;
+               }
+       } else {
+               IPW_DEBUG_INFO("No alive response from DINO\n");
+               rc = -ETIME;
+       }
+
+       /* disable DINO, otherwise for some reason
+          firmware have problem getting alive resp. */
+       ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
+
+//     spin_unlock_irqrestore(&priv->lock, flags);
+
+       return rc;
+}
+
+static int ipw_load_firmware(struct ipw_priv *priv, u8 * data,
+                            size_t len)
+{
+       int rc = -1;
+       int offset = 0;
+       struct fw_chunk *chunk;
+       dma_addr_t shared_phys;
+       u8 *shared_virt;
+
+       IPW_DEBUG_TRACE("<< : \n");
+       shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
+
+       if (!shared_virt)
+               return -ENOMEM;
+
+       memmove(shared_virt, data, len);
+
+       /* Start the Dma */
+       rc = ipw_fw_dma_enable(priv);
+
+       if (priv->sram_desc.last_cb_index > 0) {
+               /* the DMA is already ready this would be a bug. */
+               BUG();
+               goto out;
+       }
+
+       do {
+               chunk = (struct fw_chunk *)(data + offset);
+               offset += sizeof(struct fw_chunk);
+               /* build DMA packet and queue up for sending */
+               /* dma to chunk->address, the chunk->length bytes from data +
+                * offeset*/
+               /* Dma loading */
+               rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
+                                          chunk->address, chunk->length);
+               if (rc) {
+                       IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
+                       goto out;
+               }
+
+               offset += chunk->length;
+       } while (offset < len);
+
+       /* Run the DMA and wait for the answer*/
+       rc = ipw_fw_dma_kick(priv);
+       if (rc) {
+               IPW_ERROR("dmaKick Failed\n");
+               goto out;
+       }
+
+       rc = ipw_fw_dma_wait(priv);
+       if (rc) {
+               IPW_ERROR("dmaWaitSync Failed\n");
+               goto out;
+       }
+ out:
+       pci_free_consistent( priv->pci_dev, len, shared_virt, shared_phys);
+       return rc;
+}
+
+/* stop nic */
+static int ipw_stop_nic(struct ipw_priv *priv)
+{
+       int rc = 0;
+
+       /* stop*/
+       ipw_write32(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
+
+       rc = ipw_poll_bit(priv, CX2_RESET_REG,
+                         CX2_RESET_REG_MASTER_DISABLED, 500);
+       if (rc < 0) {
+               IPW_ERROR("wait for reg master disabled failed\n");
+               return rc;
+       }
+
+       ipw_set_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
+
+       return rc;
+}
+
+static void ipw_start_nic(struct ipw_priv *priv)
+{
+       IPW_DEBUG_TRACE(">>\n");
+
+       /* prvHwStartNic  release ARC*/
+       ipw_clear_bit(priv, CX2_RESET_REG,
+                     CX2_RESET_REG_MASTER_DISABLED |
+                     CX2_RESET_REG_STOP_MASTER |
+                     CBD_RESET_REG_PRINCETON_RESET);
+
+       /* enable power management */
+       ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
+
+       IPW_DEBUG_TRACE("<<\n");
+}
+
+static int ipw_init_nic(struct ipw_priv *priv)
+{
+       int rc;
+
+       IPW_DEBUG_TRACE(">>\n");
+       /* reset */
+       /*prvHwInitNic */
+       /* set "initialization complete" bit to move adapter to D0 state */
+       ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
+
+       /* low-level PLL activation */
+       ipw_write32(priv, CX2_READ_INT_REGISTER,  CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
+
+       /* wait for clock stabilization */
+       rc = ipw_poll_bit(priv, CX2_GP_CNTRL_RW,
+                         CX2_GP_CNTRL_BIT_CLOCK_READY, 250);
+       if (rc < 0 )
+               IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
+
+       /* assert SW reset */
+       ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_SW_RESET);
+
+       udelay(10);
+
+       /* set "initialization complete" bit to move adapter to D0 state */
+       ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
+
+       IPW_DEBUG_TRACE(">>\n");
+       return 0;
+}
+
+
+/* Call this function from process context, it will sleep in request_firmware.
+ * Probe is an ok place to call this from.
+ */
+static int ipw_reset_nic(struct ipw_priv *priv)
+{
+       int rc = 0;
+
+       IPW_DEBUG_TRACE(">>\n");
+
+       rc = ipw_init_nic(priv);
+
+       /* Clear the 'host command active' bit... */
+       priv->status &= ~STATUS_HCMD_ACTIVE;
+       wake_up_interruptible(&priv->wait_command_queue);
+
+       IPW_DEBUG_TRACE("<<\n");
+       return rc;
+}
+
+static int ipw_get_fw(struct ipw_priv *priv,
+                     const struct firmware **fw, const char *name)
+{
+       struct fw_header *header;
+       int rc;
+
+       /* ask firmware_class module to get the boot firmware off disk */
+       rc = request_firmware(fw, name, &priv->pci_dev->dev);
+       if (rc < 0) {
+               IPW_ERROR("%s load failed: Reason %d\n", name, rc);
+               return rc;
+       }
+
+       header = (struct fw_header *)(*fw)->data;
+       if (IPW_FW_MAJOR(header->version) != IPW_FW_MAJOR_VERSION) {
+               IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
+                         name,
+                         IPW_FW_MAJOR(header->version), IPW_FW_MAJOR_VERSION);
+               return -EINVAL;
+       }
+
+       IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
+                      name,
+                      IPW_FW_MAJOR(header->version),
+                      IPW_FW_MINOR(header->version),
+                      (*fw)->size - sizeof(struct fw_header));
+       return 0;
+}
+
+#define CX2_RX_BUF_SIZE (3000)
+
+static inline void ipw_rx_queue_reset(struct ipw_priv *priv,
+                                     struct ipw_rx_queue *rxq)
+{
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&rxq->lock, flags);
+
+       INIT_LIST_HEAD(&rxq->rx_free);
+       INIT_LIST_HEAD(&rxq->rx_used);
+
+       /* Fill the rx_used queue with _all_ of the Rx buffers */
+       for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
+               /* In the reset function, these buffers may have been allocated
+                * to an SKB, so we need to unmap and free potential storage */
+               if (rxq->pool[i].skb != NULL) {
+                       pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
+                                        CX2_RX_BUF_SIZE,
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb(rxq->pool[i].skb);
+               }
+               list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+       }
+
+       /* Set us so that we have processed and used all buffers, but have
+        * not restocked the Rx queue with fresh buffers */
+       rxq->read = rxq->write = 0;
+       rxq->processed = RX_QUEUE_SIZE - 1;
+       rxq->free_count = 0;
+       spin_unlock_irqrestore(&rxq->lock, flags);
+}
+
+#ifdef CONFIG_PM
+static int fw_loaded = 0;
+static const struct firmware *bootfw = NULL;
+static const struct firmware *firmware = NULL;
+static const struct firmware *ucode = NULL;
+#endif
+
+static int ipw_load(struct ipw_priv *priv)
+{
+#ifndef CONFIG_PM
+       const struct firmware *bootfw = NULL;
+       const struct firmware *firmware = NULL;
+       const struct firmware *ucode = NULL;
+#endif
+       int rc = 0, retries = 3;
+
+#ifdef CONFIG_PM
+       if (!fw_loaded) {
+#endif
+               rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
+               if (rc)
+                       goto error;
+
+               switch (priv->ieee->iw_mode) {
+               case IW_MODE_ADHOC:
+                       rc = ipw_get_fw(priv, &ucode,
+                                       IPW_FW_NAME("ibss_ucode"));
+                       if (rc)
+                               goto error;
+
+                       rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
+                       break;
+
+#ifdef CONFIG_IPW_PROMISC
+               case IW_MODE_MONITOR:
+                       rc = ipw_get_fw(priv, &ucode,
+                                       IPW_FW_NAME("ibss_ucode"));
+                       if (rc)
+                               goto error;
+
+                       rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("sniffer"));
+                       break;
+#endif
+               case IW_MODE_INFRA:
+                       rc = ipw_get_fw(priv, &ucode,
+                                       IPW_FW_NAME("bss_ucode"));
+                       if (rc)
+                               goto error;
+
+                       rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
+                       break;
+
+               default:
+                       rc = -EINVAL;
+               }
+
+               if (rc)
+                       goto error;
+
+#ifdef CONFIG_PM
+               fw_loaded = 1;
+       }
+#endif
+
+       if (!priv->rxq)
+               priv->rxq = ipw_rx_queue_alloc(priv);
+       else
+               ipw_rx_queue_reset(priv, priv->rxq);
+       if (!priv->rxq) {
+               IPW_ERROR("Unable to initialize Rx queue\n");
+               goto error;
+       }
+
+ retry:
+       /* Ensure interrupts are disabled */
+       ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
+       priv->status &= ~STATUS_INT_ENABLED;
+
+       /* ack pending interrupts */
+       ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
+
+       ipw_stop_nic(priv);
+
+       rc = ipw_reset_nic(priv);
+       if (rc) {
+               IPW_ERROR("Unable to reset NIC\n");
+               goto error;
+       }
+
+       ipw_zero_memory(priv, CX2_NIC_SRAM_LOWER_BOUND,
+                       CX2_NIC_SRAM_UPPER_BOUND - CX2_NIC_SRAM_LOWER_BOUND);
+
+       /* DMA the initial boot firmware into the device */
+       rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
+                              bootfw->size - sizeof(struct fw_header));
+       if (rc < 0) {
+               IPW_ERROR("Unable to load boot firmware\n");
+               goto error;
+       }
+
+       /* kick start the device */
+       ipw_start_nic(priv);
+
+       /* wait for the device to finish it's initial startup sequence */
+       rc = ipw_poll_bit(priv, CX2_INTA_RW,
+                         CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
+       if (rc < 0) {
+               IPW_ERROR("device failed to boot initial fw image\n");
+               goto error;
+       }
+       IPW_DEBUG_INFO("initial device response after %dms\n", rc);
+
+       /* ack fw init done interrupt */
+       ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
+
+       /* DMA the ucode into the device */
+       rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
+                           ucode->size - sizeof(struct fw_header));
+       if (rc < 0) {
+               IPW_ERROR("Unable to load ucode\n");
+               goto error;
+       }
+
+       /* stop nic */
+       ipw_stop_nic(priv);
+
+       /* DMA bss firmware into the device */
+       rc = ipw_load_firmware(priv, firmware->data +
+                              sizeof(struct fw_header),
+                              firmware->size - sizeof(struct fw_header));
+       if (rc < 0 ) {
+               IPW_ERROR("Unable to load firmware\n");
+               goto error;
+       }
+
+       ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
+
+       rc = ipw_queue_reset(priv);
+       if (rc) {
+               IPW_ERROR("Unable to initialize queues\n");
+               goto error;
+       }
+
+       /* Ensure interrupts are disabled */
+       ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
+
+       /* kick start the device */
+       ipw_start_nic(priv);
+
+       if (ipw_read32(priv, CX2_INTA_RW) & CX2_INTA_BIT_PARITY_ERROR) {
+               if (retries > 0) {
+                       IPW_WARNING("Parity error.  Retrying init.\n");
+                       retries--;
+                       goto retry;
+               }
+
+               IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
+               rc = -EIO;
+               goto error;
+       }
+
+       /* wait for the device */
+       rc = ipw_poll_bit(priv, CX2_INTA_RW,
+                         CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
+       if (rc < 0) {
+               IPW_ERROR("device failed to start after 500ms\n");
+               goto error;
+       }
+       IPW_DEBUG_INFO("device response after %dms\n", rc);
+
+       /* ack fw init done interrupt */
+       ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
+
+       /* read eeprom data and initialize the eeprom region of sram */
+       priv->eeprom_delay = 1;
+       ipw_eeprom_init_sram(priv);
+
+       /* enable interrupts */
+       ipw_enable_interrupts(priv);
+
+       /* Ensure our queue has valid packets */
+       ipw_rx_queue_replenish(priv);
+
+       ipw_write32(priv, CX2_RX_READ_INDEX, priv->rxq->read);
+
+       /* ack pending interrupts */
+       ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
+
+#ifndef CONFIG_PM
+       release_firmware(bootfw);
+       release_firmware(ucode);
+       release_firmware(firmware);
+#endif
+       return 0;
+
+ error:
+       if (priv->rxq) {
+               ipw_rx_queue_free(priv, priv->rxq);
+               priv->rxq = NULL;
+       }
+       ipw_tx_queue_free(priv);
+       if (bootfw)
+               release_firmware(bootfw);
+       if (ucode)
+               release_firmware(ucode);
+       if (firmware)
+               release_firmware(firmware);
+#ifdef CONFIG_PM
+       fw_loaded = 0;
+       bootfw = ucode = firmware = NULL;
+#endif
+
+       return rc;
+}
+
+/**
+ * DMA services
+ *
+ * Theory of operation
+ *
+ * A queue is a circular buffers with 'Read' and 'Write' pointers.
+ * 2 empty entries always kept in the buffer to protect from overflow.
+ *
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
+ * Tx queue resumed.
+ *
+ * The IPW operates with six queues, one receive queue in the device's
+ * sram, one transmit queue for sending commands to the device firmware,
+ * and four transmit queues for data.
+ *
+ * The four transmit queues allow for performing quality of service (qos)
+ * transmissions as per the 802.11 protocol.  Currently Linux does not
+ * provide a mechanism to the user for utilizing prioritized queues, so
+ * we only utilize the first data transmit queue (queue1).
+ */
+
+/**
+ * Driver allocates buffers of this size for Rx
+ */
+
+static inline int ipw_queue_space(const struct clx2_queue *q)
+{
+       int s = q->last_used - q->first_empty;
+       if (s <= 0)
+               s += q->n_bd;
+       s -= 2;                 /* keep some reserve to not confuse empty and full situations */
+       if (s < 0)
+               s = 0;
+       return s;
+}
+
+static inline int ipw_queue_inc_wrap(int index, int n_bd)
+{
+       return (++index == n_bd) ? 0 : index;
+}
+
+/**
+ * Initialize common DMA queue structure
+ *
+ * @param q                queue to init
+ * @param count            Number of BD's to allocate. Should be power of 2
+ * @param read_register    Address for 'read' register
+ *                         (not offset within BAR, full address)
+ * @param write_register   Address for 'write' register
+ *                         (not offset within BAR, full address)
+ * @param base_register    Address for 'base' register
+ *                         (not offset within BAR, full address)
+ * @param size             Address for 'size' register
+ *                         (not offset within BAR, full address)
+ */
+static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
+                          int count, u32 read, u32 write,
+                          u32 base, u32 size)
+{
+       q->n_bd = count;
+
+       q->low_mark = q->n_bd / 4;
+       if (q->low_mark < 4)
+               q->low_mark = 4;
+
+       q->high_mark = q->n_bd / 8;
+       if (q->high_mark < 2)
+               q->high_mark = 2;
+
+       q->first_empty = q->last_used = 0;
+       q->reg_r = read;
+       q->reg_w = write;
+
+       ipw_write32(priv, base, q->dma_addr);
+       ipw_write32(priv, size, count);
+       ipw_write32(priv, read, 0);
+       ipw_write32(priv, write, 0);
+
+       _ipw_read32(priv, 0x90);
+}
+
+static int ipw_queue_tx_init(struct ipw_priv *priv,
+                            struct clx2_tx_queue *q,
+                            int count, u32 read, u32 write,
+                            u32 base, u32 size)
+{
+       struct pci_dev *dev = priv->pci_dev;
+
+       q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
+       if (!q->txb) {
+               IPW_ERROR("vmalloc for auxilary BD structures failed\n");
+               return -ENOMEM;
+       }
+
+       q->bd = pci_alloc_consistent(dev,sizeof(q->bd[0])*count, &q->q.dma_addr);
+       if (!q->bd) {
+               IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
+                               sizeof(q->bd[0]) * count);
+               kfree(q->txb);
+               q->txb = NULL;
+               return -ENOMEM;
+       }
+
+       ipw_queue_init(priv, &q->q, count, read, write, base, size);
+       return 0;
+}
+
+/**
+ * Free one TFD, those at index [txq->q.last_used].
+ * Do NOT advance any indexes
+ *
+ * @param dev
+ * @param txq
+ */
+static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
+                                 struct clx2_tx_queue *txq)
+{
+       struct tfd_frame *bd = &txq->bd[txq->q.last_used];
+       struct pci_dev *dev = priv->pci_dev;
+       int i;
+
+       /* classify bd */
+       if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
+               /* nothing to cleanup after for host commands */
+               return;
+
+       /* sanity check */
+       if (bd->u.data.num_chunks > NUM_TFD_CHUNKS) {
+               IPW_ERROR("Too many chunks: %i\n", bd->u.data.num_chunks);
+               /** @todo issue fatal error, it is quite serious situation */
+               return;
+       }
+
+       /* unmap chunks if any */
+       for (i = 0; i < bd->u.data.num_chunks; i++) {
+               pci_unmap_single(dev, bd->u.data.chunk_ptr[i],
+                                bd->u.data.chunk_len[i], PCI_DMA_TODEVICE);
+               if (txq->txb[txq->q.last_used]) {
+                       ieee80211_txb_free(txq->txb[txq->q.last_used]);
+                       txq->txb[txq->q.last_used] = NULL;
+               }
+       }
+}
+
+/**
+ * Deallocate DMA queue.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ *
+ * @param dev
+ * @param q
+ */
+static void ipw_queue_tx_free(struct ipw_priv *priv,
+                           struct clx2_tx_queue *txq)
+{
+       struct clx2_queue *q = &txq->q;
+       struct pci_dev *dev = priv->pci_dev;
+
+       if (q->n_bd == 0)
+               return;
+
+       /* first, empty all BD's */
+       for (; q->first_empty != q->last_used;
+            q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
+               ipw_queue_tx_free_tfd(priv, txq);
+       }
+
+       /* free buffers belonging to queue itself */
+       pci_free_consistent(dev, sizeof(txq->bd[0])*q->n_bd, txq->bd,
+                           q->dma_addr);
+       kfree(txq->txb);
+
+       /* 0 fill whole structure */
+       memset(txq, 0, sizeof(*txq));
+}
+
+
+/**
+ * Destroy all DMA queues and structures
+ *
+ * @param priv
+ */
+static void ipw_tx_queue_free(struct ipw_priv *priv)
+{
+       /* Tx CMD queue */
+       ipw_queue_tx_free(priv, &priv->txq_cmd);
+
+       /* Tx queues */
+       ipw_queue_tx_free(priv, &priv->txq[0]);
+       ipw_queue_tx_free(priv, &priv->txq[1]);
+       ipw_queue_tx_free(priv, &priv->txq[2]);
+       ipw_queue_tx_free(priv, &priv->txq[3]);
+}
+
+static void inline __maybe_wake_tx(struct ipw_priv *priv)
+{
+       if (netif_running(priv->net_dev)) {
+               switch (priv->port_type) {
+               case DCR_TYPE_MU_BSS:
+               case DCR_TYPE_MU_IBSS:
+                       if (!(priv->status & STATUS_ASSOCIATED)) {
+                               return;
+                       }
+               }
+               netif_wake_queue(priv->net_dev);
+       }
+
+}
+
+static inline void ipw_create_bssid(struct ipw_priv *priv, u8 *bssid)
+{
+       /* First 3 bytes are manufacturer */
+       bssid[0] = priv->mac_addr[0];
+       bssid[1] = priv->mac_addr[1];
+       bssid[2] = priv->mac_addr[2];
+
+       /* Last bytes are random */
+        get_random_bytes(&bssid[3], ETH_ALEN-3);
+
+        bssid[0] &= 0xfe;       /* clear multicast bit */
+        bssid[0] |= 0x02;       /* set local assignment bit (IEEE802) */
+}
+
+static inline u8 ipw_add_station(struct ipw_priv *priv, u8 *bssid)
+{
+       struct ipw_station_entry entry;
+       int i;
+
+       for (i = 0; i < priv->num_stations; i++) {
+               if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
+                       /* Another node is active in network */
+                       priv->missed_adhoc_beacons = 0;
+                       if (!(priv->config & CFG_STATIC_CHANNEL))
+                               /* when other nodes drop out, we drop out */
+                               priv->config &= ~CFG_ADHOC_PERSIST;
+
+                       return i;
+               }
+       }
+
+       if (i == MAX_STATIONS)
+               return IPW_INVALID_STATION;
+
+       IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
+
+       entry.reserved = 0;
+       entry.support_mode = 0;
+       memcpy(entry.mac_addr, bssid, ETH_ALEN);
+       memcpy(priv->stations[i], bssid, ETH_ALEN);
+       ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
+                        &entry,
+                        sizeof(entry));
+       priv->num_stations++;
+
+       return i;
+}
+
+static inline u8 ipw_find_station(struct ipw_priv *priv, u8 *bssid)
+{
+       int i;
+
+       for (i = 0; i < priv->num_stations; i++)
+               if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
+                       return i;
+
+       return IPW_INVALID_STATION;
+}
+
+static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
+{
+       int err;
+
+       if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
+               IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
+               return;
+       }
+
+       IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
+                       "on channel %d.\n",
+                       MAC_ARG(priv->assoc_request.bssid),
+                       priv->assoc_request.channel);
+
+       priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
+       priv->status |= STATUS_DISASSOCIATING;
+
+       if (quiet)
+               priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
+       else
+               priv->assoc_request.assoc_type = HC_DISASSOCIATE;
+       err = ipw_send_associate(priv, &priv->assoc_request);
+       if (err) {
+               IPW_DEBUG_HC("Attempt to send [dis]associate command "
+                            "failed.\n");
+               return;
+       }
+
+}
+
+static void ipw_disassociate(void *data)
+{
+       ipw_send_disassociate(data, 0);
+}
+
+static void notify_wx_assoc_event(struct ipw_priv *priv)
+{
+       union iwreq_data wrqu;
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       if (priv->status & STATUS_ASSOCIATED)
+               memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
+       else
+               memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+       wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+struct ipw_status_code {
+       u16 status;
+       const char *reason;
+};
+
+static const struct ipw_status_code ipw_status_codes[] = {
+       {0x00, "Successful"},
+       {0x01, "Unspecified failure"},
+       {0x0A, "Cannot support all requested capabilities in the "
+        "Capability information field"},
+       {0x0B, "Reassociation denied due to inability to confirm that "
+        "association exists"},
+       {0x0C, "Association denied due to reason outside the scope of this "
+        "standard"},
+       {0x0D, "Responding station does not support the specified authentication "
+        "algorithm"},
+       {0x0E, "Received an Authentication frame with authentication sequence "
+        "transaction sequence number out of expected sequence"},
+       {0x0F, "Authentication rejected because of challenge failure"},
+       {0x10, "Authentication rejected due to timeout waiting for next "
+        "frame in sequence"},
+       {0x11, "Association denied because AP is unable to handle additional "
+        "associated stations"},
+       {0x12, "Association denied due to requesting station not supporting all "
+        "of the datarates in the BSSBasicServiceSet Parameter"},
+       {0x13, "Association denied due to requesting station not supporting "
+        "short preamble operation"},
+       {0x14, "Association denied due to requesting station not supporting "
+        "PBCC encoding"},
+       {0x15, "Association denied due to requesting station not supporting "
+        "channel agility"},
+       {0x19, "Association denied due to requesting station not supporting "
+        "short slot operation"},
+       {0x1A, "Association denied due to requesting station not supporting "
+        "DSSS-OFDM operation"},
+       {0x28, "Invalid Information Element"},
+       {0x29, "Group Cipher is not valid"},
+       {0x2A, "Pairwise Cipher is not valid"},
+       {0x2B, "AKMP is not valid"},
+       {0x2C, "Unsupported RSN IE version"},
+       {0x2D, "Invalid RSN IE Capabilities"},
+       {0x2E, "Cipher suite is rejected per security policy"},
+};
+
+#ifdef CONFIG_IPW_DEBUG
+static const char *ipw_get_status_code(u16 status)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
+               if (ipw_status_codes[i].status == status)
+                       return ipw_status_codes[i].reason;
+       return "Unknown status value.";
+}
+#endif
+
+static void inline average_init(struct average *avg)
+{
+       memset(avg, 0, sizeof(*avg));
+}
+
+static void inline average_add(struct average *avg, s16 val)
+{
+       avg->sum -= avg->entries[avg->pos];
+       avg->sum += val;
+       avg->entries[avg->pos++] = val;
+       if (unlikely(avg->pos == AVG_ENTRIES)) {
+               avg->init = 1;
+               avg->pos = 0;
+       }
+}
+
+static s16 inline average_value(struct average *avg)
+{
+       if (!unlikely(avg->init)) {
+               if (avg->pos)
+                       return avg->sum / avg->pos;
+               return 0;
+       }
+
+       return avg->sum / AVG_ENTRIES;
+}
+
+static void ipw_reset_stats(struct ipw_priv *priv)
+{
+       u32 len = sizeof(u32);
+
+       priv->quality = 0;
+
+       average_init(&priv->average_missed_beacons);
+       average_init(&priv->average_rssi);
+       average_init(&priv->average_noise);
+
+       priv->last_rate = 0;
+       priv->last_missed_beacons = 0;
+       priv->last_rx_packets = 0;
+       priv->last_tx_packets = 0;
+       priv->last_tx_failures = 0;
+
+       /* Firmware managed, reset only when NIC is restarted, so we have to
+        * normalize on the current value */
+       ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
+                       &priv->last_rx_err, &len);
+       ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
+                       &priv->last_tx_failures, &len);
+
+       /* Driver managed, reset with each association */
+       priv->missed_adhoc_beacons = 0;
+       priv->missed_beacons = 0;
+       priv->tx_packets = 0;
+       priv->rx_packets = 0;
+
+}
+
+
+static inline u32 ipw_get_max_rate(struct ipw_priv *priv)
+{
+       u32 i = 0x80000000;
+       u32 mask = priv->rates_mask;
+       /* If currently associated in B mode, restrict the maximum
+        * rate match to B rates */
+       if (priv->assoc_request.ieee_mode == IPW_B_MODE)
+               mask &= IEEE80211_CCK_RATES_MASK;
+
+       /* TODO: Verify that the rate is supported by the current rates
+        * list. */
+
+       while (i && !(mask & i)) i >>= 1;
+       switch (i) {
+       case IEEE80211_CCK_RATE_1MB_MASK: return 1000000;
+       case IEEE80211_CCK_RATE_2MB_MASK: return 2000000;
+       case IEEE80211_CCK_RATE_5MB_MASK: return 5500000;
+       case IEEE80211_OFDM_RATE_6MB_MASK: return 6000000;
+       case IEEE80211_OFDM_RATE_9MB_MASK: return 9000000;
+       case IEEE80211_CCK_RATE_11MB_MASK: return 11000000;
+       case IEEE80211_OFDM_RATE_12MB_MASK: return 12000000;
+       case IEEE80211_OFDM_RATE_18MB_MASK: return 18000000;
+       case IEEE80211_OFDM_RATE_24MB_MASK: return 24000000;
+       case IEEE80211_OFDM_RATE_36MB_MASK: return 36000000;
+       case IEEE80211_OFDM_RATE_48MB_MASK: return 48000000;
+       case IEEE80211_OFDM_RATE_54MB_MASK: return 54000000;
+       }
+
+       if (priv->ieee->mode == IEEE_B)
+               return 11000000;
+       else
+               return 54000000;
+}
+
+static u32 ipw_get_current_rate(struct ipw_priv *priv)
+{
+       u32 rate, len = sizeof(rate);
+       int err;
+
+       if (!(priv->status & STATUS_ASSOCIATED))
+               return 0;
+
+       if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
+               err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
+                                     &len);
+               if (err) {
+                       IPW_DEBUG_INFO("failed querying ordinals.\n");
+                       return 0;
+               }
+       } else
+               return ipw_get_max_rate(priv);
+
+       switch (rate) {
+       case IPW_TX_RATE_1MB:  return  1000000;
+       case IPW_TX_RATE_2MB:  return  2000000;
+       case IPW_TX_RATE_5MB:  return  5500000;
+       case IPW_TX_RATE_6MB:  return  6000000;
+       case IPW_TX_RATE_9MB:  return  9000000;
+       case IPW_TX_RATE_11MB: return 11000000;
+       case IPW_TX_RATE_12MB: return 12000000;
+       case IPW_TX_RATE_18MB: return 18000000;
+       case IPW_TX_RATE_24MB: return 24000000;
+       case IPW_TX_RATE_36MB: return 36000000;
+       case IPW_TX_RATE_48MB: return 48000000;
+       case IPW_TX_RATE_54MB: return 54000000;
+       }
+
+       return 0;
+}
+
+#define PERFECT_RSSI (-50)
+#define WORST_RSSI   (-85)
+#define IPW_STATS_INTERVAL (2 * HZ)
+static void ipw_gather_stats(struct ipw_priv *priv)
+{
+       u32 rx_err, rx_err_delta, rx_packets_delta;
+       u32 tx_failures, tx_failures_delta, tx_packets_delta;
+       u32 missed_beacons_percent, missed_beacons_delta;
+       u32 quality = 0;
+       u32 len = sizeof(u32);
+       s16 rssi;
+       u32 beacon_quality, signal_quality, tx_quality, rx_quality,
+               rate_quality;
+
+       if (!(priv->status & STATUS_ASSOCIATED)) {
+               priv->quality = 0;
+               return;
+       }
+
+       /* Update the statistics */
+       ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
+                       &priv->missed_beacons, &len);
+       missed_beacons_delta = priv->missed_beacons -
+               priv->last_missed_beacons;
+       priv->last_missed_beacons = priv->missed_beacons;
+       if (priv->assoc_request.beacon_interval) {
+               missed_beacons_percent = missed_beacons_delta *
+                       (HZ * priv->assoc_request.beacon_interval) /
+                       (IPW_STATS_INTERVAL * 10);
+       } else {
+               missed_beacons_percent = 0;
+       }
+       average_add(&priv->average_missed_beacons, missed_beacons_percent);
+
+       ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
+       rx_err_delta = rx_err - priv->last_rx_err;
+       priv->last_rx_err = rx_err;
+
+       ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
+       tx_failures_delta = tx_failures - priv->last_tx_failures;
+       priv->last_tx_failures = tx_failures;
+
+       rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
+       priv->last_rx_packets = priv->rx_packets;
+
+       tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
+       priv->last_tx_packets = priv->tx_packets;
+
+       /* Calculate quality based on the following:
+        *
+        * Missed beacon: 100% = 0, 0% = 70% missed
+        * Rate: 60% = 1Mbs, 100% = Max
+        * Rx and Tx errors represent a straight % of total Rx/Tx
+        * RSSI: 100% = > -50,  0% = < -80
+        * Rx errors: 100% = 0, 0% = 50% missed
+        *
+        * The lowest computed quality is used.
+        *
+        */
+#define BEACON_THRESHOLD 5
+       beacon_quality = 100 - missed_beacons_percent;
+       if (beacon_quality < BEACON_THRESHOLD)
+               beacon_quality = 0;
+       else
+               beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
+                       (100 - BEACON_THRESHOLD);
+       IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
+                       beacon_quality, missed_beacons_percent);
+
+       priv->last_rate = ipw_get_current_rate(priv);
+       rate_quality =  priv->last_rate * 40 / priv->last_rate + 60;
+       IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
+                       rate_quality, priv->last_rate / 1000000);
+
+       if (rx_packets_delta > 100 &&
+           rx_packets_delta + rx_err_delta)
+               rx_quality = 100 - (rx_err_delta * 100) /
+                       (rx_packets_delta + rx_err_delta);
+       else
+               rx_quality = 100;
+       IPW_DEBUG_STATS("Rx quality   : %3d%% (%u errors, %u packets)\n",
+                       rx_quality, rx_err_delta, rx_packets_delta);
+
+       if (tx_packets_delta > 100 &&
+           tx_packets_delta + tx_failures_delta)
+               tx_quality = 100 - (tx_failures_delta * 100) /
+                       (tx_packets_delta + tx_failures_delta);
+       else
+               tx_quality = 100;
+       IPW_DEBUG_STATS("Tx quality   : %3d%% (%u errors, %u packets)\n",
+                       tx_quality, tx_failures_delta, tx_packets_delta);
+
+       rssi = average_value(&priv->average_rssi);
+       if (rssi > PERFECT_RSSI)
+               signal_quality = 100;
+       else if (rssi < WORST_RSSI)
+               signal_quality = 0;
+       else
+               signal_quality = (rssi - WORST_RSSI) * 100 /
+                       (PERFECT_RSSI - WORST_RSSI);
+       IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
+                       signal_quality, rssi);
+
+       quality = min(beacon_quality,
+                     min(rate_quality,
+                         min(tx_quality, min(rx_quality, signal_quality))));
+       if (quality == beacon_quality)
+               IPW_DEBUG_STATS(
+                       "Quality (%d%%): Clamped to missed beacons.\n",
+                       quality);
+       if (quality == rate_quality)
+               IPW_DEBUG_STATS(
+                       "Quality (%d%%): Clamped to rate quality.\n",
+                       quality);
+       if (quality == tx_quality)
+               IPW_DEBUG_STATS(
+                       "Quality (%d%%): Clamped to Tx quality.\n",
+                       quality);
+       if (quality == rx_quality)
+               IPW_DEBUG_STATS(
+                       "Quality (%d%%): Clamped to Rx quality.\n",
+                       quality);
+       if (quality == signal_quality)
+               IPW_DEBUG_STATS(
+                       "Quality (%d%%): Clamped to signal quality.\n",
+                       quality);
+
+       priv->quality = quality;
+
+       queue_delayed_work(priv->workqueue, &priv->gather_stats,
+                          IPW_STATS_INTERVAL);
+}
+
+/**
+ * Handle host notification packet.
+ * Called from interrupt routine
+ */
+static inline void ipw_rx_notification(struct ipw_priv* priv,
+                                      struct ipw_rx_notification *notif)
+{
+       IPW_DEBUG_NOTIF("type = %i (%d bytes)\n",
+                       notif->subtype, notif->size);
+
+       switch (notif->subtype) {
+       case HOST_NOTIFICATION_STATUS_ASSOCIATED: {
+               struct notif_association *assoc = &notif->u.assoc;
+
+               switch (assoc->state) {
+               case CMAS_ASSOCIATED: {
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "associated: '%s' " MAC_FMT " \n",
+                                 escape_essid(priv->essid, priv->essid_len),
+                                 MAC_ARG(priv->bssid));
+
+                       switch (priv->ieee->iw_mode) {
+                       case IW_MODE_INFRA:
+                               memcpy(priv->ieee->bssid, priv->bssid,
+                                      ETH_ALEN);
+                               break;
+
+                       case IW_MODE_ADHOC:
+                               memcpy(priv->ieee->bssid, priv->bssid,
+                                      ETH_ALEN);
+
+                               /* clear out the station table */
+                               priv->num_stations = 0;
+
+                               IPW_DEBUG_ASSOC("queueing adhoc check\n");
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->adhoc_check,
+                                                  priv->assoc_request.beacon_interval);
+                               break;
+                       }
+
+                       priv->status &= ~STATUS_ASSOCIATING;
+                       priv->status |= STATUS_ASSOCIATED;
+
+                       netif_carrier_on(priv->net_dev);
+                       if (netif_queue_stopped(priv->net_dev)) {
+                               IPW_DEBUG_NOTIF("waking queue\n");
+                               netif_wake_queue(priv->net_dev);
+                       } else {
+                               IPW_DEBUG_NOTIF("starting queue\n");
+                               netif_start_queue(priv->net_dev);
+                       }
+
+                       ipw_reset_stats(priv);
+                       /* Ensure the rate is updated immediately */
+                       priv->last_rate = ipw_get_current_rate(priv);
+                       schedule_work(&priv->gather_stats);
+                       notify_wx_assoc_event(priv);
+
+/*                     queue_delayed_work(priv->workqueue,
+                                          &priv->request_scan,
+                                          SCAN_ASSOCIATED_INTERVAL);
+*/
+                       break;
+               }
+
+               case CMAS_AUTHENTICATED: {
+                       if (priv->status & (STATUS_ASSOCIATED | STATUS_AUTH)) {
+#ifdef CONFIG_IPW_DEBUG
+                               struct notif_authenticate *auth = &notif->u.auth;
+                               IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                         "deauthenticated: '%s' " MAC_FMT ": (0x%04X) - %s \n",
+                                         escape_essid(priv->essid, priv->essid_len),
+                                         MAC_ARG(priv->bssid),
+                                         ntohs(auth->status),
+                                         ipw_get_status_code(ntohs(auth->status)));
+#endif
+
+                               priv->status &= ~(STATUS_ASSOCIATING |
+                                                 STATUS_AUTH |
+                                                 STATUS_ASSOCIATED);
+
+                               netif_carrier_off(priv->net_dev);
+                               netif_stop_queue(priv->net_dev);
+                               queue_work(priv->workqueue, &priv->request_scan);
+                               notify_wx_assoc_event(priv);
+                               break;
+                       }
+
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "authenticated: '%s' " MAC_FMT "\n",
+                                 escape_essid(priv->essid, priv->essid_len),
+                                 MAC_ARG(priv->bssid));
+                       break;
+               }
+
+               case CMAS_INIT: {
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "disassociated: '%s' " MAC_FMT " \n",
+                                 escape_essid(priv->essid, priv->essid_len),
+                                 MAC_ARG(priv->bssid));
+
+                       priv->status &= ~(
+                               STATUS_DISASSOCIATING |
+                               STATUS_ASSOCIATING |
+                               STATUS_ASSOCIATED |
+                               STATUS_AUTH);
+
+                       netif_stop_queue(priv->net_dev);
+                       if (!(priv->status & STATUS_ROAMING)) {
+                               netif_carrier_off(priv->net_dev);
+                               notify_wx_assoc_event(priv);
+
+                               /* Cancel any queued work ... */
+                               cancel_delayed_work(&priv->request_scan);
+                               cancel_delayed_work(&priv->adhoc_check);
+
+                               /* Queue up another scan... */
+                               queue_work(priv->workqueue,
+                                          &priv->request_scan);
+
+                               cancel_delayed_work(&priv->gather_stats);
+                       } else {
+                               priv->status |= STATUS_ROAMING;
+                               queue_work(priv->workqueue,
+                                          &priv->request_scan);
+                       }
+
+                       ipw_reset_stats(priv);
+                       break;
+               }
+
+               default:
+                       IPW_ERROR("assoc: unknown (%d)\n",
+                                 assoc->state);
+                       break;
+               }
+
+               break;
+       }
+
+       case HOST_NOTIFICATION_STATUS_AUTHENTICATE: {
+               struct notif_authenticate *auth = &notif->u.auth;
+               switch (auth->state) {
+               case CMAS_AUTHENTICATED:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+                                 "authenticated: '%s' " MAC_FMT " \n",
+                                 escape_essid(priv->essid, priv->essid_len),
+                                 MAC_ARG(priv->bssid));
+                       priv->status |= STATUS_AUTH;
+                       break;
+
+               case CMAS_INIT:
+                       if (priv->status & STATUS_AUTH) {
+                               IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                         "authentication failed (0x%04X): %s\n",
+                                         ntohs(auth->status),
+                                         ipw_get_status_code(ntohs(auth->status)));
+                       }
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "deauthenticated: '%s' " MAC_FMT "\n",
+                                 escape_essid(priv->essid, priv->essid_len),
+                                 MAC_ARG(priv->bssid));
+
+                       priv->status &= ~(STATUS_ASSOCIATING |
+                                         STATUS_AUTH |
+                                         STATUS_ASSOCIATED);
+
+                       netif_carrier_off(priv->net_dev);
+                       netif_stop_queue(priv->net_dev);
+                       queue_work(priv->workqueue, &priv->request_scan);
+                       notify_wx_assoc_event(priv);
+                       break;
+
+               case CMAS_TX_AUTH_SEQ_1:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "AUTH_SEQ_1\n");
+                       break;
+               case CMAS_RX_AUTH_SEQ_2:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "AUTH_SEQ_2\n");
+                       break;
+               case CMAS_AUTH_SEQ_1_PASS:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "AUTH_SEQ_1_PASS\n");
+                       break;
+               case CMAS_AUTH_SEQ_1_FAIL:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "AUTH_SEQ_1_FAIL\n");
+                       break;
+               case CMAS_TX_AUTH_SEQ_3:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "AUTH_SEQ_3\n");
+                       break;
+               case CMAS_RX_AUTH_SEQ_4:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "RX_AUTH_SEQ_4\n");
+                       break;
+               case CMAS_AUTH_SEQ_2_PASS:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "AUTH_SEQ_2_PASS\n");
+                       break;
+               case CMAS_AUTH_SEQ_2_FAIL:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "AUT_SEQ_2_FAIL\n");
+                       break;
+               case CMAS_TX_ASSOC:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "TX_ASSOC\n");
+                       break;
+               case CMAS_RX_ASSOC_RESP:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "RX_ASSOC_RESP\n");
+                       break;
+               case CMAS_ASSOCIATED:
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+                                 "ASSOCIATED\n");
+                       break;
+               default:
+                       IPW_DEBUG_NOTIF("auth: failure - %d\n", auth->state);
+                       break;
+               }
+               break;
+       }
+
+       case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT: {
+               struct notif_channel_result *x = &notif->u.channel_result;
+
+               if (notif->size == sizeof(*x)) {
+                       IPW_DEBUG_SCAN("Scan result for channel %d\n",
+                                      x->channel_num);
+               } else {
+                       IPW_DEBUG_SCAN("Scan result of wrong size %d "
+                                      "(should be %zd)\n",
+                                      notif->size, sizeof(*x));
+               }
+               break;
+       }
+
+       case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED: {
+               struct notif_scan_complete* x = &notif->u.scan_complete;
+               if (notif->size == sizeof(*x)) {
+                       IPW_DEBUG_SCAN("Scan completed: type %d, %d channels, "
+                                      "%d status\n",
+                                      x->scan_type,
+                                      x->num_channels,
+                                      x->status);
+               } else {
+                       IPW_ERROR("Scan completed of wrong size %d "
+                                 "(should be %zd)\n",
+                                 notif->size, sizeof(*x));
+               }
+
+               priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
+
+               cancel_delayed_work(&priv->scan_check);
+
+               if (!(priv->status & (STATUS_ASSOCIATED |
+                                     STATUS_ASSOCIATING |
+                                     STATUS_ROAMING |
+                                     STATUS_DISASSOCIATING)))
+                       queue_work(priv->workqueue, &priv->associate);
+               else if (priv->status & STATUS_ROAMING) {
+                       /* If a scan completed and we are in roam mode, then
+                        * the scan that completed was the one requested as a
+                        * result of entering roam... so, schedule the
+                        * roam work */
+                       queue_work(priv->workqueue, &priv->roam);
+               } else if (priv->status & STATUS_SCAN_PENDING)
+                       queue_work(priv->workqueue, &priv->request_scan);
+
+               priv->ieee->scans++;
+               break;
+       }
+
+       case HOST_NOTIFICATION_STATUS_FRAG_LENGTH: {
+               struct notif_frag_length *x = &notif->u.frag_len;
+
+               if (notif->size == sizeof(*x)) {
+                       IPW_ERROR("Frag length: %d\n", x->frag_length);
+               } else {
+                       IPW_ERROR("Frag length of wrong size %d "
+                                 "(should be %zd)\n",
+                                 notif->size, sizeof(*x));
+               }
+               break;
+       }
+
+       case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION: {
+               struct notif_link_deterioration *x =
+                       &notif->u.link_deterioration;
+               if (notif->size==sizeof(*x)) {
+                       IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+                                 "link deterioration: '%s' " MAC_FMT " \n",
+                                 escape_essid(priv->essid, priv->essid_len),
+                                 MAC_ARG(priv->bssid));
+                       memcpy(&priv->last_link_deterioration, x, sizeof(*x));
+               } else {
+                       IPW_ERROR("Link Deterioration of wrong size %d "
+                                 "(should be %zd)\n",
+                                 notif->size, sizeof(*x));
+               }
+               break;
+       }
+
+       case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE: {
+               IPW_ERROR("Dino config\n");
+               if (priv->hcmd && priv->hcmd->cmd == HOST_CMD_DINO_CONFIG) {
+                       /* TODO: Do anything special? */
+               } else {
+                       IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
+               }
+               break;
+       }
+
+       case HOST_NOTIFICATION_STATUS_BEACON_STATE: {
+               struct notif_beacon_state *x = &notif->u.beacon_state;
+               if (notif->size != sizeof(*x)) {
+                       IPW_ERROR("Beacon state of wrong size %d (should "
+                                 "be %zd)\n", notif->size, sizeof(*x));
+                       break;
+               }
+
+               if (x->state == HOST_NOTIFICATION_STATUS_BEACON_MISSING) {
+                       if (priv->status & STATUS_SCANNING) {
+                               /* Stop scan to keep fw from getting
+                                * stuck... */
+                               queue_work(priv->workqueue,
+                                          &priv->abort_scan);
+                       }
+
+                       if (x->number > priv->missed_beacon_threshold &&
+                           priv->status & STATUS_ASSOCIATED) {
+                               IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
+                                         IPW_DL_STATE,
+                                         "Missed beacon: %d - disassociate\n",
+                                         x->number);
+                               queue_work(priv->workqueue,
+                                          &priv->disassociate);
+                       } else if (x->number > priv->roaming_threshold) {
+                               IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+                                         "Missed beacon: %d - initiate "
+                                         "roaming\n",
+                                         x->number);
+                               queue_work(priv->workqueue,
+                                          &priv->roam);
+                       } else {
+                               IPW_DEBUG_NOTIF("Missed beacon: %d\n",
+                                               x->number);
+                       }
+
+                       priv->notif_missed_beacons = x->number;
+
+                }
+
+
+               break;
+       }
+
+       case HOST_NOTIFICATION_STATUS_TGI_TX_KEY: {
+               struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
+               if (notif->size==sizeof(*x)) {
+                       IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
+                                 "0x%02x station %d\n",
+                                 x->key_state,x->security_type,
+                                 x->station_index);
+                       break;
+               }
+
+               IPW_ERROR("TGi Tx Key of wrong size %d (should be %zd)\n",
+                         notif->size, sizeof(*x));
+               break;
+       }
+
+       case HOST_NOTIFICATION_CALIB_KEEP_RESULTS: {
+               struct notif_calibration *x = &notif->u.calibration;
+
+               if (notif->size == sizeof(*x)) {
+                       memcpy(&priv->calib, x, sizeof(*x));
+                       IPW_DEBUG_INFO("TODO: Calibration\n");
+                       break;
+               }
+
+               IPW_ERROR("Calibration of wrong size %d (should be %zd)\n",
+                         notif->size, sizeof(*x));
+               break;
+       }
+
+       case HOST_NOTIFICATION_NOISE_STATS: {
+               if (notif->size == sizeof(u32)) {
+                       priv->last_noise = (u8)(notif->u.noise.value & 0xff);
+                       average_add(&priv->average_noise, priv->last_noise);
+                       break;
+               }
+
+               IPW_ERROR("Noise stat is wrong size %d (should be %zd)\n",
+                         notif->size, sizeof(u32));
+               break;
+       }
+
+       default:
+               IPW_ERROR("Unknown notification: "
+                         "subtype=%d,flags=0x%2x,size=%d\n",
+                         notif->subtype, notif->flags, notif->size);
+       }
+}
+
+/**
+ * Destroys all DMA structures and initialise them again
+ *
+ * @param priv
+ * @return error code
+ */
+static int ipw_queue_reset(struct ipw_priv *priv)
+{
+       int rc = 0;
+       /** @todo customize queue sizes */
+       int nTx = 64, nTxCmd = 8;
+       ipw_tx_queue_free(priv);
+       /* Tx CMD queue */
+       rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
+                              CX2_TX_CMD_QUEUE_READ_INDEX,
+                              CX2_TX_CMD_QUEUE_WRITE_INDEX,
+                              CX2_TX_CMD_QUEUE_BD_BASE,
+                              CX2_TX_CMD_QUEUE_BD_SIZE);
+       if (rc) {
+               IPW_ERROR("Tx Cmd queue init failed\n");
+               goto error;
+       }
+       /* Tx queue(s) */
+       rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
+                              CX2_TX_QUEUE_0_READ_INDEX,
+                              CX2_TX_QUEUE_0_WRITE_INDEX,
+                              CX2_TX_QUEUE_0_BD_BASE,
+                              CX2_TX_QUEUE_0_BD_SIZE);
+       if (rc) {
+               IPW_ERROR("Tx 0 queue init failed\n");
+               goto error;
+       }
+       rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
+                              CX2_TX_QUEUE_1_READ_INDEX,
+                              CX2_TX_QUEUE_1_WRITE_INDEX,
+                              CX2_TX_QUEUE_1_BD_BASE,
+                              CX2_TX_QUEUE_1_BD_SIZE);
+       if (rc) {
+               IPW_ERROR("Tx 1 queue init failed\n");
+               goto error;
+       }
+       rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
+                              CX2_TX_QUEUE_2_READ_INDEX,
+                              CX2_TX_QUEUE_2_WRITE_INDEX,
+                              CX2_TX_QUEUE_2_BD_BASE,
+                              CX2_TX_QUEUE_2_BD_SIZE);
+       if (rc) {
+               IPW_ERROR("Tx 2 queue init failed\n");
+               goto error;
+       }
+       rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
+                              CX2_TX_QUEUE_3_READ_INDEX,
+                              CX2_TX_QUEUE_3_WRITE_INDEX,
+                              CX2_TX_QUEUE_3_BD_BASE,
+                              CX2_TX_QUEUE_3_BD_SIZE);
+       if (rc) {
+               IPW_ERROR("Tx 3 queue init failed\n");
+               goto error;
+       }
+       /* statistics */
+       priv->rx_bufs_min = 0;
+       priv->rx_pend_max = 0;
+       return rc;
+
+ error:
+       ipw_tx_queue_free(priv);
+       return rc;
+}
+
+/**
+ * Reclaim Tx queue entries no more used by NIC.
+ *
+ * When FW adwances 'R' index, all entries between old and
+ * new 'R' index need to be reclaimed. As result, some free space
+ * forms. If there is enough free space (> low mark), wake Tx queue.
+ *
+ * @note Need to protect against garbage in 'R' index
+ * @param priv
+ * @param txq
+ * @param qindex
+ * @return Number of used entries remains in the queue
+ */
+static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
+                               struct clx2_tx_queue *txq, int qindex)
+{
+       u32 hw_tail;
+       int used;
+       struct clx2_queue *q = &txq->q;
+
+       hw_tail = ipw_read32(priv, q->reg_r);
+       if (hw_tail >= q->n_bd) {
+               IPW_ERROR
+                       ("Read index for DMA queue (%d) is out of range [0-%d)\n",
+                        hw_tail, q->n_bd);
+               goto done;
+       }
+       for (; q->last_used != hw_tail;
+            q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
+               ipw_queue_tx_free_tfd(priv, txq);
+               priv->tx_packets++;
+       }
+ done:
+       if (ipw_queue_space(q) > q->low_mark && qindex >= 0) {
+               __maybe_wake_tx(priv);
+       }
+       used = q->first_empty - q->last_used;
+       if (used < 0)
+               used += q->n_bd;
+
+       return used;
+}
+
+static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
+                            int len, int sync)
+{
+       struct clx2_tx_queue *txq = &priv->txq_cmd;
+       struct clx2_queue *q = &txq->q;
+       struct tfd_frame *tfd;
+
+       if (ipw_queue_space(q) < (sync ? 1 : 2)) {
+               IPW_ERROR("No space for Tx\n");
+               return -EBUSY;
+       }
+
+       tfd = &txq->bd[q->first_empty];
+       txq->txb[q->first_empty] = NULL;
+
+       memset(tfd, 0, sizeof(*tfd));
+       tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
+       tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
+       priv->hcmd_seq++;
+       tfd->u.cmd.index = hcmd;
+       tfd->u.cmd.length = len;
+       memcpy(tfd->u.cmd.payload, buf, len);
+       q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
+       ipw_write32(priv, q->reg_w, q->first_empty);
+       _ipw_read32(priv, 0x90);
+
+       return 0;
+}
+
+
+
+/*
+ * Rx theory of operation
+ *
+ * The host allocates 32 DMA target addresses and passes the host address
+ * to the firmware at register CX2_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
+ * 0 to 31
+ *
+ * Rx Queue Indexes
+ * The host/firmware share two index registers for managing the Rx buffers.
+ *
+ * The READ index maps to the first position that the firmware may be writing
+ * to -- the driver can read up to (but not including) this position and get
+ * good data.
+ * The READ index is managed by the firmware once the card is enabled.
+ *
+ * The WRITE index maps to the last position the driver has read from -- the
+ * position preceding WRITE is the last slot the firmware can place a packet.
+ *
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
+ * WRITE = READ.
+ *
+ * During initialization the host sets up the READ queue position to the first
+ * INDEX position, and WRITE to the last (READ - 1 wrapped)
+ *
+ * When the firmware places a packet in a buffer it will advance the READ index
+ * and fire the RX interrupt.  The driver can then query the READ index and
+ * process as many packets as possible, moving the WRITE index forward as it
+ * resets the Rx queue buffers with new memory.
+ *
+ * The management in the driver is as follows:
+ * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free.  When
+ *   ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
+ *   to replensish the ipw->rxq->rx_free.
+ * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
+ *   ipw->rxq is replenished and the READ INDEX is updated (updating the
+ *   'processed' and 'read' driver indexes as well)
+ * + A received packet is processed and handed to the kernel network stack,
+ *   detached from the ipw->rxq.  The driver 'processed' index is updated.
+ * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
+ *   list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
+ *   INDEX is not incremented and ipw->status(RX_STALLED) is set.  If there
+ *   were enough free buffers and RX_STALLED is set it is cleared.
+ *
+ *
+ * Driver sequence:
+ *
+ * ipw_rx_queue_alloc()       Allocates rx_free
+ * ipw_rx_queue_replenish()   Replenishes rx_free list from rx_used, and calls
+ *                            ipw_rx_queue_restock
+ * ipw_rx_queue_restock()     Moves available buffers from rx_free into Rx
+ *                            queue, updates firmware pointers, and updates
+ *                            the WRITE index.  If insufficient rx_free buffers
+ *                            are available, schedules ipw_rx_queue_replenish
+ *
+ * -- enable interrupts --
+ * ISR - ipw_rx()             Detach ipw_rx_mem_buffers from pool up to the
+ *                            READ INDEX, detaching the SKB from the pool.
+ *                            Moves the packet buffer from queue to rx_used.
+ *                            Calls ipw_rx_queue_restock to refill any empty
+ *                            slots.
+ * ...
+ *
+ */
+
+/*
+ * If there are slots in the RX queue that  need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can pulling from rx_free.
+ *
+ * This moves the 'write' index forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+static void ipw_rx_queue_restock(struct ipw_priv *priv)
+{
+       struct ipw_rx_queue *rxq = priv->rxq;
+       struct list_head *element;
+       struct ipw_rx_mem_buffer *rxb;
+       unsigned long flags;
+       int write;
+
+       spin_lock_irqsave(&rxq->lock, flags);
+       write = rxq->write;
+       while ((rxq->write != rxq->processed) && (rxq->free_count)) {
+               element = rxq->rx_free.next;
+               rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
+               list_del(element);
+
+               ipw_write32(priv, CX2_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
+                           rxb->dma_addr);
+               rxq->queue[rxq->write] = rxb;
+               rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
+               rxq->free_count--;
+       }
+       spin_unlock_irqrestore(&rxq->lock, flags);
+
+       /* If the pre-allocated buffer pool is dropping low, schedule to
+        * refill it */
+       if (rxq->free_count <= RX_LOW_WATERMARK)
+               queue_work(priv->workqueue, &priv->rx_replenish);
+
+       /* If we've added more space for the firmware to place data, tell it */
+       if (write != rxq->write)
+               ipw_write32(priv, CX2_RX_WRITE_INDEX, rxq->write);
+}
+
+/*
+ * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
+ * Also restock the Rx queue via ipw_rx_queue_restock.
+ *
+ * This is called as a scheduled work item (except for during intialization)
+ */
+static void ipw_rx_queue_replenish(void *data)
+{
+       struct ipw_priv *priv = data;
+       struct ipw_rx_queue *rxq = priv->rxq;
+       struct list_head *element;
+       struct ipw_rx_mem_buffer *rxb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rxq->lock, flags);
+       while (!list_empty(&rxq->rx_used)) {
+               element = rxq->rx_used.next;
+               rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
+               rxb->skb = alloc_skb(CX2_RX_BUF_SIZE, GFP_ATOMIC);
+               if (!rxb->skb) {
+                       printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
+                              priv->net_dev->name);
+                       /* We don't reschedule replenish work here -- we will
+                        * call the restock method and if it still needs
+                        * more buffers it will schedule replenish */
+                       break;
+               }
+               list_del(element);
+
+               rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
+               rxb->dma_addr = pci_map_single(
+                       priv->pci_dev, rxb->skb->data, CX2_RX_BUF_SIZE,
+                       PCI_DMA_FROMDEVICE);
+
+               list_add_tail(&rxb->list, &rxq->rx_free);
+               rxq->free_count++;
+       }
+       spin_unlock_irqrestore(&rxq->lock, flags);
+
+       ipw_rx_queue_restock(priv);
+}
+
+/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
+ * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
+ * This free routine walks the list of POOL entries and if SKB is set to
+ * non NULL it is unmapped and freed
+ */
+static void ipw_rx_queue_free(struct ipw_priv *priv,
+                             struct ipw_rx_queue *rxq)
+{
+       int i;
+
+       if (!rxq)
+               return;
+
+       for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
+               if (rxq->pool[i].skb != NULL) {
+                       pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
+                                        CX2_RX_BUF_SIZE,
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb(rxq->pool[i].skb);
+               }
+       }
+
+       kfree(rxq);
+}
+
+static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
+{
+       struct ipw_rx_queue *rxq;
+       int i;
+
+       rxq = (struct ipw_rx_queue *)kmalloc(sizeof(*rxq), GFP_KERNEL);
+       memset(rxq, 0, sizeof(*rxq));
+       spin_lock_init(&rxq->lock);
+       INIT_LIST_HEAD(&rxq->rx_free);
+       INIT_LIST_HEAD(&rxq->rx_used);
+
+       /* Fill the rx_used queue with _all_ of the Rx buffers */
+       for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
+               list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+
+       /* Set us so that we have processed and used all buffers, but have
+        * not restocked the Rx queue with fresh buffers */
+       rxq->read = rxq->write = 0;
+       rxq->processed = RX_QUEUE_SIZE - 1;
+       rxq->free_count = 0;
+
+       return rxq;
+}
+
+static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
+{
+       rate &= ~IEEE80211_BASIC_RATE_MASK;
+       if (ieee_mode == IEEE_A) {
+               switch (rate) {
+               case IEEE80211_OFDM_RATE_6MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
+                               1 : 0;
+               case IEEE80211_OFDM_RATE_9MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
+                               1 : 0;
+               case IEEE80211_OFDM_RATE_12MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ?
+                               1 : 0;
+               case IEEE80211_OFDM_RATE_18MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ?
+                               1 : 0;
+               case IEEE80211_OFDM_RATE_24MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ?
+                               1 : 0;
+               case IEEE80211_OFDM_RATE_36MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ?
+                               1 : 0;
+               case IEEE80211_OFDM_RATE_48MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ?
+                               1 : 0;
+               case IEEE80211_OFDM_RATE_54MB:
+                       return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ?
+                               1 : 0;
+               default:
+                       return 0;
+               }
+       }
+
+       /* B and G mixed */
+       switch (rate) {
+       case IEEE80211_CCK_RATE_1MB:
+               return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
+       case IEEE80211_CCK_RATE_2MB:
+               return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
+       case IEEE80211_CCK_RATE_5MB:
+               return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
+       case IEEE80211_CCK_RATE_11MB:
+               return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
+       }
+
+       /* If we are limited to B modulations, bail at this point */
+       if (ieee_mode == IEEE_B)
+               return 0;
+
+       /* G */
+       switch (rate) {
+       case IEEE80211_OFDM_RATE_6MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
+       case IEEE80211_OFDM_RATE_9MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
+       case IEEE80211_OFDM_RATE_12MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
+       case IEEE80211_OFDM_RATE_18MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
+       case IEEE80211_OFDM_RATE_24MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
+       case IEEE80211_OFDM_RATE_36MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
+       case IEEE80211_OFDM_RATE_48MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
+       case IEEE80211_OFDM_RATE_54MB:
+               return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
+       }
+
+       return 0;
+}
+
+static int ipw_compatible_rates(struct ipw_priv *priv,
+                               const struct ieee80211_network *network,
+                               struct ipw_supported_rates *rates)
+{
+       int num_rates, i;
+
+       memset(rates, 0, sizeof(*rates));
+       num_rates = min(network->rates_len, (u8)IPW_MAX_RATES);
+       rates->num_rates = 0;
+       for (i = 0; i < num_rates; i++) {
+               if (!ipw_is_rate_in_mask(priv, network->mode, network->rates[i])) {
+                       IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
+                                      network->rates[i], priv->rates_mask);
+                       continue;
+               }
+
+               rates->supported_rates[rates->num_rates++] = network->rates[i];
+       }
+
+       num_rates = min(network->rates_ex_len, (u8)(IPW_MAX_RATES - num_rates));
+       for (i = 0; i < num_rates; i++) {
+               if (!ipw_is_rate_in_mask(priv, network->mode, network->rates_ex[i])) {
+                       IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
+                                      network->rates_ex[i], priv->rates_mask);
+                       continue;
+               }
+
+               rates->supported_rates[rates->num_rates++] = network->rates_ex[i];
+       }
+
+       return rates->num_rates;
+}
+
+static inline void ipw_copy_rates(struct ipw_supported_rates *dest,
+                                 const struct ipw_supported_rates *src)
+{
+       u8 i;
+       for (i = 0; i < src->num_rates; i++)
+               dest->supported_rates[i] = src->supported_rates[i];
+       dest->num_rates = src->num_rates;
+}
+
+/* TODO: Look at sniffed packets in the air to determine if the basic rate
+ * mask should ever be used -- right now all callers to add the scan rates are
+ * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
+static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
+                              u8 modulation, u32 rate_mask)
+{
+       u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
+               IEEE80211_BASIC_RATE_MASK : 0;
+
+       if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
+               rates->supported_rates[rates->num_rates++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
+
+       if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
+               rates->supported_rates[rates->num_rates++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
+
+       if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
+               rates->supported_rates[rates->num_rates++] = basic_mask |
+                       IEEE80211_CCK_RATE_5MB;
+
+       if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
+               rates->supported_rates[rates->num_rates++] = basic_mask |
+                       IEEE80211_CCK_RATE_11MB;
+}
+
+static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
+                               u8 modulation, u32 rate_mask)
+{
+       u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
+               IEEE80211_BASIC_RATE_MASK : 0;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
+               rates->supported_rates[rates->num_rates++] = basic_mask |
+                       IEEE80211_OFDM_RATE_6MB;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
+               rates->supported_rates[rates->num_rates++] =
+                       IEEE80211_OFDM_RATE_9MB;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
+               rates->supported_rates[rates->num_rates++] = basic_mask |
+                       IEEE80211_OFDM_RATE_12MB;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
+               rates->supported_rates[rates->num_rates++] =
+                       IEEE80211_OFDM_RATE_18MB;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
+               rates->supported_rates[rates->num_rates++] = basic_mask |
+                       IEEE80211_OFDM_RATE_24MB;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
+               rates->supported_rates[rates->num_rates++] =
+                       IEEE80211_OFDM_RATE_36MB;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
+               rates->supported_rates[rates->num_rates++] =
+                       IEEE80211_OFDM_RATE_48MB;
+
+       if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
+               rates->supported_rates[rates->num_rates++] =
+                       IEEE80211_OFDM_RATE_54MB;
+}
+
+struct ipw_network_match {
+       struct ieee80211_network *network;
+       struct ipw_supported_rates rates;
+};
+
+static int ipw_best_network(
+       struct ipw_priv *priv,
+       struct ipw_network_match *match,
+       struct ieee80211_network *network,
+       int roaming)
+{
+       struct ipw_supported_rates rates;
+
+       /* Verify that this network's capability is compatible with the
+        * current mode (AdHoc or Infrastructure) */
+       if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
+            !(network->capability & WLAN_CAPABILITY_ESS)) ||
+           (priv->ieee->iw_mode == IW_MODE_ADHOC &&
+            !(network->capability & WLAN_CAPABILITY_IBSS))) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
+                               "capability mismatch.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid));
+               return 0;
+       }
+
+       /* If we do not have an ESSID for this AP, we can not associate with
+        * it */
+       if (network->flags & NETWORK_EMPTY_ESSID) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of hidden ESSID.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid));
+               return 0;
+       }
+
+       if (unlikely(roaming)) {
+               /* If we are roaming, then ensure check if this is a valid
+                * network to try and roam to */
+               if ((network->ssid_len != match->network->ssid_len) ||
+                   memcmp(network->ssid, match->network->ssid,
+                          network->ssid_len)) {
+                       IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
+                                       "because of non-network ESSID.\n",
+                                       escape_essid(network->ssid,
+                                                    network->ssid_len),
+                                       MAC_ARG(network->bssid));
+                       return 0;
+               }
+       } else {
+               /* If an ESSID has been configured then compare the broadcast
+                * ESSID to ours */
+               if ((priv->config & CFG_STATIC_ESSID) &&
+                   ((network->ssid_len != priv->essid_len) ||
+                    memcmp(network->ssid, priv->essid,
+                           min(network->ssid_len, priv->essid_len)))) {
+                       char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+                       strncpy(escaped, escape_essid(
+                                       network->ssid, network->ssid_len),
+                               sizeof(escaped));
+                       IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                                       "because of ESSID mismatch: '%s'.\n",
+                                       escaped, MAC_ARG(network->bssid),
+                                       escape_essid(priv->essid, priv->essid_len));
+                       return 0;
+               }
+       }
+
+       /* If the old network rate is better than this one, don't bother
+        * testing everything else. */
+       if (match->network && match->network->stats.rssi >
+           network->stats.rssi) {
+               char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+               strncpy(escaped,
+                       escape_essid(network->ssid, network->ssid_len),
+                       sizeof(escaped));
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
+                               "'%s (" MAC_FMT ")' has a stronger signal.\n",
+                               escaped, MAC_ARG(network->bssid),
+                               escape_essid(match->network->ssid,
+                                            match->network->ssid_len),
+                               MAC_ARG(match->network->bssid));
+               return 0;
+       }
+
+       /* If this network has already had an association attempt within the
+        * last 3 seconds, do not try and associate again... */
+       if (network->last_associate &&
+           time_after(network->last_associate + (HZ * 5UL), jiffies)) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of storming (%lu since last "
+                               "assoc attempt).\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid),
+                               (jiffies - network->last_associate) / HZ);
+               return 0;
+       }
+
+       /* Now go through and see if the requested network is valid... */
+       if (priv->ieee->scan_age != 0 &&
+           jiffies - network->last_scanned > priv->ieee->scan_age) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of age: %lums.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid),
+                               (jiffies - network->last_scanned) / (HZ / 100));
+               return 0;
+       }
+
+       if ((priv->config & CFG_STATIC_CHANNEL) &&
+           (network->channel != priv->channel)) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of channel mismatch: %d != %d.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid),
+                               network->channel, priv->channel);
+               return 0;
+       }
+
+       /* Verify privacy compatability */
+       if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
+           ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of privacy mismatch: %s != %s.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid),
+                               priv->capability & CAP_PRIVACY_ON ? "on" :
+                               "off",
+                               network->capability &
+                               WLAN_CAPABILITY_PRIVACY ?"on" : "off");
+               return 0;
+       }
+
+       if ((priv->config & CFG_STATIC_BSSID) &&
+           memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of BSSID mismatch: " MAC_FMT ".\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid),
+                               MAC_ARG(priv->bssid));
+               return 0;
+       }
+
+       /* Filter out any incompatible freq / mode combinations */
+       if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of invalid frequency/mode "
+                               "combination.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid));
+               return 0;
+       }
+
+       ipw_compatible_rates(priv, network, &rates);
+       if (rates.num_rates == 0) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of no compatible rates.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid));
+               return 0;
+       }
+
+       /* TODO: Perform any further minimal comparititive tests.  We do not
+        * want to put too much policy logic here; intelligent scan selection
+        * should occur within a generic IEEE 802.11 user space tool.  */
+
+       /* Set up 'new' AP to this network */
+       ipw_copy_rates(&match->rates, &rates);
+       match->network = network;
+
+       IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
+                       escape_essid(network->ssid, network->ssid_len),
+                       MAC_ARG(network->bssid));
+
+       return 1;
+}
+
+
+static void ipw_adhoc_create(struct ipw_priv *priv,
+                           struct ieee80211_network *network)
+{
+       /*
+        * For the purposes of scanning, we can set our wireless mode
+        * to trigger scans across combinations of bands, but when it
+        * comes to creating a new ad-hoc network, we have tell the FW
+        * exactly which band to use.
+        *
+        * We also have the possibility of an invalid channel for the
+        * chossen band.  Attempting to create a new ad-hoc network
+        * with an invalid channel for wireless mode will trigger a
+        * FW fatal error.
+        */
+       network->mode = is_valid_channel(priv->ieee->mode, priv->channel);
+       if (network->mode) {
+               network->channel = priv->channel;
+       } else {
+               IPW_WARNING("Overriding invalid channel\n");
+               if (priv->ieee->mode & IEEE_A) {
+                       network->mode = IEEE_A;
+                       priv->channel = band_a_active_channel[0];
+               } else if (priv->ieee->mode & IEEE_G) {
+                       network->mode = IEEE_G;
+                       priv->channel = band_b_active_channel[0];
+               } else {
+                       network->mode = IEEE_B;
+                       priv->channel = band_b_active_channel[0];
+               }
+       }
+
+       network->channel = priv->channel;
+       priv->config |= CFG_ADHOC_PERSIST;
+       ipw_create_bssid(priv, network->bssid);
+       network->ssid_len = priv->essid_len;
+       memcpy(network->ssid, priv->essid, priv->essid_len);
+       memset(&network->stats, 0, sizeof(network->stats));
+       network->capability = WLAN_CAPABILITY_IBSS;
+       if (priv->capability & CAP_PRIVACY_ON)
+               network->capability |= WLAN_CAPABILITY_PRIVACY;
+       network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
+       memcpy(network->rates, priv->rates.supported_rates,
+              network->rates_len);
+       network->rates_ex_len = priv->rates.num_rates - network->rates_len;
+       memcpy(network->rates_ex,
+              &priv->rates.supported_rates[network->rates_len],
+              network->rates_ex_len);
+       network->last_scanned = 0;
+       network->flags = 0;
+       network->last_associate = 0;
+       network->time_stamp[0] = 0;
+       network->time_stamp[1] = 0;
+       network->beacon_interval = 100; /* Default */
+       network->listen_interval = 10;  /* Default */
+       network->atim_window = 0;       /* Default */
+#ifdef CONFIG_IEEE80211_WPA
+       network->wpa_ie_len = 0;
+       network->rsn_ie_len = 0;
+#endif /* CONFIG_IEEE80211_WPA */
+}
+
+static void ipw_send_wep_keys(struct ipw_priv *priv)
+{
+       struct ipw_wep_key *key;
+       int i;
+       struct host_cmd cmd = {
+               .cmd = IPW_CMD_WEP_KEY,
+               .len = sizeof(*key)
+       };
+
+       key = (struct ipw_wep_key *)&cmd.param;
+       key->cmd_id = DINO_CMD_WEP_KEY;
+       key->seq_num = 0;
+
+       for (i = 0; i < 4; i++) {
+               key->key_index = i;
+               if (!(priv->sec.flags & (1 << i))) {
+                       key->key_size = 0;
+               } else {
+                       key->key_size = priv->sec.key_sizes[i];
+                       memcpy(key->key, priv->sec.keys[i], key->key_size);
+               }
+
+               if (ipw_send_cmd(priv, &cmd)) {
+                       IPW_ERROR("failed to send WEP_KEY command\n");
+                       return;
+               }
+       }
+}
+
+static void ipw_adhoc_check(void *data)
+{
+       struct ipw_priv *priv = data;
+
+       if (priv->missed_adhoc_beacons++ > priv->missed_beacon_threshold &&
+           !(priv->config & CFG_ADHOC_PERSIST)) {
+               IPW_DEBUG_SCAN("Disassociating due to missed beacons\n");
+               ipw_remove_current_network(priv);
+               ipw_disassociate(priv);
+               return;
+       }
+
+       queue_delayed_work(priv->workqueue, &priv->adhoc_check,
+                          priv->assoc_request.beacon_interval);
+}
+
+#ifdef CONFIG_IPW_DEBUG
+static void ipw_debug_config(struct ipw_priv *priv)
+{
+       IPW_DEBUG_INFO("Scan completed, no valid APs matched "
+                      "[CFG 0x%08X]\n", priv->config);
+       if (priv->config & CFG_STATIC_CHANNEL)
+               IPW_DEBUG_INFO("Channel locked to %d\n",
+                              priv->channel);
+       else
+               IPW_DEBUG_INFO("Channel unlocked.\n");
+       if (priv->config & CFG_STATIC_ESSID)
+               IPW_DEBUG_INFO("ESSID locked to '%s'\n",
+                              escape_essid(priv->essid,
+                                           priv->essid_len));
+       else
+               IPW_DEBUG_INFO("ESSID unlocked.\n");
+       if (priv->config & CFG_STATIC_BSSID)
+               IPW_DEBUG_INFO("BSSID locked to %d\n", priv->channel);
+       else
+               IPW_DEBUG_INFO("BSSID unlocked.\n");
+       if (priv->capability & CAP_PRIVACY_ON)
+               IPW_DEBUG_INFO("PRIVACY on\n");
+       else
+               IPW_DEBUG_INFO("PRIVACY off\n");
+       IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
+}
+#else
+#define ipw_debug_config(x) do {} while (0)
+#endif
+
+static inline void ipw_set_fixed_rate(struct ipw_priv *priv,
+                                     struct ieee80211_network *network)
+{
+       /* TODO: Verify that this works... */
+       struct ipw_fixed_rate fr = {
+               .tx_rates = priv->rates_mask
+       };
+       u32 reg;
+       u16 mask = 0;
+
+       /* Identify 'current FW band' and match it with the fixed
+        * Tx rates */
+
+       switch (priv->ieee->freq_band) {
+       case IEEE80211_52GHZ_BAND: /* A only */
+               /* IEEE_A */
+               if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
+                       /* Invalid fixed rate mask */
+                       fr.tx_rates = 0;
+                       break;
+               }
+
+               fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
+               break;
+
+       default: /* 2.4Ghz or Mixed */
+               /* IEEE_B */
+               if (network->mode == IEEE_B) {
+                       if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
+                               /* Invalid fixed rate mask */
+                               fr.tx_rates = 0;
+                       }
+                       break;
+               }
+
+               /* IEEE_G */
+               if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
+                                   IEEE80211_OFDM_RATES_MASK)) {
+                       /* Invalid fixed rate mask */
+                       fr.tx_rates = 0;
+                       break;
+               }
+
+               if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
+                       mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
+                       fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
+               }
+
+               if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
+                       mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
+                       fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
+               }
+
+               if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
+                       mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
+                       fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
+               }
+
+               fr.tx_rates |= mask;
+               break;
+       }
+
+       reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
+       ipw_write_reg32(priv, reg, *(u32*)&fr);
+}
+
+static int ipw_associate_network(struct ipw_priv *priv,
+                                struct ieee80211_network *network,
+                                struct ipw_supported_rates *rates,
+                                int roaming)
+{
+       int err;
+
+       if (priv->config & CFG_FIXED_RATE)
+               ipw_set_fixed_rate(priv, network);
+
+       if (!(priv->config & CFG_STATIC_ESSID)) {
+               priv->essid_len = min(network->ssid_len,
+                                     (u8)IW_ESSID_MAX_SIZE);
+               memcpy(priv->essid, network->ssid, priv->essid_len);
+       }
+
+       network->last_associate = jiffies;
+
+       memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
+       priv->assoc_request.channel = network->channel;
+       if ((priv->capability & CAP_PRIVACY_ON) &&
+           (priv->capability & CAP_SHARED_KEY)) {
+               priv->assoc_request.auth_type = AUTH_SHARED_KEY;
+               priv->assoc_request.auth_key = priv->sec.active_key;
+       } else {
+               priv->assoc_request.auth_type = AUTH_OPEN;
+               priv->assoc_request.auth_key = 0;
+       }
+
+       if (priv->capability & CAP_PRIVACY_ON)
+               ipw_send_wep_keys(priv);
+
+       /*
+        * It is valid for our ieee device to support multiple modes, but
+        * when it comes to associating to a given network we have to choose
+        * just one mode.
+        */
+       if (network->mode & priv->ieee->mode & IEEE_A)
+               priv->assoc_request.ieee_mode = IPW_A_MODE;
+       else if (network->mode & priv->ieee->mode & IEEE_G)
+               priv->assoc_request.ieee_mode = IPW_G_MODE;
+       else if (network->mode & priv->ieee->mode & IEEE_B)
+               priv->assoc_request.ieee_mode = IPW_B_MODE;
+
+       IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
+                       "802.11%c [%d], enc=%s%s%s%c%c\n",
+                       roaming ? "Rea" : "A",
+                       escape_essid(priv->essid, priv->essid_len),
+                       network->channel,
+                       ipw_modes[priv->assoc_request.ieee_mode],
+                       rates->num_rates,
+                       priv->capability & CAP_PRIVACY_ON ? "on " : "off",
+                       priv->capability & CAP_PRIVACY_ON ?
+                       (priv->capability & CAP_SHARED_KEY ? "(shared)" :
+                        "(open)") : "",
+                       priv->capability & CAP_PRIVACY_ON ? " key=" : "",
+                       priv->capability & CAP_PRIVACY_ON ?
+                       '1' + priv->sec.active_key : '.',
+                       priv->capability & CAP_PRIVACY_ON ?
+                       '.' : ' ');
+
+       priv->assoc_request.beacon_interval = network->beacon_interval;
+       if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
+           (network->time_stamp[0] == 0) &&
+           (network->time_stamp[1] == 0)) {
+               priv->assoc_request.assoc_type = HC_IBSS_START;
+               priv->assoc_request.assoc_tsf_msw = 0;
+               priv->assoc_request.assoc_tsf_lsw = 0;
+       } else {
+               if (unlikely(roaming))
+                       priv->assoc_request.assoc_type = HC_REASSOCIATE;
+               else
+                       priv->assoc_request.assoc_type = HC_ASSOCIATE;
+               priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
+               priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
+       }
+
+       memcpy(&priv->assoc_request.bssid, network->bssid, ETH_ALEN);
+
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+               memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
+               priv->assoc_request.atim_window = network->atim_window;
+       } else {
+               memcpy(&priv->assoc_request.dest, network->bssid,
+                      ETH_ALEN);
+               priv->assoc_request.atim_window = 0;
+       }
+
+       priv->assoc_request.capability = network->capability;
+       priv->assoc_request.listen_interval = network->listen_interval;
+
+       err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
+       if (err) {
+               IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
+               return err;
+       }
+
+       rates->ieee_mode = priv->assoc_request.ieee_mode;
+       rates->purpose = IPW_RATE_CONNECT;
+       ipw_send_supported_rates(priv, rates);
+
+       if (priv->assoc_request.ieee_mode == IPW_G_MODE)
+               priv->sys_config.dot11g_auto_detection = 1;
+       else
+               priv->sys_config.dot11g_auto_detection = 0;
+       err = ipw_send_system_config(priv, &priv->sys_config);
+       if (err) {
+               IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
+               return err;
+       }
+
+       IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
+       err = ipw_set_sensitivity(priv, network->stats.rssi);
+       if (err) {
+               IPW_DEBUG_HC("Attempt to send associate command failed.\n");
+               return err;
+       }
+
+       /*
+        * If preemption is enabled, it is possible for the association
+        * to complete before we return from ipw_send_associate.  Therefore
+        * we have to be sure and update our priviate data first.
+        */
+       priv->channel = network->channel;
+       memcpy(priv->bssid, network->bssid, ETH_ALEN);
+       priv->status |= STATUS_ASSOCIATING;
+       priv->status &= ~STATUS_SECURITY_UPDATED;
+
+       priv->assoc_network = network;
+
+       err = ipw_send_associate(priv, &priv->assoc_request);
+       if (err) {
+               IPW_DEBUG_HC("Attempt to send associate command failed.\n");
+               return err;
+       }
+
+       IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
+                 escape_essid(priv->essid, priv->essid_len),
+                 MAC_ARG(priv->bssid));
+
+       return 0;
+}
+
+static void ipw_roam(void *data)
+{
+       struct ipw_priv *priv = data;
+       struct ieee80211_network *network = NULL;
+       struct ipw_network_match match = {
+               .network = priv->assoc_network
+       };
+
+       /* The roaming process is as follows:
+        *
+        * 1.  Missed beacon threshold triggers the roaming process by
+        *     setting the status ROAM bit and requesting a scan.
+        * 2.  When the scan completes, it schedules the ROAM work
+        * 3.  The ROAM work looks at all of the known networks for one that
+        *     is a better network than the currently associated.  If none
+        *     found, the ROAM process is over (ROAM bit cleared)
+        * 4.  If a better network is found, a disassociation request is
+        *     sent.
+        * 5.  When the disassociation completes, the roam work is again
+        *     scheduled.  The second time through, the driver is no longer
+        *     associated, and the newly selected network is sent an
+        *     association request.
+        * 6.  At this point ,the roaming process is complete and the ROAM
+        *     status bit is cleared.
+        */
+
+       /* If we are no longer associated, and the roaming bit is no longer
+        * set, then we are not actively roaming, so just return */
+       if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
+               return;
+
+       if (priv->status & STATUS_ASSOCIATED) {
+               /* First pass through ROAM process -- look for a better
+                * network */
+               u8 rssi = priv->assoc_network->stats.rssi;
+               priv->assoc_network->stats.rssi = -128;
+               list_for_each_entry(network, &priv->ieee->network_list, list) {
+                       if (network != priv->assoc_network)
+                               ipw_best_network(priv, &match, network, 1);
+               }
+               priv->assoc_network->stats.rssi = rssi;
+
+               if (match.network == priv->assoc_network) {
+                       IPW_DEBUG_ASSOC("No better APs in this network to "
+                                       "roam to.\n");
+                       priv->status &= ~STATUS_ROAMING;
+                       ipw_debug_config(priv);
+                       return;
+               }
+
+               ipw_send_disassociate(priv, 1);
+               priv->assoc_network = match.network;
+
+               return;
+       }
+
+       /* Second pass through ROAM process -- request association */
+       ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
+       ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
+       priv->status &= ~STATUS_ROAMING;
+}
+
+static void ipw_associate(void *data)
+{
+       struct ipw_priv *priv = data;
+
+       struct ieee80211_network *network = NULL;
+       struct ipw_network_match match = {
+               .network = NULL
+       };
+       struct ipw_supported_rates *rates;
+       struct list_head *element;
+
+       if (!(priv->config & CFG_ASSOCIATE) &&
+           !(priv->config & (CFG_STATIC_ESSID |
+                             CFG_STATIC_CHANNEL |
+                             CFG_STATIC_BSSID))) {
+               IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
+               return;
+       }
+
+       list_for_each_entry(network, &priv->ieee->network_list, list)
+               ipw_best_network(priv, &match, network, 0);
+
+       network = match.network;
+       rates = &match.rates;
+
+       if (network == NULL &&
+           priv->ieee->iw_mode == IW_MODE_ADHOC &&
+           priv->config & CFG_ADHOC_CREATE &&
+           priv->config & CFG_STATIC_ESSID &&
+           !list_empty(&priv->ieee->network_free_list)) {
+               element = priv->ieee->network_free_list.next;
+               network = list_entry(element, struct ieee80211_network,
+                                    list);
+               ipw_adhoc_create(priv, network);
+               rates = &priv->rates;
+               list_del(element);
+               list_add_tail(&network->list, &priv->ieee->network_list);
+       }
+
+       /* If we reached the end of the list, then we don't have any valid
+        * matching APs */
+       if (!network) {
+               ipw_debug_config(priv);
+
+               queue_delayed_work(priv->workqueue, &priv->request_scan,
+                                  SCAN_INTERVAL);
+
+               return;
+       }
+
+       ipw_associate_network(priv, network, rates, 0);
+}
+
+static inline void ipw_handle_data_packet(struct ipw_priv *priv,
+                                             struct ipw_rx_mem_buffer *rxb,
+                                             struct ieee80211_rx_stats *stats)
+{
+       struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+
+       /* We received data from the HW, so stop the watchdog */
+       priv->net_dev->trans_start = jiffies;
+
+       /* We only process data packets if the
+        * interface is open */
+       if (unlikely((pkt->u.frame.length + IPW_RX_FRAME_SIZE) >
+                    skb_tailroom(rxb->skb))) {
+               priv->ieee->stats.rx_errors++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+               return;
+       } else if (unlikely(!netif_running(priv->net_dev))) {
+               priv->ieee->stats.rx_dropped++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+               return;
+       }
+
+       /* Advance skb->data to the start of the actual payload */
+       skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
+
+       /* Set the size of the skb to the size of the frame */
+       skb_put(rxb->skb, pkt->u.frame.length);
+
+       IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
+
+       if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
+               priv->ieee->stats.rx_errors++;
+       else /* ieee80211_rx succeeded, so it now owns the SKB */
+               rxb->skb = NULL;
+}
+
+
+/*
+ * Main entry function for recieving a packet with 80211 headers.  This
+ * should be called when ever the FW has notified us that there is a new
+ * skb in the recieve queue.
+ */
+static void ipw_rx(struct ipw_priv *priv)
+{
+       struct ipw_rx_mem_buffer *rxb;
+       struct ipw_rx_packet *pkt;
+       struct ieee80211_hdr *header;
+       u32 r, w, i;
+       u8 network_packet;
+
+       r = ipw_read32(priv, CX2_RX_READ_INDEX);
+       w = ipw_read32(priv, CX2_RX_WRITE_INDEX);
+       i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
+
+       while (i != r) {
+               rxb = priv->rxq->queue[i];
+#ifdef CONFIG_IPW_DEBUG
+               if (unlikely(rxb == NULL)) {
+                       printk(KERN_CRIT "Queue not allocated!\n");
+                       break;
+               }
+#endif
+               priv->rxq->queue[i] = NULL;
+
+               pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
+                                           CX2_RX_BUF_SIZE,
+                                           PCI_DMA_FROMDEVICE);
+
+               pkt = (struct ipw_rx_packet *)rxb->skb->data;
+               IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
+                            pkt->header.message_type,
+                            pkt->header.rx_seq_num,
+                            pkt->header.control_bits);
+
+               switch (pkt->header.message_type) {
+               case RX_FRAME_TYPE: /* 802.11 frame */ {
+                       struct ieee80211_rx_stats stats = {
+                               .rssi = pkt->u.frame.rssi_dbm -
+                               IPW_RSSI_TO_DBM,
+                               .signal = pkt->u.frame.signal,
+                               .rate = pkt->u.frame.rate,
+                               .mac_time = jiffies,
+                               .received_channel =
+                               pkt->u.frame.received_channel,
+                               .freq = (pkt->u.frame.control & (1<<0)) ?
+                               IEEE80211_24GHZ_BAND : IEEE80211_52GHZ_BAND,
+                               .len = pkt->u.frame.length,
+                       };
+
+                       if (stats.rssi != 0)
+                               stats.mask |= IEEE80211_STATMASK_RSSI;
+                       if (stats.signal != 0)
+                               stats.mask |= IEEE80211_STATMASK_SIGNAL;
+                       if (stats.rate != 0)
+                               stats.mask |= IEEE80211_STATMASK_RATE;
+
+                       priv->rx_packets++;
+
+#ifdef CONFIG_IPW_PROMISC
+                       if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+                               ipw_handle_data_packet(priv, rxb, &stats);
+                               break;
+                       }
+#endif
+
+                       header = (struct ieee80211_hdr *)(rxb->skb->data +
+                                                         IPW_RX_FRAME_SIZE);
+                               /* TODO: Check Ad-Hoc dest/source and make sure
+                                * that we are actually parsing these packets
+                                * correctly -- we should probably use the
+                                * frame control of the packet and disregard
+                                * the current iw_mode */
+                       switch (priv->ieee->iw_mode) {
+                       case IW_MODE_ADHOC:
+                               network_packet =
+                                       !memcmp(header->addr1,
+                                               priv->net_dev->dev_addr,
+                                               ETH_ALEN) ||
+                                       !memcmp(header->addr3,
+                                               priv->bssid, ETH_ALEN) ||
+                                       is_broadcast_ether_addr(header->addr1) ||
+                                       is_multicast_ether_addr(header->addr1);
+                               break;
+
+                       case IW_MODE_INFRA:
+                       default:
+                               network_packet =
+                                       !memcmp(header->addr3,
+                                               priv->bssid, ETH_ALEN) ||
+                                       !memcmp(header->addr1,
+                                               priv->net_dev->dev_addr,
+                                               ETH_ALEN) ||
+                                       is_broadcast_ether_addr(header->addr1) ||
+                                       is_multicast_ether_addr(header->addr1);
+                               break;
+                       }
+
+                       if (network_packet && priv->assoc_network) {
+                               priv->assoc_network->stats.rssi = stats.rssi;
+                               average_add(&priv->average_rssi,
+                                           stats.rssi);
+                               priv->last_rx_rssi = stats.rssi;
+                       }
+
+                       IPW_DEBUG_RX("Frame: len=%u\n", pkt->u.frame.length);
+
+                       if (pkt->u.frame.length < frame_hdr_len(header)) {
+                               IPW_DEBUG_DROP("Received packet is too small. "
+                                              "Dropping.\n");
+                               priv->ieee->stats.rx_errors++;
+                               priv->wstats.discard.misc++;
+                               break;
+                       }
+
+                       switch (WLAN_FC_GET_TYPE(header->frame_ctl)) {
+                       case IEEE80211_FTYPE_MGMT:
+                               ieee80211_rx_mgt(priv->ieee, header, &stats);
+                               if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
+                                   ((WLAN_FC_GET_STYPE(header->frame_ctl) ==
+                                     IEEE80211_STYPE_PROBE_RESP) ||
+                                    (WLAN_FC_GET_STYPE(header->frame_ctl) ==
+                                     IEEE80211_STYPE_BEACON)) &&
+                                   !memcmp(header->addr3, priv->bssid, ETH_ALEN))
+                                       ipw_add_station(priv, header->addr2);
+                               break;
+
+                       case IEEE80211_FTYPE_CTL:
+                               break;
+
+                       case IEEE80211_FTYPE_DATA:
+                               if (network_packet)
+                                       ipw_handle_data_packet(priv, rxb, &stats);
+                               else
+                                       IPW_DEBUG_DROP("Dropping: " MAC_FMT
+                                                      ", " MAC_FMT ", " MAC_FMT "\n",
+                                                      MAC_ARG(header->addr1), MAC_ARG(header->addr2),
+                                                      MAC_ARG(header->addr3));
+                               break;
+                       }
+                       break;
+               }
+
+               case RX_HOST_NOTIFICATION_TYPE: {
+                       IPW_DEBUG_RX("Notification: subtype=%02X flags=%02X size=%d\n",
+                                    pkt->u.notification.subtype,
+                                    pkt->u.notification.flags,
+                                    pkt->u.notification.size);
+                       ipw_rx_notification(priv, &pkt->u.notification);
+                       break;
+               }
+
+               default:
+                       IPW_DEBUG_RX("Bad Rx packet of type %d\n",
+                                    pkt->header.message_type);
+                       break;
+               }
+
+               /* For now we just don't re-use anything.  We can tweak this
+                * later to try and re-use notification packets and SKBs that
+                * fail to Rx correctly */
+               if (rxb->skb != NULL) {
+                       dev_kfree_skb_any(rxb->skb);
+                       rxb->skb = NULL;
+               }
+
+               pci_unmap_single(priv->pci_dev, rxb->dma_addr,
+                                CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+               list_add_tail(&rxb->list, &priv->rxq->rx_used);
+
+               i = (i + 1) % RX_QUEUE_SIZE;
+       }
+
+       /* Backtrack one entry */
+       priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
+
+       ipw_rx_queue_restock(priv);
+}
+
+static void ipw_abort_scan(struct ipw_priv *priv)
+{
+       int err;
+
+       if (priv->status & STATUS_SCAN_ABORTING) {
+               IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
+               return;
+       }
+       priv->status |= STATUS_SCAN_ABORTING;
+
+       err = ipw_send_scan_abort(priv);
+       if (err)
+               IPW_DEBUG_HC("Request to abort scan failed.\n");
+}
+
+static int ipw_request_scan(struct ipw_priv *priv)
+{
+       struct ipw_scan_request_ext scan;
+       int channel_index = 0;
+       int i, err, scan_type;
+
+       if (priv->status & STATUS_EXIT_PENDING) {
+               IPW_DEBUG_SCAN("Aborting scan due to device shutdown\n");
+               priv->status |= STATUS_SCAN_PENDING;
+               return 0;
+       }
+
+       if (priv->status & STATUS_SCANNING) {
+               IPW_DEBUG_HC("Concurrent scan requested.  Aborting first.\n");
+               priv->status |= STATUS_SCAN_PENDING;
+               ipw_abort_scan(priv);
+               return 0;
+       }
+
+       if (priv->status & STATUS_SCAN_ABORTING) {
+               IPW_DEBUG_HC("Scan request while abort pending.  Queuing.\n");
+               priv->status |= STATUS_SCAN_PENDING;
+               return 0;
+       }
+
+       if (priv->status & STATUS_RF_KILL_MASK) {
+               IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
+               priv->status |= STATUS_SCAN_PENDING;
+               return 0;
+       }
+
+       memset(&scan, 0, sizeof(scan));
+
+       scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = 20;
+       scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = 20;
+       scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = 20;
+
+       scan.full_scan_index = ieee80211_get_scans(priv->ieee);
+       /* If we are roaming, then make this a directed scan for the current
+        * network.  Otherwise, ensure that every other scan is a fast
+        * channel hop scan */
+       if ((priv->status & STATUS_ROAMING) || (
+                   !(priv->status & STATUS_ASSOCIATED) &&
+                   (priv->config & CFG_STATIC_ESSID) &&
+                   (scan.full_scan_index % 2))) {
+               err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
+               if (err) {
+                       IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
+                       return err;
+               }
+
+               scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
+       } else {
+               scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
+       }
+
+        if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
+               int start = channel_index;
+               for (i = 0; i < MAX_A_CHANNELS; i++) {
+                       if (band_a_active_channel[i] == 0)
+                               break;
+                       if ((priv->status & STATUS_ASSOCIATED) &&
+                           band_a_active_channel[i] == priv->channel)
+                               continue;
+                       channel_index++;
+                       scan.channels_list[channel_index] =
+                               band_a_active_channel[i];
+                       ipw_set_scan_type(&scan, channel_index, scan_type);
+               }
+
+               if (start != channel_index) {
+                       scan.channels_list[start] = (u8)(IPW_A_MODE << 6) |
+                               (channel_index - start);
+                       channel_index++;
+               }
+       }
+
+        if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
+               int start = channel_index;
+               for (i = 0; i < MAX_B_CHANNELS; i++) {
+                       if (band_b_active_channel[i] == 0)
+                               break;
+                       if ((priv->status & STATUS_ASSOCIATED) &&
+                           band_b_active_channel[i] == priv->channel)
+                               continue;
+                       channel_index++;
+                       scan.channels_list[channel_index] =
+                               band_b_active_channel[i];
+                       ipw_set_scan_type(&scan, channel_index, scan_type);
+               }
+
+               if (start != channel_index) {
+                       scan.channels_list[start] = (u8)(IPW_B_MODE << 6) |
+                               (channel_index - start);
+               }
+       }
+
+       err = ipw_send_scan_request_ext(priv, &scan);
+       if (err) {
+               IPW_DEBUG_HC("Sending scan command failed: %08X\n",
+                            err);
+               return -EIO;
+       }
+
+       priv->status |= STATUS_SCANNING;
+       priv->status &= ~STATUS_SCAN_PENDING;
+
+       return 0;
+}
+
+/*
+ * This file defines the Wireless Extension handlers.  It does not
+ * define any methods of hardware manipulation and relies on the
+ * functions defined in ipw_main to provide the HW interaction.
+ *
+ * The exception to this is the use of the ipw_get_ordinal()
+ * function used to poll the hardware vs. making unecessary calls.
+ *
+ */
+
+static int ipw_wx_get_name(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       if (!(priv->status & STATUS_ASSOCIATED))
+               strcpy(wrqu->name, "unassociated");
+       else
+               snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
+                        ipw_modes[priv->assoc_request.ieee_mode]);
+       IPW_DEBUG_WX("Name: %s\n", wrqu->name);
+       return 0;
+}
+
+static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
+{
+       if (channel == 0) {
+               IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
+               priv->config &= ~CFG_STATIC_CHANNEL;
+               if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
+                                     STATUS_ASSOCIATING))) {
+                       IPW_DEBUG_ASSOC("Attempting to associate with new "
+                                       "parameters.\n");
+                       ipw_associate(priv);
+               }
+
+               return 0;
+       }
+
+       priv->config |= CFG_STATIC_CHANNEL;
+
+       if (priv->channel == channel) {
+               IPW_DEBUG_INFO(
+                       "Request to set channel to current value (%d)\n",
+                       channel);
+               return 0;
+       }
+
+       IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
+       priv->channel = channel;
+
+       /* If we are currently associated, or trying to associate
+        * then see if this is a new channel (causing us to disassociate) */
+       if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+               IPW_DEBUG_ASSOC("Disassociating due to channel change.\n");
+               ipw_disassociate(priv);
+       } else {
+               ipw_associate(priv);
+       }
+
+       return 0;
+}
+
+static int ipw_wx_set_freq(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       struct iw_freq *fwrq = &wrqu->freq;
+
+       /* if setting by freq convert to channel */
+       if (fwrq->e == 1) {
+               if ((fwrq->m >= (int) 2.412e8 &&
+                    fwrq->m <= (int) 2.487e8)) {
+                       int f = fwrq->m / 100000;
+                       int c = 0;
+
+                       while ((c < REG_MAX_CHANNEL) &&
+                              (f != ipw_frequencies[c]))
+                               c++;
+
+                       /* hack to fall through */
+                       fwrq->e = 0;
+                       fwrq->m = c + 1;
+               }
+       }
+
+       if (fwrq->e > 0 || fwrq->m > 1000)
+               return -EOPNOTSUPP;
+
+       IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
+       return ipw_set_channel(priv, (u8)fwrq->m);
+
+       return 0;
+}
+
+
+static int ipw_wx_get_freq(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       wrqu->freq.e = 0;
+
+       /* If we are associated, trying to associate, or have a statically
+        * configured CHANNEL then return that; otherwise return ANY */
+       if (priv->config & CFG_STATIC_CHANNEL ||
+           priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
+               wrqu->freq.m = priv->channel;
+       else
+               wrqu->freq.m = 0;
+
+       IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
+       return 0;
+}
+
+static int ipw_wx_set_mode(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       int err = 0;
+
+       IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
+
+       if (wrqu->mode == priv->ieee->iw_mode)
+               return 0;
+
+       switch (wrqu->mode) {
+#ifdef CONFIG_IPW_PROMISC
+       case IW_MODE_MONITOR:
+#endif
+       case IW_MODE_ADHOC:
+       case IW_MODE_INFRA:
+               break;
+       case IW_MODE_AUTO:
+               wrqu->mode = IW_MODE_INFRA;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+#ifdef CONFIG_IPW_PROMISC
+       if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+               priv->net_dev->type = ARPHRD_ETHER;
+
+       if (wrqu->mode == IW_MODE_MONITOR)
+               priv->net_dev->type = ARPHRD_IEEE80211;
+#endif /* CONFIG_IPW_PROMISC */
+
+#ifdef CONFIG_PM
+       /* Free the existing firmware and reset the fw_loaded
+        * flag so ipw_load() will bring in the new firmawre */
+       if (fw_loaded) {
+               fw_loaded = 0;
+       }
+
+       release_firmware(bootfw);
+       release_firmware(ucode);
+       release_firmware(firmware);
+       bootfw = ucode = firmware = NULL;
+#endif
+
+       priv->ieee->iw_mode = wrqu->mode;
+       ipw_adapter_restart(priv);
+
+       return err;
+}
+
+static int ipw_wx_get_mode(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       wrqu->mode = priv->ieee->iw_mode;
+       IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
+
+       return 0;
+}
+
+
+#define DEFAULT_RTS_THRESHOLD     2304U
+#define MIN_RTS_THRESHOLD         1U
+#define MAX_RTS_THRESHOLD         2304U
+#define DEFAULT_BEACON_INTERVAL   100U
+#define        DEFAULT_SHORT_RETRY_LIMIT 7U
+#define        DEFAULT_LONG_RETRY_LIMIT  4U
+
+/* Values are in microsecond */
+static const s32 timeout_duration[] = {
+       350000,
+       250000,
+       75000,
+       37000,
+       25000,
+};
+
+static const s32 period_duration[] = {
+       400000,
+       700000,
+       1000000,
+       1000000,
+       1000000
+};
+
+static int ipw_wx_get_range(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       struct iw_range *range = (struct iw_range *)extra;
+       u16 val;
+       int i;
+
+       wrqu->data.length = sizeof(*range);
+       memset(range, 0, sizeof(*range));
+
+       /* 54Mbs == ~27 Mb/s real (802.11g) */
+       range->throughput = 27 * 1000 * 1000;
+
+       range->max_qual.qual = 100;
+       /* TODO: Find real max RSSI and stick here */
+       range->max_qual.level = 0;
+       range->max_qual.noise = 0;
+       range->max_qual.updated = 7; /* Updated all three */
+
+       range->avg_qual.qual = 70;
+       /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+       range->avg_qual.level = 0; /* FIXME to real average level */
+       range->avg_qual.noise = 0;
+       range->avg_qual.updated = 7; /* Updated all three */
+
+       range->num_bitrates = min(priv->rates.num_rates, (u8)IW_MAX_BITRATES);
+
+       for (i = 0; i < range->num_bitrates; i++)
+               range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
+                       500000;
+
+       range->max_rts = DEFAULT_RTS_THRESHOLD;
+       range->min_frag = MIN_FRAG_THRESHOLD;
+       range->max_frag = MAX_FRAG_THRESHOLD;
+
+       range->encoding_size[0] = 5;
+       range->encoding_size[1] = 13;
+       range->num_encoding_sizes = 2;
+       range->max_encoding_tokens = WEP_KEYS;
+
+       /* Set the Wireless Extension versions */
+       range->we_version_compiled = WIRELESS_EXT;
+       range->we_version_source = 16;
+
+        range->num_channels = FREQ_COUNT;
+
+       val = 0;
+       for (i = 0; i < FREQ_COUNT; i++) {
+               range->freq[val].i = i + 1;
+               range->freq[val].m = ipw_frequencies[i] * 100000;
+               range->freq[val].e = 1;
+               val++;
+
+               if (val == IW_MAX_FREQUENCIES)
+                       break;
+       }
+       range->num_frequency = val;
+
+       IPW_DEBUG_WX("GET Range\n");
+       return 0;
+}
+
+static int ipw_wx_set_wap(struct net_device *dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       static const unsigned char any[] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+       };
+       static const unsigned char off[] = {
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+
+       if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+               return -EINVAL;
+
+       if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
+           !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+               /* we disable mandatory BSSID association */
+               IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
+               priv->config &= ~CFG_STATIC_BSSID;
+               if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
+                                     STATUS_ASSOCIATING))) {
+                       IPW_DEBUG_ASSOC("Attempting to associate with new "
+                                       "parameters.\n");
+                       ipw_associate(priv);
+               }
+
+               return 0;
+       }
+
+       priv->config |= CFG_STATIC_BSSID;
+       if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+               IPW_DEBUG_WX("BSSID set to current BSSID.\n");
+               return 0;
+       }
+
+       IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
+                    MAC_ARG(wrqu->ap_addr.sa_data));
+
+       memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+       /* If we are currently associated, or trying to associate
+        * then see if this is a new BSSID (causing us to disassociate) */
+       if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+               IPW_DEBUG_ASSOC("Disassociating due to BSSID change.\n");
+               ipw_disassociate(priv);
+       } else {
+               ipw_associate(priv);
+       }
+
+       return 0;
+}
+
+static int ipw_wx_get_wap(struct net_device *dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       /* If we are associated, trying to associate, or have a statically
+        * configured BSSID then return that; otherwise return ANY */
+       if (priv->config & CFG_STATIC_BSSID ||
+           priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+               wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(wrqu->ap_addr.sa_data, &priv->bssid, ETH_ALEN);
+       } else
+               memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+
+       IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
+                    MAC_ARG(wrqu->ap_addr.sa_data));
+       return 0;
+}
+
+static int ipw_wx_set_essid(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       char *essid = ""; /* ANY */
+       int length = 0;
+
+       if (wrqu->essid.flags && wrqu->essid.length) {
+               length = wrqu->essid.length - 1;
+               essid = extra;
+       }
+       if (length == 0) {
+               IPW_DEBUG_WX("Setting ESSID to ANY\n");
+               priv->config &= ~CFG_STATIC_ESSID;
+               if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
+                                     STATUS_ASSOCIATING))) {
+                       IPW_DEBUG_ASSOC("Attempting to associate with new "
+                                       "parameters.\n");
+                       ipw_associate(priv);
+               }
+
+               return 0;
+       }
+
+       length = min(length, IW_ESSID_MAX_SIZE);
+
+       priv->config |= CFG_STATIC_ESSID;
+
+       if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
+               IPW_DEBUG_WX("ESSID set to current ESSID.\n");
+               return 0;
+       }
+
+       IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
+                    length);
+
+       priv->essid_len = length;
+       memcpy(priv->essid, essid, priv->essid_len);
+
+       /* If we are currently associated, or trying to associate
+        * then see if this is a new ESSID (causing us to disassociate) */
+       if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+               IPW_DEBUG_ASSOC("Disassociating due to ESSID change.\n");
+               ipw_disassociate(priv);
+       } else {
+               ipw_associate(priv);
+       }
+
+       return 0;
+}
+
+static int ipw_wx_get_essid(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       /* If we are associated, trying to associate, or have a statically
+        * configured ESSID then return that; otherwise return ANY */
+       if (priv->config & CFG_STATIC_ESSID ||
+           priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+               IPW_DEBUG_WX("Getting essid: '%s'\n",
+                            escape_essid(priv->essid, priv->essid_len));
+               memcpy(extra, priv->essid, priv->essid_len);
+               wrqu->essid.length = priv->essid_len;
+               wrqu->essid.flags = 1; /* active */
+       } else {
+               IPW_DEBUG_WX("Getting essid: ANY\n");
+               wrqu->essid.length = 0;
+               wrqu->essid.flags = 0; /* active */
+       }
+
+       return 0;
+}
+
+static int ipw_wx_set_nick(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
+       if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+               return -E2BIG;
+
+       wrqu->data.length = min((size_t)wrqu->data.length, sizeof(priv->nick));
+       memset(priv->nick, 0, sizeof(priv->nick));
+       memcpy(priv->nick, extra,  wrqu->data.length);
+       IPW_DEBUG_TRACE("<<\n");
+       return 0;
+
+}
+
+
+static int ipw_wx_get_nick(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       IPW_DEBUG_WX("Getting nick\n");
+       wrqu->data.length = strlen(priv->nick) + 1;
+       memcpy(extra, priv->nick, wrqu->data.length);
+       wrqu->data.flags = 1; /* active */
+       return 0;
+}
+
+
+static int ipw_wx_set_rate(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
+       return -EOPNOTSUPP;
+}
+
+static int ipw_wx_get_rate(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv * priv = ieee80211_priv(dev);
+       wrqu->bitrate.value = priv->last_rate;
+
+       IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
+       return 0;
+}
+
+
+static int ipw_wx_set_rts(struct net_device *dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       if (wrqu->rts.disabled)
+               priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
+       else {
+               if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
+                   wrqu->rts.value > MAX_RTS_THRESHOLD)
+                       return -EINVAL;
+
+               priv->rts_threshold = wrqu->rts.value;
+       }
+
+       ipw_send_rts_threshold(priv, priv->rts_threshold);
+       IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
+       return 0;
+}
+
+static int ipw_wx_get_rts(struct net_device *dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       wrqu->rts.value = priv->rts_threshold;
+       wrqu->rts.fixed = 0;    /* no auto select */
+       wrqu->rts.disabled =
+               (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
+
+       IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
+       return 0;
+}
+
+
+static int ipw_wx_set_txpow(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       struct ipw_tx_power tx_power;
+       int i;
+
+       if (ipw_radio_kill_sw(priv, wrqu->power.disabled))
+               return -EINPROGRESS;
+
+       if (wrqu->power.flags != IW_TXPOW_DBM)
+               return -EINVAL;
+
+       if ((wrqu->power.value > 20) ||
+           (wrqu->power.value < -12))
+               return -EINVAL;
+
+       priv->tx_power = wrqu->power.value;
+
+       memset(&tx_power, 0, sizeof(tx_power));
+
+       /* configure device for 'G' band */
+       tx_power.ieee_mode = IPW_G_MODE;
+       tx_power.num_channels = 11;
+       for (i = 0; i < 11; i++) {
+               tx_power.channels_tx_power[i].channel_number = i + 1;
+               tx_power.channels_tx_power[i].tx_power = priv->tx_power;
+       }
+       if (ipw_send_tx_power(priv, &tx_power))
+               goto error;
+
+       /* configure device to also handle 'B' band */
+       tx_power.ieee_mode = IPW_B_MODE;
+       if (ipw_send_tx_power(priv, &tx_power))
+               goto error;
+
+       return 0;
+
+ error:
+       return -EIO;
+}
+
+
+static int ipw_wx_get_txpow(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       wrqu->power.value = priv->tx_power;
+       wrqu->power.fixed = 1;
+       wrqu->power.flags = IW_TXPOW_DBM;
+       wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
+
+       IPW_DEBUG_WX("GET TX Power -> %s %d \n",
+                    wrqu->power.disabled ? "ON" : "OFF",
+                    wrqu->power.value);
+
+       return 0;
+}
+
+static int ipw_wx_set_frag(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       if (wrqu->frag.disabled)
+               priv->ieee->fts = DEFAULT_FTS;
+       else {
+               if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+                   wrqu->frag.value > MAX_FRAG_THRESHOLD)
+                       return -EINVAL;
+
+               priv->ieee->fts = wrqu->frag.value & ~0x1;
+       }
+
+       ipw_send_frag_threshold(priv, wrqu->frag.value);
+       IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
+       return 0;
+}
+
+static int ipw_wx_get_frag(struct net_device *dev,
+                              struct iw_request_info *info,
+                              union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       wrqu->frag.value = priv->ieee->fts;
+       wrqu->frag.fixed = 0;   /* no auto select */
+       wrqu->frag.disabled =
+               (wrqu->frag.value == DEFAULT_FTS);
+
+       IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
+
+       return 0;
+}
+
+static int ipw_wx_set_retry(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
+       return -EOPNOTSUPP;
+}
+
+
+static int ipw_wx_get_retry(struct net_device *dev,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra)
+{
+       IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
+       return -EOPNOTSUPP;
+}
+
+
+static int ipw_wx_set_scan(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       IPW_DEBUG_WX("Start scan\n");
+       if (ipw_request_scan(priv))
+               return -EIO;
+       return 0;
+}
+
+static int ipw_wx_get_scan(struct net_device *dev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
+}
+
+static int ipw_wx_set_encode(struct net_device *dev,
+                                struct iw_request_info *info,
+                                union iwreq_data *wrqu, char *key)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw_wx_get_encode(struct net_device *dev,
+                                struct iw_request_info *info,
+                                union iwreq_data *wrqu, char *key)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw_wx_set_power(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       int err;
+
+       if (wrqu->power.disabled) {
+               priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+               err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
+               if (err) {
+                       IPW_DEBUG_WX("failed setting power mode.\n");
+                       return err;
+               }
+
+               IPW_DEBUG_WX("SET Power Management Mode -> off\n");
+
+               return 0;
+       }
+
+       switch (wrqu->power.flags & IW_POWER_MODE) {
+       case IW_POWER_ON:    /* If not specified */
+       case IW_POWER_MODE:  /* If set all mask */
+       case IW_POWER_ALL_R: /* If explicitely state all */
+               break;
+       default: /* Otherwise we don't support it */
+               IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
+                            wrqu->power.flags);
+               return -EOPNOTSUPP;
+       }
+
+       /* If the user hasn't specified a power management mode yet, default
+        * to BATTERY */
+        if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
+               priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
+       else
+               priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+       err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
+       if (err) {
+               IPW_DEBUG_WX("failed setting power mode.\n");
+               return err;
+       }
+
+       IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n",
+                    priv->power_mode);
+
+       return 0;
+}
+
+static int ipw_wx_get_power(struct net_device *dev,
+                               struct iw_request_info *info,
+                               union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+               wrqu->power.disabled = 1;
+       } else {
+               wrqu->power.disabled = 0;
+       }
+
+       IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
+
+       return 0;
+}
+
+static int ipw_wx_set_powermode(struct net_device *dev,
+                                   struct iw_request_info *info,
+                                   union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       int mode = *(int *)extra;
+       int err;
+
+       if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
+               mode = IPW_POWER_AC;
+               priv->power_mode = mode;
+       } else {
+               priv->power_mode = IPW_POWER_ENABLED | mode;
+       }
+
+       if (priv->power_mode != mode) {
+               err = ipw_send_power_mode(priv, mode);
+
+               if (err) {
+                       IPW_DEBUG_WX("failed setting power mode.\n");
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+#define MAX_WX_STRING 80
+static int ipw_wx_get_powermode(struct net_device *dev,
+                                   struct iw_request_info *info,
+                                   union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       int level = IPW_POWER_LEVEL(priv->power_mode);
+       char *p = extra;
+
+       p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
+
+       switch (level) {
+       case IPW_POWER_AC:
+               p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
+               break;
+       case IPW_POWER_BATTERY:
+               p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
+               break;
+       default:
+               p += snprintf(p, MAX_WX_STRING - (p - extra),
+                             "(Timeout %dms, Period %dms)",
+                             timeout_duration[level - 1] / 1000,
+                             period_duration[level - 1] / 1000);
+       }
+
+       if (!(priv->power_mode & IPW_POWER_ENABLED))
+               p += snprintf(p, MAX_WX_STRING - (p - extra)," OFF");
+
+       wrqu->data.length = p - extra + 1;
+
+       return 0;
+}
+
+static int ipw_wx_set_wireless_mode(struct net_device *dev,
+                                    struct iw_request_info *info,
+                                    union iwreq_data *wrqu, char *extra)
+{
+        struct ipw_priv *priv = ieee80211_priv(dev);
+       int mode = *(int *)extra;
+       u8 band = 0, modulation = 0;
+
+       if (mode == 0 || mode & ~IEEE_MODE_MASK) {
+               IPW_WARNING("Attempt to set invalid wireless mode: %d\n",
+                           mode);
+               return -EINVAL;
+       }
+
+       if (priv->adapter == IPW_2915ABG) {
+               priv->ieee->abg_ture = 1;
+               if (mode & IEEE_A) {
+                       band |= IEEE80211_52GHZ_BAND;
+                       modulation |= IEEE80211_OFDM_MODULATION;
+               } else
+                       priv->ieee->abg_ture = 0;
+       } else {
+               if (mode & IEEE_A) {
+                       IPW_WARNING("Attempt to set 2200BG into "
+                                   "802.11a mode\n");
+                       return -EINVAL;
+               }
+
+               priv->ieee->abg_ture = 0;
+       }
+
+       if (mode & IEEE_B) {
+               band |= IEEE80211_24GHZ_BAND;
+               modulation |= IEEE80211_CCK_MODULATION;
+       } else
+               priv->ieee->abg_ture = 0;
+
+       if (mode & IEEE_G) {
+               band |= IEEE80211_24GHZ_BAND;
+               modulation |= IEEE80211_OFDM_MODULATION;
+       } else
+               priv->ieee->abg_ture = 0;
+
+       priv->ieee->mode = mode;
+       priv->ieee->freq_band = band;
+       priv->ieee->modulation = modulation;
+       init_supported_rates(priv, &priv->rates);
+
+       /* If we are currently associated, or trying to associate
+         * then see if this is a new configuration (causing us to
+        * disassociate) */
+        if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+               /* The resulting association will trigger
+                * the new rates to be sent to the device */
+                IPW_DEBUG_ASSOC("Disassociating due to mode change.\n");
+                ipw_disassociate(priv);
+       } else
+               ipw_send_supported_rates(priv, &priv->rates);
+
+       IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
+                    mode & IEEE_A ? 'a' : '.',
+                    mode & IEEE_B ? 'b' : '.',
+                    mode & IEEE_G ? 'g' : '.');
+       return 0;
+}
+
+static int ipw_wx_get_wireless_mode(struct net_device *dev,
+                                    struct iw_request_info *info,
+                                    union iwreq_data *wrqu, char *extra)
+{
+        struct ipw_priv *priv = ieee80211_priv(dev);
+
+       switch (priv->ieee->freq_band) {
+       case IEEE80211_24GHZ_BAND:
+               switch (priv->ieee->modulation) {
+               case IEEE80211_CCK_MODULATION:
+                       strncpy(extra, "802.11b (2)", MAX_WX_STRING);
+                       break;
+               case IEEE80211_OFDM_MODULATION:
+                       strncpy(extra, "802.11g (4)", MAX_WX_STRING);
+                       break;
+               default:
+                       strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
+                       break;
+               }
+               break;
+
+       case IEEE80211_52GHZ_BAND:
+               strncpy(extra, "802.11a (1)", MAX_WX_STRING);
+               break;
+
+       default: /* Mixed Band */
+               switch (priv->ieee->modulation) {
+               case IEEE80211_CCK_MODULATION:
+                       strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
+                       break;
+               case IEEE80211_OFDM_MODULATION:
+                       strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
+                       break;
+               default:
+                       strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
+                       break;
+               }
+               break;
+       }
+
+       IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
+
+        wrqu->data.length = strlen(extra) + 1;
+
+        return 0;
+}
+
+#ifdef CONFIG_IPW_PROMISC
+static int ipw_wx_set_promisc(struct net_device *dev,
+                             struct iw_request_info *info,
+                             union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       int *parms = (int *)extra;
+       int enable = (parms[0] > 0);
+
+       IPW_DEBUG_WX("SET PROMISC: %d %d\n", enable, parms[1]);
+       if (enable) {
+               if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+                       priv->net_dev->type = ARPHRD_IEEE80211;
+                       ipw_adapter_restart(priv);
+               }
+
+               ipw_set_channel(priv, parms[1]);
+       } else {
+               if (priv->ieee->iw_mode != IW_MODE_MONITOR)
+                       return 0;
+               priv->net_dev->type = ARPHRD_ETHER;
+               ipw_adapter_restart(priv);
+       }
+       return 0;
+}
+
+
+static int ipw_wx_reset(struct net_device *dev,
+                       struct iw_request_info *info,
+                       union iwreq_data *wrqu, char *extra)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       IPW_DEBUG_WX("RESET\n");
+       ipw_adapter_restart(priv);
+       return 0;
+}
+#endif // CONFIG_IPW_PROMISC
+
+/* Rebase the WE IOCTLs to zero for the handler array */
+#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
+static iw_handler ipw_wx_handlers[] =
+{
+       IW_IOCTL(SIOCGIWNAME)   = ipw_wx_get_name,
+       IW_IOCTL(SIOCSIWFREQ)   = ipw_wx_set_freq,
+       IW_IOCTL(SIOCGIWFREQ)   = ipw_wx_get_freq,
+       IW_IOCTL(SIOCSIWMODE)   = ipw_wx_set_mode,
+       IW_IOCTL(SIOCGIWMODE)   = ipw_wx_get_mode,
+       IW_IOCTL(SIOCGIWRANGE)  = ipw_wx_get_range,
+       IW_IOCTL(SIOCSIWAP)     = ipw_wx_set_wap,
+       IW_IOCTL(SIOCGIWAP)     = ipw_wx_get_wap,
+       IW_IOCTL(SIOCSIWSCAN)   = ipw_wx_set_scan,
+       IW_IOCTL(SIOCGIWSCAN)   = ipw_wx_get_scan,
+       IW_IOCTL(SIOCSIWESSID)  = ipw_wx_set_essid,
+       IW_IOCTL(SIOCGIWESSID)  = ipw_wx_get_essid,
+       IW_IOCTL(SIOCSIWNICKN)  = ipw_wx_set_nick,
+       IW_IOCTL(SIOCGIWNICKN)  = ipw_wx_get_nick,
+       IW_IOCTL(SIOCSIWRATE)   = ipw_wx_set_rate,
+       IW_IOCTL(SIOCGIWRATE)   = ipw_wx_get_rate,
+       IW_IOCTL(SIOCSIWRTS)    = ipw_wx_set_rts,
+       IW_IOCTL(SIOCGIWRTS)    = ipw_wx_get_rts,
+       IW_IOCTL(SIOCSIWFRAG)   = ipw_wx_set_frag,
+       IW_IOCTL(SIOCGIWFRAG)   = ipw_wx_get_frag,
+       IW_IOCTL(SIOCSIWTXPOW)  = ipw_wx_set_txpow,
+       IW_IOCTL(SIOCGIWTXPOW)  = ipw_wx_get_txpow,
+       IW_IOCTL(SIOCSIWRETRY)  = ipw_wx_set_retry,
+       IW_IOCTL(SIOCGIWRETRY)  = ipw_wx_get_retry,
+       IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
+       IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
+       IW_IOCTL(SIOCSIWPOWER)  = ipw_wx_set_power,
+       IW_IOCTL(SIOCGIWPOWER)  = ipw_wx_get_power,
+};
+
+#define IPW_PRIV_SET_POWER     SIOCIWFIRSTPRIV
+#define IPW_PRIV_GET_POWER     SIOCIWFIRSTPRIV+1
+#define IPW_PRIV_SET_MODE      SIOCIWFIRSTPRIV+2
+#define IPW_PRIV_GET_MODE      SIOCIWFIRSTPRIV+3
+#define IPW_PRIV_SET_PROMISC   SIOCIWFIRSTPRIV+4
+#define IPW_PRIV_RESET         SIOCIWFIRSTPRIV+5
+
+
+static struct iw_priv_args ipw_priv_args[] = {
+       {
+               .cmd = IPW_PRIV_SET_POWER,
+               .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+               .name = "set_power"
+       },
+       {
+               .cmd = IPW_PRIV_GET_POWER,
+               .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+               .name = "get_power"
+       },
+       {
+               .cmd = IPW_PRIV_SET_MODE,
+               .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+               .name = "set_mode"
+       },
+       {
+               .cmd = IPW_PRIV_GET_MODE,
+               .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+               .name = "get_mode"
+       },
+#ifdef CONFIG_IPW_PROMISC
+       {
+               IPW_PRIV_SET_PROMISC,
+               IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"
+       },
+       {
+               IPW_PRIV_RESET,
+               IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"
+       },
+#endif /* CONFIG_IPW_PROMISC */
+};
+
+static iw_handler ipw_priv_handler[] = {
+       ipw_wx_set_powermode,
+       ipw_wx_get_powermode,
+       ipw_wx_set_wireless_mode,
+       ipw_wx_get_wireless_mode,
+#ifdef CONFIG_IPW_PROMISC
+       ipw_wx_set_promisc,
+       ipw_wx_reset,
+#endif
+};
+
+static struct iw_handler_def ipw_wx_handler_def =
+{
+       .standard       = ipw_wx_handlers,
+       .num_standard   = ARRAY_SIZE(ipw_wx_handlers),
+       .num_private    = ARRAY_SIZE(ipw_priv_handler),
+       .num_private_args = ARRAY_SIZE(ipw_priv_args),
+       .private        = ipw_priv_handler,
+       .private_args   = ipw_priv_args,
+};
+
+
+
+
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ * Also called by SIOCGIWSTATS
+ */
+static struct iw_statistics *ipw_get_wireless_stats(struct net_device * dev)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       struct iw_statistics *wstats;
+
+       wstats = &priv->wstats;
+
+       /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
+        * ipw2100_wx_wireless_stats seems to be called before fw is
+        * initialized.  STATUS_ASSOCIATED will only be set if the hw is up
+        * and associated; if not associcated, the values are all meaningless
+        * anyway, so set them all to NULL and INVALID */
+       if (!(priv->status & STATUS_ASSOCIATED)) {
+               wstats->miss.beacon = 0;
+               wstats->discard.retries = 0;
+               wstats->qual.qual = 0;
+               wstats->qual.level = 0;
+               wstats->qual.noise = 0;
+               wstats->qual.updated = 7;
+               wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+                       IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+               return wstats;
+       }
+
+       wstats->qual.qual = priv->quality;
+       wstats->qual.level = average_value(&priv->average_rssi);
+       wstats->qual.noise = average_value(&priv->average_noise);
+       wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
+               IW_QUAL_NOISE_UPDATED;
+
+       wstats->miss.beacon = average_value(&priv->average_missed_beacons);
+       wstats->discard.retries = priv->last_tx_failures;
+       wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
+
+/*     if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
+       goto fail_get_ordinal;
+       wstats->discard.retries += tx_retry; */
+
+       return wstats;
+}
+
+
+/* net device stuff */
+
+static inline void init_sys_config(struct ipw_sys_config *sys_config)
+{
+        memset(sys_config, 0, sizeof(struct ipw_sys_config));
+       sys_config->bt_coexistence = 1; /* We may need to look into prvStaBtConfig */
+       sys_config->answer_broadcast_ssid_probe = 0;
+       sys_config->accept_all_data_frames = 0;
+       sys_config->accept_non_directed_frames = 1;
+       sys_config->exclude_unicast_unencrypted = 0;
+       sys_config->disable_unicast_decryption = 1;
+       sys_config->exclude_multicast_unencrypted = 0;
+       sys_config->disable_multicast_decryption = 1;
+       sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
+       sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
+       sys_config->dot11g_auto_detection = 0;
+       sys_config->enable_cts_to_self = 0;
+       sys_config->bt_coexist_collision_thr = 0;
+       sys_config->pass_noise_stats_to_host = 1;
+}
+
+static int ipw_net_open(struct net_device *dev)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       IPW_DEBUG_INFO("dev->open\n");
+       /* we should be verifying the device is ready to be opened */
+       if (!(priv->status & STATUS_RF_KILL_MASK) &&
+           (priv->status & STATUS_ASSOCIATED))
+               netif_start_queue(dev);
+       return 0;
+}
+
+static int ipw_net_stop(struct net_device *dev)
+{
+       IPW_DEBUG_INFO("dev->close\n");
+       netif_stop_queue(dev);
+       return 0;
+}
+
+/*
+todo:
+
+modify to send one tfd per fragment instead of using chunking.  otherwise
+we need to heavily modify the ieee80211_skb_to_txb.
+*/
+
+static inline void ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)
+               txb->fragments[0]->data;
+       int i = 0;
+       struct tfd_frame *tfd;
+       struct clx2_tx_queue *txq = &priv->txq[0];
+       struct clx2_queue *q = &txq->q;
+       u8 id, hdr_len, unicast;
+       u16 remaining_bytes;
+
+       switch (priv->ieee->iw_mode) {
+       case IW_MODE_ADHOC:
+               hdr_len = IEEE80211_3ADDR_LEN;
+               unicast = !is_broadcast_ether_addr(hdr->addr1) &&
+                       !is_multicast_ether_addr(hdr->addr1);
+               id = ipw_find_station(priv, hdr->addr1);
+               if (id == IPW_INVALID_STATION) {
+                       id = ipw_add_station(priv, hdr->addr1);
+                       if (id == IPW_INVALID_STATION) {
+                               IPW_WARNING("Attempt to send data to "
+                                           "invalid cell: " MAC_FMT "\n",
+                                           MAC_ARG(hdr->addr1));
+                               goto drop;
+                       }
+               }
+               break;
+
+       case IW_MODE_INFRA:
+       default:
+               unicast = !is_broadcast_ether_addr(hdr->addr3) &&
+                       !is_multicast_ether_addr(hdr->addr3);
+               hdr_len = IEEE80211_3ADDR_LEN;
+               id = 0;
+               break;
+       }
+
+       tfd = &txq->bd[q->first_empty];
+       txq->txb[q->first_empty] = txb;
+       memset(tfd, 0, sizeof(*tfd));
+       tfd->u.data.station_number = id;
+
+       tfd->control_flags.message_type = TX_FRAME_TYPE;
+       tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
+
+       tfd->u.data.cmd_id = DINO_CMD_TX;
+       tfd->u.data.len = txb->payload_size;
+       remaining_bytes = txb->payload_size;
+       if (unlikely(!unicast))
+               tfd->u.data.tx_flags = DCT_FLAG_NO_WEP;
+       else
+               tfd->u.data.tx_flags = DCT_FLAG_NO_WEP | DCT_FLAG_ACK_REQD;
+
+       if (priv->assoc_request.ieee_mode == IPW_B_MODE)
+               tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_CCK;
+       else
+               tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_OFDM;
+
+       if (priv->config & CFG_PREAMBLE)
+               tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREMBL;
+
+       memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
+
+       /* payload */
+       tfd->u.data.num_chunks = min((u8)(NUM_TFD_CHUNKS - 2), txb->nr_frags);
+       for (i = 0; i < tfd->u.data.num_chunks; i++) {
+               IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
+                            i, tfd->u.data.num_chunks,
+                            txb->fragments[i]->len - hdr_len);
+               printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
+                          txb->fragments[i]->len - hdr_len);
+
+               tfd->u.data.chunk_ptr[i] = pci_map_single(
+                       priv->pci_dev, txb->fragments[i]->data + hdr_len,
+                       txb->fragments[i]->len - hdr_len, PCI_DMA_TODEVICE);
+               tfd->u.data.chunk_len[i] = txb->fragments[i]->len - hdr_len;
+       }
+
+       if (i != txb->nr_frags) {
+               struct sk_buff *skb;
+               u16 remaining_bytes = 0;
+               int j;
+
+               for (j = i; j < txb->nr_frags; j++)
+                       remaining_bytes += txb->fragments[j]->len - hdr_len;
+
+               printk(KERN_INFO "Trying to reallocate for %d bytes\n",
+                      remaining_bytes);
+               skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
+               if (skb != NULL) {
+                       tfd->u.data.chunk_len[i] = remaining_bytes;
+                       for (j = i; j < txb->nr_frags; j++) {
+                               int size = txb->fragments[j]->len - hdr_len;
+                               printk(KERN_INFO "Adding frag %d %d...\n",
+                                       j, size);
+                               memcpy(skb_put(skb, size),
+                                       txb->fragments[j]->data + hdr_len,
+                                       size);
+                       }
+                       dev_kfree_skb_any(txb->fragments[i]);
+                       txb->fragments[i] = skb;
+                       tfd->u.data.chunk_ptr[i] = pci_map_single(
+                               priv->pci_dev, skb->data,
+                               tfd->u.data.chunk_len[i], PCI_DMA_TODEVICE);
+                       tfd->u.data.num_chunks++;
+               }
+       }
+
+       /* kick DMA */
+       q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
+       ipw_write32(priv, q->reg_w, q->first_empty);
+
+       if (ipw_queue_space(q) < q->high_mark)
+               netif_stop_queue(priv->net_dev);
+
+       return;
+
+ drop:
+       IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
+       ieee80211_txb_free(txb);
+}
+
+static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
+                                  struct net_device *dev)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       unsigned long flags;
+
+       IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       if (!(priv->status & STATUS_ASSOCIATED)) {
+               IPW_DEBUG_INFO("Tx attempt while not associated.\n");
+               priv->ieee->stats.tx_carrier_errors++;
+               netif_stop_queue(dev);
+               goto fail_unlock;
+       }
+
+       ipw_tx_skb(priv, txb);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return 0;
+
+ fail_unlock:
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return 1;
+}
+
+static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       priv->ieee->stats.tx_packets = priv->tx_packets;
+       priv->ieee->stats.rx_packets = priv->rx_packets;
+       return &priv->ieee->stats;
+}
+
+static void ipw_net_set_multicast_list(struct net_device *dev)
+{
+
+}
+
+static int ipw_net_set_mac_address(struct net_device *dev, void *p)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       struct sockaddr *addr = p;
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+       priv->config |= CFG_CUSTOM_MAC;
+       memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
+       printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
+              priv->net_dev->name, MAC_ARG(priv->mac_addr));
+       ipw_adapter_restart(priv);
+       return 0;
+}
+
+static void ipw_ethtool_get_drvinfo(struct net_device *dev,
+                                   struct ethtool_drvinfo *info)
+{
+       struct ipw_priv *p = ieee80211_priv(dev);
+       char vers[64];
+       char date[32];
+       u32 len;
+
+       strcpy(info->driver, DRV_NAME);
+       strcpy(info->version, DRV_VERSION);
+
+       len = sizeof(vers);
+       ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
+       len = sizeof(date);
+       ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
+
+       snprintf(info->fw_version, sizeof(info->fw_version),"%s (%s)",
+                vers, date);
+       strcpy(info->bus_info, pci_name(p->pci_dev));
+       info->eedump_len = CX2_EEPROM_IMAGE_SIZE;
+}
+
+static u32 ipw_ethtool_get_link(struct net_device *dev)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       return (priv->status & STATUS_ASSOCIATED) != 0;
+}
+
+static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
+{
+       return CX2_EEPROM_IMAGE_SIZE;
+}
+
+static int ipw_ethtool_get_eeprom(struct net_device *dev,
+                                 struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+       struct ipw_priv *p = ieee80211_priv(dev);
+
+       if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
+               return -EINVAL;
+
+       memcpy(bytes, &((u8 *)p->eeprom)[eeprom->offset], eeprom->len);
+       return 0;
+}
+
+static int ipw_ethtool_set_eeprom(struct net_device *dev,
+                                 struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+       struct ipw_priv *p = ieee80211_priv(dev);
+       int i;
+
+       if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
+               return -EINVAL;
+
+       memcpy(&((u8 *)p->eeprom)[eeprom->offset], bytes, eeprom->len);
+       for (i = IPW_EEPROM_DATA;
+            i < IPW_EEPROM_DATA + CX2_EEPROM_IMAGE_SIZE;
+            i++)
+               ipw_write8(p, i, p->eeprom[i]);
+
+       return 0;
+}
+
+static struct ethtool_ops ipw_ethtool_ops = {
+        .get_link       = ipw_ethtool_get_link,
+        .get_drvinfo    = ipw_ethtool_get_drvinfo,
+        .get_eeprom_len = ipw_ethtool_get_eeprom_len,
+        .get_eeprom     = ipw_ethtool_get_eeprom,
+        .set_eeprom     = ipw_ethtool_set_eeprom,
+};
+
+static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
+{
+       struct ipw_priv *priv = data;
+       u32 inta, inta_mask;
+
+       if (!priv)
+               return IRQ_NONE;
+
+       spin_lock(&priv->lock);
+
+       if (!(priv->status & STATUS_INT_ENABLED)) {
+               /* Shared IRQ */
+               goto none;
+       }
+
+       inta = ipw_read32(priv, CX2_INTA_RW);
+       inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
+
+       if (inta == 0xFFFFFFFF) {
+               /* Hardware disappeared */
+               IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
+               goto none;
+       }
+
+       if (!(inta & (CX2_INTA_MASK_ALL & inta_mask))) {
+               /* Shared interrupt */
+               goto none;
+       }
+
+       /* tell the device to stop sending interrupts */
+       ipw_disable_interrupts(priv);
+
+       /* ack current interrupts */
+       inta &= (CX2_INTA_MASK_ALL & inta_mask);
+       ipw_write32(priv, CX2_INTA_RW, inta);
+
+       /* Cache INTA value for our tasklet */
+       priv->isr_inta = inta;
+
+       tasklet_schedule(&priv->irq_tasklet);
+
+       spin_unlock(&priv->lock);
+
+       return IRQ_HANDLED;
+ none:
+       spin_unlock(&priv->lock);
+       return IRQ_NONE;
+}
+
+static void ipw_rf_kill(void *adapter)
+{
+       struct ipw_priv *priv = adapter;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       if (rf_kill_active(priv)) {
+               IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
+               if (priv->workqueue)
+                       queue_delayed_work(priv->workqueue,
+                                          &priv->rf_kill, 2 * HZ);
+               goto exit_unlock;
+       }
+
+       /* RF Kill is now disabled, so bring the device back up */
+
+       if (!(priv->status & STATUS_RF_KILL_MASK)) {
+               IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
+                                 "device\n");
+
+               /* we can not do an adapter restart while inside an irq lock */
+               queue_work(priv->workqueue, &priv->adapter_restart);
+       } else
+               IPW_DEBUG_RF_KILL("HW RF Kill deactivated.  SW RF Kill still "
+                                 "enabled\n");
+
+ exit_unlock:
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ipw_setup_deferred_work(struct ipw_priv *priv)
+{
+       int ret = 0;
+
+       priv->workqueue = create_workqueue(DRV_NAME);
+       init_waitqueue_head(&priv->wait_command_queue);
+
+       INIT_WORK(&priv->adhoc_check, ipw_adhoc_check, priv);
+       INIT_WORK(&priv->associate, ipw_associate, priv);
+       INIT_WORK(&priv->disassociate, ipw_disassociate, priv);
+       INIT_WORK(&priv->rx_replenish, ipw_rx_queue_replenish, priv);
+       INIT_WORK(&priv->adapter_restart, ipw_adapter_restart, priv);
+       INIT_WORK(&priv->rf_kill, ipw_rf_kill, priv);
+       INIT_WORK(&priv->up, (void (*)(void *))ipw_up, priv);
+       INIT_WORK(&priv->down, (void (*)(void *))ipw_down, priv);
+       INIT_WORK(&priv->request_scan,
+                 (void (*)(void *))ipw_request_scan, priv);
+       INIT_WORK(&priv->gather_stats,
+                 (void (*)(void *))ipw_gather_stats, priv);
+       INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_abort_scan, priv);
+       INIT_WORK(&priv->roam, ipw_roam, priv);
+       INIT_WORK(&priv->scan_check, ipw_scan_check, priv);
+
+       tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+                    ipw_irq_tasklet, (unsigned long)priv);
+
+       return ret;
+}
+
+
+static void shim__set_security(struct net_device *dev,
+                              struct ieee80211_security *sec)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               if (sec->flags & (1 << i)) {
+                       priv->sec.key_sizes[i] = sec->key_sizes[i];
+                       if (sec->key_sizes[i] == 0)
+                               priv->sec.flags &= ~(1 << i);
+                       else
+                               memcpy(priv->sec.keys[i], sec->keys[i],
+                                      sec->key_sizes[i]);
+                       priv->sec.flags |= (1 << i);
+                       priv->status |= STATUS_SECURITY_UPDATED;
+               }
+       }
+
+       if ((sec->flags & SEC_ACTIVE_KEY) &&
+           priv->sec.active_key != sec->active_key) {
+               if (sec->active_key <= 3) {
+                       priv->sec.active_key = sec->active_key;
+                       priv->sec.flags |= SEC_ACTIVE_KEY;
+               } else
+                       priv->sec.flags &= ~SEC_ACTIVE_KEY;
+               priv->status |= STATUS_SECURITY_UPDATED;
+       }
+
+       if ((sec->flags & SEC_AUTH_MODE) &&
+           (priv->sec.auth_mode != sec->auth_mode)) {
+               priv->sec.auth_mode = sec->auth_mode;
+               priv->sec.flags |= SEC_AUTH_MODE;
+               if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
+                       priv->capability |= CAP_SHARED_KEY;
+               else
+                       priv->capability &= ~CAP_SHARED_KEY;
+               priv->status |= STATUS_SECURITY_UPDATED;
+       }
+
+       if (sec->flags & SEC_ENABLED &&
+           priv->sec.enabled != sec->enabled) {
+               priv->sec.flags |= SEC_ENABLED;
+               priv->sec.enabled = sec->enabled;
+               priv->status |= STATUS_SECURITY_UPDATED;
+               if (sec->enabled)
+                       priv->capability |= CAP_PRIVACY_ON;
+               else
+                       priv->capability &= ~CAP_PRIVACY_ON;
+       }
+
+       if (sec->flags & SEC_LEVEL &&
+           priv->sec.level != sec->level) {
+               priv->sec.level = sec->level;
+               priv->sec.flags |= SEC_LEVEL;
+               priv->status |= STATUS_SECURITY_UPDATED;
+       }
+
+       /* To match current functionality of ipw2100 (which works well w/
+        * various supplicants, we don't force a disassociate if the
+        * privacy capability changes ... */
+#if 0
+       if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
+           (((priv->assoc_request.capability &
+              WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
+            (!(priv->assoc_request.capability &
+                WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
+               IPW_DEBUG_ASSOC("Disassociating due to capability "
+                               "change.\n");
+               ipw_disassociate(priv);
+       }
+#endif
+}
+
+static int init_supported_rates(struct ipw_priv *priv,
+                               struct ipw_supported_rates *rates)
+{
+       /* TODO: Mask out rates based on priv->rates_mask */
+
+       memset(rates, 0, sizeof(*rates));
+        /* configure supported rates */
+       switch (priv->ieee->freq_band) {
+       case IEEE80211_52GHZ_BAND:
+               rates->ieee_mode = IPW_A_MODE;
+               rates->purpose = IPW_RATE_CAPABILITIES;
+               ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
+                                       IEEE80211_OFDM_DEFAULT_RATES_MASK);
+               break;
+
+       default: /* Mixed or 2.4Ghz */
+               rates->ieee_mode = IPW_G_MODE;
+               rates->purpose = IPW_RATE_CAPABILITIES;
+               ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
+                                      IEEE80211_CCK_DEFAULT_RATES_MASK);
+               if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
+                       ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
+                                               IEEE80211_OFDM_DEFAULT_RATES_MASK);
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static int ipw_config(struct ipw_priv *priv)
+{
+       int i;
+       struct ipw_tx_power tx_power;
+
+       memset(&priv->sys_config, 0, sizeof(priv->sys_config));
+       memset(&tx_power, 0, sizeof(tx_power));
+
+       /* This is only called from ipw_up, which resets/reloads the firmware
+          so, we don't need to first disable the card before we configure
+          it */
+
+       /* configure device for 'G' band */
+       tx_power.ieee_mode = IPW_G_MODE;
+       tx_power.num_channels = 11;
+       for (i = 0; i < 11; i++) {
+               tx_power.channels_tx_power[i].channel_number = i + 1;
+               tx_power.channels_tx_power[i].tx_power = priv->tx_power;
+       }
+       if (ipw_send_tx_power(priv, &tx_power))
+               goto error;
+
+       /* configure device to also handle 'B' band */
+       tx_power.ieee_mode = IPW_B_MODE;
+       if (ipw_send_tx_power(priv, &tx_power))
+               goto error;
+
+       /* initialize adapter address */
+       if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
+               goto error;
+
+       /* set basic system config settings */
+       init_sys_config(&priv->sys_config);
+       if (ipw_send_system_config(priv, &priv->sys_config))
+               goto error;
+
+        init_supported_rates(priv, &priv->rates);
+        if (ipw_send_supported_rates(priv, &priv->rates))
+               goto error;
+
+       /* Set request-to-send threshold */
+       if (priv->rts_threshold) {
+               if (ipw_send_rts_threshold(priv, priv->rts_threshold))
+                       goto error;
+       }
+
+       if (ipw_set_random_seed(priv))
+               goto error;
+
+       /* final state transition to the RUN state */
+       if (ipw_send_host_complete(priv))
+               goto error;
+
+       /* If configured to try and auto-associate, kick off a scan */
+       if ((priv->config & CFG_ASSOCIATE) && ipw_request_scan(priv))
+               goto error;
+
+       return 0;
+
+ error:
+       return -EIO;
+}
+
+#define MAX_HW_RESTARTS 5
+static int ipw_up(struct ipw_priv *priv)
+{
+       int rc, i;
+
+       if (priv->status & STATUS_EXIT_PENDING)
+               return -EIO;
+
+       for (i = 0; i < MAX_HW_RESTARTS; i++ ) {
+               /* Load the microcode, firmware, and eeprom.
+                * Also start the clocks. */
+               rc = ipw_load(priv);
+               if (rc) {
+                       IPW_ERROR("Unable to load firmware: 0x%08X\n",
+                                       rc);
+                       return rc;
+               }
+
+               ipw_init_ordinals(priv);
+               if (!(priv->config & CFG_CUSTOM_MAC))
+                       eeprom_parse_mac(priv, priv->mac_addr);
+               memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+
+               if (priv->status & STATUS_RF_KILL_MASK)
+                       return 0;
+
+               rc = ipw_config(priv);
+               if (!rc) {
+                       IPW_DEBUG_INFO("Configured device on count %i\n", i);
+                       priv->notif_missed_beacons = 0;
+                       netif_start_queue(priv->net_dev);
+                       return 0;
+               } else {
+                       IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n",
+                                      rc);
+               }
+
+               IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
+                              i, MAX_HW_RESTARTS);
+
+               /* We had an error bringing up the hardware, so take it
+                * all the way back down so we can try again */
+               ipw_down(priv);
+       }
+
+       /* tried to restart and config the device for as long as our
+        * patience could withstand */
+       IPW_ERROR("Unable to initialize device after %d attempts.\n",
+                 i);
+       return -EIO;
+}
+
+static void ipw_down(struct ipw_priv *priv)
+{
+       /* Attempt to disable the card */
+#if 0
+       ipw_send_card_disable(priv, 0);
+#endif
+
+       /* tell the device to stop sending interrupts */
+       ipw_disable_interrupts(priv);
+
+       /* Clear all bits but the RF Kill */
+       priv->status &= STATUS_RF_KILL_MASK;
+
+       netif_carrier_off(priv->net_dev);
+       netif_stop_queue(priv->net_dev);
+
+       ipw_stop_nic(priv);
+}
+
+/* Called by register_netdev() */
+static int ipw_net_init(struct net_device *dev)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+
+       if (priv->status & STATUS_RF_KILL_SW) {
+               IPW_WARNING("Radio disabled by module parameter.\n");
+               return 0;
+       } else if (rf_kill_active(priv)) {
+               IPW_WARNING("Radio Frequency Kill Switch is On:\n"
+                           "Kill switch must be turned off for "
+                           "wireless networking to work.\n");
+               queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
+               return 0;
+       }
+
+       if (ipw_up(priv))
+               return -EIO;
+
+       return 0;
+}
+
+/* PCI driver stuff */
+static struct pci_device_id card_ids[] = {
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
+       {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 2225BG */
+       {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
+       {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
+
+       /* required last entry */
+       {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, card_ids);
+
+static struct attribute *ipw_sysfs_entries[] = {
+       &dev_attr_rf_kill.attr,
+       &dev_attr_direct_dword.attr,
+       &dev_attr_indirect_byte.attr,
+       &dev_attr_indirect_dword.attr,
+       &dev_attr_mem_gpio_reg.attr,
+       &dev_attr_command_event_reg.attr,
+       &dev_attr_nic_type.attr,
+       &dev_attr_status.attr,
+       &dev_attr_cfg.attr,
+       &dev_attr_dump_errors.attr,
+       &dev_attr_dump_events.attr,
+       &dev_attr_eeprom_delay.attr,
+       &dev_attr_ucode_version.attr,
+       &dev_attr_rtc.attr,
+       NULL
+};
+
+static struct attribute_group ipw_attribute_group = {
+       .name = NULL,           /* put in device directory */
+       .attrs  = ipw_sysfs_entries,
+};
+
+static int ipw_pci_probe(struct pci_dev *pdev,
+                        const struct pci_device_id *ent)
+{
+       int err = 0;
+       struct net_device *net_dev;
+       void __iomem *base;
+       u32 length, val;
+       struct ipw_priv *priv;
+       int band, modulation;
+
+       net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
+       if (net_dev == NULL) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       priv = ieee80211_priv(net_dev);
+       priv->ieee = netdev_priv(net_dev);
+       priv->net_dev = net_dev;
+       priv->pci_dev = pdev;
+#ifdef CONFIG_IPW_DEBUG
+       ipw_debug_level = debug;
+#endif
+       spin_lock_init(&priv->lock);
+
+       if (pci_enable_device(pdev)) {
+               err = -ENODEV;
+               goto out_free_ieee80211;
+       }
+
+       pci_set_master(pdev);
+
+       err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+       if (!err)
+               err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+       if (err) {
+               printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
+               goto out_pci_disable_device;
+       }
+
+       pci_set_drvdata(pdev, priv);
+
+       err = pci_request_regions(pdev, DRV_NAME);
+       if (err)
+               goto out_pci_disable_device;
+
+       /* We disable the RETRY_TIMEOUT register (0x41) to keep
+        * PCI Tx retries from interfering with C3 CPU state */
+       pci_read_config_dword(pdev, 0x40, &val);
+       if ((val & 0x0000ff00) != 0)
+               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+       length = pci_resource_len(pdev, 0);
+       priv->hw_len = length;
+
+       base = ioremap_nocache(pci_resource_start(pdev, 0), length);
+       if (!base) {
+               err = -ENODEV;
+               goto out_pci_release_regions;
+       }
+
+       priv->hw_base = base;
+       IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
+       IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
+
+       err = ipw_setup_deferred_work(priv);
+       if (err) {
+               IPW_ERROR("Unable to setup deferred work\n");
+               goto out_iounmap;
+       }
+
+       /* Initialize module parameter values here */
+       if (ifname)
+               strncpy(net_dev->name, ifname, IFNAMSIZ);
+
+       if (associate)
+               priv->config |= CFG_ASSOCIATE;
+       else
+               IPW_DEBUG_INFO("Auto associate disabled.\n");
+
+       if (auto_create)
+               priv->config |= CFG_ADHOC_CREATE;
+       else
+               IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
+
+       if (disable) {
+               priv->status |= STATUS_RF_KILL_SW;
+               IPW_DEBUG_INFO("Radio disabled.\n");
+       }
+
+       if (channel != 0) {
+               priv->config |= CFG_STATIC_CHANNEL;
+               priv->channel = channel;
+               IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
+               IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
+               /* TODO: Validate that provided channel is in range */
+       }
+
+       switch (mode) {
+       case 1:
+               priv->ieee->iw_mode = IW_MODE_ADHOC;
+               break;
+#ifdef CONFIG_IPW_PROMISC
+       case 2:
+               priv->ieee->iw_mode = IW_MODE_MONITOR;
+               break;
+#endif
+       default:
+       case 0:
+               priv->ieee->iw_mode = IW_MODE_INFRA;
+               break;
+       }
+
+       if ((priv->pci_dev->device == 0x4223) ||
+           (priv->pci_dev->device == 0x4224)) {
+               printk(KERN_INFO DRV_NAME
+                      ": Detected Intel PRO/Wireless 2915ABG Network "
+                      "Connection\n");
+               priv->ieee->abg_ture = 1;
+               band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
+               modulation = IEEE80211_OFDM_MODULATION |
+                       IEEE80211_CCK_MODULATION;
+               priv->adapter = IPW_2915ABG;
+               priv->ieee->mode = IEEE_A|IEEE_G|IEEE_B;
+       } else {
+               if (priv->pci_dev->device == 0x4221)
+                       printk(KERN_INFO DRV_NAME
+                              ": Detected Intel PRO/Wireless 2225BG Network "
+                              "Connection\n");
+               else
+                       printk(KERN_INFO DRV_NAME
+                              ": Detected Intel PRO/Wireless 2200BG Network "
+                              "Connection\n");
+
+               priv->ieee->abg_ture = 0;
+               band = IEEE80211_24GHZ_BAND;
+               modulation = IEEE80211_OFDM_MODULATION |
+                       IEEE80211_CCK_MODULATION;
+               priv->adapter = IPW_2200BG;
+               priv->ieee->mode = IEEE_G|IEEE_B;
+       }
+
+       priv->ieee->freq_band = band;
+       priv->ieee->modulation = modulation;
+
+       priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
+
+       priv->missed_beacon_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
+       priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
+
+       priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
+
+       /* If power management is turned on, default to AC mode */
+        priv->power_mode = IPW_POWER_AC;
+       priv->tx_power = IPW_DEFAULT_TX_POWER;
+
+       err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME,
+                         priv);
+       if (err) {
+               IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
+               goto out_destroy_workqueue;
+       }
+
+       SET_MODULE_OWNER(net_dev);
+       SET_NETDEV_DEV(net_dev, &pdev->dev);
+
+       priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
+       priv->ieee->set_security = shim__set_security;
+
+       net_dev->open = ipw_net_open;
+       net_dev->stop = ipw_net_stop;
+       net_dev->init = ipw_net_init;
+       net_dev->get_stats = ipw_net_get_stats;
+       net_dev->set_multicast_list = ipw_net_set_multicast_list;
+       net_dev->set_mac_address = ipw_net_set_mac_address;
+       net_dev->get_wireless_stats = ipw_get_wireless_stats;
+       net_dev->wireless_handlers = &ipw_wx_handler_def;
+       net_dev->ethtool_ops = &ipw_ethtool_ops;
+       net_dev->irq = pdev->irq;
+       net_dev->base_addr = (unsigned long )priv->hw_base;
+       net_dev->mem_start = pci_resource_start(pdev, 0);
+       net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
+
+       err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
+       if (err) {
+               IPW_ERROR("failed to create sysfs device attributes\n");
+               goto out_release_irq;
+       }
+
+       err = register_netdev(net_dev);
+       if (err) {
+               IPW_ERROR("failed to register network device\n");
+               goto out_remove_group;
+       }
+
+       return 0;
+
+ out_remove_group:
+       sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
+ out_release_irq:
+       free_irq(pdev->irq, priv);
+ out_destroy_workqueue:
+       destroy_workqueue(priv->workqueue);
+       priv->workqueue = NULL;
+ out_iounmap:
+       iounmap(priv->hw_base);
+ out_pci_release_regions:
+       pci_release_regions(pdev);
+ out_pci_disable_device:
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+ out_free_ieee80211:
+       free_ieee80211(priv->net_dev);
+ out:
+       return err;
+}
+
+static void ipw_pci_remove(struct pci_dev *pdev)
+{
+       struct ipw_priv *priv = pci_get_drvdata(pdev);
+       if (!priv)
+               return;
+
+       priv->status |= STATUS_EXIT_PENDING;
+
+       sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
+
+       ipw_down(priv);
+
+       unregister_netdev(priv->net_dev);
+
+       if (priv->rxq) {
+               ipw_rx_queue_free(priv, priv->rxq);
+               priv->rxq = NULL;
+       }
+       ipw_tx_queue_free(priv);
+
+       /* ipw_down will ensure that there is no more pending work
+        * in the workqueue's, so we can safely remove them now. */
+       if (priv->workqueue) {
+               cancel_delayed_work(&priv->adhoc_check);
+               cancel_delayed_work(&priv->gather_stats);
+               cancel_delayed_work(&priv->request_scan);
+               cancel_delayed_work(&priv->rf_kill);
+               cancel_delayed_work(&priv->scan_check);
+               destroy_workqueue(priv->workqueue);
+               priv->workqueue = NULL;
+       }
+
+       free_irq(pdev->irq, priv);
+       iounmap(priv->hw_base);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+       free_ieee80211(priv->net_dev);
+
+#ifdef CONFIG_PM
+       if (fw_loaded) {
+               release_firmware(bootfw);
+               release_firmware(ucode);
+               release_firmware(firmware);
+               fw_loaded = 0;
+       }
+#endif
+}
+
+
+#ifdef CONFIG_PM
+static int ipw_pci_suspend(struct pci_dev *pdev, u32 state)
+{
+       struct ipw_priv *priv = pci_get_drvdata(pdev);
+       struct net_device *dev = priv->net_dev;
+
+       printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
+
+       /* Take down the device; powers it off, etc. */
+       ipw_down(priv);
+
+       /* Remove the PRESENT state of the device */
+       netif_device_detach(dev);
+
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, state);
+
+       return 0;
+}
+
+static int ipw_pci_resume(struct pci_dev *pdev)
+{
+       struct ipw_priv *priv = pci_get_drvdata(pdev);
+       struct net_device *dev = priv->net_dev;
+       u32 val;
+
+       printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
+
+       pci_set_power_state(pdev, 0);
+       pci_enable_device(pdev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+       pci_restore_state(pdev, priv->pm_state);
+#else
+       pci_restore_state(pdev);
+#endif
+       /*
+        * Suspend/Resume resets the PCI configuration space, so we have to
+        * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
+        * from interfering with C3 CPU state. pci_restore_state won't help
+        * here since it only restores the first 64 bytes pci config header.
+        */
+       pci_read_config_dword(pdev, 0x40, &val);
+       if ((val & 0x0000ff00) != 0)
+               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+       /* Set the device back into the PRESENT state; this will also wake
+        * the queue of needed */
+       netif_device_attach(dev);
+
+       /* Bring the device back up */
+       queue_work(priv->workqueue, &priv->up);
+
+       return 0;
+}
+#endif
+
+/* driver initialization stuff */
+static struct pci_driver ipw_driver = {
+       .name = DRV_NAME,
+       .id_table = card_ids,
+       .probe = ipw_pci_probe,
+       .remove = __devexit_p(ipw_pci_remove),
+#ifdef CONFIG_PM
+       .suspend = ipw_pci_suspend,
+       .resume = ipw_pci_resume,
+#endif
+};
+
+static int __init ipw_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
+       printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
+
+       ret = pci_module_init(&ipw_driver);
+       if (ret) {
+               IPW_ERROR("Unable to initialize PCI module\n");
+               return ret;
+       }
+
+       ret = driver_create_file(&ipw_driver.driver,
+                                &driver_attr_debug_level);
+       if (ret) {
+               IPW_ERROR("Unable to create driver sysfs file\n");
+               pci_unregister_driver(&ipw_driver);
+               return ret;
+       }
+
+       return ret;
+}
+
+static void __exit ipw_exit(void)
+{
+       driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
+       pci_unregister_driver(&ipw_driver);
+}
+
+module_param(disable, int, 0444);
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
+
+module_param(associate, int, 0444);
+MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
+
+module_param(auto_create, int, 0444);
+MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
+
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "debug output mask");
+
+module_param(channel, int, 0444);
+MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
+
+module_param(ifname, charp, 0444);
+MODULE_PARM_DESC(ifname, "network device name (default eth%d)");
+
+#ifdef CONFIG_IPW_PROMISC
+module_param(mode, int, 0444);
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
+#else
+module_param(mode, int, 0444);
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
+#endif
+
+module_exit(ipw_exit);
+module_init(ipw_init);
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h
new file mode 100644 (file)
index 0000000..3bff09d
--- /dev/null
@@ -0,0 +1,1742 @@
+/******************************************************************************
+
+  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
+
+******************************************************************************/
+
+#ifndef __ipw2200_h__
+#define __ipw2200_h__
+
+#define WEXT_USECHANNELS 1
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+
+#include <linux/firmware.h>
+#include <linux/wireless.h>
+#include <asm/io.h>
+
+#include <net/ieee80211.h>
+
+#define DRV_NAME       "ipw2200"
+
+#include <linux/workqueue.h>
+
+/* Authentication  and Association States */
+enum connection_manager_assoc_states
+{
+       CMAS_INIT = 0,
+       CMAS_TX_AUTH_SEQ_1,
+       CMAS_RX_AUTH_SEQ_2,
+       CMAS_AUTH_SEQ_1_PASS,
+       CMAS_AUTH_SEQ_1_FAIL,
+       CMAS_TX_AUTH_SEQ_3,
+       CMAS_RX_AUTH_SEQ_4,
+       CMAS_AUTH_SEQ_2_PASS,
+       CMAS_AUTH_SEQ_2_FAIL,
+       CMAS_AUTHENTICATED,
+       CMAS_TX_ASSOC,
+       CMAS_RX_ASSOC_RESP,
+       CMAS_ASSOCIATED,
+       CMAS_LAST
+};
+
+
+#define IPW_WAIT                     (1<<0)
+#define IPW_QUIET                    (1<<1)
+#define IPW_ROAMING                  (1<<2)
+
+#define IPW_POWER_MODE_CAM           0x00      //(always on)
+#define IPW_POWER_INDEX_1            0x01
+#define IPW_POWER_INDEX_2            0x02
+#define IPW_POWER_INDEX_3            0x03
+#define IPW_POWER_INDEX_4            0x04
+#define IPW_POWER_INDEX_5            0x05
+#define IPW_POWER_AC                 0x06
+#define IPW_POWER_BATTERY            0x07
+#define IPW_POWER_LIMIT              0x07
+#define IPW_POWER_MASK               0x0F
+#define IPW_POWER_ENABLED            0x10
+#define IPW_POWER_LEVEL(x)           ((x) & IPW_POWER_MASK)
+
+#define IPW_CMD_HOST_COMPLETE                 2
+#define IPW_CMD_POWER_DOWN                    4
+#define IPW_CMD_SYSTEM_CONFIG                 6
+#define IPW_CMD_MULTICAST_ADDRESS             7
+#define IPW_CMD_SSID                          8
+#define IPW_CMD_ADAPTER_ADDRESS              11
+#define IPW_CMD_PORT_TYPE                    12
+#define IPW_CMD_RTS_THRESHOLD                15
+#define IPW_CMD_FRAG_THRESHOLD               16
+#define IPW_CMD_POWER_MODE                   17
+#define IPW_CMD_WEP_KEY                      18
+#define IPW_CMD_TGI_TX_KEY                   19
+#define IPW_CMD_SCAN_REQUEST                 20
+#define IPW_CMD_ASSOCIATE                    21
+#define IPW_CMD_SUPPORTED_RATES              22
+#define IPW_CMD_SCAN_ABORT                   23
+#define IPW_CMD_TX_FLUSH                     24
+#define IPW_CMD_QOS_PARAMETERS               25
+#define IPW_CMD_SCAN_REQUEST_EXT             26
+#define IPW_CMD_DINO_CONFIG                  30
+#define IPW_CMD_RSN_CAPABILITIES             31
+#define IPW_CMD_RX_KEY                       32
+#define IPW_CMD_CARD_DISABLE                 33
+#define IPW_CMD_SEED_NUMBER                  34
+#define IPW_CMD_TX_POWER                     35
+#define IPW_CMD_COUNTRY_INFO                 36
+#define IPW_CMD_AIRONET_INFO                 37
+#define IPW_CMD_AP_TX_POWER                  38
+#define IPW_CMD_CCKM_INFO                    39
+#define IPW_CMD_CCX_VER_INFO                 40
+#define IPW_CMD_SET_CALIBRATION              41
+#define IPW_CMD_SENSITIVITY_CALIB            42
+#define IPW_CMD_RETRY_LIMIT                  51
+#define IPW_CMD_IPW_PRE_POWER_DOWN           58
+#define IPW_CMD_VAP_BEACON_TEMPLATE          60
+#define IPW_CMD_VAP_DTIM_PERIOD              61
+#define IPW_CMD_EXT_SUPPORTED_RATES          62
+#define IPW_CMD_VAP_LOCAL_TX_PWR_CONSTRAINT  63
+#define IPW_CMD_VAP_QUIET_INTERVALS          64
+#define IPW_CMD_VAP_CHANNEL_SWITCH           65
+#define IPW_CMD_VAP_MANDATORY_CHANNELS       66
+#define IPW_CMD_VAP_CELL_PWR_LIMIT           67
+#define IPW_CMD_VAP_CF_PARAM_SET             68
+#define IPW_CMD_VAP_SET_BEACONING_STATE      69
+#define IPW_CMD_MEASUREMENT                  80
+#define IPW_CMD_POWER_CAPABILITY             81
+#define IPW_CMD_SUPPORTED_CHANNELS           82
+#define IPW_CMD_TPC_REPORT                   83
+#define IPW_CMD_WME_INFO                     84
+#define IPW_CMD_PRODUCTION_COMMAND          85
+#define IPW_CMD_LINKSYS_EOU_INFO             90
+
+#define RFD_SIZE                              4
+#define NUM_TFD_CHUNKS                        6
+
+#define TX_QUEUE_SIZE                        32
+#define RX_QUEUE_SIZE                        32
+
+#define DINO_CMD_WEP_KEY                   0x08
+#define DINO_CMD_TX                        0x0B
+#define DCT_ANTENNA_A                      0x01
+#define DCT_ANTENNA_B                      0x02
+
+#define IPW_A_MODE                         0
+#define IPW_B_MODE                         1
+#define IPW_G_MODE                         2
+
+/*
+ * TX Queue Flag Definitions
+ */
+
+/* abort attempt if mgmt frame is rx'd */
+#define DCT_FLAG_ABORT_MGMT                0x01
+
+/* require CTS */
+#define DCT_FLAG_CTS_REQUIRED              0x02
+
+/* use short preamble */
+#define DCT_FLAG_SHORT_PREMBL              0x04
+
+/* RTS/CTS first */
+#define DCT_FLAG_RTS_REQD                  0x08
+
+/* dont calculate duration field */
+#define DCT_FLAG_DUR_SET                   0x10
+
+/* even if MAC WEP set (allows pre-encrypt) */
+#define DCT_FLAG_NO_WEP              0x20
+
+/* overwrite TSF field */
+#define DCT_FLAG_TSF_REQD                  0x40
+
+/* ACK rx is expected to follow */
+#define DCT_FLAG_ACK_REQD                  0x80
+
+#define DCT_FLAG_EXT_MODE_CCK  0x01
+#define DCT_FLAG_EXT_MODE_OFDM 0x00
+
+
+#define TX_RX_TYPE_MASK                    0xFF
+#define TX_FRAME_TYPE                      0x00
+#define TX_HOST_COMMAND_TYPE               0x01
+#define RX_FRAME_TYPE                      0x09
+#define RX_HOST_NOTIFICATION_TYPE          0x03
+#define RX_HOST_CMD_RESPONSE_TYPE          0x04
+#define RX_TX_FRAME_RESPONSE_TYPE          0x05
+#define TFD_NEED_IRQ_MASK                  0x04
+
+#define HOST_CMD_DINO_CONFIG               30
+
+#define HOST_NOTIFICATION_STATUS_ASSOCIATED             10
+#define HOST_NOTIFICATION_STATUS_AUTHENTICATE           11
+#define HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT    12
+#define HOST_NOTIFICATION_STATUS_SCAN_COMPLETED         13
+#define HOST_NOTIFICATION_STATUS_FRAG_LENGTH            14
+#define HOST_NOTIFICATION_STATUS_LINK_DETERIORATION     15
+#define HOST_NOTIFICATION_DINO_CONFIG_RESPONSE          16
+#define HOST_NOTIFICATION_STATUS_BEACON_STATE           17
+#define HOST_NOTIFICATION_STATUS_TGI_TX_KEY             18
+#define HOST_NOTIFICATION_TX_STATUS                     19
+#define HOST_NOTIFICATION_CALIB_KEEP_RESULTS            20
+#define HOST_NOTIFICATION_MEASUREMENT_STARTED           21
+#define HOST_NOTIFICATION_MEASUREMENT_ENDED             22
+#define HOST_NOTIFICATION_CHANNEL_SWITCHED              23
+#define HOST_NOTIFICATION_RX_DURING_QUIET_PERIOD        24
+#define HOST_NOTIFICATION_NOISE_STATS                  25
+#define HOST_NOTIFICATION_S36_MEASUREMENT_ACCEPTED      30
+#define HOST_NOTIFICATION_S36_MEASUREMENT_REFUSED       31
+
+#define HOST_NOTIFICATION_STATUS_BEACON_MISSING         1
+#define IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT           24
+#define IPW_MB_ROAMING_THRESHOLD_DEFAULT                8
+#define IPW_REAL_RATE_RX_PACKET_THRESHOLD               300
+
+#define MACADRR_BYTE_LEN                     6
+
+#define DCR_TYPE_AP                       0x01
+#define DCR_TYPE_WLAP                     0x02
+#define DCR_TYPE_MU_ESS                   0x03
+#define DCR_TYPE_MU_IBSS                  0x04
+#define DCR_TYPE_MU_PIBSS                 0x05
+#define DCR_TYPE_SNIFFER                  0x06
+#define DCR_TYPE_MU_BSS        DCR_TYPE_MU_ESS
+
+/**
+ * Generic queue structure
+ *
+ * Contains common data for Rx and Tx queues
+ */
+struct clx2_queue {
+       int n_bd;                      /**< number of BDs in this queue */
+       int first_empty;               /**< 1-st empty entry (index) */
+       int last_used;                 /**< last used entry (index) */
+       u32 reg_w;                   /**< 'write' reg (queue head), addr in domain 1 */
+       u32 reg_r;                   /**< 'read' reg (queue tail), addr in domain 1 */
+       dma_addr_t dma_addr;            /**< physical addr for BD's */
+       int low_mark;                  /**< low watermark, resume queue if free space more than this */
+       int high_mark;                 /**< high watermark, stop queue if free space less than this */
+} __attribute__ ((packed));
+
+struct machdr32
+{
+       u16 frame_ctl;
+       u16 duration;     // watch out for endians!
+       u8 addr1[ MACADRR_BYTE_LEN ];
+       u8 addr2[ MACADRR_BYTE_LEN ];
+       u8 addr3[ MACADRR_BYTE_LEN ];
+       u16 seq_ctrl;     // more endians!
+       u8 addr4[ MACADRR_BYTE_LEN ];
+       u16 qos_ctrl;
+} __attribute__ ((packed)) ;
+
+struct machdr30
+{
+       u16 frame_ctl;
+       u16 duration;     // watch out for endians!
+       u8 addr1[ MACADRR_BYTE_LEN ];
+       u8 addr2[ MACADRR_BYTE_LEN ];
+       u8 addr3[ MACADRR_BYTE_LEN ];
+       u16 seq_ctrl;     // more endians!
+       u8 addr4[ MACADRR_BYTE_LEN ];
+} __attribute__ ((packed)) ;
+
+struct machdr26
+{
+       u16 frame_ctl;
+       u16 duration;     // watch out for endians!
+       u8 addr1[ MACADRR_BYTE_LEN ];
+       u8 addr2[ MACADRR_BYTE_LEN ];
+       u8 addr3[ MACADRR_BYTE_LEN ];
+       u16 seq_ctrl;     // more endians!
+       u16 qos_ctrl;
+} __attribute__ ((packed)) ;
+
+struct machdr24
+{
+       u16 frame_ctl;
+       u16 duration;     // watch out for endians!
+       u8 addr1[ MACADRR_BYTE_LEN ];
+       u8 addr2[ MACADRR_BYTE_LEN ];
+       u8 addr3[ MACADRR_BYTE_LEN ];
+       u16 seq_ctrl;     // more endians!
+} __attribute__ ((packed)) ;
+
+// TX TFD with 32 byte MAC Header
+struct tx_tfd_32
+{
+       struct machdr32    mchdr;                      // 32
+       u32                uivplaceholder[2];          // 8
+} __attribute__ ((packed)) ;
+
+// TX TFD with 30 byte MAC Header
+struct tx_tfd_30
+{
+       struct machdr30    mchdr;                      // 30
+       u8                 reserved[2];                // 2
+       u32                uivplaceholder[2];          // 8
+} __attribute__ ((packed)) ;
+
+// tx tfd with 26 byte mac header
+struct tx_tfd_26
+{
+       struct machdr26    mchdr;                      // 26
+       u8                 reserved1[2];               // 2
+       u32                uivplaceholder[2];          // 8
+       u8                 reserved2[4];               // 4
+} __attribute__ ((packed)) ;
+
+// tx tfd with 24 byte mac header
+struct tx_tfd_24
+{
+       struct machdr24    mchdr;                      // 24
+       u32                uivplaceholder[2];          // 8
+       u8                 reserved[8];                // 8
+} __attribute__ ((packed)) ;
+
+
+#define DCT_WEP_KEY_FIELD_LENGTH 16
+
+struct tfd_command
+{
+       u8 index;
+       u8 length;
+       u16 reserved;
+       u8 payload[0];
+} __attribute__ ((packed)) ;
+
+struct tfd_data {
+       /* Header */
+       u32 work_area_ptr;
+       u8 station_number; /* 0 for BSS */
+       u8 reserved1;
+       u16 reserved2;
+
+       /* Tx Parameters */
+       u8 cmd_id;
+       u8 seq_num;
+       u16 len;
+       u8 priority;
+       u8 tx_flags;
+       u8 tx_flags_ext;
+       u8 key_index;
+       u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH];
+       u8 rate;
+       u8 antenna;
+       u16 next_packet_duration;
+       u16 next_frag_len;
+       u16 back_off_counter; //////txop;
+       u8 retrylimit;
+       u16 cwcurrent;
+       u8 reserved3;
+
+       /* 802.11 MAC Header */
+       union
+       {
+               struct tx_tfd_24 tfd_24;
+               struct tx_tfd_26 tfd_26;
+               struct tx_tfd_30 tfd_30;
+               struct tx_tfd_32 tfd_32;
+       } tfd;
+
+       /* Payload DMA info */
+       u32 num_chunks;
+       u32 chunk_ptr[NUM_TFD_CHUNKS];
+       u16 chunk_len[NUM_TFD_CHUNKS];
+} __attribute__ ((packed));
+
+struct txrx_control_flags
+{
+       u8 message_type;
+       u8 rx_seq_num;
+       u8 control_bits;
+       u8 reserved;
+} __attribute__ ((packed));
+
+#define  TFD_SIZE                           128
+#define  TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH   (TFD_SIZE - sizeof(struct txrx_control_flags))
+
+struct tfd_frame
+{
+       struct txrx_control_flags control_flags;
+       union {
+               struct tfd_data data;
+               struct tfd_command cmd;
+               u8 raw[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH];
+       } u;
+} __attribute__ ((packed)) ;
+
+typedef void destructor_func(const void*);
+
+/**
+ * Tx Queue for DMA. Queue consists of circular buffer of
+ * BD's and required locking structures.
+ */
+struct clx2_tx_queue {
+       struct clx2_queue q;
+       struct tfd_frame* bd;
+       struct ieee80211_txb **txb;
+};
+
+/*
+ * RX related structures and functions
+ */
+#define RX_FREE_BUFFERS 32
+#define RX_LOW_WATERMARK 8
+
+#define SUP_RATE_11A_MAX_NUM_CHANNELS  (8)
+#define SUP_RATE_11B_MAX_NUM_CHANNELS  (4)
+#define SUP_RATE_11G_MAX_NUM_CHANNELS  (12)
+
+// Used for passing to driver number of successes and failures per rate
+struct rate_histogram
+{
+       union {
+               u32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+               u32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+               u32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+       } success;
+       union {
+               u32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+               u32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+               u32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+       } failed;
+} __attribute__ ((packed));
+
+/* statistics command response */
+struct ipw_cmd_stats {
+       u8 cmd_id;
+       u8 seq_num;
+       u16 good_sfd;
+       u16 bad_plcp;
+       u16 wrong_bssid;
+       u16 valid_mpdu;
+       u16 bad_mac_header;
+       u16 reserved_frame_types;
+       u16 rx_ina;
+       u16 bad_crc32;
+       u16 invalid_cts;
+       u16 invalid_acks;
+       u16 long_distance_ina_fina;
+       u16 dsp_silence_unreachable;
+       u16 accumulated_rssi;
+       u16 rx_ovfl_frame_tossed;
+       u16 rssi_silence_threshold;
+       u16 rx_ovfl_frame_supplied;
+       u16 last_rx_frame_signal;
+       u16 last_rx_frame_noise;
+       u16 rx_autodetec_no_ofdm;
+       u16 rx_autodetec_no_barker;
+       u16 reserved;
+} __attribute__ ((packed));
+
+struct notif_channel_result {
+       u8 channel_num;
+       struct ipw_cmd_stats stats;
+       u8 uReserved;
+} __attribute__ ((packed));
+
+struct notif_scan_complete {
+       u8 scan_type;
+       u8 num_channels;
+       u8 status;
+       u8 reserved;
+}  __attribute__ ((packed));
+
+struct notif_frag_length {
+       u16 frag_length;
+       u16 reserved;
+}  __attribute__ ((packed));
+
+struct notif_beacon_state {
+       u32 state;
+       u32 number;
+} __attribute__ ((packed));
+
+struct notif_tgi_tx_key {
+       u8 key_state;
+       u8 security_type;
+       u8 station_index;
+       u8 reserved;
+} __attribute__ ((packed));
+
+struct notif_link_deterioration {
+       struct ipw_cmd_stats stats;
+       u8 rate;
+       u8 modulation;
+       struct rate_histogram histogram;
+       u8 reserved1;
+       u16 reserved2;
+} __attribute__ ((packed));
+
+struct notif_association {
+       u8 state;
+} __attribute__ ((packed));
+
+struct notif_authenticate {
+       u8 state;
+       struct machdr24 addr;
+       u16 status;
+} __attribute__ ((packed));
+
+struct notif_calibration {
+       u8 data[104];
+} __attribute__ ((packed));
+
+struct notif_noise {
+       u32 value;
+} __attribute__ ((packed));
+
+struct ipw_rx_notification {
+       u8 reserved[8];
+       u8 subtype;
+       u8 flags;
+       u16 size;
+       union {
+               struct notif_association assoc;
+               struct notif_authenticate auth;
+               struct notif_channel_result channel_result;
+               struct notif_scan_complete scan_complete;
+               struct notif_frag_length frag_len;
+               struct notif_beacon_state beacon_state;
+               struct notif_tgi_tx_key tgi_tx_key;
+               struct notif_link_deterioration link_deterioration;
+               struct notif_calibration calibration;
+               struct notif_noise noise;
+               u8 raw[0];
+       } u;
+} __attribute__ ((packed));
+
+struct ipw_rx_frame {
+       u32 reserved1;
+       u8 parent_tsf[4];     // fw_use[0] is boolean for OUR_TSF_IS_GREATER
+       u8 received_channel;  // The channel that this frame was received on.
+                             // Note that for .11b this does not have to be
+                             // the same as the channel that it was sent.
+                              // Filled by LMAC
+       u8 frameStatus;
+       u8 rate;
+       u8 rssi;
+       u8 agc;
+       u8 rssi_dbm;
+       u16 signal;
+       u16 noise;
+       u8 antennaAndPhy;
+       u8 control;           // control bit should be on in bg
+       u8 rtscts_rate;       // rate of rts or cts (in rts cts sequence rate
+                             // is identical)
+       u8 rtscts_seen;       // 0x1 RTS seen ; 0x2 CTS seen
+       u16 length;
+       u8 data[0];
+} __attribute__ ((packed));
+
+struct ipw_rx_header {
+       u8 message_type;
+       u8 rx_seq_num;
+       u8 control_bits;
+       u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_rx_packet
+{
+       struct ipw_rx_header header;
+       union {
+               struct ipw_rx_frame frame;
+               struct ipw_rx_notification notification;
+       } u;
+} __attribute__ ((packed));
+
+#define IPW_RX_NOTIFICATION_SIZE sizeof(struct ipw_rx_header) + 12
+#define IPW_RX_FRAME_SIZE        sizeof(struct ipw_rx_header) + \
+                                 sizeof(struct ipw_rx_frame)
+
+struct ipw_rx_mem_buffer {
+       dma_addr_t dma_addr;
+       struct ipw_rx_buffer *rxb;
+       struct sk_buff *skb;
+       struct list_head list;
+}; /* Not transferred over network, so not  __attribute__ ((packed)) */
+
+struct ipw_rx_queue {
+       struct ipw_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
+       struct ipw_rx_mem_buffer *queue[RX_QUEUE_SIZE];
+       u32 processed; /* Internal index to last handled Rx packet */
+       u32 read;      /* Shared index to newest available Rx buffer */
+       u32 write;     /* Shared index to oldest written Rx packet */
+       u32 free_count;/* Number of pre-allocated buffers in rx_free */
+       /* Each of these lists is used as a FIFO for ipw_rx_mem_buffers */
+       struct list_head rx_free;  /* Own an SKBs */
+       struct list_head rx_used;  /* No SKB allocated */
+       spinlock_t lock;
+}; /* Not transferred over network, so not  __attribute__ ((packed)) */
+
+
+struct alive_command_responce {
+       u8 alive_command;
+       u8 sequence_number;
+       u16 software_revision;
+       u8 device_identifier;
+       u8 reserved1[5];
+       u16 reserved2;
+       u16 reserved3;
+       u16 clock_settle_time;
+       u16 powerup_settle_time;
+       u16 reserved4;
+       u8 time_stamp[5];       /* month, day, year, hours, minutes */
+       u8 ucode_valid;
+} __attribute__ ((packed));
+
+#define IPW_MAX_RATES 12
+
+struct ipw_rates {
+       u8 num_rates;
+       u8 rates[IPW_MAX_RATES];
+} __attribute__ ((packed));
+
+struct command_block
+{
+       unsigned int control;
+       u32 source_addr;
+       u32 dest_addr;
+       unsigned int status;
+} __attribute__ ((packed));
+
+#define CB_NUMBER_OF_ELEMENTS_SMALL 64
+struct fw_image_desc
+{
+       unsigned long last_cb_index;
+       unsigned long current_cb_index;
+       struct command_block cb_list[CB_NUMBER_OF_ELEMENTS_SMALL];
+       void * v_addr;
+       unsigned long p_addr;
+       unsigned long len;
+};
+
+struct ipw_sys_config
+{
+       u8 bt_coexistence;
+       u8 reserved1;
+       u8 answer_broadcast_ssid_probe;
+       u8 accept_all_data_frames;
+       u8 accept_non_directed_frames;
+       u8 exclude_unicast_unencrypted;
+       u8 disable_unicast_decryption;
+       u8 exclude_multicast_unencrypted;
+       u8 disable_multicast_decryption;
+       u8 antenna_diversity;
+       u8 pass_crc_to_host;
+       u8 dot11g_auto_detection;
+       u8 enable_cts_to_self;
+       u8 enable_multicast_filtering;
+       u8 bt_coexist_collision_thr;
+       u8 reserved2;
+       u8 accept_all_mgmt_bcpr;
+       u8 accept_all_mgtm_frames;
+       u8 pass_noise_stats_to_host;
+       u8 reserved3;
+} __attribute__ ((packed));
+
+struct ipw_multicast_addr
+{
+       u8 num_of_multicast_addresses;
+       u8 reserved[3];
+       u8 mac1[6];
+       u8 mac2[6];
+       u8 mac3[6];
+       u8 mac4[6];
+} __attribute__ ((packed));
+
+struct ipw_wep_key
+{
+       u8 cmd_id;
+       u8 seq_num;
+       u8 key_index;
+       u8 key_size;
+       u8 key[16];
+} __attribute__ ((packed));
+
+struct ipw_tgi_tx_key
+{
+       u8 key_id;
+       u8 security_type;
+       u8 station_index;
+       u8 flags;
+       u8 key[16];
+       u32 tx_counter[2];
+} __attribute__ ((packed));
+
+#define IPW_SCAN_CHANNELS 54
+
+struct ipw_scan_request
+{
+       u8 scan_type;
+       u16 dwell_time;
+       u8 channels_list[IPW_SCAN_CHANNELS];
+       u8 channels_reserved[3];
+} __attribute__ ((packed));
+
+enum {
+       IPW_SCAN_PASSIVE_TILL_FIRST_BEACON_SCAN = 0,
+       IPW_SCAN_PASSIVE_FULL_DWELL_SCAN,
+       IPW_SCAN_ACTIVE_DIRECT_SCAN,
+       IPW_SCAN_ACTIVE_BROADCAST_SCAN,
+       IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN,
+       IPW_SCAN_TYPES
+};
+
+struct ipw_scan_request_ext
+{
+       u32 full_scan_index;
+       u8 channels_list[IPW_SCAN_CHANNELS];
+       u8 scan_type[IPW_SCAN_CHANNELS / 2];
+       u8 reserved;
+       u16 dwell_time[IPW_SCAN_TYPES];
+} __attribute__ ((packed));
+
+extern inline u8 ipw_get_scan_type(struct ipw_scan_request_ext *scan, u8 index)
+{
+       if (index % 2)
+               return scan->scan_type[index / 2] & 0x0F;
+       else
+               return (scan->scan_type[index / 2] & 0xF0) >> 4;
+}
+
+extern inline void ipw_set_scan_type(struct ipw_scan_request_ext *scan,
+                                    u8 index, u8 scan_type)
+{
+       if (index % 2)
+               scan->scan_type[index / 2] =
+                       (scan->scan_type[index / 2] & 0xF0) |
+                       (scan_type & 0x0F);
+       else
+               scan->scan_type[index / 2] =
+                       (scan->scan_type[index / 2] & 0x0F) |
+                       ((scan_type & 0x0F) << 4);
+}
+
+struct ipw_associate
+{
+       u8 channel;
+       u8 auth_type:4,
+          auth_key:4;
+       u8 assoc_type;
+       u8 reserved;
+       u16 policy_support;
+       u8 preamble_length;
+       u8 ieee_mode;
+       u8 bssid[ETH_ALEN];
+       u32 assoc_tsf_msw;
+       u32 assoc_tsf_lsw;
+       u16 capability;
+       u16 listen_interval;
+       u16 beacon_interval;
+       u8 dest[ETH_ALEN];
+       u16 atim_window;
+       u8 smr;
+       u8 reserved1;
+       u16 reserved2;
+} __attribute__ ((packed));
+
+struct ipw_supported_rates
+{
+       u8 ieee_mode;
+       u8 num_rates;
+       u8 purpose;
+       u8 reserved;
+       u8 supported_rates[IPW_MAX_RATES];
+} __attribute__ ((packed));
+
+struct ipw_rts_threshold
+{
+       u16 rts_threshold;
+       u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_frag_threshold
+{
+       u16 frag_threshold;
+       u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_retry_limit
+{
+       u8 short_retry_limit;
+       u8 long_retry_limit;
+       u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_dino_config
+{
+       u32 dino_config_addr;
+       u16 dino_config_size;
+       u8 dino_response;
+       u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_aironet_info
+{
+       u8 id;
+       u8 length;
+       u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_rx_key
+{
+       u8 station_index;
+       u8 key_type;
+       u8 key_id;
+       u8 key_flag;
+       u8 key[16];
+       u8 station_address[6];
+       u8 key_index;
+       u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_country_channel_info
+{
+       u8 first_channel;
+       u8 no_channels;
+       s8 max_tx_power;
+} __attribute__ ((packed));
+
+struct ipw_country_info
+{
+       u8 id;
+       u8 length;
+       u8 country_str[3];
+       struct ipw_country_channel_info groups[7];
+} __attribute__ ((packed));
+
+struct ipw_channel_tx_power
+{
+       u8 channel_number;
+       s8 tx_power;
+} __attribute__ ((packed));
+
+#define SCAN_ASSOCIATED_INTERVAL (HZ)
+#define SCAN_INTERVAL (HZ / 10)
+#define MAX_A_CHANNELS  37
+#define MAX_B_CHANNELS  14
+
+struct ipw_tx_power
+{
+       u8 num_channels;
+       u8 ieee_mode;
+       struct ipw_channel_tx_power channels_tx_power[MAX_A_CHANNELS];
+} __attribute__ ((packed));
+
+struct ipw_qos_parameters
+{
+       u16 cw_min[4];
+       u16 cw_max[4];
+       u8 aifs[4];
+       u8 flag[4];
+       u16 tx_op_limit[4];
+} __attribute__ ((packed));
+
+struct ipw_rsn_capabilities
+{
+       u8 id;
+       u8 length;
+       u16 version;
+} __attribute__ ((packed));
+
+struct ipw_sensitivity_calib
+{
+       u16 beacon_rssi_raw;
+       u16 reserved;
+} __attribute__ ((packed));
+
+/**
+ * Host command structure.
+ *
+ * On input, the following fields should be filled:
+ * - cmd
+ * - len
+ * - status_len
+ * - param (if needed)
+ *
+ * On output,
+ * - \a status contains status;
+ * - \a param filled with status parameters.
+ */
+struct ipw_cmd {
+  u32 cmd;         /**< Host command */
+  u32 status;      /**< Status */
+  u32 status_len;  /**< How many 32 bit parameters in the status */
+  u32 len;         /**< incoming parameters length, bytes */
+  /**
+   * command parameters.
+   * There should be enough space for incoming and
+   * outcoming parameters.
+   * Incoming parameters listed 1-st, followed by outcoming params.
+   * nParams=(len+3)/4+status_len
+   */
+  u32 param[0];
+} __attribute__ ((packed));
+
+#define STATUS_HCMD_ACTIVE      (1<<0)  /**< host command in progress */
+
+#define STATUS_INT_ENABLED      (1<<1)
+#define STATUS_RF_KILL_HW       (1<<2)
+#define STATUS_RF_KILL_SW       (1<<3)
+#define STATUS_RF_KILL_MASK     (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW)
+
+#define STATUS_INIT             (1<<5)
+#define STATUS_AUTH             (1<<6)
+#define STATUS_ASSOCIATED       (1<<7)
+#define STATUS_STATE_MASK       (STATUS_INIT | STATUS_AUTH | STATUS_ASSOCIATED)
+
+#define STATUS_ASSOCIATING      (1<<8)
+#define STATUS_DISASSOCIATING   (1<<9)
+#define STATUS_ROAMING          (1<<10)
+#define STATUS_EXIT_PENDING     (1<<11)
+#define STATUS_DISASSOC_PENDING (1<<12)
+#define STATUS_STATE_PENDING    (1<<13)
+
+#define STATUS_SCAN_PENDING     (1<<20)
+#define STATUS_SCANNING         (1<<21)
+#define STATUS_SCAN_ABORTING    (1<<22)
+
+#define STATUS_INDIRECT_BYTE    (1<<28) /* sysfs entry configured for access */
+#define STATUS_INDIRECT_DWORD   (1<<29) /* sysfs entry configured for access */
+#define STATUS_DIRECT_DWORD     (1<<30) /* sysfs entry configured for access */
+
+#define STATUS_SECURITY_UPDATED (1<<31) /* Security sync needed */
+
+#define CFG_STATIC_CHANNEL      (1<<0) /* Restrict assoc. to single channel */
+#define CFG_STATIC_ESSID        (1<<1) /* Restrict assoc. to single SSID */
+#define CFG_STATIC_BSSID        (1<<2) /* Restrict assoc. to single BSSID */
+#define CFG_CUSTOM_MAC          (1<<3)
+#define CFG_PREAMBLE            (1<<4)
+#define CFG_ADHOC_PERSIST       (1<<5)
+#define CFG_ASSOCIATE           (1<<6)
+#define CFG_FIXED_RATE          (1<<7)
+#define CFG_ADHOC_CREATE        (1<<8)
+
+#define CAP_SHARED_KEY          (1<<0) /* Off = OPEN */
+#define CAP_PRIVACY_ON          (1<<1) /* Off = No privacy */
+
+#define MAX_STATIONS            32
+#define IPW_INVALID_STATION     (0xff)
+
+struct ipw_station_entry {
+       u8 mac_addr[ETH_ALEN];
+       u8 reserved;
+       u8 support_mode;
+};
+
+#define AVG_ENTRIES 8
+struct average {
+       s16 entries[AVG_ENTRIES];
+       u8 pos;
+       u8 init;
+       s32 sum;
+};
+
+struct ipw_priv {
+       /* ieee device used by generic ieee processing code */
+       struct ieee80211_device *ieee;
+       struct ieee80211_security sec;
+
+       /* spinlock */
+       spinlock_t lock;
+
+       /* basic pci-network driver stuff */
+       struct pci_dev *pci_dev;
+       struct net_device *net_dev;
+
+       /* pci hardware address support */
+       void __iomem *hw_base;
+       unsigned long hw_len;
+
+       struct fw_image_desc sram_desc;
+
+       /* result of ucode download */
+       struct alive_command_responce dino_alive;
+
+       wait_queue_head_t wait_command_queue;
+       wait_queue_head_t wait_state;
+
+       /* Rx and Tx DMA processing queues */
+       struct ipw_rx_queue *rxq;
+       struct clx2_tx_queue txq_cmd;
+       struct clx2_tx_queue txq[4];
+       u32 status;
+       u32 config;
+       u32 capability;
+
+       u8 last_rx_rssi;
+       u8 last_noise;
+       struct average average_missed_beacons;
+       struct average average_rssi;
+       struct average average_noise;
+       u32 port_type;
+       int rx_bufs_min;          /**< minimum number of bufs in Rx queue */
+       int rx_pend_max;          /**< maximum pending buffers for one IRQ */
+       u32 hcmd_seq;             /**< sequence number for hcmd */
+       u32 missed_beacon_threshold;
+       u32 roaming_threshold;
+
+       struct ipw_associate assoc_request;
+       struct ieee80211_network *assoc_network;
+
+       unsigned long ts_scan_abort;
+       struct ipw_supported_rates rates;
+       struct ipw_rates phy[3];           /**< PHY restrictions, per band */
+       struct ipw_rates supp;             /**< software defined */
+       struct ipw_rates extended;         /**< use for corresp. IE, AP only */
+
+       struct notif_link_deterioration last_link_deterioration; /** for statistics */
+       struct ipw_cmd* hcmd; /**< host command currently executed */
+
+       wait_queue_head_t hcmd_wq;     /**< host command waits for execution */
+       u32 tsf_bcn[2];              /**< TSF from latest beacon */
+
+       struct notif_calibration calib; /**< last calibration */
+
+       /* ordinal interface with firmware */
+       u32 table0_addr;
+       u32 table0_len;
+       u32 table1_addr;
+       u32 table1_len;
+       u32 table2_addr;
+       u32 table2_len;
+
+       /* context information */
+       u8 essid[IW_ESSID_MAX_SIZE];
+       u8 essid_len;
+       u8 nick[IW_ESSID_MAX_SIZE];
+       u16 rates_mask;
+       u8 channel;
+       struct ipw_sys_config sys_config;
+       u32 power_mode;
+       u8 bssid[ETH_ALEN];
+       u16 rts_threshold;
+       u8 mac_addr[ETH_ALEN];
+       u8 num_stations;
+       u8 stations[MAX_STATIONS][ETH_ALEN];
+
+       u32 notif_missed_beacons;
+
+       /* Statistics and counters normalized with each association */
+       u32 last_missed_beacons;
+       u32 last_tx_packets;
+       u32 last_rx_packets;
+       u32 last_tx_failures;
+       u32 last_rx_err;
+       u32 last_rate;
+
+       u32 missed_adhoc_beacons;
+       u32 missed_beacons;
+       u32 rx_packets;
+       u32 tx_packets;
+       u32 quality;
+
+        /* eeprom */
+       u8 eeprom[0x100];  /* 256 bytes of eeprom */
+       int eeprom_delay;
+
+       struct iw_statistics wstats;
+
+       struct workqueue_struct *workqueue;
+
+       struct work_struct adhoc_check;
+       struct work_struct associate;
+       struct work_struct disassociate;
+       struct work_struct rx_replenish;
+       struct work_struct request_scan;
+       struct work_struct adapter_restart;
+       struct work_struct rf_kill;
+       struct work_struct up;
+       struct work_struct down;
+       struct work_struct gather_stats;
+       struct work_struct abort_scan;
+       struct work_struct roam;
+       struct work_struct scan_check;
+
+       struct tasklet_struct irq_tasklet;
+
+
+#define IPW_2200BG  1
+#define IPW_2915ABG 2
+       u8 adapter;
+
+#define IPW_DEFAULT_TX_POWER 0x14
+       u8 tx_power;
+
+#ifdef CONFIG_PM
+       u32 pm_state[16];
+#endif
+
+       /* network state */
+
+       /* Used to pass the current INTA value from ISR to Tasklet */
+       u32 isr_inta;
+
+       /* debugging info */
+       u32 indirect_dword;
+       u32 direct_dword;
+       u32 indirect_byte;
+};                             /*ipw_priv */
+
+
+/* debug macros */
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW_DEBUG(level, fmt, args...) \
+do { if (ipw_debug_level & (level)) \
+  printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \
+         in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
+#else
+#define IPW_DEBUG(level, fmt, args...) do {} while (0)
+#endif                         /* CONFIG_IPW_DEBUG */
+
+/*
+ * 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:
+ *
+ * #define IPW_DL_xxxx VALUE
+ *
+ * shifting value to the left one bit from the previous entry.  xxxx should be
+ * the name of the classification (for example, WEP)
+ *
+ * You then need to either add a IPW_xxxx_DEBUG() macro definition for your
+ * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * To add your debug level to the list of levels seen when you perform
+ *
+ * % cat /proc/net/ipw/debug_level
+ *
+ * you simply need to add your entry to the ipw_debug_levels array.
+ *
+ * If you do not see debug_level in /proc/net/ipw then you do not have
+ * CONFIG_IPW_DEBUG defined in your kernel configuration
+ *
+ */
+
+#define IPW_DL_ERROR         (1<<0)
+#define IPW_DL_WARNING       (1<<1)
+#define IPW_DL_INFO          (1<<2)
+#define IPW_DL_WX            (1<<3)
+#define IPW_DL_HOST_COMMAND  (1<<5)
+#define IPW_DL_STATE         (1<<6)
+
+#define IPW_DL_NOTIF         (1<<10)
+#define IPW_DL_SCAN          (1<<11)
+#define IPW_DL_ASSOC         (1<<12)
+#define IPW_DL_DROP          (1<<13)
+#define IPW_DL_IOCTL         (1<<14)
+
+#define IPW_DL_MANAGE        (1<<15)
+#define IPW_DL_FW            (1<<16)
+#define IPW_DL_RF_KILL       (1<<17)
+#define IPW_DL_FW_ERRORS     (1<<18)
+
+
+#define IPW_DL_ORD           (1<<20)
+
+#define IPW_DL_FRAG          (1<<21)
+#define IPW_DL_WEP           (1<<22)
+#define IPW_DL_TX            (1<<23)
+#define IPW_DL_RX            (1<<24)
+#define IPW_DL_ISR           (1<<25)
+#define IPW_DL_FW_INFO       (1<<26)
+#define IPW_DL_IO            (1<<27)
+#define IPW_DL_TRACE         (1<<28)
+
+#define IPW_DL_STATS         (1<<29)
+
+
+#define IPW_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a)
+#define IPW_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a)
+#define IPW_DEBUG_INFO(f, a...)    IPW_DEBUG(IPW_DL_INFO, f, ## a)
+
+#define IPW_DEBUG_WX(f, a...)     IPW_DEBUG(IPW_DL_WX, f, ## a)
+#define IPW_DEBUG_SCAN(f, a...)   IPW_DEBUG(IPW_DL_SCAN, f, ## a)
+#define IPW_DEBUG_STATUS(f, a...) IPW_DEBUG(IPW_DL_STATUS, f, ## a)
+#define IPW_DEBUG_TRACE(f, a...)  IPW_DEBUG(IPW_DL_TRACE, f, ## a)
+#define IPW_DEBUG_RX(f, a...)     IPW_DEBUG(IPW_DL_RX, f, ## a)
+#define IPW_DEBUG_TX(f, a...)     IPW_DEBUG(IPW_DL_TX, f, ## a)
+#define IPW_DEBUG_ISR(f, a...)    IPW_DEBUG(IPW_DL_ISR, f, ## a)
+#define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a)
+#define IPW_DEBUG_WEP(f, a...)    IPW_DEBUG(IPW_DL_WEP, f, ## a)
+#define IPW_DEBUG_HC(f, a...) IPW_DEBUG(IPW_DL_HOST_COMMAND, f, ## a)
+#define IPW_DEBUG_FRAG(f, a...) IPW_DEBUG(IPW_DL_FRAG, f, ## a)
+#define IPW_DEBUG_FW(f, a...) IPW_DEBUG(IPW_DL_FW, f, ## a)
+#define IPW_DEBUG_RF_KILL(f, a...) IPW_DEBUG(IPW_DL_RF_KILL, f, ## a)
+#define IPW_DEBUG_DROP(f, a...) IPW_DEBUG(IPW_DL_DROP, f, ## a)
+#define IPW_DEBUG_IO(f, a...) IPW_DEBUG(IPW_DL_IO, f, ## a)
+#define IPW_DEBUG_ORD(f, a...) IPW_DEBUG(IPW_DL_ORD, f, ## a)
+#define IPW_DEBUG_FW_INFO(f, a...) IPW_DEBUG(IPW_DL_FW_INFO, f, ## a)
+#define IPW_DEBUG_NOTIF(f, a...) IPW_DEBUG(IPW_DL_NOTIF, f, ## a)
+#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_STATS(f, a...) IPW_DEBUG(IPW_DL_STATS, f, ## a)
+
+#include <linux/ctype.h>
+
+/*
+* Register bit definitions
+*/
+
+/* Dino control registers bits */
+
+#define DINO_ENABLE_SYSTEM 0x80
+#define DINO_ENABLE_CS     0x40
+#define DINO_RXFIFO_DATA   0x01
+#define DINO_CONTROL_REG   0x00200000
+
+#define CX2_INTA_RW       0x00000008
+#define CX2_INTA_MASK_R   0x0000000C
+#define CX2_INDIRECT_ADDR 0x00000010
+#define CX2_INDIRECT_DATA 0x00000014
+#define CX2_AUTOINC_ADDR  0x00000018
+#define CX2_AUTOINC_DATA  0x0000001C
+#define CX2_RESET_REG     0x00000020
+#define CX2_GP_CNTRL_RW   0x00000024
+
+#define CX2_READ_INT_REGISTER 0xFF4
+
+#define CX2_GP_CNTRL_BIT_INIT_DONE     0x00000004
+
+#define CX2_REGISTER_DOMAIN1_END        0x00001000
+#define CX2_SRAM_READ_INT_REGISTER     0x00000ff4
+
+#define CX2_SHARED_LOWER_BOUND          0x00000200
+#define CX2_INTERRUPT_AREA_LOWER_BOUND  0x00000f80
+
+#define CX2_NIC_SRAM_LOWER_BOUND        0x00000000
+#define CX2_NIC_SRAM_UPPER_BOUND        0x00030000
+
+#define CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29)
+#define CX2_GP_CNTRL_BIT_CLOCK_READY    0x00000001
+#define CX2_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002
+
+/*
+ * RESET Register Bit Indexes
+ */
+#define CBD_RESET_REG_PRINCETON_RESET 0x00000001  /* Bit 0 (LSB) */
+#define CX2_RESET_REG_SW_RESET        0x00000080  /* Bit 7       */
+#define CX2_RESET_REG_MASTER_DISABLED 0x00000100  /* Bit 8       */
+#define CX2_RESET_REG_STOP_MASTER     0x00000200  /* Bit 9       */
+#define CX2_ARC_KESHET_CONFIG         0x08000000  /* Bit 27      */
+#define CX2_START_STANDBY             0x00000004  /* Bit 2       */
+
+#define CX2_CSR_CIS_UPPER_BOUND        0x00000200
+#define CX2_DOMAIN_0_END 0x1000
+#define CLX_MEM_BAR_SIZE 0x1000
+
+#define CX2_BASEBAND_CONTROL_STATUS    0X00200000
+#define CX2_BASEBAND_TX_FIFO_WRITE     0X00200004
+#define CX2_BASEBAND_RX_FIFO_READ      0X00200004
+#define CX2_BASEBAND_CONTROL_STORE     0X00200010
+
+#define CX2_INTERNAL_CMD_EVENT         0X00300004
+#define CX2_BASEBAND_POWER_DOWN 0x00000001
+
+#define CX2_MEM_HALT_AND_RESET  0x003000e0
+
+/* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */
+#define CX2_BIT_HALT_RESET_ON  0x80000000
+#define CX2_BIT_HALT_RESET_OFF         0x00000000
+
+#define CB_LAST_VALID     0x20000000
+#define CB_INT_ENABLED    0x40000000
+#define CB_VALID          0x80000000
+#define CB_SRC_LE         0x08000000
+#define CB_DEST_LE        0x04000000
+#define CB_SRC_AUTOINC    0x00800000
+#define CB_SRC_IO_GATED   0x00400000
+#define CB_DEST_AUTOINC   0x00080000
+#define CB_SRC_SIZE_LONG  0x00200000
+#define CB_DEST_SIZE_LONG 0x00020000
+
+
+/* DMA DEFINES */
+
+#define DMA_CONTROL_SMALL_CB_CONST_VALUE 0x00540000
+#define DMA_CB_STOP_AND_ABORT            0x00000C00
+#define DMA_CB_START                     0x00000100
+
+
+#define CX2_SHARED_SRAM_SIZE               0x00030000
+#define CX2_SHARED_SRAM_DMA_CONTROL        0x00027000
+#define CB_MAX_LENGTH                      0x1FFF
+
+#define CX2_HOST_EEPROM_DATA_SRAM_SIZE 0xA18
+#define CX2_EEPROM_IMAGE_SIZE          0x100
+
+
+/* DMA defs */
+#define CX2_DMA_I_CURRENT_CB  0x003000D0
+#define CX2_DMA_O_CURRENT_CB  0x003000D4
+#define CX2_DMA_I_DMA_CONTROL 0x003000A4
+#define CX2_DMA_I_CB_BASE     0x003000A0
+
+#define CX2_TX_CMD_QUEUE_BD_BASE        (0x00000200)
+#define CX2_TX_CMD_QUEUE_BD_SIZE        (0x00000204)
+#define CX2_TX_QUEUE_0_BD_BASE          (0x00000208)
+#define CX2_TX_QUEUE_0_BD_SIZE          (0x0000020C)
+#define CX2_TX_QUEUE_1_BD_BASE          (0x00000210)
+#define CX2_TX_QUEUE_1_BD_SIZE          (0x00000214)
+#define CX2_TX_QUEUE_2_BD_BASE          (0x00000218)
+#define CX2_TX_QUEUE_2_BD_SIZE          (0x0000021C)
+#define CX2_TX_QUEUE_3_BD_BASE          (0x00000220)
+#define CX2_TX_QUEUE_3_BD_SIZE          (0x00000224)
+#define CX2_RX_BD_BASE                  (0x00000240)
+#define CX2_RX_BD_SIZE                  (0x00000244)
+#define CX2_RFDS_TABLE_LOWER            (0x00000500)
+
+#define CX2_TX_CMD_QUEUE_READ_INDEX     (0x00000280)
+#define CX2_TX_QUEUE_0_READ_INDEX       (0x00000284)
+#define CX2_TX_QUEUE_1_READ_INDEX       (0x00000288)
+#define CX2_TX_QUEUE_2_READ_INDEX       (0x0000028C)
+#define CX2_TX_QUEUE_3_READ_INDEX       (0x00000290)
+#define CX2_RX_READ_INDEX               (0x000002A0)
+
+#define CX2_TX_CMD_QUEUE_WRITE_INDEX    (0x00000F80)
+#define CX2_TX_QUEUE_0_WRITE_INDEX      (0x00000F84)
+#define CX2_TX_QUEUE_1_WRITE_INDEX      (0x00000F88)
+#define CX2_TX_QUEUE_2_WRITE_INDEX      (0x00000F8C)
+#define CX2_TX_QUEUE_3_WRITE_INDEX      (0x00000F90)
+#define CX2_RX_WRITE_INDEX              (0x00000FA0)
+
+/*
+ * EEPROM Related Definitions
+ */
+
+#define IPW_EEPROM_DATA_SRAM_ADDRESS (CX2_SHARED_LOWER_BOUND + 0x814)
+#define IPW_EEPROM_DATA_SRAM_SIZE    (CX2_SHARED_LOWER_BOUND + 0x818)
+#define IPW_EEPROM_LOAD_DISABLE      (CX2_SHARED_LOWER_BOUND + 0x81C)
+#define IPW_EEPROM_DATA              (CX2_SHARED_LOWER_BOUND + 0x820)
+#define IPW_EEPROM_UPPER_ADDRESS     (CX2_SHARED_LOWER_BOUND + 0x9E0)
+
+#define IPW_STATION_TABLE_LOWER      (CX2_SHARED_LOWER_BOUND + 0xA0C)
+#define IPW_STATION_TABLE_UPPER      (CX2_SHARED_LOWER_BOUND + 0xB0C)
+#define IPW_REQUEST_ATIM             (CX2_SHARED_LOWER_BOUND + 0xB0C)
+#define IPW_ATIM_SENT                (CX2_SHARED_LOWER_BOUND + 0xB10)
+#define IPW_WHO_IS_AWAKE             (CX2_SHARED_LOWER_BOUND + 0xB14)
+#define IPW_DURING_ATIM_WINDOW       (CX2_SHARED_LOWER_BOUND + 0xB18)
+
+
+#define MSB                             1
+#define LSB                             0
+#define WORD_TO_BYTE(_word)             ((_word) * sizeof(u16))
+
+#define GET_EEPROM_ADDR(_wordoffset,_byteoffset) \
+    ( WORD_TO_BYTE(_wordoffset) + (_byteoffset) )
+
+/* EEPROM access by BYTE */
+#define EEPROM_PME_CAPABILITY   (GET_EEPROM_ADDR(0x09,MSB))     /* 1 byte   */
+#define EEPROM_MAC_ADDRESS      (GET_EEPROM_ADDR(0x21,LSB))     /* 6 byte   */
+#define EEPROM_VERSION          (GET_EEPROM_ADDR(0x24,MSB))     /* 1 byte   */
+#define EEPROM_NIC_TYPE         (GET_EEPROM_ADDR(0x25,LSB))     /* 1 byte   */
+#define EEPROM_SKU_CAPABILITY   (GET_EEPROM_ADDR(0x25,MSB))     /* 1 byte   */
+#define EEPROM_COUNTRY_CODE     (GET_EEPROM_ADDR(0x26,LSB))     /* 3 bytes  */
+#define EEPROM_IBSS_CHANNELS_BG (GET_EEPROM_ADDR(0x28,LSB))     /* 2 bytes  */
+#define EEPROM_IBSS_CHANNELS_A  (GET_EEPROM_ADDR(0x29,MSB))     /* 5 bytes  */
+#define EEPROM_BSS_CHANNELS_BG  (GET_EEPROM_ADDR(0x2c,LSB))     /* 2 bytes  */
+#define EEPROM_HW_VERSION       (GET_EEPROM_ADDR(0x72,LSB))     /* 2 bytes  */
+
+/* NIC type as found in the one byte EEPROM_NIC_TYPE  offset*/
+#define EEPROM_NIC_TYPE_STANDARD        0
+#define EEPROM_NIC_TYPE_DELL            1
+#define EEPROM_NIC_TYPE_FUJITSU         2
+#define EEPROM_NIC_TYPE_IBM             3
+#define EEPROM_NIC_TYPE_HP              4
+
+#define FW_MEM_REG_LOWER_BOUND          0x00300000
+#define FW_MEM_REG_EEPROM_ACCESS        (FW_MEM_REG_LOWER_BOUND + 0x40)
+
+#define EEPROM_BIT_SK                   (1<<0)
+#define EEPROM_BIT_CS                   (1<<1)
+#define EEPROM_BIT_DI                   (1<<2)
+#define EEPROM_BIT_DO                   (1<<4)
+
+#define EEPROM_CMD_READ                 0x2
+
+/* Interrupts masks */
+#define CX2_INTA_NONE   0x00000000
+
+#define CX2_INTA_BIT_RX_TRANSFER                   0x00000002
+#define CX2_INTA_BIT_STATUS_CHANGE                 0x00000010
+#define CX2_INTA_BIT_BEACON_PERIOD_EXPIRED         0x00000020
+
+//Inta Bits for CF
+#define CX2_INTA_BIT_TX_CMD_QUEUE                  0x00000800
+#define CX2_INTA_BIT_TX_QUEUE_1                    0x00001000
+#define CX2_INTA_BIT_TX_QUEUE_2                    0x00002000
+#define CX2_INTA_BIT_TX_QUEUE_3                    0x00004000
+#define CX2_INTA_BIT_TX_QUEUE_4                    0x00008000
+
+#define CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE      0x00010000
+
+#define CX2_INTA_BIT_PREPARE_FOR_POWER_DOWN        0x00100000
+#define CX2_INTA_BIT_POWER_DOWN                    0x00200000
+
+#define CX2_INTA_BIT_FW_INITIALIZATION_DONE        0x01000000
+#define CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE  0x02000000
+#define CX2_INTA_BIT_RF_KILL_DONE                  0x04000000
+#define CX2_INTA_BIT_FATAL_ERROR             0x40000000
+#define CX2_INTA_BIT_PARITY_ERROR            0x80000000
+
+/* Interrupts enabled at init time. */
+#define CX2_INTA_MASK_ALL                        \
+        (CX2_INTA_BIT_TX_QUEUE_1               | \
+        CX2_INTA_BIT_TX_QUEUE_2               | \
+        CX2_INTA_BIT_TX_QUEUE_3               | \
+        CX2_INTA_BIT_TX_QUEUE_4               | \
+        CX2_INTA_BIT_TX_CMD_QUEUE             | \
+        CX2_INTA_BIT_RX_TRANSFER              | \
+        CX2_INTA_BIT_FATAL_ERROR              | \
+        CX2_INTA_BIT_PARITY_ERROR             | \
+        CX2_INTA_BIT_STATUS_CHANGE            | \
+        CX2_INTA_BIT_FW_INITIALIZATION_DONE   | \
+        CX2_INTA_BIT_BEACON_PERIOD_EXPIRED    | \
+        CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \
+        CX2_INTA_BIT_PREPARE_FOR_POWER_DOWN   | \
+        CX2_INTA_BIT_POWER_DOWN               | \
+         CX2_INTA_BIT_RF_KILL_DONE )
+
+#define IPWSTATUS_ERROR_LOG     (CX2_SHARED_LOWER_BOUND + 0x410)
+#define IPW_EVENT_LOG     (CX2_SHARED_LOWER_BOUND + 0x414)
+
+/* FW event log definitions */
+#define EVENT_ELEM_SIZE     (3 * sizeof(u32))
+#define EVENT_START_OFFSET  (1 * sizeof(u32) + 2 * sizeof(u16))
+
+/* FW error log definitions */
+#define ERROR_ELEM_SIZE     (7 * sizeof(u32))
+#define ERROR_START_OFFSET  (1 * sizeof(u32))
+
+enum {
+       IPW_FW_ERROR_OK = 0,
+       IPW_FW_ERROR_FAIL,
+       IPW_FW_ERROR_MEMORY_UNDERFLOW,
+       IPW_FW_ERROR_MEMORY_OVERFLOW,
+       IPW_FW_ERROR_BAD_PARAM,
+       IPW_FW_ERROR_BAD_CHECKSUM,
+       IPW_FW_ERROR_NMI_INTERRUPT,
+       IPW_FW_ERROR_BAD_DATABASE,
+       IPW_FW_ERROR_ALLOC_FAIL,
+       IPW_FW_ERROR_DMA_UNDERRUN,
+       IPW_FW_ERROR_DMA_STATUS,
+       IPW_FW_ERROR_DINOSTATUS_ERROR,
+       IPW_FW_ERROR_EEPROMSTATUS_ERROR,
+       IPW_FW_ERROR_SYSASSERT,
+       IPW_FW_ERROR_FATAL_ERROR
+};
+
+#define AUTH_OPEN       0
+#define AUTH_SHARED_KEY 1
+#define AUTH_IGNORE     3
+
+#define HC_ASSOCIATE      0
+#define HC_REASSOCIATE    1
+#define HC_DISASSOCIATE   2
+#define HC_IBSS_START     3
+#define HC_IBSS_RECONF    4
+#define HC_DISASSOC_QUIET 5
+
+#define IPW_RATE_CAPABILITIES 1
+#define IPW_RATE_CONNECT      0
+
+
+/*
+ * Rate values and masks
+ */
+#define IPW_TX_RATE_1MB  0x0A
+#define IPW_TX_RATE_2MB  0x14
+#define IPW_TX_RATE_5MB  0x37
+#define IPW_TX_RATE_6MB  0x0D
+#define IPW_TX_RATE_9MB  0x0F
+#define IPW_TX_RATE_11MB 0x6E
+#define IPW_TX_RATE_12MB 0x05
+#define IPW_TX_RATE_18MB 0x07
+#define IPW_TX_RATE_24MB 0x09
+#define IPW_TX_RATE_36MB 0x0B
+#define IPW_TX_RATE_48MB 0x01
+#define IPW_TX_RATE_54MB 0x03
+
+#define IPW_ORD_TABLE_ID_MASK             0x0000FF00
+#define IPW_ORD_TABLE_VALUE_MASK          0x000000FF
+
+#define IPW_ORD_TABLE_0_MASK              0x0000F000
+#define IPW_ORD_TABLE_1_MASK              0x0000F100
+#define IPW_ORD_TABLE_2_MASK              0x0000F200
+#define IPW_ORD_TABLE_3_MASK              0x0000F300
+#define IPW_ORD_TABLE_4_MASK              0x0000F400
+#define IPW_ORD_TABLE_5_MASK              0x0000F500
+#define IPW_ORD_TABLE_6_MASK              0x0000F600
+#define IPW_ORD_TABLE_7_MASK              0x0000F700
+
+/*
+ * Table 0 Entries (all entries are 32 bits)
+ */
+enum {
+       IPW_ORD_STAT_TX_CURR_RATE = IPW_ORD_TABLE_0_MASK + 1,
+       IPW_ORD_STAT_FRAG_TRESHOLD,
+       IPW_ORD_STAT_RTS_THRESHOLD,
+       IPW_ORD_STAT_TX_HOST_REQUESTS,
+       IPW_ORD_STAT_TX_HOST_COMPLETE,
+       IPW_ORD_STAT_TX_DIR_DATA,
+       IPW_ORD_STAT_TX_DIR_DATA_B_1,
+       IPW_ORD_STAT_TX_DIR_DATA_B_2,
+       IPW_ORD_STAT_TX_DIR_DATA_B_5_5,
+       IPW_ORD_STAT_TX_DIR_DATA_B_11,
+       /* Hole */
+
+
+
+
+
+
+
+       IPW_ORD_STAT_TX_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 19,
+       IPW_ORD_STAT_TX_DIR_DATA_G_2,
+       IPW_ORD_STAT_TX_DIR_DATA_G_5_5,
+       IPW_ORD_STAT_TX_DIR_DATA_G_6,
+       IPW_ORD_STAT_TX_DIR_DATA_G_9,
+       IPW_ORD_STAT_TX_DIR_DATA_G_11,
+       IPW_ORD_STAT_TX_DIR_DATA_G_12,
+       IPW_ORD_STAT_TX_DIR_DATA_G_18,
+       IPW_ORD_STAT_TX_DIR_DATA_G_24,
+       IPW_ORD_STAT_TX_DIR_DATA_G_36,
+       IPW_ORD_STAT_TX_DIR_DATA_G_48,
+       IPW_ORD_STAT_TX_DIR_DATA_G_54,
+       IPW_ORD_STAT_TX_NON_DIR_DATA,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_B_1,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_B_2,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_B_5_5,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_B_11,
+       /* Hole */
+
+
+
+
+
+
+
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 44,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_2,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_5_5,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_6,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_9,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_11,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_12,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_18,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_24,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_36,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_48,
+       IPW_ORD_STAT_TX_NON_DIR_DATA_G_54,
+       IPW_ORD_STAT_TX_RETRY,
+       IPW_ORD_STAT_TX_FAILURE,
+       IPW_ORD_STAT_RX_ERR_CRC,
+       IPW_ORD_STAT_RX_ERR_ICV,
+       IPW_ORD_STAT_RX_NO_BUFFER,
+       IPW_ORD_STAT_FULL_SCANS,
+       IPW_ORD_STAT_PARTIAL_SCANS,
+       IPW_ORD_STAT_TGH_ABORTED_SCANS,
+       IPW_ORD_STAT_TX_TOTAL_BYTES,
+       IPW_ORD_STAT_CURR_RSSI_RAW,
+       IPW_ORD_STAT_RX_BEACON,
+       IPW_ORD_STAT_MISSED_BEACONS,
+       IPW_ORD_TABLE_0_LAST
+};
+
+#define IPW_RSSI_TO_DBM 112
+
+/* Table 1 Entries
+ */
+enum {
+       IPW_ORD_TABLE_1_LAST = IPW_ORD_TABLE_1_MASK | 1,
+};
+
+/*
+ * Table 2 Entries
+ *
+ * FW_VERSION:    16 byte string
+ * FW_DATE:       16 byte string (only 14 bytes used)
+ * UCODE_VERSION: 4 byte version code
+ * UCODE_DATE:    5 bytes code code
+ * ADDAPTER_MAC:  6 byte MAC address
+ * RTC:           4 byte clock
+ */
+enum {
+       IPW_ORD_STAT_FW_VERSION = IPW_ORD_TABLE_2_MASK | 1,
+       IPW_ORD_STAT_FW_DATE,
+       IPW_ORD_STAT_UCODE_VERSION,
+       IPW_ORD_STAT_UCODE_DATE,
+       IPW_ORD_STAT_ADAPTER_MAC,
+       IPW_ORD_STAT_RTC,
+       IPW_ORD_TABLE_2_LAST
+};
+
+/* Table 3 */
+enum {
+       IPW_ORD_STAT_TX_PACKET = IPW_ORD_TABLE_3_MASK | 0,
+       IPW_ORD_STAT_TX_PACKET_FAILURE,
+       IPW_ORD_STAT_TX_PACKET_SUCCESS,
+       IPW_ORD_STAT_TX_PACKET_ABORTED,
+       IPW_ORD_TABLE_3_LAST
+};
+
+/* Table 4 */
+enum {
+       IPW_ORD_TABLE_4_LAST = IPW_ORD_TABLE_4_MASK
+};
+
+/* Table 5 */
+enum {
+       IPW_ORD_STAT_AVAILABLE_AP_COUNT = IPW_ORD_TABLE_5_MASK,
+       IPW_ORD_STAT_AP_ASSNS,
+       IPW_ORD_STAT_ROAM,
+       IPW_ORD_STAT_ROAM_CAUSE_MISSED_BEACONS,
+       IPW_ORD_STAT_ROAM_CAUSE_UNASSOC,
+       IPW_ORD_STAT_ROAM_CAUSE_RSSI,
+       IPW_ORD_STAT_ROAM_CAUSE_LINK_QUALITY,
+       IPW_ORD_STAT_ROAM_CAUSE_AP_LOAD_BALANCE,
+       IPW_ORD_STAT_ROAM_CAUSE_AP_NO_TX,
+       IPW_ORD_STAT_LINK_UP,
+       IPW_ORD_STAT_LINK_DOWN,
+       IPW_ORD_ANTENNA_DIVERSITY,
+       IPW_ORD_CURR_FREQ,
+       IPW_ORD_TABLE_5_LAST
+};
+
+/* Table 6 */
+enum {
+       IPW_ORD_COUNTRY_CODE = IPW_ORD_TABLE_6_MASK,
+       IPW_ORD_CURR_BSSID,
+       IPW_ORD_CURR_SSID,
+       IPW_ORD_TABLE_6_LAST
+};
+
+/* Table 7 */
+enum {
+       IPW_ORD_STAT_PERCENT_MISSED_BEACONS = IPW_ORD_TABLE_7_MASK,
+       IPW_ORD_STAT_PERCENT_TX_RETRIES,
+       IPW_ORD_STAT_PERCENT_LINK_QUALITY,
+       IPW_ORD_STAT_CURR_RSSI_DBM,
+       IPW_ORD_TABLE_7_LAST
+};
+
+#define IPW_ORDINALS_TABLE_LOWER        (CX2_SHARED_LOWER_BOUND + 0x500)
+#define IPW_ORDINALS_TABLE_0            (CX2_SHARED_LOWER_BOUND + 0x180)
+#define IPW_ORDINALS_TABLE_1            (CX2_SHARED_LOWER_BOUND + 0x184)
+#define IPW_ORDINALS_TABLE_2            (CX2_SHARED_LOWER_BOUND + 0x188)
+#define IPW_MEM_FIXED_OVERRIDE          (CX2_SHARED_LOWER_BOUND + 0x41C)
+
+struct ipw_fixed_rate {
+       u16 tx_rates;
+       u16 reserved;
+} __attribute__ ((packed));
+
+#define CX2_INDIRECT_ADDR_MASK (~0x3ul)
+
+struct host_cmd {
+       u8 cmd;
+       u8 len;
+       u16 reserved;
+       u32 param[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH];
+} __attribute__ ((packed));
+
+#define CFG_BT_COEXISTENCE_MIN                  0x00
+#define CFG_BT_COEXISTENCE_DEFER                0x02
+#define CFG_BT_COEXISTENCE_KILL                 0x04
+#define CFG_BT_COEXISTENCE_WME_OVER_BT          0x08
+#define CFG_BT_COEXISTENCE_OOB                  0x10
+#define CFG_BT_COEXISTENCE_MAX                  0xFF
+#define CFG_BT_COEXISTENCE_DEF                  0x80 /* read Bt from EEPROM*/
+
+#define CFG_CTS_TO_ITSELF_ENABLED_MIN  0x0
+#define CFG_CTS_TO_ITSELF_ENABLED_MAX  0x1
+#define CFG_CTS_TO_ITSELF_ENABLED_DEF  CFG_CTS_TO_ITSELF_ENABLED_MIN
+
+#define CFG_SYS_ANTENNA_BOTH                      0x000
+#define CFG_SYS_ANTENNA_A                         0x001
+#define CFG_SYS_ANTENNA_B                         0x003
+
+/*
+ * The definitions below were lifted off the ipw2100 driver, which only
+ * supports 'b' mode, so I'm sure these are not exactly correct.
+ *
+ * Somebody fix these!!
+ */
+#define REG_MIN_CHANNEL             0
+#define REG_MAX_CHANNEL             14
+
+#define REG_CHANNEL_MASK            0x00003FFF
+#define IPW_IBSS_11B_DEFAULT_MASK   0x87ff
+
+static const long ipw_frequencies[] = {
+       2412, 2417, 2422, 2427,
+       2432, 2437, 2442, 2447,
+       2452, 2457, 2462, 2467,
+       2472, 2484
+};
+
+#define FREQ_COUNT ARRAY_SIZE(ipw_frequencies)
+
+#define IPW_MAX_CONFIG_RETRIES 10
+
+static inline u32 frame_hdr_len(struct ieee80211_hdr *hdr)
+{
+       u32 retval;
+       u16 fc;
+
+       retval = sizeof(struct ieee80211_hdr);
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       /*
+        * Function     ToDS    FromDS
+        * IBSS         0       0
+        * To AP        1       0
+        * From AP      0       1
+        * WDS (bridge) 1       1
+        *
+        * Only WDS frames use Address4 among them. --YZ
+        */
+       if (!(fc & IEEE80211_FCTL_TODS) || !(fc & IEEE80211_FCTL_FROMDS))
+               retval -= ETH_ALEN;
+
+       return retval;
+}
+
+#endif /* __ipw2200_h__ */
index 9c2d07cde0101a311c298bedba42d318861a9d34..d7947358e49db7bd55a59058fd0ca88ac65091ce 100644 (file)
@@ -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>
 #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);
index 6c42b573a95a9138fb407df50eb5ef12ad797b57..4b0acae22b0d67e01d11f3b1c2f3ee991222d9f4 100644 (file)
@@ -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;
index f6130a53b7966fe7f9ba5d267b120c9b4f08eff5..183c4732ef65ca0eb24e99c79d1b43197a3f8930 100644 (file)
 /* 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;
index 29cff6daf860af6c8c5b8ec1ef190a0f750762fe..fabc63ee153c7c8de715dc51f212055f35de8efa 100644 (file)
@@ -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 ****************************/
index 677ff71883cb826a1e81eafdc4b8d9e9befabfad..01d882be8790c1b9c8d2b81d71fcc64248210dad 100644 (file)
@@ -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 */
index 8636d93067854bbeb30ce0868369f0cd121e625a..b5719437e981a680ec655863e89bed5ce88379ec 100644 (file)
@@ -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));
 
 /*
index dd902126d0183aec8f5439a834e4d8ba51b3b3cd..7cc5edbf6edee7f4c41bfd8503319801c19a208c 100644 (file)
@@ -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;
index 713c78f3a65d22950adccf6a5111ecc03a328778..49bd21702314ccda11e716c1b99aa2e2d1229ade 100644 (file)
  * between the ROM and other resources, so enabling it may disable access
  * to MMIO registers or other card memory.
  */
-static void pci_enable_rom(struct pci_dev *pdev)
+static int pci_enable_rom(struct pci_dev *pdev)
 {
+       struct resource *res = pdev->resource + PCI_ROM_RESOURCE;
+       struct pci_bus_region region;
        u32 rom_addr;
 
+       if (!res->flags)
+               return -1;
+
+       pcibios_resource_to_bus(pdev, &region, res);
        pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
-       rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+       rom_addr &= ~PCI_ROM_ADDRESS_MASK;
+       rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE;
        pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+       return 0;
 }
 
 /**
@@ -71,19 +79,21 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
        } else {
                if (res->flags & IORESOURCE_ROM_COPY) {
                        *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
-                       return (void __iomem *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+                       return (void __iomem *)pci_resource_start(pdev,
+                                                            PCI_ROM_RESOURCE);
                } else {
                        /* assign the ROM an address if it doesn't have one */
-                       if (res->parent == NULL)
-                               pci_assign_resource(pdev, PCI_ROM_RESOURCE);
-
+                       if (res->parent == NULL &&
+                           pci_assign_resource(pdev,PCI_ROM_RESOURCE))
+                               return NULL;
                        start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
                        *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
                        if (*size == 0)
                                return NULL;
 
                        /* Enable ROM space decodes */
-                       pci_enable_rom(pdev);
+                       if (pci_enable_rom(pdev))
+                               return NULL;
                }
        }
 
index 16f352195512e1d972be935e81b9e9cbf160f37d..fe3fd4115e1e31608745a070aded61063eaba818 100644 (file)
@@ -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/
index e32a80b39182b5dbbc06d15b2f96abf1897f9e7f..fc013978837e21fd756a594d53b6d52326b2646f 100644 (file)
@@ -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);
index 7495026e2c18bd5c8fd9daa5c237f39e046c2867..e350dcb544e82a2dc94034a35807f8d850347991 100644 (file)
@@ -383,39 +383,45 @@ __ixp4xx_insl(u32 io_addr, u32 *vaddr, u32 count)
                *vaddr++ = inl(io_addr);
 }
 
-#define        __is_io_address(p)      (((unsigned long)p >= 0x0) && \
-                                       ((unsigned long)p <= 0x0000ffff))
+#define PIO_OFFSET      0x10000UL
+#define PIO_MASK        0x0ffffUL
+
+#define        __is_io_address(p)      (((unsigned long)p >= PIO_OFFSET) && \
+                                       ((unsigned long)p <= (PIO_MASK + PIO_OFFSET)))
 static inline unsigned int
-__ixp4xx_ioread8(void __iomem *port)
+__ixp4xx_ioread8(void __iomem *addr)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               return  (unsigned int)__ixp4xx_inb((unsigned int)port);
+               return  (unsigned int)__ixp4xx_inb(port & PIO_MASK);
        else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
-               return (unsigned int)__raw_readb((u32)port);
+               return (unsigned int)__raw_readb(port);
 #else
-               return (unsigned int)__ixp4xx_readb((u32)port);
+               return (unsigned int)__ixp4xx_readb(port);
 #endif
 }
 
 static inline void
-__ixp4xx_ioread8_rep(u32 port, u8 *vaddr, u32 count)
+__ixp4xx_ioread8_rep(void __iomem *addr, void *vaddr, u32 count)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_insb(port, vaddr, count);
+               __ixp4xx_insb(port & PIO_MASK, vaddr, count);
        else
 #ifndef        CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_readsb((void __iomem *)port, vaddr, count);
+               __raw_readsb(addr, vaddr, count);
 #else
                __ixp4xx_readsb(port, vaddr, count);
 #endif
 }
 
 static inline unsigned int
-__ixp4xx_ioread16(void __iomem *port)
+__ixp4xx_ioread16(void __iomem *addr)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               return  (unsigned int)__ixp4xx_inw((unsigned int)port);
+               return  (unsigned int)__ixp4xx_inw(port & PIO_MASK);
        else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
                return le16_to_cpu(__raw_readw((u32)port));
@@ -425,23 +431,25 @@ __ixp4xx_ioread16(void __iomem *port)
 }
 
 static inline void
-__ixp4xx_ioread16_rep(u32 port, u16 *vaddr, u32 count)
+__ixp4xx_ioread16_rep(void __iomem *addr, void *vaddr, u32 count)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_insw(port, vaddr, count);
+               __ixp4xx_insw(port & PIO_MASK, vaddr, count);
        else
 #ifndef        CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_readsw((void __iomem *)port, vaddr, count);
+               __raw_readsw(addr, vaddr, count);
 #else
                __ixp4xx_readsw(port, vaddr, count);
 #endif
 }
 
 static inline unsigned int
-__ixp4xx_ioread32(void __iomem *port)
+__ixp4xx_ioread32(void __iomem *addr)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               return  (unsigned int)__ixp4xx_inl((unsigned int)port);
+               return  (unsigned int)__ixp4xx_inl(port & PIO_MASK);
        else {
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
                return le32_to_cpu(__raw_readl((u32)port));
@@ -452,90 +460,100 @@ __ixp4xx_ioread32(void __iomem *port)
 }
 
 static inline void
-__ixp4xx_ioread32_rep(u32 port, u32 *vaddr, u32 count)
+__ixp4xx_ioread32_rep(void __iomem *addr, void *vaddr, u32 count)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_insl(port, vaddr, count);
+               __ixp4xx_insl(port & PIO_MASK, vaddr, count);
        else
 #ifndef        CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_readsl((void __iomem *)port, vaddr, count);
+               __raw_readsl(addr, vaddr, count);
 #else
                __ixp4xx_readsl(port, vaddr, count);
 #endif
 }
 
 static inline void
-__ixp4xx_iowrite8(u8 value, void __iomem *port)
+__ixp4xx_iowrite8(u8 value, void __iomem *addr)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_outb(value, (unsigned int)port);
+               __ixp4xx_outb(value, port & PIO_MASK);
        else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_writeb(value, (u32)port);
+               __raw_writeb(value, port);
 #else
-               __ixp4xx_writeb(value, (u32)port);
+               __ixp4xx_writeb(value, port);
 #endif
 }
 
 static inline void
-__ixp4xx_iowrite8_rep(u32 port, u8 *vaddr, u32 count)
+__ixp4xx_iowrite8_rep(void __iomem *addr, const void *vaddr, u32 count)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_outsb(port, vaddr, count);
+               __ixp4xx_outsb(port & PIO_MASK, vaddr, count);
+       else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_writesb((void __iomem *)port, vaddr, count);
+               __raw_writesb(addr, vaddr, count);
 #else
                __ixp4xx_writesb(port, vaddr, count);
 #endif
 }
 
 static inline void
-__ixp4xx_iowrite16(u16 value, void __iomem *port)
+__ixp4xx_iowrite16(u16 value, void __iomem *addr)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_outw(value, (unsigned int)port);
+               __ixp4xx_outw(value, port & PIO_MASK);
        else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_writew(cpu_to_le16(value), (u32)port);
+               __raw_writew(cpu_to_le16(value), addr);
 #else
-               __ixp4xx_writew(value, (u32)port);
+               __ixp4xx_writew(value, port);
 #endif
 }
 
 static inline void
-__ixp4xx_iowrite16_rep(u32 port, u16 *vaddr, u32 count)
+__ixp4xx_iowrite16_rep(void __iomem *addr, const void *vaddr, u32 count)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_outsw(port, vaddr, count);
+               __ixp4xx_outsw(port & PIO_MASK, vaddr, count);
+       else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_readsw((void __iomem *)port, vaddr, count);
+               __raw_writesw(addr, vaddr, count);
 #else
                __ixp4xx_writesw(port, vaddr, count);
 #endif
 }
 
 static inline void
-__ixp4xx_iowrite32(u32 value, void __iomem *port)
+__ixp4xx_iowrite32(u32 value, void __iomem *addr)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_outl(value, (unsigned int)port);
+               __ixp4xx_outl(value, port & PIO_MASK);
        else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_writel(cpu_to_le32(value), (u32)port);
+               __raw_writel(cpu_to_le32(value), port);
 #else
-               __ixp4xx_writel(value, (u32)port);
+               __ixp4xx_writel(value, port);
 #endif
 }
 
 static inline void
-__ixp4xx_iowrite32_rep(u32 port, u32 *vaddr, u32 count)
+__ixp4xx_iowrite32_rep(void __iomem *addr, const void *vaddr, u32 count)
 {
+       unsigned long port = (unsigned long __force)addr;
        if (__is_io_address(port))
-               __ixp4xx_outsl(port, vaddr, count);
+               __ixp4xx_outsl(port & PIO_MASK, vaddr, count);
+       else
 #ifndef CONFIG_IXP4XX_INDIRECT_PCI
-               __raw_readsl((void __iomem *)port, vaddr, count);
+               __raw_writesl(addr, vaddr, count);
 #else
-               __ixp4xx_outsl(port, vaddr, count);
+               __ixp4xx_writesl(port, vaddr, count);
 #endif
 }
 
@@ -555,7 +573,7 @@ __ixp4xx_iowrite32_rep(u32 port, u32 *vaddr, u32 count)
 #define        iowrite16_rep(p, v, c)          __ixp4xx_iowrite16_rep(p, v, c)
 #define        iowrite32_rep(p, v, c)          __ixp4xx_iowrite32_rep(p, v, c)
 
-#define        ioport_map(port, nr)            ((void __iomem*)port)
+#define        ioport_map(port, nr)            ((void __iomem*)(port + PIO_OFFSET))
 #define        ioport_unmap(addr)
 
 #endif //  __ASM_ARM_ARCH_IO_H
index 3a626c03ea2665915df82795616edc06606843ea..d13ee7f78c70a93b6528b06874971fdf4961cb49 100644 (file)
@@ -83,17 +83,6 @@ extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys);
 #define IXP4XX_GPIO_OUT                0x1
 #define IXP4XX_GPIO_IN                 0x2
 
-#define IXP4XX_GPIO_INTSTYLE_MASK      0x7C  /* Bits [6:2] define interrupt style */
-
-/* 
- * GPIO interrupt types.
- */
-#define IXP4XX_GPIO_ACTIVE_HIGH                0x4 /* Default */
-#define IXP4XX_GPIO_ACTIVE_LOW         0x8
-#define IXP4XX_GPIO_RISING_EDGE                0x10
-#define IXP4XX_GPIO_FALLING_EDGE       0x20
-#define IXP4XX_GPIO_TRANSITIONAL       0x40
-
 /* GPIO signal types */
 #define IXP4XX_GPIO_LOW                        0
 #define IXP4XX_GPIO_HIGH               1
@@ -102,7 +91,13 @@ extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys);
 #define IXP4XX_GPIO_CLK_0              14
 #define IXP4XX_GPIO_CLK_1              15
 
-extern void gpio_line_config(u8 line, u32 style);
+static inline void gpio_line_config(u8 line, u32 direction)
+{
+       if (direction == IXP4XX_GPIO_OUT)
+               *IXP4XX_GPIO_GPOER |= (1 << line);
+       else
+               *IXP4XX_GPIO_GPOER &= ~(1 << line);
+}
 
 static inline void gpio_line_get(u8 line, int *value)
 {
index 51f0fe0ac165bb4a6ee64f69faeaa2576994fce8..939d9e5020a0915f5c7ac7e5a50c49533187904f 100644 (file)
 #define UDCOTGICR_IEIDF        (1 << 0)        /* OTG ID Change Falling Edge
                                           Interrupt Enable */
 
+#define UP2OCR           __REG(0x40600020)  /* USB Port 2 Output Control register */
+
+#define UP2OCR_CPVEN   (1 << 0)        /* Charge Pump Vbus Enable */
+#define UP2OCR_CPVPE   (1 << 1)        /* Charge Pump Vbus Pulse Enable */
+#define UP2OCR_DPPDE   (1 << 2)        /* Host Port 2 Transceiver D+ Pull Down Enable */
+#define UP2OCR_DMPDE   (1 << 3)        /* Host Port 2 Transceiver D- Pull Down Enable */
+#define UP2OCR_DPPUE   (1 << 4)        /* Host Port 2 Transceiver D+ Pull Up Enable */
+#define UP2OCR_DMPUE   (1 << 5)        /* Host Port 2 Transceiver D- Pull Up Enable */
+#define UP2OCR_DPPUBE  (1 << 6)        /* Host Port 2 Transceiver D+ Pull Up Bypass Enable */
+#define UP2OCR_DMPUBE  (1 << 7)        /* Host Port 2 Transceiver D- Pull Up Bypass Enable */
+#define UP2OCR_EXSP            (1 << 8)        /* External Transceiver Speed Control */
+#define UP2OCR_EXSUS   (1 << 9)        /* External Transceiver Speed Enable */
+#define UP2OCR_IDON            (1 << 10)       /* OTG ID Read Enable */
+#define UP2OCR_HXS             (1 << 16)       /* Host Port 2 Transceiver Output Select */
+#define UP2OCR_HXOE            (1 << 17)       /* Host Port 2 Transceiver Output Enable */
+#define UP2OCR_SEOS            (1 << 24)       /* Single-Ended Output Select */
+
 #define UDCCSN(x)      __REG2(0x40600100, (x) << 2)
 #define UDCCSR0         __REG(0x40600100) /* UDC Control/Status register - Endpoint 0 */
 #define UDCCSR0_SA     (1 << 7)        /* Setup Active */
 #define GPIO84_NSSP_RX         (84 | GPIO_ALT_FN_2_IN)
 #define GPIO85_nPCE_1_MD       (85 | GPIO_ALT_FN_1_OUT)
 #define GPIO92_MMCDAT0_MD      (92 | GPIO_ALT_FN_1_OUT)
+#define GPIO104_pSKTSEL_MD     (104 | GPIO_ALT_FN_1_OUT)
 #define GPIO109_MMCDAT1_MD     (109 | GPIO_ALT_FN_1_OUT)
 #define GPIO110_MMCDAT2_MD     (110 | GPIO_ALT_FN_1_OUT)
 #define GPIO110_MMCCS0_MD      (110 | GPIO_ALT_FN_1_OUT)
 #define PSSR_BFS       (1 << 1)        /* Battery Fault Status */
 #define PSSR_SSS       (1 << 0)        /* Software Sleep Status */
 
+#define PSLR_SL_ROD    (1 << 20)       /* Sleep-Mode/Depp-Sleep Mode nRESET_OUT Disable */
+
 #define PCFR_RO                (1 << 15)       /* RDH Override */
 #define PCFR_PO                (1 << 14)       /* PH Override */
 #define PCFR_GPROD     (1 << 12)       /* GPIO nRESET_OUT Disable */
 #define PCFR_FVC       (1 << 10)       /* Frequency/Voltage Change */
 #define PCFR_DC_EN     (1 << 7)        /* Sleep/deep-sleep DC-DC Converter Enable */
 #define PCFR_PI2CEN    (1 << 6)        /* Enable PI2C controller */
+#define PCFR_GPR_EN    (1 << 4)        /* nRESET_GPIO Pin Enable */
 #define PCFR_DS                (1 << 3)        /* Deep Sleep Mode */
 #define PCFR_FS                (1 << 2)        /* Float Static Chip Selects */
 #define PCFR_FP                (1 << 1)        /* Float PCMCIA controls */
 #define LCCR0_PDD_S    12
 #define LCCR0_BM       (1 << 20)       /* Branch mask */
 #define LCCR0_OUM      (1 << 21)       /* Output FIFO underrun mask */
+#define LCCR0_LCDT      (1 << 22)       /* LCD panel type */
+#define LCCR0_RDSTM     (1 << 23)       /* Read status interrupt mask */
+#define LCCR0_CMDIM     (1 << 24)       /* Command interrupt mask */
+#define LCCR0_OUC       (1 << 25)       /* Overlay Underlay control bit */
+#define LCCR0_LDDALT    (1 << 26)       /* LDD alternate mapping control */
 
 #define LCCR1_PPL       Fld (10, 0)      /* Pixels Per Line - 1 */
 #define LCCR1_DisWdth(Pixel)            /* Display Width [1..800 pix.]  */ \
 #define UHCFMN         __REG(0x4C00003C) /* UHC Frame Number */
 #define UHCPERS                __REG(0x4C000040) /* UHC Periodic Start */
 #define UHCLS          __REG(0x4C000044) /* UHC Low Speed Threshold */
+
 #define UHCRHDA                __REG(0x4C000048) /* UHC Root Hub Descriptor A */
+#define UHCRHDA_NOCP   (1 << 12)       /* No over current protection */
+
 #define UHCRHDB                __REG(0x4C00004C) /* UHC Root Hub Descriptor B */
 #define UHCRHS         __REG(0x4C000050) /* UHC Root Hub Status */
 #define UHCRHPS1       __REG(0x4C000054) /* UHC Root Hub Port 1 Status */
index e5e938b79accf555747e88008d86d17a50d056d9..16f4c3cc1388bd70ce8bfe9a93b16d34a5b5dabf 100644 (file)
@@ -1,7 +1,7 @@
 /* linux/include/asm/arch-s3c2410/regs-clock.h
  *
- * Copyright (c) 2003,2004 Simtec Electronics <linux@simtec.co.uk>
- *                   http://www.simtec.co.uk/products/SWLINUX/
+ * Copyright (c) 2003,2004,2005 Simtec Electronics <linux@simtec.co.uk>
+ *                   http://armlinux.simtec.co.uk/
  *
  * 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
@@ -17,6 +17,7 @@
  *    29-Sep-2004 Ben Dooks        Fixed usage for assembly inclusion
  *    10-Feb-2005 Ben Dooks        Fixed CAMDIVN address (Guillaume Gourat)
  *    10-Mar-2005 Lucas Villa Real  Changed S3C2410_VA to S3C24XX_VA
+ *    27-Aug-2005 Ben Dooks        Add clock-slow info
  */
 
 #ifndef __ASM_ARM_REGS_CLOCK
 #define S3C2410_CLKDIVN_PDIVN       (1<<0)
 #define S3C2410_CLKDIVN_HDIVN       (1<<1)
 
+#define S3C2410_CLKSLOW_UCLK_OFF       (1<<7)
+#define S3C2410_CLKSLOW_MPLL_OFF       (1<<5)
+#define S3C2410_CLKSLOW_SLOW           (1<<4)
+#define S3C2410_CLKSLOW_SLOWVAL(x)     (x)
+#define S3C2410_CLKSLOW_GET_SLOWVAL(x) ((x) & 7)
+
 #ifndef __ASSEMBLY__
 
 static inline unsigned int
index abb36e54c966b4cbed6c1b8f7fef60a60ab5e81b..278de61224d1c82ca667ebee87f62a536bc06986 100644 (file)
 #define __NR_fstatfs64                 (__NR_SYSCALL_BASE+267)
 #define __NR_tgkill                    (__NR_SYSCALL_BASE+268)
 #define __NR_utimes                    (__NR_SYSCALL_BASE+269)
-#define __NR_fadvise64_64              (__NR_SYSCALL_BASE+270)
+#define __NR_arm_fadvise64_64          (__NR_SYSCALL_BASE+270)
 #define __NR_pciconfig_iobase          (__NR_SYSCALL_BASE+271)
 #define __NR_pciconfig_read            (__NR_SYSCALL_BASE+272)
 #define __NR_pciconfig_write           (__NR_SYSCALL_BASE+273)
@@ -515,7 +515,6 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6
 #define __ARCH_WANT_SYS_TIME
 #define __ARCH_WANT_SYS_UTIME
 #define __ARCH_WANT_SYS_SOCKETCALL
-#define __ARCH_WANT_SYS_FADVISE64
 #define __ARCH_WANT_SYS_GETPGRP
 #define __ARCH_WANT_SYS_LLSEEK
 #define __ARCH_WANT_SYS_NICE
index 4c06d455139c776f0cc2f78838dfcbeb2e24d48c..3a544ffc500860f5f1c166e3e2b9bff57e7e11b5 100644 (file)
@@ -116,6 +116,11 @@ extern int __initdata nid_to_pxm_map[MAX_NUMNODES];
 
 extern u16 ia64_acpiid_to_sapicid[];
 
+/*
+ * Refer Intel ACPI _PDC support document for bit definitions
+ */
+#define ACPI_PDC_EST_CAPABILITY_SMP     0x8
+
 #endif /*__KERNEL__*/
 
 #endif /*_ASM_ACPI_H*/
index c9f8d835d0cc0860345312eff324beebdf7becd4..cee16ea1780aa6e9046bd2982ad9694b2188018d 100644 (file)
@@ -81,6 +81,7 @@ struct flock {
 
 #define F_LINUX_SPECIFIC_BASE  1024
 
-#define force_o_largefile() ( ! (current->personality & PER_LINUX32) )
+#define force_o_largefile()    \
+               (personality(current->personality) != PER_LINUX32)
 
 #endif /* _ASM_IA64_FCNTL_H */
index 54e7637a326c4e1ffb6aab9835abab5f384e3be0..cf772a67f858763719a615f03ea8b8d117501973 100644 (file)
@@ -23,7 +23,7 @@
 #define __SLOW_DOWN_IO do { } while (0)
 #define SLOW_DOWN_IO   do { } while (0)
 
-#define __IA64_UNCACHED_OFFSET 0xc000000000000000UL    /* region 6 */
+#define __IA64_UNCACHED_OFFSET RGN_BASE(RGN_UNCACHED)
 
 /*
  * The legacy I/O space defined by the ia64 architecture supports only 65536 ports, but
@@ -41,7 +41,7 @@
 #define IO_SPACE_BASE(space)           ((space) << IO_SPACE_BITS)
 #define IO_SPACE_PORT(port)            ((port) & (IO_SPACE_SIZE - 1))
 
-#define IO_SPACE_SPARSE_ENCODING(p)    ((((p) >> 2) << 12) | (p & 0xfff))
+#define IO_SPACE_SPARSE_ENCODING(p)    ((((p) >> 2) << 12) | ((p) & 0xfff))
 
 struct io_space {
        unsigned long mmio_base;        /* base in MMIO space */
index ae1525352a25fb569c3243e475aafde7e53e81c2..611432ba579c0d91f64c402c2fc115f0ef1fe487 100644 (file)
@@ -2,10 +2,12 @@
 #define __MMU_H
 
 /*
- * Type for a context number.  We declare it volatile to ensure proper ordering when it's
- * accessed outside of spinlock'd critical sections (e.g., as done in activate_mm() and
- * init_new_context()).
+ * Type for a context number.  We declare it volatile to ensure proper
+ * ordering when it's accessed outside of spinlock'd critical sections
+ * (e.g., as done in activate_mm() and init_new_context()).
  */
 typedef volatile unsigned long mm_context_t;
 
+typedef unsigned long nv_mm_context_t;
+
 #endif
index e3e5fededb04c4c63301afd1e309c1f022d749b0..8d6e72f7b08efce6251952c7e69241ce243f8e28 100644 (file)
@@ -19,6 +19,7 @@
 
 #define ia64_rid(ctx,addr)     (((ctx) << 3) | (addr >> 61))
 
+# include <asm/page.h>
 # ifndef __ASSEMBLY__
 
 #include <linux/compiler.h>
@@ -55,34 +56,46 @@ static inline void
 delayed_tlb_flush (void)
 {
        extern void local_flush_tlb_all (void);
+       unsigned long flags;
 
        if (unlikely(__ia64_per_cpu_var(ia64_need_tlb_flush))) {
-               local_flush_tlb_all();
-               __ia64_per_cpu_var(ia64_need_tlb_flush) = 0;
+               spin_lock_irqsave(&ia64_ctx.lock, flags);
+               {
+                       if (__ia64_per_cpu_var(ia64_need_tlb_flush)) {
+                               local_flush_tlb_all();
+                               __ia64_per_cpu_var(ia64_need_tlb_flush) = 0;
+                       }
+               }
+               spin_unlock_irqrestore(&ia64_ctx.lock, flags);
        }
 }
 
-static inline mm_context_t
+static inline nv_mm_context_t
 get_mmu_context (struct mm_struct *mm)
 {
        unsigned long flags;
-       mm_context_t context = mm->context;
-
-       if (context)
-               return context;
-
-       spin_lock_irqsave(&ia64_ctx.lock, flags);
-       {
-               /* re-check, now that we've got the lock: */
-               context = mm->context;
-               if (context == 0) {
-                       cpus_clear(mm->cpu_vm_mask);
-                       if (ia64_ctx.next >= ia64_ctx.limit)
-                               wrap_mmu_context(mm);
-                       mm->context = context = ia64_ctx.next++;
+       nv_mm_context_t context = mm->context;
+
+       if (unlikely(!context)) {
+               spin_lock_irqsave(&ia64_ctx.lock, flags);
+               {
+                       /* re-check, now that we've got the lock: */
+                       context = mm->context;
+                       if (context == 0) {
+                               cpus_clear(mm->cpu_vm_mask);
+                               if (ia64_ctx.next >= ia64_ctx.limit)
+                                       wrap_mmu_context(mm);
+                               mm->context = context = ia64_ctx.next++;
+                       }
                }
+               spin_unlock_irqrestore(&ia64_ctx.lock, flags);
        }
-       spin_unlock_irqrestore(&ia64_ctx.lock, flags);
+       /*
+        * Ensure we're not starting to use "context" before any old
+        * uses of it are gone from our TLB.
+        */
+       delayed_tlb_flush();
+
        return context;
 }
 
@@ -104,13 +117,13 @@ destroy_context (struct mm_struct *mm)
 }
 
 static inline void
-reload_context (mm_context_t context)
+reload_context (nv_mm_context_t context)
 {
        unsigned long rid;
        unsigned long rid_incr = 0;
        unsigned long rr0, rr1, rr2, rr3, rr4, old_rr4;
 
-       old_rr4 = ia64_get_rr(0x8000000000000000UL);
+       old_rr4 = ia64_get_rr(RGN_BASE(RGN_HPAGE));
        rid = context << 3;     /* make space for encoding the region number */
        rid_incr = 1 << 8;
 
@@ -122,6 +135,10 @@ reload_context (mm_context_t context)
        rr4 = rr0 + 4*rid_incr;
 #ifdef  CONFIG_HUGETLB_PAGE
        rr4 = (rr4 & (~(0xfcUL))) | (old_rr4 & 0xfc);
+
+#  if RGN_HPAGE != 4
+#    error "reload_context assumes RGN_HPAGE is 4"
+#  endif
 #endif
 
        ia64_set_rr(0x0000000000000000UL, rr0);
@@ -138,7 +155,7 @@ reload_context (mm_context_t context)
 static inline void
 activate_context (struct mm_struct *mm)
 {
-       mm_context_t context;
+       nv_mm_context_t context;
 
        do {
                context = get_mmu_context(mm);
@@ -157,8 +174,6 @@ activate_context (struct mm_struct *mm)
 static inline void
 activate_mm (struct mm_struct *prev, struct mm_struct *next)
 {
-       delayed_tlb_flush();
-
        /*
         * We may get interrupts here, but that's OK because interrupt handlers cannot
         * touch user-space.
index 08894f73abf0384df70d33888228ee389be33bcb..9edffad8c28b730b301a9725d2af79463be57bde 100644 (file)
 #include <asm/intrinsics.h>
 #include <asm/types.h>
 
+/*
+ * The top three bits of an IA64 address are its Region Number.
+ * Different regions are assigned to different purposes.
+ */
+#define RGN_SHIFT      (61)
+#define RGN_BASE(r)    (__IA64_UL_CONST(r)<<RGN_SHIFT)
+#define RGN_BITS       (RGN_BASE(-1))
+
+#define RGN_KERNEL     7       /* Identity mapped region */
+#define RGN_UNCACHED    6      /* Identity mapped I/O region */
+#define RGN_GATE       5       /* Gate page, Kernel text, etc */
+#define RGN_HPAGE      4       /* For Huge TLB pages */
+
 /*
  * PAGE_SHIFT determines the actual kernel page size.
  */
 
 #define RGN_MAP_LIMIT  ((1UL << (4*PAGE_SHIFT - 12)) - PAGE_SIZE)      /* per region addr limit */
 
+
 #ifdef CONFIG_HUGETLB_PAGE
-# define REGION_HPAGE          (4UL)   /* note: this is hardcoded in reload_context()!*/
-# define REGION_SHIFT          61
-# define HPAGE_REGION_BASE     (REGION_HPAGE << REGION_SHIFT)
+# define HPAGE_REGION_BASE     RGN_BASE(RGN_HPAGE)
 # define HPAGE_SHIFT           hpage_shift
 # define HPAGE_SHIFT_DEFAULT   28      /* check ia64 SDM for architecture supported size */
 # define HPAGE_SIZE            (__IA64_UL_CONST(1) << HPAGE_SHIFT)
@@ -130,16 +142,13 @@ typedef union ia64_va {
 #define REGION_NUMBER(x)       ({ia64_va _v; _v.l = (long) (x); _v.f.reg;})
 #define REGION_OFFSET(x)       ({ia64_va _v; _v.l = (long) (x); _v.f.off;})
 
-#define REGION_SIZE            REGION_NUMBER(1)
-#define REGION_KERNEL          7
-
 #ifdef CONFIG_HUGETLB_PAGE
 # define htlbpage_to_page(x)   (((unsigned long) REGION_NUMBER(x) << 61)                       \
                                 | (REGION_OFFSET(x) >> (HPAGE_SHIFT-PAGE_SHIFT)))
 # define HUGETLB_PAGE_ORDER    (HPAGE_SHIFT - PAGE_SHIFT)
 # define is_hugepage_only_range(mm, addr, len)         \
-        (REGION_NUMBER(addr) == REGION_HPAGE &&        \
-         REGION_NUMBER((addr)+(len)-1) == REGION_HPAGE)
+        (REGION_NUMBER(addr) == RGN_HPAGE &&   \
+         REGION_NUMBER((addr)+(len)-1) == RGN_HPAGE)
 extern unsigned int hpage_shift;
 #endif
 
@@ -197,7 +206,7 @@ get_order (unsigned long size)
 # define __pgprot(x)   (x)
 #endif /* !STRICT_MM_TYPECHECKS */
 
-#define PAGE_OFFSET                    __IA64_UL_CONST(0xe000000000000000)
+#define PAGE_OFFSET                    RGN_BASE(RGN_KERNEL)
 
 #define VM_DATA_DEFAULT_FLAGS          (VM_READ | VM_WRITE |                                   \
                                         VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC |                \
index 2303a10ee595405455d4f4520d3a334fd2fdfe56..e828377ad295cec5d77dd3a06b1bbdf06f014737 100644 (file)
@@ -75,6 +75,8 @@
 #define PAL_CACHE_READ         259     /* read tag & data of cacheline for diagnostic testing */
 #define PAL_CACHE_WRITE                260     /* write tag & data of cacheline for diagnostic testing */
 #define PAL_VM_TR_READ         261     /* read contents of translation register */
+#define PAL_GET_PSTATE         262     /* get the current P-state */
+#define PAL_SET_PSTATE         263     /* set the P-state */
 
 #ifndef __ASSEMBLY__
 
@@ -1111,6 +1113,25 @@ ia64_pal_halt_info (pal_power_mgmt_info_u_t *power_buf)
        return iprv.status;
 }
 
+/* Get the current P-state information */
+static inline s64
+ia64_pal_get_pstate (u64 *pstate_index)
+{
+       struct ia64_pal_retval iprv;
+       PAL_CALL_STK(iprv, PAL_GET_PSTATE, 0, 0, 0);
+       *pstate_index = iprv.v0;
+       return iprv.status;
+}
+
+/* Set the P-state */
+static inline s64
+ia64_pal_set_pstate (u64 pstate_index)
+{
+       struct ia64_pal_retval iprv;
+       PAL_CALL_STK(iprv, PAL_SET_PSTATE, pstate_index, 0, 0);
+       return iprv.status;
+}
+
 /* Cause the processor to enter LIGHT HALT state, where prefetching and execution are
  * suspended, but cache and TLB coherency is maintained.
  */
index 48586e08f432b5cc2793d0d4952869497e11d7bf..2e34c06e677795268249609786ceb012e81e4f84 100644 (file)
@@ -204,21 +204,18 @@ ia64_phys_addr_valid (unsigned long addr)
 #define set_pte(ptep, pteval)  (*(ptep) = (pteval))
 #define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval)
 
-#define RGN_SIZE       (1UL << 61)
-#define RGN_KERNEL     7
-
-#define VMALLOC_START          0xa000000200000000UL
+#define VMALLOC_START          (RGN_BASE(RGN_GATE) + 0x200000000UL)
 #ifdef CONFIG_VIRTUAL_MEM_MAP
-# define VMALLOC_END_INIT      (0xa000000000000000UL + (1UL << (4*PAGE_SHIFT - 9)))
+# define VMALLOC_END_INIT      (RGN_BASE(RGN_GATE) + (1UL << (4*PAGE_SHIFT - 9)))
 # define VMALLOC_END           vmalloc_end
   extern unsigned long vmalloc_end;
 #else
-# define VMALLOC_END           (0xa000000000000000UL + (1UL << (4*PAGE_SHIFT - 9)))
+# define VMALLOC_END           (RGN_BASE(RGN_GATE) + (1UL << (4*PAGE_SHIFT - 9)))
 #endif
 
 /* fs/proc/kcore.c */
-#define        kc_vaddr_to_offset(v) ((v) - 0xa000000000000000UL)
-#define        kc_offset_to_vaddr(o) ((o) + 0xa000000000000000UL)
+#define        kc_vaddr_to_offset(v) ((v) - RGN_BASE(RGN_GATE))
+#define        kc_offset_to_vaddr(o) ((o) + RGN_BASE(RGN_GATE))
 
 /*
  * Conversion functions: convert page frame number (pfn) and a protection value to a page
index 6ece5061dc1904256c13f062bb1de4679b4e8511..e18b5ab0cb75c0cd6ee7014260cf23bdd79a59a6 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2003 Ken Chen <kenneth.w.chen@intel.com>
  * Copyright (C) 2003 Asit Mallick <asit.k.mallick@intel.com>
+ * Copyright (C) 2005 Christoph Lameter <clameter@sgi.com>
  *
  * Based on asm-i386/rwsem.h and other architecture implementation.
  *
@@ -11,9 +12,9 @@
  *
  * The lock count is initialized to 0 (no active and no waiting lockers).
  *
- * When a writer subtracts WRITE_BIAS, it'll get 0xffff0001 for the case
- * of an uncontended lock. Readers increment by 1 and see a positive value
- * when uncontended, negative if there are writers (and maybe) readers
+ * When a writer subtracts WRITE_BIAS, it'll get 0xffffffff00000001 for
+ * the case of an uncontended lock. Readers increment by 1 and see a positive
+ * value when uncontended, negative if there are writers (and maybe) readers
  * waiting (in which case it goes to sleep).
  */
 
@@ -29,7 +30,7 @@
  * the semaphore definition
  */
 struct rw_semaphore {
-       signed int              count;
+       signed long             count;
        spinlock_t              wait_lock;
        struct list_head        wait_list;
 #if RWSEM_DEBUG
@@ -37,10 +38,10 @@ struct rw_semaphore {
 #endif
 };
 
-#define RWSEM_UNLOCKED_VALUE           0x00000000
-#define RWSEM_ACTIVE_BIAS              0x00000001
-#define RWSEM_ACTIVE_MASK              0x0000ffff
-#define RWSEM_WAITING_BIAS             (-0x00010000)
+#define RWSEM_UNLOCKED_VALUE           __IA64_UL_CONST(0x0000000000000000)
+#define RWSEM_ACTIVE_BIAS              __IA64_UL_CONST(0x0000000000000001)
+#define RWSEM_ACTIVE_MASK              __IA64_UL_CONST(0x00000000ffffffff)
+#define RWSEM_WAITING_BIAS             -__IA64_UL_CONST(0x0000000100000000)
 #define RWSEM_ACTIVE_READ_BIAS         RWSEM_ACTIVE_BIAS
 #define RWSEM_ACTIVE_WRITE_BIAS                (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
 
@@ -83,7 +84,7 @@ init_rwsem (struct rw_semaphore *sem)
 static inline void
 __down_read (struct rw_semaphore *sem)
 {
-       int result = ia64_fetchadd4_acq((unsigned int *)&sem->count, 1);
+       long result = ia64_fetchadd8_acq((unsigned long *)&sem->count, 1);
 
        if (result < 0)
                rwsem_down_read_failed(sem);
@@ -95,7 +96,7 @@ __down_read (struct rw_semaphore *sem)
 static inline void
 __down_write (struct rw_semaphore *sem)
 {
-       int old, new;
+       long old, new;
 
        do {
                old = sem->count;
@@ -112,7 +113,7 @@ __down_write (struct rw_semaphore *sem)
 static inline void
 __up_read (struct rw_semaphore *sem)
 {
-       int result = ia64_fetchadd4_rel((unsigned int *)&sem->count, -1);
+       long result = ia64_fetchadd8_rel((unsigned long *)&sem->count, -1);
 
        if (result < 0 && (--result & RWSEM_ACTIVE_MASK) == 0)
                rwsem_wake(sem);
@@ -124,7 +125,7 @@ __up_read (struct rw_semaphore *sem)
 static inline void
 __up_write (struct rw_semaphore *sem)
 {
-       int old, new;
+       long old, new;
 
        do {
                old = sem->count;
@@ -141,7 +142,7 @@ __up_write (struct rw_semaphore *sem)
 static inline int
 __down_read_trylock (struct rw_semaphore *sem)
 {
-       int tmp;
+       long tmp;
        while ((tmp = sem->count) >= 0) {
                if (tmp == cmpxchg_acq(&sem->count, tmp, tmp+1)) {
                        return 1;
@@ -156,7 +157,7 @@ __down_read_trylock (struct rw_semaphore *sem)
 static inline int
 __down_write_trylock (struct rw_semaphore *sem)
 {
-       int tmp = cmpxchg_acq(&sem->count, RWSEM_UNLOCKED_VALUE,
+       long tmp = cmpxchg_acq(&sem->count, RWSEM_UNLOCKED_VALUE,
                              RWSEM_ACTIVE_WRITE_BIAS);
        return tmp == RWSEM_UNLOCKED_VALUE;
 }
@@ -167,7 +168,7 @@ __down_write_trylock (struct rw_semaphore *sem)
 static inline void
 __downgrade_write (struct rw_semaphore *sem)
 {
-       int old, new;
+       long old, new;
 
        do {
                old = sem->count;
@@ -182,7 +183,7 @@ __downgrade_write (struct rw_semaphore *sem)
  * Implement atomic add functionality.  These used to be "inline" functions, but GCC v3.1
  * doesn't quite optimize this stuff right and ends up with bad calls to fetchandadd.
  */
-#define rwsem_atomic_add(delta, sem)   atomic_add(delta, (atomic_t *)(&(sem)->count))
-#define rwsem_atomic_update(delta, sem)        atomic_add_return(delta, (atomic_t *)(&(sem)->count))
+#define rwsem_atomic_add(delta, sem)   atomic64_add(delta, (atomic64_t *)(&(sem)->count))
+#define rwsem_atomic_update(delta, sem)        atomic64_add_return(delta, (atomic64_t *)(&(sem)->count))
 
 #endif /* _ASM_IA64_RWSEM_H */
index 103d745dc5f2f9fc8eda359854310424cd63545c..2c32e4b77b54051e1237fb99a55825555c8b71cf 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (c) 1992-1999,2001-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (c) 1992-1999,2001-2005 Silicon Graphics, Inc. All rights reserved.
  */
 
 #ifndef _ASM_IA64_SN_ADDRS_H
@@ -65,7 +65,6 @@
 
 #define NASID_MASK              ((u64)NASID_BITMASK << NASID_SHIFT)
 #define AS_MASK                        ((u64)AS_BITMASK << AS_SHIFT)
-#define REGION_BITS            0xe000000000000000UL
 
 
 /*
 #define AS_CAC_SPACE           (AS_CAC_VAL << AS_SHIFT)
 
 
-/*
- * Base addresses for various address ranges.
- */
-#define CACHED                 0xe000000000000000UL
-#define UNCACHED                0xc000000000000000UL
-#define UNCACHED_PHYS           0x8000000000000000UL
-
-
 /* 
  * Virtual Mode Local & Global MMR space.  
  */
 #define SH1_LOCAL_MMR_OFFSET   0x8000000000UL
 #define SH2_LOCAL_MMR_OFFSET   0x0200000000UL
 #define LOCAL_MMR_OFFSET       (is_shub2() ? SH2_LOCAL_MMR_OFFSET : SH1_LOCAL_MMR_OFFSET)
-#define LOCAL_MMR_SPACE                (UNCACHED | LOCAL_MMR_OFFSET)
-#define LOCAL_PHYS_MMR_SPACE   (UNCACHED_PHYS | LOCAL_MMR_OFFSET)
+#define LOCAL_MMR_SPACE                (__IA64_UNCACHED_OFFSET | LOCAL_MMR_OFFSET)
+#define LOCAL_PHYS_MMR_SPACE   (RGN_BASE(RGN_HPAGE) | LOCAL_MMR_OFFSET)
 
 #define SH1_GLOBAL_MMR_OFFSET  0x0800000000UL
 #define SH2_GLOBAL_MMR_OFFSET  0x0300000000UL
 #define GLOBAL_MMR_OFFSET      (is_shub2() ? SH2_GLOBAL_MMR_OFFSET : SH1_GLOBAL_MMR_OFFSET)
-#define GLOBAL_MMR_SPACE       (UNCACHED | GLOBAL_MMR_OFFSET)
+#define GLOBAL_MMR_SPACE       (__IA64_UNCACHED_OFFSET | GLOBAL_MMR_OFFSET)
 
 /*
  * Physical mode addresses
  */
-#define GLOBAL_PHYS_MMR_SPACE  (UNCACHED_PHYS | GLOBAL_MMR_OFFSET)
+#define GLOBAL_PHYS_MMR_SPACE  (RGN_BASE(RGN_HPAGE) | GLOBAL_MMR_OFFSET)
 
 
 /*
  * Clear region & AS bits.
  */
-#define TO_PHYS_MASK           (~(REGION_BITS | AS_MASK))
+#define TO_PHYS_MASK           (~(RGN_BITS | AS_MASK))
 
 
 /*
 #define GLOBAL_MMR_PHYS_ADDR(n,a) (GLOBAL_PHYS_MMR_SPACE | REMOTE_ADDR(n,a))
 #define GLOBAL_CAC_ADDR(n,a)   (CAC_BASE | REMOTE_ADDR(n,a))
 #define CHANGE_NASID(n,x)      ((void *)(((u64)(x) & ~NASID_MASK) | NASID_SPACE(n)))
+#define IS_TIO_NASID(n)                ((n) & 1)
 
 
 /* non-II mmr's start at top of big window space (4G) */
 /*
  * general address defines
  */
-#define CAC_BASE               (CACHED   | AS_CAC_SPACE)
-#define AMO_BASE               (UNCACHED | AS_AMO_SPACE)
-#define AMO_PHYS_BASE          (UNCACHED_PHYS | AS_AMO_SPACE)
-#define GET_BASE               (CACHED   | AS_GET_SPACE)
+#define CAC_BASE               (PAGE_OFFSET | AS_CAC_SPACE)
+#define AMO_BASE               (__IA64_UNCACHED_OFFSET | AS_AMO_SPACE)
+#define AMO_PHYS_BASE          (RGN_BASE(RGN_HPAGE) | AS_AMO_SPACE)
+#define GET_BASE               (PAGE_OFFSET | AS_GET_SPACE)
 
 /*
  * Convert Memory addresses between various addressing modes.
  *           the chiplet id is zero.  If we implement TIO-TIO dma, we might need
  *           to insert a chiplet id into this macro.  However, it is our belief
  *           right now that this chiplet id will be ICE, which is also zero.
- *           Nasid starts on bit 40.
  */
-#define PHYS_TO_TIODMA(x)      ( (((u64)(NASID_GET(x))) << 40) | NODE_OFFSET(x))
-#define PHYS_TO_DMA(x)          ( (((u64)(x) & NASID_MASK) >> 2) | NODE_OFFSET(x))
+#define SH1_TIO_PHYS_TO_DMA(x)                                                 \
+       ((((u64)(NASID_GET(x))) << 40) | NODE_OFFSET(x))
+
+#define SH2_NETWORK_BANK_OFFSET(x)                                     \
+        ((u64)(x) & ((1UL << (sn_hub_info->nasid_shift - 4)) -1))
+
+#define SH2_NETWORK_BANK_SELECT(x)                                     \
+        ((((u64)(x) & (0x3UL << (sn_hub_info->nasid_shift - 4)))       \
+               >> (sn_hub_info->nasid_shift - 4)) << 36)
+
+#define SH2_NETWORK_ADDRESS(x)                                                 \
+       (SH2_NETWORK_BANK_OFFSET(x) | SH2_NETWORK_BANK_SELECT(x))
+
+#define SH2_TIO_PHYS_TO_DMA(x)                                                 \
+        (((u64)(NASID_GET(x)) << 40) |         SH2_NETWORK_ADDRESS(x))
+
+#define PHYS_TO_TIODMA(x)                                              \
+       (is_shub1() ? SH1_TIO_PHYS_TO_DMA(x) : SH2_TIO_PHYS_TO_DMA(x))
+
+#define PHYS_TO_DMA(x)                                                 \
+       ((((u64)(x) & NASID_MASK) >> 2) | NODE_OFFSET(x))
 
 
 /*
  * Macros to test for address type.
  */
-#define IS_AMO_ADDRESS(x)      (((u64)(x) & (REGION_BITS | AS_MASK)) == AMO_BASE)
-#define IS_AMO_PHYS_ADDRESS(x) (((u64)(x) & (REGION_BITS | AS_MASK)) == AMO_PHYS_BASE)
+#define IS_AMO_ADDRESS(x)      (((u64)(x) & (RGN_BITS | AS_MASK)) == AMO_BASE)
+#define IS_AMO_PHYS_ADDRESS(x) (((u64)(x) & (RGN_BITS | AS_MASK)) == AMO_PHYS_BASE)
 
 
 /*
 #define TIO_SWIN_BASE(n, w)            (TIO_IO_BASE(n) + \
                                            ((u64) (w) << TIO_SWIN_SIZE_BITS))
 #define NODE_IO_BASE(n)                        (GLOBAL_MMR_SPACE | NASID_SPACE(n))
-#define TIO_IO_BASE(n)                  (UNCACHED | NASID_SPACE(n))
+#define TIO_IO_BASE(n)                  (__IA64_UNCACHED_OFFSET | NASID_SPACE(n))
 #define BWIN_SIZE                      (1UL << BWIN_SIZE_BITS)
 #define NODE_BWIN_BASE0(n)             (NODE_IO_BASE(n) + BWIN_SIZE)
 #define NODE_BWIN_BASE(n, w)           (NODE_BWIN_BASE0(n) + ((u64) (w) << BWIN_SIZE_BITS))
 #define RAW_NODE_SWIN_BASE(n, w)       (NODE_IO_BASE(n) + ((u64) (w) << SWIN_SIZE_BITS))
 #define BWIN_WIDGET_MASK               0x7
 #define BWIN_WINDOWNUM(x)              (((x) >> BWIN_SIZE_BITS) & BWIN_WIDGET_MASK)
+#define SH1_IS_BIG_WINDOW_ADDR(x)      ((x) & BWIN_TOP)
 
 #define TIO_BWIN_WINDOW_SELECT_MASK    0x7
 #define TIO_BWIN_WINDOWNUM(x)          (((x) >> TIO_BWIN_SIZE_BITS) & TIO_BWIN_WINDOW_SELECT_MASK)
 
-
+#define TIO_HWIN_SHIFT_BITS            33
+#define TIO_HWIN(x)                    (NODE_OFFSET(x) >> TIO_HWIN_SHIFT_BITS)
 
 /*
  * The following definitions pertain to the IO special address
 #define TIO_SWIN_WIDGETNUM(x)          (((x)  >> TIO_SWIN_SIZE_BITS) & TIO_SWIN_WIDGET_MASK)
 
 
-#define TIO_IOSPACE_ADDR(n,x)                                  \
-       /* Move in the Chiplet ID for TIO Local Block MMR */    \
-       (REMOTE_ADDR(n,x) | 1UL << (NASID_SHIFT - 2))
-
 /*
  * The following macros produce the correct base virtual address for
  * the hub registers. The REMOTE_HUB_* macro produce
  *     Otherwise, the recommended approach is to use *_HUB_L() and *_HUB_S().
  *     They're always safe.
  */
+/* Shub1 TIO & MMR addressing macros */
+#define SH1_TIO_IOSPACE_ADDR(n,x)                                      \
+       GLOBAL_MMR_ADDR(n,x)
+
+#define SH1_REMOTE_BWIN_MMR(n,x)                                       \
+       GLOBAL_MMR_ADDR(n,x)
+
+#define SH1_REMOTE_SWIN_MMR(n,x)                                       \
+       (NODE_SWIN_BASE(n,1) + 0x800000UL + (x))
+
+#define SH1_REMOTE_MMR(n,x)                                            \
+       (SH1_IS_BIG_WINDOW_ADDR(x) ? SH1_REMOTE_BWIN_MMR(n,x) :         \
+               SH1_REMOTE_SWIN_MMR(n,x))
+
+/* Shub1 TIO & MMR addressing macros */
+#define SH2_TIO_IOSPACE_ADDR(n,x)                                      \
+       ((__IA64_UNCACHED_OFFSET | REMOTE_ADDR(n,x) | 1UL << (NASID_SHIFT - 2)))
+
+#define SH2_REMOTE_MMR(n,x)                                            \
+       GLOBAL_MMR_ADDR(n,x)
+
+
+/* TIO & MMR addressing macros that work on both shub1 & shub2 */
+#define TIO_IOSPACE_ADDR(n,x)                                          \
+       ((u64 *)(is_shub1() ? SH1_TIO_IOSPACE_ADDR(n,x) :               \
+                SH2_TIO_IOSPACE_ADDR(n,x)))
+
+#define SH_REMOTE_MMR(n,x)                                             \
+       (is_shub1() ? SH1_REMOTE_MMR(n,x) : SH2_REMOTE_MMR(n,x))
+
 #define REMOTE_HUB_ADDR(n,x)                                           \
-       ((n & 1) ?                                                      \
-       /* TIO: */                                                      \
-       (is_shub2() ?                                                   \
-       /* TIO on Shub2 */                                              \
-       (volatile u64 *)(TIO_IOSPACE_ADDR(n,x))                         \
-       : /* TIO on shub1 */                                            \
-       (volatile u64 *)(GLOBAL_MMR_ADDR(n,x)))                         \
-                                                                       \
-       : /* SHUB1 and SHUB2 MMRs: */                                   \
-       (((x) & BWIN_TOP) ? ((volatile u64 *)(GLOBAL_MMR_ADDR(n,x)))    \
-       : ((volatile u64 *)(NODE_SWIN_BASE(n,1) + 0x800000 + (x)))))
+       (IS_TIO_NASID(n) ?  ((volatile u64*)TIO_IOSPACE_ADDR(n,x)) :    \
+        ((volatile u64*)SH_REMOTE_MMR(n,x)))
+
 
 #define HUB_L(x)                       (*((volatile typeof(*x) *)x))
 #define        HUB_S(x,d)                      (*((volatile typeof(*x) *)x) = (d))
index 84b254603b8d18ed2a046c98f083873ab9e8ce54..f083c94340663319cda9ccccdcfbcc8a8409875d 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 1992 - 1997, 2000-2005 Silicon Graphics, Inc. All rights reserved.
  */
 
 #ifndef _ASM_IA64_SN_GEO_H
@@ -108,7 +108,6 @@ typedef union geoid_u {
 #define INVALID_SLAB            (slabid_t)-1
 #define INVALID_SLOT            (slotid_t)-1
 #define INVALID_MODULE          ((moduleid_t)-1)
-#define INVALID_PARTID          ((partid_t)-1)
 
 static inline slabid_t geo_slab(geoid_t g)
 {
index e190dd4213d56233938104e66cd1b93958abd99e..e35074f526d9fb8c146dcc6f97be49ca094d7d25 100644 (file)
 #include <linux/rcupdate.h>
 
 #define SGI_UART_VECTOR                (0xe9)
-#define SGI_PCIBR_ERROR                (0x33)
 
 /* Reserved IRQs : Note, not to exceed IA64_SN2_FIRST_DEVICE_VECTOR */
 #define SGI_XPC_ACTIVATE                (0x30)
 #define SGI_II_ERROR                    (0x31)
 #define SGI_XBOW_ERROR                  (0x32)
-#define SGI_PCIBR_ERROR                 (0x33)
+#define SGI_PCIASIC_ERROR               (0x33)
 #define SGI_ACPI_SCI_INT                (0x34)
 #define SGI_TIOCA_ERROR                 (0x35)
 #define SGI_TIO_ERROR                   (0x36)
index 7138b1eafd6b46d48a5f83f10fc7606eb1885ef7..47bb8100fd00e93a972511f7612a6999e92badfb 100644 (file)
@@ -37,7 +37,6 @@ struct phys_cpuid {
 
 struct nodepda_s {
        void            *pdinfo;        /* Platform-dependent per-node info */
-       spinlock_t              bist_lock;
 
        /*
         * The BTEs on this node are shared by the local cpus
@@ -55,6 +54,8 @@ struct nodepda_s {
         * Array of physical cpu identifiers. Indexed by cpuid.
         */
        struct phys_cpuid       phys_cpuid[NR_CPUS];
+       spinlock_t              ptc_lock ____cacheline_aligned_in_smp;
+       spinlock_t              bist_lock;
 };
 
 typedef struct nodepda_s nodepda_t;
index 976f5eff0539ff283809c53ae304efbf37a156a3..ad0e8e8ae53fef47a2a6abaa3a5efaebf2090cce 100644 (file)
@@ -18,8 +18,9 @@
 #define PCIIO_ASIC_TYPE_PIC    2
 #define PCIIO_ASIC_TYPE_TIOCP  3
 #define PCIIO_ASIC_TYPE_TIOCA  4
+#define PCIIO_ASIC_TYPE_TIOCE  5
 
-#define PCIIO_ASIC_MAX_TYPES   5
+#define PCIIO_ASIC_MAX_TYPES   6
 
 /*
  * Common pciio bus provider data.  There should be one of these as the
@@ -30,7 +31,8 @@
 struct pcibus_bussoft {
        uint32_t                bs_asic_type;   /* chipset type */
        uint32_t                bs_xid;         /* xwidget id */
-       uint64_t                bs_persist_busnum; /* Persistent Bus Number */
+       uint32_t                bs_persist_busnum; /* Persistent Bus Number */
+       uint32_t                bs_persist_segment; /* Segment Number */
        uint64_t                bs_legacy_io;   /* legacy io pio addr */
        uint64_t                bs_legacy_mem;  /* legacy mem pio addr */
        uint64_t                bs_base;        /* widget base */
@@ -47,6 +49,8 @@ struct sn_pcibus_provider {
        dma_addr_t      (*dma_map_consistent)(struct pci_dev *, unsigned long, size_t);
        void            (*dma_unmap)(struct pci_dev *, dma_addr_t, int);
        void *          (*bus_fixup)(struct pcibus_bussoft *, struct pci_controller *);
+       void            (*force_interrupt)(struct sn_irq_info *);
+       void            (*target_interrupt)(struct sn_irq_info *);
 };
 
 extern struct sn_pcibus_provider *sn_pci_provider[];
index ea5590c76ca48eafc3505ced25ef048d1d8d1b0e..1c5108d44d8bf9f4ced5a99577546e650dfc95d2 100644 (file)
@@ -39,7 +39,6 @@ typedef struct pda_s {
        unsigned long pio_write_status_val;
        volatile unsigned long *pio_shub_war_cam_addr;
 
-       unsigned long   sn_soft_irr[4];
        unsigned long   sn_in_service_ivecs[4];
        int             sn_lb_int_war_ticks;
        int             sn_last_irq;
index df75f4c4aec3bfce3385f8f5cefe1eaafc3dd1d3..291ef3d69da225983ebf85dfd2c81523e77e5396 100644 (file)
@@ -43,6 +43,7 @@ struct sn_hwperf_object_info {
 
 /* macros for object classification */
 #define SN_HWPERF_IS_NODE(x)           ((x) && strstr((x)->name, "SHub"))
+#define SN_HWPERF_IS_NODE_SHUB2(x)     ((x) && strstr((x)->name, "SHub 2."))
 #define SN_HWPERF_IS_IONODE(x)         ((x) && strstr((x)->name, "TIO"))
 #define SN_HWPERF_IS_ROUTER(x)         ((x) && strstr((x)->name, "Router"))
 #define SN_HWPERF_IS_NL3ROUTER(x)      ((x) && strstr((x)->name, "NL3Router"))
@@ -214,6 +215,15 @@ struct sn_hwperf_ioctl_args {
  */
 #define SN_HWPERF_GET_NODE_NASID       (102|SN_HWPERF_OP_MEM_COPYOUT)
 
+/*
+ * Given a node id, determine the id of the nearest node with CPUs
+ * and the id of the nearest node that has memory. The argument
+ * node would normally be a "headless" node, e.g. an "IO node".
+ * Return 0 on success.
+ */
+extern int sn_hwperf_get_nearest_node(cnodeid_t node,
+       cnodeid_t *near_mem, cnodeid_t *near_cpu);
+
 /* return codes */
 #define SN_HWPERF_OP_OK                        0
 #define SN_HWPERF_OP_NOMEM             1
index 27976d22318657e56fcdc48833cf993a887309b1..e67825ad1930cee26dca3971b5c674f1e21c79e1 100644 (file)
@@ -55,7 +55,6 @@
 #define  SN_SAL_BUS_CONFIG                        0x02000037
 #define  SN_SAL_SYS_SERIAL_GET                    0x02000038
 #define  SN_SAL_PARTITION_SERIAL_GET              0x02000039
-#define  SN_SAL_SYSCTL_PARTITION_GET              0x0200003a
 #define  SN_SAL_SYSTEM_POWER_DOWN                 0x0200003b
 #define  SN_SAL_GET_MASTER_BASEIO_NASID                   0x0200003c
 #define  SN_SAL_COHERENCE                          0x0200003d
@@ -78,7 +77,8 @@
 
 #define SN_SAL_HUB_ERROR_INTERRUPT                0x02000060
 #define SN_SAL_BTE_RECOVER                        0x02000061
-#define SN_SAL_IOIF_GET_PCI_TOPOLOGY              0x02000062
+#define SN_SAL_RESERVED_DO_NOT_USE                0x02000062
+#define SN_SAL_IOIF_GET_PCI_TOPOLOGY              0x02000064
 
 /*
  * Service-specific constants
@@ -585,35 +585,6 @@ sn_partition_serial_number_val(void) {
        return sn_partition_serial_number;
 }
 
-/*
- * Returns the partition id of the nasid passed in as an argument,
- * or INVALID_PARTID if the partition id cannot be retrieved.
- */
-static inline partid_t
-ia64_sn_sysctl_partition_get(nasid_t nasid)
-{
-       struct ia64_sal_retval ret_stuff;
-       ia64_sal_oemcall_nolock(&ret_stuff, SN_SAL_SYSCTL_PARTITION_GET, nasid,
-                               0, 0, 0, 0, 0, 0);
-       if (ret_stuff.status != 0)
-           return INVALID_PARTID;
-       return ((partid_t)ret_stuff.v0);
-}
-
-/*
- * Returns the partition id of the current processor.
- */
-
-extern partid_t sn_partid;
-
-static inline partid_t
-sn_local_partid(void) {
-       if (unlikely(sn_partid < 0)) {
-               sn_partid = ia64_sn_sysctl_partition_get(cpuid_to_nasid(smp_processor_id()));
-       }
-       return sn_partid;
-}
-
 /*
  * Returns the physical address of the partition's reserved page through
  * an iterative number of calls.
@@ -749,7 +720,8 @@ ia64_sn_power_down(void)
 {
        struct ia64_sal_retval ret_stuff;
        SAL_CALL(ret_stuff, SN_SAL_SYSTEM_POWER_DOWN, 0, 0, 0, 0, 0, 0, 0);
-       while(1);
+       while(1)
+               cpu_relax();
        /* never returns */
 }
 
@@ -1018,24 +990,6 @@ ia64_sn_get_sn_info(int fc, u8 *shubtype, u16 *nasid_bitmask, u8 *nasid_shift,
        ret_stuff.v2 = 0;
        SAL_CALL_NOLOCK(ret_stuff, SN_SAL_GET_SN_INFO, fc, 0, 0, 0, 0, 0, 0);
 
-/***** BEGIN HACK - temp til old proms no longer supported ********/
-       if (ret_stuff.status == SALRET_NOT_IMPLEMENTED) {
-               int nasid = get_sapicid() & 0xfff;;
-#define SH_SHUB_ID_NODES_PER_BIT_MASK 0x001f000000000000UL                                               
-#define SH_SHUB_ID_NODES_PER_BIT_SHFT 48                                                               
-               if (shubtype) *shubtype = 0;
-               if (nasid_bitmask) *nasid_bitmask = 0x7ff;
-               if (nasid_shift) *nasid_shift = 38;
-               if (systemsize) *systemsize = 11;
-               if (sharing_domain_size) *sharing_domain_size = 9;
-               if (partid) *partid = ia64_sn_sysctl_partition_get(nasid);
-               if (coher) *coher = nasid >> 9;
-               if (reg) *reg = (HUB_L((u64 *) LOCAL_MMR_ADDR(SH1_SHUB_ID)) & SH_SHUB_ID_NODES_PER_BIT_MASK) >>
-                       SH_SHUB_ID_NODES_PER_BIT_SHFT;
-               return 0;
-       }
-/***** END HACK *******/
-
        if (ret_stuff.status < 0)
                return ret_stuff.status;
 
@@ -1068,12 +1022,10 @@ ia64_sn_hwperf_op(nasid_t nasid, u64 opcode, u64 a0, u64 a1, u64 a2,
 }
 
 static inline int
-ia64_sn_ioif_get_pci_topology(u64 rack, u64 bay, u64 slot, u64 slab,
-                             u64 buf, u64 len)
+ia64_sn_ioif_get_pci_topology(u64 buf, u64 len)
 {
        struct ia64_sal_retval rv;
-       SAL_CALL_NOLOCK(rv, SN_SAL_IOIF_GET_PCI_TOPOLOGY,
-               rack, bay, slot, slab, buf, len, 0);
+       SAL_CALL_NOLOCK(rv, SN_SAL_IOIF_GET_PCI_TOPOLOGY, buf, len, 0, 0, 0, 0, 0);
        return (int) rv.status;
 }
 
diff --git a/include/asm-ia64/sn/tioce.h b/include/asm-ia64/sn/tioce.h
new file mode 100644 (file)
index 0000000..2287985
--- /dev/null
@@ -0,0 +1,740 @@
+/**************************************************************************
+ *                                                                        *
+ *  Unpublished copyright (c) 2005, Silicon Graphics, Inc.                *
+ *  THIS IS UNPUBLISHED CONFIDENTIAL AND PROPRIETARY SOURCE CODE OF SGI.  *
+ *                                                                        *
+ *  The copyright notice above does  not evidence any actual or intended  *
+ *  publication  or  disclosure  of  this source  code,  which  includes  *
+ *  information that is confidential  and/or proprietary, and is a trade  *
+ *  secret, of  Silicon Graphics, Inc.   ANY REPRODUCTION, MODIFICATION,  *
+ *  DISTRIBUTION, PUBLIC  PERFORMANCE, OR  PUBLIC DISPLAY OF  OR THROUGH  *
+ *  USE  OF THIS  SOURCE CODE  WITHOUT  THE EXPRESS  WRITTEN CONSENT  OF  *
+ *  SILICON GRAPHICS, INC.  IS  STRICTLY PROHIBITED, AND IN VIOLATION OF  *
+ *  APPLICABLE  LAWS   AND  INTERNATIONAL  TREATIES.    THE  RECEIPT  OR  *
+ *  POSSESSION OF  THIS SOURCE CODE AND/OR RELATED  INFORMATION DOES NOT  *
+ *  CONVEY OR IMPLY ANY RIGHTS  TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS  *
+ *  CONTENTS,  OR TO  MANUFACTURE, USE,  OR  SELL ANYTHING  THAT IT  MAY  *
+ *  DESCRIBE, IN WHOLE OR IN PART.                                        *
+ *                                                                        *
+ **************************************************************************/
+
+#ifndef __ASM_IA64_SN_TIOCE_H__
+#define __ASM_IA64_SN_TIOCE_H__
+
+/* CE ASIC part & mfgr information  */
+#define TIOCE_PART_NUM                 0xCE00
+#define TIOCE_MFGR_NUM                 0x36
+#define TIOCE_REV_A                    0x1
+
+/* CE Virtual PPB Vendor/Device IDs */
+#define CE_VIRT_PPB_VENDOR_ID          0x10a9
+#define CE_VIRT_PPB_DEVICE_ID          0x4002
+
+/* CE Host Bridge Vendor/Device IDs */
+#define CE_HOST_BRIDGE_VENDOR_ID       0x10a9
+#define CE_HOST_BRIDGE_DEVICE_ID       0x4003
+
+
+#define TIOCE_NUM_M40_ATES             4096
+#define TIOCE_NUM_M3240_ATES           2048
+#define TIOCE_NUM_PORTS                        2
+
+/*
+ * Register layout for TIOCE.  MMR offsets are shown at the far right of the
+ * structure definition.
+ */
+typedef volatile struct tioce {
+       /*
+        * ADMIN : Administration Registers
+        */
+       uint64_t        ce_adm_id;                              /* 0x000000 */
+       uint64_t        ce_pad_000008;                          /* 0x000008 */
+       uint64_t        ce_adm_dyn_credit_status;               /* 0x000010 */
+       uint64_t        ce_adm_last_credit_status;              /* 0x000018 */
+       uint64_t        ce_adm_credit_limit;                    /* 0x000020 */
+       uint64_t        ce_adm_force_credit;                    /* 0x000028 */
+       uint64_t        ce_adm_control;                         /* 0x000030 */
+       uint64_t        ce_adm_mmr_chn_timeout;                 /* 0x000038 */
+       uint64_t        ce_adm_ssp_ure_timeout;                 /* 0x000040 */
+       uint64_t        ce_adm_ssp_dre_timeout;                 /* 0x000048 */
+       uint64_t        ce_adm_ssp_debug_sel;                   /* 0x000050 */
+       uint64_t        ce_adm_int_status;                      /* 0x000058 */
+       uint64_t        ce_adm_int_status_alias;                /* 0x000060 */
+       uint64_t        ce_adm_int_mask;                        /* 0x000068 */
+       uint64_t        ce_adm_int_pending;                     /* 0x000070 */
+       uint64_t        ce_adm_force_int;                       /* 0x000078 */
+       uint64_t        ce_adm_ure_ups_buf_barrier_flush;       /* 0x000080 */
+       uint64_t        ce_adm_int_dest[15];        /* 0x000088 -- 0x0000F8 */
+       uint64_t        ce_adm_error_summary;                   /* 0x000100 */
+       uint64_t        ce_adm_error_summary_alias;             /* 0x000108 */
+       uint64_t        ce_adm_error_mask;                      /* 0x000110 */
+       uint64_t        ce_adm_first_error;                     /* 0x000118 */
+       uint64_t        ce_adm_error_overflow;                  /* 0x000120 */
+       uint64_t        ce_adm_error_overflow_alias;            /* 0x000128 */
+       uint64_t        ce_pad_000130[2];           /* 0x000130 -- 0x000138 */
+       uint64_t        ce_adm_tnum_error;                      /* 0x000140 */
+       uint64_t        ce_adm_mmr_err_detail;                  /* 0x000148 */
+       uint64_t        ce_adm_msg_sram_perr_detail;            /* 0x000150 */
+       uint64_t        ce_adm_bap_sram_perr_detail;            /* 0x000158 */
+       uint64_t        ce_adm_ce_sram_perr_detail;             /* 0x000160 */
+       uint64_t        ce_adm_ce_credit_oflow_detail;          /* 0x000168 */
+       uint64_t        ce_adm_tx_link_idle_max_timer;          /* 0x000170 */
+       uint64_t        ce_adm_pcie_debug_sel;                  /* 0x000178 */
+       uint64_t        ce_pad_000180[16];          /* 0x000180 -- 0x0001F8 */
+
+       uint64_t        ce_adm_pcie_debug_sel_top;              /* 0x000200 */
+       uint64_t        ce_adm_pcie_debug_lat_sel_lo_top;       /* 0x000208 */
+       uint64_t        ce_adm_pcie_debug_lat_sel_hi_top;       /* 0x000210 */
+       uint64_t        ce_adm_pcie_debug_trig_sel_top;         /* 0x000218 */
+       uint64_t        ce_adm_pcie_debug_trig_lat_sel_lo_top;  /* 0x000220 */
+       uint64_t        ce_adm_pcie_debug_trig_lat_sel_hi_top;  /* 0x000228 */
+       uint64_t        ce_adm_pcie_trig_compare_top;           /* 0x000230 */
+       uint64_t        ce_adm_pcie_trig_compare_en_top;        /* 0x000238 */
+       uint64_t        ce_adm_ssp_debug_sel_top;               /* 0x000240 */
+       uint64_t        ce_adm_ssp_debug_lat_sel_lo_top;        /* 0x000248 */
+       uint64_t        ce_adm_ssp_debug_lat_sel_hi_top;        /* 0x000250 */
+       uint64_t        ce_adm_ssp_debug_trig_sel_top;          /* 0x000258 */
+       uint64_t        ce_adm_ssp_debug_trig_lat_sel_lo_top;   /* 0x000260 */
+       uint64_t        ce_adm_ssp_debug_trig_lat_sel_hi_top;   /* 0x000268 */
+       uint64_t        ce_adm_ssp_trig_compare_top;            /* 0x000270 */
+       uint64_t        ce_adm_ssp_trig_compare_en_top;         /* 0x000278 */
+       uint64_t        ce_pad_000280[48];          /* 0x000280 -- 0x0003F8 */
+
+       uint64_t        ce_adm_bap_ctrl;                        /* 0x000400 */
+       uint64_t        ce_pad_000408[127];         /* 0x000408 -- 0x0007F8 */
+
+       uint64_t        ce_msg_buf_data63_0[35];    /* 0x000800 -- 0x000918 */
+       uint64_t        ce_pad_000920[29];          /* 0x000920 -- 0x0009F8 */
+
+       uint64_t        ce_msg_buf_data127_64[35];  /* 0x000A00 -- 0x000B18 */
+       uint64_t        ce_pad_000B20[29];          /* 0x000B20 -- 0x000BF8 */
+
+       uint64_t        ce_msg_buf_parity[35];      /* 0x000C00 -- 0x000D18 */
+       uint64_t        ce_pad_000D20[29];          /* 0x000D20 -- 0x000DF8 */
+
+       uint64_t        ce_pad_000E00[576];         /* 0x000E00 -- 0x001FF8 */
+
+       /*
+        * LSI : LSI's PCI Express Link Registers (Link#1 and Link#2)
+        * Link#1 MMRs at start at 0x002000, Link#2 MMRs at 0x003000
+        * NOTE: the comment offsets at far right: let 'z' = {2 or 3}
+        */
+       #define ce_lsi(link_num)        ce_lsi[link_num-1]
+       struct ce_lsi_reg {
+               uint64_t        ce_lsi_lpu_id;                  /* 0x00z000 */
+               uint64_t        ce_lsi_rst;                     /* 0x00z008 */
+               uint64_t        ce_lsi_dbg_stat;                /* 0x00z010 */
+               uint64_t        ce_lsi_dbg_cfg;                 /* 0x00z018 */
+               uint64_t        ce_lsi_ltssm_ctrl;              /* 0x00z020 */
+               uint64_t        ce_lsi_lk_stat;                 /* 0x00z028 */
+               uint64_t        ce_pad_00z030[2];   /* 0x00z030 -- 0x00z038 */
+               uint64_t        ce_lsi_int_and_stat;            /* 0x00z040 */
+               uint64_t        ce_lsi_int_mask;                /* 0x00z048 */
+               uint64_t        ce_pad_00z050[22];  /* 0x00z050 -- 0x00z0F8 */
+               uint64_t        ce_lsi_lk_perf_cnt_sel;         /* 0x00z100 */
+               uint64_t        ce_pad_00z108;                  /* 0x00z108 */
+               uint64_t        ce_lsi_lk_perf_cnt_ctrl;        /* 0x00z110 */
+               uint64_t        ce_pad_00z118;                  /* 0x00z118 */
+               uint64_t        ce_lsi_lk_perf_cnt1;            /* 0x00z120 */
+               uint64_t        ce_lsi_lk_perf_cnt1_test;       /* 0x00z128 */
+               uint64_t        ce_lsi_lk_perf_cnt2;            /* 0x00z130 */
+               uint64_t        ce_lsi_lk_perf_cnt2_test;       /* 0x00z138 */
+               uint64_t        ce_pad_00z140[24];  /* 0x00z140 -- 0x00z1F8 */
+               uint64_t        ce_lsi_lk_lyr_cfg;              /* 0x00z200 */
+               uint64_t        ce_lsi_lk_lyr_status;           /* 0x00z208 */
+               uint64_t        ce_lsi_lk_lyr_int_stat;         /* 0x00z210 */
+               uint64_t        ce_lsi_lk_ly_int_stat_test;     /* 0x00z218 */
+               uint64_t        ce_lsi_lk_ly_int_stat_mask;     /* 0x00z220 */
+               uint64_t        ce_pad_00z228[3];   /* 0x00z228 -- 0x00z238 */
+               uint64_t        ce_lsi_fc_upd_ctl;              /* 0x00z240 */
+               uint64_t        ce_pad_00z248[3];   /* 0x00z248 -- 0x00z258 */
+               uint64_t        ce_lsi_flw_ctl_upd_to_timer;    /* 0x00z260 */
+               uint64_t        ce_lsi_flw_ctl_upd_timer0;      /* 0x00z268 */
+               uint64_t        ce_lsi_flw_ctl_upd_timer1;      /* 0x00z270 */
+               uint64_t        ce_pad_00z278[49];  /* 0x00z278 -- 0x00z3F8 */
+               uint64_t        ce_lsi_freq_nak_lat_thrsh;      /* 0x00z400 */
+               uint64_t        ce_lsi_ack_nak_lat_tmr;         /* 0x00z408 */
+               uint64_t        ce_lsi_rply_tmr_thr;            /* 0x00z410 */
+               uint64_t        ce_lsi_rply_tmr;                /* 0x00z418 */
+               uint64_t        ce_lsi_rply_num_stat;           /* 0x00z420 */
+               uint64_t        ce_lsi_rty_buf_max_addr;        /* 0x00z428 */
+               uint64_t        ce_lsi_rty_fifo_ptr;            /* 0x00z430 */
+               uint64_t        ce_lsi_rty_fifo_rd_wr_ptr;      /* 0x00z438 */
+               uint64_t        ce_lsi_rty_fifo_cred;           /* 0x00z440 */
+               uint64_t        ce_lsi_seq_cnt;                 /* 0x00z448 */
+               uint64_t        ce_lsi_ack_sent_seq_num;        /* 0x00z450 */
+               uint64_t        ce_lsi_seq_cnt_fifo_max_addr;   /* 0x00z458 */
+               uint64_t        ce_lsi_seq_cnt_fifo_ptr;        /* 0x00z460 */
+               uint64_t        ce_lsi_seq_cnt_rd_wr_ptr;       /* 0x00z468 */
+               uint64_t        ce_lsi_tx_lk_ts_ctl;            /* 0x00z470 */
+               uint64_t        ce_pad_00z478;                  /* 0x00z478 */
+               uint64_t        ce_lsi_mem_addr_ctl;            /* 0x00z480 */
+               uint64_t        ce_lsi_mem_d_ld0;               /* 0x00z488 */
+               uint64_t        ce_lsi_mem_d_ld1;               /* 0x00z490 */
+               uint64_t        ce_lsi_mem_d_ld2;               /* 0x00z498 */
+               uint64_t        ce_lsi_mem_d_ld3;               /* 0x00z4A0 */
+               uint64_t        ce_lsi_mem_d_ld4;               /* 0x00z4A8 */
+               uint64_t        ce_pad_00z4B0[2];   /* 0x00z4B0 -- 0x00z4B8 */
+               uint64_t        ce_lsi_rty_d_cnt;               /* 0x00z4C0 */
+               uint64_t        ce_lsi_seq_buf_cnt;             /* 0x00z4C8 */
+               uint64_t        ce_lsi_seq_buf_bt_d;            /* 0x00z4D0 */
+               uint64_t        ce_pad_00z4D8;                  /* 0x00z4D8 */
+               uint64_t        ce_lsi_ack_lat_thr;             /* 0x00z4E0 */
+               uint64_t        ce_pad_00z4E8[3];   /* 0x00z4E8 -- 0x00z4F8 */
+               uint64_t        ce_lsi_nxt_rcv_seq_1_cntr;      /* 0x00z500 */
+               uint64_t        ce_lsi_unsp_dllp_rcvd;          /* 0x00z508 */
+               uint64_t        ce_lsi_rcv_lk_ts_ctl;           /* 0x00z510 */
+               uint64_t        ce_pad_00z518[29];  /* 0x00z518 -- 0x00z5F8 */
+               uint64_t        ce_lsi_phy_lyr_cfg;             /* 0x00z600 */
+               uint64_t        ce_pad_00z608;                  /* 0x00z608 */
+               uint64_t        ce_lsi_phy_lyr_int_stat;        /* 0x00z610 */
+               uint64_t        ce_lsi_phy_lyr_int_stat_test;   /* 0x00z618 */
+               uint64_t        ce_lsi_phy_lyr_int_mask;        /* 0x00z620 */
+               uint64_t        ce_pad_00z628[11];  /* 0x00z628 -- 0x00z678 */
+               uint64_t        ce_lsi_rcv_phy_cfg;             /* 0x00z680 */
+               uint64_t        ce_lsi_rcv_phy_stat1;           /* 0x00z688 */
+               uint64_t        ce_lsi_rcv_phy_stat2;           /* 0x00z690 */
+               uint64_t        ce_lsi_rcv_phy_stat3;           /* 0x00z698 */
+               uint64_t        ce_lsi_rcv_phy_int_stat;        /* 0x00z6A0 */
+               uint64_t        ce_lsi_rcv_phy_int_stat_test;   /* 0x00z6A8 */
+               uint64_t        ce_lsi_rcv_phy_int_mask;        /* 0x00z6B0 */
+               uint64_t        ce_pad_00z6B8[9];   /* 0x00z6B8 -- 0x00z6F8 */
+               uint64_t        ce_lsi_tx_phy_cfg;              /* 0x00z700 */
+               uint64_t        ce_lsi_tx_phy_stat;             /* 0x00z708 */
+               uint64_t        ce_lsi_tx_phy_int_stat;         /* 0x00z710 */
+               uint64_t        ce_lsi_tx_phy_int_stat_test;    /* 0x00z718 */
+               uint64_t        ce_lsi_tx_phy_int_mask;         /* 0x00z720 */
+               uint64_t        ce_lsi_tx_phy_stat2;            /* 0x00z728 */
+               uint64_t        ce_pad_00z730[10];  /* 0x00z730 -- 0x00z77F */
+               uint64_t        ce_lsi_ltssm_cfg1;              /* 0x00z780 */
+               uint64_t        ce_lsi_ltssm_cfg2;              /* 0x00z788 */
+               uint64_t        ce_lsi_ltssm_cfg3;              /* 0x00z790 */
+               uint64_t        ce_lsi_ltssm_cfg4;              /* 0x00z798 */
+               uint64_t        ce_lsi_ltssm_cfg5;              /* 0x00z7A0 */
+               uint64_t        ce_lsi_ltssm_stat1;             /* 0x00z7A8 */
+               uint64_t        ce_lsi_ltssm_stat2;             /* 0x00z7B0 */
+               uint64_t        ce_lsi_ltssm_int_stat;          /* 0x00z7B8 */
+               uint64_t        ce_lsi_ltssm_int_stat_test;     /* 0x00z7C0 */
+               uint64_t        ce_lsi_ltssm_int_mask;          /* 0x00z7C8 */
+               uint64_t        ce_lsi_ltssm_stat_wr_en;        /* 0x00z7D0 */
+               uint64_t        ce_pad_00z7D8[5];   /* 0x00z7D8 -- 0x00z7F8 */
+               uint64_t        ce_lsi_gb_cfg1;                 /* 0x00z800 */
+               uint64_t        ce_lsi_gb_cfg2;                 /* 0x00z808 */
+               uint64_t        ce_lsi_gb_cfg3;                 /* 0x00z810 */
+               uint64_t        ce_lsi_gb_cfg4;                 /* 0x00z818 */
+               uint64_t        ce_lsi_gb_stat;                 /* 0x00z820 */
+               uint64_t        ce_lsi_gb_int_stat;             /* 0x00z828 */
+               uint64_t        ce_lsi_gb_int_stat_test;        /* 0x00z830 */
+               uint64_t        ce_lsi_gb_int_mask;             /* 0x00z838 */
+               uint64_t        ce_lsi_gb_pwr_dn1;              /* 0x00z840 */
+               uint64_t        ce_lsi_gb_pwr_dn2;              /* 0x00z848 */
+               uint64_t        ce_pad_00z850[246]; /* 0x00z850 -- 0x00zFF8 */
+       } ce_lsi[2];
+
+       uint64_t        ce_pad_004000[10];          /* 0x004000 -- 0x004048 */
+
+       /*
+        * CRM: Coretalk Receive Module Registers
+        */
+       uint64_t        ce_crm_debug_mux;                       /* 0x004050 */
+       uint64_t        ce_pad_004058;                          /* 0x004058 */
+       uint64_t        ce_crm_ssp_err_cmd_wrd;                 /* 0x004060 */
+       uint64_t        ce_crm_ssp_err_addr;                    /* 0x004068 */
+       uint64_t        ce_crm_ssp_err_syn;                     /* 0x004070 */
+
+       uint64_t        ce_pad_004078[499];         /* 0x004078 -- 0x005008 */
+
+       /*
+         * CXM: Coretalk Xmit Module Registers
+         */
+       uint64_t        ce_cxm_dyn_credit_status;               /* 0x005010 */
+       uint64_t        ce_cxm_last_credit_status;              /* 0x005018 */
+       uint64_t        ce_cxm_credit_limit;                    /* 0x005020 */
+       uint64_t        ce_cxm_force_credit;                    /* 0x005028 */
+       uint64_t        ce_cxm_disable_bypass;                  /* 0x005030 */
+       uint64_t        ce_pad_005038[3];           /* 0x005038 -- 0x005048 */
+       uint64_t        ce_cxm_debug_mux;                       /* 0x005050 */
+
+        uint64_t        ce_pad_005058[501];         /* 0x005058 -- 0x005FF8 */
+
+       /*
+        * DTL: Downstream Transaction Layer Regs (Link#1 and Link#2)
+        * DTL: Link#1 MMRs at start at 0x006000, Link#2 MMRs at 0x008000
+        * DTL: the comment offsets at far right: let 'y' = {6 or 8}
+        *
+        * UTL: Downstream Transaction Layer Regs (Link#1 and Link#2)
+        * UTL: Link#1 MMRs at start at 0x007000, Link#2 MMRs at 0x009000
+        * UTL: the comment offsets at far right: let 'z' = {7 or 9}
+        */
+       #define ce_dtl(link_num)        ce_dtl_utl[link_num-1]
+       #define ce_utl(link_num)        ce_dtl_utl[link_num-1]
+       struct ce_dtl_utl_reg {
+               /* DTL */
+               uint64_t        ce_dtl_dtdr_credit_limit;       /* 0x00y000 */
+               uint64_t        ce_dtl_dtdr_credit_force;       /* 0x00y008 */
+               uint64_t        ce_dtl_dyn_credit_status;       /* 0x00y010 */
+               uint64_t        ce_dtl_dtl_last_credit_stat;    /* 0x00y018 */
+               uint64_t        ce_dtl_dtl_ctrl;                /* 0x00y020 */
+               uint64_t        ce_pad_00y028[5];   /* 0x00y028 -- 0x00y048 */
+               uint64_t        ce_dtl_debug_sel;               /* 0x00y050 */
+               uint64_t        ce_pad_00y058[501]; /* 0x00y058 -- 0x00yFF8 */
+
+               /* UTL */
+               uint64_t        ce_utl_utl_ctrl;                /* 0x00z000 */
+               uint64_t        ce_utl_debug_sel;               /* 0x00z008 */
+               uint64_t        ce_pad_00z010[510]; /* 0x00z010 -- 0x00zFF8 */
+       } ce_dtl_utl[2];
+
+       uint64_t        ce_pad_00A000[514];         /* 0x00A000 -- 0x00B008 */
+
+       /*
+        * URE: Upstream Request Engine
+         */
+       uint64_t        ce_ure_dyn_credit_status;               /* 0x00B010 */
+       uint64_t        ce_ure_last_credit_status;              /* 0x00B018 */
+       uint64_t        ce_ure_credit_limit;                    /* 0x00B020 */
+       uint64_t        ce_pad_00B028;                          /* 0x00B028 */
+       uint64_t        ce_ure_control;                         /* 0x00B030 */
+       uint64_t        ce_ure_status;                          /* 0x00B038 */
+       uint64_t        ce_pad_00B040[2];           /* 0x00B040 -- 0x00B048 */
+       uint64_t        ce_ure_debug_sel;                       /* 0x00B050 */
+       uint64_t        ce_ure_pcie_debug_sel;                  /* 0x00B058 */
+       uint64_t        ce_ure_ssp_err_cmd_wrd;                 /* 0x00B060 */
+       uint64_t        ce_ure_ssp_err_addr;                    /* 0x00B068 */
+       uint64_t        ce_ure_page_map;                        /* 0x00B070 */
+       uint64_t        ce_ure_dir_map[TIOCE_NUM_PORTS];        /* 0x00B078 */
+       uint64_t        ce_ure_pipe_sel1;                       /* 0x00B088 */
+       uint64_t        ce_ure_pipe_mask1;                      /* 0x00B090 */
+       uint64_t        ce_ure_pipe_sel2;                       /* 0x00B098 */
+       uint64_t        ce_ure_pipe_mask2;                      /* 0x00B0A0 */
+       uint64_t        ce_ure_pcie1_credits_sent;              /* 0x00B0A8 */
+       uint64_t        ce_ure_pcie1_credits_used;              /* 0x00B0B0 */
+       uint64_t        ce_ure_pcie1_credit_limit;              /* 0x00B0B8 */
+       uint64_t        ce_ure_pcie2_credits_sent;              /* 0x00B0C0 */
+       uint64_t        ce_ure_pcie2_credits_used;              /* 0x00B0C8 */
+       uint64_t        ce_ure_pcie2_credit_limit;              /* 0x00B0D0 */
+       uint64_t        ce_ure_pcie_force_credit;               /* 0x00B0D8 */
+       uint64_t        ce_ure_rd_tnum_val;                     /* 0x00B0E0 */
+       uint64_t        ce_ure_rd_tnum_rsp_rcvd;                /* 0x00B0E8 */
+       uint64_t        ce_ure_rd_tnum_esent_timer;             /* 0x00B0F0 */
+       uint64_t        ce_ure_rd_tnum_error;                   /* 0x00B0F8 */
+       uint64_t        ce_ure_rd_tnum_first_cl;                /* 0x00B100 */
+       uint64_t        ce_ure_rd_tnum_link_buf;                /* 0x00B108 */
+       uint64_t        ce_ure_wr_tnum_val;                     /* 0x00B110 */
+       uint64_t        ce_ure_sram_err_addr0;                  /* 0x00B118 */
+       uint64_t        ce_ure_sram_err_addr1;                  /* 0x00B120 */
+       uint64_t        ce_ure_sram_err_addr2;                  /* 0x00B128 */
+       uint64_t        ce_ure_sram_rd_addr0;                   /* 0x00B130 */
+       uint64_t        ce_ure_sram_rd_addr1;                   /* 0x00B138 */
+       uint64_t        ce_ure_sram_rd_addr2;                   /* 0x00B140 */
+       uint64_t        ce_ure_sram_wr_addr0;                   /* 0x00B148 */
+       uint64_t        ce_ure_sram_wr_addr1;                   /* 0x00B150 */
+       uint64_t        ce_ure_sram_wr_addr2;                   /* 0x00B158 */
+       uint64_t        ce_ure_buf_flush10;                     /* 0x00B160 */
+       uint64_t        ce_ure_buf_flush11;                     /* 0x00B168 */
+       uint64_t        ce_ure_buf_flush12;                     /* 0x00B170 */
+       uint64_t        ce_ure_buf_flush13;                     /* 0x00B178 */
+       uint64_t        ce_ure_buf_flush20;                     /* 0x00B180 */
+       uint64_t        ce_ure_buf_flush21;                     /* 0x00B188 */
+       uint64_t        ce_ure_buf_flush22;                     /* 0x00B190 */
+       uint64_t        ce_ure_buf_flush23;                     /* 0x00B198 */
+       uint64_t        ce_ure_pcie_control1;                   /* 0x00B1A0 */
+       uint64_t        ce_ure_pcie_control2;                   /* 0x00B1A8 */
+
+       uint64_t        ce_pad_00B1B0[458];         /* 0x00B1B0 -- 0x00BFF8 */
+
+       /* Upstream Data Buffer, Port1 */
+       struct ce_ure_maint_ups_dat1_data {
+               uint64_t        data63_0[512];      /* 0x00C000 -- 0x00CFF8 */
+               uint64_t        data127_64[512];    /* 0x00D000 -- 0x00DFF8 */
+               uint64_t        parity[512];        /* 0x00E000 -- 0x00EFF8 */
+       } ce_ure_maint_ups_dat1;
+
+       /* Upstream Header Buffer, Port1 */
+       struct ce_ure_maint_ups_hdr1_data {
+               uint64_t        data63_0[512];      /* 0x00F000 -- 0x00FFF8 */
+               uint64_t        data127_64[512];    /* 0x010000 -- 0x010FF8 */
+               uint64_t        parity[512];        /* 0x011000 -- 0x011FF8 */
+       } ce_ure_maint_ups_hdr1;
+
+       /* Upstream Data Buffer, Port2 */
+       struct ce_ure_maint_ups_dat2_data {
+               uint64_t        data63_0[512];      /* 0x012000 -- 0x012FF8 */
+               uint64_t        data127_64[512];    /* 0x013000 -- 0x013FF8 */
+               uint64_t        parity[512];        /* 0x014000 -- 0x014FF8 */
+       } ce_ure_maint_ups_dat2;
+
+       /* Upstream Header Buffer, Port2 */
+       struct ce_ure_maint_ups_hdr2_data {
+               uint64_t        data63_0[512];      /* 0x015000 -- 0x015FF8 */
+               uint64_t        data127_64[512];    /* 0x016000 -- 0x016FF8 */
+               uint64_t        parity[512];        /* 0x017000 -- 0x017FF8 */
+       } ce_ure_maint_ups_hdr2;
+
+       /* Downstream Data Buffer */
+       struct ce_ure_maint_dns_dat_data {
+               uint64_t        data63_0[512];      /* 0x018000 -- 0x018FF8 */
+               uint64_t        data127_64[512];    /* 0x019000 -- 0x019FF8 */
+               uint64_t        parity[512];        /* 0x01A000 -- 0x01AFF8 */
+       } ce_ure_maint_dns_dat;
+
+       /* Downstream Header Buffer */
+       struct  ce_ure_maint_dns_hdr_data {
+               uint64_t        data31_0[64];       /* 0x01B000 -- 0x01B1F8 */
+               uint64_t        data95_32[64];      /* 0x01B200 -- 0x01B3F8 */
+               uint64_t        parity[64];         /* 0x01B400 -- 0x01B5F8 */
+       } ce_ure_maint_dns_hdr;
+
+       /* RCI Buffer Data */
+       struct  ce_ure_maint_rci_data {
+               uint64_t        data41_0[64];       /* 0x01B600 -- 0x01B7F8 */
+               uint64_t        data69_42[64];      /* 0x01B800 -- 0x01B9F8 */
+       } ce_ure_maint_rci;
+
+       /* Response Queue */
+       uint64_t        ce_ure_maint_rspq[64];      /* 0x01BA00 -- 0x01BBF8 */
+
+       uint64_t        ce_pad_01C000[4224];        /* 0x01BC00 -- 0x023FF8 */
+
+       /* Admin Build-a-Packet Buffer */
+       struct  ce_adm_maint_bap_buf_data {
+               uint64_t        data63_0[258];      /* 0x024000 -- 0x024808 */
+               uint64_t        data127_64[258];    /* 0x024810 -- 0x025018 */
+               uint64_t        parity[258];        /* 0x025020 -- 0x025828 */
+       } ce_adm_maint_bap_buf;
+
+       uint64_t        ce_pad_025830[5370];        /* 0x025830 -- 0x02FFF8 */
+
+       /* URE: 40bit PMU ATE Buffer */             /* 0x030000 -- 0x037FF8 */
+       uint64_t        ce_ure_ate40[TIOCE_NUM_M40_ATES];
+
+       /* URE: 32/40bit PMU ATE Buffer */          /* 0x038000 -- 0x03BFF8 */
+       uint64_t        ce_ure_ate3240[TIOCE_NUM_M3240_ATES];
+
+       uint64_t        ce_pad_03C000[2050];        /* 0x03C000 -- 0x040008 */
+
+       /*
+        * DRE: Down Stream Request Engine
+         */
+       uint64_t        ce_dre_dyn_credit_status1;              /* 0x040010 */
+       uint64_t        ce_dre_dyn_credit_status2;              /* 0x040018 */
+       uint64_t        ce_dre_last_credit_status1;             /* 0x040020 */
+       uint64_t        ce_dre_last_credit_status2;             /* 0x040028 */
+       uint64_t        ce_dre_credit_limit1;                   /* 0x040030 */
+       uint64_t        ce_dre_credit_limit2;                   /* 0x040038 */
+       uint64_t        ce_dre_force_credit1;                   /* 0x040040 */
+       uint64_t        ce_dre_force_credit2;                   /* 0x040048 */
+       uint64_t        ce_dre_debug_mux1;                      /* 0x040050 */
+       uint64_t        ce_dre_debug_mux2;                      /* 0x040058 */
+       uint64_t        ce_dre_ssp_err_cmd_wrd;                 /* 0x040060 */
+       uint64_t        ce_dre_ssp_err_addr;                    /* 0x040068 */
+       uint64_t        ce_dre_comp_err_cmd_wrd;                /* 0x040070 */
+       uint64_t        ce_dre_comp_err_addr;                   /* 0x040078 */
+       uint64_t        ce_dre_req_status;                      /* 0x040080 */
+       uint64_t        ce_dre_config1;                         /* 0x040088 */
+       uint64_t        ce_dre_config2;                         /* 0x040090 */
+       uint64_t        ce_dre_config_req_status;               /* 0x040098 */
+       uint64_t        ce_pad_0400A0[12];          /* 0x0400A0 -- 0x0400F8 */
+       uint64_t        ce_dre_dyn_fifo;                        /* 0x040100 */
+       uint64_t        ce_pad_040108[3];           /* 0x040108 -- 0x040118 */
+       uint64_t        ce_dre_last_fifo;                       /* 0x040120 */
+
+       uint64_t        ce_pad_040128[27];          /* 0x040128 -- 0x0401F8 */
+
+       /* DRE Downstream Head Queue */
+       struct  ce_dre_maint_ds_head_queue {
+               uint64_t        data63_0[32];       /* 0x040200 -- 0x0402F8 */
+               uint64_t        data127_64[32];     /* 0x040300 -- 0x0403F8 */
+               uint64_t        parity[32];         /* 0x040400 -- 0x0404F8 */
+       } ce_dre_maint_ds_head_q;
+
+       uint64_t        ce_pad_040500[352];         /* 0x040500 -- 0x040FF8 */
+
+       /* DRE Downstream Data Queue */
+       struct  ce_dre_maint_ds_data_queue {
+               uint64_t        data63_0[256];      /* 0x041000 -- 0x0417F8 */
+               uint64_t        ce_pad_041800[256]; /* 0x041800 -- 0x041FF8 */
+               uint64_t        data127_64[256];    /* 0x042000 -- 0x0427F8 */
+               uint64_t        ce_pad_042800[256]; /* 0x042800 -- 0x042FF8 */
+               uint64_t        parity[256];        /* 0x043000 -- 0x0437F8 */
+               uint64_t        ce_pad_043800[256]; /* 0x043800 -- 0x043FF8 */
+       } ce_dre_maint_ds_data_q;
+
+       /* DRE URE Upstream Response Queue */
+       struct  ce_dre_maint_ure_us_rsp_queue {
+               uint64_t        data63_0[8];        /* 0x044000 -- 0x044038 */
+               uint64_t        ce_pad_044040[24];  /* 0x044040 -- 0x0440F8 */
+               uint64_t        data127_64[8];      /* 0x044100 -- 0x044138 */
+               uint64_t        ce_pad_044140[24];  /* 0x044140 -- 0x0441F8 */
+               uint64_t        parity[8];          /* 0x044200 -- 0x044238 */
+               uint64_t        ce_pad_044240[24];  /* 0x044240 -- 0x0442F8 */
+       } ce_dre_maint_ure_us_rsp_q;
+
+       uint64_t        ce_dre_maint_us_wrt_rsp[32];/* 0x044300 -- 0x0443F8 */
+
+       uint64_t        ce_end_of_struct;                       /* 0x044400 */
+} tioce_t;
+
+
+/* ce_adm_int_mask/ce_adm_int_status register bit defines */
+#define CE_ADM_INT_CE_ERROR_SHFT               0
+#define CE_ADM_INT_LSI1_IP_ERROR_SHFT          1
+#define CE_ADM_INT_LSI2_IP_ERROR_SHFT          2
+#define CE_ADM_INT_PCIE_ERROR_SHFT             3
+#define CE_ADM_INT_PORT1_HOTPLUG_EVENT_SHFT    4
+#define CE_ADM_INT_PORT2_HOTPLUG_EVENT_SHFT    5
+#define CE_ADM_INT_PCIE_PORT1_DEV_A_SHFT       6
+#define CE_ADM_INT_PCIE_PORT1_DEV_B_SHFT       7
+#define CE_ADM_INT_PCIE_PORT1_DEV_C_SHFT       8
+#define CE_ADM_INT_PCIE_PORT1_DEV_D_SHFT       9
+#define CE_ADM_INT_PCIE_PORT2_DEV_A_SHFT       10
+#define CE_ADM_INT_PCIE_PORT2_DEV_B_SHFT       11
+#define CE_ADM_INT_PCIE_PORT2_DEV_C_SHFT       12
+#define CE_ADM_INT_PCIE_PORT2_DEV_D_SHFT       13
+#define CE_ADM_INT_PCIE_MSG_SHFT               14 /*see int_dest_14*/
+#define CE_ADM_INT_PCIE_MSG_SLOT_0_SHFT                14
+#define CE_ADM_INT_PCIE_MSG_SLOT_1_SHFT                15
+#define CE_ADM_INT_PCIE_MSG_SLOT_2_SHFT                16
+#define CE_ADM_INT_PCIE_MSG_SLOT_3_SHFT                17
+#define CE_ADM_INT_PORT1_PM_PME_MSG_SHFT       22
+#define CE_ADM_INT_PORT2_PM_PME_MSG_SHFT       23
+
+/* ce_adm_force_int register bit defines */
+#define CE_ADM_FORCE_INT_PCIE_PORT1_DEV_A_SHFT 0
+#define CE_ADM_FORCE_INT_PCIE_PORT1_DEV_B_SHFT 1
+#define CE_ADM_FORCE_INT_PCIE_PORT1_DEV_C_SHFT 2
+#define CE_ADM_FORCE_INT_PCIE_PORT1_DEV_D_SHFT 3
+#define CE_ADM_FORCE_INT_PCIE_PORT2_DEV_A_SHFT 4
+#define CE_ADM_FORCE_INT_PCIE_PORT2_DEV_B_SHFT 5
+#define CE_ADM_FORCE_INT_PCIE_PORT2_DEV_C_SHFT 6
+#define CE_ADM_FORCE_INT_PCIE_PORT2_DEV_D_SHFT 7
+#define CE_ADM_FORCE_INT_ALWAYS_SHFT           8
+
+/* ce_adm_int_dest register bit masks & shifts */
+#define INTR_VECTOR_SHFT                       56
+
+/* ce_adm_error_mask and ce_adm_error_summary register bit masks */
+#define CE_ADM_ERR_CRM_SSP_REQ_INVALID                 (0x1ULL <<  0)
+#define CE_ADM_ERR_SSP_REQ_HEADER                      (0x1ULL <<  1)
+#define CE_ADM_ERR_SSP_RSP_HEADER                      (0x1ULL <<  2)
+#define CE_ADM_ERR_SSP_PROTOCOL_ERROR                  (0x1ULL <<  3)
+#define CE_ADM_ERR_SSP_SBE                             (0x1ULL <<  4)
+#define CE_ADM_ERR_SSP_MBE                             (0x1ULL <<  5)
+#define CE_ADM_ERR_CXM_CREDIT_OFLOW                    (0x1ULL <<  6)
+#define CE_ADM_ERR_DRE_SSP_REQ_INVAL                   (0x1ULL <<  7)
+#define CE_ADM_ERR_SSP_REQ_LONG                                (0x1ULL <<  8)
+#define CE_ADM_ERR_SSP_REQ_OFLOW                       (0x1ULL <<  9)
+#define CE_ADM_ERR_SSP_REQ_SHORT                       (0x1ULL << 10)
+#define CE_ADM_ERR_SSP_REQ_SIDEBAND                    (0x1ULL << 11)
+#define CE_ADM_ERR_SSP_REQ_ADDR_ERR                    (0x1ULL << 12)
+#define CE_ADM_ERR_SSP_REQ_BAD_BE                      (0x1ULL << 13)
+#define CE_ADM_ERR_PCIE_COMPL_TIMEOUT                  (0x1ULL << 14)
+#define CE_ADM_ERR_PCIE_UNEXP_COMPL                    (0x1ULL << 15)
+#define CE_ADM_ERR_PCIE_ERR_COMPL                      (0x1ULL << 16)
+#define CE_ADM_ERR_DRE_CREDIT_OFLOW                    (0x1ULL << 17)
+#define CE_ADM_ERR_DRE_SRAM_PE                         (0x1ULL << 18)
+#define CE_ADM_ERR_SSP_RSP_INVALID                     (0x1ULL << 19)
+#define CE_ADM_ERR_SSP_RSP_LONG                                (0x1ULL << 20)
+#define CE_ADM_ERR_SSP_RSP_SHORT                       (0x1ULL << 21)
+#define CE_ADM_ERR_SSP_RSP_SIDEBAND                    (0x1ULL << 22)
+#define CE_ADM_ERR_URE_SSP_RSP_UNEXP                   (0x1ULL << 23)
+#define CE_ADM_ERR_URE_SSP_WR_REQ_TIMEOUT              (0x1ULL << 24)
+#define CE_ADM_ERR_URE_SSP_RD_REQ_TIMEOUT              (0x1ULL << 25)
+#define CE_ADM_ERR_URE_ATE3240_PAGE_FAULT              (0x1ULL << 26)
+#define CE_ADM_ERR_URE_ATE40_PAGE_FAULT                        (0x1ULL << 27)
+#define CE_ADM_ERR_URE_CREDIT_OFLOW                    (0x1ULL << 28)
+#define CE_ADM_ERR_URE_SRAM_PE                         (0x1ULL << 29)
+#define CE_ADM_ERR_ADM_SSP_RSP_UNEXP                   (0x1ULL << 30)
+#define CE_ADM_ERR_ADM_SSP_REQ_TIMEOUT                 (0x1ULL << 31)
+#define CE_ADM_ERR_MMR_ACCESS_ERROR                    (0x1ULL << 32)
+#define CE_ADM_ERR_MMR_ADDR_ERROR                      (0x1ULL << 33)
+#define CE_ADM_ERR_ADM_CREDIT_OFLOW                    (0x1ULL << 34)
+#define CE_ADM_ERR_ADM_SRAM_PE                         (0x1ULL << 35)
+#define CE_ADM_ERR_DTL1_MIN_PDATA_CREDIT_ERR           (0x1ULL << 36)
+#define CE_ADM_ERR_DTL1_INF_COMPL_CRED_UPDT_ERR                (0x1ULL << 37)
+#define CE_ADM_ERR_DTL1_INF_POSTED_CRED_UPDT_ERR       (0x1ULL << 38)
+#define CE_ADM_ERR_DTL1_INF_NPOSTED_CRED_UPDT_ERR      (0x1ULL << 39)
+#define CE_ADM_ERR_DTL1_COMP_HD_CRED_MAX_ERR           (0x1ULL << 40)
+#define CE_ADM_ERR_DTL1_COMP_D_CRED_MAX_ERR            (0x1ULL << 41)
+#define CE_ADM_ERR_DTL1_NPOSTED_HD_CRED_MAX_ERR                (0x1ULL << 42)
+#define CE_ADM_ERR_DTL1_NPOSTED_D_CRED_MAX_ERR         (0x1ULL << 43)
+#define CE_ADM_ERR_DTL1_POSTED_HD_CRED_MAX_ERR         (0x1ULL << 44)
+#define CE_ADM_ERR_DTL1_POSTED_D_CRED_MAX_ERR          (0x1ULL << 45)
+#define CE_ADM_ERR_DTL2_MIN_PDATA_CREDIT_ERR           (0x1ULL << 46)
+#define CE_ADM_ERR_DTL2_INF_COMPL_CRED_UPDT_ERR                (0x1ULL << 47)
+#define CE_ADM_ERR_DTL2_INF_POSTED_CRED_UPDT_ERR       (0x1ULL << 48)
+#define CE_ADM_ERR_DTL2_INF_NPOSTED_CRED_UPDT_ERR      (0x1ULL << 49)
+#define CE_ADM_ERR_DTL2_COMP_HD_CRED_MAX_ERR           (0x1ULL << 50)
+#define CE_ADM_ERR_DTL2_COMP_D_CRED_MAX_ERR            (0x1ULL << 51)
+#define CE_ADM_ERR_DTL2_NPOSTED_HD_CRED_MAX_ERR                (0x1ULL << 52)
+#define CE_ADM_ERR_DTL2_NPOSTED_D_CRED_MAX_ERR         (0x1ULL << 53)
+#define CE_ADM_ERR_DTL2_POSTED_HD_CRED_MAX_ERR         (0x1ULL << 54)
+#define CE_ADM_ERR_DTL2_POSTED_D_CRED_MAX_ERR          (0x1ULL << 55)
+#define CE_ADM_ERR_PORT1_PCIE_COR_ERR                  (0x1ULL << 56)
+#define CE_ADM_ERR_PORT1_PCIE_NFAT_ERR                 (0x1ULL << 57)
+#define CE_ADM_ERR_PORT1_PCIE_FAT_ERR                  (0x1ULL << 58)
+#define CE_ADM_ERR_PORT2_PCIE_COR_ERR                  (0x1ULL << 59)
+#define CE_ADM_ERR_PORT2_PCIE_NFAT_ERR                 (0x1ULL << 60)
+#define CE_ADM_ERR_PORT2_PCIE_FAT_ERR                  (0x1ULL << 61)
+
+/* ce_adm_ure_ups_buf_barrier_flush register bit masks and shifts */
+#define FLUSH_SEL_PORT1_PIPE0_SHFT     0
+#define FLUSH_SEL_PORT1_PIPE1_SHFT     4
+#define FLUSH_SEL_PORT1_PIPE2_SHFT     8
+#define FLUSH_SEL_PORT1_PIPE3_SHFT     12
+#define FLUSH_SEL_PORT2_PIPE0_SHFT     16
+#define FLUSH_SEL_PORT2_PIPE1_SHFT     20
+#define FLUSH_SEL_PORT2_PIPE2_SHFT     24
+#define FLUSH_SEL_PORT2_PIPE3_SHFT     28
+
+/* ce_dre_config1 register bit masks and shifts */
+#define CE_DRE_RO_ENABLE               (0x1ULL << 0)
+#define CE_DRE_DYN_RO_ENABLE           (0x1ULL << 1)
+#define CE_DRE_SUP_CONFIG_COMP_ERROR   (0x1ULL << 2)
+#define CE_DRE_SUP_IO_COMP_ERROR       (0x1ULL << 3)
+#define CE_DRE_ADDR_MODE_SHFT          4
+
+/* ce_dre_config_req_status register bit masks */
+#define CE_DRE_LAST_CONFIG_COMPLETION  (0x7ULL << 0)
+#define CE_DRE_DOWNSTREAM_CONFIG_ERROR (0x1ULL << 3)
+#define CE_DRE_CONFIG_COMPLETION_VALID (0x1ULL << 4)
+#define CE_DRE_CONFIG_REQUEST_ACTIVE   (0x1ULL << 5)
+
+/* ce_ure_control register bit masks & shifts */
+#define CE_URE_RD_MRG_ENABLE           (0x1ULL << 0)
+#define CE_URE_WRT_MRG_ENABLE1         (0x1ULL << 4)
+#define CE_URE_WRT_MRG_ENABLE2         (0x1ULL << 5)
+#define CE_URE_RSPQ_BYPASS_DISABLE     (0x1ULL << 24)
+#define CE_URE_UPS_DAT1_PAR_DISABLE    (0x1ULL << 32)
+#define CE_URE_UPS_HDR1_PAR_DISABLE    (0x1ULL << 33)
+#define CE_URE_UPS_DAT2_PAR_DISABLE    (0x1ULL << 34)
+#define CE_URE_UPS_HDR2_PAR_DISABLE    (0x1ULL << 35)
+#define CE_URE_ATE_PAR_DISABLE         (0x1ULL << 36)
+#define CE_URE_RCI_PAR_DISABLE         (0x1ULL << 37)
+#define CE_URE_RSPQ_PAR_DISABLE                (0x1ULL << 38)
+#define CE_URE_DNS_DAT_PAR_DISABLE     (0x1ULL << 39)
+#define CE_URE_DNS_HDR_PAR_DISABLE     (0x1ULL << 40)
+#define CE_URE_MALFORM_DISABLE         (0x1ULL << 44)
+#define CE_URE_UNSUP_DISABLE           (0x1ULL << 45)
+
+/* ce_ure_page_map register bit masks & shifts */
+#define CE_URE_ATE3240_ENABLE          (0x1ULL << 0)
+#define CE_URE_ATE40_ENABLE            (0x1ULL << 1)
+#define CE_URE_PAGESIZE_SHFT           4
+#define CE_URE_PAGESIZE_MASK           (0x7ULL << CE_URE_PAGESIZE_SHFT)
+#define CE_URE_4K_PAGESIZE             (0x0ULL << CE_URE_PAGESIZE_SHFT)
+#define CE_URE_16K_PAGESIZE            (0x1ULL << CE_URE_PAGESIZE_SHFT)
+#define CE_URE_64K_PAGESIZE            (0x2ULL << CE_URE_PAGESIZE_SHFT)
+#define CE_URE_128K_PAGESIZE           (0x3ULL << CE_URE_PAGESIZE_SHFT)
+#define CE_URE_256K_PAGESIZE           (0x4ULL << CE_URE_PAGESIZE_SHFT)
+
+/* ce_ure_pipe_sel register bit masks & shifts */
+#define PKT_TRAFIC_SHRT                        16
+#define BUS_SRC_ID_SHFT                        8
+#define DEV_SRC_ID_SHFT                        3
+#define FNC_SRC_ID_SHFT                        0
+#define CE_URE_TC_MASK                 (0x07ULL << PKT_TRAFIC_SHRT)
+#define CE_URE_BUS_MASK                        (0xFFULL << BUS_SRC_ID_SHFT)
+#define CE_URE_DEV_MASK                        (0x1FULL << DEV_SRC_ID_SHFT)
+#define CE_URE_FNC_MASK                        (0x07ULL << FNC_SRC_ID_SHFT)
+#define CE_URE_PIPE_BUS(b)             (((uint64_t)(b) << BUS_SRC_ID_SHFT) & \
+                                        CE_URE_BUS_MASK)
+#define CE_URE_PIPE_DEV(d)             (((uint64_t)(d) << DEV_SRC_ID_SHFT) & \
+                                        CE_URE_DEV_MASK)
+#define CE_URE_PIPE_FNC(f)             (((uint64_t)(f) << FNC_SRC_ID_SHFT) & \
+                                        CE_URE_FNC_MASK)
+
+#define CE_URE_SEL1_SHFT               0
+#define CE_URE_SEL2_SHFT               20
+#define CE_URE_SEL3_SHFT               40
+#define CE_URE_SEL1_MASK               (0x7FFFFULL << CE_URE_SEL1_SHFT)
+#define CE_URE_SEL2_MASK               (0x7FFFFULL << CE_URE_SEL2_SHFT)
+#define CE_URE_SEL3_MASK               (0x7FFFFULL << CE_URE_SEL3_SHFT)
+
+
+/* ce_ure_pipe_mask register bit masks & shifts */
+#define CE_URE_MASK1_SHFT              0
+#define CE_URE_MASK2_SHFT              20
+#define CE_URE_MASK3_SHFT              40
+#define CE_URE_MASK1_MASK              (0x7FFFFULL << CE_URE_MASK1_SHFT)
+#define CE_URE_MASK2_MASK              (0x7FFFFULL << CE_URE_MASK2_SHFT)
+#define CE_URE_MASK3_MASK              (0x7FFFFULL << CE_URE_MASK3_SHFT)
+
+
+/* ce_ure_pcie_control1 register bit masks & shifts */
+#define CE_URE_SI                      (0x1ULL << 0)
+#define CE_URE_ELAL_SHFT               4
+#define CE_URE_ELAL_MASK               (0x7ULL << CE_URE_ELAL_SHFT)
+#define CE_URE_ELAL1_SHFT              8
+#define CE_URE_ELAL1_MASK              (0x7ULL << CE_URE_ELAL1_SHFT)
+#define CE_URE_SCC                     (0x1ULL << 12)
+#define CE_URE_PN1_SHFT                        16
+#define CE_URE_PN1_MASK                        (0xFFULL << CE_URE_PN1_SHFT)
+#define CE_URE_PN2_SHFT                        24
+#define CE_URE_PN2_MASK                        (0xFFULL << CE_URE_PN2_SHFT)
+#define CE_URE_PN1_SET(n)              (((uint64_t)(n) << CE_URE_PN1_SHFT) & \
+                                        CE_URE_PN1_MASK)
+#define CE_URE_PN2_SET(n)              (((uint64_t)(n) << CE_URE_PN2_SHFT) & \
+                                        CE_URE_PN2_MASK)
+
+/* ce_ure_pcie_control2 register bit masks & shifts */
+#define CE_URE_ABP                     (0x1ULL << 0)
+#define CE_URE_PCP                     (0x1ULL << 1)
+#define CE_URE_MSP                     (0x1ULL << 2)
+#define CE_URE_AIP                     (0x1ULL << 3)
+#define CE_URE_PIP                     (0x1ULL << 4)
+#define CE_URE_HPS                     (0x1ULL << 5)
+#define CE_URE_HPC                     (0x1ULL << 6)
+#define CE_URE_SPLV_SHFT               7
+#define CE_URE_SPLV_MASK               (0xFFULL << CE_URE_SPLV_SHFT)
+#define CE_URE_SPLS_SHFT               15
+#define CE_URE_SPLS_MASK               (0x3ULL << CE_URE_SPLS_SHFT)
+#define CE_URE_PSN1_SHFT               19
+#define CE_URE_PSN1_MASK               (0x1FFFULL << CE_URE_PSN1_SHFT)
+#define CE_URE_PSN2_SHFT               32
+#define CE_URE_PSN2_MASK               (0x1FFFULL << CE_URE_PSN2_SHFT)
+#define CE_URE_PSN1_SET(n)             (((uint64_t)(n) << CE_URE_PSN1_SHFT) & \
+                                        CE_URE_PSN1_MASK)
+#define CE_URE_PSN2_SET(n)             (((uint64_t)(n) << CE_URE_PSN2_SHFT) & \
+                                        CE_URE_PSN2_MASK)
+
+/*
+ * PIO address space ranges for CE
+ */
+
+/* Local CE Registers Space */
+#define CE_PIO_MMR                     0x00000000
+#define CE_PIO_MMR_LEN                 0x04000000
+
+/* PCI Compatible Config Space */
+#define CE_PIO_CONFIG_SPACE            0x04000000
+#define CE_PIO_CONFIG_SPACE_LEN                0x04000000
+
+/* PCI I/O Space Alias */
+#define CE_PIO_IO_SPACE_ALIAS          0x08000000
+#define CE_PIO_IO_SPACE_ALIAS_LEN      0x08000000
+
+/* PCI Enhanced Config Space */
+#define CE_PIO_E_CONFIG_SPACE          0x10000000
+#define CE_PIO_E_CONFIG_SPACE_LEN      0x10000000
+
+/* PCI I/O Space */
+#define CE_PIO_IO_SPACE                        0x100000000
+#define CE_PIO_IO_SPACE_LEN            0x100000000
+
+/* PCI MEM Space */
+#define CE_PIO_MEM_SPACE               0x200000000
+#define CE_PIO_MEM_SPACE_LEN           TIO_HWIN_SIZE
+
+
+/*
+ * CE PCI Enhanced Config Space shifts & masks
+ */
+#define CE_E_CONFIG_BUS_SHFT           20
+#define CE_E_CONFIG_BUS_MASK           (0xFF << CE_E_CONFIG_BUS_SHFT)
+#define CE_E_CONFIG_DEVICE_SHFT                15
+#define CE_E_CONFIG_DEVICE_MASK                (0x1F << CE_E_CONFIG_DEVICE_SHFT)
+#define CE_E_CONFIG_FUNC_SHFT          12
+#define CE_E_CONFIG_FUNC_MASK          (0x7  << CE_E_CONFIG_FUNC_SHFT)
+
+#endif /* __ASM_IA64_SN_TIOCE_H__ */
diff --git a/include/asm-ia64/sn/tioce_provider.h b/include/asm-ia64/sn/tioce_provider.h
new file mode 100644 (file)
index 0000000..7f63dec
--- /dev/null
@@ -0,0 +1,66 @@
+/**************************************************************************
+ *             Copyright (C) 2005, Silicon Graphics, Inc.                 *
+ *                                                                       *
+ *  These coded instructions, statements, and computer programs         contain  *
+ *  unpublished         proprietary  information of Silicon Graphics, Inc., and  *
+ *  are protected by Federal copyright law.  They  may not be disclosed  *
+ *  to third  parties  or copied or duplicated in any form, in whole or  *
+ *  in part, without the prior written consent of Silicon Graphics, Inc.  *
+ *                                                                       *
+ **************************************************************************/
+
+#ifndef _ASM_IA64_SN_CE_PROVIDER_H
+#define _ASM_IA64_SN_CE_PROVIDER_H
+
+#include <asm/sn/pcibus_provider_defs.h>
+#include <asm/sn/tioce.h>
+
+/*
+ * Common TIOCE structure shared between the prom and kernel
+ *
+ * DO NOT CHANGE THIS STRUCT WITHOUT MAKING CORRESPONDING CHANGES TO THE
+ * PROM VERSION.
+ */
+struct tioce_common {
+       struct pcibus_bussoft   ce_pcibus;      /* common pciio header */
+
+       uint32_t                ce_rev;
+       uint64_t                ce_kernel_private;
+       uint64_t                ce_prom_private;
+};
+
+struct tioce_kernel {
+       struct tioce_common     *ce_common;
+       spinlock_t              ce_lock;
+       struct list_head        ce_dmamap_list;
+
+       uint64_t                ce_ate40_shadow[TIOCE_NUM_M40_ATES];
+       uint64_t                ce_ate3240_shadow[TIOCE_NUM_M3240_ATES];
+       uint32_t                ce_ate3240_pagesize;
+
+       uint8_t                 ce_port1_secondary;
+
+       /* per-port resources */
+       struct {
+               int             dirmap_refcnt;
+               uint64_t        dirmap_shadow;
+       } ce_port[TIOCE_NUM_PORTS];
+};
+
+struct tioce_dmamap {
+       struct list_head        ce_dmamap_list; /* headed by tioce_kernel */
+       uint32_t                refcnt;
+
+       uint64_t                nbytes;         /* # bytes mapped */
+
+       uint64_t                ct_start;       /* coretalk start address */
+       uint64_t                pci_start;      /* bus start address */
+
+       uint64_t                *ate_hw;        /* hw ptr of first ate in map */
+       uint64_t                *ate_shadow;    /* shadow ptr of firat ate */
+       uint16_t                ate_count;      /* # ate's in the map */
+};
+
+extern int tioce_init_provider(void);
+
+#endif  /* __ASM_IA64_SN_CE_PROVIDER_H */
index 909936f25512e8ebbc3967d0eb775a12c989dd03..d2430aa0d49db76db4b6c771bc1eb9a9bc402d36 100644 (file)
@@ -93,7 +93,15 @@ _raw_spin_lock_flags (spinlock_t *lock, unsigned long flags)
 # endif /* CONFIG_MCKINLEY */
 #endif
 }
+
 #define _raw_spin_lock(lock) _raw_spin_lock_flags(lock, 0)
+
+/* Unlock by doing an ordered store and releasing the cacheline with nta */
+static inline void _raw_spin_unlock(spinlock_t *x) {
+       barrier();
+       asm volatile ("st4.rel.nta [%0] = r0\n\t" :: "r"(x));
+}
+
 #else /* !ASM_SUPPORTED */
 #define _raw_spin_lock_flags(lock, flags) _raw_spin_lock(lock)
 # define _raw_spin_lock(x)                                                             \
@@ -109,16 +117,16 @@ do {                                                                                      \
                } while (ia64_spinlock_val);                                            \
        }                                                                               \
 } while (0)
+#define _raw_spin_unlock(x)    do { barrier(); ((spinlock_t *) x)->lock = 0; } while (0)
 #endif /* !ASM_SUPPORTED */
 
 #define spin_is_locked(x)      ((x)->lock != 0)
-#define _raw_spin_unlock(x)    do { barrier(); ((spinlock_t *) x)->lock = 0; } while (0)
 #define _raw_spin_trylock(x)   (cmpxchg_acq(&(x)->lock, 0, 1) == 0)
 #define spin_unlock_wait(x)    do { barrier(); } while ((x)->lock)
 
 typedef struct {
-       volatile unsigned int read_counter      : 31;
-       volatile unsigned int write_lock        :  1;
+       volatile unsigned int read_counter      : 24;
+       volatile unsigned int write_lock        :  8;
 #ifdef CONFIG_PREEMPT
        unsigned int break_lock;
 #endif
@@ -174,6 +182,13 @@ do {                                                                               \
        (result == 0);                                                          \
 })
 
+static inline void _raw_write_unlock(rwlock_t *x)
+{
+       u8 *y = (u8 *)x;
+       barrier();
+       asm volatile ("st1.rel.nta [%0] = r0\n\t" :: "r"(y+3) : "memory" );
+}
+
 #else /* !ASM_SUPPORTED */
 
 #define _raw_write_lock(l)                                                             \
@@ -195,14 +210,14 @@ do {                                                                              \
        (ia64_val == 0);                                                \
 })
 
+static inline void _raw_write_unlock(rwlock_t *x)
+{
+       barrier();
+       x->write_lock = 0;
+}
+
 #endif /* !ASM_SUPPORTED */
 
 #define _raw_read_trylock(lock) generic_raw_read_trylock(lock)
 
-#define _raw_write_unlock(x)                                                           \
-({                                                                                     \
-       smp_mb__before_clear_bit();     /* need barrier before releasing lock... */     \
-       clear_bit(31, (x));                                                             \
-})
-
 #endif /*  _ASM_IA64_SPINLOCK_H */
index cd2cf76b2db1d77cb22a69f928f96c4a74d1c59c..33256db4a7cf1b0ad4f82cdcf935b0bd421a42ed 100644 (file)
 #include <asm/pal.h>
 #include <asm/percpu.h>
 
-#define GATE_ADDR              __IA64_UL_CONST(0xa000000000000000)
+#define GATE_ADDR              RGN_BASE(RGN_GATE)
+
 /*
  * 0xa000000000000000+2*PERCPU_PAGE_SIZE
  * - 0xa000000000000000+3*PERCPU_PAGE_SIZE remain unmapped (guard page)
  */
-#define KERNEL_START            __IA64_UL_CONST(0xa000000100000000)
+#define KERNEL_START            (GATE_ADDR+0x100000000)
 #define PERCPU_ADDR            (-PERCPU_PAGE_SIZE)
 
 #ifndef __ASSEMBLY__
index ce8518e658b6a0c2219071ed0fbfb0e462fb9115..4522c7186bf378de1c4162b042ad8a3b78fc09ac 100644 (file)
@@ -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
index db09580ad14b8031ee17cce1e38272453817b877..dc36b1be6745ac7c8b2e8e28810b9b99bc49de66 100644 (file)
  */
 #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
 #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 */
diff --git a/include/net/ieee80211_crypt.h b/include/net/ieee80211_crypt.h
new file mode 100644 (file)
index 0000000..b58a3bc
--- /dev/null
@@ -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
index c07aafb59a0fb5a829e68b14bf16c597137d15e3..2bdd5623fdd50889327bd4889e137f82bea6279c 100644 (file)
@@ -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
index 7e6eff206c813f36d2df224f23d594637f40758b..4aa2f46d2a561f5ccc514a6f19312487cabba807 100644 (file)
@@ -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
diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig
new file mode 100644 (file)
index 0000000..58ed431
--- /dev/null
@@ -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".
+
diff --git a/net/ieee80211/Makefile b/net/ieee80211/Makefile
new file mode 100644 (file)
index 0000000..a6ccac5
--- /dev/null
@@ -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
+
diff --git a/net/ieee80211/ieee80211_crypt.c b/net/ieee80211/ieee80211_crypt.c
new file mode 100644 (file)
index 0000000..05a6f2f
--- /dev/null
@@ -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);
diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c
new file mode 100644 (file)
index 0000000..11d1557
--- /dev/null
@@ -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);
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
new file mode 100644 (file)
index 0000000..f91d92c
--- /dev/null
@@ -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);
diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c
new file mode 100644 (file)
index 0000000..bec1d34
--- /dev/null
@@ -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);
diff --git a/net/ieee80211/ieee80211_module.c b/net/ieee80211/ieee80211_module.c
new file mode 100644 (file)
index 0000000..553acb2
--- /dev/null
@@ -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);
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
new file mode 100644 (file)
index 0000000..d582faa
--- /dev/null
@@ -0,0 +1,1189 @@
+/*
+ * Original code based Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3 - hostap.o module, common routines
+ *
+ * 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>
+ * 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.
+ */
+
+#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 <linux/ctype.h>
+
+#include <net/ieee80211.h>
+
+static inline void ieee80211_monitor_rx(struct ieee80211_device *ieee,
+                                       struct sk_buff *skb,
+                                       struct ieee80211_rx_stats *rx_stats)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+       skb->dev = ieee->dev;
+       skb->mac.raw = skb->data;
+       skb_pull(skb, ieee80211_get_hdrlen(fc));
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = __constant_htons(ETH_P_80211_RAW);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct ieee80211_frag_entry *
+ieee80211_frag_cache_find(struct ieee80211_device *ieee, unsigned int seq,
+                         unsigned int frag, u8 *src, u8 *dst)
+{
+       struct ieee80211_frag_entry *entry;
+       int i;
+
+       for (i = 0; i < IEEE80211_FRAG_CACHE_LEN; i++) {
+               entry = &ieee->frag_cache[i];
+               if (entry->skb != NULL &&
+                   time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+                       IEEE80211_DEBUG_FRAG(
+                               "expiring fragment cache entry "
+                               "seq=%u last_frag=%u\n",
+                               entry->seq, entry->last_frag);
+                       dev_kfree_skb_any(entry->skb);
+                       entry->skb = NULL;
+               }
+
+               if (entry->skb != NULL && entry->seq == seq &&
+                   (entry->last_frag + 1 == frag || frag == -1) &&
+                   memcmp(entry->src_addr, src, ETH_ALEN) == 0 &&
+                   memcmp(entry->dst_addr, dst, ETH_ALEN) == 0)
+                       return entry;
+       }
+
+       return NULL;
+}
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *
+ieee80211_frag_cache_get(struct ieee80211_device *ieee,
+                        struct ieee80211_hdr *hdr)
+{
+       struct sk_buff *skb = NULL;
+       u16 sc;
+       unsigned int frag, seq;
+       struct ieee80211_frag_entry *entry;
+
+       sc = le16_to_cpu(hdr->seq_ctl);
+       frag = WLAN_GET_SEQ_FRAG(sc);
+       seq = WLAN_GET_SEQ_SEQ(sc);
+
+       if (frag == 0) {
+               /* Reserve enough space to fit maximum frame length */
+               skb = dev_alloc_skb(ieee->dev->mtu +
+                                   sizeof(struct ieee80211_hdr) +
+                                   8 /* LLC */ +
+                                   2 /* alignment */ +
+                                   8 /* WEP */ + ETH_ALEN /* WDS */);
+               if (skb == NULL)
+                       return NULL;
+
+               entry = &ieee->frag_cache[ieee->frag_next_idx];
+               ieee->frag_next_idx++;
+               if (ieee->frag_next_idx >= IEEE80211_FRAG_CACHE_LEN)
+                       ieee->frag_next_idx = 0;
+
+               if (entry->skb != NULL)
+                       dev_kfree_skb_any(entry->skb);
+
+               entry->first_frag_time = jiffies;
+               entry->seq = seq;
+               entry->last_frag = frag;
+               entry->skb = skb;
+               memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+               memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+       } else {
+               /* received a fragment of a frame for which the head fragment
+                * should have already been received */
+               entry = ieee80211_frag_cache_find(ieee, seq, frag, hdr->addr2,
+                                                 hdr->addr1);
+               if (entry != NULL) {
+                       entry->last_frag = frag;
+                       skb = entry->skb;
+               }
+       }
+
+       return skb;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee,
+                                          struct ieee80211_hdr *hdr)
+{
+       u16 sc;
+       unsigned int seq;
+       struct ieee80211_frag_entry *entry;
+
+       sc = le16_to_cpu(hdr->seq_ctl);
+       seq = WLAN_GET_SEQ_SEQ(sc);
+
+       entry = ieee80211_frag_cache_find(ieee, seq, -1, hdr->addr2,
+                                         hdr->addr1);
+
+       if (entry == NULL) {
+               IEEE80211_DEBUG_FRAG(
+                       "could not invalidate fragment cache "
+                       "entry (seq=%u)\n", seq);
+               return -1;
+       }
+
+       entry->skb = NULL;
+       return 0;
+}
+
+
+#ifdef NOT_YET
+/* ieee80211_rx_frame_mgtmt
+ *
+ * Responsible for handling management control frames
+ *
+ * Called by ieee80211_rx */
+static inline int
+ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb,
+                       struct ieee80211_rx_stats *rx_stats, u16 type,
+                       u16 stype)
+{
+       if (ieee->iw_mode == IW_MODE_MASTER) {
+               printk(KERN_DEBUG "%s: Master mode not yet suppported.\n",
+                      ieee->dev->name);
+               return 0;
+/*
+  hostap_update_sta_ps(ieee, (struct hostap_ieee80211_hdr *)
+  skb->data);*/
+       }
+
+       if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) {
+               if (stype == WLAN_FC_STYPE_BEACON &&
+                   ieee->iw_mode == IW_MODE_MASTER) {
+                       struct sk_buff *skb2;
+                       /* Process beacon frames also in kernel driver to
+                        * update STA(AP) table statistics */
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb2)
+                               hostap_rx(skb2->dev, skb2, rx_stats);
+               }
+
+               /* send management frames to the user space daemon for
+                * processing */
+               ieee->apdevstats.rx_packets++;
+               ieee->apdevstats.rx_bytes += skb->len;
+               prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT);
+               return 0;
+       }
+
+           if (ieee->iw_mode == IW_MODE_MASTER) {
+               if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) {
+                       printk(KERN_DEBUG "%s: unknown management frame "
+                              "(type=0x%02x, stype=0x%02x) dropped\n",
+                              skb->dev->name, type, stype);
+                       return -1;
+               }
+
+               hostap_rx(skb->dev, skb, rx_stats);
+               return 0;
+       }
+
+       printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame "
+              "received in non-Host AP mode\n", skb->dev->name);
+       return -1;
+}
+#endif
+
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+/* Called by ieee80211_rx_frame_decrypt */
+static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee,
+                                   struct sk_buff *skb)
+{
+       struct net_device *dev = ieee->dev;
+       u16 fc, ethertype;
+       struct ieee80211_hdr *hdr;
+       u8 *pos;
+
+       if (skb->len < 24)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       /* check that the frame is unicast frame to us */
+       if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+           IEEE80211_FCTL_TODS &&
+           memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 &&
+           memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+               /* ToDS frame with own addr BSSID and DA */
+       } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+                  IEEE80211_FCTL_FROMDS &&
+                  memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+               /* FromDS frame with own addr as DA */
+       } else
+               return 0;
+
+       if (skb->len < 24 + 8)
+               return 0;
+
+       /* check for port access entity Ethernet type */
+       pos = skb->data + 24;
+       ethertype = (pos[6] << 8) | pos[7];
+       if (ethertype == ETH_P_PAE)
+               return 1;
+
+       return 0;
+}
+
+/* Called only as a tasklet (software IRQ), by ieee80211_rx */
+static inline int
+ieee80211_rx_frame_decrypt(struct ieee80211_device* ieee, struct sk_buff *skb,
+                          struct ieee80211_crypt_data *crypt)
+{
+       struct ieee80211_hdr *hdr;
+       int res, hdrlen;
+
+       if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+#ifdef CONFIG_IEEE80211_CRYPT_TKIP
+       if (ieee->tkip_countermeasures &&
+           strcmp(crypt->ops->name, "TKIP") == 0) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+                              "received packet from " MAC_FMT "\n",
+                              ieee->dev->name, MAC_ARG(hdr->addr2));
+               }
+               return -1;
+       }
+#endif
+
+       atomic_inc(&crypt->refcnt);
+       res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
+       atomic_dec(&crypt->refcnt);
+       if (res < 0) {
+               IEEE80211_DEBUG_DROP(
+                       "decryption failed (SA=" MAC_FMT
+                       ") res=%d\n", MAC_ARG(hdr->addr2), res);
+               if (res == -2)
+                       IEEE80211_DEBUG_DROP("Decryption failed ICV "
+                                            "mismatch (key %d)\n",
+                                            skb->data[hdrlen + 3] >> 6);
+               ieee->ieee_stats.rx_discards_undecryptable++;
+               return -1;
+       }
+
+       return res;
+}
+
+
+/* Called only as a tasklet (software IRQ), by ieee80211_rx */
+static inline int
+ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device* ieee, struct sk_buff *skb,
+                            int keyidx, struct ieee80211_crypt_data *crypt)
+{
+       struct ieee80211_hdr *hdr;
+       int res, hdrlen;
+
+       if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+       atomic_inc(&crypt->refcnt);
+       res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
+       atomic_dec(&crypt->refcnt);
+       if (res < 0) {
+               printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
+                      " (SA=" MAC_FMT " keyidx=%d)\n",
+                      ieee->dev->name, MAC_ARG(hdr->addr2), keyidx);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/* All received frames are sent to this function. @skb contains the frame in
+ * IEEE 802.11 format, i.e., in the format it was sent over air.
+ * This function is called only as a tasklet (software IRQ). */
+int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
+                struct ieee80211_rx_stats *rx_stats)
+{
+       struct net_device *dev = ieee->dev;
+       struct ieee80211_hdr *hdr;
+       size_t hdrlen;
+       u16 fc, type, stype, sc;
+       struct net_device_stats *stats;
+       unsigned int frag;
+       u8 *payload;
+       u16 ethertype;
+#ifdef NOT_YET
+       struct net_device *wds = NULL;
+       struct sk_buff *skb2 = NULL;
+       struct net_device *wds = NULL;
+       int frame_authorized = 0;
+       int from_assoc_ap = 0;
+       void *sta = NULL;
+#endif
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       struct ieee80211_crypt_data *crypt = NULL;
+       int keyidx = 0;
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       stats = &ieee->stats;
+
+       if (skb->len < 10) {
+               printk(KERN_INFO "%s: SKB length < 10\n",
+                      dev->name);
+               goto rx_dropped;
+       }
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+       sc = le16_to_cpu(hdr->seq_ctl);
+       frag = WLAN_GET_SEQ_FRAG(sc);
+       hdrlen = ieee80211_get_hdrlen(fc);
+
+#ifdef NOT_YET
+#if WIRELESS_EXT > 15
+       /* Put this code here so that we avoid duplicating it in all
+        * Rx paths. - Jean II */
+#ifdef IW_WIRELESS_SPY         /* defined in iw_handler.h */
+       /* If spy monitoring on */
+       if (iface->spy_data.spy_number > 0) {
+               struct iw_quality wstats;
+               wstats.level = rx_stats->signal;
+               wstats.noise = rx_stats->noise;
+               wstats.updated = 6;     /* No qual value */
+               /* Update spy records */
+               wireless_spy_update(dev, hdr->addr2, &wstats);
+       }
+#endif /* IW_WIRELESS_SPY */
+#endif /* WIRELESS_EXT > 15 */
+       hostap_update_rx_stats(local->ap, hdr, rx_stats);
+#endif
+
+#if WIRELESS_EXT > 15
+       if (ieee->iw_mode == IW_MODE_MONITOR) {
+               ieee80211_monitor_rx(ieee, skb, rx_stats);
+               stats->rx_packets++;
+               stats->rx_bytes += skb->len;
+               return 1;
+       }
+#endif
+
+       if (ieee->host_decrypt) {
+               int idx = 0;
+               if (skb->len >= hdrlen + 3)
+                       idx = skb->data[hdrlen + 3] >> 6;
+               crypt = ieee->crypt[idx];
+#ifdef NOT_YET
+               sta = NULL;
+
+               /* Use station specific key to override default keys if the
+                * receiver address is a unicast address ("individual RA"). If
+                * bcrx_sta_key parameter is set, station specific key is used
+                * even with broad/multicast targets (this is against IEEE
+                * 802.11, but makes it easier to use different keys with
+                * stations that do not support WEP key mapping). */
+
+               if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
+                       (void) hostap_handle_sta_crypto(local, hdr, &crypt,
+                                                       &sta);
+#endif
+
+               /* allow NULL decrypt to indicate an station specific override
+                * for default encryption */
+               if (crypt && (crypt->ops == NULL ||
+                             crypt->ops->decrypt_mpdu == NULL))
+                       crypt = NULL;
+
+               if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) {
+                       /* This seems to be triggered by some (multicast?)
+                        * frames from other than current BSS, so just drop the
+                        * frames silently instead of filling system log with
+                        * these reports. */
+                       IEEE80211_DEBUG_DROP("Decryption failed (not set)"
+                                            " (SA=" MAC_FMT ")\n",
+                                            MAC_ARG(hdr->addr2));
+                       ieee->ieee_stats.rx_discards_undecryptable++;
+                       goto rx_dropped;
+               }
+       }
+
+#ifdef NOT_YET
+       if (type != WLAN_FC_TYPE_DATA) {
+               if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH &&
+                   fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt &&
+                   (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0)
+               {
+                       printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
+                              "from " MAC_FMT "\n", dev->name,
+                              MAC_ARG(hdr->addr2));
+                       /* TODO: could inform hostapd about this so that it
+                        * could send auth failure report */
+                       goto rx_dropped;
+               }
+
+               if (ieee80211_rx_frame_mgmt(ieee, skb, rx_stats, type, stype))
+                       goto rx_dropped;
+               else
+                       goto rx_exit;
+       }
+#endif
+
+       /* Data frame - extract src/dst addresses */
+       if (skb->len < IEEE80211_3ADDR_LEN)
+               goto rx_dropped;
+
+       switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+       case IEEE80211_FCTL_FROMDS:
+               memcpy(dst, hdr->addr1, ETH_ALEN);
+               memcpy(src, hdr->addr3, ETH_ALEN);
+               break;
+       case IEEE80211_FCTL_TODS:
+               memcpy(dst, hdr->addr3, ETH_ALEN);
+               memcpy(src, hdr->addr2, ETH_ALEN);
+               break;
+       case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+               if (skb->len < IEEE80211_4ADDR_LEN)
+                       goto rx_dropped;
+               memcpy(dst, hdr->addr3, ETH_ALEN);
+               memcpy(src, hdr->addr4, ETH_ALEN);
+               break;
+       case 0:
+               memcpy(dst, hdr->addr1, ETH_ALEN);
+               memcpy(src, hdr->addr2, ETH_ALEN);
+               break;
+       }
+
+#ifdef NOT_YET
+       if (hostap_rx_frame_wds(ieee, hdr, fc, &wds))
+               goto rx_dropped;
+       if (wds) {
+               skb->dev = dev = wds;
+               stats = hostap_get_stats(dev);
+       }
+
+       if (ieee->iw_mode == IW_MODE_MASTER && !wds &&
+           (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS &&
+           ieee->stadev &&
+           memcmp(hdr->addr2, ieee->assoc_ap_addr, ETH_ALEN) == 0) {
+               /* Frame from BSSID of the AP for which we are a client */
+               skb->dev = dev = ieee->stadev;
+               stats = hostap_get_stats(dev);
+               from_assoc_ap = 1;
+       }
+#endif
+
+       dev->last_rx = jiffies;
+
+#ifdef NOT_YET
+       if ((ieee->iw_mode == IW_MODE_MASTER ||
+            ieee->iw_mode == IW_MODE_REPEAT) &&
+           !from_assoc_ap) {
+               switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats,
+                                            wds != NULL)) {
+               case AP_RX_CONTINUE_NOT_AUTHORIZED:
+                       frame_authorized = 0;
+                       break;
+               case AP_RX_CONTINUE:
+                       frame_authorized = 1;
+                       break;
+               case AP_RX_DROP:
+                       goto rx_dropped;
+               case AP_RX_EXIT:
+                       goto rx_exit;
+               }
+       }
+#endif
+
+       /* Nullfunc frames may have PS-bit set, so they must be passed to
+        * hostap_handle_sta_rx() before being dropped here. */
+       if (stype != IEEE80211_STYPE_DATA &&
+           stype != IEEE80211_STYPE_DATA_CFACK &&
+           stype != IEEE80211_STYPE_DATA_CFPOLL &&
+           stype != IEEE80211_STYPE_DATA_CFACKPOLL) {
+               if (stype != IEEE80211_STYPE_NULLFUNC)
+                       IEEE80211_DEBUG_DROP(
+                               "RX: dropped data frame "
+                               "with no data (type=0x%02x, "
+                               "subtype=0x%02x, len=%d)\n",
+                               type, stype, skb->len);
+               goto rx_dropped;
+       }
+
+       /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
+
+       if (ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+           (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0)
+               goto rx_dropped;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       /* skb: hdr + (possibly fragmented) plaintext payload */
+       // PR: FIXME: hostap has additional conditions in the "if" below:
+       // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+       if ((frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) {
+               int flen;
+               struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr);
+               IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag);
+
+               if (!frag_skb) {
+                       IEEE80211_DEBUG(IEEE80211_DL_RX | IEEE80211_DL_FRAG,
+                                       "Rx cannot get skb from fragment "
+                                       "cache (morefrag=%d seq=%u frag=%u)\n",
+                                       (fc & IEEE80211_FCTL_MOREFRAGS) != 0,
+                                       WLAN_GET_SEQ_SEQ(sc), frag);
+                       goto rx_dropped;
+               }
+
+               flen = skb->len;
+               if (frag != 0)
+                       flen -= hdrlen;
+
+               if (frag_skb->tail + flen > frag_skb->end) {
+                       printk(KERN_WARNING "%s: host decrypted and "
+                              "reassembled frame did not fit skb\n",
+                              dev->name);
+                       ieee80211_frag_cache_invalidate(ieee, hdr);
+                       goto rx_dropped;
+               }
+
+               if (frag == 0) {
+                       /* copy first fragment (including full headers) into
+                        * beginning of the fragment cache skb */
+                       memcpy(skb_put(frag_skb, flen), skb->data, flen);
+               } else {
+                       /* append frame payload to the end of the fragment
+                        * cache skb */
+                       memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
+                              flen);
+               }
+               dev_kfree_skb_any(skb);
+               skb = NULL;
+
+               if (fc & IEEE80211_FCTL_MOREFRAGS) {
+                       /* more fragments expected - leave the skb in fragment
+                        * cache for now; it will be delivered to upper layers
+                        * after all fragments have been received */
+                       goto rx_exit;
+               }
+
+               /* this was the last fragment and the frame will be
+                * delivered, so remove skb from fragment cache */
+               skb = frag_skb;
+               hdr = (struct ieee80211_hdr *) skb->data;
+               ieee80211_frag_cache_invalidate(ieee, hdr);
+       }
+
+       /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
+        * encrypted/authenticated */
+       if (ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+           ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt))
+               goto rx_dropped;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) {
+               if (/*ieee->ieee802_1x &&*/
+                   ieee80211_is_eapol_frame(ieee, skb)) {
+                       /* pass unencrypted EAPOL frames even if encryption is
+                        * configured */
+               } else {
+                       IEEE80211_DEBUG_DROP(
+                               "encryption configured, but RX "
+                               "frame not encrypted (SA=" MAC_FMT ")\n",
+                               MAC_ARG(hdr->addr2));
+                       goto rx_dropped;
+               }
+       }
+
+       if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep &&
+           !ieee80211_is_eapol_frame(ieee, skb)) {
+               IEEE80211_DEBUG_DROP(
+                       "dropped unencrypted RX data "
+                       "frame from " MAC_FMT
+                       " (drop_unencrypted=1)\n",
+                       MAC_ARG(hdr->addr2));
+               goto rx_dropped;
+       }
+
+       /* skb: hdr + (possible reassembled) full plaintext payload */
+
+       payload = skb->data + hdrlen;
+       ethertype = (payload[6] << 8) | payload[7];
+
+#ifdef NOT_YET
+       /* If IEEE 802.1X is used, check whether the port is authorized to send
+        * the received frame. */
+       if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) {
+               if (ethertype == ETH_P_PAE) {
+                       printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n",
+                              dev->name);
+                       if (ieee->hostapd && ieee->apdev) {
+                               /* Send IEEE 802.1X frames to the user
+                                * space daemon for processing */
+                               prism2_rx_80211(ieee->apdev, skb, rx_stats,
+                                               PRISM2_RX_MGMT);
+                               ieee->apdevstats.rx_packets++;
+                               ieee->apdevstats.rx_bytes += skb->len;
+                               goto rx_exit;
+                       }
+               } else if (!frame_authorized) {
+                       printk(KERN_DEBUG "%s: dropped frame from "
+                              "unauthorized port (IEEE 802.1X): "
+                              "ethertype=0x%04x\n",
+                              dev->name, ethertype);
+                       goto rx_dropped;
+               }
+       }
+#endif
+
+       /* convert hdr + possible LLC headers into Ethernet header */
+       if (skb->len - hdrlen >= 8 &&
+           ((memcmp(payload, rfc1042_header, SNAP_SIZE) == 0 &&
+             ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+            memcmp(payload, bridge_tunnel_header, SNAP_SIZE) == 0)) {
+               /* remove RFC1042 or Bridge-Tunnel encapsulation and
+                * replace EtherType */
+               skb_pull(skb, hdrlen + SNAP_SIZE);
+               memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+               memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+       } else {
+               u16 len;
+               /* Leave Ethernet header part of hdr and full payload */
+               skb_pull(skb, hdrlen);
+               len = htons(skb->len);
+               memcpy(skb_push(skb, 2), &len, 2);
+               memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+               memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+       }
+
+#ifdef NOT_YET
+       if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+                   IEEE80211_FCTL_TODS) &&
+           skb->len >= ETH_HLEN + ETH_ALEN) {
+               /* Non-standard frame: get addr4 from its bogus location after
+                * the payload */
+               memcpy(skb->data + ETH_ALEN,
+                      skb->data + skb->len - ETH_ALEN, ETH_ALEN);
+               skb_trim(skb, skb->len - ETH_ALEN);
+       }
+#endif
+
+       stats->rx_packets++;
+       stats->rx_bytes += skb->len;
+
+#ifdef NOT_YET
+       if (ieee->iw_mode == IW_MODE_MASTER && !wds &&
+           ieee->ap->bridge_packets) {
+               if (dst[0] & 0x01) {
+                       /* copy multicast frame both to the higher layers and
+                        * to the wireless media */
+                       ieee->ap->bridged_multicast++;
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb2 == NULL)
+                               printk(KERN_DEBUG "%s: skb_clone failed for "
+                                      "multicast frame\n", dev->name);
+               } else if (hostap_is_sta_assoc(ieee->ap, dst)) {
+                       /* send frame directly to the associated STA using
+                        * wireless media and not passing to higher layers */
+                       ieee->ap->bridged_unicast++;
+                       skb2 = skb;
+                       skb = NULL;
+               }
+       }
+
+       if (skb2 != NULL) {
+               /* send to wireless media */
+               skb2->protocol = __constant_htons(ETH_P_802_3);
+               skb2->mac.raw = skb2->nh.raw = skb2->data;
+               /* skb2->nh.raw = skb2->data + ETH_HLEN; */
+               skb2->dev = dev;
+               dev_queue_xmit(skb2);
+       }
+
+#endif
+
+       if (skb) {
+               skb->protocol = eth_type_trans(skb, dev);
+               memset(skb->cb, 0, sizeof(skb->cb));
+               skb->dev = dev;
+               skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
+               netif_rx(skb);
+       }
+
+ rx_exit:
+#ifdef NOT_YET
+       if (sta)
+               hostap_handle_sta_release(sta);
+#endif
+       return 1;
+
+ rx_dropped:
+       stats->rx_dropped++;
+
+       /* Returning 0 indicates to caller that we have not handled the SKB--
+        * so it is still allocated and can be used again by underlying
+        * hardware as a DMA target */
+       return 0;
+}
+
+#define MGMT_FRAME_FIXED_PART_LENGTH           0x24
+
+static inline int ieee80211_is_ofdm_rate(u8 rate)
+{
+       switch (rate & ~IEEE80211_BASIC_RATE_MASK) {
+       case IEEE80211_OFDM_RATE_6MB:
+       case IEEE80211_OFDM_RATE_9MB:
+       case IEEE80211_OFDM_RATE_12MB:
+       case IEEE80211_OFDM_RATE_18MB:
+       case IEEE80211_OFDM_RATE_24MB:
+       case IEEE80211_OFDM_RATE_36MB:
+       case IEEE80211_OFDM_RATE_48MB:
+       case IEEE80211_OFDM_RATE_54MB:
+               return 1;
+       }
+        return 0;
+}
+
+
+static inline int ieee80211_network_init(
+       struct ieee80211_device *ieee,
+       struct ieee80211_probe_response *beacon,
+       struct ieee80211_network *network,
+       struct ieee80211_rx_stats *stats)
+{
+#ifdef CONFIG_IEEE80211_DEBUG
+       char rates_str[64];
+       char *p;
+#endif
+       struct ieee80211_info_element *info_element;
+       u16 left;
+       u8 i;
+
+       /* Pull out fixed field data */
+       memcpy(network->bssid, beacon->header.addr3, ETH_ALEN);
+       network->capability = beacon->capability;
+       network->last_scanned = jiffies;
+       network->time_stamp[0] = beacon->time_stamp[0];
+       network->time_stamp[1] = beacon->time_stamp[1];
+       network->beacon_interval = beacon->beacon_interval;
+       /* Where to pull this? beacon->listen_interval;*/
+       network->listen_interval = 0x0A;
+       network->rates_len = network->rates_ex_len = 0;
+       network->last_associate = 0;
+       network->ssid_len = 0;
+       network->flags = 0;
+       network->atim_window = 0;
+
+       if (stats->freq == IEEE80211_52GHZ_BAND) {
+               /* for A band (No DS info) */
+               network->channel = stats->received_channel;
+       } else
+               network->flags |= NETWORK_HAS_CCK;
+
+       network->wpa_ie_len = 0;
+       network->rsn_ie_len = 0;
+
+       info_element = &beacon->info_element;
+       left = stats->len - ((void *)info_element - (void *)beacon);
+       while (left >= sizeof(struct ieee80211_info_element_hdr)) {
+               if (sizeof(struct ieee80211_info_element_hdr) + info_element->len > left) {
+                       IEEE80211_DEBUG_SCAN("SCAN: parse failed: info_element->len + 2 > left : info_element->len+2=%d left=%d.\n",
+                                            info_element->len + sizeof(struct ieee80211_info_element),
+                                            left);
+                       return 1;
+                       }
+
+               switch (info_element->id) {
+               case MFIE_TYPE_SSID:
+                       if (ieee80211_is_empty_essid(info_element->data,
+                                                    info_element->len)) {
+                               network->flags |= NETWORK_EMPTY_ESSID;
+                               break;
+                       }
+
+                       network->ssid_len = min(info_element->len,
+                                               (u8)IW_ESSID_MAX_SIZE);
+                       memcpy(network->ssid, info_element->data, network->ssid_len);
+                       if (network->ssid_len < IW_ESSID_MAX_SIZE)
+                               memset(network->ssid + network->ssid_len, 0,
+                                      IW_ESSID_MAX_SIZE - network->ssid_len);
+
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_SSID: '%s' len=%d.\n",
+                                            network->ssid, network->ssid_len);
+                       break;
+
+               case MFIE_TYPE_RATES:
+#ifdef CONFIG_IEEE80211_DEBUG
+                       p = rates_str;
+#endif
+                       network->rates_len = min(info_element->len, MAX_RATES_LENGTH);
+                       for (i = 0; i < network->rates_len; i++) {
+                               network->rates[i] = info_element->data[i];
+#ifdef CONFIG_IEEE80211_DEBUG
+                               p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]);
+#endif
+                               if (ieee80211_is_ofdm_rate(info_element->data[i])) {
+                                       network->flags |= NETWORK_HAS_OFDM;
+                                       if (info_element->data[i] &
+                                           IEEE80211_BASIC_RATE_MASK)
+                                               network->flags &=
+                                                       ~NETWORK_HAS_CCK;
+                               }
+                       }
+
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_RATES: '%s' (%d)\n",
+                                            rates_str, network->rates_len);
+                       break;
+
+               case MFIE_TYPE_RATES_EX:
+#ifdef CONFIG_IEEE80211_DEBUG
+                       p = rates_str;
+#endif
+                       network->rates_ex_len = min(info_element->len, MAX_RATES_EX_LENGTH);
+                       for (i = 0; i < network->rates_ex_len; i++) {
+                               network->rates_ex[i] = info_element->data[i];
+#ifdef CONFIG_IEEE80211_DEBUG
+                               p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]);
+#endif
+                               if (ieee80211_is_ofdm_rate(info_element->data[i])) {
+                                       network->flags |= NETWORK_HAS_OFDM;
+                                       if (info_element->data[i] &
+                                           IEEE80211_BASIC_RATE_MASK)
+                                               network->flags &=
+                                                       ~NETWORK_HAS_CCK;
+                               }
+                       }
+
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_RATES_EX: '%s' (%d)\n",
+                                            rates_str, network->rates_ex_len);
+                       break;
+
+               case MFIE_TYPE_DS_SET:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_DS_SET: %d\n",
+                                            info_element->data[0]);
+                       if (stats->freq == IEEE80211_24GHZ_BAND)
+                               network->channel = info_element->data[0];
+                       break;
+
+               case MFIE_TYPE_FH_SET:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_FH_SET: ignored\n");
+                       break;
+
+               case MFIE_TYPE_CF_SET:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_CF_SET: ignored\n");
+                       break;
+
+               case MFIE_TYPE_TIM:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_TIM: ignored\n");
+                       break;
+
+               case MFIE_TYPE_IBSS_SET:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_IBSS_SET: ignored\n");
+                       break;
+
+               case MFIE_TYPE_CHALLENGE:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_CHALLENGE: ignored\n");
+                       break;
+
+               case MFIE_TYPE_GENERIC:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_GENERIC: %d bytes\n",
+                                            info_element->len);
+                       if (info_element->len >= 4  &&
+                           info_element->data[0] == 0x00 &&
+                           info_element->data[1] == 0x50 &&
+                           info_element->data[2] == 0xf2 &&
+                           info_element->data[3] == 0x01) {
+                               network->wpa_ie_len = min(info_element->len + 2,
+                                                        MAX_WPA_IE_LEN);
+                               memcpy(network->wpa_ie, info_element,
+                                      network->wpa_ie_len);
+                       }
+                       break;
+
+               case MFIE_TYPE_RSN:
+                       IEEE80211_DEBUG_SCAN("MFIE_TYPE_RSN: %d bytes\n",
+                                            info_element->len);
+                       network->rsn_ie_len = min(info_element->len + 2,
+                                                MAX_WPA_IE_LEN);
+                       memcpy(network->rsn_ie, info_element,
+                              network->rsn_ie_len);
+                       break;
+
+               default:
+                       IEEE80211_DEBUG_SCAN("unsupported IE %d\n",
+                                            info_element->id);
+                        break;
+               }
+
+               left -= sizeof(struct ieee80211_info_element_hdr) +
+                       info_element->len;
+               info_element = (struct ieee80211_info_element *)
+                       &info_element->data[info_element->len];
+       }
+
+       network->mode = 0;
+       if (stats->freq == IEEE80211_52GHZ_BAND)
+               network->mode = IEEE_A;
+       else {
+               if (network->flags & NETWORK_HAS_OFDM)
+                       network->mode |= IEEE_G;
+               if (network->flags & NETWORK_HAS_CCK)
+                       network->mode |= IEEE_B;
+       }
+
+       if (network->mode == 0) {
+               IEEE80211_DEBUG_SCAN("Filtered out '%s (" MAC_FMT ")' "
+                                    "network.\n",
+                                    escape_essid(network->ssid,
+                                                 network->ssid_len),
+                                    MAC_ARG(network->bssid));
+               return 1;
+       }
+
+       if (ieee80211_is_empty_essid(network->ssid, network->ssid_len))
+               network->flags |= NETWORK_EMPTY_ESSID;
+
+       memcpy(&network->stats, stats, sizeof(network->stats));
+
+       return 0;
+}
+
+static inline int is_same_network(struct ieee80211_network *src,
+                                 struct ieee80211_network *dst)
+{
+       /* A network is only a duplicate if the channel, BSSID, and ESSID
+        * all match.  We treat all <hidden> with the same BSSID and channel
+        * as one network */
+       return ((src->ssid_len == dst->ssid_len) &&
+               (src->channel == dst->channel) &&
+               !memcmp(src->bssid, dst->bssid, ETH_ALEN) &&
+               !memcmp(src->ssid, dst->ssid, src->ssid_len));
+}
+
+static inline void update_network(struct ieee80211_network *dst,
+                                 struct ieee80211_network *src)
+{
+       memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats));
+       dst->capability = src->capability;
+       memcpy(dst->rates, src->rates, src->rates_len);
+       dst->rates_len = src->rates_len;
+       memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len);
+       dst->rates_ex_len = src->rates_ex_len;
+
+       dst->mode = src->mode;
+       dst->flags = src->flags;
+       dst->time_stamp[0] = src->time_stamp[0];
+       dst->time_stamp[1] = src->time_stamp[1];
+
+       dst->beacon_interval = src->beacon_interval;
+       dst->listen_interval = src->listen_interval;
+       dst->atim_window = src->atim_window;
+
+       memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len);
+       dst->wpa_ie_len = src->wpa_ie_len;
+       memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len);
+       dst->rsn_ie_len = src->rsn_ie_len;
+
+       dst->last_scanned = jiffies;
+       /* dst->last_associate is not overwritten */
+}
+
+static inline void ieee80211_process_probe_response(
+       struct ieee80211_device *ieee,
+       struct ieee80211_probe_response *beacon,
+       struct ieee80211_rx_stats *stats)
+{
+       struct ieee80211_network network;
+       struct ieee80211_network *target;
+       struct ieee80211_network *oldest = NULL;
+#ifdef CONFIG_IEEE80211_DEBUG
+       struct ieee80211_info_element *info_element = &beacon->info_element;
+#endif
+       unsigned long flags;
+
+       IEEE80211_DEBUG_SCAN(
+               "'%s' (" MAC_FMT "): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n",
+               escape_essid(info_element->data, info_element->len),
+               MAC_ARG(beacon->header.addr3),
+               (beacon->capability & (1<<0xf)) ? '1' : '0',
+               (beacon->capability & (1<<0xe)) ? '1' : '0',
+               (beacon->capability & (1<<0xd)) ? '1' : '0',
+               (beacon->capability & (1<<0xc)) ? '1' : '0',
+               (beacon->capability & (1<<0xb)) ? '1' : '0',
+               (beacon->capability & (1<<0xa)) ? '1' : '0',
+               (beacon->capability & (1<<0x9)) ? '1' : '0',
+               (beacon->capability & (1<<0x8)) ? '1' : '0',
+               (beacon->capability & (1<<0x7)) ? '1' : '0',
+               (beacon->capability & (1<<0x6)) ? '1' : '0',
+               (beacon->capability & (1<<0x5)) ? '1' : '0',
+               (beacon->capability & (1<<0x4)) ? '1' : '0',
+               (beacon->capability & (1<<0x3)) ? '1' : '0',
+               (beacon->capability & (1<<0x2)) ? '1' : '0',
+               (beacon->capability & (1<<0x1)) ? '1' : '0',
+               (beacon->capability & (1<<0x0)) ? '1' : '0');
+
+       if (ieee80211_network_init(ieee, beacon, &network, stats)) {
+               IEEE80211_DEBUG_SCAN("Dropped '%s' (" MAC_FMT ") via %s.\n",
+                                    escape_essid(info_element->data,
+                                                 info_element->len),
+                                    MAC_ARG(beacon->header.addr3),
+                                    WLAN_FC_GET_STYPE(beacon->header.frame_ctl) ==
+                                    IEEE80211_STYPE_PROBE_RESP ?
+                                    "PROBE RESPONSE" : "BEACON");
+               return;
+       }
+
+       /* The network parsed correctly -- so now we scan our known networks
+        * to see if we can find it in our list.
+        *
+        * NOTE:  This search is definitely not optimized.  Once its doing
+        *        the "right thing" we'll optimize it for efficiency if
+        *        necessary */
+
+       /* Search for this entry in the list and update it if it is
+        * already there. */
+
+       spin_lock_irqsave(&ieee->lock, flags);
+
+       list_for_each_entry(target, &ieee->network_list, list) {
+               if (is_same_network(target, &network))
+                       break;
+
+               if ((oldest == NULL) ||
+                   (target->last_scanned < oldest->last_scanned))
+                       oldest = target;
+       }
+
+       /* If we didn't find a match, then get a new network slot to initialize
+        * with this beacon's information */
+       if (&target->list == &ieee->network_list) {
+               if (list_empty(&ieee->network_free_list)) {
+                       /* If there are no more slots, expire the oldest */
+                       list_del(&oldest->list);
+                       target = oldest;
+                       IEEE80211_DEBUG_SCAN("Expired '%s' (" MAC_FMT ") from "
+                                            "network list.\n",
+                                            escape_essid(target->ssid,
+                                                         target->ssid_len),
+                                            MAC_ARG(target->bssid));
+               } else {
+                       /* Otherwise just pull from the free list */
+                       target = list_entry(ieee->network_free_list.next,
+                                           struct ieee80211_network, list);
+                       list_del(ieee->network_free_list.next);
+               }
+
+
+#ifdef CONFIG_IEEE80211_DEBUG
+               IEEE80211_DEBUG_SCAN("Adding '%s' (" MAC_FMT ") via %s.\n",
+                                    escape_essid(network.ssid,
+                                                 network.ssid_len),
+                                    MAC_ARG(network.bssid),
+                                    WLAN_FC_GET_STYPE(beacon->header.frame_ctl) ==
+                                    IEEE80211_STYPE_PROBE_RESP ?
+                                    "PROBE RESPONSE" : "BEACON");
+#endif
+               memcpy(target, &network, sizeof(*target));
+               list_add_tail(&target->list, &ieee->network_list);
+       } else {
+               IEEE80211_DEBUG_SCAN("Updating '%s' (" MAC_FMT ") via %s.\n",
+                                    escape_essid(target->ssid,
+                                                 target->ssid_len),
+                                    MAC_ARG(target->bssid),
+                                    WLAN_FC_GET_STYPE(beacon->header.frame_ctl) ==
+                                    IEEE80211_STYPE_PROBE_RESP ?
+                                    "PROBE RESPONSE" : "BEACON");
+               update_network(target, &network);
+       }
+
+       spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+void ieee80211_rx_mgt(struct ieee80211_device *ieee,
+                     struct ieee80211_hdr *header,
+                     struct ieee80211_rx_stats *stats)
+{
+       switch (WLAN_FC_GET_STYPE(header->frame_ctl)) {
+       case IEEE80211_STYPE_ASSOC_RESP:
+               IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n",
+                                    WLAN_FC_GET_STYPE(header->frame_ctl));
+               break;
+
+       case IEEE80211_STYPE_REASSOC_RESP:
+               IEEE80211_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n",
+                                    WLAN_FC_GET_STYPE(header->frame_ctl));
+               break;
+
+       case IEEE80211_STYPE_PROBE_RESP:
+               IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n",
+                                    WLAN_FC_GET_STYPE(header->frame_ctl));
+               IEEE80211_DEBUG_SCAN("Probe response\n");
+               ieee80211_process_probe_response(
+                       ieee, (struct ieee80211_probe_response *)header, stats);
+               break;
+
+       case IEEE80211_STYPE_BEACON:
+               IEEE80211_DEBUG_MGMT("received BEACON (%d)\n",
+                                    WLAN_FC_GET_STYPE(header->frame_ctl));
+               IEEE80211_DEBUG_SCAN("Beacon\n");
+               ieee80211_process_probe_response(
+                       ieee, (struct ieee80211_probe_response *)header, stats);
+               break;
+
+       default:
+               IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n",
+                                    WLAN_FC_GET_STYPE(header->frame_ctl));
+               IEEE80211_WARNING("%s: Unknown management packet: %d\n",
+                                 ieee->dev->name,
+                                 WLAN_FC_GET_STYPE(header->frame_ctl));
+               break;
+       }
+}
+
+
+EXPORT_SYMBOL(ieee80211_rx_mgt);
+EXPORT_SYMBOL(ieee80211_rx);
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
new file mode 100644 (file)
index 0000000..b7ea3e2
--- /dev/null
@@ -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);
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
new file mode 100644 (file)
index 0000000..2cd571c
--- /dev/null
@@ -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);