diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig	2005-02-23 10:19:26.114821080 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig	2005-02-24 13:06:17.665076520 -0800
@@ -357,6 +357,8 @@
 
 source "drivers/net/wireless/hostap/Kconfig"
 
+source "drivers/net/wireless/ath/Kconfig"
+
 # yes, this works even when no drivers are selected
 config NET_WIRELESS
 	bool
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig.orig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig.orig
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig.orig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig.orig	2005-02-23 10:19:26.000000000 -0800
@@ -0,0 +1,367 @@
+#
+# Wireless LAN device configuration
+#
+
+menu "Wireless LAN (non-hamradio)"
+	depends on NETDEVICES
+
+config NET_RADIO
+	bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions"
+	---help---
+	  Support for wireless LANs and everything having to do with radio,
+	  but not with amateur radio or FM broadcasting.
+
+	  Saying Y here also enables the Wireless Extensions (creates
+	  /proc/net/wireless and enables iwconfig access). The Wireless
+	  Extension is a generic API allowing a driver to expose to the user
+	  space configuration and statistics specific to common Wireless LANs.
+	  The beauty of it is that a single set of tool can support all the
+	  variations of Wireless LANs, regardless of their type (as long as
+	  the driver supports Wireless Extension). Another advantage is that
+	  these parameters may be changed on the fly without restarting the
+	  driver (or Linux). If you wish to use Wireless Extensions with
+	  wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch
+	  the tools from
+	  <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+
+	  Some user-level drivers for scarab devices which don't require
+	  special kernel support are available from
+	  <ftp://shadow.cabi.net/pub/Linux/>.
+
+# Note : the cards are obsolete (can't buy them anymore), but the drivers
+# are not, as people are still using them...
+comment "Obsolete Wireless cards support (pre-802.11)"
+	depends on NET_RADIO && (INET || ISA || PCMCIA)
+
+config STRIP
+	tristate "STRIP (Metricom starmode radio IP)"
+	depends on NET_RADIO && INET
+	---help---
+	  Say Y if you have a Metricom radio and intend to use Starmode Radio
+	  IP. STRIP is a radio protocol developed for the MosquitoNet project
+	  (on the WWW at <http://mosquitonet.stanford.edu/>) to send Internet
+	  traffic using Metricom radios.  Metricom radios are small, battery
+	  powered, 100kbit/sec packet radio transceivers, about the size and
+	  weight of a cellular telephone. (You may also have heard them called
+	  "Metricom modems" but we avoid the term "modem" because it misleads
+	  many people into thinking that you can plug a Metricom modem into a
+	  phone line and use it as a modem.)
+
+	  You can use STRIP on any Linux machine with a serial port, although
+	  it is obviously most useful for people with laptop computers. If you
+	  think you might get a Metricom radio in the future, there is no harm
+	  in saying Y to STRIP now, except that it makes the kernel a bit
+	  bigger.
+
+	  To compile this as a module, choose M here: the module will be
+	  called strip.
+
+config ARLAN
+	tristate "Aironet Arlan 655 & IC2200 DS support"
+	depends on NET_RADIO && ISA && !64BIT
+	---help---
+	  Aironet makes Arlan, a class of wireless LAN adapters. These use the
+	  www.Telxon.com chip, which is also used on several similar cards.
+	  This driver is tested on the 655 and IC2200 series cards. Look at
+	  <http://www.ylenurme.ee/~elmer/655/> for the latest information.
+
+	  The driver is built as two modules, arlan and arlan-proc. The latter
+	  is the /proc interface and is not needed most of time.
+
+	  On some computers the card ends up in non-valid state after some
+	  time. Use a ping-reset script to clear it.
+
+config WAVELAN
+	tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support"
+	depends on NET_RADIO && ISA
+	---help---
+	  The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is
+	  a Radio LAN (wireless Ethernet-like Local Area Network) using the
+	  radio frequencies 900 MHz and 2.4 GHz.
+
+	  This driver support the ISA version of the WaveLAN card.  A separate
+	  driver for the PCMCIA (PC-card) hardware is available in David
+	  Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
+	  for location).
+
+	  If you want to use an ISA WaveLAN card under Linux, say Y and read
+	  the Ethernet-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>. Some more specific
+	  information is contained in
+	  <file:Documentation/networking/wavelan.txt> and in the source code
+	  <file:drivers/net/wavelan.p.h>.
+
+	  You will also need the wireless tools package available from
+	  <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+	  Please read the man pages contained therein.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called wavelan.
+
+config PCMCIA_WAVELAN
+	tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support"
+	depends on NET_RADIO && PCMCIA
+	help
+	  Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA
+	  (PC-card) wireless Ethernet networking card to your computer.  This
+	  driver is for the non-IEEE-802.11 Wavelan cards.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called wavelan_cs.  If unsure, say N.
+
+config PCMCIA_NETWAVE
+	tristate "Xircom Netwave AirSurfer Pcmcia wireless support"
+	depends on NET_RADIO && PCMCIA
+	help
+	  Say Y here if you intend to attach this type of PCMCIA (PC-card)
+	  wireless Ethernet networking card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called netwave_cs.  If unsure, say N.
+
+comment "Wireless 802.11 Frequency Hopping cards support"
+	depends on NET_RADIO && PCMCIA
+
+config PCMCIA_RAYCS
+	tristate "Aviator/Raytheon 2.4MHz wireless support"
+	depends on NET_RADIO && PCMCIA
+	---help---
+	  Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
+	  (PC-card) wireless Ethernet networking card to your computer.
+	  Please read the file <file:Documentation/networking/ray_cs.txt> for
+	  details.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ray_cs.  If unsure, say N.
+
+comment "Wireless 802.11b ISA/PCI cards support"
+	depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
+
+config AIRO
+	tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
+	depends on NET_RADIO && ISA && (PCI || BROKEN)
+	---help---
+	  This is the standard Linux driver to support Cisco/Aironet ISA and
+	  PCI 802.11 wireless cards.
+	  It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
+	  - with or without encryption) as well as card before the Cisco
+	  aquisition (Aironet 4500, Aironet 4800, Aironet 4800B).
+
+	  This driver support both the standard Linux Wireless Extensions
+	  and Cisco proprietary API, so both the Linux Wireless Tools and the
+	  Cisco Linux utilities can be used to configure the card.
+
+	  The driver can be compiled as a module and will be named "airo".
+
+config HERMES
+	tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
+	depends on NET_RADIO && (PPC_PMAC || PCI || PCMCIA)
+	---help---
+	  A driver for 802.11b wireless cards based based on the "Hermes" or
+	  Intersil HFA384x (Prism 2) MAC controller.  This includes the vast
+	  majority of the PCMCIA 802.11b cards (which are nearly all rebadges)
+	  - except for the Cisco/Aironet cards.  Cards supported include the
+	  Apple Airport (not a PCMCIA card), WavelanIEEE/Orinoco,
+	  Cabletron/EnteraSys Roamabout, ELSA AirLancer, MELCO Buffalo, Avaya,
+	  IBM High Rate Wireless, Farralon Syyline, Samsung MagicLAN, Netgear
+	  MA401, LinkSys WPC-11, D-Link DWL-650, 3Com AirConnect, Intel
+	  PRO/Wireless, and Symbol Spectrum24 High Rate amongst others.
+
+	  This option includes the guts of the driver, but in order to
+	  actually use a card you will also need to enable support for PCMCIA
+	  Hermes cards, PLX9052 based PCI adaptors or the Apple Airport below.
+
+	  You will also very likely also need the Wireless Tools in order to
+	  configure your card and that /etc/pcmcia/wireless.opts works :
+	  <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>
+
+config APPLE_AIRPORT
+	tristate "Apple Airport support (built-in)"
+	depends on PPC_PMAC && HERMES
+	help
+	  Say Y here to support the Airport 802.11b wireless Ethernet hardware
+	  built into the Macintosh iBook and other recent PowerPC-based
+	  Macintosh machines. This is essentially a Lucent Orinoco card with 
+	  a non-standard interface
+
+config PLX_HERMES
+	tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.) (EXPERIMENTAL)"
+	depends on PCI && HERMES && EXPERIMENTAL
+	help
+	  Enable support for PCMCIA cards supported by the "Hermes" (aka
+	  orinoco) driver when used in PLX9052 based PCI adaptors.  These
+	  adaptors are not a full PCMCIA controller but act as a more limited
+	  PCI <-> PCMCIA bridge.  Several vendors sell such adaptors so that
+	  802.11b PCMCIA cards can be used in desktop machines.  The Netgear
+	  MA301 is such an adaptor.
+
+	  Support for these adaptors is so far still incomplete and buggy.
+	  You have been warned.
+
+config TMD_HERMES
+	tristate "Hermes in TMD7160 based PCI adaptor support (EXPERIMENTAL)"
+	depends on PCI && HERMES && EXPERIMENTAL
+	help
+	  Enable support for PCMCIA cards supported by the "Hermes" (aka
+	  orinoco) driver when used in TMD7160 based PCI adaptors.  These
+	  adaptors are not a full PCMCIA controller but act as a more limited
+	  PCI <-> PCMCIA bridge.  Several vendors sell such adaptors so that
+	  802.11b PCMCIA cards can be used in desktop machines.
+
+	  Support for these adaptors is so far still incomplete and buggy.
+	  You have been warned.
+
+config PCI_HERMES
+	tristate "Prism 2.5 PCI 802.11b adaptor support (EXPERIMENTAL)"
+	depends on PCI && HERMES && EXPERIMENTAL
+	help
+	  Enable support for PCI and mini-PCI 802.11b wireless NICs based on
+	  the Prism 2.5 chipset.  These are true PCI cards, not the 802.11b
+	  PCMCIA cards bundled with PCI<->PCMCIA adaptors which are also
+	  common.  Some of the built-in wireless adaptors in laptops are of
+	  this variety.
+
+config ATMEL
+      tristate "Atmel at76c50x chipset  802.11b support"
+      depends on NET_RADIO && EXPERIMENTAL
+      select FW_LOADER
+      select CRC32
+       ---help---
+        A driver 802.11b wireless cards based on the Atmel fast-vnet
+        chips. This driver supports standard Linux wireless extensions. 
+ 
+        Many  cards based on this chipset do not have flash memory
+        and need their firmware loaded at start-up. If yours is 
+        one of these, you will need to provide a firmware image
+        to be loaded into the card by the driver. The Atmel
+        firmware package can be downloaded from
+        <http://www.thekelleys.org.uk/atmel>
+
+config PCI_ATMEL
+      tristate "Atmel at76c506 PCI cards"
+      depends on ATMEL && PCI
+       ---help---
+        Enable support for PCI and mini-PCI cards containing the
+        Atmel at76c506 chip.
+
+# If Pcmcia is compiled in, offer Pcmcia cards...
+comment "Wireless 802.11b Pcmcia/Cardbus cards support"
+	depends on NET_RADIO && PCMCIA
+
+config PCMCIA_HERMES
+	tristate "Hermes PCMCIA card support"
+	depends on NET_RADIO && PCMCIA && HERMES
+	---help---
+	  A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
+	  as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
+	  EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and
+	  others).  It should also be usable on various Prism II based cards
+	  such as the Linksys, D-Link and Farallon Skyline.  It should also
+	  work on Symbol cards such as the 3Com AirConnect and Ericsson WLAN.
+
+	  To use your PC-cards, you will need supporting software from David
+	  Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
+	  for location).  You also want to check out the PCMCIA-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.
+
+	  You will also very likely also need the Wireless Tools in order to
+	  configure your card and that /etc/pcmcia/wireless.opts works:
+	  <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+
+config AIRO_CS
+	tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
+	depends on NET_RADIO && PCMCIA
+	---help---
+	  This is the standard Linux driver to support Cisco/Aironet PCMCIA
+	  802.11 wireless cards.  This driver is the same as the Aironet
+	  driver part of the Linux Pcmcia package.
+	  It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
+	  - with or without encryption) as well as card before the Cisco
+	  aquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also
+	  supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom
+	  802.11b cards.
+
+	  This driver support both the standard Linux Wireless Extensions
+	  and Cisco proprietary API, so both the Linux Wireless Tools and the
+	  Cisco Linux utilities can be used to configure the card.
+
+	  To use your PC-cards, you will need supporting software from David
+	  Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
+	  for location).  You also want to check out the PCMCIA-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.
+
+config PCMCIA_ATMEL
+	tristate "Atmel at76c502/at76c504 PCMCIA cards"
+	depends on NET_RADIO && ATMEL && PCMCIA
+	select FW_LOADER
+	select CRC32
+	---help---
+	  Enable support for PCMCIA cards containing the
+	  Atmel at76c502 and at76c504 chips.
+
+config PCMCIA_WL3501
+      tristate "Planet WL3501 PCMCIA cards"
+      depends on NET_RADIO && EXPERIMENTAL && PCMCIA
+       ---help---
+         A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
+	 It has basic support for Linux wireless extensions and initial
+	 micro support for ethtool.
+
+comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support"
+	depends on NET_RADIO && PCI
+config PRISM54
+	tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus' 
+	depends on PCI && NET_RADIO && EXPERIMENTAL
+	select FW_LOADER
+	---help---
+	  Enable PCI and Cardbus support for the following chipset based cards:
+
+	  ISL3880 - Prism GT            802.11 b/g
+	  ISL3877 - Prism Indigo        802.11 a
+	  ISL3890 - Prism Duette        802.11 a/b/g
+	  
+	  For a complete list of supported cards visit <http://prism54.org>.
+	  Here is the latest confirmed list of supported cards:
+
+	  3com OfficeConnect 11g Cardbus Card aka 3CRWE154G72
+	  Allnet ALL0271 PCI Card
+	  Compex WL54G Cardbus Card
+	  Corega CG-WLCB54GT Cardbus Card
+	  D-Link Air Plus Xtreme G A1 Cardbus Card aka DWL-g650
+	  I-O Data WN-G54/CB Cardbus Card
+	  Kobishi XG-300 aka Z-Com Cardbus Card
+	  Netgear WG511 Cardbus Card
+	  Ovislink WL-5400PCI PCI Card
+	  Peabird WLG-PCI PCI Card
+	  Sitecom WL-100i Cardbus Card
+	  Sitecom WL-110i PCI Card
+	  SMC2802W -    EZ Connect g 2.4GHz 54 Mbps Wireless PCI Card
+	  SMC2835W -    EZ Connect g 2.4GHz 54 Mbps Wireless Cardbus Card
+	  SMC2835W-V2 - EZ Connect g 2.4GHz 54 Mbps Wireless Cardbus Card
+	  Z-Com XG-900 PCI Card
+	  Zyxel G-100 Cardbus Card
+
+	  If you enable this you will need a firmware file as well.
+	  You will need to copy this to /usr/lib/hotplug/firmware/isl3890.
+	  You can get this non-GPL'd firmware file from the Prism54 project page:
+	  <http://prism54.org>
+	  You will also need the /etc/hotplug/firmware.agent script from
+	  a current hotplug package.
+
+	  Note: You need a motherboard with DMA support to use any of these cards 
+	  
+	  If you want to compile the driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  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
+	depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
+	default y
+
+endmenu
+
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile	2005-02-23 10:19:26.131818496 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile	2005-02-24 13:06:17.717068616 -0800
@@ -33,3 +33,8 @@
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
 obj-$(CONFIG_PCMCIA_WL3501)	+= wl3501_cs.o
+
+obj-$(CONFIG_ATHEROS_HAL)	+= _ath_hal/
+obj-$(CONFIG_ATHEROS_RATE)	+= _ath_rate/
+obj-$(CONFIG_ATHEROS)		+= ath/
+obj-$(CONFIG_NET80211)		+= net80211/
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile.orig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile.orig
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile.orig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile.orig	2005-02-23 10:19:26.000000000 -0800
@@ -0,0 +1,35 @@
+#
+# Makefile for the Linux Wireless network device drivers.
+#
+
+obj-$(CONFIG_STRIP) += strip.o
+obj-$(CONFIG_ARLAN) += arlan.o 
+
+arlan-objs := arlan-main.o arlan-proc.o
+
+# Obsolete cards
+obj-$(CONFIG_WAVELAN)		+= wavelan.o
+obj-$(CONFIG_PCMCIA_NETWAVE)	+= netwave_cs.o
+obj-$(CONFIG_PCMCIA_WAVELAN)	+= wavelan_cs.o
+
+obj-$(CONFIG_HERMES)		+= orinoco.o hermes.o
+obj-$(CONFIG_PCMCIA_HERMES)	+= orinoco_cs.o
+obj-$(CONFIG_APPLE_AIRPORT)	+= airport.o
+obj-$(CONFIG_PLX_HERMES)	+= orinoco_plx.o
+obj-$(CONFIG_PCI_HERMES)	+= orinoco_pci.o
+obj-$(CONFIG_TMD_HERMES)	+= orinoco_tmd.o
+
+obj-$(CONFIG_AIRO)		+= airo.o
+obj-$(CONFIG_AIRO_CS)		+= airo_cs.o airo.o
+
+obj-$(CONFIG_ATMEL)             += atmel.o
+obj-$(CONFIG_PCI_ATMEL)         += atmel_pci.o 
+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
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Kconfig
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Kconfig	2005-02-24 13:06:17.213145224 -0800
@@ -0,0 +1,2 @@
+config ATHEROS_HAL
+	tristate
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Makefile
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Makefile	2005-02-24 13:06:17.216144768 -0800
@@ -0,0 +1,64 @@
+#
+# Makefile for the Atheros HAL Module.
+#
+
+
+#
+# Use ARCH to guess the target platform.
+# XXX use .config contents in addition?
+#
+ifeq ($(strip ${ARCH}),i386)
+TARGET=	i386-elf
+endif
+ifeq ($(strip ${ARCH}),mips)
+TARGET=	mipsisa32-be-elf
+endif
+ifeq ($(strip ${ARCH}),x86_64)
+TARGET=	x86_64-elf
+endif
+
+EXTRA_CFLAGS+=	-I. -I${HAL}/.. -I${HAL}
+
+ifndef OS
+OS = linux
+endif
+
+ifndef HAL_OBJDIR
+#
+# Use release version of HAL.
+#
+# NB: binary HAL's are distributed in a uuencode'd file; on some
+#     systems uuencode is not installed by default and you must
+#     specially install it from the sharutils package.
+#
+UUDECODE?=	uudecode
+
+HAL=		$(src)/../hal
+OPT_AH_H=	${HAL}/public/${TARGET}.opt_ah.h
+else
+#
+# Use private version of HAL.
+#
+HAL=		$(shell dirname `dirname ${HAL_OBJDIR}`)
+OPT_AH_H=	${HAL_OBJDIR}/opt_ah.h
+endif
+
+obj-$(CONFIG_ATHEROS_HAL) += ath_hal.o
+ath_hal-objs	:= ah_osdep.o hal.o
+
+
+
+
+ifndef HAL_OBJDIR
+$(obj)/hal.o:	${HAL}/public/${TARGET}.hal.o.uu
+	${UUDECODE} -o $(obj)/hal.o < $<
+else
+$(obj)/hal.o:	${HAL_OBJDIR}/hal.o
+	cp ${HAL_OBJDIR}/hal.o $(obj)/hal.o
+endif
+
+# XXX need opt_ah.h to do dependencies so give this bogus dependency
+$(obj)/ah_osdep.c: ${HAL}/ah_osdep.c $(obj)/opt_ah.h
+	cp ${HAL}/${OS}/ah_osdep.c $(obj)/ah_osdep.c
+$(obj)/opt_ah.h: ${OPT_AH_H}
+	cp ${OPT_AH_H} $(obj)/opt_ah.h
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Kconfig
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Kconfig	2005-02-24 13:06:17.234142032 -0800
@@ -0,0 +1,2 @@
+config ATHEROS_RATE
+	tristate
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Makefile
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Makefile	2005-02-24 13:06:17.237141576 -0800
@@ -0,0 +1,20 @@
+#
+# Makefile for Onoe's rate control algorithm.
+#
+# $Id: Makefile.kernel,v 1.2 2004/09/29 18:19:39 samleffler Exp $
+#
+
+
+HAL=	$(src)/../hal
+ATH=	$(src)/../ath
+WLAN=	${src}/../net80211
+COMPAT=	${WLAN}/compat
+
+INCS=	-include ${COMPAT}/compat.h -I${COMPAT}
+EXTRA_CFLAGS+=	${INCS} -I${HAL} -I${HAL}/linux -I${ATH} -I${WLAN} -I${src}/..
+
+obj-$(CONFIG_ATHEROS_RATE) += ath_rate_onoe.o
+ath_rate_onoe-objs	:= onoe.o
+
+
+
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.c	2005-02-24 13:06:17.231142488 -0800
@@ -0,0 +1,579 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: onoe.c,v 1.2 2005/02/22 14:17:22 otaku Exp $
+ */
+
+/*
+ * Atsushi Onoe's rate control algorithm.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/delay.h>
+#include <linux/cache.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+
+#include <asm/uaccess.h>
+
+#include <net80211/if_media.h>
+#include <net80211/ieee80211_var.h>
+
+#include "if_athvar.h"
+#include "ah_desc.h"
+
+#include "onoe.h"
+
+#define	ONOE_DEBUG
+#ifdef ONOE_DEBUG
+#define	DPRINTF(sc, _fmt, ...) do {					\
+	if (sc->sc_debug & 0x10)					\
+		printk(_fmt, __VA_ARGS__);				\
+} while (0)
+#else
+#define	DPRINTF(sc, _fmt, ...)
+#endif
+
+/*
+ * Default parameters for the rate control algorithm.  These are
+ * all tunable with sysctls.  The rate controller runs periodically
+ * (each ath_rateinterval ms) analyzing transmit statistics for each
+ * neighbor/station (when operating in station mode this is only the AP).
+ * If transmits look to be working well over a sampling period then
+ * it gives a "raise rate credit".  If transmits look to not be working
+ * well than it deducts a credit.  If the credits cross a threshold then
+ * the transmit rate is raised.  Various error conditions force the
+ * the transmit rate to be dropped.
+ *
+ * The decision to issue/deduct a credit is based on the errors and
+ * retries accumulated over the sampling period.  ath_rate_raise defines
+ * the percent of retransmits for which a credit is issued/deducted.
+ * ath_rate_raise_threshold defines the threshold on credits at which
+ * the transmit rate is increased.
+ *
+ * XXX this algorithm is flawed.
+ */
+static	int ath_rateinterval = 1000;		/* rate ctl interval (ms)  */
+static	int ath_rate_raise = 10;		/* add credit threshold */
+static	int ath_rate_raise_threshold = 10;	/* rate ctl raise threshold */
+
+static void	ath_ratectl(unsigned long);
+static void	ath_rate_update(struct ath_softc *, struct ieee80211_node *,
+			int rate);
+static void	ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
+static void	ath_rate_ctl(void *, struct ieee80211_node *);
+
+void
+ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
+{
+	/* NB: assumed to be zero'd by caller */
+	ath_rate_update(sc, &an->an_node, 0);
+}
+EXPORT_SYMBOL(ath_rate_node_init);
+
+void
+ath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an)
+{
+}
+EXPORT_SYMBOL(ath_rate_node_cleanup);
+
+void
+ath_rate_node_copy(struct ath_softc *sc,
+	struct ath_node *dst, const struct ath_node *src)
+{
+	struct onoe_node *odst = ATH_NODE_ONOE(dst);
+	const struct onoe_node *osrc = (const struct onoe_node *)&src[1];
+
+	memcpy(odst, osrc, sizeof(struct onoe_node));
+}
+EXPORT_SYMBOL(ath_rate_node_copy);
+
+void
+ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
+	HAL_BOOL shortPreamble, size_t frameLen,
+	u_int8_t *rix, int *try0, u_int8_t *txrate)
+{
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+
+	*rix = on->on_tx_rix0;
+	*try0 = on->on_tx_try0;
+	if (shortPreamble)
+		*txrate = on->on_tx_rate0sp;
+	else
+		*txrate = on->on_tx_rate0;
+}
+EXPORT_SYMBOL(ath_rate_findrate);
+
+void
+ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an,
+	struct ath_desc *ds, HAL_BOOL shortPreamble, u_int8_t rix)
+{
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+
+	ath_hal_setupxtxdesc(sc->sc_ah, ds
+		, on->on_tx_rate1sp, 2	/* series 1 */
+		, on->on_tx_rate2sp, 2	/* series 2 */
+		, on->on_tx_rate3sp, 2	/* series 3 */
+	);
+}
+EXPORT_SYMBOL(ath_rate_setupxtxdesc);
+
+void
+ath_rate_tx_complete(struct ath_softc *sc,
+	struct ath_node *an, const struct ath_desc *ds)
+{
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+
+	if (ds->ds_txstat.ts_status == 0)
+		on->on_tx_ok++;
+	else
+		on->on_tx_err++;
+	on->on_tx_retr += ds->ds_txstat.ts_shortretry
+			+ ds->ds_txstat.ts_longretry;
+}
+EXPORT_SYMBOL(ath_rate_tx_complete);
+
+void
+ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
+{
+	if (isnew)
+		ath_rate_ctl_start(sc, &an->an_node);
+}
+EXPORT_SYMBOL(ath_rate_newassoc);
+
+static void
+ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
+{
+	struct ath_node *an = ATH_NODE(ni);
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	u_int8_t rix;
+
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+	DPRINTF(sc, "%s: set xmit rate for %s to %dM\n",
+	    __func__, ether_sprintf(ni->ni_macaddr),
+	    ni->ni_rates.rs_nrates > 0 ?
+		(ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
+
+	ni->ni_txrate = rate;
+	/* XXX management/control frames always go at the lowest speed */
+	an->an_tx_mgtrate = rt->info[0].rateCode;
+	an->an_tx_mgtratesp = an->an_tx_mgtrate | rt->info[0].shortPreamble;
+	/*
+	 * Before associating a node has no rate set setup
+	 * so we can't calculate any transmit codes to use.
+	 * This is ok since we should never be sending anything
+	 * but management frames and those always go at the
+	 * lowest hardware rate.
+	 */
+	if (ni->ni_rates.rs_nrates == 0)
+		goto done;
+	on->on_tx_rix0 = sc->sc_rixmap[
+		ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+	on->on_tx_rate0 = rt->info[on->on_tx_rix0].rateCode;
+	
+	on->on_tx_rate0sp = on->on_tx_rate0 |
+		rt->info[on->on_tx_rix0].shortPreamble;
+	if (sc->sc_mrretry) {
+		/*
+		 * Hardware supports multi-rate retry; setup two
+		 * step-down retry rates and make the lowest rate
+		 * be the ``last chance''.  We use 4, 2, 2, 2 tries
+		 * respectively (4 is set here, the rest are fixed
+		 * in the xmit routine).
+		 */
+		on->on_tx_try0 = 1 + 3;		/* 4 tries at rate 0 */
+		if (--rate >= 0) {
+			rix = sc->sc_rixmap[
+				ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+			on->on_tx_rate1 = rt->info[rix].rateCode;
+			on->on_tx_rate1sp = on->on_tx_rate1 |
+				rt->info[rix].shortPreamble;
+		} else {
+			on->on_tx_rate1 = on->on_tx_rate1sp = 0;
+		}
+		if (--rate >= 0) {
+			rix = sc->sc_rixmap[
+				ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+			on->on_tx_rate2 = rt->info[rix].rateCode;
+			on->on_tx_rate2sp = on->on_tx_rate2 |
+				rt->info[rix].shortPreamble;
+		} else {
+			on->on_tx_rate2 = on->on_tx_rate2sp = 0;
+		}
+		if (rate > 0) {
+			/* NB: only do this if we didn't already do it above */
+			on->on_tx_rate3 = rt->info[0].rateCode;
+			on->on_tx_rate3sp =
+				an->an_tx_mgtrate | rt->info[0].shortPreamble;
+		} else {
+			on->on_tx_rate3 = on->on_tx_rate3sp = 0;
+		}
+	} else {
+		on->on_tx_try0 = ATH_TXMAXTRY;	/* max tries at rate 0 */
+		on->on_tx_rate1 = on->on_tx_rate1sp = 0;
+		on->on_tx_rate2 = on->on_tx_rate2sp = 0;
+		on->on_tx_rate3 = on->on_tx_rate3sp = 0;
+	}
+done:
+	on->on_tx_ok = on->on_tx_err = on->on_tx_retr = on->on_tx_upper = 0;
+}
+
+/*
+ * Set the starting transmit rate for a node.
+ */
+static void
+ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+	struct ieee80211com *ic = &sc->sc_ic;
+	int srate;
+
+	KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
+	if (ic->ic_fixed_rate == -1) {
+		/*
+		 * No fixed rate is requested. For 11b start with
+		 * the highest negotiated rate; otherwise, for 11g
+		 * and 11a, we start "in the middle" at 24Mb or 36Mb.
+		 */
+		srate = ni->ni_rates.rs_nrates - 1;
+		if (sc->sc_curmode != IEEE80211_MODE_11B) {
+			/*
+			 * Scan the negotiated rate set to find the
+			 * closest rate.
+			 */
+			/* NB: the rate set is assumed sorted */
+			for (; srate >= 0 && RATE(srate) > 72; srate--)
+				;
+			KASSERT(srate >= 0, ("bogus rate set"));
+		}
+	} else {
+		/*
+		 * A fixed rate is to be used; ic_fixed_rate is an
+		 * index into the supported rate set.  Convert this
+		 * to the index into the negotiated rate set for
+		 * the node.  We know the rate is there because the
+		 * rate set is checked when the station associates.
+		 */
+		const struct ieee80211_rateset *rs =
+			&ic->ic_sup_rates[ic->ic_curmode];
+		int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
+		/* NB: the rate set is assumed sorted */
+		srate = ni->ni_rates.rs_nrates - 1;
+		for (; srate >= 0 && RATE(srate) != r; srate--)
+			;
+		KASSERT(srate >= 0,
+			("fixed rate %d not in rate set", ic->ic_fixed_rate));
+	}
+	ath_rate_update(sc, ni, srate);
+#undef RATE
+}
+
+/*
+ * Reset the rate control state for each 802.11 state transition.
+ */
+void
+ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+{
+	struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni;
+
+	if (state == IEEE80211_S_INIT) {
+		del_timer(&osc->timer);
+		return;
+	}
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		/*
+		 * Reset local xmit state; this is really only
+		 * meaningful when operating in station mode.
+		 */
+		ni = ic->ic_bss;
+		if (state == IEEE80211_S_RUN) {
+			ath_rate_ctl_start(sc, ni);
+		} else {
+			ath_rate_update(sc, ni, 0);
+		}
+	} else {
+		/*
+		 * When operating as a station the node table holds
+		 * the AP's that were discovered during scanning.
+		 * For any other operating mode we want to reset the
+		 * tx rate state of each node.
+		 */
+		TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+			ath_rate_update(sc, ni, 0);	/* use lowest rate */
+		ath_rate_update(sc, ic->ic_bss, 0);
+	}
+	if (ic->ic_fixed_rate == -1 && state == IEEE80211_S_RUN) {
+		int interval;
+		/*
+		 * Start the background rate control thread if we
+		 * are not configured to use a fixed xmit rate.
+		 */
+		interval = ath_rateinterval;
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			interval /= 2;
+		mod_timer(&osc->timer, jiffies + ((HZ * interval) / 1000));
+	}
+}
+EXPORT_SYMBOL(ath_rate_newstate);
+
+/* 
+ * Examine and potentially adjust the transmit rate.
+ */
+static void
+ath_rate_ctl(void *arg, struct ieee80211_node *ni)
+{
+	struct ath_softc *sc = arg;
+	struct onoe_node *on = ATH_NODE_ONOE(ATH_NODE(ni));
+	struct ieee80211_rateset *rs = &ni->ni_rates;
+	int dir = 0, nrate, enough;
+
+	/*
+	 * Rate control
+	 * XXX: very primitive version.
+	 */
+	enough = (on->on_tx_ok + on->on_tx_err >= 10);
+
+	/* no packet reached -> down */
+	if (on->on_tx_err > 0 && on->on_tx_ok == 0)
+		dir = -1;
+
+	/* all packets needs retry in average -> down */
+	if (enough && on->on_tx_ok < on->on_tx_retr)
+		dir = -1;
+
+	/* no error and less than rate_raise% of packets need retry -> up */
+	if (enough && on->on_tx_err == 0 &&
+	    on->on_tx_retr < (on->on_tx_ok * ath_rate_raise) / 100)
+		dir = 1;
+
+	DPRINTF(sc, "%s: ok %d err %d retr %d upper %d dir %d\n",
+		ether_sprintf(ni->ni_macaddr),
+		on->on_tx_ok, on->on_tx_err, on->on_tx_retr,
+		on->on_tx_upper, dir);
+
+	nrate = ni->ni_txrate;
+	switch (dir) {
+	case 0:
+		if (enough && on->on_tx_upper > 0)
+			on->on_tx_upper--;
+		break;
+	case -1:
+		if (nrate > 0) {
+			nrate--;
+			sc->sc_stats.ast_rate_drop++;
+		}
+		on->on_tx_upper = 0;
+		break;
+	case 1:
+		/* raise rate if we hit rate_raise_threshold */
+		if (++on->on_tx_upper < ath_rate_raise_threshold)
+			break;
+		on->on_tx_upper = 0;
+		if (nrate + 1 < rs->rs_nrates) {
+			nrate++;
+			sc->sc_stats.ast_rate_raise++;
+		}
+		break;
+	}
+
+	if (nrate != ni->ni_txrate) {
+		DPRINTF(sc, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n",
+		    __func__,
+		    (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL) / 2,
+		    (rs->rs_rates[nrate] & IEEE80211_RATE_VAL) / 2,
+		    on->on_tx_ok, on->on_tx_err, on->on_tx_retr);
+		ath_rate_update(sc, ni, nrate);
+	} else if (enough)
+		on->on_tx_ok = on->on_tx_err = on->on_tx_retr = 0;
+}
+
+static void
+ath_ratectl(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_softc *sc = dev->priv;
+	struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int interval;
+
+	if (dev->flags & IFF_RUNNING) {
+		sc->sc_stats.ast_rate_calls++;
+
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			ath_rate_ctl(sc, ic->ic_bss);	/* NB: no reference */
+		else
+			ieee80211_iterate_nodes(ic, ath_rate_ctl, sc);
+	}
+	interval = ath_rateinterval;
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		interval /= 2;
+	osc->timer.expires = jiffies + ((HZ * interval) / 1000);
+	add_timer(&osc->timer);
+}
+
+struct ath_ratectrl *
+ath_rate_attach(struct ath_softc *sc)
+{
+	struct onoe_softc *osc;
+
+	osc = kmalloc(sizeof(struct onoe_softc), GFP_ATOMIC);
+	if (osc == NULL)
+		return NULL;
+	osc->arc.arc_space = sizeof(struct onoe_node);
+	init_timer(&osc->timer);
+	osc->timer.data = (unsigned long) &sc->sc_dev;
+	osc->timer.function = ath_ratectl;
+
+	return &osc->arc;
+}
+EXPORT_SYMBOL(ath_rate_attach);
+
+void
+ath_rate_detach(struct ath_ratectrl *arc)
+{
+	struct onoe_softc *osc = (struct onoe_softc *) arc;
+
+	del_timer(&osc->timer);
+	kfree(osc);
+}
+EXPORT_SYMBOL(ath_rate_detach);
+
+static	int minrateinterval = 500;		/* 500ms */
+static	int maxpercent = 100;			/* 100% */
+static	int minpercent = 0;			/* 0% */
+static	int maxint = 0x7fffffff;		/* 32-bit big */
+
+#define	CTL_AUTO	-2	/* cannot be CTL_ANY or CTL_NONE */
+
+/*
+ * Static (i.e. global) sysctls.
+ */
+enum {
+	DEV_ATH		= 9,			/* XXX known by many */
+};
+
+static ctl_table ath_rate_static_sysctls[] = {
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "interval",
+	  .mode		= 0644,
+	  .data		= &ath_rateinterval,
+	  .maxlen	= sizeof(ath_rateinterval),
+	  .extra1	= &minrateinterval,
+	  .extra2	= &maxint,
+	  .proc_handler	= proc_dointvec_minmax
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "raise",
+	  .mode		= 0644,
+	  .data		= &ath_rate_raise,
+	  .maxlen	= sizeof(ath_rate_raise),
+	  .extra1	= &minpercent,
+	  .extra2	= &maxpercent,
+	  .proc_handler	= proc_dointvec_minmax
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "raise_threshold",
+	  .mode		= 0644,
+	  .data		= &ath_rate_raise_threshold,
+	  .maxlen	= sizeof(ath_rate_raise_threshold),
+	  .proc_handler	= proc_dointvec
+	},
+	{ 0 }
+};
+static ctl_table ath_rate_table[] = {
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "rate",
+	  .mode		= 0555,
+	  .child	= ath_rate_static_sysctls
+	}, { 0 }
+};
+static ctl_table ath_ath_table[] = {
+	{ .ctl_name	= DEV_ATH,
+	  .procname	= "ath",
+	  .mode		= 0555,
+	  .child	= ath_rate_table
+	}, { 0 }
+};
+static ctl_table ath_root_table[] = {
+	{ .ctl_name	= CTL_DEV,
+	  .procname	= "dev",
+	  .mode		= 0555,
+	  .child	= ath_ath_table
+	}, { 0 }
+};
+static struct ctl_table_header *ath_sysctl_header;
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("Atsushi Onoe's rate control algorithm for Atheros devices");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static char *version = "1.0";
+static char *dev_info = "ath_rate_onoe";
+
+static int __init
+init_ath_rate_onoe(void)
+{
+	printk(KERN_INFO "%s: %s\n", dev_info, version);
+
+#ifdef CONFIG_SYSCTL
+	ath_sysctl_header = register_sysctl_table(ath_root_table, 1);
+#endif
+	return (0);
+}
+module_init(init_ath_rate_onoe);
+
+static void __exit
+exit_ath_rate_onoe(void)
+{
+#ifdef CONFIG_SYSCTL
+	if (ath_sysctl_header != NULL)
+		unregister_sysctl_table(ath_sysctl_header);
+#endif
+
+	printk(KERN_INFO "%s: unloaded\n", dev_info);
+}
+module_exit(exit_ath_rate_onoe);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.h	2005-02-24 13:06:17.231142488 -0800
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: onoe.h,v 1.2 2005/02/22 14:17:22 otaku Exp $
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_RATE_ONOE_H
+#define _DEV_ATH_RATE_ONOE_H
+
+/* per-device state */
+struct onoe_softc {
+	struct ath_ratectrl arc;	/* base state */
+	struct timer_list timer;	/* periodic timer */
+};
+
+/* per-node state */
+struct onoe_node {
+	u_int		on_tx_ok;	/* tx ok pkt */
+	u_int		on_tx_err;	/* tx !ok pkt */
+	u_int		on_tx_retr;	/* tx retry count */
+	int		on_tx_upper;	/* tx upper rate req cnt */
+	u_int8_t	on_tx_rix0;	/* series 0 rate index */
+	u_int8_t	on_tx_try0;	/* series 0 try count */
+	u_int8_t	on_tx_rate0;	/* series 0 h/w rate */
+	u_int8_t	on_tx_rate1;	/* series 1 h/w rate */
+	u_int8_t	on_tx_rate2;	/* series 2 h/w rate */
+	u_int8_t	on_tx_rate3;	/* series 3 h/w rate */
+	u_int8_t	on_tx_rate0sp;	/* series 0 short preamble h/w rate */
+	u_int8_t	on_tx_rate1sp;	/* series 1 short preamble h/w rate */
+	u_int8_t	on_tx_rate2sp;	/* series 2 short preamble h/w rate */
+	u_int8_t	on_tx_rate3sp;	/* series 3 short preamble h/w rate */
+};
+#define	ATH_NODE_ONOE(an)	((struct onoe_node *)&an[1])
+#endif /* _DEV_ATH_RATE_ONOE_H */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Kconfig
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Kconfig	2005-02-24 13:06:17.188149024 -0800
@@ -0,0 +1,26 @@
+
+comment "Atheros 802.11(a/b/g) PCI/Cardbus support"
+
+config ATHEROS
+	tristate "Atheros PCI/Cardbus cards"
+	depends on PCI && NET_RADIO && EXPERIMENTAL && HOTPLUG
+	select ATHEROS_HAL
+	select ATHEROS_RATE
+	select NET80211
+       ---help---
+	  Say Y here if you intend to attach an Atheros Cardbus or PCI
+	  wireless Ethernet networking card to your computer.  This
+	  driver support the standard Linux Wireless Extensions.
+
+	  You will also very likely also need the Wireless Tools in order to
+	  configure your card and that /etc/pcmcia/wireless.opts works:
+	  <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ath_pci.  If unsure, say N.
+
+source "drivers/net/wireless/_ath_hal/Kconfig"
+
+source "drivers/net/wireless/_ath_rate/Kconfig"
+
+source "drivers/net/wireless/net80211/Kconfig"
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Makefile
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Makefile	2005-02-24 13:06:17.191148568 -0800
@@ -0,0 +1,30 @@
+#
+# Makefile for the Atheros WLAN driver.
+#
+
+
+HAL=	$(src)/../hal
+ATH_HAL=$(src)/../_ath_hal
+WLAN=	${src}/../net80211
+COMPAT=	${WLAN}/compat
+
+#
+# Select bus-specific code.  Note that this defaults to PCI.
+#
+ifeq ($(strip ${BUS}),AHB)
+BUSNAME=ahb
+EXTRA_CFLAGS+= -DATH_AHB
+else
+BUSNAME=pci
+EXTRA_CFLAGS+= -DATH_PCI
+endif
+
+INCS=	-include ${COMPAT}/compat.h -I${COMPAT}
+EXTRA_CFLAGS+=	${INCS} -I${HAL} -I${HAL}/linux -I${ATH_HAL} -I${WLAN} \
+	-I${src}/..
+
+obj-$(CONFIG_ATHEROS) += ath_${BUSNAME}.o
+ath_${BUSNAME}-objs	:= if_ath.o if_ath_${BUSNAME}.o
+
+
+
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath.c	2005-02-24 13:06:17.174151152 -0800
@@ -0,0 +1,4789 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_ath.c,v 1.37 2005/02/16 16:08:38 samleffler Exp $
+ */
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/delay.h>
+#include <linux/cache.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+
+#include <asm/uaccess.h>
+
+#include "if_ethersubr.h"		/* for ETHER_IS_MULTICAST */
+#include "if_media.h"
+#include "if_llc.h"
+
+#include <net80211/ieee80211_var.h>
+
+#define	AR_DEBUG
+#include "if_athvar.h"
+#include "ah_desc.h"
+#include "ah_devid.h"			/* XXX to identify IBM cards */
+#include "ah.h"
+
+#ifdef ATH_PCI		/* PCI BUS */
+#include "if_ath_pci.h"
+#endif			/* PCI BUS */
+#ifdef ATH_AHB		/* AHB BUS */
+#include "if_ath_ahb.h"
+#endif			/* AHB BUS */
+extern	void bus_read_cachesize(struct ath_softc *sc, u_int8_t *csz);
+
+/* unalligned little endian access */     
+#define LE_READ_2(p)							\
+	((u_int16_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)							\
+	((u_int32_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8) |	\
+	  (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
+
+static int	ath_init(struct net_device *);
+static int	ath_reset(struct net_device *);
+static void	ath_fatal_tasklet(TQUEUE_ARG);
+static void	ath_rxorn_tasklet(TQUEUE_ARG);
+static void	ath_bmiss_tasklet(TQUEUE_ARG);
+static int	ath_stop_locked(struct net_device *);
+static int	ath_stop(struct net_device *);
+static int	ath_media_change(struct net_device *);
+static void	ath_initkeytable(struct ath_softc *);
+static int	ath_key_alloc(struct ieee80211com *,
+			const struct ieee80211_key *);
+static int	ath_key_delete(struct ieee80211com *,
+			const struct ieee80211_key *);
+static int	ath_key_set(struct ieee80211com *, const struct ieee80211_key *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+static void	ath_key_update_begin(struct ieee80211com *);
+static void	ath_key_update_end(struct ieee80211com *);
+static void	ath_mode_init(struct net_device *);
+static void	ath_setslottime(struct ath_softc *);
+static void	ath_updateslot(struct net_device *);
+static int	ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
+static void	ath_beacon_tasklet(struct net_device *);
+static void	ath_beacon_free(struct ath_softc *);
+static void	ath_beacon_config(struct ath_softc *);
+static int	ath_desc_alloc(struct ath_softc *);
+static void	ath_desc_free(struct ath_softc *);
+static struct ieee80211_node *ath_node_alloc(struct ieee80211com *);
+static void	ath_node_cleanup(struct ieee80211com *,
+			struct ieee80211_node *);
+static void	ath_node_copy(struct ieee80211com *,
+			struct ieee80211_node *, const struct ieee80211_node *);
+static u_int8_t	ath_node_getrssi(struct ieee80211com *,
+			struct ieee80211_node *);
+static int	ath_rxbuf_init(struct ath_softc *, struct ath_buf *);
+static void	ath_recv_mgmt(struct ieee80211com *, struct sk_buff *,
+			struct ieee80211_node *,
+			int subtype, int rssi, u_int32_t rstamp);
+static void	ath_rx_tasklet(TQUEUE_ARG data);
+static int	ath_hardstart(struct sk_buff *, struct net_device *);
+static int	ath_mgtstart(struct ieee80211com *ic, struct sk_buff *skb);
+static int	ath_tx_setup(struct ath_softc *, int ac, int txq);
+static int	ath_tx_start(struct net_device *, struct ieee80211_node *,
+			     struct ath_buf *, struct sk_buff *);
+static void	ath_tx_tasklet_q0(TQUEUE_ARG data);
+static void	ath_tx_tasklet_q0123(TQUEUE_ARG data);
+static void	ath_tx_tasklet(TQUEUE_ARG data);
+static void	ath_tx_timeout(struct net_device *);
+static int	ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
+static void	ath_draintxq(struct ath_softc *);
+static void	ath_stoprecv(struct ath_softc *);
+static int	ath_startrecv(struct ath_softc *);
+static void	ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
+static void	ath_next_scan(unsigned long);
+static void	ath_calibrate(unsigned long);
+static int	ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
+#if IEEE80211_VLAN_TAG_USED
+static void	ath_vlan_register(struct net_device *, struct vlan_group *);
+static void	ath_vlan_kill_vid(struct net_device *, unsigned short );
+#endif
+static struct net_device_stats *ath_getstats(struct net_device *);
+#ifdef CONFIG_NET_WIRELESS
+static struct iw_statistics *ath_iw_getstats(struct net_device *);
+static struct iw_handler_def ath_iw_handler_def;
+#endif
+static void	ath_newassoc(struct ieee80211com *,
+			struct ieee80211_node *, int);
+static int	ath_getchannels(struct net_device *, u_int cc,
+			HAL_BOOL outdoor, HAL_BOOL xchanmode);
+static void	ath_update_led(struct ath_softc *);
+
+static int	ath_set_mac_address(struct net_device *, void *);
+static int	ath_change_mtu(struct net_device *, int);
+static int	ath_ioctl(struct net_device *, struct ifreq *, int);
+
+static int	ath_rate_setup(struct net_device *, u_int mode);
+static void	ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
+
+#ifdef CONFIG_SYSCTL
+static void	ath_dynamic_sysctl_register(struct ath_softc *);
+static void	ath_dynamic_sysctl_unregister(struct ath_softc *);
+#endif /* CONFIG_SYSCTL */
+static void	ath_announce(struct net_device *);
+
+static const char *acnames[] = {
+	"WME_AC_BE",
+	"WME_AC_BK",
+	"WME_AC_VI",
+	"WME_AC_VO",
+	"WME_UPSD",
+};
+
+static const char *hal_status_desc[] = {
+	"No error",
+	"No hardware present or device not yet supported",
+	"Memory allocation failed",
+	"Hardware didn't respond as expected",
+	"EEPROM magic number invalid",
+	"EEPROM version invalid",
+	"EEPROM unreadable",
+	"EEPROM checksum invalid",
+	"EEPROM read problem",
+	"EEPROM mac address invalid",
+	"EEPROM size not supported",
+	"Attempt to change write-locked EEPROM",
+	"Invalid parameter to function",
+	"Hardware revision not supported",
+	"Hardware self-test failed",
+	"Operation incomplete"
+};
+
+static	int ath_dwelltime = 200;		/* 5 channels/second */
+static	int ath_calinterval = 30;		/* calibrate every 30 secs */
+static	int ath_countrycode = CTRY_DEFAULT;	/* country code */
+static	int ath_regdomain = 0;			/* regulatory domain */
+static	int ath_outdoor = AH_FALSE;		/* enable outdoor use */
+static	int ath_xchanmode = AH_TRUE;		/* enable extended channels */
+
+#ifdef AR_DEBUG
+int	ath_debug = 0;
+#define	IFF_DUMPPKTS(sc, _m) \
+	((sc->sc_debug & _m) || ieee80211_msg_dumppkts(&sc->sc_ic))
+static	void ath_printrxbuf(struct ath_buf *bf, int);
+static	void ath_printtxbuf(struct ath_buf *bf, int);
+enum {
+	ATH_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
+	ATH_DEBUG_XMIT_DESC	= 0x00000002,	/* xmit descriptors */
+	ATH_DEBUG_RECV		= 0x00000004,	/* basic recv operation */
+	ATH_DEBUG_RECV_DESC	= 0x00000008,	/* recv descriptors */
+	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
+	ATH_DEBUG_RESET		= 0x00000020,	/* reset processing */
+	ATH_DEBUG_MODE		= 0x00000040,	/* mode init/setup */
+	ATH_DEBUG_BEACON 	= 0x00000080,	/* beacon handling */
+	ATH_DEBUG_WATCHDOG 	= 0x00000100,	/* watchdog timeout */
+	ATH_DEBUG_INTR		= 0x00001000,	/* ISR */
+	ATH_DEBUG_TX_PROC	= 0x00002000,	/* tx ISR proc */
+	ATH_DEBUG_RX_PROC	= 0x00004000,	/* rx ISR proc */
+	ATH_DEBUG_BEACON_PROC	= 0x00008000,	/* beacon ISR proc */
+	ATH_DEBUG_CALIBRATE	= 0x00010000,	/* periodic calibration */
+	ATH_DEBUG_KEYCACHE	= 0x00020000,	/* key cache management */
+	ATH_DEBUG_STATE		= 0x00040000,	/* 802.11 state transitions */
+	ATH_DEBUG_NODE		= 0x00080000,	/* node management */
+	ATH_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
+	ATH_DEBUG_ANY		= 0xffffffff
+};
+#define	DPRINTF(sc, _m, _fmt, ...) do {				\
+	if (sc->sc_debug & _m)					\
+		printk(_fmt, __VA_ARGS__);			\
+} while (0)
+#define	KEYPRINTF(sc, ix, hk, mac) do {				\
+	if (sc->sc_debug & ATH_DEBUG_KEYCACHE)			\
+		ath_keyprint(__func__, ix, hk, mac);		\
+} while (0)
+#else
+#define	IFF_DUMPPKTS(sc, _m)	netif_msg_dumppkts(&sc->sc_ic)
+#define	DPRINTF(sc, _m, _fmt, ...)
+#define	KEYPRINTF(sc, k, ix, mac)
+#endif
+
+static	int countrycode = -1;
+MODULE_PARM(countrycode, "i");
+MODULE_PARM_DESC(countrycode, "Override default country code");
+static	int outdoor = -1;
+MODULE_PARM(outdoor, "i");
+MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use");
+static	int xchanmode = -1;
+MODULE_PARM(xchanmode, "i");
+MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode");
+
+int
+ath_attach(u_int16_t devid, struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah;
+	HAL_STATUS status;
+	int error = 0, i;
+	u_int8_t csz;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	bus_read_cachesize(sc, &csz);
+	/* XXX assert csz is non-zero */
+	sc->sc_cachelsz = csz << 2;		/* convert to bytes */
+
+	ATH_LOCK_INIT(sc);
+	ATH_TXBUF_LOCK_INIT(sc);
+
+	ATH_INIT_TQUEUE(&sc->sc_rxtq,	ath_rx_tasklet,		dev);
+	ATH_INIT_TQUEUE(&sc->sc_txtq,	ath_tx_tasklet,		dev);
+	ATH_INIT_TQUEUE(&sc->sc_bmisstq,ath_bmiss_tasklet,	dev);
+	ATH_INIT_TQUEUE(&sc->sc_rxorntq,ath_rxorn_tasklet,	dev);
+	ATH_INIT_TQUEUE(&sc->sc_fataltq,ath_fatal_tasklet,	dev);
+
+	/*
+	 * Attach the hal and verify ABI compatibility by checking
+	 * the hal's ABI signature against the one the driver was
+	 * compiled with.  A mismatch indicates the driver was
+	 * built with an ah.h that does not correspond to the hal
+	 * module loaded in the kernel.
+	 */
+	ah = _ath_hal_attach(devid, sc, 0, (void *) dev->mem_start, &status);
+	if (ah == NULL) {
+		printk(KERN_ERR "%s: unable to attach hardware: '%s' (HAL status %u)\n",
+			dev->name, hal_status_desc[status], status);
+		error = ENXIO;
+		goto bad;
+	}
+	if (ah->ah_abi != HAL_ABI_VERSION) {
+		printk(KERN_ERR "%s: HAL ABI mismatch; "
+			"driver expects 0x%x, HAL reports 0x%x\n",
+			dev->name, HAL_ABI_VERSION, ah->ah_abi);
+		error = ENXIO;		/* XXX */
+		goto bad;
+	}
+	sc->sc_ah = ah;
+
+	/*
+	 * Check if the MAC has multi-rate retry support.
+	 * We do this by trying to setup a fake extended
+	 * descriptor.  MAC's that don't have support will
+	 * return false w/o doing anything.  MAC's that do
+	 * support it will return true w/o doing anything.
+	 */
+	sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0);
+
+	/*
+	 * Check if the device has hardware counters for PHY
+	 * errors.  If so we need to enable the MIB interrupt
+	 * so we can act on stat triggers.
+	 */
+	if (ath_hal_hwphycounters(ah))
+		sc->sc_needmib = 1;
+
+	/*
+	 * Get the hardware key cache size.
+	 */
+	sc->sc_keymax = ath_hal_keycachesize(ah);
+	if (sc->sc_keymax > sizeof(sc->sc_keymap) * NBBY) {
+		printk("%s: Warning, using only %u entries in %u key cache\n",
+			dev->name, sizeof(sc->sc_keymap) * NBBY, sc->sc_keymax);
+		sc->sc_keymax = sizeof(sc->sc_keymap) * NBBY;
+	}
+	/*
+	 * Reset the key cache since some parts do not
+	 * reset the contents on initial power up.
+	 */
+	for (i = 0; i < sc->sc_keymax; i++)
+		ath_hal_keyreset(ah, i);
+	/*
+	 * Mark key cache slots associated with global keys
+	 * as in use.  If we knew TKIP was not to be used we
+	 * could leave the +32, +64, and +32+64 slots free.
+	 * XXX only for splitmic.
+	 */
+	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+		setbit(sc->sc_keymap, i);
+		setbit(sc->sc_keymap, i+32);
+		setbit(sc->sc_keymap, i+64);
+		setbit(sc->sc_keymap, i+32+64);
+	}
+
+	/*
+	 * Collect the channel list using the default country
+	 * code and including outdoor channels.  The 802.11 layer
+	 * is resposible for filtering this list based on settings
+	 * like the phy mode.
+	 */
+	if (countrycode != -1)
+		ath_countrycode = countrycode;
+	if (outdoor != -1)
+		ath_outdoor = outdoor;
+	if (xchanmode != -1)
+		ath_xchanmode = xchanmode;
+	error = ath_getchannels(dev, ath_countrycode,
+			ath_outdoor, ath_xchanmode);
+	if (error != 0)
+		goto bad;
+
+	/*
+	 * Setup rate tables for all potential media types.
+	 */
+	ath_rate_setup(dev, IEEE80211_MODE_11A);
+	ath_rate_setup(dev, IEEE80211_MODE_11B);
+	ath_rate_setup(dev, IEEE80211_MODE_11G);
+	ath_rate_setup(dev, IEEE80211_MODE_TURBO);
+	/* NB: setup here so ath_rate_update is happy */
+	ath_setcurmode(sc, IEEE80211_MODE_11A);
+
+	/*
+	 * Allocate tx+rx descriptors and populate the lists.
+	 */
+	error = ath_desc_alloc(sc);
+	if (error != 0) {
+		printk(KERN_ERR "%s: failed to allocate descriptors: %d\n",
+			dev->name, error);
+		goto bad;
+	}
+
+	/*
+	 * Allocate hardware transmit queues: one queue for
+	 * beacon frames and one data queue for each QoS
+	 * priority.  Note that the hal handles reseting
+	 * these queues at the needed time.
+	 *
+	 * XXX PS-Poll
+	 */
+	sc->sc_bhalq = ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, NULL);
+	if (sc->sc_bhalq == (u_int) -1) {
+		printk(KERN_ERR "%s: unable to setup a beacon xmit queue!\n",
+			dev->name);
+		error = EIO;
+		goto bad2;
+	}
+	/* NB: insure BK queue is h/w queue 0 */
+	if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BK) ||
+	    !ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BE) ||
+	    !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) ||
+	    !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) {
+		error = EIO;
+		goto bad2;
+	}
+	/* 
+	 * Special case certain configurations.
+	 */
+	switch (sc->sc_txqsetup) {
+	case 0x01:
+		ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet_q0, dev);
+		break;
+	case 0x0f:
+		ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet_q0123, dev);
+		break;
+	}
+
+	sc->sc_rc = ath_rate_attach(sc);
+	if (sc->sc_rc == NULL) {
+		error = EIO;
+		goto bad2;
+	}
+
+	init_timer(&sc->sc_scan_ch);
+	sc->sc_scan_ch.function = ath_next_scan;
+	sc->sc_scan_ch.data = (unsigned long) dev;
+
+	init_timer(&sc->sc_cal_ch);
+	sc->sc_cal_ch.function = ath_calibrate;
+	sc->sc_cal_ch.data = (unsigned long) dev;
+
+	sc->sc_ledstate = 1;
+	/*
+	 * Auto-enable soft led processing for IBM cards and for
+	 * 5211 minipci cards.  Users can also manually enable/disable
+	 * support with a sysctl.
+	 */
+	sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID);
+	if (sc->sc_softled) {
+		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+		ath_hal_gpioset(ah, sc->sc_ledpin, 0);
+	}
+
+	ether_setup(dev);
+	dev->open = ath_init;
+	dev->stop = ath_stop;
+	dev->hard_start_xmit = ath_hardstart;
+	dev->tx_timeout = ath_tx_timeout;
+	dev->watchdog_timeo = 5 * HZ;			/* XXX */
+	dev->set_multicast_list = ath_mode_init;
+	dev->do_ioctl = ath_ioctl;
+	dev->get_stats = ath_getstats;
+	dev->set_mac_address = ath_set_mac_address;
+ 	dev->change_mtu = &ath_change_mtu;
+	dev->tx_queue_len = ATH_TXBUF-1;		/* 1 for mgmt frame */
+#ifdef CONFIG_NET_WIRELESS
+	dev->get_wireless_stats = ath_iw_getstats;
+	ieee80211_ioctl_iwsetup(&ath_iw_handler_def);
+	dev->wireless_handlers = &ath_iw_handler_def;
+#endif /* CONFIG_NET_WIRELESS */
+#if IEEE80211_VLAN_TAG_USED
+	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+	dev->vlan_rx_register = ath_vlan_register;
+	dev->vlan_rx_kill_vid = ath_vlan_kill_vid;
+#endif /* IEEE80211_VLAN_TAG_USED */
+
+	ic->ic_dev = dev;
+	ic->ic_devstats = &sc->sc_devstats;
+	ic->ic_mgtstart = ath_mgtstart;
+	ic->ic_init = ath_init;
+	ic->ic_reset = ath_reset;
+	ic->ic_newassoc = ath_newassoc;
+	ic->ic_updateslot = ath_updateslot;
+	/* XXX not right but it's not used anywhere important */
+	ic->ic_phytype = IEEE80211_T_OFDM;
+	ic->ic_opmode = IEEE80211_M_STA;
+	ic->ic_caps =
+		  IEEE80211_C_IBSS		/* ibss, nee adhoc, mode */
+		| IEEE80211_C_HOSTAP		/* hostap mode */
+		| IEEE80211_C_MONITOR		/* monitor mode */
+		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
+		| IEEE80211_C_SHSLOT		/* short slot time supported */
+		| IEEE80211_C_TXPMGT		/* transmit power control */
+		;
+	/*
+	 * Query the hal to figure out h/w crypto support.
+	 */
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP))
+		ic->ic_caps |= IEEE80211_C_WEP;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB))
+		ic->ic_caps |= IEEE80211_C_AES;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM))
+		ic->ic_caps |= IEEE80211_C_AES_CCM;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP))
+		ic->ic_caps |= IEEE80211_C_CKIP;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) {
+		ic->ic_caps |= IEEE80211_C_TKIP;
+		/*
+		 * Check if h/w does the MIC and/or whether the
+		 * separate key cache entries are required to
+		 * handle both tx+rx MIC keys.
+		 */
+		if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
+			ic->ic_caps |= IEEE80211_C_TKIPMIC;
+		if (ath_hal_tkipsplit(ah))
+			sc->sc_splitmic = 1;
+	}
+	/*
+	 * Indicate we need the 802.11 header padded to a
+	 * 32-bit boundary for 4-address and QoS frames.
+	 */
+	ic->ic_flags |= IEEE80211_F_DATAPAD;
+
+	/*
+	 * Query the hal about antenna support.
+	 */
+	if (ath_hal_hasdiversity(ah)) {
+		sc->sc_hasdiversity = 1;
+		sc->sc_diversity = ath_hal_getdiversity(ah);
+	}
+	sc->sc_defant = ath_hal_getdefantenna(ah);
+
+	/* get mac address from hardware */
+	ath_hal_getmac(ah, ic->ic_myaddr);
+	IEEE80211_ADDR_COPY(dev->dev_addr, ic->ic_myaddr);
+
+	/* call MI attach routine. */
+	ieee80211_ifattach(ic);
+	/* override default methods */
+	ic->ic_node_alloc = ath_node_alloc;
+	sc->sc_node_cleanup = ic->ic_node_cleanup;
+	ic->ic_node_cleanup = ath_node_cleanup;
+	sc->sc_node_copy = ic->ic_node_copy;
+	ic->ic_node_copy = ath_node_copy;
+	ic->ic_node_getrssi = ath_node_getrssi;
+	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
+	ic->ic_recv_mgmt = ath_recv_mgmt;
+	sc->sc_newstate = ic->ic_newstate;
+	ic->ic_newstate = ath_newstate;
+	ic->ic_crypto.cs_key_alloc = ath_key_alloc;
+	ic->ic_crypto.cs_key_delete = ath_key_delete;
+	ic->ic_crypto.cs_key_set = ath_key_set;
+	ic->ic_crypto.cs_key_update_begin = ath_key_update_begin;
+	ic->ic_crypto.cs_key_update_end = ath_key_update_end;
+
+	/* complete initialization */
+	ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
+
+	if (register_netdev(dev)) {
+		printk(KERN_ERR "%s: unable to register device\n", dev->name);
+		goto bad3;
+	}
+	/*
+	 * Attach dynamic MIB vars and announce support
+	 * now that we have a device name with unit number.
+	 */
+#ifdef CONFIG_SYSCTL
+	ath_dynamic_sysctl_register(sc);
+	ieee80211_sysctl_register(ic);
+#endif /* CONFIG_SYSCTL */
+	ieee80211_announce(ic);
+	ath_announce(dev);
+	return 0;
+bad3:
+	ieee80211_ifdetach(ic);
+	ath_rate_detach(sc->sc_rc);
+bad2:
+	if (sc->sc_txq[WME_AC_BK].axq_qnum != (u_int) -1)
+		ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_BK]);
+	if (sc->sc_txq[WME_AC_BE].axq_qnum != (u_int) -1)
+		ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_BE]);
+	if (sc->sc_txq[WME_AC_VI].axq_qnum != (u_int) -1)
+		ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_VI]);
+	if (sc->sc_txq[WME_AC_VO].axq_qnum != (u_int) -1)
+		ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_VO]);
+	ath_desc_free(sc);
+bad:
+	if (ah)
+		ath_hal_detach(ah);
+	ATH_TXBUF_LOCK_DESTROY(sc);
+	ATH_LOCK_DESTROY(sc);
+	sc->sc_invalid = 1;
+	return error;
+}
+
+int
+ath_detach(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int i;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags);
+	ath_stop(dev);
+	sc->sc_invalid = 1;
+	/*
+	 * NB: Must do this before detaching the hal to insure
+	 *     callbacks into the driver to delete global key
+	 *     entries can be handled.
+	 */
+	ieee80211_ifdetach(ic);
+	ath_rate_detach(sc->sc_rc);
+	ath_desc_free(sc);
+	ath_hal_detach(sc->sc_ah);
+
+	/*
+	 * NB: can't reclaim these until after ieee80211_ifdetach
+	 * returns because we'll get called back to reclaim node
+	 * state and potentially want to use them.
+	 */
+	ATH_TXBUF_LOCK_DESTROY(sc);
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i))
+			ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[i]);
+
+#ifdef CONFIG_SYSCTL
+	ath_dynamic_sysctl_unregister(sc);
+#endif /* CONFIG_SYSCTL */
+	ATH_LOCK_DESTROY(sc);
+	unregister_netdev(dev);
+
+	return 0;
+}
+
+void
+ath_suspend(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags);
+	ath_stop(dev);
+}
+
+void
+ath_resume(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags);
+	ath_init(dev);
+}
+
+void
+ath_shutdown(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags);
+	ath_stop(dev);
+}
+
+/*
+ * Interrupt handler.  Most of the actual processing is deferred.
+ */
+irqreturn_t
+ath_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_INT status;
+	int needmark;
+
+	if (sc->sc_invalid) {
+		/*
+		 * The hardware is not ready/present, don't touch anything.
+		 * Note this can happen early on if the IRQ is shared.
+		 */
+		return IRQ_NONE;
+	}
+	if (!ath_hal_intrpend(ah))		/* shared irq, not for us */
+		return IRQ_NONE;
+	if ((dev->flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP)) {
+		DPRINTF(sc, ATH_DEBUG_INTR, "%s: flags 0x%x\n",
+			__func__, dev->flags);
+		ath_hal_getisr(ah, &status);	/* clear ISR */
+		ath_hal_intrset(ah, 0);		/* disable further intr's */
+		return IRQ_HANDLED;
+	}
+	needmark = 0;
+	/*
+	 * Figure out the reason(s) for the interrupt.  Note
+	 * that the hal returns a pseudo-ISR that may include
+	 * bits we haven't explicitly enabled so we mask the
+	 * value to insure we only process bits we requested.
+	 */
+	ath_hal_getisr(ah, &status);		/* NB: clears ISR too */
+	DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status);
+	status &= sc->sc_imask;			/* discard unasked for bits */
+	if (status & HAL_INT_FATAL) {
+		/*
+		 * Fatal errors are unrecoverable.  Typically
+		 * these are caused by DMA errors.  Unfortunately
+		 * the exact reason is not (presently) returned
+		 * by the hal.
+		 */
+		sc->sc_stats.ast_hardware++;
+		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
+		ATH_SCHEDULE_TQUEUE(&sc->sc_fataltq, &needmark);
+	} else if (status & HAL_INT_RXORN) {
+		sc->sc_stats.ast_rxorn++;
+		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
+		ATH_SCHEDULE_TQUEUE(&sc->sc_rxorntq, &needmark);
+	} else {
+		if (status & HAL_INT_RXEOL) {
+			/*
+			 * NB: the hardware should re-read the link when
+			 *     RXE bit is written, but it doesn't work at
+			 *     least on older hardware revs.
+			 */
+			sc->sc_stats.ast_rxeol++;
+			sc->sc_rxlink = NULL;
+		}
+		if (status & HAL_INT_TXURN) {
+			sc->sc_stats.ast_txurn++;
+			/* bump tx trigger level */
+			ath_hal_updatetxtriglevel(ah, AH_TRUE);
+		}
+		if (status & HAL_INT_RX)
+			ATH_SCHEDULE_TQUEUE(&sc->sc_rxtq, &needmark);
+		if (status & HAL_INT_TX)
+			ATH_SCHEDULE_TQUEUE(&sc->sc_txtq, &needmark);
+		if (status & HAL_INT_SWBA) {
+			/*
+			 * Software beacon alert--time to send a beacon.
+			 * Handle beacon transmission directly; deferring
+			 * this is too slow to meet timing constraints
+			 * under load.
+			 */
+			ath_beacon_tasklet(dev);
+		}
+		if (status & HAL_INT_BMISS) {
+			sc->sc_stats.ast_bmiss++;
+			ATH_SCHEDULE_TQUEUE(&sc->sc_bmisstq, &needmark);
+		}
+		if (status & HAL_INT_MIB) {
+			sc->sc_stats.ast_mib++;
+			/*
+			 * Disable interrupts until we service the MIB
+			 * interrupt; otherwise it will continue to fire.
+			 */
+			ath_hal_intrset(ah, 0);
+			/*
+			 * Let the hal handle the event.  We assume it will
+			 * clear whatever condition caused the interrupt.
+			 */
+			ath_hal_mibevent(ah,
+				&ATH_NODE(sc->sc_ic.ic_bss)->an_halstats);
+			ath_hal_intrset(ah, sc->sc_imask);
+		}
+	}
+	if (needmark)
+		mark_bh(IMMEDIATE_BH);
+	return IRQ_HANDLED;
+}
+
+static void
+ath_fatal_tasklet(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+
+	printk("%s: hardware error; reseting\n", dev->name);
+	ath_reset(dev);
+}
+
+static void
+ath_rxorn_tasklet(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+
+	printk("%s: rx FIFO overrun; reseting\n", dev->name);
+	ath_reset(dev);
+}
+
+static void
+ath_bmiss_tasklet(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s\n", __func__);
+	KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+		("unexpect operating mode %u", ic->ic_opmode));
+
+	if (ic->ic_state == IEEE80211_S_RUN) {
+		/*
+		 * Rather than go directly to scan state, try to
+		 * reassociate first.  If that fails then the state
+		 * machine will drop us into scanning after timing
+		 * out waiting for a probe response.
+		 */
+		ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
+	}
+}
+
+static u_int
+ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	static const u_int modeflags[] = {
+		0,			/* IEEE80211_MODE_AUTO */
+		CHANNEL_A,		/* IEEE80211_MODE_11A */
+		CHANNEL_B,		/* IEEE80211_MODE_11B */
+		CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
+		0,			/* IEEE80211_MODE_FH */
+		CHANNEL_T		/* IEEE80211_MODE_TURBO */
+	};
+	enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan);
+
+	KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode));
+	KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode));
+	return modeflags[mode];
+#undef N
+}
+
+static int
+ath_init(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni;
+	enum ieee80211_phymode mode;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_STATUS status;
+	int error = 0;
+
+	ATH_LOCK(sc);
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: mode %d\n", __func__, ic->ic_opmode);
+
+	/*
+	 * Stop anything previously setup.  This is safe
+	 * whether this is the first time through or not.
+	 */
+	ath_stop_locked(dev);
+
+	/*
+	 * Resize receive skb's if changing to or from monitor mode
+	 */
+	if ((dev->type == ARPHRD_ETHER &&
+	     ic->ic_opmode == IEEE80211_M_MONITOR) ||
+	    (dev->type == ARPHRD_IEEE80211_PRISM &&
+	     ic->ic_opmode != IEEE80211_M_MONITOR)) {
+		struct ath_buf *bf;
+		STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list)
+			if (bf->bf_skb != NULL) {
+				bus_unmap_single(sc->sc_bdev,
+					bf->bf_skbaddr, sc->sc_rxbufsize,
+					BUS_DMA_FROMDEVICE);
+				dev_kfree_skb(bf->bf_skb);
+				bf->bf_skb = NULL;
+			}
+	}
+	/*
+	 * Change our interface type if we are in monitor mode.
+	 */
+	dev->type = (ic->ic_opmode == IEEE80211_M_MONITOR) ?
+		ARPHRD_IEEE80211_PRISM : ARPHRD_ETHER;
+
+	/*
+	 * The basic interface to setting the hardware in a good
+	 * state is ``reset''.  On return the hardware is known to
+	 * be powered up and with interrupts disabled.  This must
+	 * be followed by initialization of the appropriate bits
+	 * and then setup of the interrupt mask.
+	 */
+	sc->sc_curchan.channel = ic->ic_ibss_chan->ic_freq;
+	sc->sc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_ibss_chan);
+	if (!ath_hal_reset(ah, ic->ic_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
+		printk("%s: unable to reset hardware: '%s' (HAL status %u)\n",
+			dev->name, hal_status_desc[status], status);
+		error = -EIO;
+		goto done;
+	}
+
+	/*
+	 * Setup the hardware after reset: the key cache
+	 * is filled as needed and the receive engine is
+	 * set going.  Frame transmit is handled entirely
+	 * in the frame output path; there's nothing to do
+	 * here except setup the interrupt mask.
+	 */
+	ath_initkeytable(sc);		/* XXX still needed? */
+	if (ath_startrecv(sc) != 0) {
+		printk("%s: unable to start recv logic\n", dev->name);
+		error = -EIO;
+		goto done;
+	}
+
+	/*
+	 * Enable interrupts.
+	 */
+	sc->sc_imask = HAL_INT_RX | HAL_INT_TX
+		  | HAL_INT_RXEOL | HAL_INT_RXORN
+		  | HAL_INT_FATAL | HAL_INT_GLOBAL;
+	/*
+	 * Enable MIB interrupts when there are hardware phy counters.
+	 * Note we only do this (at the moment) for station mode.
+	 */
+	if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
+		sc->sc_imask |= HAL_INT_MIB;
+	ath_hal_intrset(ah, sc->sc_imask);
+
+	dev->flags |= IFF_RUNNING;
+	ic->ic_state = IEEE80211_S_INIT;
+
+	/*
+	 * The hardware should be ready to go now so it's safe
+	 * to kick the 802.11 state machine as it's likely to
+	 * immediately call back to us to send mgmt frames.
+	 */
+	ni = ic->ic_bss;
+	ni->ni_chan = ic->ic_ibss_chan;
+	mode = ieee80211_chan2mode(ic, ni->ni_chan);
+	if (mode != sc->sc_curmode)
+		ath_setcurmode(sc, mode);
+	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+	} else
+		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+done:
+	ATH_UNLOCK(sc);
+	return error;
+}
+
+static int
+ath_stop_locked(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: invalid %u flags 0x%x\n",
+		__func__, sc->sc_invalid, dev->flags);
+
+	if (dev->flags & IFF_RUNNING) {
+		/*
+		 * Shutdown the hardware and driver:
+		 *    reset 802.11 state machine (do first so
+		 *	station deassoc/deauth frames can be sent)
+		 *    stop output from above
+		 *    disable interrupts
+		 *    turn off timers
+		 *    turn off the radio
+		 *    clear transmit machinery
+		 *    clear receive machinery
+		 *    reclaim beacon resources
+		 *    power down hardware
+		 *
+		 * Note that some of this work is not possible if the
+		 * hardware is gone (invalid).
+		 */
+		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+		netif_stop_queue(dev);
+		dev->flags &= ~IFF_RUNNING;
+		if (!sc->sc_invalid) {
+			if (sc->sc_softled)
+				ath_hal_gpioset(ah, sc->sc_ledpin, 1);
+			ath_hal_intrset(ah, 0);
+		}
+		ath_draintxq(sc);
+		if (!sc->sc_invalid) {
+			ath_stoprecv(sc);
+			ath_hal_phydisable(ah);
+		} else
+			sc->sc_rxlink = NULL;
+		ath_beacon_free(sc);
+	}
+	return 0;
+}
+
+/*
+ * Stop the device, grabbing the top-level lock to protect
+ * against concurrent entry through ath_init (which can happen
+ * if another thread does a system call and the thread doing the
+ * stop is preempted).
+ */
+static int
+ath_stop(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	int error;
+
+	ATH_LOCK(sc);
+	error = ath_stop_locked(dev);
+	if (error == 0 && !sc->sc_invalid) {
+		/*
+		 * Set the chip in full sleep mode.  Note that we are
+		 * careful to do this only when bringing the interface
+		 * completely to a stop.  When the chip is in this state
+		 * it must be carefully woken up or references to
+		 * registers in the PCI clock domain may freeze the bus
+		 * (and system).  This varies by chip and is mostly an
+		 * issue with newer parts that go to sleep more quickly.
+		 */
+		ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP, 0);
+	}
+	ATH_UNLOCK(sc);
+	return error;
+}
+
+/*
+ * Reset the hardware w/o losing operational state.  This is
+ * basically a more efficient way of doing ath_stop, ath_init,
+ * followed by state transitions to the current 802.11
+ * operational state.  Used to recover from errors rx overrun
+ * and to reset the hardware when rf gain settings must be reset.
+ */
+static int
+ath_reset(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211_channel *c;
+	HAL_STATUS status;
+
+	/*
+	 * Convert to a HAL channel description with the flags
+	 * constrained to reflect the current operating mode.
+	 */
+	c = ic->ic_ibss_chan;
+	sc->sc_curchan.channel = c->ic_freq;
+	sc->sc_curchan.channelFlags = ath_chan2flags(ic, c);
+
+	ath_hal_intrset(ah, 0);		/* disable interrupts */
+	ath_draintxq(sc);		/* stop xmit side */
+	ath_stoprecv(sc);		/* stop recv side */
+	/* NB: indicate channel change so we do a full reset */
+	if (!ath_hal_reset(ah, ic->ic_opmode, &sc->sc_curchan, AH_TRUE, &status))
+		printk("%s: %s: unable to reset hardware: '%s' (HAL status %u)\n",
+			dev->name, __func__, hal_status_desc[status], status);
+	if (ath_startrecv(sc) != 0)	/* restart recv */
+		printk("%s: %s: unable to start recv logic\n",
+			dev->name, __func__);
+	/*
+	 * We may be doing a reset in response to an ioctl
+	 * that changes the channel so update any state that
+	 * might change as a result.
+	 */
+	ath_chan_change(sc, c);
+	if (ic->ic_state == IEEE80211_S_RUN)
+		ath_beacon_config(sc);	/* restart beacons */
+	ath_hal_intrset(ah, sc->sc_imask);
+
+	if (ic->ic_state == IEEE80211_S_RUN)
+		netif_wake_queue(dev);	/* restart xmit */
+	return 0;
+}
+
+/*
+ * Transmit a data packet.  On failure caller is
+ * assumed to reclaim the resources.
+ */
+static int
+ath_hardstart(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni = NULL;
+	struct ath_buf *bf = NULL;
+	int pktlen;
+
+	if ((dev->flags & IFF_RUNNING) == 0 || sc->sc_invalid) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: discard, invalid %d flags %x\n",
+			__func__, sc->sc_invalid, dev->flags);
+		sc->sc_stats.ast_tx_invalid++;
+		return -ENETDOWN;
+	}
+	/*
+	 * No data frames go out unless we're associated; this
+	 * should not happen as the 802.11 layer does not enable
+	 * the xmit queue until we enter the RUN state.
+	 */
+	if (ic->ic_state != IEEE80211_S_RUN) {
+		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard, state %u\n",
+			__func__, ic->ic_state);
+		sc->sc_stats.ast_tx_discard++;
+		goto bad;
+	}
+
+	/*
+	 * Grab a TX buffer and associated resources.
+	 */
+	ATH_TXBUF_LOCK_BH(sc);
+	bf = STAILQ_FIRST(&sc->sc_txbuf);
+	if (bf != NULL)
+		STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+	/* XXX use a counter and leave at least one for mgmt frames */
+	if (STAILQ_EMPTY(&sc->sc_txbuf)) {
+		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__);
+		sc->sc_stats.ast_tx_qstop++;
+		netif_stop_queue(dev);
+	}
+	ATH_TXBUF_UNLOCK_BH(sc);
+	if (bf == NULL) {		/* NB: should not happen */
+		printk("%s: discard, no xmit buf\n", __func__);
+		sc->sc_stats.ast_tx_nobuf++;
+		goto bad;
+	}
+
+	/*
+	 * Encapsulate the packet for transmission.
+	 */
+	skb = ieee80211_encap(ic, skb, &ni);
+	if (skb == NULL) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: discard, encapsulation failure\n", __func__);
+		sc->sc_stats.ast_tx_encap++;
+		goto bad;
+	}
+	pktlen = skb->len;		/* NB: don't reference skb below */
+	if (ath_tx_start(dev, ni, bf, skb) == 0) {
+		sc->sc_devstats.tx_packets++;
+		sc->sc_devstats.tx_bytes += pktlen;
+		return 0;
+	}
+	/* fall thru... */
+bad:
+	if (ni && ni != ic->ic_bss)
+		ieee80211_free_node(ic, ni);
+	if (bf != NULL) {
+		ATH_TXBUF_LOCK_BH(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK_BH(sc);
+	}
+	if (skb)
+		dev_kfree_skb(skb);
+	return 0;	/* NB: return !0 only in a ``hard error condition'' */
+}
+
+/*
+ * Transmit a management frame.  On failure we reclaim the skbuff.
+ * Note that management frames come directly from the 802.11 layer
+ * and do not honor the send queue flow control.  Need to investigate
+ * using priority queueing so management frames can bypass data.
+ */
+static int
+ath_mgtstart(struct ieee80211com *ic, struct sk_buff *skb)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211_node *ni = NULL;
+	struct ath_buf *bf = NULL;
+	struct ieee80211_frame *wh;
+	struct ieee80211_cb *cb;
+	int error;
+
+	if ((dev->flags & IFF_RUNNING) == 0 || sc->sc_invalid) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: discard, invalid %d flags %x\n",
+			__func__, sc->sc_invalid, dev->flags);
+		sc->sc_stats.ast_tx_invalid++;
+		error = -ENETDOWN;
+		goto bad;
+	}
+	/*
+	 * Grab a TX buffer and associated resources.
+	 */
+	ATH_TXBUF_LOCK_BH(sc);
+	bf = STAILQ_FIRST(&sc->sc_txbuf);
+	if (bf != NULL)
+		STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+	if (STAILQ_EMPTY(&sc->sc_txbuf))	{
+		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__);
+		sc->sc_stats.ast_tx_qstop++;
+		netif_stop_queue(dev);
+	}
+	ATH_TXBUF_UNLOCK_BH(sc);
+	if (bf == NULL) {
+		printk("ath_mgtstart: discard, no xmit buf\n");
+		sc->sc_stats.ast_tx_nobufmgt++;
+		error = -ENOBUFS;
+		goto bad;
+	}
+
+	wh = (struct ieee80211_frame *) skb->data;
+	if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+		/* fill time stamp */
+		u_int64_t tsf;
+		u_int32_t *tstamp;
+
+		tsf = ath_hal_gettsf64(ah);
+		/* XXX: adjust 100us delay to xmit */
+		tsf += 100;
+		tstamp = (u_int32_t *)&wh[1];
+		tstamp[0] = cpu_to_le32(tsf & 0xffffffff);
+		tstamp[1] = cpu_to_le32(tsf >> 32);
+	}
+
+	/*
+	 * NB: the referenced node pointer is in the
+	 * control block of the sk_buff.  This is
+	 * placed there by ieee80211_mgmt_output because
+	 * we need to hold the reference with the frame.
+	 */
+	cb = (struct ieee80211_cb *)skb->cb;
+	ni = cb->ni;
+	error = ath_tx_start(dev, ni, bf, skb);
+	if (error == 0) {
+		sc->sc_stats.ast_tx_mgmt++;
+		return 0;
+	}
+	/* fall thru... */
+bad:
+	if (ni && ni != ic->ic_bss)
+		ieee80211_free_node(ic, ni);
+	if (bf != NULL) {
+		ATH_TXBUF_LOCK_BH(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK_BH(sc);
+	}
+	dev_kfree_skb(skb);
+	return error;
+}
+
+static int
+ath_media_change(struct net_device *dev)
+{
+	int error;
+
+	error = ieee80211_media_change(dev);
+	if (error == ENETRESET) {
+		if ((dev->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+			error = ath_init(dev);
+		else
+			error = 0;
+	}
+	return error;
+}
+
+
+#ifdef AR_DEBUG
+static void
+ath_keyprint(const char *tag, u_int ix,
+	const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	static const char *ciphers[] = {
+		"WEP",
+		"AES-OCB",
+		"AES-CCM",
+		"CKIP",
+		"TKIP",
+		"CLR",
+	};
+	int i, n;
+
+	printk("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]);
+	for (i = 0, n = hk->kv_len; i < n; i++)
+		printk("%02x", hk->kv_val[i]);
+	printk(" mac %s", ether_sprintf(mac));
+	if (hk->kv_type == HAL_CIPHER_TKIP) {
+		printk(" mic ");
+		for (i = 0; i < sizeof(hk->kv_mic); i++)
+			printk("%02x", hk->kv_mic[i]);
+	}
+	printk("\n");
+}
+#endif
+
+/*
+ * Set a TKIP key into the hardware.  This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP.
+ */
+static int
+ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
+	HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+#define	IEEE80211_KEY_XR	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
+	static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+	struct ath_hal *ah = sc->sc_ah;
+
+	KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP,
+		("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher));
+	KASSERT(sc->sc_splitmic, ("key cache !split"));
+	if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
+		/*
+		 * TX key goes at first index, RX key at +32.
+		 * The hal handles the MIC keys at index+64.
+		 */
+		memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
+		KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
+		if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
+			return 0;
+
+		memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
+		KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
+		/* XXX delete tx key on failure? */
+		return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
+	} else if (k->wk_flags & IEEE80211_KEY_XR) {
+		/*
+		 * TX/RX key goes at first index.
+		 * The hal handles the MIC keys are index+64.
+		 */
+		KASSERT(k->wk_keyix < IEEE80211_WEP_NKID,
+			("group key at index %u", k->wk_keyix));
+		memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
+			k->wk_txmic : k->wk_rxmic, sizeof(hk->kv_mic));
+		KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
+		return ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid);
+	}
+	/* XXX key w/o xmit/recv; need this for compression? */
+	return 0;
+#undef IEEE80211_KEY_XR
+}
+
+/*
+ * Set a net80211 key into the hardware.  This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP with hardware MIC support.
+ */
+static int
+ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k,
+	const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	static const u_int8_t ciphermap[] = {
+		HAL_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
+		HAL_CIPHER_TKIP,	/* IEEE80211_CIPHER_TKIP */
+		HAL_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
+		HAL_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
+		(u_int8_t) -1,		/* 4 is not allocated */
+		HAL_CIPHER_CKIP,	/* IEEE80211_CIPHER_CKIP */
+		HAL_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
+	};
+	struct ath_hal *ah = sc->sc_ah;
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	HAL_KEYVAL hk;
+
+	memset(&hk, 0, sizeof(hk));
+	/*
+	 * Software crypto uses a "clear key" so non-crypto
+	 * state kept in the key cache are maintained and
+	 * so that rx frames have an entry to match.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
+		KASSERT(cip->ic_cipher < N(ciphermap),
+			("invalid cipher type %u", cip->ic_cipher));
+		hk.kv_type = ciphermap[cip->ic_cipher];
+		hk.kv_len = k->wk_keylen;
+		memcpy(hk.kv_val, k->wk_key, k->wk_keylen);
+	} else
+		hk.kv_type = HAL_CIPHER_CLR;
+
+	if (hk.kv_type == HAL_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+	    sc->sc_splitmic) {
+		return ath_keyset_tkip(sc, k, &hk, mac);
+	} else {
+		KEYPRINTF(sc, k->wk_keyix, &hk, mac);
+		return ath_hal_keyset(ah, k->wk_keyix, &hk, mac);
+	}
+#undef N
+}
+
+/*
+ * Fill the hardware key cache with key entries.
+ */
+static void
+ath_initkeytable(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct net_device *dev = ic->ic_dev;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int8_t *bssid;
+	int i;
+
+	/* XXX maybe should reset all keys when !PRIVACY */
+	if (ic->ic_state == IEEE80211_S_SCAN)
+		bssid = dev->broadcast;
+	else
+		bssid = ic->ic_bss->ni_bssid;
+	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+		struct ieee80211_key *k = &ic->ic_nw_keys[i];
+
+		if (k->wk_keylen == 0) {
+			ath_hal_keyreset(ah, i);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: reset key %u\n",
+				__func__, i);
+		} else {
+			ath_keyset(sc, k, bssid);
+		}
+	}
+}
+
+/*
+ * Allocate tx/rx key slots for TKIP.  We allocate two slots for
+ * each key, one for decrypt/encrypt and the other for the MIC.
+ */
+static u_int16_t
+key_alloc_2pair(struct ath_softc *sc)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	u_int i, keyix;
+
+	KASSERT(sc->sc_splitmic, ("key cache !split"));
+	/* XXX could optimize */
+	for (i = 0; i < N(sc->sc_keymap)/4; i++) {
+		u_int8_t b = sc->sc_keymap[i];
+		if (b != 0xff) {
+			/*
+			 * One or more slots in this byte are free.
+			 */
+			keyix = i*NBBY;
+			while (b & 1) {
+		again:
+				keyix++;
+				b >>= 1;
+			}
+			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
+			if (isset(sc->sc_keymap, keyix+32) ||
+			    isset(sc->sc_keymap, keyix+64) ||
+			    isset(sc->sc_keymap, keyix+32+64)) {
+				/* full pair unavailable */
+				/* XXX statistic */
+				if (keyix == (i+1)*NBBY) {
+					/* no slots were appropriate, advance */
+					continue;
+				}
+				goto again;
+			}
+			setbit(sc->sc_keymap, keyix);
+			setbit(sc->sc_keymap, keyix+64);
+			setbit(sc->sc_keymap, keyix+32);
+			setbit(sc->sc_keymap, keyix+32+64);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+				"%s: key pair %u,%u %u,%u\n",
+				__func__, keyix, keyix+64,
+				keyix+32, keyix+32+64);
+			return keyix;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
+	return IEEE80211_KEYIX_NONE;
+#undef N
+}
+
+/*
+ * Allocate a single key cache slot.
+ */
+static u_int16_t
+key_alloc_single(struct ath_softc *sc)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	u_int i, keyix;
+
+	/* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
+	for (i = 0; i < N(sc->sc_keymap); i++) {
+		u_int8_t b = sc->sc_keymap[i];
+		if (b != 0xff) {
+			/*
+			 * One or more slots are free.
+			 */
+			keyix = i*NBBY;
+			while (b & 1)
+				keyix++, b >>= 1;
+			setbit(sc->sc_keymap, keyix);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n",
+				__func__, keyix);
+			return keyix;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__);
+	return IEEE80211_KEYIX_NONE;
+#undef N
+}
+
+/*
+ * Allocate one or more key cache slots for a uniacst key.  The
+ * key itself is needed only to identify the cipher.  For hardware
+ * TKIP with split cipher+MIC keys we allocate two key cache slot
+ * pairs so that we can setup separate TX and RX MIC keys.  Note
+ * that the MIC key for a TKIP key at slot i is assumed by the
+ * hardware to be at slot i+64.  This limits TKIP keys to the first
+ * 64 entries.
+ */
+static int
+ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	/*
+	 * We allocate two pair for TKIP when using the h/w to do
+	 * the MIC.  For everything else, including software crypto,
+	 * we allocate a single entry.  Note that s/w crypto requires
+	 * a pass-through slot on the 5211 and 5212.  The 5210 does
+	 * not support pass-through cache entries and we map all
+	 * those requests to slot 0.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+		return key_alloc_single(sc);
+	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
+		return key_alloc_2pair(sc);
+	} else {
+		return key_alloc_single(sc);
+	}
+}
+
+/*
+ * Delete an entry in the key cache allocated by ath_key_alloc.
+ */
+static int
+ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	u_int keyix = k->wk_keyix;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix);
+
+	ath_hal_keyreset(ah, keyix);
+	/*
+	 * Handle split tx/rx keying required for TKIP with h/w MIC.
+	 */
+	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic)
+		ath_hal_keyreset(ah, keyix+32);		/* RX key */
+	if (keyix >= IEEE80211_WEP_NKID) {
+		/*
+		 * Don't touch keymap entries for global keys so
+		 * they are never considered for dynamic allocation.
+		 */
+		clrbit(sc->sc_keymap, keyix);
+		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+		    sc->sc_splitmic) {
+			clrbit(sc->sc_keymap, keyix+64);	/* TX key MIC */
+			clrbit(sc->sc_keymap, keyix+32);	/* RX key */
+			clrbit(sc->sc_keymap, keyix+32+64);	/* RX key MIC */
+		}
+	}
+	return 1;
+}
+
+/*
+ * Set the key cache contents for the specified key.  Key cache
+ * slot(s) must already have been allocated by ath_key_alloc.
+ */
+static int
+ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+	const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	return ath_keyset(sc, k, mac);
+}
+
+/*
+ * Block/unblock tx+rx processing while a key change is done.
+ * We assume the caller serializes key management operations
+ * so we only need to worry about synchronization with other
+ * uses that originate in the driver.
+ */
+static void
+ath_key_update_begin(struct ieee80211com *ic)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
+	/*
+	 * When called from the rx tasklet we cannot use
+	 * tasklet_disable because it will block waiting
+	 * for us to complete execution.
+	 *
+	 * XXX Using in_softirq is not right since we might
+	 * be called from other soft irq contexts than
+	 * ath_rx_tasklet.
+	 */
+	if (!in_softirq())
+		tasklet_disable(&sc->sc_rxtq);
+	netif_stop_queue(dev);
+}
+
+static void
+ath_key_update_end(struct ieee80211com *ic)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
+	netif_start_queue(dev);
+	if (!in_softirq())		/* NB: see above */
+		tasklet_enable(&sc->sc_rxtq);
+}
+
+/*
+ * Calculate the receive filter according to the
+ * operating mode and state:
+ *
+ * o always accept unicast, broadcast, and multicast traffic
+ * o maintain current state of phy error reception (the hal
+ *   may enable phy error frames for noise immunity work)
+ * o probe request frames are accepted only when operating in
+ *   hostap, adhoc, or monitor modes
+ * o enable promiscuous mode according to the interface state
+ * o accept beacons:
+ *   - when operating in adhoc mode so the 802.11 layer creates
+ *     node table entries for peers,
+ *   - when operating in station mode for collecting rssi data when
+ *     the station is otherwise quiet, or
+ *   - when scanning
+ */
+static u_int32_t
+ath_calcrxfilter(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct net_device *dev = ic->ic_dev;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t rfilt;
+
+	rfilt = (ath_hal_getrxfilter(ah) & HAL_RX_FILTER_PHYERR)
+	      | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		rfilt |= HAL_RX_FILTER_PROBEREQ;
+	if (ic->ic_opmode != IEEE80211_M_HOSTAP && (dev->flags & IFF_PROMISC))
+		rfilt |= HAL_RX_FILTER_PROM;
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_IBSS ||
+	    ic->ic_state == IEEE80211_S_SCAN)
+		rfilt |= HAL_RX_FILTER_BEACON;
+	return rfilt;
+}
+
+static void
+ath_mode_init(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t rfilt, mfilt[2], val;
+	u_int8_t pos;
+	struct dev_mc_list *mc;
+
+	/* configure rx filter */
+	rfilt = ath_calcrxfilter(sc);
+	ath_hal_setrxfilter(ah, rfilt);
+
+	/* configure operational mode */
+	ath_hal_setopmode(ah);
+
+	/* calculate and install multicast filter */
+	if ((dev->flags & IFF_ALLMULTI) == 0) {
+		mfilt[0] = mfilt[1] = 0;
+		for (mc = dev->mc_list; mc; mc = mc->next) {
+			/* calculate XOR of eight 6bit values */
+			val = LE_READ_4(mc->dmi_addr + 0);
+			pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+			val = LE_READ_4(mc->dmi_addr + 3);
+			pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+			pos &= 0x3f;
+			mfilt[pos / 32] |= (1 << (pos % 32));
+		}
+	} else {
+		mfilt[0] = mfilt[1] = ~0;
+	}
+	ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]);
+	DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, MC filter %08x:%08x\n",
+		__func__, rfilt, mfilt[0], mfilt[1]);
+}
+
+static struct sk_buff *
+ath_alloc_skb(u_int size, u_int align)
+{
+	struct sk_buff *skb;
+	u_int off;
+
+	skb = dev_alloc_skb(size + align-1);
+	if (skb != NULL) {
+		off = ((unsigned long) skb->data) % align;
+		if (off != 0)
+			skb_reserve(skb, align - off);
+	}
+	return skb;
+}
+
+/*
+ * Setup the beacon frame.  The frame is mapped for DMA
+ * and the transmit descriptor is filled in.
+ */
+static int
+ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf, struct sk_buff *skb)
+{
+#define	USE_SHPREAMBLE(_ic) \
+	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
+		== IEEE80211_F_SHPREAMBLE)
+#define	MIN(a,b)	((a) < (b) ? (a) : (b))
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_desc *ds;
+	u_int8_t rate;
+
+	bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+		skb->data, skb->len, BUS_DMA_TODEVICE);
+	DPRINTF(sc, ATH_DEBUG_BEACON,
+		"%s: skb %p [data %p len %u] skbaddr %p\n",
+		__func__, skb, skb->data, skb->len, (caddr_t) bf->bf_skbaddr);
+
+	/* setup descriptors */
+	ds = bf->bf_desc;
+
+	ds->ds_link = 0;
+	ds->ds_data = bf->bf_skbaddr;
+	/*
+	 * Calculate rate code.
+	 * XXX everything at min xmit rate
+	 */
+	if (USE_SHPREAMBLE(ic))
+		rate = an->an_tx_mgtratesp;
+	else
+		rate = an->an_tx_mgtrate;
+	ath_hal_setuptxdesc(ah, ds
+		, skb->len + IEEE80211_CRC_LEN	/* frame length */
+		, sizeof(struct ieee80211_frame)/* header length */
+		, HAL_PKT_TYPE_BEACON		/* Atheros packet type */
+		, MIN(ni->ni_txpower,60)	/* txpower XXX */
+		, rate, 1			/* series 0 rate/tries */
+		, HAL_TXKEYIX_INVALID		/* no encryption */
+		, 0				/* antenna mode */
+		, HAL_TXDESC_NOACK		/* no ack for beacons */
+		, 0				/* rts/cts rate */
+		, 0				/* rts/cts duration */
+	);
+	/* NB: beacon's BufLen must be a multiple of 4 bytes */
+	ath_hal_filltxdesc(ah, ds
+		, roundup(skb->len, 4)		/* buffer length */
+		, AH_TRUE			/* first segment */
+		, AH_TRUE			/* last segment */
+		, ds				/* first descriptor */
+	);
+	return 0;
+#undef MIN
+#undef USE_SHPREAMBLE
+}
+
+/*
+ * Set the slot time based on the current setting.
+ */
+static void
+ath_setslottime(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
+	else
+		ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
+	sc->sc_updateslot = OK;
+}
+
+/*
+ * Callback from the 802.11 layer to update the
+ * slot time based on the current setting.
+ */
+static void
+ath_updateslot(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	/*
+	 * In station mode we change the hardware immediately.
+	 * For other operation we defer the change until beacon
+	 * updates have propagated to the stations.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		ath_setslottime(sc);
+	else
+		sc->sc_updateslot = UPDATE;
+}
+
+/*
+ * Allocate and setup an initial beacon frame.
+ */
+static int
+ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_buf *bf;
+	struct sk_buff *skb;
+	int error;
+
+	bf = sc->sc_bcbuf;
+	if (bf->bf_skb != NULL) {
+		bus_unmap_single(sc->sc_bdev,
+			bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+		dev_kfree_skb(bf->bf_skb);
+		bf->bf_skb = NULL;
+		bf->bf_node = NULL;
+	}
+	/*
+	 * NB: the beacon data buffer must be 32-bit aligned;
+	 * we assume the mbuf routines will return us something
+	 * with this alignment (perhaps should assert).
+	 */
+	skb = ieee80211_beacon_alloc(ic, ni, &sc->sc_boff);
+	if (skb == NULL) {
+		DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get sk_buff\n",
+			__func__);
+		sc->sc_stats.ast_be_nobuf++;
+		return ENOMEM;
+	}
+	bf->bf_node = ni;
+	error = ath_beacon_setup(sc, bf, skb);
+	if (error == 0)
+		bf->bf_skb = skb;
+	else
+		dev_kfree_skb(skb);
+	return error;
+}
+
+/*
+ * Transmit a beacon frame at SWBA.  Dynamic updates to the
+ * frame contents are done as needed and the slot time is
+ * also adjusted based on current state.
+ */
+static void
+ath_beacon_tasklet(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_buf *bf = sc->sc_bcbuf;
+	struct ath_hal *ah = sc->sc_ah;
+	struct sk_buff *skb;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s\n", __func__);
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_MONITOR ||
+	    bf == NULL || bf->bf_skb == NULL) {
+		DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_flags=%x bf=%p bf_m=%p\n",
+			 __func__, ic->ic_flags, bf, bf ? bf->bf_skb : NULL);
+		return;
+	}
+
+	/*
+	 * Update dynamic beacon contents.  If this returns non-zero
+	 * then we need to update the descriptor state because the
+	 * beacon frame changed size and/or was re-allocated.
+	 */
+	skb = bf->bf_skb;
+	if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, &skb)) {
+		/* NB: the old sk_buff is free'd */
+		bus_unmap_single(sc->sc_bdev,
+			bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+		bf->bf_skb = NULL;
+		if (ath_beacon_setup(sc, bf, skb) != 0) {
+			dev_kfree_skb(skb);
+			/* XXX statistic */
+			return;		/* XXX??? */
+		}
+		bf->bf_skb = skb;
+	}
+
+	/*
+	 * Handle slot time change when a non-ERP station joins/leaves
+	 * an 11g network.  The 802.11 layer notifies us via callback,
+	 * we mark updateslot, then wait one beacon before effecting
+	 * the change.  This gives associated stations at least one
+	 * beacon interval to note the state change.
+	 */
+	/* XXX locking */
+	if (sc->sc_updateslot == UPDATE)
+		sc->sc_updateslot = COMMIT;	/* commit next beacon */
+	else if (sc->sc_updateslot == COMMIT)
+		ath_setslottime(sc);		/* commit change to h/w */
+
+	/*
+	 * Stop any current dma and put the new frame on the queue.
+	 */
+	if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			"%s: beacon queue %u did not stop?\n",
+			__func__, sc->sc_bhalq);
+		/* NB: the HAL still stops DMA, so proceed */
+	}
+	bus_dma_sync_single(sc->sc_bdev,
+		bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+
+	ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
+	ath_hal_txstart(ah, sc->sc_bhalq);
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: TXDP%u = %p (%p)\n", __func__,
+		sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc);
+}
+
+/*
+ * Reclaim beacon resources.
+ */
+static void
+ath_beacon_free(struct ath_softc *sc)
+{
+	struct ath_buf *bf = sc->sc_bcbuf;
+
+	if (bf->bf_skb != NULL) {
+		bus_unmap_single(sc->sc_bdev,
+			bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+		dev_kfree_skb(bf->bf_skb);
+		bf->bf_skb = NULL;
+		bf->bf_node = NULL;
+	}
+}
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wakeup in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+static void
+ath_beacon_config(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni = ic->ic_bss;
+	u_int32_t nexttbtt, intval;
+	
+	nexttbtt = (LE_READ_4(ni->ni_tstamp.data + 4) << 22) |
+	    (LE_READ_4(ni->ni_tstamp.data) >> 10);
+	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt=%u\n", __func__, nexttbtt);
+	nexttbtt += ni->ni_intval;
+	intval = ni->ni_intval & HAL_BEACON_PERIOD;
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		HAL_BEACON_STATE bs;
+		u_int32_t bmisstime;
+
+		/* NB: no PCF support right now */
+		memset(&bs, 0, sizeof(bs));
+		/*
+		 * Reset our tsf so the hardware will update the
+		 * tsf register to reflect timestamps found in
+		 * received beacons.
+		 */
+		bs.bs_intval = intval | HAL_BEACON_RESET_TSF;
+		bs.bs_nexttbtt = nexttbtt;
+		bs.bs_dtimperiod = bs.bs_intval;
+		bs.bs_nextdtim = nexttbtt;
+		/*
+		 * The 802.11 layer records the offset to the DTIM
+		 * bitmap while receiving beacons; use it here to
+		 * enable h/w detection of our AID being marked in
+		 * the bitmap vector (to indicate frames for us are
+		 * pending at the AP).
+		 */
+		bs.bs_timoffset = ni->ni_timoff;
+		/*
+		 * Calculate the number of consecutive beacons to miss
+		 * before taking a BMISS interrupt.  The configuration
+		 * is specified in ms, so we need to convert that to
+		 * TU's and then calculate based on the beacon interval.
+		 * Note that we clamp the result to at most 10 beacons.
+		 */
+		bmisstime = (ic->ic_bmisstimeout * 1000) / 1024;
+		bs.bs_bmissthreshold = howmany(bmisstime,ni->ni_intval);
+		if (bs.bs_bmissthreshold > 10)
+			bs.bs_bmissthreshold = 10;
+		else if (bs.bs_bmissthreshold <= 0)
+			bs.bs_bmissthreshold = 1;
+
+		/*
+		 * Calculate sleep duration.  The configuration is
+		 * given in ms.  We insure a multiple of the beacon
+		 * period is used.  Also, if the sleep duration is
+		 * greater than the DTIM period then it makes senses
+		 * to make it a multiple of that.
+		 *
+		 * XXX fixed at 100ms
+		 */
+		bs.bs_sleepduration =
+			roundup((100 * 1000) / 1024, bs.bs_intval);
+		if (bs.bs_sleepduration > bs.bs_dtimperiod)
+			bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
+
+		DPRINTF(sc, ATH_DEBUG_BEACON, 
+			"%s: intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n"
+			, __func__
+			, bs.bs_intval
+			, bs.bs_nexttbtt
+			, bs.bs_dtimperiod
+			, bs.bs_nextdtim
+			, bs.bs_bmissthreshold
+			, bs.bs_sleepduration
+			, bs.bs_cfpperiod
+			, bs.bs_cfpmaxduration
+			, bs.bs_cfpnext
+			, bs.bs_timoffset
+		);
+		ath_hal_intrset(ah, 0);
+		ath_hal_beacontimers(ah, &bs);
+		sc->sc_imask |= HAL_INT_BMISS;
+		ath_hal_intrset(ah, sc->sc_imask);
+	} else {
+		DPRINTF(sc, ATH_DEBUG_BEACON, "%s: intval %u nexttbtt %u\n",
+			__func__, ni->ni_intval, nexttbtt);
+		ath_hal_intrset(ah, 0);
+		if (nexttbtt == ni->ni_intval)
+			intval |= HAL_BEACON_RESET_TSF;
+		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+			intval |= HAL_BEACON_ENA;
+			sc->sc_imask |= HAL_INT_SWBA;	/* beacon prepare */
+		}
+		ath_hal_beaconinit(ah, nexttbtt, intval);
+		ath_hal_intrset(ah, sc->sc_imask);
+	}
+}
+
+static int
+ath_desc_alloc(struct ath_softc *sc)
+{
+#define	DS2PHYS(_sc, _ds) \
+	((_sc)->sc_desc_daddr + ((caddr_t)(_ds) - (caddr_t)(_sc)->sc_desc))
+	int i, bsize;
+	struct ath_desc *ds;
+	struct ath_buf *bf;
+
+	/* allocate descriptors */
+	sc->sc_desc_len = sizeof(struct ath_desc) *
+				(ATH_TXBUF * ATH_TXDESC + ATH_RXBUF + 1);
+	sc->sc_desc = bus_alloc_consistent(sc->sc_bdev,
+				sc->sc_desc_len, &sc->sc_desc_daddr);
+	if (sc->sc_desc == NULL)
+		return ENOMEM;
+	ds = sc->sc_desc;
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: DMA map: %p (%d) -> %p\n",
+	    __func__, ds, sc->sc_desc_len, (caddr_t) sc->sc_desc_daddr);
+
+	/* allocate buffers */
+	bsize = sizeof(struct ath_buf) * (ATH_TXBUF + ATH_RXBUF + 1);
+	bf = kmalloc(bsize, GFP_KERNEL);
+	if (bf == NULL)
+		goto bad;
+	memset(bf, 0, bsize);
+	sc->sc_bufptr = bf;
+
+	STAILQ_INIT(&sc->sc_rxbuf);
+	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++) {
+		bf->bf_desc = ds;
+		bf->bf_daddr = DS2PHYS(sc, ds);
+		STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+	}
+
+	STAILQ_INIT(&sc->sc_txbuf);
+	for (i = 0; i < ATH_TXBUF; i++, bf++, ds += ATH_TXDESC) {
+		bf->bf_desc = ds;
+		bf->bf_daddr = DS2PHYS(sc, ds);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+	}
+
+	/* beacon buffer */
+	bf->bf_desc = ds;
+	bf->bf_daddr = DS2PHYS(sc, ds);
+	sc->sc_bcbuf = bf;
+	return 0;
+bad:
+	bus_free_consistent(sc->sc_bdev, sc->sc_desc_len,
+		sc->sc_desc, sc->sc_desc_daddr);
+	sc->sc_desc = NULL;
+	return ENOMEM;
+#undef DS2PHYS
+}
+
+static void
+ath_desc_free(struct ath_softc *sc)
+{
+	struct ath_buf *bf;
+
+	/*
+	 * NB: TX queues have already been freed in ath_draintxq(),
+	 * which must be called before calling this function.
+	 */
+
+	/* Free all pre-allocated RX skb */
+	STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list)
+		if (bf->bf_skb != NULL) {
+			bus_unmap_single(sc->sc_bdev,
+				bf->bf_skbaddr, sc->sc_rxbufsize,
+				BUS_DMA_FROMDEVICE);
+			dev_kfree_skb(bf->bf_skb);
+			bf->bf_skb = NULL;
+		}
+
+	/* If beacon skb has not been freed yet, do it now */
+	if (sc->sc_bcbuf != NULL) {
+		bf = sc->sc_bcbuf;
+		if (bf->bf_skb != NULL) {
+			bus_unmap_single(sc->sc_bdev, bf->bf_skbaddr,
+					 bf->bf_skb->len, BUS_DMA_TODEVICE);
+		}
+		sc->sc_bcbuf = NULL;
+	}
+
+	/* Free memory associated with all descriptors */
+	bus_free_consistent(sc->sc_bdev, sc->sc_desc_len,
+		sc->sc_desc, sc->sc_desc_daddr);
+
+	STAILQ_INIT(&sc->sc_rxbuf);
+	STAILQ_INIT(&sc->sc_txbuf);
+	kfree(sc->sc_bufptr);
+	sc->sc_bufptr = NULL;
+}
+
+static struct ieee80211_node *
+ath_node_alloc(struct ieee80211com *ic)
+{
+	struct ath_softc *sc = ic->ic_dev->priv;
+	const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space;
+	struct ath_node *an;
+
+	an = kmalloc(space, GFP_ATOMIC);
+	if (an == NULL)
+		return NULL;
+	memset(an, 0, space);
+	an->an_avgrssi = ATH_RSSI_DUMMY_MARKER;
+	an->an_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
+	an->an_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
+	an->an_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
+	ath_rate_node_init(sc, an);
+	return &an->an_node;
+}
+
+/*
+ * Clear any references to a node in a transmit queue.
+ * This happens when the node is removed so we don't
+ * need to worry about reclaiming reference counts; we
+ * just null the pointer and the right thing will happen
+ * when the buffer is cleaned.
+ */
+static void
+ath_tx_cleanq(struct ath_txq *txq, struct ieee80211_node *ni)
+{
+	struct ath_buf *bf;
+
+	ATH_TXQ_LOCK_BH(txq);
+	STAILQ_FOREACH(bf, &txq->axq_q, bf_list) {
+		if (bf->bf_node == ni)
+			bf->bf_node = NULL;
+	}
+	ATH_TXQ_UNLOCK_BH(txq);
+}
+
+static void
+ath_node_cleanup(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+        struct ath_softc *sc = ic->ic_dev->priv;
+	int i;
+
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i))
+			ath_tx_cleanq(&sc->sc_txq[i], ni);
+	ath_rate_node_cleanup(sc, ATH_NODE(ni));
+	sc->sc_node_cleanup(ic, ni);
+}
+
+static void
+ath_node_copy(struct ieee80211com *ic,
+	struct ieee80211_node *dst, const struct ieee80211_node *src)
+{
+        struct ath_softc *sc = ic->ic_dev->priv;
+	const struct ath_node *an = (const struct ath_node *)src;
+
+	/*
+	 * NB: Must copy first so the cleanup done by node_copy is
+	 *     done before we copy bits around below.
+	 */
+	sc->sc_node_copy(ic, dst, src);
+	memcpy(&dst[1], &src[1],
+		sizeof(struct ath_node) - sizeof(struct ieee80211_node));
+	ath_rate_node_copy(sc, ATH_NODE(dst), an);
+}
+
+static u_int8_t
+ath_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+#define	HAL_EP_RND(x, mul) \
+	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+	u_int32_t avgrssi = ATH_NODE_CONST(ni)->an_avgrssi;
+	int32_t rssi;
+
+	/*
+	 * When only one frame is received there will be no state in
+	 * avgrssi so fallback on the value recorded by the 802.11 layer.
+	 */
+	if (avgrssi != ATH_RSSI_DUMMY_MARKER)
+		rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER);
+	else
+		rssi = ni->ni_rssi;
+	/* NB: theoretically we shouldn't need this, but be paranoid */
+	return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
+#undef HAL_EP_RND
+}
+
+/*
+ * For packet capture, define the same physical layer packet header 
+ * structure as used in the wlan-ng driver 
+ */
+enum {
+	DIDmsg_lnxind_wlansniffrm		= 0x00000044,
+	DIDmsg_lnxind_wlansniffrm_hosttime	= 0x00010044,
+	DIDmsg_lnxind_wlansniffrm_mactime	= 0x00020044,
+	DIDmsg_lnxind_wlansniffrm_channel	= 0x00030044,
+	DIDmsg_lnxind_wlansniffrm_rssi		= 0x00040044,
+	DIDmsg_lnxind_wlansniffrm_sq		= 0x00050044,
+	DIDmsg_lnxind_wlansniffrm_signal	= 0x00060044,
+	DIDmsg_lnxind_wlansniffrm_noise		= 0x00070044,
+	DIDmsg_lnxind_wlansniffrm_rate		= 0x00080044,
+	DIDmsg_lnxind_wlansniffrm_istx		= 0x00090044,
+	DIDmsg_lnxind_wlansniffrm_frmlen	= 0x000A0044
+};
+enum {
+	P80211ENUM_msgitem_status_no_value	= 0x00
+};
+enum {
+	P80211ENUM_truth_false			= 0x00
+};
+
+typedef struct {
+	u_int32_t did;
+	u_int16_t status;
+	u_int16_t len;
+	u_int32_t data;
+} p80211item_uint32_t;
+
+typedef struct {
+	u_int32_t msgcode;
+	u_int32_t msglen;
+#define WLAN_DEVNAMELEN_MAX 16
+	u_int8_t devname[WLAN_DEVNAMELEN_MAX];
+	p80211item_uint32_t hosttime;
+	p80211item_uint32_t mactime;
+	p80211item_uint32_t channel;
+	p80211item_uint32_t rssi;
+	p80211item_uint32_t sq;
+	p80211item_uint32_t signal;
+	p80211item_uint32_t noise;
+	p80211item_uint32_t rate;
+	p80211item_uint32_t istx;
+	p80211item_uint32_t frmlen;
+} wlan_ng_prism2_header;
+
+static int
+ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct sk_buff *skb;
+	struct ath_desc *ds;
+
+	skb = bf->bf_skb;
+	if (skb == NULL) {
+ 		if (sc->sc_ic.ic_opmode == IEEE80211_M_MONITOR) {
+ 			u_int off;
+ 			/*
+ 			 * Allocate buffer for monitor mode with space for the
+			 * wlan-ng style physical layer header at the start.
+ 			 */
+ 			skb = dev_alloc_skb(sc->sc_rxbufsize +
+					    sizeof(wlan_ng_prism2_header) +
+					    sc->sc_cachelsz - 1);
+ 			if (skb == NULL) {
+ 				DPRINTF(sc, ATH_DEBUG_ANY,
+					"%s: skbuff alloc of size %u failed\n",
+					__func__,
+					sc->sc_rxbufsize
+					+ sizeof(wlan_ng_prism2_header)
+					+ sc->sc_cachelsz -1);
+ 				sc->sc_stats.ast_rx_nobuf++;
+ 				return ENOMEM;
+ 			}
+ 			/*
+			 * Reserve space for the Prism header.
+ 			 */
+ 			skb_reserve(skb, sizeof(wlan_ng_prism2_header));
+			/*
+ 			 * Align to cache line.
+			 */
+ 			off = ((unsigned long) skb->data) % sc->sc_cachelsz;
+ 			if (off != 0)
+ 				skb_reserve(skb, sc->sc_cachelsz - off);
+		} else {
+			/*
+			 * Cache-line-align.  This is important (for the
+			 * 5210 at least) as not doing so causes bogus data
+			 * in rx'd frames.
+			 */
+			skb = ath_alloc_skb(sc->sc_rxbufsize, sc->sc_cachelsz);
+			if (skb == NULL) {
+				DPRINTF(sc, ATH_DEBUG_ANY,
+					"%s: skbuff alloc of size %u failed\n",
+					__func__, sc->sc_rxbufsize);
+				sc->sc_stats.ast_rx_nobuf++;
+				return ENOMEM;
+			}
+		}
+		skb->dev = &sc->sc_dev;
+		bf->bf_skb = skb;
+		bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+			skb->data, sc->sc_rxbufsize, BUS_DMA_FROMDEVICE);
+	}
+
+	/*
+	 * Setup descriptors.  For receive we always terminate
+	 * the descriptor list with a self-linked entry so we'll
+	 * not get overrun under high load (as can happen with a
+	 * 5212 when ANI processing enables PHY error frames).
+	 *
+	 * To insure the last descriptor is self-linked we create
+	 * each descriptor as self-linked and add it to the end.  As
+	 * each additional descriptor is added the previous self-linked
+	 * entry is ``fixed'' naturally.  This should be safe even
+	 * if DMA is happening.  When processing RX interrupts we
+	 * never remove/process the last, self-linked, entry on the
+	 * descriptor list.  This insures the hardware always has
+	 * someplace to write a new frame.
+	 */
+	ds = bf->bf_desc;
+	ds->ds_link = bf->bf_daddr;		/* link to self */
+	ds->ds_data = bf->bf_skbaddr;
+	ath_hal_setuprxdesc(ah, ds
+		, skb_tailroom(skb)		/* buffer size */
+		, 0
+	);
+
+	if (sc->sc_rxlink != NULL)
+		*sc->sc_rxlink = bf->bf_daddr;
+	sc->sc_rxlink = &ds->ds_link;
+	return 0;
+}
+
+/*
+ * Add a prism2 header to a received frame and
+ * dispatch it to capture tools like kismet.
+ */
+static void
+ath_rx_capture(struct net_device *dev, struct ath_desc *ds, struct sk_buff *skb)
+{
+#define	IS_QOS_DATA(wh) \
+	((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK|IEEE80211_FC0_SUBTYPE_MASK))==\
+		(IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int len = ds->ds_rxstat.rs_datalen;
+	struct ieee80211_frame *wh;
+ 	wlan_ng_prism2_header *ph;
+	u_int32_t tsf;
+
+	skb->protocol = ETH_P_CONTROL;
+	skb->pkt_type = PACKET_OTHERHOST;
+	skb_put(skb, len);
+	
+	KASSERT(ic->ic_flags & IEEE80211_F_DATAPAD,
+		("data padding not enabled?"));
+	/* Remove pad bytes */
+	wh = (struct ieee80211_frame *) skb->data;
+	if (IS_QOS_DATA(wh)) {
+		int headersize = ieee80211_hdrsize(wh);
+		int padbytes = roundup(headersize,4) - headersize;
+
+		/*
+		 * Copy up 802.11 header and strip h/w padding.
+		 */
+		if (padbytes > 0) {
+			memmove(skb->data + padbytes, skb->data, headersize);
+			skb_pull(skb, padbytes);
+		}
+	}
+
+	ph = (wlan_ng_prism2_header *)
+		skb_push(skb, sizeof(wlan_ng_prism2_header));
+	memset(ph, 0, sizeof(wlan_ng_prism2_header));
+
+	ph->msgcode = DIDmsg_lnxind_wlansniffrm;
+	ph->msglen = sizeof(wlan_ng_prism2_header);
+	strcpy(ph->devname, dev->name);
+
+	ph->hosttime.did = DIDmsg_lnxind_wlansniffrm_hosttime;
+	ph->hosttime.status = 0;
+	ph->hosttime.len = 4;
+	ph->hosttime.data = jiffies;
+
+	/* Pass up tsf clock in mactime */
+	ph->mactime.did = DIDmsg_lnxind_wlansniffrm_mactime;
+	ph->mactime.status = 0;
+	ph->mactime.len = 4;
+	/*
+	 * Rx descriptor has the low 15 bits of the tsf at
+	 * the time the frame was received.  Use the current
+	 * tsf to extend this to 32 bits.
+	 */
+	tsf = ath_hal_gettsf32(sc->sc_ah);
+	if ((tsf & 0x7fff) < ds->ds_rxstat.rs_tstamp)
+		tsf -= 0x8000;
+	ph->mactime.data = ds->ds_rxstat.rs_tstamp | (tsf &~ 0x7fff);
+
+	ph->istx.did = DIDmsg_lnxind_wlansniffrm_istx;
+	ph->istx.status = 0;
+	ph->istx.len = 4;
+	ph->istx.data = P80211ENUM_truth_false;
+
+	ph->frmlen.did = DIDmsg_lnxind_wlansniffrm_frmlen;
+	ph->frmlen.status = 0;
+	ph->frmlen.len = 4;
+	ph->frmlen.data = len;
+
+	ph->channel.did = DIDmsg_lnxind_wlansniffrm_channel;
+	ph->channel.status = 0;
+	ph->channel.len = 4;
+	ph->channel.data = ieee80211_mhz2ieee(ic->ic_ibss_chan->ic_freq,0);
+
+	ph->rssi.did = DIDmsg_lnxind_wlansniffrm_rssi;
+	ph->rssi.status = P80211ENUM_msgitem_status_no_value;
+	ph->rssi.len = 4;
+	ph->rssi.data = 0;
+
+	ph->signal.did = DIDmsg_lnxind_wlansniffrm_signal;
+	ph->signal.status = 0;
+	ph->signal.len = 4;
+	ph->signal.data = ds->ds_rxstat.rs_rssi;
+
+	ph->rate.did = DIDmsg_lnxind_wlansniffrm_rate;
+	ph->rate.status = 0;
+	ph->rate.len = 4;
+	ph->rate.data = sc->sc_hwmap[ds->ds_rxstat.rs_rate];
+
+	skb->dev = dev;
+	skb->mac.raw = skb->data;
+	skb->ip_summed = CHECKSUM_NONE;
+	skb->pkt_type = PACKET_OTHERHOST;
+	skb->protocol = __constant_htons(0x0019);  /* ETH_P_80211_RAW */
+
+	netif_rx(skb);
+#undef IS_QOS_DATA
+}
+
+/*
+ * Intercept management frames to collect beacon rssi data
+ * and to do ibss merges.
+ */
+static void
+ath_recv_mgmt(struct ieee80211com *ic, struct sk_buff *skb,
+	struct ieee80211_node *ni,
+	int subtype, int rssi, u_int32_t rstamp)
+{
+	struct ath_softc *sc = ic->ic_dev->priv;
+
+	/*
+	 * Call up first so subsequent work can use information
+	 * potentially stored in the node (e.g. for ibss merge).
+	 */
+	sc->sc_recv_mgmt(ic, skb, ni, subtype, rssi, rstamp);
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_BEACON:
+		/* update rssi statistics for use by the hal */
+		ATH_RSSI_LPF(ATH_NODE(ni)->an_halstats.ns_avgbrssi, rssi);
+		/* fall thru... */
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		break;
+	}
+}
+
+static void
+ath_setdefantenna(struct ath_softc *sc, u_int antenna)
+{
+	struct ath_hal *ah = sc->sc_ah;
+
+	/* XXX block beacon interrupts */
+	ath_hal_setdefantenna(ah, antenna);
+	if (sc->sc_defant != antenna)
+		sc->sc_stats.ast_ant_defswitch++;
+	sc->sc_defant = antenna;
+	sc->sc_rxotherant = 0;
+}
+
+static void
+ath_rx_tasklet(TQUEUE_ARG data)
+{
+#define	PA2DESC(_sc, _pa) \
+	((struct ath_desc *)((caddr_t)(_sc)->sc_desc + \
+		((_pa) - (_sc)->sc_desc_daddr)))
+#define	IS_CTL(wh) \
+	((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_buf *bf;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_desc *ds;
+	struct sk_buff *skb;
+	struct ieee80211_node *ni;
+	struct ath_node *an;
+	int len;
+	u_int phyerr;
+	HAL_STATUS status;
+
+	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s\n", __func__);
+	do {
+		bf = STAILQ_FIRST(&sc->sc_rxbuf);
+		if (bf == NULL) {		/* XXX ??? can this happen */
+			printk("%s: no buffer (%s)\n", dev->name, __func__);
+			break;
+		}
+		ds = bf->bf_desc;
+		if (ds->ds_link == bf->bf_daddr) {
+			/* NB: never process the self-linked entry at the end */
+			break;
+		}
+		skb = bf->bf_skb;
+		if (skb == NULL) {		/* XXX ??? can this happen */
+			printk("%s: no skbuff (%s)\n", dev->name, __func__);
+			continue;
+		}
+		/* XXX sync descriptor memory */
+		/*
+		 * Must provide the virtual address of the current
+		 * descriptor, the physical address, and the virtual
+		 * address of the next descriptor in the h/w chain.
+		 * This allows the HAL to look ahead to see if the
+		 * hardware is done with a descriptor by checking the
+		 * done bit in the following descriptor and the address
+		 * of the current descriptor the DMA engine is working
+		 * on.  All this is necessary because of our use of
+		 * a self-linked list to avoid rx overruns.
+		 */
+		status = ath_hal_rxprocdesc(ah, ds,
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link));
+#ifdef AR_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
+			ath_printrxbuf(bf, status == HAL_OK); 
+#endif
+		if (status == HAL_EINPROGRESS)
+			break;
+		STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
+
+		if (ds->ds_rxstat.rs_more) {
+			/*
+			 * Frame spans multiple descriptors; this
+			 * cannot happen yet as we don't support
+			 * jumbograms.  If not in monitor mode,
+			 * discard the frame.
+			 */
+#ifndef ERROR_FRAMES
+			/*
+			 * Enable this if you want to see
+			 * error frames in Monitor mode.
+			 */
+			if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+				sc->sc_stats.ast_rx_toobig++;
+				goto rx_next;
+			}
+#endif
+			/* fall thru for monitor mode handling... */
+		} else if (ds->ds_rxstat.rs_status != 0) {
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
+				sc->sc_stats.ast_rx_crcerr++;
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_FIFO)
+				sc->sc_stats.ast_rx_fifoerr++;
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_PHY) {
+				sc->sc_stats.ast_rx_phyerr++;
+				phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
+				sc->sc_stats.ast_rx_phy[phyerr]++;
+				goto rx_next;
+			}
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_DECRYPT) {
+				/*
+				 * Decrypt error.  If the error occurred
+				 * because there was no hardware key, then
+				 * let the frame through so the upper layers
+				 * can process it.  This is necessary for 5210
+				 * parts which have no way to setup a ``clear''
+				 * key cache entry.
+				 *
+				 * XXX do key cache faulting
+				 */
+				if (ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID)
+					goto rx_accept;
+				sc->sc_stats.ast_rx_badcrypt++;
+			}
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_MIC) {
+				sc->sc_stats.ast_rx_badmic++;
+				/*
+				 * Do minimal work required to hand off
+				 * the 802.11 header for notifcation.
+				 */
+				/* XXX frag's and qos frames */
+				len = ds->ds_rxstat.rs_datalen;
+				if (len >= sizeof (struct ieee80211_frame)) {
+					bus_dma_sync_single(sc->sc_bdev,
+					    bf->bf_skbaddr, len,
+					    BUS_DMA_FROMDEVICE);
+					ieee80211_notify_michael_failure(ic,
+						(struct ieee80211_frame *)
+						    skb->data,
+						ds->ds_rxstat.rs_keyix);
+				}
+			}
+			/*
+			 * Reject error frames, we normally don't want
+			 * to see them in monitor mode (in monitor mode
+			 * allow through packets that have crypto problems).
+			 */
+			if ((ds->ds_rxstat.rs_status &~
+				(HAL_RXERR_DECRYPT|HAL_RXERR_MIC)) ||
+			    sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR)
+				goto rx_next;
+		}
+rx_accept:
+		/*
+		 * Sync and unmap the frame.  At this point we're
+		 * committed to passing the sk_buff somewhere so
+		 * clear buf_skb; this means a new sk_buff must be
+		 * allocated when the rx descriptor is setup again
+		 * to receive another frame.
+		 */
+		len = ds->ds_rxstat.rs_datalen;
+		bus_dma_sync_single(sc->sc_bdev,
+			bf->bf_skbaddr, len, BUS_DMA_FROMDEVICE);
+		bus_unmap_single(sc->sc_bdev, bf->bf_skbaddr,
+			sc->sc_rxbufsize, BUS_DMA_FROMDEVICE);
+		bf->bf_skb = NULL;
+
+		if (sc->sc_softled)
+			ath_update_led(sc);
+		sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++;
+
+		if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+			/*
+			 * Monitor mode: discard anything shorter than
+			 * an ack or cts, clean the skbuff, fabricate
+			 * the Prism header existing tools expect,
+			 * and dispatch.
+			 */
+			if (len < IEEE80211_ACK_LEN) {
+				DPRINTF(sc, ATH_DEBUG_RECV,
+					"%s: runt packet %d\n", __func__, len);
+				sc->sc_stats.ast_rx_tooshort++;
+				dev_kfree_skb(skb);
+				goto rx_next;
+			}
+			ath_rx_capture(dev, ds, skb);
+			goto rx_next;
+		}
+
+		/*
+		 * From this point on we assume the frame is at least
+		 * as large as ieee80211_frame_min; verify that.
+		 */
+		if (len < IEEE80211_MIN_LEN) {
+			DPRINTF(sc, ATH_DEBUG_RECV, "%s: short packet %d\n",
+				__func__, len);
+			sc->sc_stats.ast_rx_tooshort++;
+			dev_kfree_skb(skb);
+			goto rx_next;
+		}
+
+		/*
+		 * Normal receive.
+		 */
+		skb_put(skb, len);
+		skb->protocol = ETH_P_CONTROL;		/* XXX */
+
+		if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
+			ieee80211_dump_pkt(skb->data, len,
+				   sc->sc_hwmap[ds->ds_rxstat.rs_rate],
+				   ds->ds_rxstat.rs_rssi);
+		}
+
+		skb_trim(skb, skb->len - IEEE80211_CRC_LEN);
+
+		/*
+		 * Locate the node for sender, track state, and then
+		 * pass the (referenced) node up to the 802.11 layer
+		 * for its use.  We are required to pass some node so
+		 * we fall back to ic_bss when this frame is from an
+		 * unknown sender.  The 802.11 layer knows this means the
+		 * sender wasn't in the node table and acts accordingly.
+		 * Note also that by convention we do not reference
+		 * count ic_bss, only other nodes (ic_bss is never free'd).
+		 */
+		if (ic->ic_opmode != IEEE80211_M_STA) {
+			struct ieee80211_frame_min *wh =
+				(struct ieee80211_frame_min *) skb->data;
+			if (IS_CTL(wh))
+				ni = ieee80211_find_node(ic, wh->i_addr1);
+			else
+				ni = ieee80211_find_node(ic, wh->i_addr2);
+			if (ni == NULL)
+				ni = ic->ic_bss;
+		} else
+			ni = ic->ic_bss;
+
+		/*
+		 * Track rx rssi and do any rx antenna management.
+		 */
+		an = ATH_NODE(ni);
+		ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
+		if (sc->sc_diversity) {
+			/*
+			 * When using fast diversity, change the default rx
+			 * antenna if diversity chooses the other antenna 3
+			 * times in a row.
+			 */
+			if (sc->sc_defant != ds->ds_rxstat.rs_antenna) {
+				if (++sc->sc_rxotherant >= 3)
+					ath_setdefantenna(sc,
+						ds->ds_rxstat.rs_antenna);
+			} else
+				sc->sc_rxotherant = 0;
+		}
+
+		/*
+		 * Send frame up for processing.
+		 */
+		ieee80211_input(ic, skb, ni,
+			ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
+
+		/*
+		 * Reclaim node reference.
+		 */
+		if (ni != ic->ic_bss)
+			ieee80211_free_node(ic, ni);
+rx_next:
+		STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+	} while (ath_rxbuf_init(sc, bf) == 0);
+
+	/* rx signal state monitoring */
+	ath_hal_rxmonitor(ah, &ATH_NODE(ic->ic_bss)->an_halstats);
+#undef IS_CTL
+#undef PA2DESC
+}
+
+/*
+ * Setup a hardware data transmit queue for the specified
+ * access control.  The hal may not support all requested
+ * queues in which case it will return a reference to a
+ * previously setup queue.  We record the mapping from ac's
+ * to h/w queues for use by ath_tx_start and also track
+ * the set of h/w queues being used to optimize work in the
+ * transmit interrupt handler and related routines.
+ */
+static int
+ath_tx_setup(struct ath_softc *sc, int ac, int haltype)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_TXQ_INFO qi;
+	int qnum;
+
+	if (ac >= N(sc->sc_ac2q)) {
+		printk("%s: AC %u out of range, max %u!\n",
+			sc->sc_dev.name, ac, N(sc->sc_ac2q));
+		return 0;
+	}
+	memset(&qi, 0, sizeof(qi));
+	qi.tqi_subtype = haltype;
+	/*
+	 * Enable interrupts only for EOL and DESC conditions.
+	 * We mark tx descriptors to receive a DESC interrupt
+	 * when a tx queue gets deep; otherwise waiting for the
+	 * EOL to reap descriptors.  Note that this is done to
+	 * reduce interrupt load and this only defers reaping
+	 * descriptors, never transmitting frames.  Aside from
+	 * reducing interrupts this also permits more concurrency.
+	 * The only potential downside is if the tx queue backs
+	 * up in which case the top half of the kernel may backup
+	 * due to a lack of tx descriptors.
+	 */
+	qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
+	qnum = ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_DATA, &qi);
+	if (qnum == -1) {
+		printk("%s: Unable to setup hardware queue for %s traffic!\n",
+			sc->sc_dev.name, acnames[ac]);
+		return 0;
+	}
+	if (qnum >= N(sc->sc_txq)) {
+		printk("%s: hal qnum %u out of range, max %u!\n",
+			sc->sc_dev.name, qnum, N(sc->sc_txq));
+		return 0;
+	}
+	if (!ATH_TXQ_SETUP(sc, qnum)) {
+		struct ath_txq *txq = &sc->sc_txq[qnum];
+
+		txq->axq_qnum = qnum;
+		txq->axq_depth = 0;
+		txq->axq_intrcnt = 0;
+		txq->axq_link = NULL;
+		STAILQ_INIT(&txq->axq_q);
+		ATH_TXQ_LOCK_INIT(txq);
+		sc->sc_txqsetup |= 1<<qnum;
+	}
+	sc->sc_ac2q[ac] = &sc->sc_txq[qnum];
+	return 1;
+#undef N
+}
+
+static int
+ath_tx_start(struct net_device *dev, struct ieee80211_node *ni, struct ath_buf *bf,
+    struct sk_buff *skb)
+{
+#define	MIN(a,b)	((a) < (b) ? (a) : (b))
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	int iswep, ismcast, keyix, hdrlen, pktlen, try0;
+	u_int8_t rix, txrate, ctsrate;
+	u_int8_t cix = 0xff;		/* NB: silence compiler */
+	struct ath_desc *ds;
+	struct ath_txq *txq;
+	struct ieee80211_frame *wh;
+	u_int subtype, flags, ctsduration;
+	HAL_PKT_TYPE atype;
+	const HAL_RATE_TABLE *rt;
+	HAL_BOOL shortPreamble;
+	struct ath_node *an;
+
+	wh = (struct ieee80211_frame *) skb->data;
+	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
+	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+	hdrlen = ieee80211_anyhdrsize(wh);
+	pktlen = skb->len;
+
+	if (iswep) {
+		const struct ieee80211_cipher *cip;
+		struct ieee80211_key *k;
+
+		/*
+		 * Construct the 802.11 header+trailer for an encrypted
+		 * frame. The only reason this can fail is because of an
+		 * unknown or unsupported cipher/key type.
+		 */
+		k = ieee80211_crypto_encap(ic, ni, skb);
+		if (k == NULL) {
+			/*
+			 * This can happen when the key is yanked after the
+			 * frame was queued.  Just discard the frame; the
+			 * 802.11 layer counts failures and provides
+			 * debugging/diagnostics.
+			 */
+			return -EIO;
+		}
+		/*
+		 * Adjust the packet + header lengths for the crypto
+		 * additions and calculate the h/w key index.  When
+		 * a s/w mic is done the frame will have had any mic
+		 * added to it prior to entry so skb->len above will
+		 * account for it. Otherwise we need to add it to the
+		 * packet length.
+		 */
+		cip = k->wk_cipher;
+		hdrlen += cip->ic_header;
+		pktlen += cip->ic_header + cip->ic_trailer;
+		if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
+			pktlen += cip->ic_miclen;
+		keyix = k->wk_keyix;
+
+		/* packet header may have moved, reset our local pointer */
+		wh = (struct ieee80211_frame *) skb->data;
+	} else
+		keyix = HAL_TXKEYIX_INVALID;
+
+	pktlen += IEEE80211_CRC_LEN;
+
+	/*
+	 * Load the DMA map so any coalescing is done.  This
+	 * also calculates the number of descriptors we need.
+	 */
+	bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+		skb->data, pktlen, BUS_DMA_TODEVICE);
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: skb %p [data %p len %u] skbaddr %x\n",
+		__func__, skb, skb->data, skb->len, bf->bf_skbaddr);
+	bf->bf_skb = skb;
+	bf->bf_node = ni;
+
+	/* setup descriptors */
+	ds = bf->bf_desc;
+	rt = sc->sc_currates;
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+	/*
+	 * NB: the 802.11 layer marks whether or not we should
+	 * use short preamble based on the current mode and
+	 * negotiated parameters.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+		shortPreamble = AH_TRUE;
+		sc->sc_stats.ast_tx_shortpre++;
+	} else {
+		shortPreamble = AH_FALSE;
+	}
+
+	an = ATH_NODE(ni);
+	/*
+	 * Calculate Atheros packet type from IEEE80211 packet header,
+	 * setup for rate calculations, and select h/w transmit queue.
+	 */
+	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+	case IEEE80211_FC0_TYPE_MGT:
+		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
+			atype = HAL_PKT_TYPE_BEACON;
+		else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+			atype = HAL_PKT_TYPE_PROBE_RESP;
+		else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
+			atype = HAL_PKT_TYPE_ATIM;
+		else
+			atype = HAL_PKT_TYPE_NORMAL;	/* XXX */
+		rix = 0;			/* XXX lowest rate */
+		try0 = ATH_TXMAXTRY;
+		if (shortPreamble)
+			txrate = an->an_tx_mgtratesp;
+		else
+			txrate = an->an_tx_mgtrate;
+		/* NB: force all management frames to highest queue */
+		txq = sc->sc_ac2q[WME_AC_VO];
+		break;
+	case IEEE80211_FC0_TYPE_CTL:
+		atype = HAL_PKT_TYPE_PSPOLL;	/* stop setting of duration */
+		rix = 0;			/* XXX lowest rate */
+		try0 = ATH_TXMAXTRY;
+		if (shortPreamble)
+			txrate = an->an_tx_mgtratesp;
+		else
+			txrate = an->an_tx_mgtrate;
+		/* NB: force all ctl frames to highest queue */
+		txq = sc->sc_ac2q[WME_AC_VO];
+		break;
+	case IEEE80211_FC0_TYPE_DATA:
+		atype = HAL_PKT_TYPE_NORMAL;		/* default */
+		/*
+		 * Data frames; consult the rate control module.
+		 */
+		ath_rate_findrate(sc, an, shortPreamble, skb->len,
+			&rix, &try0, &txrate);
+		/*
+		 * Default all non-QoS traffic to the background queue.
+		 */
+		if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+			/* XXX validate skb->priority */
+			txq = sc->sc_ac2q[skb->priority];
+		} else
+			txq = sc->sc_ac2q[WME_AC_BK];
+		break;
+	default:
+		printk("%s: bogus frame type 0x%x (%s)\n", dev->name,
+			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
+		/* XXX statistic */
+		return -EIO;
+	}
+
+	/*
+	 * Calculate miscellaneous flags.
+	 */
+	flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
+	if (ismcast) {
+		flags |= HAL_TXDESC_NOACK;	/* no ack on broad/multicast */
+		sc->sc_stats.ast_tx_noack++;
+	} else if (pktlen > ic->ic_rtsthreshold) {
+		flags |= HAL_TXDESC_RTSENA;	/* RTS based on frame length */
+		cix = rt->info[rix].controlRate;
+		sc->sc_stats.ast_tx_rts++;
+	}
+
+	/*
+	 * If 802.11g protection is enabled, determine whether
+	 * to use RTS/CTS or just CTS.  Note that this is only
+	 * done for OFDM unicast frames.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+	    rt->info[rix].phy == IEEE80211_T_OFDM &&
+	    (flags & HAL_TXDESC_NOACK) == 0) {
+		/* XXX fragments must use CCK rates w/ protection */
+		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+			flags |= HAL_TXDESC_RTSENA;
+		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+			flags |= HAL_TXDESC_CTSENA;
+		cix = rt->info[sc->sc_protrix].controlRate;
+		sc->sc_stats.ast_tx_protect++;
+	}
+
+	/*
+	 * Calculate duration.  This logically belongs in the 802.11
+	 * layer but it lacks sufficient information to calculate it.
+	 */
+	if ((flags & HAL_TXDESC_NOACK) == 0 &&
+	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
+		u_int16_t dur;
+		/*
+		 * XXX not right with fragmentation.
+		 */
+		if (shortPreamble)
+			dur = rt->info[rix].spAckDuration;
+		else
+			dur = rt->info[rix].lpAckDuration;
+		*(u_int16_t *)wh->i_dur = cpu_to_le16(dur);
+	}
+
+	/*
+	 * Calculate RTS/CTS rate and duration if needed.
+	 */
+	ctsduration = 0;
+	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
+		/*
+		 * CTS transmit rate is derived from the transmit rate
+		 * by looking in the h/w rate table.  We must also factor
+		 * in whether or not a short preamble is to be used.
+		 */
+		/* NB: cix is set above where RTS/CTS is enabled */
+		KASSERT(cix != 0xff, ("cix not setup"));
+		ctsrate = rt->info[cix].rateCode;
+		/*
+		 * Compute the transmit duration based on the frame
+		 * size and the size of an ACK frame.  We call into the
+		 * HAL to do the computation since it depends on the
+		 * characteristics of the actual PHY being used.
+		 *
+		 * NB: CTS is assumed the same size as an ACK so we can
+		 *     use the precalculated ACK durations.
+		 */
+		if (shortPreamble) {
+			ctsrate |= rt->info[cix].shortPreamble;
+			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
+				ctsduration += rt->info[cix].spAckDuration;
+			ctsduration += ath_hal_computetxtime(ah,
+				rt, pktlen, rix, AH_TRUE);
+			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
+				ctsduration += rt->info[cix].spAckDuration;
+		} else {
+			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
+				ctsduration += rt->info[cix].lpAckDuration;
+			ctsduration += ath_hal_computetxtime(ah,
+				rt, pktlen, rix, AH_FALSE);
+			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
+				ctsduration += rt->info[cix].lpAckDuration;
+		}
+		/*
+		 * Must disable multi-rate retry when using RTS/CTS.
+		 */
+		try0 = ATH_TXMAXTRY;
+	} else
+		ctsrate = 0;
+
+	if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
+		ieee80211_dump_pkt(skb->data, skb->len,
+			sc->sc_hwmap[txrate], -1);
+
+	/* 
+	 * Determine if a tx interrupt should be generated for
+	 * this descriptor.  We take a tx interrupt to reap
+	 * descriptors when the h/w hits an EOL condition or
+	 * when the descriptor is specifically marked to generate
+	 * an interrupt.  We periodically mark descriptors in this
+	 * way to insure timely replenishing of the supply needed
+	 * for sending frames.  Defering interrupts reduces system
+	 * load and potentially allows more concurrent work to be
+	 * done but if done to aggressively can cause senders to
+	 * backup.
+	 *
+	 * NB: use >= to deal with sc_txintrperiod changing
+	 *     dynamically through sysctl.
+	 */
+	if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
+		flags |= HAL_TXDESC_INTREQ;
+		txq->axq_intrcnt = 0;
+	}
+
+	/*
+	 * Formulate first tx descriptor with tx controls.
+	 */
+	/* XXX check return value? */
+	ath_hal_setuptxdesc(ah, ds
+		, pktlen		/* packet length */
+		, hdrlen		/* header length */
+		, atype			/* Atheros packet type */
+		, MIN(ni->ni_txpower,60)/* txpower */
+		, txrate, try0		/* series 0 rate/tries */
+		, keyix			/* key cache index */
+		, sc->sc_txantenna	/* antenna mode */
+		, flags			/* flags */
+		, ctsrate		/* rts/cts rate */
+		, ctsduration		/* rts/cts duration */
+	);
+	/*
+	 * Setup the multi-rate retry state only when we're
+	 * going to use it.  This assumes ath_hal_setuptxdesc
+	 * initializes the descriptors (so we don't have to)
+	 * when the hardware supports multi-rate retry and
+	 * we don't use it.
+	 */
+	if (try0 != ATH_TXMAXTRY)
+		ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
+
+	ds->ds_link = 0;
+	ds->ds_data = bf->bf_skbaddr;
+	ath_hal_filltxdesc(ah, ds
+		, skb->len		/* segment length */
+		, AH_TRUE		/* first segment */
+		, AH_TRUE		/* last segment */
+		, ds			/* first descriptor */
+	);
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: Q%d: %08x %08x %08x %08x %08x %08x\n",
+	    __func__, txq->axq_qnum, ds->ds_link, ds->ds_data,
+	    ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
+
+	/*
+	 * Insert the frame on the outbound list and
+	 * pass it on to the hardware.
+	 */
+	ATH_TXQ_LOCK_BH(txq);
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+	DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: txq depth = %d\n",
+		__func__, txq->axq_depth);
+	if (txq->axq_link == NULL) {
+		ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: TXDP[%u] = %p (%p)\n",
+			__func__,
+		    txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc);
+	} else {
+		*txq->axq_link = bf->bf_daddr;
+		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: link[%u](%p)=%p (%p)\n",
+			__func__,
+		    txq->axq_qnum, txq->axq_link,
+		    (caddr_t)bf->bf_daddr, bf->bf_desc);
+	}
+	txq->axq_link = &ds->ds_link;
+	ATH_TXQ_UNLOCK_BH(txq);
+
+	if (sc->sc_softled)
+		ath_update_led(sc);
+
+	ath_hal_txstart(ah, txq->axq_qnum);
+	dev->trans_start = jiffies;
+	return 0;
+#undef MIN
+}
+
+/*
+ * Process completed xmit descriptors from the specified queue.
+ */
+static void
+ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_buf *bf;
+	struct ath_desc *ds;
+	struct ieee80211_node *ni;
+	struct ath_node *an;
+	int sr, lr;
+	HAL_STATUS status;
+
+	DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %p, link %p\n", __func__,
+		(caddr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum),
+		txq->axq_link);
+	for (;;) {
+		ATH_TXQ_LOCK(txq);
+		txq->axq_intrcnt = 0;	/* reset periodic desc intr count */
+		bf = STAILQ_FIRST(&txq->axq_q);
+		if (bf == NULL) {
+			txq->axq_link = NULL;
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+		ds = bf->bf_desc;		/* NB: last decriptor */
+		status = ath_hal_txprocdesc(ah, ds);
+#ifdef AR_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_XMIT_DESC)
+			ath_printtxbuf(bf, status == HAL_OK);
+#endif
+		if (status == HAL_EINPROGRESS) {
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+		ATH_TXQ_UNLOCK(txq);
+
+		ni = bf->bf_node;
+		if (ni != NULL) {
+			an = ATH_NODE(ni);
+			if (ds->ds_txstat.ts_status == 0) {
+				sc->sc_stats.ast_ant_tx[ds->ds_txstat.ts_antenna]++;
+				if (ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE)
+					sc->sc_stats.ast_tx_altrate++;
+				sc->sc_stats.ast_tx_rssi =
+					ds->ds_txstat.ts_rssi;
+				ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
+					ds->ds_txstat.ts_rssi);
+			} else {
+				if (ds->ds_txstat.ts_status & HAL_TXERR_XRETRY)
+					sc->sc_stats.ast_tx_xretries++;
+				if (ds->ds_txstat.ts_status & HAL_TXERR_FIFO)
+					sc->sc_stats.ast_tx_fifoerr++;
+				if (ds->ds_txstat.ts_status & HAL_TXERR_FILT)
+					sc->sc_stats.ast_tx_filtered++;
+			}
+			sr = ds->ds_txstat.ts_shortretry;
+			lr = ds->ds_txstat.ts_longretry;
+			sc->sc_stats.ast_tx_shortretry += sr;
+			sc->sc_stats.ast_tx_longretry += lr;
+			/*
+			 * Hand the descriptor to the rate control algorithm.
+			 */
+			ath_rate_tx_complete(sc, ATH_NODE(ni), ds);
+			/*
+			 * Reclaim reference to node.
+			 *
+			 * NB: the node may be reclaimed here if, for example
+			 *     this is a DEAUTH message that was sent and the
+			 *     node was timed out due to inactivity.
+			 */
+			if (ni != ic->ic_bss)
+				ieee80211_free_node(ic, ni);
+		}
+		bus_unmap_single(sc->sc_bdev,
+			bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+		dev_kfree_skb(bf->bf_skb);
+		bf->bf_skb = NULL;
+		bf->bf_node = NULL;
+
+		ATH_TXBUF_LOCK(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK(sc);
+	}
+}
+
+/*
+ * Deferred processing of transmit interrupt; special-cased
+ * for a single hardware transmit queue (e.g. 5210 and 5211).
+ */
+static void
+ath_tx_tasklet_q0(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	ath_tx_processq(sc, &sc->sc_txq[0]);
+	/*
+	 * Don't wakeup unless we're associated; this insures we don't
+	 * signal the upper layer it's ok to start sending data frames.
+	 */
+	/* XXX use a low watermark to reduce wakeups */
+	if (ic->ic_state == IEEE80211_S_RUN)
+		netif_wake_queue(dev);
+}
+
+/*
+ * Deferred processing of transmit interrupt; special-cased
+ * for four hardware queues, 0-3 (e.g. 5212 w/ WME support).
+ */
+static void
+ath_tx_tasklet_q0123(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	/*
+	 * Process each active queue.
+	 */
+	ath_tx_processq(sc, &sc->sc_txq[0]);
+	ath_tx_processq(sc, &sc->sc_txq[1]);
+	ath_tx_processq(sc, &sc->sc_txq[2]);
+	ath_tx_processq(sc, &sc->sc_txq[3]);
+	/*
+	 * Don't wakeup unless we're associated; this insures we don't
+	 * signal the upper layer it's ok to start sending data frames.
+	 */
+	/* XXX use a low watermark to reduce wakeups */
+	if (ic->ic_state == IEEE80211_S_RUN)
+		netif_wake_queue(dev);
+}
+
+/*
+ * Deferred processing of transmit interrupt.
+ */
+static void
+ath_tx_tasklet(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int i;
+
+	/*
+	 * Process each active queue.
+	 */
+	/* XXX faster to read ISR_S0_S and ISR_S1_S to determine q's? */
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i))
+			ath_tx_processq(sc, &sc->sc_txq[i]);
+	/*
+	 * Don't wakeup unless we're associated; this insures we don't
+	 * signal the upper layer it's ok to start sending data frames.
+	 */
+	/* XXX use a low watermark to reduce wakeups */
+	if (ic->ic_state == IEEE80211_S_RUN)
+		netif_wake_queue(dev);
+}
+
+static void
+ath_tx_timeout(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	sc->sc_stats.ast_watchdog++;
+	ath_init(dev);
+}
+
+static void
+ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni;
+	struct ath_buf *bf;
+
+	/*
+	 * NB: this assumes output has been stopped and
+	 *     we do not need to block ath_tx_tasklet
+	 */
+	for (;;) {
+		ATH_TXQ_LOCK(txq);
+		bf = STAILQ_FIRST(&txq->axq_q);
+		if (bf == NULL) {
+			txq->axq_link = NULL;
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+		ATH_TXQ_UNLOCK(txq);
+#ifdef AR_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_RESET)
+			ath_printtxbuf(bf,
+				ath_hal_txprocdesc(ah, bf->bf_desc) == HAL_OK);
+#endif /* AR_DEBUG */
+		bus_unmap_single(sc->sc_bdev,
+			bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+		dev_kfree_skb(bf->bf_skb);
+		bf->bf_skb = NULL;
+		ni = bf->bf_node;
+		bf->bf_node = NULL;
+		if (ni != NULL && ni != ic->ic_bss) {
+			/*
+			 * Reclaim node reference.
+			 */
+			ieee80211_free_node(ic, ni);
+		}
+		ATH_TXBUF_LOCK(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK(sc);
+	}
+}
+
+static void
+ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_hal *ah = sc->sc_ah;
+
+	(void) ath_hal_stoptxdma(ah, txq->axq_qnum);
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n",
+	    __func__, txq->axq_qnum,
+	    (caddr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), txq->axq_link);
+}
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void
+ath_draintxq(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	int i;
+
+	/* XXX return value */
+	if (!sc->sc_invalid) {
+		(void) ath_hal_stoptxdma(ah, sc->sc_bhalq);
+		DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %p\n", __func__,
+		    (caddr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq));
+		for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+			if (ATH_TXQ_SETUP(sc, i))
+				ath_tx_stopdma(sc, &sc->sc_txq[i]);
+	}
+	sc->sc_dev.trans_start = jiffies;
+	netif_start_queue(&sc->sc_dev);
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i))
+			ath_tx_draintxq(sc, &sc->sc_txq[i]);
+}
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath_stoprecv(struct ath_softc *sc)
+{
+#define	PA2DESC(_sc, _pa) \
+	((struct ath_desc *)((caddr_t)(_sc)->sc_desc + \
+		((_pa) - (_sc)->sc_desc_daddr)))
+	struct ath_hal *ah = sc->sc_ah;
+
+	ath_hal_stoppcurecv(ah);	/* disable PCU */
+	ath_hal_setrxfilter(ah, 0);	/* clear recv filter */
+	ath_hal_stopdmarecv(ah);	/* disable DMA engine */
+	mdelay(3);			/* 3ms is long enough for 1 frame */
+#ifdef AR_DEBUG
+	if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
+		struct ath_buf *bf;
+
+		printk("ath_stoprecv: rx queue %p, link %p\n",
+		    (caddr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink);
+		STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+			struct ath_desc *ds = bf->bf_desc;
+			HAL_STATUS status = ath_hal_rxprocdesc(ah, ds,
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link));
+			if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
+				ath_printrxbuf(bf, status == HAL_OK);
+		}
+	}
+#endif
+	sc->sc_rxlink = NULL;		/* just in case */
+#undef PA2DESC
+}
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath_startrecv(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct net_device *dev = ic->ic_dev;
+	struct ath_buf *bf;
+
+	/*
+	 * Cisco's VPN software requires that drivers be able to
+	 * receive encapsulated frames that are larger than the MTU.
+	 * Since we can't be sure how large a frame we'll get, setup
+	 * to handle the larges on possible.  If you're not using the
+	 * VPN driver and want to save memory, define ATH_ENFORCE_MTU
+	 * and you'll allocate only what you really need.
+	 */
+#ifdef ATH_ENFORCE_MTU
+	if (sc->sc_ic.ic_opmode == IEEE80211_M_MONITOR) {
+		sc->sc_rxbufsize = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz);
+	} else {
+		sc->sc_rxbufsize = roundup(sizeof(struct ieee80211_frame) +
+			dev->mtu + IEEE80211_CRC_LEN +
+			(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+			 IEEE80211_WEP_CRCLEN), sc->sc_cachelsz);
+	}
+#else
+	sc->sc_rxbufsize = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz);
+#endif
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: mtu %u cachelsz %u rxbufsize %u\n",
+		__func__, dev->mtu, sc->sc_cachelsz, sc->sc_rxbufsize);
+
+	sc->sc_rxlink = NULL;
+	STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+		int error = ath_rxbuf_init(sc, bf);
+		if (error != 0)
+			return error;
+	}
+
+	bf = STAILQ_FIRST(&sc->sc_rxbuf);
+	ath_hal_putrxbuf(ah, bf->bf_daddr);
+	ath_hal_rxena(ah);		/* enable recv descriptors */
+	ath_mode_init(&sc->sc_dev);	/* set filters, etc. */
+	ath_hal_startpcurecv(ah);	/* re-enable PCU/DMA engine */
+	return 0;
+}
+
+/* 
+ * Update internal state after a channel change.
+ */
+static void
+ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	enum ieee80211_phymode mode;
+
+	/*
+	 * Change channels and update the h/w rate map
+	 * if we're switching; e.g. 11a to 11b/g.
+	 */
+	mode = ieee80211_chan2mode(ic, chan);
+	if (mode != sc->sc_curmode)
+		ath_setcurmode(sc, mode);
+#ifdef notyet
+	/*
+	 * Update BPF state.
+	 */
+	sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
+		htole16(chan->ic_freq);
+	sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
+		htole16(chan->ic_flags);
+#endif
+}
+
+/*
+ * Set/change channels.  If the channel is really being changed,
+ * it's done by reseting the chip.  To accomplish this we must
+ * first cleanup any pending DMA, then restart stuff after a la
+ * ath_init.
+ */
+static int
+ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct net_device *dev = ic->ic_dev;
+	HAL_CHANNEL hchan;
+
+	/*
+	 * Convert to a HAL channel description with
+	 * the flags constrained to reflect the current
+	 * operating mode.
+	 */
+	hchan.channel = chan->ic_freq;
+	hchan.channelFlags = ath_chan2flags(ic, chan);
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n",
+		__func__,
+	    ath_hal_mhz2ieee(sc->sc_curchan.channel,
+		sc->sc_curchan.channelFlags),
+	    	sc->sc_curchan.channel,
+	    ath_hal_mhz2ieee(hchan.channel, hchan.channelFlags), hchan.channel);
+	if (hchan.channel != sc->sc_curchan.channel ||
+	    hchan.channelFlags != sc->sc_curchan.channelFlags) {
+		HAL_STATUS status;
+
+		/*
+		 * To switch channels clear any pending DMA operations;
+		 * wait long enough for the RX fifo to drain, reset the
+		 * hardware at the new frequency, and then re-enable
+		 * the relevant bits of the h/w.
+		 */
+		ath_hal_intrset(ah, 0);		/* disable interrupts */
+		ath_draintxq(sc);		/* clear pending tx frames */
+		ath_stoprecv(sc);		/* turn off frame recv */
+		if (!ath_hal_reset(ah, ic->ic_opmode, &hchan, AH_TRUE, &status)) {
+			printk("%s: %s: unable to reset channel %u (%uMhz): '%s'"
+				" (HAL status %u)\n", dev->name, __func__,
+				ieee80211_chan2ieee(ic, chan), chan->ic_freq,
+				hal_status_desc[status], status);
+			return EIO;
+		}
+		sc->sc_curchan = hchan;
+
+		/*
+		 * Re-enable rx framework.
+		 */
+		if (ath_startrecv(sc) != 0) {
+			printk("%s: %s: unable to restart recv logic\n",
+				dev->name, __func__);
+			return EIO;
+		}
+
+		/*
+		 * Change channels and update the h/w rate map
+		 * if we're switching; e.g. 11a to 11b/g.
+		 */
+		ic->ic_ibss_chan = chan;
+		ath_chan_change(sc, chan);
+
+		/*
+		 * Re-enable interrupts.
+		 */
+		ath_hal_intrset(ah, sc->sc_imask);
+	}
+	return 0;
+}
+
+static void
+ath_next_scan(unsigned long arg)
+{
+	struct net_device *dev = (struct net_device *) arg;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	if (ic->ic_state == IEEE80211_S_SCAN)
+		ieee80211_next_scan(ic);
+}
+
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void
+ath_calibrate(unsigned long arg)
+{
+	struct net_device *dev = (struct net_device *) arg;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+
+	sc->sc_stats.ast_per_cal++;
+
+	DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: channel %u/%x\n",
+		__func__, sc->sc_curchan.channel, sc->sc_curchan.channelFlags);
+
+	if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) {
+		/*
+		 * Rfgain is out of bounds, reset the chip
+		 * to load new gain values.
+		 */
+		sc->sc_stats.ast_per_rfgain++;
+		ath_reset(dev);
+	}
+	if (!ath_hal_calibrate(ah, &sc->sc_curchan)) {
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			"%s: calibration of channel %u failed\n",
+			__func__, sc->sc_curchan.channel);
+		sc->sc_stats.ast_per_calfail++;
+	}
+	sc->sc_cal_ch.expires = jiffies + (ath_calinterval * HZ);
+	add_timer(&sc->sc_cal_ch);
+}
+
+static int
+ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211_node *ni;
+	int i, error;
+	u_int8_t *bssid;
+	u_int32_t rfilt;
+	static const HAL_LED_STATE leds[] = {
+	    HAL_LED_INIT,	/* IEEE80211_S_INIT */
+	    HAL_LED_SCAN,	/* IEEE80211_S_SCAN */
+	    HAL_LED_AUTH,	/* IEEE80211_S_AUTH */
+	    HAL_LED_ASSOC, 	/* IEEE80211_S_ASSOC */
+	    HAL_LED_RUN, 	/* IEEE80211_S_RUN */
+	};
+
+	DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+		ieee80211_state_name[ic->ic_state],
+		ieee80211_state_name[nstate]);
+
+	del_timer(&sc->sc_scan_ch);		/* ap/neighbor scan timer */
+	del_timer(&sc->sc_cal_ch);		/* periodic calibration timer */
+	ath_hal_setledstate(ah, leds[nstate]);	/* set LED */
+	netif_stop_queue(dev);			/* before we do anything else */
+
+	if (nstate == IEEE80211_S_INIT) {
+		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+		ath_hal_intrset(ah, sc->sc_imask);
+		goto done;
+	}
+	ni = ic->ic_bss;
+	error = ath_chan_set(sc, ni->ni_chan);
+	if (error != 0)
+		goto bad;
+	rfilt = ath_calcrxfilter(sc);
+	if (nstate == IEEE80211_S_SCAN)
+		bssid = dev->broadcast;
+	else
+		bssid = ni->ni_bssid;
+	ath_hal_setrxfilter(ah, rfilt);
+	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s\n",
+		 __func__, rfilt, ether_sprintf(bssid));
+
+	if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA)
+		ath_hal_setassocid(ah, bssid, ni->ni_associd);
+	else
+		ath_hal_setassocid(ah, bssid, 0);
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		for (i = 0; i < IEEE80211_WEP_NKID; i++)
+			if (ath_hal_keyisvalid(ah, i))
+				ath_hal_keysetmac(ah, i, bssid);
+	}
+
+	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+		/* nothing to do */;
+	} else if (nstate == IEEE80211_S_RUN) {
+		DPRINTF(sc, ATH_DEBUG_STATE,
+			"%s(RUN): ic_flags=0x%08x iv=%d bssid=%s "
+			"capinfo=0x%04x chan=%d\n"
+			 , __func__
+			 , ic->ic_flags
+			 , ni->ni_intval
+			 , ether_sprintf(ni->ni_bssid)
+			 , ni->ni_capinfo
+			 , ieee80211_chan2ieee(ic, ni->ni_chan));
+
+		/*
+		 * Allocate and setup the beacon frame for AP or adhoc mode.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		    ic->ic_opmode == IEEE80211_M_IBSS) {
+			error = ath_beacon_alloc(sc, ni);
+			if (error != 0)
+				goto bad;
+		}
+
+		/*
+		 * Configure the beacon and sleep timers.
+		 */
+		ath_beacon_config(sc);
+	} else {
+		ath_hal_intrset(ah,
+			sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
+		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+	}
+done:
+	/*
+	 * Notify the rate control algorithm.
+	 */
+	ath_rate_newstate(sc, nstate);
+	/*
+	 * Invoke the parent method to complete the work.
+	 */
+	error = sc->sc_newstate(ic, nstate, arg);
+	/*
+	 * Finally, start any timers.
+	 */
+	if (nstate == IEEE80211_S_RUN) {
+		/* start periodic recalibration timer */
+		mod_timer(&sc->sc_cal_ch, jiffies + (ath_calinterval * HZ));
+	} else if (nstate == IEEE80211_S_SCAN) {
+		/* start ap/neighbor scan timer */
+		mod_timer(&sc->sc_scan_ch,
+			jiffies + ((HZ * ath_dwelltime) / 1000));
+	}
+bad:
+	netif_start_queue(dev);
+	return error;
+}
+
+/*
+ * Setup driver-specific state for a newly associated node.
+ * Note that we're called also on a re-associate, the isnew
+ * param tells us if this is the first time or not.
+ */
+static void
+ath_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
+{
+	struct ath_softc *sc = ic->ic_dev->priv;
+
+	ath_rate_newassoc(sc, ATH_NODE(ni), isnew);
+}
+
+static int
+ath_getchannels(struct net_device *dev, u_int cc,
+	HAL_BOOL outdoor, HAL_BOOL xchanmode)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_CHANNEL *chans;
+	int i, ix, nchan;
+
+	chans = kmalloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL), GFP_KERNEL);
+	if (chans == NULL) {
+		printk("%s: unable to allocate channel table\n", dev->name);
+		return ENOMEM;
+	}
+	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
+	    cc, HAL_MODE_ALL, outdoor, xchanmode)) {
+		u_int32_t rd;
+
+		ath_hal_getregdomain(ah, &rd);
+		printk("%s: unable to collect channel list from hal; "
+			"regdomain likely %u country code %u\n",
+			dev->name, rd, cc);
+		kfree(chans);
+		return EINVAL;
+	}
+
+	/*
+	 * Convert HAL channels to ieee80211 ones and insert
+	 * them in the table according to their channel number.
+	 */
+	for (i = 0; i < nchan; i++) {
+		HAL_CHANNEL *c = &chans[i];
+		ix = ath_hal_mhz2ieee(c->channel, c->channelFlags);
+		if (ix > IEEE80211_CHAN_MAX) {
+			printk("%s: bad hal channel %u (%u/%x) ignored\n",
+				dev->name, ix, c->channel, c->channelFlags);
+			continue;
+		}
+		/* NB: flags are known to be compatible */
+		if (ic->ic_channels[ix].ic_freq == 0) {
+			ic->ic_channels[ix].ic_freq = c->channel;
+			ic->ic_channels[ix].ic_flags = c->channelFlags;
+		} else {
+			/* channels overlap; e.g. 11g and 11b */
+			ic->ic_channels[ix].ic_flags |= c->channelFlags;
+		}
+	}
+	kfree(chans);
+	return 0;
+}
+
+static void
+ath_update_led(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t threshold;
+
+	/*
+	 * When not associated, flash LED on for 5s, off for 200ms.
+	 * XXX this assumes 100ms beacon interval.
+	 */
+	if (ic->ic_state != IEEE80211_S_RUN) {
+		threshold = 2 + sc->sc_ledstate * 48;
+	} else {
+		threshold = 2 + sc->sc_ledstate * 18;
+	}
+	if (ic->ic_stats.is_rx_beacon - sc->sc_beacons >= threshold) {
+		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+		ath_hal_gpioset(ah, sc->sc_ledpin, sc->sc_ledstate);
+		sc->sc_ledstate ^= 1;
+		sc->sc_beacons = ic->ic_stats.is_rx_beacon;
+	}
+}
+
+static int
+ath_rate_setup(struct net_device *dev, u_int mode)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	const HAL_RATE_TABLE *rt;
+	struct ieee80211_rateset *rs;
+	int i, maxrates;
+
+	switch (mode) {
+	case IEEE80211_MODE_11A:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11A);
+		break;
+	case IEEE80211_MODE_11B:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11B);
+		break;
+	case IEEE80211_MODE_11G:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11G);
+		break;
+	case IEEE80211_MODE_TURBO:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_TURBO);
+		break;
+	default:
+		DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n",
+			__func__, mode);
+		return 0;
+	}
+	rt = sc->sc_rates[mode];
+	if (rt == NULL)
+		return 0;
+	if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			"%s: rate table too small (%u > %u)\n",
+			__func__, rt->rateCount, IEEE80211_RATE_MAXSIZE);
+		maxrates = IEEE80211_RATE_MAXSIZE;
+	} else
+		maxrates = rt->rateCount;
+	rs = &ic->ic_sup_rates[mode];
+	for (i = 0; i < maxrates; i++)
+		rs->rs_rates[i] = rt->info[i].dot11Rate;
+	rs->rs_nrates = maxrates;
+	return 1;
+}
+
+static void
+ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
+{
+	const HAL_RATE_TABLE *rt;
+	int i;
+
+	memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
+	rt = sc->sc_rates[mode];
+	KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode));
+	for (i = 0; i < rt->rateCount; i++)
+		sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
+	memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
+	for (i = 0; i < 32; i++) {
+		u_int8_t ix = rt->rateCodeToIndex[i];
+		if (ix != 0xff)
+			sc->sc_hwmap[i] = rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
+	}
+	sc->sc_currates = rt;
+	sc->sc_curmode = mode;
+	/*
+	 * All protection frames are transmited at 2Mb/s for
+	 * 11g, otherwise at 1Mb/s.
+	 * XXX select protection rate index from rate table.
+	 */
+	sc->sc_protrix = (mode == IEEE80211_MODE_11G ? 1 : 0);
+	/* NB: caller is responsible for reseting rate control state */
+}
+
+#if IEEE80211_VLAN_TAG_USED
+static void
+ath_vlan_register(struct net_device *dev, struct vlan_group *grp)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	ieee80211_vlan_register(ic, grp);
+}
+
+static void
+ath_vlan_kill_vid(struct net_device *dev, unsigned short vid)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	ieee80211_vlan_kill_vid(ic, vid);
+}
+#endif /* IEEE80211_VLAN_TAG_USED */
+
+#ifdef AR_DEBUG
+static void
+ath_printrxbuf(struct ath_buf *bf, int done)
+{
+	struct ath_desc *ds = bf->bf_desc;
+
+	printk("R (%p %p) %08x %08x %08x %08x %08x %08x %c\n",
+	    ds, (struct ath_desc *)bf->bf_daddr,
+	    ds->ds_link, ds->ds_data,
+	    ds->ds_ctl0, ds->ds_ctl1,
+	    ds->ds_hw[0], ds->ds_hw[1],
+	    !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!');
+}
+
+static void
+ath_printtxbuf(struct ath_buf *bf, int done)
+{
+	struct ath_desc *ds = bf->bf_desc;
+
+	printk("T (%p %p) %08x %08x %08x %08x %08x %08x %08x %08x %c\n",
+	    ds, (struct ath_desc *)bf->bf_daddr,
+	    ds->ds_link, ds->ds_data,
+	    ds->ds_ctl0, ds->ds_ctl1,
+	    ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
+	    !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!');
+}
+#endif /* AR_DEBUG */
+
+/*
+ * Return netdevice statistics.
+ */
+static struct net_device_stats *
+ath_getstats(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct net_device_stats *stats = &sc->sc_devstats;
+
+	/* update according to private statistics */
+	stats->tx_errors = sc->sc_stats.ast_tx_encap
+			 + sc->sc_stats.ast_tx_nonode
+			 + sc->sc_stats.ast_tx_xretries
+			 + sc->sc_stats.ast_tx_fifoerr
+			 + sc->sc_stats.ast_tx_filtered
+			 ;
+	stats->tx_dropped = sc->sc_stats.ast_tx_nobuf
+			+ sc->sc_stats.ast_tx_nobufmgt;
+	stats->rx_errors = sc->sc_stats.ast_rx_tooshort
+			+ sc->sc_stats.ast_rx_crcerr
+			+ sc->sc_stats.ast_rx_fifoerr
+			+ sc->sc_stats.ast_rx_badcrypt
+			;
+	stats->rx_crc_errors = sc->sc_stats.ast_rx_crcerr;
+
+	return stats;
+}
+
+#ifdef CONFIG_NET_WIRELESS
+/*
+ * Return wireless extensions statistics.
+ */
+static struct iw_statistics *
+ath_iw_getstats(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct iw_statistics *is = &sc->sc_iwstats;
+
+	ieee80211_iw_getstats(ic, is);
+	/* add in statistics maintained by the driver */
+	is->discard.code += sc->sc_stats.ast_rx_badcrypt;
+	is->discard.retries += sc->sc_stats.ast_tx_xretries;
+	is->discard.misc += sc->sc_stats.ast_tx_encap
+			 + sc->sc_stats.ast_tx_nonode
+			 + sc->sc_stats.ast_tx_xretries
+			 + sc->sc_stats.ast_tx_fifoerr
+			 + sc->sc_stats.ast_tx_filtered
+			 + sc->sc_stats.ast_tx_nobuf
+			 + sc->sc_stats.ast_tx_nobufmgt;
+			 ;
+	is->miss.beacon = sc->sc_stats.ast_bmiss;
+
+	return &sc->sc_iwstats;
+}
+
+#include <net/iw_handler.h>
+/*
+ * Bounce functions to get to the 802.11 code. These are
+ * necessary for now because wireless extensions operations
+ * are done on the underlying device and not the 802.11 instance.
+ * This will change when there can be multiple 802.11 instances
+ * associated with a device and we must have a net_device for
+ * each so we can manipulate them individually.
+ */
+#define	ATH_CHAR_BOUNCE(name)						\
+static int								\
+ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info,	\
+	     	 char *erq, char *extra)				\
+{									\
+	struct ath_softc *sc = dev->priv;				\
+	return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra);	\
+}
+#define	ATH_POINT_BOUNCE(name)						\
+static int								\
+ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info,	\
+	     	 struct iw_point *erq, char *extra)			\
+{									\
+	struct ath_softc *sc = dev->priv;				\
+	return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra);	\
+}
+#define	ATH_PARAM_BOUNCE(name)						\
+static int								\
+ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info,	\
+	     	 struct iw_param *erq, char *extra)			\
+{									\
+	struct ath_softc *sc = dev->priv;				\
+	return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra);	\
+}
+#define	ATH_SOCKADDR_BOUNCE(name)					\
+static int								\
+ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info,	\
+	     	 struct sockaddr *erq, char *extra)			\
+{									\
+	struct ath_softc *sc = dev->priv;				\
+	return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra);	\
+}
+#define	ATH_FREQ_BOUNCE(name)						\
+static int								\
+ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info,	\
+	     	 struct iw_freq *erq, char *extra)			\
+{									\
+	struct ath_softc *sc = dev->priv;				\
+	return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra);	\
+}
+#define	ATH_U32_BOUNCE(name)						\
+static int								\
+ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info,	\
+	     	 __u32 *erq, char *extra)				\
+{									\
+	struct ath_softc *sc = dev->priv;				\
+	return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra);	\
+}
+#define	ATH_VOID_BOUNCE(name)						\
+static int								\
+ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info,	\
+	     	 void *erq, char *extra)				\
+{									\
+	struct ath_softc *sc = dev->priv;				\
+	return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra);	\
+}
+
+ATH_CHAR_BOUNCE(giwname)
+ATH_POINT_BOUNCE(siwencode)
+ATH_POINT_BOUNCE(giwencode)
+ATH_PARAM_BOUNCE(siwrate)
+ATH_PARAM_BOUNCE(giwrate)
+ATH_PARAM_BOUNCE(siwsens)
+ATH_PARAM_BOUNCE(giwsens)
+ATH_PARAM_BOUNCE(siwrts)
+ATH_PARAM_BOUNCE(giwrts)
+ATH_PARAM_BOUNCE(siwfrag)
+ATH_PARAM_BOUNCE(giwfrag)
+ATH_SOCKADDR_BOUNCE(siwap)
+ATH_SOCKADDR_BOUNCE(giwap)
+ATH_POINT_BOUNCE(siwnickn)
+ATH_POINT_BOUNCE(giwnickn)
+ATH_FREQ_BOUNCE(siwfreq)
+ATH_FREQ_BOUNCE(giwfreq)
+ATH_POINT_BOUNCE(siwessid)
+ATH_POINT_BOUNCE(giwessid)
+ATH_POINT_BOUNCE(giwrange)
+ATH_U32_BOUNCE(siwmode)
+ATH_U32_BOUNCE(giwmode)
+ATH_PARAM_BOUNCE(siwpower)
+ATH_PARAM_BOUNCE(giwpower)
+ATH_PARAM_BOUNCE(siwretry)
+ATH_PARAM_BOUNCE(giwretry)
+ATH_PARAM_BOUNCE(siwtxpow)
+ATH_PARAM_BOUNCE(giwtxpow)
+ATH_POINT_BOUNCE(iwaplist)
+#ifdef SIOCGIWSCAN
+ATH_POINT_BOUNCE(siwscan)
+ATH_POINT_BOUNCE(giwscan)
+#endif
+ATH_VOID_BOUNCE(setparam)
+ATH_VOID_BOUNCE(getparam)
+ATH_VOID_BOUNCE(setkey)
+ATH_VOID_BOUNCE(delkey)
+ATH_VOID_BOUNCE(setmlme)
+ATH_VOID_BOUNCE(setoptie)
+ATH_VOID_BOUNCE(getoptie)
+ATH_VOID_BOUNCE(addmac)
+ATH_VOID_BOUNCE(delmac)
+ATH_VOID_BOUNCE(chanlist)
+
+/* Structures to export the Wireless Handlers */
+static const iw_handler ath_handlers[] = {
+	(iw_handler) NULL,				/* SIOCSIWCOMMIT */
+	(iw_handler) ath_ioctl_giwname,			/* SIOCGIWNAME */
+	(iw_handler) NULL,				/* SIOCSIWNWID */
+	(iw_handler) NULL,				/* SIOCGIWNWID */
+	(iw_handler) ath_ioctl_siwfreq,			/* SIOCSIWFREQ */
+	(iw_handler) ath_ioctl_giwfreq,			/* SIOCGIWFREQ */
+	(iw_handler) ath_ioctl_siwmode,			/* SIOCSIWMODE */
+	(iw_handler) ath_ioctl_giwmode,			/* SIOCGIWMODE */
+	(iw_handler) ath_ioctl_siwsens,			/* SIOCSIWSENS */
+	(iw_handler) ath_ioctl_giwsens,			/* SIOCGIWSENS */
+	(iw_handler) NULL /* not used */,		/* SIOCSIWRANGE */
+	(iw_handler) ath_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) NULL,				/* SIOCSIWSPY */
+	(iw_handler) NULL,				/* SIOCGIWSPY */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) ath_ioctl_siwap,			/* SIOCSIWAP */
+	(iw_handler) ath_ioctl_giwap,			/* SIOCGIWAP */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) ath_ioctl_iwaplist,		/* SIOCGIWAPLIST */
+#ifdef SIOCGIWSCAN
+	(iw_handler) ath_ioctl_siwscan,			/* SIOCSIWSCAN */
+	(iw_handler) ath_ioctl_giwscan,			/* SIOCGIWSCAN */
+#else
+	(iw_handler) NULL,				/* SIOCSIWSCAN */
+	(iw_handler) NULL,				/* SIOCGIWSCAN */
+#endif /* SIOCGIWSCAN */
+	(iw_handler) ath_ioctl_siwessid,		/* SIOCSIWESSID */
+	(iw_handler) ath_ioctl_giwessid,		/* SIOCGIWESSID */
+	(iw_handler) ath_ioctl_siwnickn,		/* SIOCSIWNICKN */
+	(iw_handler) ath_ioctl_giwnickn,		/* SIOCGIWNICKN */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) ath_ioctl_siwrate,			/* SIOCSIWRATE */
+	(iw_handler) ath_ioctl_giwrate,			/* SIOCGIWRATE */
+	(iw_handler) ath_ioctl_siwrts,			/* SIOCSIWRTS */
+	(iw_handler) ath_ioctl_giwrts,			/* SIOCGIWRTS */
+	(iw_handler) ath_ioctl_siwfrag,			/* SIOCSIWFRAG */
+	(iw_handler) ath_ioctl_giwfrag,			/* SIOCGIWFRAG */
+	(iw_handler) ath_ioctl_siwtxpow,		/* SIOCSIWTXPOW */
+	(iw_handler) ath_ioctl_giwtxpow,		/* SIOCGIWTXPOW */
+	(iw_handler) ath_ioctl_siwretry,		/* SIOCSIWRETRY */
+	(iw_handler) ath_ioctl_giwretry,		/* SIOCGIWRETRY */
+	(iw_handler) ath_ioctl_siwencode,		/* SIOCSIWENCODE */
+	(iw_handler) ath_ioctl_giwencode,		/* SIOCGIWENCODE */
+	(iw_handler) ath_ioctl_siwpower,		/* SIOCSIWPOWER */
+	(iw_handler) ath_ioctl_giwpower,		/* SIOCGIWPOWER */
+};
+static const iw_handler ath_priv_handlers[] = {
+	(iw_handler) ath_ioctl_setparam,		/* SIOCWFIRSTPRIV+0 */
+	(iw_handler) ath_ioctl_getparam,		/* SIOCWFIRSTPRIV+1 */
+	(iw_handler) ath_ioctl_setkey,			/* SIOCWFIRSTPRIV+2 */
+	(iw_handler) NULL,				/* SIOCWFIRSTPRIV+3 */
+	(iw_handler) ath_ioctl_delkey,			/* SIOCWFIRSTPRIV+4 */
+	(iw_handler) NULL,				/* SIOCWFIRSTPRIV+5 */
+	(iw_handler) ath_ioctl_setmlme,			/* SIOCWFIRSTPRIV+6 */
+	(iw_handler) NULL,				/* SIOCWFIRSTPRIV+7 */
+	(iw_handler) ath_ioctl_setoptie,		/* SIOCWFIRSTPRIV+8 */
+	(iw_handler) ath_ioctl_getoptie,		/* SIOCWFIRSTPRIV+9 */
+	(iw_handler) ath_ioctl_addmac,			/* SIOCWFIRSTPRIV+10 */
+	(iw_handler) NULL,				/* SIOCWFIRSTPRIV+11 */
+	(iw_handler) ath_ioctl_delmac,			/* SIOCWFIRSTPRIV+12 */
+	(iw_handler) NULL,				/* SIOCWFIRSTPRIV+13 */
+	(iw_handler) ath_ioctl_chanlist,		/* SIOCWFIRSTPRIV+14 */
+};
+
+static struct iw_handler_def ath_iw_handler_def = {
+#define	N(a)	(sizeof (a) / sizeof (a[0]))
+	.standard		= (iw_handler *) ath_handlers,
+	.num_standard		= N(ath_handlers),
+	.private		= (iw_handler *) ath_priv_handlers,
+	.num_private		= N(ath_priv_handlers),
+#undef N
+};
+#endif /* CONFIG_NET_WIRELESS */
+
+static int
+ath_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct sockaddr *mac = addr;
+	int error;
+
+	if (netif_running(dev)) {
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			"%s: cannot set address; device running\n", __func__);
+		return -EBUSY;
+	}
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+		__func__,
+		mac->sa_data[0], mac->sa_data[1], mac->sa_data[2],
+		mac->sa_data[3], mac->sa_data[4], mac->sa_data[5]);
+
+	ATH_LOCK(sc);
+	/* XXX not right for multiple vap's */
+	IEEE80211_ADDR_COPY(ic->ic_myaddr, mac->sa_data);
+	IEEE80211_ADDR_COPY(dev->dev_addr, mac->sa_data);
+	ath_hal_setmac(ah, dev->dev_addr);
+	error = -ath_reset(dev);
+	ATH_UNLOCK(sc);
+
+	return error;
+}
+
+static int      
+ath_change_mtu(struct net_device *dev, int mtu) 
+{
+	struct ath_softc *sc = dev->priv;
+	int error;
+
+	if (!(ATH_MIN_MTU < mtu && mtu <= ATH_MAX_MTU)) {
+		DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %d, min %u, max %u\n",
+			__func__, mtu, ATH_MIN_MTU, ATH_MAX_MTU);
+		return -EINVAL;
+	}
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: %d\n", __func__, mtu);
+
+	ATH_LOCK(sc);
+	dev->mtu = mtu;
+	/* NB: the rx buffers may need to be reallocated */
+	tasklet_disable(&sc->sc_rxtq);
+	error = -ath_reset(dev);
+	tasklet_enable(&sc->sc_rxtq);
+	ATH_UNLOCK(sc);
+
+	return error;
+}
+
+/*
+ * Diagnostic interface to the HAL.  This is used by various
+ * tools to do things like retrieve register contents for
+ * debugging.  The mechanism is intentionally opaque so that
+ * it can change frequently w/o concern for compatiblity.
+ */
+static int
+ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	u_int id = ad->ad_id & ATH_DIAG_ID;
+	void *indata = NULL;
+	void *outdata = NULL;
+	u_int32_t insize = ad->ad_in_size;
+	u_int32_t outsize = ad->ad_out_size;
+	int error = 0;
+
+	if (ad->ad_id & ATH_DIAG_IN) {
+		/*
+		 * Copy in data.
+		 */
+		indata = kmalloc(insize, GFP_KERNEL);
+		if (indata == NULL) {
+			error = -ENOMEM;
+			goto bad;
+		}
+		if (copy_from_user(indata, ad->ad_in_data, insize)) {
+			error = -EFAULT;
+			goto bad;
+		}
+	}
+	if (ad->ad_id & ATH_DIAG_DYN) {
+		/*
+		 * Allocate a buffer for the results (otherwise the HAL
+		 * returns a pointer to a buffer where we can read the
+		 * results).  Note that we depend on the HAL leaving this
+		 * pointer for us to use below in reclaiming the buffer;
+		 * may want to be more defensive.
+		 */
+		outdata = kmalloc(outsize, GFP_KERNEL);
+		if (outdata == NULL) {
+			error = -ENOMEM;
+			goto bad;
+		}
+	}
+	if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) {
+		if (outsize < ad->ad_out_size)
+			ad->ad_out_size = outsize;
+		if (outdata &&
+		     copy_to_user(ad->ad_out_data, outdata, ad->ad_out_size))
+			error = -EFAULT;
+	} else {
+		error = -EINVAL;
+	}
+bad:
+	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
+		kfree(indata);
+	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
+		kfree(outdata);
+	return error;
+}
+
+extern	int ath_ioctl_ethtool(struct ath_softc *sc, int cmd, void *addr);
+
+static int
+ath_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int error;
+
+	ATH_LOCK(sc);
+	switch (cmd) {
+	case SIOCGATHSTATS:
+		sc->sc_stats.ast_rx_rssi = ieee80211_getrssi(ic);
+		if (copy_to_user(ifr->ifr_data, &sc->sc_stats,
+		    sizeof (sc->sc_stats)))
+			error = -EFAULT;
+		else
+			error = 0;
+		break;
+	case SIOCGATHDIAG:
+		if (!capable(CAP_NET_ADMIN))
+			error = -EPERM;
+		else
+			error = ath_ioctl_diag(sc, (struct ath_diag *) ifr);
+		break;
+	case SIOCETHTOOL:
+		if (copy_from_user(&cmd, ifr->ifr_data, sizeof(cmd)))
+			error = -EFAULT;
+		else
+			error = ath_ioctl_ethtool(sc, cmd, ifr->ifr_data);
+			break;
+	default:
+		error = ieee80211_ioctl(ic, ifr, cmd);
+		break;
+	}
+	ATH_UNLOCK(sc);
+	return error;
+}
+
+#ifdef CONFIG_SYSCTL
+/*
+ * Sysctls are split into ``static'' and ``dynamic'' tables.
+ * The former are defined at module load time and are used
+ * control parameters common to all devices.  The latter are
+ * tied to particular device instances and come and go with
+ * each device.  The split is currently a bit tenuous; many of
+ * the static ones probably should be dynamic but having them
+ * static (e.g. debug) means they can be set after a module is
+ * loaded and before bringing up a device.  The alternative
+ * is to add module parameters.
+ */
+
+/*
+ * Dynamic (i.e. per-device) sysctls.  These are automatically
+ * mirrored in /proc/sys.
+ */
+enum {
+	ATH_SLOTTIME	= 1,
+	ATH_ACKTIMEOUT	= 2,
+	ATH_CTSTIMEOUT	= 3,
+	ATH_SOFTLED	= 4,
+	ATH_LEDPIN	= 5,
+	ATH_COUNTRYCODE	= 6,
+	ATH_REGDOMAIN	= 7,
+	ATH_DEBUG	= 8,
+	ATH_TXANTENNA	= 9,
+	ATH_RXANTENNA	= 10,
+	ATH_DIVERSITY	= 11,
+	ATH_TXINTRPERIOD= 12,
+};
+
+static int
+ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos)
+{
+	struct ath_softc *sc = ctl->extra1;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int val;
+	int ret;
+
+	ctl->data = &val;
+	ctl->maxlen = sizeof(val);
+	if (write) {
+		ret = ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
+				lenp, ppos);
+		if (ret == 0) {
+			switch (ctl->ctl_name) {
+			case ATH_SLOTTIME:
+				if (!ath_hal_setslottime(ah, val))
+					ret = -EINVAL;
+				break;
+			case ATH_ACKTIMEOUT:
+				if (!ath_hal_setacktimeout(ah, val))
+					ret = -EINVAL;
+				break;
+			case ATH_CTSTIMEOUT:
+				if (!ath_hal_setctstimeout(ah, val))
+					ret = -EINVAL;
+				break;
+			case ATH_SOFTLED:
+				if (val != sc->sc_softled) {
+					if (val)
+						ath_hal_gpioCfgOutput(ah,
+							sc->sc_ledpin);
+					ath_hal_gpioset(ah, sc->sc_ledpin,!val);
+					sc->sc_softled = val;
+				}
+				break;
+			case ATH_LEDPIN:
+				sc->sc_ledpin = val;
+				break;
+			case ATH_DEBUG:
+				sc->sc_debug = val;
+				break;
+			case ATH_TXANTENNA:
+				/* XXX validate? */
+				sc->sc_txantenna = val;
+				break;
+			case ATH_RXANTENNA:
+				/* XXX validate? */
+				ath_setdefantenna(sc, val);
+				break;
+			case ATH_DIVERSITY:
+				/* XXX validate? */
+				if (!sc->sc_hasdiversity)
+					return -EINVAL;
+				sc->sc_diversity = val;
+				ath_hal_setdiversity(ah, val);
+				break;
+			case ATH_TXINTRPERIOD:
+				sc->sc_txintrperiod = val;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+	} else {
+		switch (ctl->ctl_name) {
+		case ATH_SLOTTIME:
+			val = ath_hal_getslottime(ah);
+			break;
+		case ATH_ACKTIMEOUT:
+			val = ath_hal_getacktimeout(ah);
+			break;
+		case ATH_CTSTIMEOUT:
+			val = ath_hal_getctstimeout(ah);
+			break;
+		case ATH_SOFTLED:
+			val = sc->sc_softled;
+			break;
+		case ATH_LEDPIN:
+			val = sc->sc_ledpin;
+			break;
+		case ATH_COUNTRYCODE:
+			ath_hal_getcountrycode(ah, &val);
+			break;
+		case ATH_REGDOMAIN:
+			ath_hal_getregdomain(ah, &val);
+			break;
+		case ATH_DEBUG:
+			val = sc->sc_debug;
+			break;
+		case ATH_TXANTENNA:
+			val = sc->sc_txantenna;
+			break;
+		case ATH_RXANTENNA:
+			val = ath_hal_getdefantenna(ah);
+			break;
+		case ATH_DIVERSITY:
+			val = sc->sc_diversity;
+			break;
+		case ATH_TXINTRPERIOD:
+			val = sc->sc_txintrperiod;
+			break;
+		default:
+			return -EINVAL;
+		}
+		ret = ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
+				lenp, ppos);
+	}
+	return ret;
+}
+
+static	int mindwelltime = 100;			/* 100ms */
+static	int mincalibrate = 1;			/* once a second */
+static	int maxint = 0x7fffffff;		/* 32-bit big */
+
+#define	CTL_AUTO	-2	/* cannot be CTL_ANY or CTL_NONE */
+
+static const ctl_table ath_sysctl_template[] = {
+	{ .ctl_name	= ATH_SLOTTIME,
+	  .procname	= "slottime",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_ACKTIMEOUT,
+	  .procname	= "acktimeout",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_CTSTIMEOUT,
+	  .procname	= "ctstimeout",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_SOFTLED,
+	  .procname	= "softled",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_LEDPIN,
+	  .procname	= "ledpin",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_COUNTRYCODE,
+	  .procname	= "countrycode",
+	  .mode		= 0444,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_REGDOMAIN,
+	  .procname	= "regdomain",
+	  .mode		= 0444,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+#ifdef AR_DEBUG
+	{ .ctl_name	= ATH_DEBUG,
+	  .procname	= "debug",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+#endif
+	{ .ctl_name	= ATH_TXANTENNA,
+	  .procname	= "txantenna",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_RXANTENNA,
+	  .procname	= "rxantenna",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_DIVERSITY,
+	  .procname	= "diversity",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ .ctl_name	= ATH_TXINTRPERIOD,
+	  .procname	= "txintrperiod",
+	  .mode		= 0644,
+	  .proc_handler	= ath_sysctl_halparam
+	},
+	{ 0 }
+};
+
+static void
+ath_dynamic_sysctl_register(struct ath_softc *sc)
+{
+	int i, space;
+
+	space = 5*sizeof(struct ctl_table) + sizeof(ath_sysctl_template);
+	sc->sc_sysctls = kmalloc(space, GFP_KERNEL);
+	if (sc->sc_sysctls == NULL) {
+		printk("%s: no memory for sysctl table!\n", __func__);
+		return;
+	}
+
+	/* setup the table */
+	memset(sc->sc_sysctls, 0, space);
+	sc->sc_sysctls[0].ctl_name = CTL_DEV;
+	sc->sc_sysctls[0].procname = "dev";
+	sc->sc_sysctls[0].mode = 0555;
+	sc->sc_sysctls[0].child = &sc->sc_sysctls[2];
+	/* [1] is NULL terminator */
+	sc->sc_sysctls[2].ctl_name = CTL_AUTO;
+	sc->sc_sysctls[2].procname = sc->sc_dev.name;
+	sc->sc_sysctls[2].mode = 0555;
+	sc->sc_sysctls[2].child = &sc->sc_sysctls[4];
+	/* [3] is NULL terminator */
+	/* copy in pre-defined data */
+	memcpy(&sc->sc_sysctls[4], ath_sysctl_template,
+		sizeof(ath_sysctl_template));
+
+	/* add in dynamic data references */
+	for (i = 4; sc->sc_sysctls[i].ctl_name; i++)
+		if (sc->sc_sysctls[i].extra1 == NULL)
+			sc->sc_sysctls[i].extra1 = sc;
+
+	/* and register everything */
+	sc->sc_sysctl_header = register_sysctl_table(sc->sc_sysctls, 1);
+	if (!sc->sc_sysctl_header) {
+		printk("%s: failed to register sysctls!\n", sc->sc_dev.name);
+		kfree(sc->sc_sysctls);
+		sc->sc_sysctls = NULL;
+	}
+
+	/* initialize values */
+	sc->sc_debug = ath_debug;
+	sc->sc_txantenna = 0;		/* default to auto-selection */
+	sc->sc_txintrperiod = ATH_TXINTR_PERIOD;
+}
+
+static void
+ath_dynamic_sysctl_unregister(struct ath_softc *sc)
+{
+	if (sc->sc_sysctl_header) {
+		unregister_sysctl_table(sc->sc_sysctl_header);
+		sc->sc_sysctl_header = NULL;
+	}
+	if (sc->sc_sysctls) {
+		kfree(sc->sc_sysctls);
+		sc->sc_sysctls = NULL;
+	}
+}
+
+/*
+ * Announce various information on device/driver attach.
+ */
+static void
+ath_announce(struct net_device *dev)
+{
+#define	HAL_MODE_DUALBAND	(HAL_MODE_11A|HAL_MODE_11B)
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int modes, cc;
+	int i;
+
+	printk("%s: mac %d.%d phy %d.%d", dev->name,
+		ah->ah_macVersion, ah->ah_macRev,
+		ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf);
+	/*
+	 * Print radio revision(s).  We check the wireless modes
+	 * to avoid falsely printing revs for inoperable parts.
+	 * Dual-band radio revs are returned in the 5Ghz rev number.
+	 */
+	ath_hal_getcountrycode(ah, &cc);
+	modes = ath_hal_getwirelessmodes(ah, cc);
+	if ((modes & HAL_MODE_DUALBAND) == HAL_MODE_DUALBAND) {
+		if (ah->ah_analog5GhzRev && ah->ah_analog2GhzRev)
+			printk(" 5ghz radio %d.%d 2ghz radio %d.%d",
+				ah->ah_analog5GhzRev >> 4,
+				ah->ah_analog5GhzRev & 0xf,
+				ah->ah_analog2GhzRev >> 4,
+				ah->ah_analog2GhzRev & 0xf);
+		else
+			printk(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
+				ah->ah_analog5GhzRev & 0xf);
+	} else
+		printk(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
+			ah->ah_analog5GhzRev & 0xf);
+	printk("\n");
+	printk("%s: 802.11 address: %s\n",
+		dev->name, ether_sprintf(dev->dev_addr));
+	for (i = 0; i <= WME_AC_VO; i++) {
+		struct ath_txq *txq = sc->sc_ac2q[i];
+		printk("%s: Use hw queue %u for %s traffic\n",
+			dev->name, txq->axq_qnum, acnames[i]);
+	}
+#undef HAL_MODE_DUALBAND
+}
+
+/*
+ * Static (i.e. global) sysctls.  Note that the hal sysctls
+ * are located under ours by sharing the setting for DEV_ATH.
+ */
+enum {
+	DEV_ATH		= 9,			/* XXX known by hal */
+};
+
+static ctl_table ath_static_sysctls[] = {
+#ifdef AR_DEBUG
+	{ .ctl_name	= CTL_AUTO,
+	   .procname	= "debug",
+	  .mode		= 0644,
+	  .data		= &ath_debug,
+	  .maxlen	= sizeof(ath_debug),
+	  .proc_handler	= proc_dointvec
+	},
+#endif
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "countrycode",
+	  .mode		= 0444,
+	  .data		= &ath_countrycode,
+	  .maxlen	= sizeof(ath_countrycode),
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "regdomain",
+	  .mode		= 0444,
+	  .data		= &ath_regdomain,
+	  .maxlen	= sizeof(ath_regdomain),
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "outdoor",
+	  .mode		= 0444,
+	  .data		= &ath_outdoor,
+	  .maxlen	= sizeof(ath_outdoor),
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "xchanmode",
+	  .mode		= 0444,
+	  .data		= &ath_xchanmode,
+	  .maxlen	= sizeof(ath_xchanmode),
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "dwelltime",
+	  .mode		= 0644,
+	  .data		= &ath_dwelltime,
+	  .maxlen	= sizeof(ath_dwelltime),
+	  .extra1	= &mindwelltime,
+	  .extra2	= &maxint,
+	  .proc_handler	= proc_dointvec_minmax
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "calibrate",
+	  .mode		= 0644,
+	  .data		= &ath_calinterval,
+	  .maxlen	= sizeof(ath_calinterval),
+	  .extra1	= &mincalibrate,
+	  .extra2	= &maxint,
+	  .proc_handler	= proc_dointvec_minmax
+	},
+	{ 0 }
+};
+static ctl_table ath_ath_table[] = {
+	{ .ctl_name	= DEV_ATH,
+	  .procname	= "ath",
+	  .mode		= 0555,
+	  .child	= ath_static_sysctls
+	}, { 0 }
+};
+static ctl_table ath_root_table[] = {
+	{ .ctl_name	= CTL_DEV,
+	  .procname	= "dev",
+	  .mode		= 0555,
+	  .child	= ath_ath_table
+	}, { 0 }
+};
+static struct ctl_table_header *ath_sysctl_header;
+
+void
+ath_sysctl_register(void)
+{
+	static int initialized = 0;
+
+	if (!initialized) {
+		ath_sysctl_header =
+			register_sysctl_table(ath_root_table, 1);
+		initialized = 1;
+	}
+}
+
+void
+ath_sysctl_unregister(void)
+{
+	if (ath_sysctl_header)
+		unregister_sysctl_table(ath_sysctl_header);
+}
+#endif /* CONFIG_SYSCTL */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.c	2005-02-24 13:06:17.184149632 -0800
@@ -0,0 +1,361 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_ath_pci.c,v 1.7 2005/02/16 16:08:41 samleffler Exp $
+ */
+#include "opt_ah.h"
+
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/cache.h>
+
+#include <linux/pci.h>
+
+#include <asm/uaccess.h>
+
+#include "if_media.h"
+#include <net80211/ieee80211_var.h>
+
+#include "if_athvar.h"
+#include "if_ath_pci.h"
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
+/*
+ * PCI initialization uses Linux 2.4.x version and
+ * older kernels do not support this
+ */
+#error Atheros PCI version requires at least Linux kernel version 2.4.0
+#endif /* kernel < 2.4.0 */
+
+struct ath_pci_softc {
+	struct ath_softc	aps_sc;
+#ifdef CONFIG_PM
+	u32			aps_pmstate[16];
+#endif
+};
+
+/*
+ * User a static table of PCI id's for now.  While this is the
+ * "new way" to do things, we may want to switch back to having
+ * the HAL check them by defining a probe method.
+ */
+static struct pci_device_id ath_pci_id_table[] __devinitdata = {
+	{ 0x168c, 0x0007, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x168c, 0x0012, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x168c, 0x0013, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0xa727, 0x0013, PCI_ANY_ID, PCI_ANY_ID },	/* 3com */
+	{ 0x10b7, 0x0013, PCI_ANY_ID, PCI_ANY_ID },	/* 3com 3CRDAG675 */
+	{ 0x168c, 0x1014, PCI_ANY_ID, PCI_ANY_ID },	/* IBM minipci 5212 */
+	{ 0x168c, 0x0015, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x168c, 0x0016, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x168c, 0x0017, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x168c, 0x0018, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x168c, 0x0019, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x168c, 0x001a, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0 }
+};
+
+static int
+ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	unsigned long phymem;
+	unsigned long mem;
+	struct ath_pci_softc *sc;
+	struct net_device *dev;
+	const char *athname;
+	u_int8_t csz;
+	u32 val;
+
+	if (pci_enable_device(pdev))
+		return (-EIO);
+
+	/* XXX 32-bit addressing only */
+	if (pci_set_dma_mask(pdev, 0xffffffff)) {
+		printk(KERN_ERR "ath_pci: 32-bit DMA not available\n");
+		goto bad;
+	}
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
+	if (csz == 0) {
+		/*
+		 * Linux 2.4.18 (at least) writes the cache line size
+		 * register as a 16-bit wide register which is wrong.
+		 * We must have this setup properly for rx buffer
+		 * DMA to work so force a reasonable value here if it
+		 * comes up zero.
+		 */
+		csz = L1_CACHE_BYTES / sizeof(u_int32_t);
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
+	}
+	/*
+	 * The default setting of latency timer yields poor results,
+	 * set it to the value used by other systems.  It may be worth
+	 * tweaking this setting more.
+	 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+
+	pci_set_master(pdev);
+
+	/*
+	 * Disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state.
+	 *
+	 * Code taken from ipw2100 driver - jg
+	 */
+	pci_read_config_dword(pdev, 0x40, &val);
+	if ((val & 0x0000ff00) != 0)
+		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+	phymem = pci_resource_start(pdev, 0);
+	if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "ath")) {
+		printk(KERN_ERR "ath_pci: cannot reserve PCI memory region\n");
+		goto bad;
+	}
+
+	mem = (unsigned long) ioremap(phymem, pci_resource_len(pdev, 0));
+	if (!mem) {
+		printk(KERN_ERR "ath_pci: cannot remap PCI memory region\n") ;
+		goto bad1;
+	}
+
+	sc = kmalloc(sizeof(struct ath_pci_softc), GFP_KERNEL);
+	if (sc == NULL) {
+		printk(KERN_ERR "ath_pci: no memory for device state\n");
+		goto bad2;
+	}
+	memset(sc, 0, sizeof(struct ath_pci_softc));
+
+	/*
+	 * Mark the device as detached to avoid processing
+	 * interrupts until setup is complete.
+	 */
+	sc->aps_sc.sc_invalid = 1;
+
+	dev = &sc->aps_sc.sc_dev;	/* XXX blech, violate layering */
+	memcpy(dev->name, "ath%d", sizeof("ath%d"));
+
+	dev->irq = pdev->irq;
+	dev->mem_start = mem;
+	dev->mem_end = mem + pci_resource_len(pdev, 0);
+	dev->priv = sc;
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	sc->aps_sc.sc_bdev = (void *) pdev;
+
+	pci_set_drvdata(pdev, dev);
+
+	if (request_irq(dev->irq, ath_intr, SA_SHIRQ, dev->name, dev)) {
+		printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
+		goto bad3;
+	}
+
+	if (ath_attach(id->device, dev) != 0)
+		goto bad4;
+
+	athname = ath_hal_probe(id->vendor, id->device);
+	printk(KERN_INFO "%s: %s: mem=0x%lx, irq=%d\n",
+		dev->name, athname ? athname : "Atheros ???", phymem, dev->irq);
+
+	/* ready to process interrupts */
+	sc->aps_sc.sc_invalid = 0;
+
+	return 0;
+bad4:
+	free_irq(dev->irq, dev);
+bad3:
+	kfree(sc);
+bad2:
+	iounmap((void *) mem);
+bad1:
+	release_mem_region(phymem, pci_resource_len(pdev, 0));
+bad:
+	pci_disable_device(pdev);
+	return (-ENODEV);
+}
+
+static void
+ath_pci_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	ath_detach(dev);
+	if (dev->irq)
+		free_irq(dev->irq, dev);
+	iounmap((void *) dev->mem_start);
+	release_mem_region(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+	pci_disable_device(pdev);
+	free_netdev(dev);
+}
+
+#ifdef CONFIG_PM
+static int
+ath_pci_suspend(struct pci_dev *pdev, u32 state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	ath_suspend(dev);
+	PCI_SAVE_STATE(pdev,
+		((struct ath_pci_softc *)dev->priv)->aps_pmstate);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, 3);
+
+	return (0);
+}
+
+static int
+ath_pci_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	u32 val;
+
+	pci_enable_device(pdev);
+	PCI_RESTORE_STATE(pdev,
+		((struct ath_pci_softc *)dev->priv)->aps_pmstate);
+	/*
+	 * 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
+	 *
+	 * Code taken from ipw2100 driver - jg
+	 */
+	pci_read_config_dword(pdev, 0x40, &val);
+	if ((val & 0x0000ff00) != 0)
+		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+	ath_resume(dev);
+
+	return (0);
+}
+#endif /* CONFIG_PM */
+
+MODULE_DEVICE_TABLE(pci, ath_pci_id_table);
+
+static struct pci_driver ath_pci_drv_id = {
+	.name		= "ath_pci",
+	.id_table	= ath_pci_id_table,
+	.probe		= ath_pci_probe,
+	.remove		= ath_pci_remove,
+#ifdef CONFIG_PM
+	.suspend	= ath_pci_suspend,
+	.resume		= ath_pci_resume,
+#endif /* CONFIG_PM */
+	/* Linux 2.4.6 has save_state and enable_wake that are not used here */
+};
+
+/*
+ * Module glue.
+ */
+#include "version.h"
+static char *version = ATH_PCI_VERSION " (EXPERIMENTAL)";
+static char *dev_info = "ath_pci";
+
+#include <linux/ethtool.h>
+
+int
+ath_ioctl_ethtool(struct ath_softc *sc, int cmd, void *addr)
+{
+	struct ethtool_drvinfo info;
+
+	if (cmd != ETHTOOL_GDRVINFO)
+		return -EOPNOTSUPP;
+	memset(&info, 0, sizeof(info));
+	info.cmd = cmd;
+	strncpy(info.driver, dev_info, sizeof(info.driver)-1);
+	strncpy(info.version, version, sizeof(info.version)-1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22)
+	/* include the device name so later versions of kudzu DTRT */
+	strncpy(info.bus_info, pci_name((struct pci_dev *)sc->sc_bdev),
+		sizeof(info.bus_info)-1);
+#endif
+	return copy_to_user(addr, &info, sizeof(info)) ? -EFAULT : 0;
+}
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("Support for Atheros 802.11 wireless LAN cards.");
+MODULE_SUPPORTED_DEVICE("Atheros WLAN cards");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static int __init
+init_ath_pci(void)
+{
+	printk(KERN_INFO "%s: %s\n", dev_info, version);
+
+	if (pci_register_driver(&ath_pci_drv_id) < 0) {
+		printk("ath_pci: No devices found, driver not installed.\n");
+		pci_unregister_driver(&ath_pci_drv_id);
+		return (-ENODEV);
+	}
+#ifdef CONFIG_SYSCTL
+	ath_sysctl_register();
+#endif
+	return (0);
+}
+module_init(init_ath_pci);
+
+static void __exit
+exit_ath_pci(void)
+{
+#ifdef CONFIG_SYSCTL
+	ath_sysctl_unregister();
+#endif
+	pci_unregister_driver(&ath_pci_drv_id);
+
+	printk(KERN_INFO "%s: driver unloaded\n", dev_info);
+}
+module_exit(exit_ath_pci);
+
+/* return bus cachesize in 4B word units */
+void
+bus_read_cachesize(struct ath_softc *sc, u_int8_t *csz)
+{
+	pci_read_config_byte(sc->sc_bdev, PCI_CACHE_LINE_SIZE, csz);
+}
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.h	2005-02-24 13:06:17.184149632 -0800
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_ath_pci.h,v 1.3 2005/02/16 16:08:41 samleffler Exp $
+ */
+ 
+#ifndef _DEV_ATH_PCI_H_
+#define _DEV_ATH_PCI_H_
+
+#include <linux/pci.h>
+#define bus_map_single		pci_map_single
+#define bus_unmap_single	pci_unmap_single
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+#define bus_dma_sync_single	pci_dma_sync_single_for_cpu
+#define	PCI_SAVE_STATE(a,b)	pci_save_state(a)
+#define	PCI_RESTORE_STATE(a,b)	pci_restore_state(a)
+#else
+#define bus_dma_sync_single	pci_dma_sync_single
+#define	PCI_SAVE_STATE(a,b)	pci_save_state(a,b)
+#define	PCI_RESTORE_STATE(a,b)	pci_restore_state(a,b)
+#endif
+#define bus_alloc_consistent	pci_alloc_consistent
+#define bus_free_consistent	pci_free_consistent
+#define BUS_DMA_FROMDEVICE	PCI_DMA_FROMDEVICE
+#define BUS_DMA_TODEVICE	PCI_DMA_TODEVICE
+
+#endif   /* _DEV_ATH_PCI_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athioctl.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athioctl.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athioctl.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athioctl.h	2005-02-24 13:06:17.184149632 -0800
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: src/sys/dev/ath/if_athioctl.h,v 1.5 2003/12/28 07:00:32 sam Exp $
+ */
+
+/*
+ * Ioctl-related defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHIOCTL_H
+#define _DEV_ATH_ATHIOCTL_H
+
+struct ath_stats {
+	u_int32_t	ast_watchdog;	/* device reset by watchdog */
+	u_int32_t	ast_hardware;	/* fatal hardware error interrupts */
+	u_int32_t	ast_bmiss;	/* beacon miss interrupts */
+	u_int32_t	ast_rxorn;	/* rx overrun interrupts */
+	u_int32_t	ast_rxeol;	/* rx eol interrupts */
+	u_int32_t	ast_txurn;	/* tx underrun interrupts */
+	u_int32_t	ast_mib;	/* mib interrupts */
+	u_int32_t	ast_tx_mgmt;	/* management frames transmitted */
+	u_int32_t	ast_tx_discard;	/* frames discarded prior to assoc */
+	u_int32_t	ast_tx_invalid;	/* frames discarded 'cuz device gone */
+	u_int32_t	ast_tx_qstop;	/* tx queue stopped 'cuz full */
+	u_int32_t	ast_tx_encap;	/* tx encapsulation failed */
+	u_int32_t	ast_tx_nonode;	/* tx failed 'cuz no node */
+	u_int32_t	ast_tx_nobuf;	/* tx failed 'cuz no tx buffer (data) */
+	u_int32_t	ast_tx_nobufmgt;/* tx failed 'cuz no tx buffer (mgmt)*/
+	u_int32_t	ast_tx_xretries;/* tx failed 'cuz too many retries */
+	u_int32_t	ast_tx_fifoerr;	/* tx failed 'cuz FIFO underrun */
+	u_int32_t	ast_tx_filtered;/* tx failed 'cuz xmit filtered */
+	u_int32_t	ast_tx_shortretry;/* tx on-chip retries (short) */
+	u_int32_t	ast_tx_longretry;/* tx on-chip retries (long) */
+	u_int32_t	ast_tx_badrate;	/* tx failed 'cuz bogus xmit rate */
+	u_int32_t	ast_tx_noack;	/* tx frames with no ack marked */
+	u_int32_t	ast_tx_rts;	/* tx frames with rts enabled */
+	u_int32_t	ast_tx_cts;	/* tx frames with cts enabled */
+	u_int32_t	ast_tx_shortpre;/* tx frames with short preamble */
+	u_int32_t	ast_tx_altrate;	/* tx frames with alternate rate */
+	u_int32_t	ast_tx_protect;	/* tx frames with protection */
+	u_int32_t	ast_rx_orn;	/* rx failed 'cuz of desc overrun */
+	u_int32_t	ast_rx_crcerr;	/* rx failed 'cuz of bad CRC */
+	u_int32_t	ast_rx_fifoerr;	/* rx failed 'cuz of FIFO overrun */
+	u_int32_t	ast_rx_badcrypt;/* rx failed 'cuz decryption */
+	u_int32_t	ast_rx_badmic;	/* rx failed 'cuz MIC failure */
+	u_int32_t	ast_rx_phyerr;	/* rx PHY error summary count */
+	u_int32_t	ast_rx_phy[32];	/* rx PHY error per-code counts */
+	u_int32_t	ast_rx_tooshort;/* rx discarded 'cuz frame too short */
+	u_int32_t	ast_rx_toobig;	/* rx discarded 'cuz frame too large */
+	u_int32_t	ast_rx_nobuf;	/* rx setup failed 'cuz no skbuff */
+	u_int32_t	ast_rx_mgt;	/* management frames received */
+	u_int32_t	ast_rx_ctl;	/* control frames received */
+	int8_t		ast_tx_rssi;	/* tx rssi of last ack */
+	int8_t		ast_rx_rssi;	/* rx rssi from histogram */
+	u_int32_t	ast_be_nobuf;	/* no skbuff available for beacon */
+	u_int32_t	ast_per_cal;	/* periodic calibration calls */
+	u_int32_t	ast_per_calfail;/* periodic calibration failed */
+	u_int32_t	ast_per_rfgain;	/* periodic calibration rfgain reset */
+	u_int32_t	ast_rate_calls;	/* rate control checks */
+	u_int32_t	ast_rate_raise;	/* rate control raised xmit rate */
+	u_int32_t	ast_rate_drop;	/* rate control dropped xmit rate */
+	u_int32_t	ast_ant_defswitch;/* rx/default antenna switches */
+	u_int32_t	ast_ant_txswitch;/* tx antenna switches */
+	u_int32_t	ast_ant_rx[8];	/* rx frames with antenna */
+	u_int32_t	ast_ant_tx[8];	/* tx frames with antenna */
+};
+
+struct ath_diag {
+	char	ad_name[IFNAMSIZ];	/* if name, e.g. "ath0" */
+	u_int16_t ad_id;
+#define	ATH_DIAG_DYN	0x8000		/* allocate buffer in caller */
+#define	ATH_DIAG_IN	0x4000		/* copy in parameters */
+#define	ATH_DIAG_OUT	0x0000		/* copy out results (always) */
+#define	ATH_DIAG_ID	0x0fff
+	u_int16_t ad_in_size;		/* pack to fit, yech */
+	caddr_t	ad_in_data;
+	caddr_t	ad_out_data;
+	u_int	ad_out_size;
+
+};
+
+#ifdef __linux__
+#define	SIOCGATHSTATS	(SIOCDEVPRIVATE+0)
+#define	SIOCGATHDIAG	(SIOCDEVPRIVATE+1)
+#else
+#define	SIOCGATHSTATS	_IOWR('i', 137, struct ifreq)
+#define	SIOCGATHDIAG	_IOWR('i', 138, struct ath_diag)
+#endif
+#endif /* _DEV_ATH_ATHIOCTL_H */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athrate.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athrate.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athrate.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athrate.h	2005-02-24 13:06:17.185149480 -0800
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Video54 Technologies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_athrate.h,v 1.2 2005/02/16 16:08:42 samleffler Exp $
+ */
+#ifndef _ATH_RATECTRL_H_
+#define _ATH_RATECTRL_H_
+
+/*
+ * Interface definitions for transmit rate control modules for the
+ * Atheros driver.
+ *
+ * A rate control module is responsible for choosing the transmit rate
+ * for each data frame.  Management+control frames are always sent at
+ * a fixed rate.
+ *
+ * Only one module may be present at a time; the driver references
+ * rate control interfaces by symbol name.  If multiple modules are
+ * to be supported we'll need to switch to a registration-based scheme
+ * as is currently done, for example, for authentication modules.
+ *
+ * An instance of the rate control module is attached to each device
+ * at attach time and detached when the device is destroyed.  The module
+ * may associate data with each device and each node (station).  Both
+ * sets of storage are opaque except for the size of the per-node storage
+ * which must be provided when the module is attached.
+ *
+ * The rate control module is notified for each state transition and
+ * station association/reassociation.  Otherwise it is queried for a
+ * rate for each outgoing frame and provided status from each transmitted
+ * frame.  Any ancillary processing is the responsibility of the module
+ * (e.g. if periodic processing is required then the module should setup
+ * it's own timer).
+ *
+ * In addition to the transmit rate for each frame the module must also
+ * indicate the number of attempts to make at the specified rate.  If this
+ * number is != ATH_TXMAXTRY then an additional callback is made to setup
+ * additional transmit state.  The rate control code is assumed to write
+ * this additional data directly to the transmit descriptor.
+ */
+struct ath_softc;
+struct ath_node;
+struct ath_desc;
+
+struct ath_ratectrl {
+	size_t	arc_space;	/* space required for per-node state */
+};
+/*
+ * Attach/detach a rate control module.
+ */
+struct ath_ratectrl *ath_rate_attach(struct ath_softc *);
+void	ath_rate_detach(struct ath_ratectrl *);
+
+
+/*
+ * State storage handling.
+ */
+/*
+ * Initialize per-node state already allocated for the specified
+ * node; this space can be assumed initialized to zero.
+ */
+void	ath_rate_node_init(struct ath_softc *, struct ath_node *);
+/*
+ * Cleanup any per-node state prior to the node being reclaimed.
+ */
+void	ath_rate_node_cleanup(struct ath_softc *, struct ath_node *);
+/*
+ * Copy per-node state; currently used only to duplicate bss on
+ * station association.
+ */
+void	ath_rate_node_copy(struct ath_softc *,
+		struct ath_node *, const struct ath_node *);
+/*
+ * Update rate control state on station associate/reassociate 
+ * (when operating as an ap or for nodes discovered when operating
+ * in ibss mode).
+ */
+void	ath_rate_newassoc(struct ath_softc *, struct ath_node *,
+		int isNewAssociation);
+/*
+ * Update/reset rate control state for 802.11 state transitions.
+ * Important mostly as the analog to ath_rate_newassoc when operating
+ * in station mode.
+ */
+void	ath_rate_newstate(struct ath_softc *, enum ieee80211_state);
+
+/*
+ * Transmit handling.
+ */
+/*
+ * Return the transmit info for a data packet.  If multi-rate state
+ * is to be setup then try0 should contain a value other than ATH_TXMATRY
+ * and ath_rate_setupxtxdesc will be called after deciding if the frame
+ * can be transmitted with multi-rate retry.
+ */
+void	ath_rate_findrate(struct ath_softc *, struct ath_node *,
+		HAL_BOOL shortPreamble, size_t frameLen,
+		u_int8_t *rix, int *try0, u_int8_t *txrate);
+/*
+ * Setup any extended (multi-rate) descriptor state for a data packet.
+ * The rate index returned by ath_rate_findrate is passed back in.
+ */
+void	ath_rate_setupxtxdesc(struct ath_softc *, struct ath_node *,
+		struct ath_desc *, HAL_BOOL shortPreamble, u_int8_t rix);
+/*
+ * Update rate control state for a packet associated with the
+ * supplied transmit descriptor.  The routine is invoked both
+ * for packets that were successfully sent and for those that
+ * failed (consult the descriptor for details).
+ */
+void	ath_rate_tx_complete(struct ath_softc *, struct ath_node *,
+		const struct ath_desc *);
+#endif /* _ATH_RATECTRL_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athvar.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athvar.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athvar.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athvar.h	2005-02-24 13:06:17.185149480 -0800
@@ -0,0 +1,478 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_athvar.h,v 1.18 2005/02/16 16:08:42 samleffler Exp $
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHVAR_H
+#define _DEV_ATH_ATHVAR_H
+
+#include "ah.h"
+#include "if_athioctl.h"
+#include "if_athrate.h"
+
+#ifdef CONFIG_NET_WIRELESS
+#include <linux/wireless.h>
+#endif
+
+/*
+ * Deduce if tasklets are available.  If not then
+ * fall back to using the immediate work queue.
+ */
+#include <linux/interrupt.h>
+#ifdef DECLARE_TASKLET			/* native tasklets */
+#define tq_struct tasklet_struct
+#define ATH_INIT_TQUEUE(a,b,c)		tasklet_init((a),(b),(unsigned long)(c))
+#define ATH_SCHEDULE_TQUEUE(a,b)	tasklet_schedule((a))
+typedef unsigned long TQUEUE_ARG;
+#define mark_bh(a)
+#else					/* immediate work queue */
+#define ATH_INIT_TQUEUE(a,b,c)		INIT_TQUEUE(a,b,c)
+#define ATH_SCHEDULE_TQUEUE(a,b) do {		\
+	*(b) |= queue_task((a), &tq_immediate);	\
+} while(0)
+typedef void *TQUEUE_ARG;
+#define	tasklet_disable(t)	do { (void) t; local_bh_disable(); } while (0)
+#define	tasklet_enable(t)	do { (void) t; local_bh_enable(); } while (0)
+#endif /* !DECLARE_TASKLET */
+
+/*
+ * Guess how the interrupt handler should work.
+ */
+#if !defined(IRQ_NONE)
+typedef void irqreturn_t;
+#define	IRQ_NONE
+#define	IRQ_HANDLED
+#endif /* !defined(IRQ_NONE) */
+
+#ifndef SET_MODULE_OWNER
+#define	SET_MODULE_OWNER(dev) do {		\
+	dev->owner = THIS_MODULE;		\
+} while (0)
+#endif
+
+#ifndef SET_NETDEV_DEV
+#define	SET_NETDEV_DEV(ndev, pdev)
+#endif
+
+/*
+ * Deal with the sysctl handler api changing.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+#define	ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
+	f(ctl_table *ctl, int write, struct file *filp, void *buffer, \
+		size_t *lenp)
+#define	ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+	proc_dointvec(ctl, write, filp, buffer, lenp)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) */
+#define	ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
+	f(ctl_table *ctl, int write, struct file *filp, void *buffer,\
+		size_t *lenp, loff_t *ppos)
+#define	ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+	proc_dointvec(ctl, write, filp, buffer, lenp, ppos)
+#endif
+
+#define	ATH_TIMEOUT		1000
+
+/*
+ * Maximum acceptable MTU
+ * MAXFRAMEBODY - WEP - QOS - RSN/WPA:
+ * 2312 - 8 - 2 - 12 = 2290
+ */
+#define ATH_MAX_MTU     2290
+#define ATH_MIN_MTU     32  
+
+#define	ATH_RXBUF	40		/* number of RX buffers */
+#define	ATH_TXBUF	200		/* number of TX buffers */
+#define	ATH_TXDESC	1		/* number of descriptors per buffer */
+#define	ATH_TXMAXTRY	11		/* max number of transmit attempts */
+#define	ATH_TXINTR_PERIOD 5		/* max number of batched tx descriptors */
+
+/* driver-specific node state */
+struct ath_node {
+	struct ieee80211_node an_node;	/* base class */
+	u_int8_t	an_tx_mgtrate;	/* h/w rate for management/ctl frames */
+	u_int8_t	an_tx_mgtratesp;/* short preamble h/w rate for " " */
+	u_int32_t	an_avgrssi;	/* average rssi over all rx frames */
+	HAL_NODE_STATS	an_halstats;	/* rssi statistics used by hal */
+	/* variable-length rate control state follows */
+};
+#define	ATH_NODE(_n)	((struct ath_node *)(_n))
+#define ATH_NODE_CONST(_n)	((const struct ath_node *)(_n))
+
+#define ATH_RSSI_LPF_LEN	10
+#define ATH_RSSI_DUMMY_MARKER	0x127
+#define ATH_EP_MUL(x, mul)	((x) * (mul))
+#define ATH_RSSI_IN(x)		(ATH_EP_MUL((x), HAL_RSSI_EP_MULTIPLIER))
+#define ATH_LPF_RSSI(x, y, len) \
+    ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define ATH_RSSI_LPF(x, y) do {						\
+    if ((y) >= -20)							\
+    	x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN);	\
+} while (0)
+
+struct ath_buf {
+	STAILQ_ENTRY(ath_buf)	bf_list;
+	struct ath_desc		*bf_desc;	/* virtual addr of desc */
+	dma_addr_t		bf_daddr;	/* physical addr of desc */
+	struct sk_buff		*bf_skb;	/* skbuff for buf */
+	dma_addr_t		bf_skbaddr;	/* physical addr of skb data */
+	struct ieee80211_node	*bf_node;	/* pointer to the node */
+};
+
+struct ath_hal;
+struct ath_desc;
+struct proc_dir_entry;
+
+/*
+ * Data transmit queue state.  One of these exists for each
+ * hardware transmit queue.  Packets sent to us from above
+ * are assigned to queues based on their priority.  Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath_txq {
+	u_int			axq_qnum;	/* hardware q number */
+	u_int			axq_depth;	/* queue depth (stat only) */
+	u_int			axq_intrcnt;	/* interrupt count */
+	u_int32_t		*axq_link;	/* link ptr in last TX desc */
+	STAILQ_HEAD(, ath_buf)	axq_q;		/* transmit queue */
+	spinlock_t		axq_lock;	/* lock on q and link */
+};
+
+#define	ATH_TXQ_LOCK_INIT(_tq)		spin_lock_init(&(_tq)->axq_lock)
+#define	ATH_TXQ_LOCK_DESTROY(_tq)	
+#define	ATH_TXQ_LOCK(_tq)		spin_lock(&(_tq)->axq_lock)
+#define	ATH_TXQ_UNLOCK(_tq)		spin_unlock(&(_tq)->axq_lock)
+#define	ATH_TXQ_LOCK_BH(_tq)		spin_lock_bh(&(_tq)->axq_lock)
+#define	ATH_TXQ_UNLOCK_BH(_tq)		spin_unlock_bh(&(_tq)->axq_lock)
+#define	ATH_TXQ_LOCK_ASSERT(_tq) \
+	KASSERT(spin_is_locked(&(_tq)->axq_lock), ("txq not locked!"))
+
+#define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \
+	STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \
+	(_tq)->axq_depth++; \
+} while (0)
+#define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \
+	STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
+	(_tq)->axq_depth--; \
+} while (0)
+
+struct ath_softc {
+	struct net_device	sc_dev;		/* NB: must be first */
+	struct semaphore	sc_lock;	/* dev-level lock */
+	struct net_device_stats	sc_devstats;	/* device statistics */
+	struct ath_stats	sc_stats;	/* private statistics */
+	struct ieee80211com	sc_ic;		/* IEEE 802.11 common */
+	int			sc_debug;
+	void			(*sc_recv_mgmt)(struct ieee80211com *,
+					struct sk_buff *,
+					struct ieee80211_node *,
+					int, int, u_int32_t);
+	int			(*sc_newstate)(struct ieee80211com *,
+					enum ieee80211_state, int);
+	void 			(*sc_node_cleanup)(struct ieee80211com *,
+					struct ieee80211_node *);
+	void			(*sc_node_copy)(struct ieee80211com *,
+					struct ieee80211_node *,
+					const struct ieee80211_node *);
+	struct ath_hal		*sc_ah;		/* Atheros HAL */
+	struct ath_ratectrl	*sc_rc;		/* tx rate control support */
+	unsigned int		sc_invalid : 1,	/* being detached */
+				sc_mrretry : 1,	/* multi-rate retry support */
+				sc_softled : 1,	/* enable LED gpio status */
+				sc_splitmic: 1,	/* split TKIP MIC keys */
+				sc_needmib : 1,	/* enable MIB stats intr */
+				sc_hasdiversity : 1,/* rx diversity available */
+				sc_diversity : 1;/* enable rx diversity */
+						/* rate tables */
+	const HAL_RATE_TABLE	*sc_rates[IEEE80211_MODE_MAX];
+	const HAL_RATE_TABLE	*sc_currates;	/* current rate table */
+	enum ieee80211_phymode	sc_curmode;	/* current phy mode */
+	HAL_CHANNEL		sc_curchan;	/* current h/w channel */
+	u_int8_t		sc_rixmap[256];	/* IEEE to h/w rate table ix */
+	u_int8_t		sc_hwmap[32];	/* h/w rate ix to IEEE table */
+	u_int8_t		sc_protrix;	/* protection rate index */
+	u_int8_t		sc_txantenna;	/* tx antenna (fixed or auto) */
+	HAL_INT			sc_imask;	/* interrupt mask copy */
+	u_int			sc_keymax;	/* size of key cache */
+	u_int8_t		sc_keymap[16];	/* bit map of key cache use */
+
+	u_int32_t		sc_beacons;
+	u_int16_t		sc_ledstate;
+	u_int16_t		sc_ledpin;	/* GPIO pin for LED */
+
+	void			*sc_bdev;	/* associated bus device */
+	struct ath_desc		*sc_desc;	/* TX/RX descriptors */
+	size_t			sc_desc_len;	/* size of TX/RX descriptors */
+	u_int16_t		sc_cachelsz;	/* cache line size */
+	dma_addr_t		sc_desc_daddr;	/* DMA (physical) address */
+
+	struct tq_struct	sc_fataltq;	/* fatal error intr tasklet */
+
+	int			sc_rxbufsize;	/* rx size based on mtu */
+	STAILQ_HEAD(, ath_buf)	sc_rxbuf;	/* receive buffer */
+	u_int32_t		*sc_rxlink;	/* link ptr in last RX desc */
+	struct tq_struct	sc_rxtq;	/* rx intr tasklet */
+	struct tq_struct	sc_rxorntq;	/* rxorn intr tasklet */
+	u_int8_t		sc_defant;	/* current default antenna */
+	u_int8_t		sc_rxotherant;	/* rx's on non-default antenna*/
+
+	STAILQ_HEAD(, ath_buf)	sc_txbuf;	/* tx buffer queue */
+	spinlock_t		sc_txbuflock;	/* txbuf lock */
+	u_int			sc_txqsetup;	/* h/w queues setup */
+	u_int			sc_txintrperiod;/* tx interrupt batching */
+	struct ath_txq		sc_txq[HAL_NUM_TX_QUEUES];
+	struct ath_txq		*sc_ac2q[5];	/* WME AC -> h/w qnum */ 
+	struct tq_struct	sc_txtq;	/* tx intr tasklet */
+
+	u_int			sc_bhalq;	/* HAL q for outgoing beacons */
+	struct ath_buf		*sc_bcbuf;	/* beacon buffer */
+	struct ath_buf		*sc_bufptr;	/* allocated buffer ptr */
+	struct ieee80211_beacon_offsets sc_boff;/* dynamic update state */
+	struct tq_struct	sc_bmisstq;	/* bmiss intr tasklet */
+	enum {
+		OK,				/* no change needed */
+		UPDATE,				/* update pending */
+		COMMIT				/* beacon sent, commit change */
+	} sc_updateslot;			/* slot time update fsm */
+
+	struct timer_list	sc_cal_ch;	/* calibration timer */
+	struct timer_list	sc_scan_ch;	/* AP scan timer */
+#ifdef CONFIG_NET_WIRELESS
+	struct iw_statistics	sc_iwstats;	/* wireless statistics block */
+#endif
+#ifdef CONFIG_SYSCTL
+	struct ctl_table_header	*sc_sysctl_header;
+	struct ctl_table	*sc_sysctls;
+#endif
+};
+
+#define	ATH_TXQ_SETUP(sc, i)	((sc)->sc_txqsetup & (1<<i))
+
+#define	ATH_TXBUF_LOCK_INIT(_sc)	spin_lock_init(&(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_LOCK_DESTROY(_sc)
+#define	ATH_TXBUF_LOCK(_sc)		spin_lock(&(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_UNLOCK(_sc)		spin_unlock(&(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_LOCK_BH(_sc)		spin_lock_bh(&(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_UNLOCK_BH(_sc)	spin_unlock_bh(&(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_LOCK_ASSERT(_sc) \
+	KASSERT(spin_is_locked(&(_sc)->sc_txbuflock), ("txbuf not locked!"))
+
+#define	ATH_LOCK_INIT(_sc)		init_MUTEX(&(_sc)->sc_lock)
+#define	ATH_LOCK_DESTROY(_sc)
+#define	ATH_LOCK(_sc)			down(&(_sc)->sc_lock)
+#define	ATH_UNLOCK(_sc)			up(&(_sc)->sc_lock)
+
+int	ath_attach(u_int16_t, struct net_device *);
+int	ath_detach(struct net_device *);
+void	ath_resume(struct net_device *);
+void	ath_suspend(struct net_device *);
+void	ath_shutdown(struct net_device *);
+irqreturn_t ath_intr(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_SYSCTL
+void	ath_sysctl_register(void);
+void	ath_sysctl_unregister(void);
+#endif /* CONFIG_SYSCTL */
+
+/*
+ * HAL definitions to comply with local coding convention.
+ */
+#define	ath_hal_reset(_ah, _opmode, _chan, _outdoor, _pstatus) \
+	((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_outdoor), (_pstatus)))
+#define	ath_hal_getratetable(_ah, _mode) \
+	((*(_ah)->ah_getRateTable)((_ah), (_mode)))
+#define	ath_hal_getmac(_ah, _mac) \
+	((*(_ah)->ah_getMacAddress)((_ah), (_mac)))
+#define	ath_hal_setmac(_ah, _mac) \
+	((*(_ah)->ah_setMacAddress)((_ah), (_mac)))
+#define	ath_hal_intrset(_ah, _mask) \
+	((*(_ah)->ah_setInterrupts)((_ah), (_mask)))
+#define	ath_hal_intrget(_ah) \
+	((*(_ah)->ah_getInterrupts)((_ah)))
+#define	ath_hal_intrpend(_ah) \
+	((*(_ah)->ah_isInterruptPending)((_ah)))
+#define	ath_hal_getisr(_ah, _pmask) \
+	((*(_ah)->ah_getPendingInterrupts)((_ah), (_pmask)))
+#define	ath_hal_updatetxtriglevel(_ah, _inc) \
+	((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc)))
+#define	ath_hal_setpower(_ah, _mode, _sleepduration) \
+	((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE, (_sleepduration)))
+#define	ath_hal_keycachesize(_ah) \
+	((*(_ah)->ah_getKeyCacheSize)((_ah)))
+#define	ath_hal_keyreset(_ah, _ix) \
+	((*(_ah)->ah_resetKeyCacheEntry)((_ah), (_ix)))
+#define	ath_hal_keyset(_ah, _ix, _pk, _mac) \
+	((*(_ah)->ah_setKeyCacheEntry)((_ah), (_ix), (_pk), (_mac), AH_FALSE))
+#define	ath_hal_keyisvalid(_ah, _ix) \
+	(((*(_ah)->ah_isKeyCacheEntryValid)((_ah), (_ix))))
+#define	ath_hal_keysetmac(_ah, _ix, _mac) \
+	((*(_ah)->ah_setKeyCacheEntryMac)((_ah), (_ix), (_mac)))
+#define	ath_hal_getrxfilter(_ah) \
+	((*(_ah)->ah_getRxFilter)((_ah)))
+#define	ath_hal_setrxfilter(_ah, _filter) \
+	((*(_ah)->ah_setRxFilter)((_ah), (_filter)))
+#define	ath_hal_setmcastfilter(_ah, _mfilt0, _mfilt1) \
+	((*(_ah)->ah_setMulticastFilter)((_ah), (_mfilt0), (_mfilt1)))
+#define	ath_hal_waitforbeacon(_ah, _bf) \
+	((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr))
+#define	ath_hal_putrxbuf(_ah, _bufaddr) \
+	((*(_ah)->ah_setRxDP)((_ah), (_bufaddr)))
+#define	ath_hal_gettsf32(_ah) \
+	((*(_ah)->ah_getTsf32)((_ah)))
+#define	ath_hal_gettsf64(_ah) \
+	((*(_ah)->ah_getTsf64)((_ah)))
+#define	ath_hal_resettsf(_ah) \
+	((*(_ah)->ah_resetTsf)((_ah)))
+#define	ath_hal_rxena(_ah) \
+	((*(_ah)->ah_enableReceive)((_ah)))
+#define	ath_hal_puttxbuf(_ah, _q, _bufaddr) \
+	((*(_ah)->ah_setTxDP)((_ah), (_q), (_bufaddr)))
+#define	ath_hal_gettxbuf(_ah, _q) \
+	((*(_ah)->ah_getTxDP)((_ah), (_q)))
+#define	ath_hal_getrxbuf(_ah) \
+	((*(_ah)->ah_getRxDP)((_ah)))
+#define	ath_hal_txstart(_ah, _q) \
+	((*(_ah)->ah_startTxDma)((_ah), (_q)))
+#define	ath_hal_setchannel(_ah, _chan) \
+	((*(_ah)->ah_setChannel)((_ah), (_chan)))
+#define	ath_hal_calibrate(_ah, _chan) \
+	((*(_ah)->ah_perCalibration)((_ah), (_chan)))
+#define	ath_hal_setledstate(_ah, _state) \
+	((*(_ah)->ah_setLedState)((_ah), (_state)))
+#define	ath_hal_beaconinit(_ah, _nextb, _bperiod) \
+	((*(_ah)->ah_beaconInit)((_ah), (_nextb), (_bperiod)))
+#define	ath_hal_beaconreset(_ah) \
+	((*(_ah)->ah_resetStationBeaconTimers)((_ah)))
+#define	ath_hal_beacontimers(_ah, _bs) \
+	((*(_ah)->ah_setStationBeaconTimers)((_ah), (_bs)))
+#define	ath_hal_setassocid(_ah, _bss, _associd) \
+	((*(_ah)->ah_writeAssocid)((_ah), (_bss), (_associd)))
+#define	ath_hal_phydisable(_ah) \
+	((*(_ah)->ah_phyDisable)((_ah)))
+#define	ath_hal_setopmode(_ah) \
+	((*(_ah)->ah_setPCUConfig)((_ah)))
+#define	ath_hal_stoptxdma(_ah, _qnum) \
+	((*(_ah)->ah_stopTxDma)((_ah), (_qnum)))
+#define	ath_hal_stoppcurecv(_ah) \
+	((*(_ah)->ah_stopPcuReceive)((_ah)))
+#define	ath_hal_startpcurecv(_ah) \
+	((*(_ah)->ah_startPcuReceive)((_ah)))
+#define	ath_hal_stopdmarecv(_ah) \
+	((*(_ah)->ah_stopDmaReceive)((_ah)))
+#define	ath_hal_getdiagstate(_ah, _id, _indata, _insize, _outdata, _outsize) \
+	((*(_ah)->ah_getDiagState)((_ah), (_id), \
+		(_indata), (_insize), (_outdata), (_outsize)))
+#define	ath_hal_setuptxqueue(_ah, _type, _irq) \
+	((*(_ah)->ah_setupTxQueue)((_ah), (_type), (_irq)))
+#define	ath_hal_resettxqueue(_ah, _q) \
+	((*(_ah)->ah_resetTxQueue)((_ah), (_q)))
+#define	ath_hal_releasetxqueue(_ah, _q) \
+	((*(_ah)->ah_releaseTxQueue)((_ah), (_q)))
+#define	ath_hal_hasveol(_ah) \
+	((*(_ah)->ah_hasVEOL)((_ah)))
+#define	ath_hal_getrfgain(_ah) \
+	((*(_ah)->ah_getRfGain)((_ah)))
+#define	ath_hal_getdefantenna(_ah) \
+	((*(_ah)->ah_getDefAntenna)((_ah)))
+#define	ath_hal_setdefantenna(_ah, _ant) \
+	((*(_ah)->ah_setDefAntenna)((_ah), (_ant)))
+#define	ath_hal_rxmonitor(_ah, _arg) \
+	((*(_ah)->ah_rxMonitor)((_ah), (_arg)))
+#define	ath_hal_mibevent(_ah, _stats) \
+	((*(_ah)->ah_procMibEvent)((_ah), (_stats)))
+#define	ath_hal_setslottime(_ah, _us) \
+	((*(_ah)->ah_setSlotTime)((_ah), (_us)))
+#define	ath_hal_getslottime(_ah) \
+	((*(_ah)->ah_getSlotTime)((_ah)))
+#define	ath_hal_setacktimeout(_ah, _us) \
+	((*(_ah)->ah_setAckTimeout)((_ah), (_us)))
+#define	ath_hal_getacktimeout(_ah) \
+	((*(_ah)->ah_getAckTimeout)((_ah)))
+#define	ath_hal_setctstimeout(_ah, _us) \
+	((*(_ah)->ah_setCTSTimeout)((_ah), (_us)))
+#define	ath_hal_getctstimeout(_ah) \
+	((*(_ah)->ah_getCTSTimeout)((_ah)))
+#define	ath_hal_getcapability(_ah, _cap, _param, _result) \
+	((*(_ah)->ah_getCapability)((_ah), (_cap), (_param), (_result)))
+#define	ath_hal_setcapability(_ah, _cap, _param, _v, _status) \
+	((*(_ah)->ah_setCapability)((_ah), (_cap), (_param), (_v), (_status)))
+#define	ath_hal_ciphersupported(_ah, _cipher) \
+	(ath_hal_getcapability(_ah, HAL_CAP_CIPHER, _cipher, NULL) == HAL_OK)
+#define	ath_hal_getregdomain(_ah, _prd) \
+	ath_hal_getcapability(_ah, HAL_CAP_REG_DMN, 0, (_prd))
+#define	ath_hal_getcountrycode(_ah, _pcc) \
+	(*(_pcc) = (_ah)->ah_countryCode)
+#define	ath_hal_tkipsplit(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK)
+#define	ath_hal_hwphycounters(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK)
+#define	ath_hal_hasdiversity(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 0, NULL) == HAL_OK)
+#define	ath_hal_getdiversity(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 1, NULL) == HAL_OK)
+#define	ath_hal_setdiversity(_ah, _v) \
+	ath_hal_setcapability(_ah, HAL_CAP_DIVERSITY, 1, _v, NULL)
+
+#define	ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \
+	((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq)))
+#define	ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext) \
+	((*(_ah)->ah_procRxDesc)((_ah), (_ds), (_dspa), (_dsnext)))
+#define	ath_hal_setuptxdesc(_ah, _ds, _plen, _hlen, _atype, _txpow, \
+		_txr0, _txtr0, _keyix, _ant, _flags, \
+		_rtsrate, _rtsdura) \
+	((*(_ah)->ah_setupTxDesc)((_ah), (_ds), (_plen), (_hlen), (_atype), \
+		(_txpow), (_txr0), (_txtr0), (_keyix), (_ant), \
+		(_flags), (_rtsrate), (_rtsdura)))
+#define	ath_hal_setupxtxdesc(_ah, _ds, \
+		_txr1, _txtr1, _txr2, _txtr2, _txr3, _txtr3) \
+	((*(_ah)->ah_setupXTxDesc)((_ah), (_ds), \
+		(_txr1), (_txtr1), (_txr2), (_txtr2), (_txr3), (_txtr3)))
+#define	ath_hal_filltxdesc(_ah, _ds, _l, _first, _last, _ds0) \
+	((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_l), (_first), (_last), (_ds0)))
+#define	ath_hal_txprocdesc(_ah, _ds) \
+	((*(_ah)->ah_procTxDesc)((_ah), (_ds)))
+
+#define ath_hal_gpioCfgOutput(_ah, _gpio) \
+        ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio)))
+#define ath_hal_gpioset(_ah, _gpio, _b) \
+        ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b)))
+
+#endif /* _DEV_ATH_ATHVAR_H */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/version.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/version.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/version.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/version.h	2005-02-24 13:06:17.186149328 -0800
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: version.h,v 1.18 2005/02/16 16:08:42 samleffler Exp $
+ */
+#define	ATH_PCI_VERSION	"0.9.4.12"
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah.h	2005-02-24 13:06:17.244140512 -0800
@@ -0,0 +1,691 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah.h,v 1.8 2005/02/22 14:17:22 otaku Exp $
+ */
+
+#ifndef _ATH_AH_H_
+#define _ATH_AH_H_
+/*
+ * Atheros Hardware Access Layer
+ *
+ * Clients of the HAL call ath_hal_attach to obtain a reference to an ath_hal
+ * structure for use with the device.  Hardware-related operations that
+ * follow must call back into the HAL through interface, supplying the
+ * reference as the first parameter.
+ */
+#include "ah_osdep.h"
+
+/*
+ * __ahdecl is analogous to _cdecl; it defines the calling
+ * convention used within the HAL.  For most systems this
+ * can just default to be empty and the compiler will (should)
+ * use _cdecl.  For systems where _cdecl is not compatible this
+ * must be defined.  See linux/ah_osdep.h for an example.
+ */
+#ifndef __ahdecl
+#define __ahdecl
+#endif
+
+/*
+ * Status codes that may be returned by the HAL.  Note that
+ * interfaces that return a status code set it only when an
+ * error occurs--i.e. you cannot check it for success.
+ */
+typedef enum {
+	HAL_OK		= 0,	/* No error */
+	HAL_ENXIO	= 1,	/* No hardware present */
+	HAL_ENOMEM	= 2,	/* Memory allocation failed */
+	HAL_EIO		= 3,	/* Hardware didn't respond as expected */
+	HAL_EEMAGIC	= 4,	/* EEPROM magic number invalid */
+	HAL_EEVERSION	= 5,	/* EEPROM version invalid */
+	HAL_EELOCKED	= 6,	/* EEPROM unreadable */
+	HAL_EEBADSUM	= 7,	/* EEPROM checksum invalid */
+	HAL_EEREAD	= 8,	/* EEPROM read problem */
+	HAL_EEBADMAC	= 9,	/* EEPROM mac address invalid */
+	HAL_EESIZE	= 10,	/* EEPROM size not supported */
+	HAL_EEWRITE	= 11,	/* Attempt to change write-locked EEPROM */
+	HAL_EINVAL	= 12,	/* Invalid parameter to function */
+	HAL_ENOTSUPP	= 13,	/* Hardware revision not supported */
+	HAL_ESELFTEST	= 14,	/* Hardware self-test failed */
+	HAL_EINPROGRESS	= 15,	/* Operation incomplete */
+} HAL_STATUS;
+
+typedef enum {
+	AH_FALSE = 0,		/* NB: lots of code assumes false is zero */
+	AH_TRUE  = 1,
+} HAL_BOOL;
+
+typedef enum {
+	HAL_CAP_REG_DMN		= 0,	/* current regulatory domain */
+	HAL_CAP_CIPHER		= 1,	/* hardware supports cipher */
+	HAL_CAP_TKIP_MIC	= 2,	/* handle TKIP MIC in hardware */
+	HAL_CAP_TKIP_SPLIT	= 3,	/* hardware TKIP uses split keys */
+	HAL_CAP_PHYCOUNTERS	= 4,	/* hardware PHY error counters */
+	HAL_CAP_DIVERSITY	= 5,	/* hardware supports fast diversity */
+	HAL_CAP_KEYCACHE_SIZE	= 6,	/* number of entries in key cache */
+	HAL_CAP_NUM_TXQUEUES	= 7,	/* number of hardware xmit queues */
+	HAL_CAP_VEOL		= 9,	/* hardware supports virtual EOL */
+	HAL_CAP_PSPOLL		= 10,	/* hardware has working PS-Poll support */
+	HAL_CAP_DIAG		= 11,	/* hardware diagnostic support */
+	HAL_CAP_COMPRESSION	= 12,	/* hardware supports compression */
+	HAL_CAP_BURST		= 13,	/* hardware supports packet bursting */
+	HAL_CAP_FASTFRAME	= 14,	/* hardware supoprts fast frames */
+	HAL_CAP_TXPOW		= 15,	/* global tx power limit  */
+	HAL_CAP_TPC		= 16,	/* per-packet tx power control  */
+} HAL_CAPABILITY_TYPE;
+
+/* 
+ * "States" for setting the LED.  These correspond to
+ * the possible 802.11 operational states and there may
+ * be a many-to-one mapping between these states and the
+ * actual hardware states for the LED's (i.e. the hardware
+ * may have fewer states).
+ */
+typedef enum {
+	HAL_LED_INIT	= 0,
+	HAL_LED_SCAN	= 1,
+	HAL_LED_AUTH	= 2,
+	HAL_LED_ASSOC	= 3,
+	HAL_LED_RUN	= 4
+} HAL_LED_STATE;
+
+/*
+ * Transmit queue types/numbers.  These are used to tag
+ * each transmit queue in the hardware and to identify a set
+ * of transmit queues for operations such as start/stop dma.
+ */
+typedef enum {
+	HAL_TX_QUEUE_INACTIVE	= 0,		/* queue is inactive/unused */
+	HAL_TX_QUEUE_DATA	= 1,		/* data xmit q's */
+	HAL_TX_QUEUE_BEACON	= 2,		/* beacon xmit q */
+	HAL_TX_QUEUE_CAB	= 3,		/* "crap after beacon" xmit q */
+	HAL_TX_QUEUE_PSPOLL	= 4,		/* power-save poll xmit q */
+} HAL_TX_QUEUE;
+
+#define	HAL_NUM_TX_QUEUES	10		/* max possible # of queues */
+
+/*
+ * Transmit queue subtype.  These map directly to
+ * WME Access Categories (except for UPSD).  Refer
+ * to Table 5 of the WME spec.
+ */
+typedef enum {
+	HAL_WME_AC_BK	= 0,			/* background access category */
+	HAL_WME_AC_BE	= 1, 			/* best effort access category*/
+	HAL_WME_AC_VI	= 2,			/* video access category */
+	HAL_WME_AC_VO	= 3,			/* voice access category */
+	HAL_WME_UPSD	= 4,			/* uplink power save */
+} HAL_TX_QUEUE_SUBTYPE;
+
+/*
+ * Transmit queue flags that control various
+ * operational parameters.
+ */
+typedef enum {
+	TXQ_FLAG_TXOKINT_ENABLE	    = 0x0001,    /* enable TXOK interrupt */
+	TXQ_FLAG_TXERRINT_ENABLE    = 0x0001,    /* enable TXERR interrupt */
+	TXQ_FLAG_TXDESCINT_ENABLE   = 0x0002,    /* enable TXDESC interrupt */
+	TXQ_FLAG_TXEOLINT_ENABLE    = 0x0004,    /* enable TXEOL interrupt */
+	TXQ_FLAG_TXURNINT_ENABLE    = 0x0008,    /* enable TXURN interrupt */
+	TXQ_FLAG_BACKOFF_DISABLE    = 0x0010,    /* disable Post Backoff  */
+	TXQ_FLAG_COMPRESSION_ENABLE = 0x0020,    /* compression enabled */
+	TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040, /* enable ready time
+							expiry policy */
+	TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080, /* enable backoff while
+							sending fragment burst*/
+} HAL_TX_QUEUE_FLAGS;
+
+typedef struct {
+	u_int32_t	tqi_ver;		/* hal TXQ version */
+	HAL_TX_QUEUE_SUBTYPE tqi_subtype;	/* subtype if applicable */
+	HAL_TX_QUEUE_FLAGS tqi_qflags;		/* flags (see above) */
+	u_int32_t	tqi_priority;		/* (not used) */
+	u_int32_t	tqi_aifs;		/* aifs */
+	u_int32_t	tqi_cwmin;		/* cwMin */
+	u_int32_t	tqi_cwmax;		/* cwMax */
+	u_int16_t	tqi_shretry;		/* rts retry limit */
+	u_int16_t	tqi_lgretry;		/* long retry limit (not used)*/
+	u_int32_t	tqi_cbrPeriod;
+	u_int32_t	tqi_cbrOverflowLimit;
+	u_int32_t	tqi_burstTime;
+	u_int32_t	tqi_readyTime;
+} HAL_TXQ_INFO;
+
+/* token to use for aifs, cwmin, cwmax */
+#define	HAL_TXQ_USEDEFAULT	((u_int32_t) -1)
+
+/*
+ * Transmit packet types.  This belongs in ah_desc.h, but
+ * is here so we can give a proper type to various parameters
+ * (and not require everyone include the file).
+ *
+ * NB: These values are intentionally assigned for
+ *     direct use when setting up h/w descriptors.
+ */
+typedef enum {
+	HAL_PKT_TYPE_NORMAL	= 0,
+	HAL_PKT_TYPE_ATIM	= 1,
+	HAL_PKT_TYPE_PSPOLL	= 2,
+	HAL_PKT_TYPE_BEACON	= 3,
+	HAL_PKT_TYPE_PROBE_RESP	= 4,
+} HAL_PKT_TYPE;
+
+/* Rx Filter Frame Types */
+typedef enum {
+	HAL_RX_FILTER_UCAST	= 0x00000001,	/* Allow unicast frames */
+	HAL_RX_FILTER_MCAST	= 0x00000002,	/* Allow multicast frames */
+	HAL_RX_FILTER_BCAST	= 0x00000004,	/* Allow broadcast frames */
+	HAL_RX_FILTER_CONTROL	= 0x00000008,	/* Allow control frames */
+	HAL_RX_FILTER_BEACON	= 0x00000010,	/* Allow beacon frames */
+	HAL_RX_FILTER_PROM	= 0x00000020,	/* Promiscuous mode */
+	HAL_RX_FILTER_PROBEREQ	= 0x00000080,	/* Allow probe request frames */
+	HAL_RX_FILTER_PHYERR	= 0x00000100,	/* Allow phy errors */
+	HAL_RX_FILTER_PHYRADAR	= 0x00000200,	/* Allow phy radar errors*/
+} HAL_RX_FILTER;
+
+typedef enum {
+	HAL_PM_UNDEFINED	= 0,
+	HAL_PM_AUTO		= 1,
+	HAL_PM_AWAKE		= 2,
+	HAL_PM_FULL_SLEEP	= 3,
+	HAL_PM_NETWORK_SLEEP	= 4
+} HAL_POWER_MODE;
+
+/*
+ * NOTE WELL:
+ * These are mapped to take advantage of the common locations for many of
+ * the bits on all of the currently supported MAC chips. This is to make
+ * the ISR as efficient as possible, while still abstracting HW differences.
+ * When new hardware breaks this commonality this enumerated type, as well
+ * as the HAL functions using it, must be modified. All values are directly
+ * mapped unless commented otherwise.
+ */
+typedef enum {
+	HAL_INT_RX	= 0x00000001,	/* Non-common mapping */
+	HAL_INT_RXDESC	= 0x00000002,
+	HAL_INT_RXNOFRM	= 0x00000008,
+	HAL_INT_RXEOL	= 0x00000010,
+	HAL_INT_RXORN	= 0x00000020,
+	HAL_INT_TX	= 0x00000040,	/* Non-common mapping */
+	HAL_INT_TXDESC	= 0x00000080,
+	HAL_INT_TXURN	= 0x00000800,
+	HAL_INT_MIB	= 0x00001000,
+	HAL_INT_RXPHY	= 0x00004000,
+	HAL_INT_RXKCM	= 0x00008000,
+	HAL_INT_SWBA	= 0x00010000,
+	HAL_INT_BMISS	= 0x00040000,
+	HAL_INT_BNR	= 0x00100000,	/* Non-common mapping */
+	HAL_INT_GPIO	= 0x01000000,
+	HAL_INT_FATAL	= 0x40000000,	/* Non-common mapping */
+	HAL_INT_GLOBAL	= 0x80000000,	/* Set/clear IER */
+
+	/* Interrupt bits that map directly to ISR/IMR bits */
+	HAL_INT_COMMON  = HAL_INT_RXNOFRM
+			| HAL_INT_RXDESC
+			| HAL_INT_RXEOL
+			| HAL_INT_RXORN
+			| HAL_INT_TXURN
+			| HAL_INT_TXDESC
+			| HAL_INT_MIB
+			| HAL_INT_RXPHY
+			| HAL_INT_RXKCM
+			| HAL_INT_SWBA
+			| HAL_INT_BMISS
+			| HAL_INT_GPIO,
+	HAL_INT_NOCARD	= 0xffffffff	/* To signal the card was removed */
+} HAL_INT;
+
+typedef enum {
+	HAL_RFGAIN_INACTIVE		= 0,
+	HAL_RFGAIN_READ_REQUESTED	= 1,
+	HAL_RFGAIN_NEED_CHANGE		= 2
+} HAL_RFGAIN;
+
+/*
+ * Channels are specified by frequency.
+ */
+typedef struct {
+	u_int16_t	channel;	/* setting in Mhz */
+	u_int16_t	channelFlags;	/* see below */
+} HAL_CHANNEL;
+
+#define	CHANNEL_RAD_INT	0x0001	/* Radar interference detected on channel */
+#define	CHANNEL_CW_INT	0x0002	/* CW interference detected on channel */
+#define	CHANNEL_BUSY	0x0004	/* Busy, occupied or overlap with adjoin chan */
+#define	CHANNEL_TURBO	0x0010	/* Turbo Channel */
+#define	CHANNEL_CCK	0x0020	/* CCK channel */
+#define	CHANNEL_OFDM	0x0040	/* OFDM channel */
+#define	CHANNEL_2GHZ	0x0080	/* 2 GHz spectrum channel. */
+#define	CHANNEL_5GHZ	0x0100	/* 5 GHz spectrum channel */
+#define	CHANNEL_PASSIVE	0x0200	/* Only passive scan allowed in the channel */
+#define	CHANNEL_DYN	0x0400	/* dynamic CCK-OFDM channel */
+#define	CHANNEL_XR	0x0800	/* XR channel */
+#define CHANNEL_AR      0x8000  /* Software use: radar detected */ 
+
+
+#define	CHANNEL_A	(CHANNEL_5GHZ|CHANNEL_OFDM)
+#define	CHANNEL_B	(CHANNEL_2GHZ|CHANNEL_CCK)
+#define	CHANNEL_PUREG	(CHANNEL_2GHZ|CHANNEL_OFDM)
+#ifdef notdef
+#define	CHANNEL_G	(CHANNEL_2GHZ|CHANNEL_DYN)
+#else
+#define	CHANNEL_G	(CHANNEL_2GHZ|CHANNEL_OFDM)
+#endif
+#define	CHANNEL_T	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_108G	(CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_X	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR)
+#define	CHANNEL_ALL \
+	(CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_5GHZ|CHANNEL_2GHZ|CHANNEL_TURBO)
+#define	CHANNEL_ALL_NOTURBO 	(CHANNEL_ALL &~ CHANNEL_TURBO)
+
+typedef struct {
+	u_int32_t	ackrcv_bad;
+	u_int32_t	rts_bad;
+	u_int32_t	rts_good;
+	u_int32_t	fcs_bad;
+	u_int32_t	beacons;
+} HAL_MIB_STATS;
+
+typedef u_int16_t HAL_CTRY_CODE;		/* country code */
+typedef u_int16_t HAL_REG_DOMAIN;		/* regulatory domain code */
+
+enum {
+	CTRY_DEBUG	= 0x1ff,		/* debug country code */
+	CTRY_DEFAULT	= 0			/* default country code */
+};
+
+enum {
+	HAL_MODE_11A	= 0x001,
+	HAL_MODE_TURBO	= 0x002,
+	HAL_MODE_11B	= 0x004,
+	HAL_MODE_PUREG	= 0x008,
+#ifdef notdef
+	HAL_MODE_11G	= 0x010,
+#else
+	HAL_MODE_11G	= 0x008,
+#endif
+	HAL_MODE_108G	= 0x020,
+	HAL_MODE_ALL	= 0xfff
+};
+
+typedef struct {
+	int		rateCount;		/* NB: for proper padding */
+	u_int8_t	rateCodeToIndex[32];	/* back mapping */
+	struct {
+		u_int8_t	valid;		/* valid for rate control use */
+		u_int8_t	phy;		/* CCK/OFDM/XR */
+		u_int16_t	rateKbps;	/* transfer rate in kbs */
+		u_int8_t	rateCode;	/* rate for h/w descriptors */
+		u_int8_t	shortPreamble;	/* mask for enabling short
+						 * preamble in CCK rate code */
+		u_int8_t	dot11Rate;	/* value for supported rates
+						 * info element of MLME */
+		u_int8_t	controlRate;	/* index of next lower basic
+						 * rate; used for dur. calcs */
+		u_int16_t	lpAckDuration;	/* long preamble ACK duration */
+		u_int16_t	spAckDuration;	/* short preamble ACK duration*/
+	} info[32];
+} HAL_RATE_TABLE;
+
+typedef struct {
+	u_int		rs_count;		/* number of valid entries */
+	u_int8_t	rs_rates[32];		/* rates */
+} HAL_RATE_SET;
+
+typedef enum {
+	HAL_ANT_VARIABLE = 0,			/* variable by programming */
+	HAL_ANT_FIXED_A	 = 1,			/* fixed to 11a frequencies */
+	HAL_ANT_FIXED_B	 = 2,			/* fixed to 11b frequencies */
+} HAL_ANT_SETTING;
+
+typedef enum {
+	HAL_M_STA	= 1,			/* infrastructure station */
+	HAL_M_IBSS	= 0,			/* IBSS (adhoc) station */
+	HAL_M_HOSTAP	= 6,			/* Software Access Point */
+	HAL_M_MONITOR	= 8			/* Monitor mode */
+} HAL_OPMODE;
+
+typedef struct {
+	u_int8_t	kv_type;		/* one of HAL_CIPHER */
+	u_int8_t	kv_pad;
+	u_int16_t	kv_len;			/* length in bits */
+	u_int8_t	kv_val[16];		/* enough for 128-bit keys */
+	u_int8_t	kv_mic[8];		/* TKIP MIC key */
+} HAL_KEYVAL;
+
+typedef enum {
+	HAL_CIPHER_WEP		= 0,
+	HAL_CIPHER_AES_OCB	= 1,
+	HAL_CIPHER_AES_CCM	= 2,
+	HAL_CIPHER_CKIP		= 3,
+	HAL_CIPHER_TKIP		= 4,
+	HAL_CIPHER_CLR		= 5,		/* no encryption */
+
+	HAL_CIPHER_MIC		= 127		/* TKIP-MIC, not a cipher */
+} HAL_CIPHER;
+
+enum {
+	HAL_SLOT_TIME_9	 = 9,
+	HAL_SLOT_TIME_20 = 20,
+};
+
+/*
+ * Per-station beacon timer state.  Note that the specified
+ * beacon interval (given in TU's) can also include flags
+ * to force a TSF reset and to enable the beacon xmit logic.
+ * If bs_cfpmaxduration is non-zero the hardware is setup to
+ * coexist with a PCF-capable AP.
+ */
+typedef struct {
+	u_int32_t	bs_nexttbtt;		/* next beacon in TU */
+	u_int32_t	bs_nextdtim;		/* next DTIM in TU */
+	u_int32_t	bs_intval;		/* beacon interval+flags */
+#define	HAL_BEACON_PERIOD	0x0000ffff	/* beacon interval period */
+#define	HAL_BEACON_ENA		0x00800000	/* beacon xmit enable */
+#define	HAL_BEACON_RESET_TSF	0x01000000	/* clear TSF */
+	u_int32_t	bs_dtimperiod;
+	u_int16_t	bs_cfpperiod;		/* CFP period in TU */
+	u_int16_t	bs_cfpmaxduration;	/* max CFP duration in TU */
+	u_int32_t	bs_cfpnext;		/* next CFP in TU */
+	u_int16_t	bs_timoffset;		/* byte offset to TIM bitmap */
+	u_int16_t	bs_bmissthreshold;	/* beacon miss threshold */
+	u_int32_t	bs_sleepduration;	/* max sleep duration */
+} HAL_BEACON_STATE;
+
+/*
+ * Per-node statistics maintained by the driver for use in
+ * optimizing signal quality and other operational aspects.
+ */
+typedef struct {
+	u_int32_t	ns_avgbrssi;	/* average beacon rssi */
+	u_int32_t	ns_avgrssi;	/* average data rssi */
+	u_int32_t	ns_avgtxrssi;	/* average tx rssi */
+} HAL_NODE_STATS;
+
+#define	HAL_RSSI_EP_MULTIPLIER	(1<<7)	/* pow2 to optimize out * and / */
+
+struct ath_desc;
+
+/*
+ * Hardware Access Layer (HAL) API.
+ *
+ * Clients of the HAL call ath_hal_attach to obtain a reference to an
+ * ath_hal structure for use with the device.  Hardware-related operations
+ * that follow must call back into the HAL through interface, supplying
+ * the reference as the first parameter.  Note that before using the
+ * reference returned by ath_hal_attach the caller should verify the
+ * ABI version number.
+ */
+struct ath_hal {
+	u_int32_t	ah_magic;	/* consistency check magic number */
+	u_int32_t	ah_abi;		/* HAL ABI version */
+#define	HAL_ABI_VERSION	0x04112900	/* YYMMDDnn */
+	u_int16_t	ah_devid;	/* PCI device ID */
+	u_int16_t	ah_subvendorid;	/* PCI subvendor ID */
+	HAL_SOFTC	ah_sc;		/* back pointer to driver/os state */
+	HAL_BUS_TAG	ah_st;		/* params for register r+w */
+	HAL_BUS_HANDLE	ah_sh;
+	HAL_CTRY_CODE	ah_countryCode;
+
+	u_int32_t	ah_macVersion;	/* MAC version id */
+	u_int16_t	ah_macRev;	/* MAC revision */
+	u_int16_t	ah_phyRev;	/* PHY revision */
+	/* NB: when only one radio is present the rev is in 5Ghz */
+	u_int16_t	ah_analog5GhzRev;/* 5GHz radio revision */
+	u_int16_t	ah_analog2GhzRev;/* 2GHz radio revision */
+
+	const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *,
+				u_int mode);
+	void	  __ahdecl(*ah_detach)(struct ath_hal*);
+
+	/* Reset functions */
+	HAL_BOOL  __ahdecl(*ah_reset)(struct ath_hal *, HAL_OPMODE,
+				HAL_CHANNEL *, HAL_BOOL bChannelChange,
+				HAL_STATUS *status);
+	HAL_BOOL  __ahdecl(*ah_phyDisable)(struct ath_hal *);
+	void	  __ahdecl(*ah_setPCUConfig)(struct ath_hal *);
+	HAL_BOOL  __ahdecl(*ah_perCalibration)(struct ath_hal*, HAL_CHANNEL *);
+	HAL_BOOL  __ahdecl(*ah_setTxPowerLimit)(struct ath_hal *, u_int32_t);
+
+	/* Transmit functions */
+	HAL_BOOL  __ahdecl(*ah_updateTxTrigLevel)(struct ath_hal*,
+				HAL_BOOL incTrigLevel);
+	int	  __ahdecl(*ah_setupTxQueue)(struct ath_hal *, HAL_TX_QUEUE,
+				const HAL_TXQ_INFO *qInfo);
+	HAL_BOOL  __ahdecl(*ah_setTxQueueProps)(struct ath_hal *, int q, 
+				const HAL_TXQ_INFO *qInfo);
+	HAL_BOOL  __ahdecl(*ah_getTxQueueProps)(struct ath_hal *, int q, 
+				HAL_TXQ_INFO *qInfo);
+	HAL_BOOL  __ahdecl(*ah_releaseTxQueue)(struct ath_hal *ah, u_int q);
+	HAL_BOOL  __ahdecl(*ah_resetTxQueue)(struct ath_hal *ah, u_int q);
+	u_int32_t __ahdecl(*ah_getTxDP)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_setTxDP)(struct ath_hal*, u_int, u_int32_t txdp);
+	u_int32_t __ahdecl(*ah_numTxPending)(struct ath_hal *, u_int q);
+	HAL_BOOL  __ahdecl(*ah_startTxDma)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_stopTxDma)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_updateCTSForBursting)(struct ath_hal *,
+				struct ath_desc *, struct ath_desc *,
+				struct ath_desc *, struct ath_desc *,
+				u_int32_t, u_int32_t);
+	HAL_BOOL  __ahdecl(*ah_setupTxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int pktLen, u_int hdrLen,
+				HAL_PKT_TYPE type, u_int txPower,
+				u_int txRate0, u_int txTries0,
+				u_int keyIx, u_int antMode, u_int flags,
+				u_int rtsctsRate, u_int rtsctsDuration);
+	HAL_BOOL  __ahdecl(*ah_setupXTxDesc)(struct ath_hal *, struct ath_desc*,
+				u_int txRate1, u_int txTries1,
+				u_int txRate2, u_int txTries2,
+				u_int txRate3, u_int txTries3);
+	HAL_BOOL  __ahdecl(*ah_fillTxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int segLen, HAL_BOOL firstSeg,
+				HAL_BOOL lastSeg, const struct ath_desc *);
+	HAL_STATUS __ahdecl(*ah_procTxDesc)(struct ath_hal *, struct ath_desc*);
+	void	   __ahdecl(*ah_getTxIntrQueue)(struct ath_hal *, u_int32_t *);
+
+	/* Receive Functions */
+	u_int32_t __ahdecl(*ah_getRxDP)(struct ath_hal*);
+	void	  __ahdecl(*ah_setRxDP)(struct ath_hal*, u_int32_t rxdp);
+	void	  __ahdecl(*ah_enableReceive)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_stopDmaReceive)(struct ath_hal*);
+	void	  __ahdecl(*ah_startPcuReceive)(struct ath_hal*);
+	void	  __ahdecl(*ah_stopPcuReceive)(struct ath_hal*);
+	void	  __ahdecl(*ah_setMulticastFilter)(struct ath_hal*,
+				u_int32_t filter0, u_int32_t filter1);
+	HAL_BOOL  __ahdecl(*ah_setMulticastFilterIndex)(struct ath_hal*,
+				u_int32_t index);
+	HAL_BOOL  __ahdecl(*ah_clrMulticastFilterIndex)(struct ath_hal*,
+				u_int32_t index);
+	u_int32_t __ahdecl(*ah_getRxFilter)(struct ath_hal*);
+	void	  __ahdecl(*ah_setRxFilter)(struct ath_hal*, u_int32_t);
+	HAL_BOOL  __ahdecl(*ah_setupRxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int32_t size, u_int flags);
+	HAL_STATUS __ahdecl(*ah_procRxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int32_t phyAddr, struct ath_desc *next);
+	void	  __ahdecl(*ah_rxMonitor)(struct ath_hal *,
+				const HAL_NODE_STATS *);
+	void	  __ahdecl(*ah_procMibEvent)(struct ath_hal *,
+				const HAL_NODE_STATS *);
+
+	/* Misc Functions */
+	HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *,
+				HAL_CAPABILITY_TYPE, u_int32_t capability,
+				u_int32_t *result);
+	HAL_BOOL   __ahdecl(*ah_setCapability)(struct ath_hal *,
+				HAL_CAPABILITY_TYPE, u_int32_t capability,
+				u_int32_t setting, HAL_STATUS *);
+	HAL_BOOL   __ahdecl(*ah_getDiagState)(struct ath_hal *, int request,
+				const void *args, u_int32_t argsize,
+				void **result, u_int32_t *resultsize);
+	void	  __ahdecl(*ah_getMacAddress)(struct ath_hal *, u_int8_t *);
+	HAL_BOOL  __ahdecl(*ah_setMacAddress)(struct ath_hal *, const u_int8_t*);
+	HAL_BOOL  __ahdecl(*ah_setRegulatoryDomain)(struct ath_hal*,
+				u_int16_t, HAL_STATUS *);
+	void	  __ahdecl(*ah_setLedState)(struct ath_hal*, HAL_LED_STATE);
+	void	  __ahdecl(*ah_writeAssocid)(struct ath_hal*,
+				const u_int8_t *bssid, u_int16_t assocId);
+	HAL_BOOL  __ahdecl(*ah_gpioCfgOutput)(struct ath_hal *, u_int32_t gpio);
+	HAL_BOOL  __ahdecl(*ah_gpioCfgInput)(struct ath_hal *, u_int32_t gpio);
+	u_int32_t __ahdecl(*ah_gpioGet)(struct ath_hal *, u_int32_t gpio);
+	HAL_BOOL  __ahdecl(*ah_gpioSet)(struct ath_hal *,
+				u_int32_t gpio, u_int32_t val);
+	void	  __ahdecl(*ah_gpioSetIntr)(struct ath_hal*, u_int, u_int32_t);
+	u_int32_t __ahdecl(*ah_getTsf32)(struct ath_hal*);
+	u_int64_t __ahdecl(*ah_getTsf64)(struct ath_hal*);
+	void	  __ahdecl(*ah_resetTsf)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_detectCardPresent)(struct ath_hal*);
+	void	  __ahdecl(*ah_updateMibCounters)(struct ath_hal*,
+				HAL_MIB_STATS*);
+	HAL_RFGAIN __ahdecl(*ah_getRfGain)(struct ath_hal*);
+	u_int	  __ahdecl(*ah_getDefAntenna)(struct ath_hal*);
+	void	  __ahdecl(*ah_setDefAntenna)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_setSlotTime)(struct ath_hal*, u_int);
+	u_int	  __ahdecl(*ah_getSlotTime)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_setAckTimeout)(struct ath_hal*, u_int);
+	u_int	  __ahdecl(*ah_getAckTimeout)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_setCTSTimeout)(struct ath_hal*, u_int);
+	u_int	  __ahdecl(*ah_getCTSTimeout)(struct ath_hal*);
+
+	/* Key Cache Functions */
+	u_int32_t __ahdecl(*ah_getKeyCacheSize)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_resetKeyCacheEntry)(struct ath_hal*, u_int16_t);
+	HAL_BOOL  __ahdecl(*ah_isKeyCacheEntryValid)(struct ath_hal *,
+				u_int16_t);
+	HAL_BOOL  __ahdecl(*ah_setKeyCacheEntry)(struct ath_hal*,
+				u_int16_t, const HAL_KEYVAL *,
+				const u_int8_t *, int);
+	HAL_BOOL  __ahdecl(*ah_setKeyCacheEntryMac)(struct ath_hal*,
+				u_int16_t, const u_int8_t *);
+
+	/* Power Management Functions */
+	HAL_BOOL  __ahdecl(*ah_setPowerMode)(struct ath_hal*,
+				HAL_POWER_MODE mode, int setChip,
+				u_int16_t sleepDuration);
+	HAL_POWER_MODE __ahdecl(*ah_getPowerMode)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_initPSPoll)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_enablePSPoll)(struct ath_hal *,
+				u_int8_t *, u_int16_t);
+	HAL_BOOL  __ahdecl(*ah_disablePSPoll)(struct ath_hal *);
+
+	/* Beacon Management Functions */
+	void	  __ahdecl(*ah_beaconInit)(struct ath_hal *,
+				u_int32_t nexttbtt, u_int32_t intval);
+	void	  __ahdecl(*ah_setStationBeaconTimers)(struct ath_hal*,
+				const HAL_BEACON_STATE *);
+	void	  __ahdecl(*ah_resetStationBeaconTimers)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_waitForBeaconDone)(struct ath_hal *,
+				HAL_BUS_ADDR);
+
+	/* Interrupt functions */
+	HAL_BOOL  __ahdecl(*ah_isInterruptPending)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_getPendingInterrupts)(struct ath_hal*, HAL_INT*);
+	HAL_INT	  __ahdecl(*ah_getInterrupts)(struct ath_hal*);
+	HAL_INT	  __ahdecl(*ah_setInterrupts)(struct ath_hal*, HAL_INT);
+};
+
+/* 
+ * Check the PCI vendor ID and device ID against Atheros' values
+ * and return a printable description for any Atheros hardware.
+ * AH_NULL is returned if the ID's do not describe Atheros hardware.
+ */
+extern	const char *__ahdecl ath_hal_probe(u_int16_t vendorid, u_int16_t devid);
+
+/*
+ * Attach the HAL for use with the specified device.  The device is
+ * defined by the PCI device ID.  The caller provides an opaque pointer
+ * to an upper-layer data structure (HAL_SOFTC) that is stored in the
+ * HAL state block for later use.  Hardware register accesses are done
+ * using the specified bus tag and handle.  On successful return a
+ * reference to a state block is returned that must be supplied in all
+ * subsequent HAL calls.  Storage associated with this reference is
+ * dynamically allocated and must be freed by calling the ah_detach
+ * method when the client is done.  If the attach operation fails a
+ * null (AH_NULL) reference will be returned and a status code will
+ * be returned if the status parameter is non-zero.
+ */
+extern	struct ath_hal * __ahdecl ath_hal_attach(u_int16_t devid, HAL_SOFTC,
+		HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS* status);
+
+/*
+ * Return a list of channels available for use with the hardware.
+ * The list is based on what the hardware is capable of, the specified
+ * country code, the modeSelect mask, and whether or not outdoor
+ * channels are to be permitted.
+ *
+ * The channel list is returned in the supplied array.  maxchans
+ * defines the maximum size of this array.  nchans contains the actual
+ * number of channels returned.  If a problem occurred or there were
+ * no channels that met the criteria then AH_FALSE is returned.
+ */
+extern	HAL_BOOL __ahdecl ath_hal_init_channels(struct ath_hal *,
+		HAL_CHANNEL *chans, u_int maxchans, u_int *nchans,
+		HAL_CTRY_CODE cc, u_int16_t modeSelect,
+		HAL_BOOL enableOutdoor, HAL_BOOL enableExtendedChannels);
+
+/*
+ * Return bit mask of wireless modes supported by the hardware.
+ */
+extern	u_int __ahdecl ath_hal_getwirelessmodes(struct ath_hal*, HAL_CTRY_CODE);
+
+/*
+ * Return rate table for specified mode (11a, 11b, 11g, etc).
+ */
+extern	const HAL_RATE_TABLE * __ahdecl ath_hal_getratetable(struct ath_hal *,
+		u_int mode);
+
+/*
+ * Calculate the transmit duration of a frame.
+ */
+extern u_int16_t __ahdecl ath_hal_computetxtime(struct ath_hal *,
+		const HAL_RATE_TABLE *rates, u_int32_t frameLen,
+		u_int16_t rateix, HAL_BOOL shortPreamble);
+
+/*
+ * Convert between IEEE channel number and channel frequency
+ * using the specified channel flags; e.g. CHANNEL_2GHZ.
+ */
+extern	u_int __ahdecl ath_hal_mhz2ieee(u_int mhz, u_int flags);
+extern	u_int __ahdecl ath_hal_ieee2mhz(u_int ieee, u_int flags);
+
+/*
+ * Return a version string for the HAL release.
+ */
+extern	char ath_hal_version[];
+/*
+ * Return a NULL-terminated array of build/configuration options.
+ */
+extern	const char* ath_hal_buildopts[];
+#endif /* _ATH_AH_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_desc.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_desc.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_desc.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_desc.h	2005-02-24 13:06:17.246140208 -0800
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_desc.h,v 1.4 2005/02/22 14:17:23 otaku Exp $
+ */
+
+#ifndef _DEV_ATH_DESC_H
+#define _DEV_ATH_DESC_H
+
+/*
+ * Transmit descriptor status.  This structure is filled
+ * in only after the tx descriptor process method finds a
+ * ``done'' descriptor; at which point it returns something
+ * other than HAL_EINPROGRESS.
+ *
+ * Note that ts_antenna may not be valid for all h/w.  It
+ * should be used only if non-zero.
+ */
+struct ath_tx_status {
+	u_int16_t	ts_seqnum;	/* h/w assigned sequence number */
+	u_int16_t	ts_tstamp;	/* h/w assigned timestamp */
+	u_int8_t	ts_status;	/* frame status, 0 => xmit ok */
+	u_int8_t	ts_rate;	/* h/w transmit rate index */
+#define	HAL_TXSTAT_ALTRATE	0x80	/* alternate xmit rate used */
+	int8_t		ts_rssi;	/* tx ack RSSI */
+	u_int8_t	ts_shortretry;	/* # short retries */
+	u_int8_t	ts_longretry;	/* # long retries */
+	u_int8_t	ts_virtcol;	/* virtual collision count */
+	u_int8_t	ts_antenna;	/* antenna information */
+};
+
+#define	HAL_TXERR_XRETRY	0x01	/* excessive retries */
+#define	HAL_TXERR_FILT		0x02	/* blocked by tx filtering */
+#define	HAL_TXERR_FIFO		0x04	/* fifo underrun */
+
+/*
+ * Receive descriptor status.  This structure is filled
+ * in only after the rx descriptor process method finds a
+ * ``done'' descriptor; at which point it returns something
+ * other than HAL_EINPROGRESS.
+ *
+ * If rx_status is zero, then the frame was received ok;
+ * otherwise the error information is indicated and rs_phyerr
+ * contains a phy error code if HAL_RXERR_PHY is set.  In general
+ * the frame contents is undefined when an error occurred thought
+ * for some errors (e.g. a decryption error), it may be meaningful.
+ *
+ * Note that the receive timestamp is expanded using the TSF to
+ * a full 16 bits (regardless of what the h/w provides directly).
+ *
+ * rx_rssi is in units of dbm above the noise floor.  This value
+ * is measured during the preamble and PLCP; i.e. with the initial
+ * 4us of detection.  The noise floor is typically a consistent
+ * -96dBm absolute power in a 20MHz channel.
+ */
+struct ath_rx_status {
+	u_int16_t	rs_datalen;	/* rx frame length */
+	u_int16_t	rs_tstamp;	/* h/w assigned timestamp */
+	u_int8_t	rs_status;	/* rx status, 0 => recv ok */
+	u_int8_t	rs_phyerr;	/* phy error code */
+	int8_t		rs_rssi;	/* rx frame RSSI */
+	u_int8_t	rs_keyix;	/* key cache index */
+	u_int8_t	rs_rate;	/* h/w receive rate index */
+	u_int8_t	rs_antenna;	/* antenna information */
+	u_int8_t	rs_more;	/* more descriptors follow */
+};
+
+#define	HAL_RXERR_CRC		0x01	/* CRC error on frame */
+#define	HAL_RXERR_PHY		0x02	/* PHY error, rs_phyerr is valid */
+#define	HAL_RXERR_FIFO		0x04	/* fifo overrun */
+#define	HAL_RXERR_DECRYPT	0x08	/* non-Michael decrypt error */
+#define	HAL_RXERR_MIC		0x10	/* Michael MIC decrypt error */
+
+enum {
+	HAL_PHYERR_UNDERRUN		= 0,	/* Transmit underrun */
+	HAL_PHYERR_TIMING		= 1,	/* Timing error */
+	HAL_PHYERR_PARITY		= 2,	/* Illegal parity */
+	HAL_PHYERR_RATE			= 3,	/* Illegal rate */
+	HAL_PHYERR_LENGTH		= 4,	/* Illegal length */
+	HAL_PHYERR_RADAR		= 5,	/* Radar detect */
+	HAL_PHYERR_SERVICE		= 6,	/* Illegal service */
+	HAL_PHYERR_TOR			= 7,	/* Transmit override receive */
+	/* NB: these are specific to the 5212 */
+	HAL_PHYERR_OFDM_TIMING		= 17,	/* */
+	HAL_PHYERR_OFDM_SIGNAL_PARITY	= 18,	/* */
+	HAL_PHYERR_OFDM_RATE_ILLEGAL	= 19,	/* */
+	HAL_PHYERR_OFDM_LENGTH_ILLEGAL	= 20,	/* */
+	HAL_PHYERR_OFDM_POWER_DROP	= 21,	/* */
+	HAL_PHYERR_OFDM_SERVICE		= 22,	/* */
+	HAL_PHYERR_OFDM_RESTART		= 23,	/* */
+	HAL_PHYERR_CCK_TIMING		= 25,	/* */
+	HAL_PHYERR_CCK_HEADER_CRC	= 26,	/* */
+	HAL_PHYERR_CCK_RATE_ILLEGAL	= 27,	/* */
+	HAL_PHYERR_CCK_SERVICE		= 30,	/* */
+	HAL_PHYERR_CCK_RESTART		= 31,	/* */
+};
+
+/* value found in rs_keyix to mark invalid entries */
+#define	HAL_RXKEYIX_INVALID	((u_int8_t) -1)
+/* value used to specify no encryption key for xmit */
+#define	HAL_TXKEYIX_INVALID	((u_int) -1)
+
+/* XXX rs_antenna definitions */
+
+/*
+ * Definitions for the software frame/packet descriptors used by
+ * the Atheros HAL.  This definition obscures hardware-specific
+ * details from the driver.  Drivers are expected to fillin the
+ * portions of a descriptor that are not opaque then use HAL calls
+ * to complete the work.  Status for completed frames is returned
+ * in a device-independent format.
+ */
+struct ath_desc {
+	/*
+	 * The following definitions are passed directly
+	 * the hardware and managed by the HAL.  Drivers
+	 * should not touch those elements marked opaque.
+	 */
+	u_int32_t	ds_link;	/* phys address of next descriptor */
+	u_int32_t	ds_data;	/* phys address of data buffer */
+	u_int32_t	ds_ctl0;	/* opaque DMA control 0 */
+	u_int32_t	ds_ctl1;	/* opaque DMA control 1 */
+	u_int32_t	ds_hw[4];	/* opaque h/w region */
+	/*
+	 * The remaining definitions are managed by software;
+	 * these are valid only after the rx/tx process descriptor
+	 * methods return a non-EINPROGRESS  code.
+	 */
+	union {
+		struct ath_tx_status tx;/* xmit status */
+		struct ath_rx_status rx;/* recv status */
+	} ds_us;
+} __packed;
+
+#define	ds_txstat	ds_us.tx
+#define	ds_rxstat	ds_us.rx
+
+/* flags passed to tx descriptor setup methods */
+#define	HAL_TXDESC_CLRDMASK	0x0001	/* clear destination filter mask */
+#define	HAL_TXDESC_NOACK	0x0002	/* don't wait for ACK */
+#define	HAL_TXDESC_RTSENA	0x0004	/* enable RTS */
+#define	HAL_TXDESC_CTSENA	0x0008	/* enable CTS */
+#define	HAL_TXDESC_INTREQ	0x0010	/* enable per-descriptor interrupt */
+#define	HAL_TXDESC_VEOL		0x0020	/* mark virtual EOL */
+
+/* flags passed to rx descriptor setup methods */
+#define	HAL_RXDESC_INTREQ	0x0020	/* enable per-descriptor interrupt */
+#endif /* _DEV_ATH_AR521XDMA_H */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_devid.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_devid.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_devid.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_devid.h	2005-02-24 13:06:17.249139752 -0800
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_devid.h,v 1.6 2005/02/22 14:17:23 otaku Exp $
+ */
+
+#ifndef _DEV_ATH_DEVID_H_
+#define _DEV_ATH_DEVID_H_
+
+#define ATHEROS_VENDOR_ID	0x168c		/* Atheros PCI vendor ID */
+/*
+ * NB: all Atheros-based devices should have a PCI vendor ID
+ *     of 0x168c, but some vendors, in their infinite wisdom
+ *     do not follow this so we must handle them specially.
+ */
+#define	ATHEROS_3COM_VENDOR_ID	0xa727		/* 3Com 3CRPAG175 vendor ID */
+#define	ATHEROS_3COM2_VENDOR_ID	0x10b7		/* 3Com 3CRDAG675 vendor ID */
+
+/* AR5210 (for reference) */
+#define AR5210_DEFAULT          0x1107          /* No eeprom HW default */
+#define AR5210_PROD             0x0007          /* Final device ID */
+#define AR5210_AP               0x0207          /* Early AP11s */
+
+/* AR5211 */
+#define AR5211_DEFAULT          0x1112          /* No eeprom HW default */
+#define AR5311_DEVID            0x0011          /* Final ar5311 devid */
+#define AR5211_DEVID            0x0012          /* Final ar5211 devid */
+#define AR5211_LEGACY           0xff12          /* Original emulation board */
+#define AR5211_FPGA11B          0xf11b          /* 11b emulation board */
+
+/* AR5212 */
+#define AR5212_DEFAULT          0x1113          /* No eeprom HW default */
+#define AR5212_DEVID            0x0013          /* Final ar5212 devid */
+#define AR5212_FPGA             0xf013          /* Emulation board */
+#define	AR5212_DEVID_IBM	0x1014          /* IBM minipci ID */
+#define AR5212_AR5312_REV2      0x0052          /* AR5312 WMAC (AP31) */
+#define AR5212_AR5312_REV7      0x0057          /* AR5312 WMAC (AP30-040) */
+#define AR5212_AR2313_REV8      0x0058          /* AR2313 WMAC (AP43-030) */
+
+/* AR5212 compatible devid's also attach to 5212 */
+#define	AR5212_DEVID_0014	0x0014
+#define	AR5212_DEVID_0015	0x0015
+#define	AR5212_DEVID_0016	0x0016
+#define	AR5212_DEVID_0017	0x0017
+#define	AR5212_DEVID_0018	0x0018
+#define	AR5212_DEVID_0019	0x0019
+#define AR5212_AR2413      	0x001a          /* AR2413 aka Griffin-lite */
+
+/* AR5213 */
+#define	AR5213_SREV_1_0		0x0055
+#define	AR5213_SREV_REG		0x4020
+
+#define	AR_SUBVENDOR_ID_NOG	0x0e11		/* No 11G subvendor ID */
+#define AR_SUBVENDOR_ID_NEW_A	0x7065		/* Update device to new RD */
+#endif /* _DEV_ATH_DEVID_H */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.c	2005-02-24 13:06:17.258138384 -0800
@@ -0,0 +1,578 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_osdep.c,v 1.7 2005/02/22 14:17:23 otaku Exp $
+ */
+#include "opt_ah.h"
+
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+
+#include <asm/io.h>
+
+#include "ah.h"
+
+#ifndef __MOD_INC_USE_COUNT
+#define	AH_MOD_INC_USE_COUNT(_m)					\
+	if (!try_module_get(_m)) {					\
+		printk(KERN_WARNING "try_module_get failed\n");		\
+		return NULL;						\
+	}
+#define	AH_MOD_DEC_USE_COUNT(_m)	module_put(_m)
+#else
+#define	AH_MOD_INC_USE_COUNT(_m)	MOD_INC_USE_COUNT
+#define	AH_MOD_DEC_USE_COUNT(_m)	MOD_DEC_USE_COUNT
+#endif
+
+#ifdef AH_DEBUG
+static	int ath_hal_debug = 0;
+#endif
+
+int	ath_hal_dma_beacon_response_time = 2;	/* in TU's */
+int	ath_hal_sw_beacon_response_time = 10;	/* in TU's */
+int	ath_hal_additional_swba_backoff = 0;	/* in TU's */
+
+struct ath_hal *
+_ath_hal_attach(u_int16_t devid, HAL_SOFTC sc,
+		HAL_BUS_TAG t, HAL_BUS_HANDLE h, void* s)
+{
+	HAL_STATUS status;
+	struct ath_hal *ah = ath_hal_attach(devid, sc, t, h, &status);
+
+	*(HAL_STATUS *)s = status;
+	if (ah)
+		AH_MOD_INC_USE_COUNT(THIS_MODULE);
+	return ah;
+}
+
+void
+ath_hal_detach(struct ath_hal *ah)
+{
+	(*ah->ah_detach)(ah);
+	AH_MOD_DEC_USE_COUNT(THIS_MODULE);
+}
+
+/*
+ * Print/log message support.
+ */
+
+void __ahdecl
+ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap)
+{
+	char buf[1024];					/* XXX */
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	printk("%s", buf);
+}
+
+void __ahdecl
+ath_hal_printf(struct ath_hal *ah, const char* fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	ath_hal_vprintf(ah, fmt, ap);
+	va_end(ap);
+}
+EXPORT_SYMBOL(ath_hal_printf);
+
+/*
+ * Format an Ethernet MAC for printing.
+ */
+const char* __ahdecl
+ath_hal_ether_sprintf(const u_int8_t *mac)
+{
+	static char etherbuf[18];
+	snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	return etherbuf;
+}
+
+#ifdef AH_ASSERT
+void __ahdecl
+ath_hal_assert_failed(const char* filename, int lineno, const char *msg)
+{
+	printk("Atheros HAL assertion failure: %s: line %u: %s\n",
+		filename, lineno, msg);
+	panic("ath_hal_assert");
+}
+#endif /* AH_ASSERT */
+
+#ifdef AH_DEBUG_ALQ
+/*
+ * ALQ register tracing support.
+ *
+ * Setting hw.ath.hal.alq=1 enables tracing of all register reads and
+ * writes to the file /tmp/ath_hal.log.  The file format is a simple
+ * fixed-size array of records.  When done logging set hw.ath.hal.alq=0
+ * and then decode the file with the ardecode program (that is part of the
+ * HAL).  If you start+stop tracing the data will be appended to an
+ * existing file.
+ *
+ * NB: doesn't handle multiple devices properly; only one DEVICE record
+ *     is emitted and the different devices are not identified.
+ */
+#include "alq/alq.h"
+#include "ah_decode.h"
+
+static	struct alq *ath_hal_alq;
+static	int ath_hal_alq_emitdev;	/* need to emit DEVICE record */
+static	u_int ath_hal_alq_lost;		/* count of lost records */
+static	const char *ath_hal_logfile = "/tmp/ath_hal.log";
+static	u_int ath_hal_alq_qsize = 8*1024;
+
+static int
+ath_hal_setlogging(int enable)
+{
+	int error;
+
+	if (enable) {
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		error = alq_open(&ath_hal_alq, ath_hal_logfile,
+				sizeof (struct athregrec), ath_hal_alq_qsize);
+		ath_hal_alq_lost = 0;
+		ath_hal_alq_emitdev = 1;
+		printk("ath_hal: logging to %s %s\n", ath_hal_logfile,
+			error == 0 ? "enabled" : "could not be setup");
+	} else {
+		if (ath_hal_alq)
+			alq_close(ath_hal_alq);
+		ath_hal_alq = NULL;
+		printk("ath_hal: logging disabled\n");
+		error = 0;
+	}
+	return error;
+}
+
+/*
+ * Deal with the sysctl handler api changing.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+#define	AH_SYSCTL_ARGS_DECL \
+	ctl_table *ctl, int write, struct file *filp, void *buffer, \
+		size_t *lenp
+#define	AH_SYSCTL_ARGS		ctl, write, filp, buffer, lenp
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8) */
+#define	AH_SYSCTL_ARGS_DECL \
+	ctl_table *ctl, int write, struct file *filp, void *buffer,\
+		size_t *lenp, loff_t *ppos
+#define	AH_SYSCTL_ARGS		ctl, write, filp, buffer, lenp, ppos
+#endif
+
+static int
+sysctl_hw_ath_hal_log(AH_SYSCTL_ARGS_DECL)
+{
+	int error, enable;
+
+	ctl->data = &enable;
+	ctl->maxlen = sizeof(enable);
+	enable = (ath_hal_alq != NULL);
+        error = proc_dointvec(AH_SYSCTL_ARGS);
+        if (error || !write)
+                return error;
+	else
+		return ath_hal_setlogging(enable);
+}
+
+static struct ale *
+ath_hal_alq_get(struct ath_hal *ah)
+{
+	struct ale *ale;
+
+	if (ath_hal_alq_emitdev) {
+		ale = alq_get(ath_hal_alq, ALQ_NOWAIT);
+		if (ale) {
+			struct athregrec *r =
+				(struct athregrec *) ale->ae_data;
+			r->op = OP_DEVICE;
+			r->reg = 0;
+			r->val = ah->ah_devid;
+			alq_post(ath_hal_alq, ale);
+			ath_hal_alq_emitdev = 0;
+		} else
+			ath_hal_alq_lost++;
+	}
+	ale = alq_get(ath_hal_alq, ALQ_NOWAIT);
+	if (!ale)
+		ath_hal_alq_lost++;
+	return ale;
+}
+
+void __ahdecl
+ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val)
+{
+	if (ath_hal_alq) {
+		unsigned long flags;
+		struct ale *ale;
+
+		local_irq_save(flags);
+		ale = ath_hal_alq_get(ah);
+		if (ale) {
+			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->op = OP_WRITE;
+			r->reg = reg;
+			r->val = val;
+			alq_post(ath_hal_alq, ale);
+		}
+		local_irq_restore(flags);
+	}
+	_OS_REG_WRITE(ah, reg, val);
+}
+EXPORT_SYMBOL(ath_hal_reg_write);
+
+u_int32_t __ahdecl
+ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg)
+{
+	u_int32_t val;
+
+	val = _OS_REG_READ(ah, reg);
+	if (ath_hal_alq) {
+		unsigned long flags;
+		struct ale *ale;
+
+		local_irq_save(flags);
+		ale = ath_hal_alq_get(ah);
+		if (ale) {
+			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->op = OP_READ;
+			r->reg = reg;
+			r->val = val;
+			alq_post(ath_hal_alq, ale);
+		}
+		local_irq_restore(flags);
+	}
+	return val;
+}
+EXPORT_SYMBOL(ath_hal_reg_read);
+
+void __ahdecl
+OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v)
+{
+	if (ath_hal_alq) {
+		unsigned long flags;
+		struct ale *ale;
+
+		local_irq_save(flags);
+		ale = ath_hal_alq_get(ah);
+		if (ale) {
+			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->op = OP_MARK;
+			r->reg = id;
+			r->val = v;
+			alq_post(ath_hal_alq, ale);
+		}
+		local_irq_restore(flags);
+	}
+}
+EXPORT_SYMBOL(OS_MARK);
+#elif defined(AH_DEBUG) || defined(AH_REGOPS_FUNC)
+/*
+ * Memory-mapped device register read/write.  These are here
+ * as routines when debugging support is enabled and/or when
+ * explicitly configured to use function calls.  The latter is
+ * for architectures that might need to do something before
+ * referencing memory (e.g. remap an i/o window).
+ *
+ * NB: see the comments in ah_osdep.h about byte-swapping register
+ *     reads and writes to understand what's going on below.
+ */
+void __ahdecl
+ath_hal_reg_write(struct ath_hal *ah, u_int reg, u_int32_t val)
+{
+#ifdef AH_DEBUG
+	if (ath_hal_debug > 1)
+		ath_hal_printf(ah, "WRITE 0x%x <= 0x%x\n", reg, val);
+#endif
+	_OS_REG_WRITE(ah, reg, val);
+}
+EXPORT_SYMBOL(ath_hal_reg_write);
+
+u_int32_t __ahdecl
+ath_hal_reg_read(struct ath_hal *ah, u_int reg)
+{
+ 	u_int32_t val;
+
+	val = _OS_REG_READ(ah, reg);
+#ifdef AH_DEBUG
+	if (ath_hal_debug > 1)
+		ath_hal_printf(ah, "READ 0x%x => 0x%x\n", reg, val);
+#endif
+	return val;
+}
+EXPORT_SYMBOL(ath_hal_reg_read);
+#endif /* AH_DEBUG || AH_REGOPS_FUNC */
+
+#ifdef AH_DEBUG
+void __ahdecl
+HALDEBUG(struct ath_hal *ah, const char* fmt, ...)
+{
+	if (ath_hal_debug) {
+		__va_list ap;
+		va_start(ap, fmt);
+		ath_hal_vprintf(ah, fmt, ap);
+		va_end(ap);
+	}
+}
+
+
+void __ahdecl
+HALDEBUGn(struct ath_hal *ah, u_int level, const char* fmt, ...)
+{
+	if (ath_hal_debug >= level) {
+		__va_list ap;
+		va_start(ap, fmt);
+		ath_hal_vprintf(ah, fmt, ap);
+		va_end(ap);
+	}
+}
+#endif /* AH_DEBUG */
+
+/*
+ * Delay n microseconds.
+ */
+void __ahdecl
+ath_hal_delay(int n)
+{
+	udelay(n);
+}
+
+u_int32_t __ahdecl
+ath_hal_getuptime(struct ath_hal *ah)
+{
+	return ((jiffies / HZ) * 1000) + (jiffies % HZ) * (1000 / HZ);
+}
+EXPORT_SYMBOL(ath_hal_getuptime);
+
+/*
+ * Allocate/free memory.
+ */
+
+void * __ahdecl
+ath_hal_malloc(size_t size)
+{
+	void *p;
+	p = kmalloc(size, GFP_KERNEL);
+	if (p)
+		OS_MEMZERO(p, size);
+	return p;
+		
+}
+
+void __ahdecl
+ath_hal_free(void* p)
+{
+	kfree(p);
+}
+
+void __ahdecl
+ath_hal_memzero(void *dst, size_t n)
+{
+	memset(dst, 0, n);
+}
+EXPORT_SYMBOL(ath_hal_memzero);
+
+void * __ahdecl
+ath_hal_memcpy(void *dst, const void *src, size_t n)
+{
+	return memcpy(dst, src, n);
+}
+EXPORT_SYMBOL(ath_hal_memcpy);
+
+#ifdef CONFIG_SYSCTL
+enum {
+	DEV_ATH		= 9,			/* XXX must match driver */
+};
+
+#define	CTL_AUTO	-2	/* cannot be CTL_ANY or CTL_NONE */
+
+static ctl_table ath_hal_sysctls[] = {
+#ifdef AH_DEBUG
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "debug",
+	  .mode		= 0644,
+	  .data		= &ath_hal_debug,
+	  .maxlen	= sizeof(ath_hal_debug),
+	  .proc_handler	= proc_dointvec
+	},
+#endif
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "dma_beacon_response_time",
+	  .data		= &ath_hal_dma_beacon_response_time,
+	  .maxlen	= sizeof(ath_hal_dma_beacon_response_time),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,	
+	  .procname	= "sw_beacon_response_time",
+	  .mode		= 0644,
+	  .data		= &ath_hal_sw_beacon_response_time,
+	  .maxlen	= sizeof(ath_hal_sw_beacon_response_time),
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "swba_backoff",
+	  .mode		= 0644,
+	  .data		= &ath_hal_additional_swba_backoff,
+	  .maxlen	= sizeof(ath_hal_additional_swba_backoff),
+	  .proc_handler	= proc_dointvec
+	},
+#ifdef AH_DEBUG_ALQ
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "alq",
+	  .mode		= 0644,
+	  .proc_handler	= sysctl_hw_ath_hal_log
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "alq_size",
+	  .mode		= 0644,
+	  .data		= &ath_hal_alq_qsize,
+	  .maxlen	= sizeof(ath_hal_alq_qsize),
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "alq_lost",
+	  .mode		= 0644,
+	  .data		= &ath_hal_alq_lost,
+	  .maxlen	= sizeof(ath_hal_alq_lost),
+	  .proc_handler	= proc_dointvec
+	},
+#endif
+	{ 0 }
+};
+static ctl_table ath_hal_table[] = {
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "hal",
+	  .mode		= 0555,
+	  .child	= ath_hal_sysctls
+	}, { 0 }
+};
+static ctl_table ath_ath_table[] = {
+	{ .ctl_name	= DEV_ATH,
+	  .procname	= "ath",
+	  .mode		= 0555,
+	  .child	= ath_hal_table
+	}, { 0 }
+};
+static ctl_table ath_root_table[] = {
+	{ .ctl_name	= CTL_DEV,
+	  .procname	= "dev",
+	  .mode		= 0555,
+	  .child	= ath_ath_table
+	}, { 0 }
+};
+static struct ctl_table_header *ath_hal_sysctl_header;
+
+void
+ath_hal_sysctl_register(void)
+{
+	static int initialized = 0;
+
+	if (!initialized) {
+		ath_hal_sysctl_header =
+			register_sysctl_table(ath_root_table, 1);
+		initialized = 1;
+	}
+}
+
+void
+ath_hal_sysctl_unregister(void)
+{
+	if (ath_hal_sysctl_header)
+		unregister_sysctl_table(ath_hal_sysctl_header);
+}
+#endif /* CONFIG_SYSCTL */
+
+/*
+ * Module glue.
+ */
+static char *dev_info = "ath_hal";
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("Atheros Hardware Access Layer (HAL)");
+MODULE_SUPPORTED_DEVICE("Atheros WLAN devices");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Proprietary");
+#endif
+
+EXPORT_SYMBOL(ath_hal_probe);
+EXPORT_SYMBOL(_ath_hal_attach);
+EXPORT_SYMBOL(ath_hal_detach);
+EXPORT_SYMBOL(ath_hal_init_channels);
+EXPORT_SYMBOL(ath_hal_getwirelessmodes);
+EXPORT_SYMBOL(ath_hal_computetxtime);
+EXPORT_SYMBOL(ath_hal_mhz2ieee);
+EXPORT_SYMBOL(ath_hal_ieee2mhz);
+
+static int __init
+init_ath_hal(void)
+{
+	const char *sep;
+	int i;
+
+	printk(KERN_INFO "%s: %s (", dev_info, ath_hal_version);
+	sep = "";
+	for (i = 0; ath_hal_buildopts[i] != NULL; i++) {
+		printk("%s%s", sep, ath_hal_buildopts[i]);
+		sep = ", ";
+	}
+	printk(")\n");
+#ifdef CONFIG_SYSCTL
+	ath_hal_sysctl_register();
+#endif
+	return (0);
+}
+module_init(init_ath_hal);
+
+static void __exit
+exit_ath_hal(void)
+{
+#ifdef CONFIG_SYSCTL
+	ath_hal_sysctl_unregister();
+#endif
+	printk(KERN_INFO "%s: driver unloaded\n", dev_info);
+}
+module_exit(exit_ath_hal);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.h	2005-02-24 13:06:17.261137928 -0800
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_osdep.h,v 1.4 2005/02/23 17:25:20 otaku Exp $
+ */
+#ifndef _ATH_AH_OSDEP_H_
+#define _ATH_AH_OSDEP_H_
+/*
+ * Atheros Hardware Access Layer (HAL) OS Dependent Definitions.
+ */
+
+/*
+ * Starting with 2.6.4 the kernel supports a configuration option
+ * to pass parameters in registers.  If this is enabled we must
+ * mark all function interfaces in+out of the HAL to pass parameters
+ * on the stack as this is the convention used internally (for
+ * maximum portability).
+ */
+#ifdef CONFIG_REGPARM
+#define	__ahdecl	__attribute__((regparm(0)))
+#else
+#define	__ahdecl
+#endif
+#ifndef __packed
+#define	__packed	__attribute__((__packed__))
+#endif
+
+/*
+ * When building the HAL proper we use no GPL-contaminated include
+ * files and must define these types ourself.  Beware of these being
+ * mismatched against the contents of <linux/types.h>
+ */
+#ifndef _LINUX_TYPES_H
+/* NB: arm defaults to unsigned so be explicit */
+typedef signed char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+typedef long long int64_t;
+
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int u_int32_t;
+typedef unsigned long long u_int64_t;
+
+typedef unsigned int size_t;
+typedef unsigned int u_int;
+typedef	void *va_list;
+#endif
+
+/*
+ * Linux/BSD gcc compatibility shims.
+ */
+#define	__printflike(_a,_b) \
+	__attribute__ ((__format__ (__printf__, _a, _b)))
+#define	__va_list	va_list 
+#define	OS_INLINE	__inline
+
+typedef void* HAL_SOFTC;
+typedef int HAL_BUS_TAG;
+typedef void* HAL_BUS_HANDLE;
+typedef u_int32_t HAL_BUS_ADDR;			/* XXX architecture dependent */
+
+/*
+ * Delay n microseconds.
+ */
+extern	void __ahdecl ath_hal_delay(int);
+#define	OS_DELAY(_n)	ath_hal_delay(_n)
+
+#define	OS_MEMZERO(_a, _n)	ath_hal_memzero((_a), (_n))
+extern void __ahdecl ath_hal_memzero(void *, size_t);
+#define	OS_MEMCPY(_d, _s, _n)	ath_hal_memcpy(_d,_s,_n)
+extern void * __ahdecl ath_hal_memcpy(void *, const void *, size_t);
+
+#ifndef abs
+#define	abs(_a)		__builtin_abs(_a)
+#endif
+
+struct ath_hal;
+extern	u_int32_t __ahdecl ath_hal_getuptime(struct ath_hal *);
+#define	OS_GETUPTIME(_ah)	ath_hal_getuptime(_ah)
+
+/*
+ * Byte order/swapping support.
+ */
+#define	AH_LITTLE_ENDIAN	1234
+#define	AH_BIG_ENDIAN		4321
+
+#if AH_BYTE_ORDER == AH_BIG_ENDIAN
+/*
+ * This could be optimized but since we only use it for
+ * a few registers there's little reason to do so.
+ */
+static inline u_int32_t
+__bswap32(u_int32_t _x)
+{
+ 	return ((u_int32_t)(
+	      (((const u_int8_t *)(&_x))[0]    ) |
+	      (((const u_int8_t *)(&_x))[1]<< 8) |
+	      (((const u_int8_t *)(&_x))[2]<<16) |
+	      (((const u_int8_t *)(&_x))[3]<<24))
+	);
+}
+#else
+#define __bswap32(_x)	(_x)
+#endif
+
+/*
+ * Register read/write; we assume the registers will always
+ * be memory-mapped.  Note that register accesses are done
+ * using target-specific functions when debugging is enabled
+ * (AH_DEBUG) or we are explicitly configured this way.  The
+ * latter is used on some platforms where the full i/o space
+ * cannot be directly mapped.
+ *
+ * The hardware registers are native little-endian byte order.
+ * Big-endian hosts are handled by enabling hardware byte-swap
+ * of register reads and writes at reset.  But the PCI clock
+ * domain registers are not byte swapped!  Thus, on big-endian
+ * platforms we have to byte-swap thoese registers specifically.
+ * Most of this code is collapsed at compile time because the
+ * register values are constants.
+ */
+#if AH_BYTE_ORDER == AH_BIG_ENDIAN
+
+#if ( defined(CONFIG_PPC_PMAC) || defined(CONFIG_ARCH_IXP425) || defined(CONFIG_ARCH_IXP4XX)) /* ixp4xx or PowerPC architecture */
+
+#define _OS_REG_WRITE(_ah, _reg, _val) do {				    \
+	if ( (_reg) >= 0x4000 && (_reg) < 0x5000)			    \
+	    writel((_val), (volatile unsigned long)((_ah)->ah_sh + (_reg)));\
+	else								    \
+	    writel(__bswap32(_val), (volatile unsigned long)((_ah)->ah_sh + (_reg))); \
+} while (0)
+#define _OS_REG_READ(_ah, _reg) \
+	(((_reg) >= 0x4000 && (_reg) < 0x5000) ?			    \
+	    readl((volatile unsigned long)((_ah)->ah_sh + (_reg))) :	    \
+	    __bswap32(readl((volatile unsigned long)((_ah)->ah_sh + (_reg)))))
+
+#else  /* normal case */
+
+#define _OS_REG_WRITE(_ah, _reg, _val) do {				    \
+	if ( (_reg) >= 0x4000 && (_reg) < 0x5000)			    \
+		*((volatile u_int32_t *)((_ah)->ah_sh + (_reg))) =	    \
+			__bswap32((_val));				    \
+	else								    \
+		*((volatile u_int32_t *)((_ah)->ah_sh + (_reg))) = (_val);  \
+} while (0)
+#define _OS_REG_READ(_ah, _reg) \
+	(((_reg) >= 0x4000 && (_reg) < 0x5000) ? \
+		__bswap32(*((volatile u_int32_t *)((_ah)->ah_sh + (_reg)))) : \
+		*((volatile u_int32_t *)((_ah)->ah_sh + (_reg))))
+
+#endif
+#else /* AH_LITTLE_ENDIAN */
+#define _OS_REG_WRITE(_ah, _reg, _val) do { \
+	*((volatile u_int32_t *)((_ah)->ah_sh + (_reg))) = (_val); \
+} while (0)
+#define _OS_REG_READ(_ah, _reg) \
+	*((volatile u_int32_t *)((_ah)->ah_sh + (_reg)))
+#endif /* AH_BYTE_ORDER */
+
+#if defined(AH_DEBUG) || defined(AH_REGOPS_FUNC) || defined(AH_DEBUG_ALQ)
+/* use functions to do register operations */
+#define	OS_REG_WRITE(_ah, _reg, _val)	ath_hal_reg_write(_ah, _reg, _val)
+#define	OS_REG_READ(_ah, _reg)		ath_hal_reg_read(_ah, _reg)
+
+extern	void __ahdecl ath_hal_reg_write(struct ath_hal *ah,
+		u_int reg, u_int32_t val);
+extern	u_int32_t __ahdecl ath_hal_reg_read(struct ath_hal *ah, u_int reg);
+#else
+/* inline register operations */
+#define OS_REG_WRITE(_ah, _reg, _val)	_OS_REG_WRITE(_ah, _reg, _val)
+#define OS_REG_READ(_ah, _reg)		_OS_REG_READ(_ah, _reg)
+#endif /* AH_DEBUG || AH_REGFUNC || AH_DEBUG_ALQ */
+
+#ifdef AH_DEBUG_ALQ
+extern	void __ahdecl OS_MARK(struct ath_hal *, u_int id, u_int32_t value);
+#else
+#define	OS_MARK(_ah, _id, _v)
+#endif
+
+/*
+ * Linux-specific attach/detach methods needed for module reference counting.
+ *
+ * XXX We can't use HAL_STATUS because the type isn't defined at this
+ *     point (circular dependency); we wack the type and patch things
+ *     up in the function.
+ *
+ * NB: These are intentionally not marked __ahdecl since they are
+ *     compiled with the default calling convetion and are not called
+ *     from within the HAL.
+ */
+extern	struct ath_hal *_ath_hal_attach(u_int16_t devid, HAL_SOFTC,
+		HAL_BUS_TAG, HAL_BUS_HANDLE, void* status);
+extern	void ath_hal_detach(struct ath_hal *);
+
+#endif /* _ATH_AH_OSDEP_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/version.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/version.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/version.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/version.h	2005-02-24 13:06:17.252139296 -0800
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: version.h,v 1.2 2005/02/22 14:17:23 otaku Exp $
+ */
+#define	ATH_HAL_VERSION	"0.9.14.9"
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Kconfig
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Kconfig	2005-02-24 13:06:17.418114064 -0800
@@ -0,0 +1,3 @@
+
+config NET80211
+	tristate
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Makefile
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Makefile	2005-02-24 13:06:17.433111784 -0800
@@ -0,0 +1,38 @@
+#
+# Makefile for the 802.11 NET80211 module.
+#
+
+
+COMPAT=	drivers/net/wireless/net80211/compat
+
+EXTRA_CFLAGS+=	-include ${COMPAT}/compat.h -I${COMPAT} -I${src}/..
+
+#
+# There are two authenticator mechanisms: an in-kernel implementation
+# (wlan_auth+wlan_radius) and an external implementation (wlan_xauth) that
+# requires a user process to manage the authentication process.  By default
+# the external authenticator is used.  ieee80211_proto.c has a table of module
+# names that defines the default module to auto-load for each authentication
+# scheme; to get the in-kernel authenticator by default modify it to load
+# wlan_auth instead of wlan_xauth or manually load wlan_auth prior to use.
+#
+MOD_AUTH	:= wlan_xauth.o
+
+obj-$(CONFIG_NET80211) += wlan.o wlan_wep.o wlan_tkip.o wlan_ccmp.o \
+		   $(MOD_AUTH) wlan_acl.o
+
+wlan-objs	:= if_media.o rc4.o \
+		   ieee80211.o ieee80211_crypto.o ieee80211_input.o \
+		   ieee80211_node.o ieee80211_output.o ieee80211_proto.o \
+		   ieee80211_wireless.o ieee80211_linux.o \
+		   ieee80211_crypto_none.o
+wlan_wep-objs	:= ieee80211_crypto_wep.o
+wlan_tkip-objs	:= ieee80211_crypto_tkip.o
+wlan_ccmp-objs	:= ieee80211_crypto_ccmp.o
+wlan_xauth-objs	:= ieee80211_xauth.o
+wlan_auth-objs	:= ieee80211_dot1x.o
+wlan_radius-objs:= ieee80211_radius.o
+wlan_acl-objs	:= ieee80211_acl.o
+
+
+
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/compat.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/compat.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/compat.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/compat.h	2005-02-24 13:06:17.440110720 -0800
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: compat.h,v 1.8 2005/02/16 16:08:44 samleffler Exp $
+ */
+#ifndef _ATH_COMPAT_H_
+#define _ATH_COMPAT_H_
+/*
+ * BSD/Linux compatibility shims.  These are used mainly to
+ * minimize differences when importing necesary BSD code.
+ */
+#define	NBBY	8			/* number of bits/byte */
+
+#define	roundup(x, y)	((((x)+((y)-1))/(y))*(y))  /* to any y */
+#define	howmany(x, y)	(((x)+((y)-1))/(y))
+
+/* Bit map related macros. */
+#define	setbit(a,i)	((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define	clrbit(a,i)	((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+#define	isset(a,i)	((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define	isclr(a,i)	(((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+
+#define	__packed	__attribute__((__packed__))
+#define	__printflike(_a,_b) \
+	__attribute__ ((__format__ (__printf__, _a, _b)))
+
+#ifndef ALIGNED_POINTER
+/*
+ * ALIGNED_POINTER is a boolean macro that checks whether an address
+ * is valid to fetch data elements of type t from on this architecture.
+ * This does not reflect the optimal alignment, just the possibility
+ * (within reasonable limits). 
+ *
+ */
+#define ALIGNED_POINTER(p,t)	1
+#endif
+
+#ifdef __KERNEL__
+#include <asm/page.h>
+
+#define	KASSERT(exp, msg) do {			\
+	if (unlikely(!(exp))) {			\
+		printk msg;			\
+		BUG();				\
+	}					\
+} while (0)
+#endif /* __KERNEL__ */
+
+/*
+ * NetBSD/FreeBSD defines for file version.
+ */
+#define	__FBSDID(_s)
+#define	__KERNEL_RCSID(_n,_s)
+#endif /* _ATH_COMPAT_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/sys/queue.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/sys/queue.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/sys/queue.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/sys/queue.h	2005-02-24 13:06:17.446109808 -0800
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.54 2002/08/05 05:18:43 alfred Exp $
+ * $Id: queue.h,v 1.5 2004/08/05 17:34:51 samleffler Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *			SLIST	LIST	STAILQ	TAILQ
+ * _HEAD		+	+	+	+
+ * _HEAD_INITIALIZER	+	+	+	+
+ * _ENTRY		+	+	+	+
+ * _INIT		+	+	+	+
+ * _EMPTY		+	+	+	+
+ * _FIRST		+	+	+	+
+ * _NEXT		+	+	+	+
+ * _PREV		-	-	-	+
+ * _LAST		-	-	+	+
+ * _FOREACH		+	+	+	+
+ * _FOREACH_REVERSE	-	-	-	+
+ * _INSERT_HEAD		+	+	+	+
+ * _INSERT_BEFORE	-	+	-	+
+ * _INSERT_AFTER	+	+	+	+
+ * _INSERT_TAIL		-	-	+	+
+ * _CONCAT		-	-	+	+
+ * _REMOVE_HEAD		+	-	+	-
+ * _REMOVE		+	+	+	+
+ *
+ */
+#define QUEUE_MACRO_DEBUG 0
+#if QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+	char * lastfile;
+	int lastline;
+	char * prevfile;
+	int prevline;
+};
+
+#define TRACEBUF	struct qm_trace trace;
+#define TRASHIT(x)	do {(x) = (void *)-1;} while (0)
+
+#define QMD_TRACE_HEAD(head) do {					\
+	(head)->trace.prevline = (head)->trace.lastline;		\
+	(head)->trace.prevfile = (head)->trace.lastfile;		\
+	(head)->trace.lastline = __LINE__;				\
+	(head)->trace.lastfile = __FILE__;				\
+} while (0)
+
+#define QMD_TRACE_ELEM(elem) do {					\
+	(elem)->trace.prevline = (elem)->trace.lastline;		\
+	(elem)->trace.prevfile = (elem)->trace.lastfile;		\
+	(elem)->trace.lastline = __LINE__;				\
+	(elem)->trace.lastfile = __FILE__;				\
+} while (0)
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define TRACEBUF
+#define TRASHIT(x)
+#endif	/* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define	SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+ 
+#define	SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+ 
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
+
+#define	SLIST_FIRST(head)	((head)->slh_first)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for ((var) = SLIST_FIRST((head));				\
+	    (var);							\
+	    (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field)			\
+	for ((varp) = &SLIST_FIRST((head));				\
+	    ((var) = *(varp)) != NULL;					\
+	    (varp) = &SLIST_NEXT((var), field))
+
+#define	SLIST_INIT(head) do {						\
+	SLIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);	\
+	SLIST_NEXT((slistelm), field) = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_FIRST((head));			\
+	SLIST_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_REMOVE(head, elm, type, field) do {			\
+	if (SLIST_FIRST((head)) == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = SLIST_FIRST((head));		\
+		while (SLIST_NEXT(curelm, field) != (elm))		\
+			curelm = SLIST_NEXT(curelm, field);		\
+		SLIST_NEXT(curelm, field) =				\
+		    SLIST_NEXT(SLIST_NEXT(curelm, field), field);	\
+	}								\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);	\
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define	STAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *stqh_first;/* first element */			\
+	struct type **stqh_last;/* addr of last next element */		\
+}
+
+#define	STAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).stqh_first }
+
+#define	STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define	STAILQ_CONCAT(head1, head2) do {				\
+	if (!STAILQ_EMPTY((head2))) {					\
+		*(head1)->stqh_last = (head2)->stqh_first;		\
+		(head1)->stqh_last = (head2)->stqh_last;		\
+		STAILQ_INIT((head2));					\
+	}								\
+} while (0)
+
+#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
+
+#define	STAILQ_FIRST(head)	((head)->stqh_first)
+
+#define	STAILQ_FOREACH(var, head, field)				\
+	for((var) = STAILQ_FIRST((head));				\
+	   (var);							\
+	   (var) = STAILQ_NEXT((var), field))
+
+#define	STAILQ_INIT(head) do {						\
+	STAILQ_FIRST((head)) = NULL;					\
+	(head)->stqh_last = &STAILQ_FIRST((head));			\
+} while (0)
+
+#define	STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {		\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_NEXT((tqelm), field) = (elm);				\
+} while (0)
+
+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL)	\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	STAILQ_NEXT((elm), field) = NULL;				\
+	*(head)->stqh_last = (elm);					\
+	(head)->stqh_last = &STAILQ_NEXT((elm), field);			\
+} while (0)
+
+#define	STAILQ_LAST(head, type, field)					\
+	(STAILQ_EMPTY((head)) ?						\
+		NULL :							\
+	        ((struct type *)					\
+		((char *)((head)->stqh_last) - __offsetof(struct type, field))))
+
+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	if (STAILQ_FIRST((head)) == (elm)) {				\
+		STAILQ_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = STAILQ_FIRST((head));		\
+		while (STAILQ_NEXT(curelm, field) != (elm))		\
+			curelm = STAILQ_NEXT(curelm, field);		\
+		if ((STAILQ_NEXT(curelm, field) =			\
+		     STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+			(head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+	}								\
+} while (0)
+
+#define	STAILQ_REMOVE_HEAD(head, field) do {				\
+	if ((STAILQ_FIRST((head)) =					\
+	     STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)		\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+#define	STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do {			\
+	if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL)	\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define	ATH_LIST_HEAD(name, type)					\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define	LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+
+#define	LIST_EMPTY(head)	((head)->lh_first == NULL)
+
+#define	LIST_FIRST(head)	((head)->lh_first)
+
+#define	LIST_FOREACH(var, head, field)					\
+	for ((var) = LIST_FIRST((head));				\
+	    (var);							\
+	    (var) = LIST_NEXT((var), field))
+
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+		LIST_NEXT((listelm), field)->field.le_prev =		\
+		    &LIST_NEXT((elm), field);				\
+	LIST_NEXT((listelm), field) = (elm);				\
+	(elm)->field.le_prev = &LIST_NEXT((listelm), field);		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	LIST_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &LIST_NEXT((elm), field);		\
+} while (0)
+
+#define	LIST_INSERT_HEAD(head, elm, field) do {				\
+	if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)	\
+		LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+	LIST_FIRST((head)) = (elm);					\
+	(elm)->field.le_prev = &LIST_FIRST((head));			\
+} while (0)
+
+#define	LIST_NEXT(elm, field)	((elm)->field.le_next)
+
+#define	LIST_REMOVE(elm, field) do {					\
+	if (LIST_NEXT((elm), field) != NULL)				\
+		LIST_NEXT((elm), field)->field.le_prev = 		\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = LIST_NEXT((elm), field);		\
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define	TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+	TRACEBUF							\
+}
+
+#define	TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define	TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+	TRACEBUF							\
+}
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_CONCAT(head1, head2, field) do {				\
+	if (!TAILQ_EMPTY(head2)) {					\
+		*(head1)->tqh_last = (head2)->tqh_first;		\
+		(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;	\
+		(head1)->tqh_last = (head2)->tqh_last;			\
+		TAILQ_INIT((head2));					\
+		QMD_TRACE_HEAD(head);					\
+		QMD_TRACE_HEAD(head2);					\
+	}								\
+} while (0)
+
+#define	TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
+
+#define	TAILQ_FIRST(head)	((head)->tqh_first)
+
+#define	TAILQ_FOREACH(var, head, field)					\
+	for ((var) = TAILQ_FIRST((head));				\
+	    (var);							\
+	    (var) = TAILQ_NEXT((var), field))
+
+#define	TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for ((var) = TAILQ_LAST((head), headname);			\
+	    (var);							\
+	    (var) = TAILQ_PREV((var), headname, field))
+
+#define	TAILQ_INIT(head) do {						\
+	TAILQ_FIRST((head)) = NULL;					\
+	(head)->tqh_last = &TAILQ_FIRST((head));			\
+	QMD_TRACE_HEAD(head);						\
+} while (0)
+
+#define	TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    &TAILQ_NEXT((elm), field);				\
+	else {								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+		QMD_TRACE_HEAD(head);					\
+	}								\
+	TAILQ_NEXT((listelm), field) = (elm);				\
+	(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);		\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+	QMD_TRACE_ELEM(&listelm->field);				\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	TAILQ_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);		\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+	QMD_TRACE_ELEM(&listelm->field);				\
+} while (0)
+
+#define	TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)	\
+		TAILQ_FIRST((head))->field.tqe_prev =			\
+		    &TAILQ_NEXT((elm), field);				\
+	else								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+	TAILQ_FIRST((head)) = (elm);					\
+	(elm)->field.tqe_prev = &TAILQ_FIRST((head));			\
+	QMD_TRACE_HEAD(head);						\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+} while (0)
+
+#define	TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	TAILQ_NEXT((elm), field) = NULL;				\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &TAILQ_NEXT((elm), field);			\
+	QMD_TRACE_HEAD(head);						\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+} while (0)
+
+#define	TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define	TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define	TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define	TAILQ_REMOVE(head, elm, field) do {				\
+	if ((TAILQ_NEXT((elm), field)) != NULL)				\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else {								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+		QMD_TRACE_HEAD(head);					\
+	}								\
+	*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);		\
+	TRASHIT((elm)->field.tqe_next);					\
+	TRASHIT((elm)->field.tqe_prev);					\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+} while (0)
+
+
+#ifdef _KERNEL
+
+/*
+ * XXX insque() and remque() are an old way of handling certain queues.
+ * They bogusly assumes that all queue heads look alike.
+ */
+
+struct quehead {
+	struct quehead *qh_link;
+	struct quehead *qh_rlink;
+};
+
+#ifdef	__GNUC__
+
+static __inline void
+insque(void *a, void *b)
+{
+	struct quehead *element = (struct quehead *)a,
+		 *head = (struct quehead *)b;
+
+	element->qh_link = head->qh_link;
+	element->qh_rlink = head;
+	head->qh_link = element;
+	element->qh_link->qh_rlink = element;
+}
+
+static __inline void
+remque(void *a)
+{
+	struct quehead *element = (struct quehead *)a;
+
+	element->qh_link->qh_rlink = element->qh_rlink;
+	element->qh_rlink->qh_link = element->qh_link;
+	element->qh_rlink = 0;
+}
+
+#else /* !__GNUC__ */
+
+void	insque(void *a, void *b);
+void	remque(void *a);
+
+#endif /* __GNUC__ */
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/eapol.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/eapol.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/eapol.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/eapol.h	2005-02-24 13:06:17.296132608 -0800
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.11 2004/01/15 08:44:27 onoe Exp $
+ */
+#ifndef _NET80211_EAPOL_H_
+#define _NET80211_EAPOL_H_
+
+/*
+ * Extendible Authentication Protocol over LAN (EAPOL) aka 802.1x.
+ */
+struct eapol_hdr {
+	u_int8_t	eapol_ver;	/* protocol version */
+#define	EAPOL_VERSION	0x01
+	u_int8_t	eapol_type;	/* packet type */
+	u_int16_t	eapol_len;	/* packet body length in bytes */
+	/* variable length body follows */
+} __attribute__((__packed__));
+
+enum {
+	EAPOL_TYPE_EAP		= 0x00,
+	EAPOL_TYPE_START	= 0x01,
+	EAPOL_TYPE_LOGOFF	= 0x02,
+	EAPOL_TYPE_KEY		= 0x03,
+	EAPOL_TYPE_EASFA	= 0x04	/* Encapsulated ASF Alert */
+};
+#define	EAPOL_TYPE_LAST	EAPOL_TYPE_EASFA
+
+/* Ethernet frame type (belongs elsewhere) */
+#define	ETH_P_EAPOL	0x888e		/* EAPOL PAE/802.1x */
+
+/*
+ * EAPOL key message.
+ */
+struct eapol_key {
+	u_int8_t	ek_type;	/* key type */
+#define	EAPOL_KEY_TYPE_RC4	0x01	/* for WEP */
+#define	EAPOL_KEY_TYPE_RSN	0x02	/* for 802.11i */
+#define	EAPOL_KEY_TYPE_WPA	0xfe	/* different struct, see below */
+	u_int16_t	ek_length;	/* frame length */
+	u_int64_t	ek_replay;	/* replay counter */
+	u_int8_t	ek_iv[16];	/* initialization vector */
+	u_int8_t	ek_index;	/* key index */
+#define	EAPOL_KEY_BCAST		0x00	/* broadcast */
+#define	EAPOL_KEY_UCAST		0x80	/* unicast */
+	u_int8_t	ek_sig[16];	/* signature */
+	/* variable length data follows */
+} __attribute__((__packed__));
+
+/*
+ * EAPOL WPA/RSN key message.
+ */
+struct eapol_wpa_key {
+	u_int8_t	ewk_type;	/* EAPOL_KEY_TYPE_WPA */
+	u_int16_t	ewk_info;	/* key info */
+#define	EAPOL_WKEY_INFO_TYPE	0x0007
+#define	EAPOL_WKEY_INFO_MD5	1	/* hmac-md5 & rc4 */
+#define	EAPOL_WKEY_INFO_AES	2	/* hmac-sha1 & aes-keywrap */
+#define	EAPOL_WKEY_INFO_PW	0x0008
+#define	EAPOL_WKEY_INFO_GROUP	0x0000
+#define	EAPOL_WKEY_INFO_INDEX	0x0030	/* key index (WPA only) */
+#define	EAPOL_WKEY_INFO_INSTALL	0x0040	/* install or tx/rx */
+#define	EAPOL_WKEY_INFO_ACK	0x0080	/* STA/AP handshake bit */
+#define	EAPOL_WKEY_INFO_MIC	0x0100	/* msg is MIC'd */
+#define	EAPOL_WKEY_INFO_SECURE	0x0200	/* pw keys in use */
+#define	EAPOL_WKEY_INFO_ERROR	0x0400	/* STA found invalid MIC */
+#define	EAPOL_WKEY_INFO_REQUEST	0x0800	/* STA requests new key */
+#define	EAPOL_WKEY_INFO_ENCRYPT	0x1000	/* encrypted key data (WPA2 only) */
+	u_int16_t	ewk_keylen;	/* key length */
+	u_int64_t	ewk_replay;	/* replay counter */
+	u_int8_t	ewk_nonce[32];
+	u_int8_t	ewk_iv[16];	/* initialization vector */
+	u_int64_t	ewk_key_rsc;
+	u_int64_t	ewk_key_id;
+	u_int8_t	ewk_mic[16];
+	u_int16_t	ewk_datalen;
+	/* variable length data follows */
+} __attribute__((__packed__));
+
+/* 
+ * EAP packet format as defined in RFC2284.
+ */
+struct eap_hdr {
+	u_int8_t	eap_code;	/* EAP packet kind */
+	u_int8_t	eap_id;		/* Request/response identifier */
+	u_int16_t	eap_len;	/* Packet length, including header */
+	u_int8_t	eap_type;	/* Request/response protocol type */
+	/* variable length data follows */
+} __attribute__((__packed__));
+
+enum {
+	EAP_CODE_REQUEST	= 0x01,
+	EAP_CODE_RESPONSE	= 0x02,
+	EAP_CODE_SUCCESS	= 0x03,
+	EAP_CODE_FAILURE	= 0x04,
+};
+
+enum {
+	EAP_TYPE_IDENTITY	= 0x01,
+	EAP_TYPE_NOTIFICATION	= 0x02,
+	EAP_TYPE_NAK		= 0x03,
+	EAP_TYPE_MD5_CHALLENGE	= 0x04,
+	EAP_TYPE_EAPTLS		= 0x0d,
+	EAP_TYPE_LEAP		= 0x11,
+};
+
+/*
+ * The short form for an EAP packet, used in
+ * SUCCESS/FAILURE messages
+ */
+struct eap_hdr_short {
+	u_int8_t	eap_code;	/* EAP packet kind */
+	u_int8_t	eap_id;		/* Request/response identifier */
+	u_int16_t	eap_len;	/* Packet length, including header */
+} __attribute__((__packed__));
+
+#define EAP_IDENTITY_MAXLEN	72	/* max NAI length per RFC2486 */
+#endif /* _NET80211_EAPOL_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.c	2005-02-24 13:06:17.297132456 -0800
@@ -0,0 +1,1005 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.9 2004/01/15 08:44:27 onoe Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ieee80211.c,v 1.7 2003/10/16 22:25:00 matt Exp $");
+
+/*
+ * IEEE 802.11 generic handler
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_dot1x.h>
+
+static void ieee80211_watchdog(unsigned long);
+static void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+		enum ieee80211_phymode);
+
+static const char *ieee80211_phymode_name[] = {
+	"auto",		/* IEEE80211_MODE_AUTO */
+	"11a",		/* IEEE80211_MODE_11A */
+	"11b",		/* IEEE80211_MODE_11B */
+	"11g",		/* IEEE80211_MODE_11G */
+	"FH",		/* IEEE80211_MODE_FH */
+	"turbo",	/* IEEE80211_MODE_TURBO */
+};
+
+/* list of all instances */
+SLIST_HEAD(ieee80211_list, ieee80211com);
+static struct ieee80211_list ieee80211_list =
+	SLIST_HEAD_INITIALIZER(ieee80211_list);
+
+int
+ieee80211_ifattach(struct ieee80211com *ic)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ieee80211_channel *c;
+	int i;
+
+	_MOD_INC_USE(THIS_MODULE, return ENODEV);
+
+	ieee80211_crypto_attach(ic);
+
+	/*
+	 * Fill in 802.11 available channel set, mark
+	 * all available channels as active, and pick
+	 * a default channel if not already specified.
+	 */
+	memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+	ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO;
+	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+		c = &ic->ic_channels[i];
+		if (c->ic_flags) {
+			/*
+			 * Verify driver passed us valid data.
+			 */
+			if (i != ieee80211_chan2ieee(ic, c)) {
+				if_printf(dev, "bad channel ignored; "
+					"freq %u flags %x number %u\n",
+					c->ic_freq, c->ic_flags, i);
+				c->ic_flags = 0;	/* NB: remove */
+				continue;
+			}
+			setbit(ic->ic_chan_avail, i);
+			/*
+			 * Identify mode capabilities.
+			 */
+			if (IEEE80211_IS_CHAN_A(c))
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_11A;
+			if (IEEE80211_IS_CHAN_B(c))
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_11B;
+			if (IEEE80211_IS_CHAN_PUREG(c))
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_11G;
+			if (IEEE80211_IS_CHAN_FHSS(c))
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_FH;
+			if (IEEE80211_IS_CHAN_T(c))
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO;
+		}
+	}
+	/* validate ic->ic_curmode */
+	if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0)
+		ic->ic_curmode = IEEE80211_MODE_AUTO;
+	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
+
+	(void) ieee80211_setmode(ic, ic->ic_curmode);
+
+	if (ic->ic_lintval == 0)
+		ic->ic_lintval = 100;		/* default sleep */
+	ic->ic_bmisstimeout = 7*ic->ic_lintval;	/* default 7 beacons */
+
+	ieee80211_node_attach(ic);
+	ieee80211_proto_attach(ic);
+
+	/* XXX lock */
+	SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next);
+
+	init_timer(&ic->ic_slowtimo);
+	ic->ic_slowtimo.data = (unsigned long) ic;
+	ic->ic_slowtimo.function = ieee80211_watchdog;
+	ieee80211_watchdog((unsigned long) ic);		/* prime timer */
+	/*
+	 * NB: ieee80211_sysctl_register is called by the driver
+	 *     since dev->name isn't setup at this point; yech!
+	 */
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ifattach);
+
+void
+ieee80211_ifdetach(struct ieee80211com *ic)
+{
+
+	/* XXX lock */
+	SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next);
+
+	del_timer(&ic->ic_slowtimo);
+	ieee80211_proto_detach(ic);
+	ieee80211_crypto_detach(ic);
+	ieee80211_node_detach(ic);
+	ifmedia_removeall(&ic->ic_media);
+#ifdef CONFIG_SYSCTL
+	ieee80211_sysctl_unregister(ic);
+#endif
+
+	_MOD_DEC_USE(THIS_MODULE);
+}
+EXPORT_SYMBOL(ieee80211_ifdetach);
+
+/*
+ * Convert MHz frequency to IEEE channel number.
+ */
+u_int
+ieee80211_mhz2ieee(u_int freq, u_int flags)
+{
+	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
+		if (freq == 2484)
+			return 14;
+		if (freq < 2484)
+			return (freq - 2407) / 5;
+		else
+			return 15 + ((freq - 2512) / 20);
+	} else if (flags & IEEE80211_CHAN_5GHZ) {	/* 5Ghz band */
+		return (freq - 5000) / 5;
+	} else {				/* either, guess */
+		if (freq == 2484)
+			return 14;
+		if (freq < 2484)
+			return (freq - 2407) / 5;
+		if (freq < 5000)
+			return 15 + ((freq - 2512) / 20);
+		return (freq - 5000) / 5;
+	}
+}
+EXPORT_SYMBOL(ieee80211_mhz2ieee);
+
+/*
+ * Convert channel to IEEE channel number.
+ */
+u_int
+ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+	if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX])
+		return c - ic->ic_channels;
+	else if (c == IEEE80211_CHAN_ANYC)
+		return IEEE80211_CHAN_ANY;
+	else if (c != NULL) {
+		if_printf(ic->ic_dev, "invalid channel freq %u flags %x\n",
+			c->ic_freq, c->ic_flags);
+		return 0;		/* XXX */
+	} else {
+		if_printf(ic->ic_dev, "invalid channel (NULL)\n");
+		return 0;		/* XXX */
+	}
+}
+EXPORT_SYMBOL(ieee80211_chan2ieee);
+
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+u_int
+ieee80211_ieee2mhz(u_int chan, u_int flags)
+{
+	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
+		if (chan == 14)
+			return 2484;
+		if (chan < 14)
+			return 2407 + chan*5;
+		else
+			return 2512 + ((chan-15)*20);
+	} else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */
+		return 5000 + (chan*5);
+	} else {				/* either, guess */
+		if (chan == 14)
+			return 2484;
+		if (chan < 14)			/* 0-13 */
+			return 2407 + chan*5;
+		if (chan < 27)			/* 15-26 */
+			return 2512 + ((chan-15)*20);
+		return 5000 + (chan*5);
+	}
+}
+EXPORT_SYMBOL(ieee80211_ieee2mhz);
+
+/*
+ * Setup the media data structures according to the channel and
+ * rate tables.  This must be called by the driver after
+ * ieee80211_attach and before most anything else.
+ */
+void
+ieee80211_media_init(struct ieee80211com *ic,
+	ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
+{
+#define	ADD(_ic, _s, _o) \
+	ifmedia_add(&(_ic)->ic_media, \
+		IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
+	struct net_device *dev = ic->ic_dev;
+	struct ifmediareq imr;
+	int i, j, mode, rate, maxrate, mword, mopt, r;
+	struct ieee80211_rateset *rs;
+	struct ieee80211_rateset allrates;
+
+	/*
+	 * Do late attach work that must wait for any subclass
+	 * (i.e. driver) work such as overriding methods.
+	 */
+	ieee80211_node_lateattach(ic);
+
+	/*
+	 * Fill in media characteristics.
+	 */
+	ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
+	maxrate = 0;
+	memset(&allrates, 0, sizeof(allrates));
+	for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
+		static const u_int mopts[] = { 
+			IFM_AUTO,
+			IFM_IEEE80211_11A,
+			IFM_IEEE80211_11B,
+			IFM_IEEE80211_11G,
+			IFM_IEEE80211_FH,
+			IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+		};
+		if ((ic->ic_modecaps & (1<<mode)) == 0)
+			continue;
+		mopt = mopts[mode];
+		ADD(ic, IFM_AUTO, mopt);	/* e.g. 11a auto */
+		if (ic->ic_caps & IEEE80211_C_IBSS)
+			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC);
+		if (ic->ic_caps & IEEE80211_C_HOSTAP)
+			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP);
+		if (ic->ic_caps & IEEE80211_C_AHDEMO)
+			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
+		if (ic->ic_caps & IEEE80211_C_MONITOR)
+			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
+		if (mode == IEEE80211_MODE_AUTO)
+			continue;
+		rs = &ic->ic_sup_rates[mode];
+		for (i = 0; i < rs->rs_nrates; i++) {
+			rate = rs->rs_rates[i];
+			mword = ieee80211_rate2media(ic, rate, mode);
+			if (mword == 0)
+				continue;
+			ADD(ic, mword, mopt);
+			if (ic->ic_caps & IEEE80211_C_IBSS)
+				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
+			if (ic->ic_caps & IEEE80211_C_HOSTAP)
+				ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
+			if (ic->ic_caps & IEEE80211_C_AHDEMO)
+				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
+			if (ic->ic_caps & IEEE80211_C_MONITOR)
+				ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+			/*
+			 * Add rate to the collection of all rates.
+			 */
+			r = rate & IEEE80211_RATE_VAL;
+			for (j = 0; j < allrates.rs_nrates; j++)
+				if (allrates.rs_rates[j] == r)
+					break;
+			if (j == allrates.rs_nrates) {
+				/* unique, add to the set */
+				allrates.rs_rates[j] = r;
+				allrates.rs_nrates++;
+			}
+			rate = (rate & IEEE80211_RATE_VAL) / 2;
+			if (rate > maxrate)
+				maxrate = rate;
+		}
+	}
+	for (i = 0; i < allrates.rs_nrates; i++) {
+		mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
+				IEEE80211_MODE_AUTO);
+		if (mword == 0)
+			continue;
+		mword = IFM_SUBTYPE(mword);	/* remove media options */
+		ADD(ic, mword, 0);
+		if (ic->ic_caps & IEEE80211_C_IBSS)
+			ADD(ic, mword, IFM_IEEE80211_ADHOC);
+		if (ic->ic_caps & IEEE80211_C_HOSTAP)
+			ADD(ic, mword, IFM_IEEE80211_HOSTAP);
+		if (ic->ic_caps & IEEE80211_C_AHDEMO)
+			ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
+		if (ic->ic_caps & IEEE80211_C_MONITOR)
+			ADD(ic, mword, IFM_IEEE80211_MONITOR);
+	}
+	ieee80211_media_status(dev, &imr);
+	ifmedia_set(&ic->ic_media, imr.ifm_active);
+#ifndef __linux__
+	if (maxrate)
+		ifp->if_baudrate = IF_Mbps(maxrate);
+#endif
+#undef ADD
+}
+EXPORT_SYMBOL(ieee80211_media_init);
+
+void
+ieee80211_announce(struct ieee80211com *ic)
+{
+	struct net_device *dev = ic->ic_dev;
+	int i, mode, rate, mword;
+	struct ieee80211_rateset *rs;
+
+	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
+		if ((ic->ic_modecaps & (1<<mode)) == 0)
+			continue;
+		if_printf(dev, "%s rates: ", ieee80211_phymode_name[mode]);
+		rs = &ic->ic_sup_rates[mode];
+		for (i = 0; i < rs->rs_nrates; i++) {
+			rate = rs->rs_rates[i];
+			mword = ieee80211_rate2media(ic, rate, mode);
+			if (mword == 0)
+				continue;
+			printf("%s%d%sMbps", (i != 0 ? " " : ""),
+			    (rate & IEEE80211_RATE_VAL) / 2,
+			    ((rate & 0x1) != 0 ? ".5" : ""));
+		}
+		printf("\n");
+	}
+}
+EXPORT_SYMBOL(ieee80211_announce);
+
+static int
+findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+{
+#define	IEEERATE(_ic,_m,_i) \
+	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
+	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
+	for (i = 0; i < nrates; i++)
+		if (IEEERATE(ic, mode, i) == rate)
+			return i;
+	return -1;
+#undef IEEERATE
+}
+
+/*
+ * Find an instance by it's mac address.
+ */
+struct ieee80211com *
+ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct ieee80211com *ic;
+
+	/* XXX lock */
+	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
+		if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr))
+			return ic;
+	return NULL;
+}
+EXPORT_SYMBOL(ieee80211_find_vap);
+
+static struct ieee80211com *
+ieee80211_find_instance(struct net_device *dev)
+{
+	struct ieee80211com *ic;
+
+	/* XXX lock */
+	/* XXX not right for multiple instances but works for now */
+	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
+		if (ic->ic_dev == dev)
+			return ic;
+	return NULL;
+}
+
+/*
+ * Handle a media change request.
+ */
+int
+ieee80211_media_change(struct net_device *dev)
+{
+	struct ieee80211com *ic;
+	struct ifmedia_entry *ime;
+	enum ieee80211_opmode newopmode;
+	enum ieee80211_phymode newphymode;
+	int i, j, newrate, error = 0;
+
+	ic = ieee80211_find_instance(dev);
+	if (!ic) {
+		if_printf(dev, "%s: no 802.11 instance!\n", __func__);
+		return EINVAL;
+	}
+	ime = ic->ic_media.ifm_cur;
+	/*
+	 * First, identify the phy mode.
+	 */
+	switch (IFM_MODE(ime->ifm_media)) {
+	case IFM_IEEE80211_11A:
+		newphymode = IEEE80211_MODE_11A;
+		break;
+	case IFM_IEEE80211_11B:
+		newphymode = IEEE80211_MODE_11B;
+		break;
+	case IFM_IEEE80211_11G:
+		newphymode = IEEE80211_MODE_11G;
+		break;
+	case IFM_IEEE80211_FH:
+		newphymode = IEEE80211_MODE_FH;
+		break;
+	case IFM_AUTO:
+		newphymode = IEEE80211_MODE_AUTO;
+		break;
+	default:
+		return EINVAL;
+	}
+	/*
+	 * Turbo mode is an ``option''.  Eventually it
+	 * needs to be applied to 11g too.
+	 */
+	if (ime->ifm_media & IFM_IEEE80211_TURBO) {
+		if (newphymode != IEEE80211_MODE_11A)
+			return EINVAL;
+		newphymode = IEEE80211_MODE_TURBO;
+	}
+	/*
+	 * Validate requested mode is available.
+	 */
+	if ((ic->ic_modecaps & (1<<newphymode)) == 0)
+		return EINVAL;
+
+	/*
+	 * Next, the fixed/variable rate.
+	 */
+	i = -1;
+	if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
+		/*
+		 * Convert media subtype to rate.
+		 */
+		newrate = ieee80211_media2rate(ime->ifm_media);
+		if (newrate == 0)
+			return EINVAL;
+		/*
+		 * Check the rate table for the specified/current phy.
+		 */
+		if (newphymode == IEEE80211_MODE_AUTO) {
+			/*
+			 * In autoselect mode search for the rate.
+			 */
+			for (j = IEEE80211_MODE_11A;
+			     j < IEEE80211_MODE_MAX; j++) {
+				if ((ic->ic_modecaps & (1<<j)) == 0)
+					continue;
+				i = findrate(ic, j, newrate);
+				if (i != -1) {
+					/* lock mode too */
+					newphymode = j;
+					break;
+				}
+			}
+		} else {
+			i = findrate(ic, newphymode, newrate);
+		}
+		if (i == -1)			/* mode/rate mismatch */
+			return EINVAL;
+	}
+	/* NB: defer rate setting to later */
+
+	/*
+	 * Deduce new operating mode but don't install it just yet.
+	 */
+	if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
+	    (IFM_IEEE80211_ADHOC|IFM_FLAG0))
+		newopmode = IEEE80211_M_AHDEMO;
+	else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
+		newopmode = IEEE80211_M_HOSTAP;
+	else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
+		newopmode = IEEE80211_M_IBSS;
+	else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
+		newopmode = IEEE80211_M_MONITOR;
+	else
+		newopmode = IEEE80211_M_STA;
+
+	/*
+	 * Autoselect doesn't make sense when operating as an AP.
+	 * If no phy mode has been selected, pick one and lock it
+	 * down so rate tables can be used in forming beacon frames
+	 * and the like.
+	 */
+	if (newopmode == IEEE80211_M_HOSTAP &&
+	    newphymode == IEEE80211_MODE_AUTO) {
+		for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++)
+			if (ic->ic_modecaps & (1<<j)) {
+				newphymode = j;
+				break;
+			}
+	}
+
+	/*
+	 * Handle phy mode change.
+	 */
+	if (ic->ic_curmode != newphymode) {		/* change phy mode */
+		error = ieee80211_setmode(ic, newphymode);
+		if (error != 0)
+			return error;
+		error = ENETRESET;
+	}
+
+	/*
+	 * Committed to changes, install the rate setting.
+	 */
+	if (ic->ic_fixed_rate != i) {
+		ic->ic_fixed_rate = i;			/* set fixed tx rate */
+		error = ENETRESET;
+	}
+
+	/*
+	 * Handle operating mode change.
+	 */
+	if (ic->ic_opmode != newopmode) {
+		ic->ic_opmode = newopmode;
+		switch (newopmode) {
+		case IEEE80211_M_AHDEMO:
+		case IEEE80211_M_HOSTAP:
+		case IEEE80211_M_STA:
+		case IEEE80211_M_MONITOR:
+			ic->ic_flags &= ~IEEE80211_F_IBSSON;
+			break;
+		case IEEE80211_M_IBSS:
+			ic->ic_flags |= IEEE80211_F_IBSSON;
+			break;
+		}
+		/*
+		 * Yech, slot time may change depending on the
+		 * operating mode so reset it to be sure everything
+		 * is setup appropriately.
+		 */
+		ieee80211_reset_erp(ic);
+		error = ENETRESET;
+	}
+#ifdef notdef
+	if (error == 0)
+		ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
+#endif
+	return error;
+}
+EXPORT_SYMBOL(ieee80211_media_change);
+
+void
+ieee80211_media_status(struct net_device *dev, struct ifmediareq *imr)
+{
+	struct ieee80211com *ic;
+	struct ieee80211_rateset *rs;
+
+	ic = ieee80211_find_instance(dev);
+	if (!ic) {
+		if_printf(dev, "%s: no 802.11 instance!\n", __func__);
+		return;
+	}
+	imr->ifm_status = IFM_AVALID;
+	imr->ifm_active = IFM_IEEE80211;
+	if (ic->ic_state == IEEE80211_S_RUN)
+		imr->ifm_status |= IFM_ACTIVE;
+	/*
+	 * Calculate a current rate if possible.
+	 */
+	if (ic->ic_fixed_rate != -1) {
+		/*
+		 * A fixed rate is set, report that.
+		 */
+		rs = &ic->ic_sup_rates[ic->ic_curmode];
+		imr->ifm_active |= ieee80211_rate2media(ic,
+			rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode);
+	} else if (ic->ic_opmode == IEEE80211_M_STA) {
+		/*
+		 * In station mode report the current transmit rate.
+		 */
+		rs = &ic->ic_bss->ni_rates;
+		imr->ifm_active |= ieee80211_rate2media(ic,
+			rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode);
+	} else
+		imr->ifm_active |= IFM_AUTO;
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_STA:
+		break;
+	case IEEE80211_M_IBSS:
+		imr->ifm_active |= IFM_IEEE80211_ADHOC;
+		break;
+	case IEEE80211_M_AHDEMO:
+		/* should not come here */
+		break;
+	case IEEE80211_M_HOSTAP:
+		imr->ifm_active |= IFM_IEEE80211_HOSTAP;
+		break;
+	case IEEE80211_M_MONITOR:
+		imr->ifm_active |= IFM_IEEE80211_MONITOR;
+		break;
+	}
+	switch (ic->ic_curmode) {
+	case IEEE80211_MODE_11A:
+		imr->ifm_active |= IFM_IEEE80211_11A;
+		break;
+	case IEEE80211_MODE_11B:
+		imr->ifm_active |= IFM_IEEE80211_11B;
+		break;
+	case IEEE80211_MODE_11G:
+		imr->ifm_active |= IFM_IEEE80211_11G;
+		break;
+	case IEEE80211_MODE_FH:
+		imr->ifm_active |= IFM_IEEE80211_FH;
+		break;
+	case IEEE80211_MODE_TURBO:
+		imr->ifm_active |= IFM_IEEE80211_11A
+				|  IFM_IEEE80211_TURBO;
+		break;
+	}
+}
+EXPORT_SYMBOL(ieee80211_media_status);
+
+static void
+ieee80211_watchdog(unsigned long data)
+{
+	struct ieee80211com *ic = (struct ieee80211com *) data;
+
+	if (ic->ic_state != IEEE80211_S_INIT) {
+		if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+		if (ic->ic_inact_timer && --ic->ic_inact_timer == 0)
+			ieee80211_timeout_nodes(ic);
+	}
+	ic->ic_slowtimo.expires = jiffies + HZ;		/* once a second */
+	add_timer(&ic->ic_slowtimo);
+}
+
+/*
+ * Mark the basic rates for the 11g rate table based on the
+ * operating mode.  For real 11g we mark all the 11b rates
+ * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
+ * 11b rates.  There's also a pseudo 11a-mode used to mark only
+ * the basic OFDM rates.
+ */
+static void
+ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+{
+	static const struct ieee80211_rateset basic[] = {
+	    { 0 },			/* IEEE80211_MODE_AUTO */
+	    { 3, { 12, 24, 48 } },	/* IEEE80211_MODE_11A */
+	    { 2, { 2, 4 } },		/* IEEE80211_MODE_11B */
+	    { 4, { 2, 4, 11, 22 } },	/* IEEE80211_MODE_11G (mixed b/g) */
+	    { 0 },			/* IEEE80211_MODE_FH */
+					/* IEEE80211_MODE_PUREG (not yet) */
+	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+	};
+	int i, j;
+
+	for (i = 0; i < rs->rs_nrates; i++) {
+		rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+		for (j = 0; j < basic[mode].rs_nrates; j++)
+			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
+				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
+				break;
+			}
+	}
+}
+
+/*
+ * Set the current phy mode and recalculate the active channel
+ * set based on the available channels for this mode.  Also
+ * select a new default/current channel if the current one is
+ * inappropriate for this mode.
+ */
+int
+ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	static const u_int chanflags[] = {
+		0,			/* IEEE80211_MODE_AUTO */
+		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
+		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
+		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
+		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
+		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO	*/
+	};
+	struct ieee80211_channel *c;
+	u_int modeflags;
+	int i;
+
+	/* validate new mode */
+	if ((ic->ic_modecaps & (1<<mode)) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: mode %u not supported (caps 0x%x)\n",
+			__func__, mode, ic->ic_modecaps));
+		return EINVAL;
+	}
+
+	/*
+	 * Verify at least one channel is present in the available
+	 * channel list before committing to the new mode.
+	 */
+	KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
+	modeflags = chanflags[mode];
+	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+		c = &ic->ic_channels[i];
+		if (mode == IEEE80211_MODE_AUTO) {
+			/* ignore turbo channels for autoselect */
+			if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
+				break;
+		} else {
+			if ((c->ic_flags & modeflags) == modeflags)
+				break;
+		}
+	}
+	if (i > IEEE80211_CHAN_MAX) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: no channels found for mode %u\n", __func__, mode));
+		return EINVAL;
+	}
+
+	/*
+	 * Calculate the active channel set.
+	 */
+	memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active));
+	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+		c = &ic->ic_channels[i];
+		if (mode == IEEE80211_MODE_AUTO) {
+			/* take anything but pure turbo channels */
+			if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
+				setbit(ic->ic_chan_active, i);
+		} else {
+			if ((c->ic_flags & modeflags) == modeflags)
+				setbit(ic->ic_chan_active, i);
+		}
+	}
+	/*
+	 * If no current/default channel is setup or the current
+	 * channel is wrong for the mode then pick the first
+	 * available channel from the active list.  This is likely
+	 * not the right one.
+	 */
+	if (ic->ic_ibss_chan == NULL ||
+	    isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
+		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+			if (isset(ic->ic_chan_active, i)) {
+				ic->ic_ibss_chan = &ic->ic_channels[i];
+				break;
+			}
+		KASSERT(ic->ic_ibss_chan != NULL &&
+		    isset(ic->ic_chan_active,
+			ieee80211_chan2ieee(ic, ic->ic_ibss_chan)),
+		    ("Bad IBSS channel %u",
+		     ieee80211_chan2ieee(ic, ic->ic_ibss_chan)));
+	}
+	/*
+	 * If the desired channel is set but no longer valid then reset it.
+	 */
+	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+	    isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan)))
+		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+
+	/*
+	 * Do mode-specific rate setup.
+	 */
+	if (mode == IEEE80211_MODE_11G) {
+		/*
+		 * Use a mixed 11b/11g rate set.
+		 */
+		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
+			IEEE80211_MODE_11G);
+	} else if (mode == IEEE80211_MODE_11B) {
+		/*
+		 * Force pure 11b rate set.
+		 */
+		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
+			IEEE80211_MODE_11B);
+	}
+	/*
+	 * Setup an initial rate set according to the
+	 * current/default channel selected above.  This
+	 * will be changed when scanning but must exist
+	 * now so driver have a consistent state of ic_ibss_chan.
+	 */
+	if (ic->ic_bss)		/* NB: can be called before lateattach */
+		ic->ic_bss->ni_rates = ic->ic_sup_rates[mode];
+
+	ic->ic_curmode = mode;
+	ieee80211_reset_erp(ic);	/* reset ERP state */
+
+	return 0;
+#undef N
+}
+EXPORT_SYMBOL(ieee80211_setmode);
+
+/*
+ * Reset 11g-related state.
+ */
+void
+ieee80211_reset_erp(struct ieee80211com *ic)
+{
+	ic->ic_flags &= ~IEEE80211_F_USEPROT;
+	ic->ic_nonerpsta = 0;
+	ic->ic_longslotsta = 0;
+	/*
+	 * Short slot time is enabled only when operating in 11g
+	 * and not in an IBSS.  We must also honor whether or not
+	 * the driver is capable of doing it.
+	 */
+	ieee80211_set_shortslottime(ic,
+		ic->ic_curmode == IEEE80211_MODE_11A ||
+		(ic->ic_curmode == IEEE80211_MODE_11G &&
+		ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		(ic->ic_caps & IEEE80211_C_SHSLOT)));
+	/*
+	 * Set short preamble and ERP barker-preamble flags.
+	 */
+	if (ic->ic_curmode == IEEE80211_MODE_11A ||
+	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
+		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+	} else {
+		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+		ic->ic_flags |= IEEE80211_F_USEBARKER;
+	}
+}
+
+/*
+ * Return the phy mode for with the specified channel so the
+ * caller can select a rate set.  This is problematic for channels
+ * where multiple operating modes are possible (e.g. 11g+11b).
+ * In those cases we defer to the current operating mode when set.
+ */
+enum ieee80211_phymode
+ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+	if (IEEE80211_IS_CHAN_5GHZ(chan)) {
+		/*
+		 * This assumes all 11a turbo channels are also
+		 * usable withut turbo, which is currently true.
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_TURBO)
+			return IEEE80211_MODE_TURBO;
+		return IEEE80211_MODE_11A;
+	} else if (IEEE80211_IS_CHAN_FHSS(chan))
+		return IEEE80211_MODE_FH;
+	else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) {
+		/*
+		 * This assumes all 11g channels are also usable
+		 * for 11b, which is currently true.
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_11B)
+			return IEEE80211_MODE_11B;
+		return IEEE80211_MODE_11G;
+	} else
+		return IEEE80211_MODE_11B;
+}
+EXPORT_SYMBOL(ieee80211_chan2mode);
+
+/*
+ * convert IEEE80211 rate value to ifmedia subtype.
+ * ieee80211 rate is in unit of 0.5Mbps.
+ */
+int
+ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	static const struct {
+		u_int	m;	/* rate + mode */
+		u_int	r;	/* if_media rate */
+	} rates[] = {
+		{   2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
+		{   4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
+		{   2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 },
+		{   4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 },
+		{  11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 },
+		{  22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 },
+		{  44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 },
+		{  12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 },
+		{  18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 },
+		{  24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 },
+		{  36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 },
+		{  48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 },
+		{  72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 },
+		{  96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 },
+		{ 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 },
+		{   2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 },
+		{   4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 },
+		{  11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 },
+		{  22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 },
+		{  12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 },
+		{  18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 },
+		{  24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 },
+		{  36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 },
+		{  48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 },
+		{  72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 },
+		{  96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 },
+		{ 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 },
+		/* NB: OFDM72 doesn't realy exist so we don't handle it */
+	};
+	u_int mask, i;
+
+	mask = rate & IEEE80211_RATE_VAL;
+	switch (mode) {
+	case IEEE80211_MODE_11A:
+	case IEEE80211_MODE_TURBO:
+		mask |= IFM_IEEE80211_11A;
+		break;
+	case IEEE80211_MODE_11B:
+		mask |= IFM_IEEE80211_11B;
+		break;
+	case IEEE80211_MODE_FH:
+		mask |= IFM_IEEE80211_FH;
+		break;
+	case IEEE80211_MODE_AUTO:
+		/* NB: ic may be NULL for some drivers */
+		if (ic && ic->ic_phytype == IEEE80211_T_FH) {
+			mask |= IFM_IEEE80211_FH;
+			break;
+		}
+		/* NB: hack, 11g matches both 11b+11a rates */
+		/* fall thru... */
+	case IEEE80211_MODE_11G:
+		mask |= IFM_IEEE80211_11G;
+		break;
+	}
+	for (i = 0; i < N(rates); i++)
+		if (rates[i].m == mask)
+			return rates[i].r;
+	return IFM_AUTO;
+#undef N
+}
+EXPORT_SYMBOL(ieee80211_rate2media);
+
+int
+ieee80211_media2rate(int mword)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	static const int ieeerates[] = {
+		-1,		/* IFM_AUTO */
+		0,		/* IFM_MANUAL */
+		0,		/* IFM_NONE */
+		2,		/* IFM_IEEE80211_FH1 */
+		4,		/* IFM_IEEE80211_FH2 */
+		2,		/* IFM_IEEE80211_DS1 */
+		4,		/* IFM_IEEE80211_DS2 */
+		11,		/* IFM_IEEE80211_DS5 */
+		22,		/* IFM_IEEE80211_DS11 */
+		44,		/* IFM_IEEE80211_DS22 */
+		12,		/* IFM_IEEE80211_OFDM6 */
+		18,		/* IFM_IEEE80211_OFDM9 */
+		24,		/* IFM_IEEE80211_OFDM12 */
+		36,		/* IFM_IEEE80211_OFDM18 */
+		48,		/* IFM_IEEE80211_OFDM24 */
+		72,		/* IFM_IEEE80211_OFDM36 */
+		96,		/* IFM_IEEE80211_OFDM48 */
+		108,		/* IFM_IEEE80211_OFDM54 */
+		144,		/* IFM_IEEE80211_OFDM72 */
+	};
+	return IFM_SUBTYPE(mword) < N(ieeerates) ?
+		ieeerates[IFM_SUBTYPE(mword)] : 0;
+#undef N
+}
+EXPORT_SYMBOL(ieee80211_media2rate);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.h	2005-02-24 13:06:17.298132304 -0800
@@ -0,0 +1,622 @@
+/*	$NetBSD: ieee80211.h,v 1.4 2003/10/15 11:43:51 dyoung Exp $	*/
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.4 2004/01/13 06:22:55 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_H_
+#define _NET80211_IEEE80211_H_
+
+/*
+ * 802.11 protocol definitions.
+ */
+
+#define	IEEE80211_ADDR_LEN	6		/* size of 802.11 address */
+/* is 802.11 address multicast/broadcast? */
+#define	IEEE80211_IS_MULTICAST(_a)	(*(_a) & 0x01)
+
+/* IEEE 802.11 PLCP header */
+struct ieee80211_plcp_hdr {
+	u_int16_t	i_sfd;
+	u_int8_t	i_signal;
+	u_int8_t	i_service;
+	u_int16_t	i_length;
+	u_int16_t	i_crc;
+} __packed;
+
+#define IEEE80211_PLCP_SFD      0xF3A0 
+#define IEEE80211_PLCP_SERVICE  0x00
+
+/*
+ * generic definitions for IEEE 802.11 frames
+ */
+struct ieee80211_frame {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+	/* see below */
+} __packed;
+
+struct ieee80211_qosframe {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_qos[2];
+	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+	/* see below */
+} __packed;
+
+struct ieee80211_qoscntl {
+	u_int8_t	i_qos[2];
+};
+
+struct ieee80211_frame_addr4 {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_addr4[IEEE80211_ADDR_LEN];
+} __packed;
+
+
+struct ieee80211_qosframe_addr4 {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_addr4[IEEE80211_ADDR_LEN];
+	u_int8_t	i_qos[2];
+} __packed;
+
+#define	IEEE80211_FC0_VERSION_MASK		0x03
+#define	IEEE80211_FC0_VERSION_SHIFT		0
+#define	IEEE80211_FC0_VERSION_0			0x00
+#define	IEEE80211_FC0_TYPE_MASK			0x0c
+#define	IEEE80211_FC0_TYPE_SHIFT		2
+#define	IEEE80211_FC0_TYPE_MGT			0x00
+#define	IEEE80211_FC0_TYPE_CTL			0x04
+#define	IEEE80211_FC0_TYPE_DATA			0x08
+
+#define	IEEE80211_FC0_SUBTYPE_MASK		0xf0
+#define	IEEE80211_FC0_SUBTYPE_SHIFT		4
+/* for TYPE_MGT */
+#define	IEEE80211_FC0_SUBTYPE_ASSOC_REQ		0x00
+#define	IEEE80211_FC0_SUBTYPE_ASSOC_RESP	0x10
+#define	IEEE80211_FC0_SUBTYPE_REASSOC_REQ	0x20
+#define	IEEE80211_FC0_SUBTYPE_REASSOC_RESP	0x30
+#define	IEEE80211_FC0_SUBTYPE_PROBE_REQ		0x40
+#define	IEEE80211_FC0_SUBTYPE_PROBE_RESP	0x50
+#define	IEEE80211_FC0_SUBTYPE_BEACON		0x80
+#define	IEEE80211_FC0_SUBTYPE_ATIM		0x90
+#define	IEEE80211_FC0_SUBTYPE_DISASSOC		0xa0
+#define	IEEE80211_FC0_SUBTYPE_AUTH		0xb0
+#define	IEEE80211_FC0_SUBTYPE_DEAUTH		0xc0
+/* for TYPE_CTL */
+#define	IEEE80211_FC0_SUBTYPE_PS_POLL		0xa0
+#define	IEEE80211_FC0_SUBTYPE_RTS		0xb0
+#define	IEEE80211_FC0_SUBTYPE_CTS		0xc0
+#define	IEEE80211_FC0_SUBTYPE_ACK		0xd0
+#define	IEEE80211_FC0_SUBTYPE_CF_END		0xe0
+#define	IEEE80211_FC0_SUBTYPE_CF_END_ACK	0xf0
+/* for TYPE_DATA (bit combination) */
+#define	IEEE80211_FC0_SUBTYPE_DATA		0x00
+#define	IEEE80211_FC0_SUBTYPE_CF_ACK		0x10
+#define	IEEE80211_FC0_SUBTYPE_CF_POLL		0x20
+#define	IEEE80211_FC0_SUBTYPE_CF_ACPL		0x30
+#define	IEEE80211_FC0_SUBTYPE_NODATA		0x40
+#define	IEEE80211_FC0_SUBTYPE_CFACK		0x50
+#define	IEEE80211_FC0_SUBTYPE_CFPOLL		0x60
+#define	IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK	0x70
+#define	IEEE80211_FC0_SUBTYPE_QOS		0x80
+#define	IEEE80211_FC0_SUBTYPE_QOS_NULL		0xc0
+
+#define	IEEE80211_FC1_DIR_MASK			0x03
+#define	IEEE80211_FC1_DIR_NODS			0x00	/* STA->STA */
+#define	IEEE80211_FC1_DIR_TODS			0x01	/* STA->AP  */
+#define	IEEE80211_FC1_DIR_FROMDS		0x02	/* AP ->STA */
+#define	IEEE80211_FC1_DIR_DSTODS		0x03	/* AP ->AP  */
+
+#define	IEEE80211_FC1_MORE_FRAG			0x04
+#define	IEEE80211_FC1_RETRY			0x08
+#define	IEEE80211_FC1_PWR_MGT			0x10
+#define	IEEE80211_FC1_MORE_DATA			0x20
+#define	IEEE80211_FC1_WEP			0x40
+#define	IEEE80211_FC1_ORDER			0x80
+
+#define	IEEE80211_SEQ_FRAG_MASK			0x000f
+#define	IEEE80211_SEQ_FRAG_SHIFT		0
+#define	IEEE80211_SEQ_SEQ_MASK			0xfff0
+#define	IEEE80211_SEQ_SEQ_SHIFT			4
+
+#define	IEEE80211_NWID_LEN			32
+
+#define	IEEE80211_QOS_TXOP			0x00ff
+/* bit 8 is reserved */
+#define	IEEE80211_QOS_ACKPOLICY			0x0600
+#define	IEEE80211_QOS_ESOP			0x0800
+#define	IEEE80211_QOS_TID			0xf000
+
+/* does frame have QoS sequence control data */
+#define	IEEE80211_QOS_HAS_SEQ(wh) \
+	(((wh)->i_fc[0] & \
+	  (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \
+	  (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+
+/*
+ * WME/802.11e information element.
+ */
+struct ieee80211_ie_wme {
+	u_int8_t	wme_id;		/* IEEE80211_ELEMID_VENDOR */
+	u_int8_t	wme_len;	/* length in bytes */
+	u_int8_t	wme_oui[3];	/* 0x00, 0x50, 0xf2 */
+	u_int8_t	wme_type;	/* OUI type */
+	u_int8_t	wme_subtype;	/* OUI subtype */
+	u_int8_t	wme_version;	/* spec revision */
+	u_int8_t	wme_info;	/* AC info */
+} __packed;
+
+/*
+ * WME/802.11e Tspec Element
+ */
+struct ieee80211_wme_tspec {
+	u_int8_t	ts_id;
+	u_int8_t	ts_len;
+	u_int8_t	ts_oui[3];
+	u_int8_t	ts_oui_type;
+	u_int8_t	ts_oui_subtype;
+	u_int8_t	ts_version;
+	u_int8_t	ts_tsinfo[3];
+	u_int8_t	ts_nom_msdu[2];
+	u_int8_t	ts_max_msdu[2];
+	u_int8_t	ts_min_svc[4];
+	u_int8_t	ts_max_svc[4];
+	u_int8_t	ts_inactv_intv[4];
+	u_int8_t	ts_susp_intv[4];
+	u_int8_t	ts_start_svc[4];
+	u_int8_t	ts_min_rate[4];
+	u_int8_t	ts_mean_rate[4];
+	u_int8_t	ts_max_burst[4];
+	u_int8_t	ts_min_phy[4];
+	u_int8_t	ts_peak_rate[4];
+	u_int8_t	ts_delay[4];
+	u_int8_t	ts_surplus[2];
+	u_int8_t	ts_medium_time[2];
+} __packed;
+
+/*
+ * Management Notification Frame
+ */
+struct ieee80211_mnf {
+	u_int8_t	mnf_category;
+	u_int8_t	mnf_action;
+	u_int8_t	mnf_dialog;
+	u_int8_t	mnf_status;
+} __packed;
+#define	MNF_SETUP_REQ	0
+#define	MNF_SETUP_RESP	1
+#define	MNF_TEARDOWN	2
+
+/*
+ * Control frames.
+ */
+struct ieee80211_frame_min {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	/* FCS */
+} __packed;
+
+struct ieee80211_frame_rts {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_ta[IEEE80211_ADDR_LEN];
+	/* FCS */
+} __packed;
+
+struct ieee80211_frame_cts {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	/* FCS */
+} __packed;
+
+struct ieee80211_frame_ack {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	/* FCS */
+} __packed;
+
+struct ieee80211_frame_pspoll {
+	u_int8_t	i_fc[2];
+	u_int8_t	i_aid[2];
+	u_int8_t	i_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	i_ta[IEEE80211_ADDR_LEN];
+	/* FCS */
+} __packed;
+
+struct ieee80211_frame_cfend {		/* NB: also CF-End+CF-Ack */
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];	/* should be zero */
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_bssid[IEEE80211_ADDR_LEN];
+	/* FCS */
+} __packed;
+
+/*
+ * BEACON management packets
+ *
+ *	octet timestamp[8]
+ *	octet beacon interval[2]
+ *	octet capability information[2]
+ *	information element
+ *		octet elemid
+ *		octet length
+ *		octet information[length]
+ */
+
+typedef u_int8_t *ieee80211_mgt_beacon_t;
+
+#define	IEEE80211_BEACON_INTERVAL(beacon) \
+	((beacon)[8] | ((beacon)[9] << 8))
+#define	IEEE80211_BEACON_CAPABILITY(beacon) \
+	((beacon)[10] | ((beacon)[11] << 8))
+
+#define	IEEE80211_CAPINFO_ESS			0x0001
+#define	IEEE80211_CAPINFO_IBSS			0x0002
+#define	IEEE80211_CAPINFO_CF_POLLABLE		0x0004
+#define	IEEE80211_CAPINFO_CF_POLLREQ		0x0008
+#define	IEEE80211_CAPINFO_PRIVACY		0x0010
+#define	IEEE80211_CAPINFO_SHORT_PREAMBLE	0x0020
+#define	IEEE80211_CAPINFO_PBCC			0x0040
+#define	IEEE80211_CAPINFO_CHNL_AGILITY		0x0080
+/* bits 8-9 are reserved */
+#define	IEEE80211_CAPINFO_SHORT_SLOTTIME	0x0400
+#define	IEEE80211_CAPINFO_RSN			0x0800
+/* bit 12 is reserved */
+#define	IEEE80211_CAPINFO_DSSSOFDM		0x2000
+/* bits 14-15 are reserved */
+
+/*
+ * 802.11i/WPA information element (maximally sized).
+ */
+struct ieee80211_ie_wpa {
+	u_int8_t	wpa_id;		/* IEEE80211_ELEMID_VENDOR */
+	u_int8_t	wpa_len;	/* length in bytes */
+	u_int8_t	wpa_oui[3];	/* 0x00, 0x50, 0xf2 */
+	u_int8_t	wpa_type;	/* OUI type */
+	u_int16_t	wpa_version;	/* spec revision */
+	u_int32_t	wpa_mcipher[1];	/* multicast/group key cipher */
+	u_int16_t	wpa_uciphercnt;	/* # pairwise key ciphers */
+	u_int32_t	wpa_uciphers[8];/* ciphers */
+	u_int16_t	wpa_authselcnt;	/* authentication selector cnt*/
+	u_int32_t	wpa_authsels[8];/* selectors */
+	u_int16_t	wpa_caps;	/* 802.11i capabilities */
+	u_int16_t	wpa_pmkidcnt;	/* 802.11i pmkid count */
+	u_int16_t	wpa_pmkids[8];	/* 802.11i pmkids */
+} __packed;
+
+/*
+ * Management information element payloads
+ */
+union ieee80211_information {
+	char	ssid[IEEE80211_NWID_LEN+1];
+	struct rates {
+		u_int8_t	*p;
+	} rates;
+	struct fh {
+		u_int16_t	dwell;
+		u_int8_t	set;
+		u_int8_t	pattern;
+		u_int8_t	index;
+	} fh;
+	struct ds {
+		u_int8_t	channel;
+	} ds;
+	struct cf {
+		u_int8_t	count;
+		u_int8_t	period;
+		u_int8_t	maxdur[2];
+		u_int8_t	dur[2];
+	} cf;
+	struct tim {
+		u_int8_t	count;
+		u_int8_t	period;
+		u_int8_t	bitctl;
+		/* u_int8_t	pvt[251]; The driver needs to use this. */
+	} tim;
+	struct ibss {
+		u_int16_t	atim;
+	} ibss;
+	struct challenge {
+		u_int8_t	*p;
+		u_int8_t	len;
+	} challenge;
+	struct erp {
+		u_int8_t	flags;
+	} erp;
+	struct country {
+		u_int8_t	cc[3];		/* ISO CC+(I)ndoor/(O)utdoor */
+		struct {
+			u_int8_t schan;		/* starting channel */
+			u_int8_t nchan;		/* number channels */
+			u_int8_t maxtxpwr;	
+		} band[4];			/* up to 4 sub bands */
+	} country;
+	struct ath {
+		u_int8_t	flags;
+	} ath;
+};
+
+enum {
+	IEEE80211_ELEMID_SSID		= 0,
+	IEEE80211_ELEMID_RATES		= 1,
+	IEEE80211_ELEMID_FHPARMS	= 2,
+	IEEE80211_ELEMID_DSPARMS	= 3,
+	IEEE80211_ELEMID_CFPARMS	= 4,
+	IEEE80211_ELEMID_TIM		= 5,
+	IEEE80211_ELEMID_IBSSPARMS	= 6,
+	IEEE80211_ELEMID_COUNTRY	= 7,
+	IEEE80211_ELEMID_CHALLENGE	= 16,
+	/* 17-31 reserved for challenge text extension */
+	IEEE80211_ELEMID_ERP		= 42,
+	IEEE80211_ELEMID_RSN		= 48,
+	IEEE80211_ELEMID_XRATES		= 50,
+	IEEE80211_ELEMID_TPC		= 150,
+	IEEE80211_ELEMID_CCKM		= 156,
+	IEEE80211_ELEMID_VENDOR		= 221,	/* vendor private */
+};
+
+#define IEEE80211_CHALLENGE_LEN		128
+
+#define	IEEE80211_RATE_BASIC		0x80
+#define	IEEE80211_RATE_VAL		0x7f
+
+/* EPR information element flags */
+#define	IEEE80211_ERP_NON_ERP_PRESENT	0x01
+#define	IEEE80211_ERP_USE_PROTECTION	0x02
+#define	IEEE80211_ERP_LONG_PREAMBLE	0x04
+
+/* Atheros private advanced capabilities info */
+#define	ATHEROS_CAP_TURBO_PRIME		0x01
+#define	ATHEROS_CAP_COMPRESSION		0x02
+#define	ATHEROS_CAP_FAST_FRAME		0x04
+/* bits 3-6 reserved */
+#define	ATHEROS_CAP_BOOST		0x80
+
+#define	ATH_OUI			0x7f0300		/* Atheros OUI */
+#define	ATH_OUI_TYPE		0x01
+#define	ATH_OUI_VERSION		0x01
+
+#define	WPA_OUI			0xf25000
+#define	WPA_OUI_TYPE		0x01
+#define	WPA_VERSION		1		/* current supported version */
+
+#define	WPA_CSE_NULL		0x00
+#define	WPA_CSE_WEP40		0x01
+#define	WPA_CSE_TKIP		0x02
+#define	WPA_CSE_CCMP		0x04
+#define	WPA_CSE_WEP104		0x05
+
+#define	WPA_ASE_NONE		0x00
+#define	WPA_ASE_8021X_UNSPEC	0x01
+#define	WPA_ASE_8021X_PSK	0x02
+
+#define	RSN_OUI			0xac0f00
+#define	RSN_VERSION		1		/* current supported version */
+
+#define	RSN_CSE_NULL		0x00
+#define	RSN_CSE_WEP40		0x01
+#define	RSN_CSE_TKIP		0x02
+#define	RSN_CSE_WRAP		0x03
+#define	RSN_CSE_CCMP		0x04
+#define	RSN_CSE_WEP104		0x05
+
+#define	RSN_ASE_NONE		0x00
+#define	RSN_ASE_8021X_UNSPEC	0x01
+#define	RSN_ASE_8021X_PSK	0x02
+
+#define	RSN_CAP_PREAUTH		0x01
+
+#define	WME_OUI			0xf25000
+#define	WME_OUI_TYPE		0x02
+#define	WME_VERSION		1
+
+/* WME stream classes */
+#define	WME_AC_BE	0		/* best effort */
+#define	WME_AC_BK	1		/* background */
+#define	WME_AC_VI	2		/* video */
+#define	WME_AC_VO	3		/* voice */
+
+/*
+ * AUTH management packets
+ *
+ *	octet algo[2]
+ *	octet seq[2]
+ *	octet status[2]
+ *	octet chal.id
+ *	octet chal.length
+ *	octet chal.text[253]
+ */
+
+typedef u_int8_t *ieee80211_mgt_auth_t;
+
+#define	IEEE80211_AUTH_ALGORITHM(auth) \
+	((auth)[0] | ((auth)[1] << 8))
+#define	IEEE80211_AUTH_TRANSACTION(auth) \
+	((auth)[2] | ((auth)[3] << 8))
+#define	IEEE80211_AUTH_STATUS(auth) \
+	((auth)[4] | ((auth)[5] << 8))
+
+#define	IEEE80211_AUTH_ALG_OPEN		0x0000
+#define	IEEE80211_AUTH_ALG_SHARED	0x0001
+#define	IEEE80211_AUTH_ALG_LEAP		0x0080
+
+enum {
+	IEEE80211_AUTH_OPEN_REQUEST		= 1,
+	IEEE80211_AUTH_OPEN_RESPONSE		= 2,
+};
+
+enum {
+	IEEE80211_AUTH_SHARED_REQUEST		= 1,
+	IEEE80211_AUTH_SHARED_CHALLENGE		= 2,
+	IEEE80211_AUTH_SHARED_RESPONSE		= 3,
+	IEEE80211_AUTH_SHARED_PASS		= 4,
+};
+
+/*
+ * Reason codes
+ *
+ * Unlisted codes are reserved
+ */
+
+enum {
+	IEEE80211_REASON_UNSPECIFIED		= 1,
+	IEEE80211_REASON_AUTH_EXPIRE		= 2,
+	IEEE80211_REASON_AUTH_LEAVE		= 3,
+	IEEE80211_REASON_ASSOC_EXPIRE		= 4,
+	IEEE80211_REASON_ASSOC_TOOMANY		= 5,
+	IEEE80211_REASON_NOT_AUTHED		= 6,
+	IEEE80211_REASON_NOT_ASSOCED		= 7,
+	IEEE80211_REASON_ASSOC_LEAVE		= 8,
+	IEEE80211_REASON_ASSOC_NOT_AUTHED	= 9,
+
+	IEEE80211_REASON_RSN_REQUIRED		= 11,
+	IEEE80211_REASON_RSN_INCONSISTENT	= 12,
+	IEEE80211_REASON_IE_INVALID		= 13,
+	IEEE80211_REASON_MIC_FAILURE		= 14,
+
+	IEEE80211_STATUS_SUCCESS		= 0,
+	IEEE80211_STATUS_UNSPECIFIED		= 1,
+	IEEE80211_STATUS_CAPINFO		= 10,
+	IEEE80211_STATUS_NOT_ASSOCED		= 11,
+	IEEE80211_STATUS_OTHER			= 12,
+	IEEE80211_STATUS_ALG			= 13,
+	IEEE80211_STATUS_SEQUENCE		= 14,
+	IEEE80211_STATUS_CHALLENGE		= 15,
+	IEEE80211_STATUS_TIMEOUT		= 16,
+	IEEE80211_STATUS_TOOMANY		= 17,
+	IEEE80211_STATUS_BASIC_RATE		= 18,
+	IEEE80211_STATUS_SP_REQUIRED		= 19,
+	IEEE80211_STATUS_PBCC_REQUIRED		= 20,
+	IEEE80211_STATUS_CA_REQUIRED		= 21,
+	IEEE80211_STATUS_TOO_MANY_STATIONS	= 22,
+	IEEE80211_STATUS_RATES			= 23,
+	IEEE80211_STATUS_SHORTSLOT_REQUIRED	= 25,
+	IEEE80211_STATUS_DSSSOFDM_REQUIRED	= 26,
+};
+
+#define	IEEE80211_WEP_KEYLEN		5	/* 40bit */
+#define	IEEE80211_WEP_IVLEN		3	/* 24bit */
+#define	IEEE80211_WEP_KIDLEN		1	/* 1 octet */
+#define	IEEE80211_WEP_CRCLEN		4	/* CRC-32 */
+#define	IEEE80211_WEP_NKID		4	/* number of key ids */
+
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP.  For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define	IEEE80211_WEP_EXTIV		0x20
+#define	IEEE80211_WEP_EXTIVLEN		4	/* extended IV length */
+#define	IEEE80211_WEP_MICLEN		8	/* trailing MIC */
+
+#define	IEEE80211_CRC_LEN		4
+
+/*
+ * Maximum acceptable MTU is:
+ *	IEEE80211_MAX_LEN - WEP overhead - CRC -
+ *		QoS overhead - RSN/WPA overhead
+ * Min is arbitrarily chosen > IEEE80211_MIN_LEN.  The default
+ * mtu is Ethernet-compatible; it's set by ether_ifattach.
+ */
+#define	IEEE80211_MTU_MAX		2290
+#define	IEEE80211_MTU_MIN		32
+
+#define	IEEE80211_MAX_LEN		(2300 + IEEE80211_CRC_LEN + \
+    (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN))
+#define	IEEE80211_ACK_LEN \
+	(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
+#define	IEEE80211_MIN_LEN \
+	(sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN)
+
+/*
+ * The 802.11 spec says at most 2007 stations may be
+ * associated at once.  For most AP's this is way more
+ * than is feasible so we use a default of 128.  This
+ * number may be overridden by the driver and/or by
+ * user configuration.
+ */
+#define	IEEE80211_AID_MAX		2007
+#define	IEEE80211_AID_DEF		128
+
+#define	IEEE80211_AID(b)	((b) &~ 0xc000)
+#define	IEEE80211_AID_SET(b, w) \
+	((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32)))
+#define	IEEE80211_AID_CLR(b, w) \
+	((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32)))
+#define	IEEE80211_AID_ISSET(b, w) \
+	((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
+
+/* 
+ * RTS frame length parameters.  The default is specified in
+ * the 802.11 spec.  The max may be wrong for jumbo frames.
+ */
+#define	IEEE80211_RTS_DEFAULT		512
+#define	IEEE80211_RTS_MIN		1
+#define	IEEE80211_RTS_MAX		IEEE80211_MAX_LEN
+
+enum {
+	IEEE80211_AUTH_NONE	= 0,
+	IEEE80211_AUTH_OPEN	= 1,		/* open */
+	IEEE80211_AUTH_SHARED	= 2,		/* shared-key */
+	IEEE80211_AUTH_8021X	= 3,		/* 802.1x */
+	IEEE80211_AUTH_AUTO	= 4,		/* auto-select/accept */
+	/* NB: these are used only for ioctls */
+	IEEE80211_AUTH_WPA	= 5,		/* WPA/RSN w/ 802.1x/PSK */
+};
+
+#endif /* _NET80211_IEEE80211_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_acl.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_acl.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_acl.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_acl.c	2005-02-24 13:06:17.299132152 -0800
@@ -0,0 +1,300 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+/*
+ * IEEE 802.11 MAC ACL support.
+ *
+ * When this module is loaded the sender address of each received
+ * frame is passed to the iac_check method and the module indicates
+ * if the frame should be accepted or rejected.  If the policy is
+ * set to ACL_POLICY_OPEN then all frames are accepted w/o checking
+ * the address.  Otherwise, the address is looked up in the database
+ * and if found the frame is either accepted (ACL_POLICY_ALLOW)
+ * or rejected (ACL_POLICY_DENT).
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+enum {
+	ACL_POLICY_OPEN		= 0,	/* open, don't check ACL's */
+	ACL_POLICY_ALLOW	= 1,	/* allow traffic from MAC */
+	ACL_POLICY_DENY		= 2,	/* deny traffic from MAC */
+};
+
+#define	ACL_HASHSIZE	32
+
+struct acl {
+	TAILQ_ENTRY(acl)	acl_list;
+	LIST_ENTRY(acl)		acl_hash;
+	u_int8_t		acl_macaddr[IEEE80211_ADDR_LEN];
+};
+struct aclstate {
+	acl_lock_t		as_lock;
+	int			as_policy;
+	TAILQ_HEAD(, acl)	as_list;	/* list of all ACL's */
+	ATH_LIST_HEAD(, acl)	as_hash[ACL_HASHSIZE];
+	struct ieee80211com	*as_ic;
+};
+
+/* simple hash is enough for variation of macaddr */
+#define	ACL_HASH(addr)	\
+	(((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
+
+MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
+
+static	int acl_free_all(struct ieee80211com *);
+
+static int
+acl_attach(struct ieee80211com *ic)
+{
+	struct aclstate *as;
+
+	_MOD_INC_USE(THIS_MODULE, return 0);
+
+	MALLOC(as, struct aclstate *, sizeof(struct aclstate),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (as == NULL) {
+		_MOD_DEC_USE(THIS_MODULE);
+		return 0;
+	}
+	ACL_LOCK_INIT(as, "acl");
+	TAILQ_INIT(&as->as_list);
+	as->as_policy = ACL_POLICY_OPEN;
+	as->as_ic = ic;
+	ic->ic_as = as;
+	return 1;
+}
+
+static void
+acl_detach(struct ieee80211com *ic)
+{
+	struct aclstate *as = ic->ic_as;
+
+	acl_free_all(ic);
+	ic->ic_as = NULL;
+	ACL_LOCK_DESTROY(as);
+	FREE(as, M_DEVBUF);
+
+	_MOD_DEC_USE(THIS_MODULE);
+}
+
+static inline struct acl *
+_find_acl(struct aclstate *as, const u_int8_t *macaddr)
+{
+	struct acl *acl;
+	int hash;
+
+	hash = ACL_HASH(macaddr);
+	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
+			return acl;
+	}
+	return NULL;
+}
+
+static void
+_acl_free(struct aclstate *as, struct acl *acl)
+{
+	ACL_LOCK_ASSERT(as);
+
+	TAILQ_REMOVE(&as->as_list, acl, acl_list);
+	LIST_REMOVE(acl, acl_hash);
+	FREE(acl, M_80211_ACL);
+}
+
+static int
+acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct aclstate *as = ic->ic_as;
+
+	switch (as->as_policy) {
+	case ACL_POLICY_OPEN:
+		return 1;
+	case ACL_POLICY_ALLOW:
+		return _find_acl(as, mac) != NULL;
+	case ACL_POLICY_DENY:
+		return _find_acl(as, mac) == NULL;
+	}
+	return 0;		/* should not happen */
+}
+
+static int
+acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct aclstate *as = ic->ic_as;
+	struct acl *acl, *new;
+	int hash;
+
+	MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
+	if (new == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+			("ACL: add %s failed, no memory\n",
+			ether_sprintf(mac)));
+		/* XXX statistic */
+		return ENOMEM;
+	}
+
+	ACL_LOCK(as);
+	hash = ACL_HASH(mac);
+	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
+			ACL_UNLOCK(as);
+			FREE(new, M_80211_ACL);
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+				("ACL: add %s failed, already present\n",
+				ether_sprintf(mac)));
+			return EEXIST;
+		}
+	}
+	IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
+	TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
+	LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
+	ACL_UNLOCK(as);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+		("ACL: add %s\n", ether_sprintf(mac)));
+	return 0;
+}
+
+static int
+acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct aclstate *as = ic->ic_as;
+	struct acl *acl;
+
+	ACL_LOCK(as);
+	acl = _find_acl(as, mac);
+	if (acl != NULL)
+		_acl_free(as, acl);
+	ACL_UNLOCK(as);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+		("ACL: remove %s%s\n", ether_sprintf(mac),
+		acl == NULL ? ", not present" : ""));
+
+	return (acl == NULL ? ENOENT : 0);
+}
+
+static int
+acl_free_all(struct ieee80211com *ic)
+{
+	struct aclstate *as = ic->ic_as;
+	struct acl *acl;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, ("ACL: free all\n"));
+
+	ACL_LOCK(as);
+	while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
+		_acl_free(as, acl);
+	ACL_UNLOCK(as);
+
+	return 0;
+}
+
+static int
+acl_setpolicy(struct ieee80211com *ic, int policy)
+{
+	struct aclstate *as = ic->ic_as;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+		("ACL: set policy to %u\n", policy));
+
+	switch (policy) {
+	case IEEE80211_MACCMD_POLICY_OPEN:
+		as->as_policy = ACL_POLICY_OPEN;
+		break;
+	case IEEE80211_MACCMD_POLICY_ALLOW:
+		as->as_policy = ACL_POLICY_ALLOW;
+		break;
+	case IEEE80211_MACCMD_POLICY_DENY:
+		as->as_policy = ACL_POLICY_DENY;
+		break;
+	default:
+		return EINVAL;
+	}
+	return 0;
+}
+
+static int
+acl_getpolicy(struct ieee80211com *ic)
+{
+	struct aclstate *as = ic->ic_as;
+
+	return as->as_policy;
+}
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: MAC-based ACL policy");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static const struct ieee80211_aclator mac = {
+	.iac_name	= "mac",
+	.iac_attach	= acl_attach,
+	.iac_detach	= acl_detach,
+	.iac_check	= acl_check,
+	.iac_add	= acl_add,
+	.iac_remove	= acl_remove,
+	.iac_flush	= acl_free_all,
+	.iac_setpolicy	= acl_setpolicy,
+	.iac_getpolicy	= acl_getpolicy,
+};
+
+static int __init
+init_ieee80211_acl(void)
+{
+	ieee80211_aclator_register(&mac);
+	return 0;
+}
+module_init(init_ieee80211_acl);
+
+static void __exit
+exit_ieee80211_acl(void)
+{
+	ieee80211_aclator_unregister(&mac);
+}
+module_exit(exit_ieee80211_acl);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.c	2005-02-24 13:06:17.299132152 -0800
@@ -0,0 +1,557 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto.c,v 1.4 2003/09/23 16:03:46 dyoung Exp $");
+
+/*
+ * IEEE 802.11 generic crypto support.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+
+#include "if_ethersubr.h"		/* XXX ETHER_HDR_LEN */
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+/*
+ * Table of registered cipher modules.
+ */
+static	const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
+
+static	int _ieee80211_crypto_delkey(struct ieee80211com *,
+		struct ieee80211_key *);
+
+/*
+ * Default "null" key management routines.
+ */
+static int
+null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	return IEEE80211_KEYIX_NONE;
+}
+static int
+null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	return 1;
+}
+static 	int
+null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+	     const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	return 1;
+}
+static void null_key_update(struct ieee80211com *ic) {}
+
+/*
+ * Write-arounds for common operations.
+ */
+static inline void
+cipher_detach(struct ieee80211_key *key)
+{
+	key->wk_cipher->ic_detach(key);
+}
+
+static inline void *
+cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	return key->wk_cipher->ic_attach(ic, key);
+}
+
+/* 
+ * Wrappers for driver key management methods.
+ */
+static inline int
+dev_key_alloc(struct ieee80211com *ic,
+	const struct ieee80211_key *key)
+{
+	return ic->ic_crypto.cs_key_alloc(ic, key);
+}
+
+static inline int
+dev_key_delete(struct ieee80211com *ic,
+	const struct ieee80211_key *key)
+{
+	return ic->ic_crypto.cs_key_delete(ic, key);
+}
+
+static inline int
+dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
+	const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	return ic->ic_crypto.cs_key_set(ic, key, mac);
+}
+
+/*
+ * Setup crypto support.
+ */
+void
+ieee80211_crypto_attach(struct ieee80211com *ic)
+{
+	struct ieee80211_crypto_state *cs = &ic->ic_crypto;
+	int i;
+
+	/* NB: we assume everything is pre-zero'd */
+	cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
+	ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+	for (i = 0; i < IEEE80211_WEP_NKID; i++)
+		ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i], i);
+	/*
+	 * Initialize the driver key support routines to noop entries.
+	 * This is useful especially for the cipher test modules.
+	 */
+	cs->cs_key_alloc = null_key_alloc;
+	cs->cs_key_set = null_key_set;
+	cs->cs_key_delete = null_key_delete;
+	cs->cs_key_update_begin = null_key_update;
+	cs->cs_key_update_end = null_key_update;
+}
+EXPORT_SYMBOL(ieee80211_crypto_attach);
+
+/*
+ * Teardown crypto support.
+ */
+void
+ieee80211_crypto_detach(struct ieee80211com *ic)
+{
+	ieee80211_crypto_delglobalkeys(ic);
+}
+EXPORT_SYMBOL(ieee80211_crypto_detach);
+
+/*
+ * Register a crypto cipher module.
+ */
+void
+ieee80211_crypto_register(const struct ieee80211_cipher *cip)
+{
+	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+		printf("%s: cipher %s has an invalid cipher index %u\n",
+			__func__, cip->ic_name, cip->ic_cipher);
+		return;
+	}
+	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+		printf("%s: cipher %s registered with a different template\n",
+			__func__, cip->ic_name);
+		return;
+	}
+	ciphers[cip->ic_cipher] = cip;
+}
+EXPORT_SYMBOL(ieee80211_crypto_register);
+
+/*
+ * Unregister a crypto cipher module.
+ */
+void
+ieee80211_crypto_unregister(const struct ieee80211_cipher *cip)
+{
+	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+		printf("%s: cipher %s has an invalid cipher index %u\n",
+			__func__, cip->ic_name, cip->ic_cipher);
+		return;
+	}
+	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+		printf("%s: cipher %s registered with a different template\n",
+			__func__, cip->ic_name);
+		return;
+	}
+	/* NB: don't complain about not being registered */
+	/* XXX disallow if references */
+	ciphers[cip->ic_cipher] = NULL;
+}
+EXPORT_SYMBOL(ieee80211_crypto_unregister);
+
+/* XXX well-known names! */
+static const char *cipher_modnames[] = {
+	"wlan_wep",	/* IEEE80211_CIPHER_WEP */
+	"wlan_tkip",	/* IEEE80211_CIPHER_TKIP */
+	"wlan_aes_ocb",	/* IEEE80211_CIPHER_AES_OCB */
+	"wlan_ccmp",	/* IEEE80211_CIPHER_AES_CCM */
+	"wlan_ckip",	/* IEEE80211_CIPHER_CKIP */
+};
+
+/*
+ * Establish a relationship between the specified key and cipher
+ * and, if not a global key, allocate a hardware index from the
+ * driver.  Note that we may be called for global keys but they
+ * should have a key index already setup so the only work done
+ * is to setup the cipher reference.
+ *
+ * This must be the first call applied to a key; all the other key
+ * routines assume wk_cipher is setup.
+ *
+ * Locking must be handled by the caller using:
+ *	ieee80211_key_update_begin(ic);
+ *	ieee80211_key_update_end(ic);
+ */
+int
+ieee80211_crypto_newkey(struct ieee80211com *ic,
+	int cipher, struct ieee80211_key *key)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	const struct ieee80211_cipher *cip;
+	void *keyctx;
+	int oflags;
+
+	/*
+	 * Validate cipher and set reference to cipher routines.
+	 */
+	if (cipher >= IEEE80211_CIPHER_MAX) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			("%s: invalid cipher %u\n", __func__, cipher));
+		ic->ic_stats.is_crypto_badcipher++;
+		return 0;
+	}
+	cip = ciphers[cipher];
+	if (cip == NULL) {
+		/*
+		 * Auto-load cipher module if we have a well-known name
+		 * for it.  It might be better to use string names rather
+		 * than numbers and craft a module name based on the cipher
+		 * name; e.g. wlan_cipher_<cipher-name>.
+		 */
+		if (cipher < N(cipher_modnames)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				("%s: unregistered cipher %u, load module %s\n",
+				__func__, cipher, cipher_modnames[cipher]));
+			request_module(cipher_modnames[cipher]);
+			/*
+			 * If cipher module loaded it should immediately
+			 * call ieee80211_crypto_register which will fill
+			 * in the entry in the ciphers array.
+			 */
+			cip = ciphers[cipher];
+		}
+		if (cip == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				("%s: unable to load cipher %u, module %s\n",
+				__func__, cipher,
+				cipher < N(cipher_modnames) ?
+					cipher_modnames[cipher] : "<unknown>"));
+			ic->ic_stats.is_crypto_nocipher++;
+			return 0;
+		}
+	}
+
+	oflags = key->wk_flags;
+	/*
+	 * If the hardware does not support the cipher then
+	 * fallback to a host-based implementation.
+	 */
+	key->wk_flags &= ~(IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC);
+	if ((ic->ic_caps & (1<<cipher)) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    ("%s: no h/w support for cipher %s, falling back to s/w\n",
+		    __func__, cip->ic_name));
+		key->wk_flags |= IEEE80211_KEY_SWCRYPT;
+	}
+	/*
+	 * Hardware TKIP with software MIC is an important
+	 * combination; we handle it by flagging each key,
+	 * the cipher modules honor it.
+	 */
+	if (cipher == IEEE80211_CIPHER_TKIP &&
+	    (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    ("%s: no h/w support for TKIP MIC, falling back to s/w\n",
+		    __func__));
+		key->wk_flags |= IEEE80211_KEY_SWMIC;
+	}
+
+	/*
+	 * Bind cipher to key instance.  Note we do this
+	 * after checking the device capabilities so the
+	 * cipher module can optimize space usage based on
+	 * whether or not it needs to do the cipher work.
+	 */
+	if (key->wk_cipher != cip || key->wk_flags != oflags) {
+again:
+		keyctx = cip->ic_attach(ic, key);
+		if (keyctx == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				("%s: unable to attach cipher %s\n",
+				__func__, cip->ic_name));
+			key->wk_flags = oflags;	/* restore old flags */
+			ic->ic_stats.is_crypto_attachfail++;
+			return 0;
+		}
+		cipher_detach(key);
+		key->wk_cipher = cip;		/* XXX refcnt? */
+		key->wk_private = keyctx;
+	}
+
+	/*
+	 * Ask the driver for a key index if we don't have one.
+	 * Note that entries in the global key table always have
+	 * an index; this means it's safe to call this routine
+	 * for these entries just to setup the reference to the
+	 * cipher template.  Note also that when using software
+	 * crypto we also call the driver to give us a key index.
+	 */
+	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+		key->wk_keyix = dev_key_alloc(ic, key);
+		if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+			/*
+			 * Driver has no room; fallback to doing crypto
+			 * in the host.  We change the flags and start the
+			 * procedure over.  If we get back here then there's
+			 * no hope and we bail.  Note that this can leave
+			 * the key in a inconsistent state if the caller
+			 * continues to use it.
+			 */
+			if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
+				ic->ic_stats.is_crypto_swfallback++;
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				    ("%s: no h/w resources for cipher %s, "
+				    "falling back to s/w\n", __func__,
+				    cip->ic_name));
+				oflags = key->wk_flags;
+				key->wk_flags |= IEEE80211_KEY_SWCRYPT;
+				if (cipher == IEEE80211_CIPHER_TKIP)
+					key->wk_flags |= IEEE80211_KEY_SWMIC;
+				goto again;
+			}
+			ic->ic_stats.is_crypto_keyfail++;
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    ("%s: unable to setup cipher %s\n",
+			    __func__, cip->ic_name));
+			return 0;
+		}
+	}
+	return 1;
+#undef N
+}
+EXPORT_SYMBOL(ieee80211_crypto_newkey);
+
+/*
+ * Remove the key (no locking, for internal use).
+ */
+static int
+_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	u_int16_t keyix;
+
+	KASSERT(key->wk_cipher != NULL, ("No cipher!"));
+
+	keyix = key->wk_keyix;
+	if (keyix != IEEE80211_KEYIX_NONE) {
+		/*
+		 * Remove hardware entry.
+		 */
+		/* XXX key cache */
+		if (!dev_key_delete(ic, key)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    ("%s: driver did not delete key index %u\n",
+			    __func__, keyix));
+			ic->ic_stats.is_crypto_delkey++;
+			/* XXX recovery? */
+		}
+	}
+	cipher_detach(key);
+	memset(key, 0, sizeof(*key));
+	key->wk_cipher = &ieee80211_cipher_none;
+	key->wk_private = cipher_attach(ic, key);
+	/* NB: cannot depend on key index to decide this */
+	if (&ic->ic_nw_keys[0] <= key &&
+	    key < &ic->ic_nw_keys[IEEE80211_WEP_NKID])
+		key->wk_keyix = keyix;		/* preserve shared key state */
+	else
+		key->wk_keyix = IEEE80211_KEYIX_NONE;
+	return 1;
+}
+
+/*
+ * Remove the specified key.
+ */
+int
+ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	int status;
+
+	ieee80211_key_update_begin(ic);
+	status = _ieee80211_crypto_delkey(ic, key);
+	ieee80211_key_update_end(ic);
+	return status;
+}
+EXPORT_SYMBOL(ieee80211_crypto_delkey);
+
+/*
+ * Clear the global key table.
+ */
+void
+ieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
+{
+	int i;
+
+	ieee80211_key_update_begin(ic);
+	for (i = 0; i < IEEE80211_WEP_NKID; i++)
+		(void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
+	ieee80211_key_update_end(ic);
+}
+EXPORT_SYMBOL(ieee80211_crypto_delglobalkeys);
+
+/*
+ * Set the contents of the specified key.
+ *
+ * Locking must be handled by the caller using:
+ *	ieee80211_key_update_begin(ic);
+ *	ieee80211_key_update_end(ic);
+ */
+int
+ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
+		const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+{
+	const struct ieee80211_cipher *cip = key->wk_cipher;
+
+	KASSERT(cip != NULL, ("No cipher!"));
+
+	/*
+	 * Give cipher a chance to validate key contents.
+	 * XXX should happen before modifying state.
+	 */
+	if (!cip->ic_setkey(key)) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    ("%s: cipher %s rejected key index %u\n",
+		    __func__, cip->ic_name, key->wk_keyix));
+		ic->ic_stats.is_crypto_setkey_cipher++;
+		return 0;
+	}
+	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+		/* XXX nothing allocated, should not happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    ("%s: no key index; should not happen!\n", __func__));
+		ic->ic_stats.is_crypto_setkey_nokey++;
+		return 0;
+	}
+	return dev_key_set(ic, key, macaddr);
+}
+EXPORT_SYMBOL(ieee80211_crypto_setkey);
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+struct ieee80211_key *
+ieee80211_crypto_encap(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct sk_buff *skb)
+{
+	struct ieee80211_key *k;
+	struct ieee80211_frame *wh;
+	const struct ieee80211_cipher *cip;
+	u_int8_t keyix;
+
+	/*
+	 * Multicast traffic always uses the multicast key.
+	 * Otherwise if a unicast key is set we use that and
+	 * it is always key index 0.  When no unicast key is
+	 * set we fall back to the default transmit key.
+	 */
+	wh = (struct ieee80211_frame *)skb->data;
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				("%s: No default xmit key for frame to %s\n",
+				__func__, ether_sprintf(wh->i_addr1)));
+			ic->ic_stats.is_tx_nodefkey++;
+			return NULL;
+		}
+		keyix = ic->ic_def_txkey;
+		k = &ic->ic_nw_keys[ic->ic_def_txkey];
+	} else {
+		keyix = 0;
+		k = &ni->ni_ucastkey;
+	}
+	cip = k->wk_cipher;
+	if (skb_headroom(skb) < cip->ic_header) {
+		/*
+		 * Should not happen; ieee80211_skbhdr_adjust should
+		 * have allocated enough space for all headers.
+		 */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			("%s: Malformed packet for cipher %s; headroom %u\n",
+			__func__, cip->ic_name, skb_headroom(skb)));
+		ic->ic_stats.is_tx_noheadroom++;
+		return NULL;
+	}
+	return ((*cip->ic_encap)(k, skb, keyix<<6) ? k : NULL);
+}
+EXPORT_SYMBOL(ieee80211_crypto_encap);
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame that has the WEP/Privacy bit set.
+ */
+struct ieee80211_key *
+ieee80211_crypto_decap(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct sk_buff *skb)
+{
+#define	IEEE80211_WEP_HDRLEN	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
+#define	IEEE80211_WEP_MINLEN \
+	(sizeof(struct ieee80211_frame) + ETHER_HDR_LEN + \
+	IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
+	struct ieee80211_key *k;
+	struct ieee80211_frame *wh;
+	u_int8_t *ivp;
+	u_int8_t keyid;
+
+	/* NB: this minimum size data frame could be bigger */
+	if (skb->len < IEEE80211_WEP_MINLEN) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: WEP data frame too short, len %u\n",
+			__func__, skb->len));
+		ic->ic_stats.is_rx_tooshort++;	/* XXX need unique stat? */
+		return NULL;
+	}
+	/*
+	 * Locate the key. If unicast and there is no unicast
+	 * key then we fall back to the key id in the header.
+	 * This assumes unicast keys are only configured when
+	 * the key id in the header is meaningless (typically 0).
+	 */
+	wh = (struct ieee80211_frame *) skb->data;
+	ivp = skb->data + ieee80211_hdrsize(wh);
+	keyid = ivp[IEEE80211_WEP_IVLEN];
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none)
+		k = &ic->ic_nw_keys[keyid >> 6];
+	else
+		k = &ni->ni_ucastkey;
+	return ((*k->wk_cipher->ic_decap)(k, skb) ? k : NULL);
+#undef IEEE80211_WEP_MINLEN
+#undef IEEE80211_WEP_HDRLEN
+}
+EXPORT_SYMBOL(ieee80211_crypto_decap);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.h	2005-02-24 13:06:17.300132000 -0800
@@ -0,0 +1,221 @@
+/*	$NetBSD: ieee80211_crypto.h,v 1.2 2003/09/14 01:14:55 dyoung Exp $	*/
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.2 2003/06/27 05:13:52 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_CRYPTO_H_
+#define _NET80211_IEEE80211_CRYPTO_H_
+
+/*
+ * 802.11 protocol crypto-related definitions.
+ */
+#define	IEEE80211_KEYBUF_SIZE	16
+#define	IEEE80211_MICBUF_SIZE	(8+8)	/* space for both tx+rx keys */
+
+/*
+ * Old WEP-style key.  Deprecated.
+ */
+struct ieee80211_wepkey {
+	u_int		wk_len;		/* key length in bytes */
+	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE];
+};
+
+struct ieee80211_cipher;
+
+/*
+ * Crypto key state.  There is sufficient room for all supported
+ * ciphers (see below).  The underlying ciphers are handled
+ * separately through loadable cipher modules that register with
+ * the generic crypto support.  A key has a reference to an instance
+ * of the cipher; any per-key state is hung off wk_private by the
+ * cipher when it is attached.  Ciphers are automatically called
+ * to detach and cleanup any such state when the key is deleted.
+ *
+ * The generic crypto support handles encap/decap of cipher-related
+ * frame contents for both hardware- and software-based implementations.
+ * A key requiring software crypto support is automatically flagged and
+ * the cipher is expected to honor this and do the necessary work.
+ * Ciphers such as TKIP may also support mixed hardware/software
+ * encrypt/decrypt and MIC processing.
+ */
+/* XXX need key index typedef */
+/* XXX pack better? */
+/* XXX 48-bit rsc/tsc */
+struct ieee80211_key {
+	u_int8_t	wk_keylen;	/* key length in bytes */
+	u_int8_t	wk_flags;
+#define	IEEE80211_KEY_XMIT	0x01	/* key used for xmit */
+#define	IEEE80211_KEY_RECV	0x02	/* key used for recv */
+#define	IEEE80211_KEY_SWCRYPT	0x04	/* host-based encrypt/decrypt */
+#define	IEEE80211_KEY_SWMIC	0x08	/* host-based enmic/demic */
+	u_int16_t	wk_keyix;	/* key index */
+	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+#define	wk_txmic	wk_key+IEEE80211_KEYBUF_SIZE+0	/* XXX can't () right */
+#define	wk_rxmic	wk_key+IEEE80211_KEYBUF_SIZE+8	/* XXX can't () right */
+	u_int64_t	wk_keyrsc;	/* key receive sequence counter */
+	u_int64_t	wk_keytsc;	/* key transmit sequence counter */
+	const struct ieee80211_cipher *wk_cipher;
+	void		*wk_private;	/* private cipher state */
+};
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering.  In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define	IEEE80211_CIPHER_WEP		0
+#define	IEEE80211_CIPHER_TKIP		1
+#define	IEEE80211_CIPHER_AES_OCB	2
+#define	IEEE80211_CIPHER_AES_CCM	3
+#define	IEEE80211_CIPHER_CKIP		5
+#define	IEEE80211_CIPHER_NONE		6	/* pseudo value */
+
+#define	IEEE80211_CIPHER_MAX		(IEEE80211_CIPHER_NONE+1)
+
+#define	IEEE80211_KEYIX_NONE	((u_int16_t) -1)
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+
+struct ieee80211com;
+struct ieee80211_node;
+struct sk_buff;
+
+/*
+ * Crypto state kept in each ieee80211com.  Some of this
+ * can/should be shared when virtual AP's are supported.
+ *
+ * XXX save reference to ieee80211com to properly encapsulate state.
+ * XXX split out crypto capabilities from ic_caps
+ */
+struct ieee80211_crypto_state {
+	struct ieee80211_key	cs_nw_keys[IEEE80211_WEP_NKID];
+	u_int16_t		cs_def_txkey;	/* default/group tx key index */
+
+	int			(*cs_key_alloc)(struct ieee80211com *,
+					const struct ieee80211_key *);
+	int			(*cs_key_delete)(struct ieee80211com *, 
+					const struct ieee80211_key *);
+	int			(*cs_key_set)(struct ieee80211com *,
+					const struct ieee80211_key *,
+					const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	void			(*cs_key_update_begin)(struct ieee80211com *);
+	void			(*cs_key_update_end)(struct ieee80211com *);
+};
+
+extern	void ieee80211_crypto_attach(struct ieee80211com *);
+extern	void ieee80211_crypto_detach(struct ieee80211com *);
+extern	int ieee80211_crypto_newkey(struct ieee80211com *,
+		int cipher, struct ieee80211_key *);
+extern	int ieee80211_crypto_delkey(struct ieee80211com *,
+		struct ieee80211_key *);
+extern	int ieee80211_crypto_setkey(struct ieee80211com *,
+		struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]);
+extern	void ieee80211_crypto_delglobalkeys(struct ieee80211com *);
+
+/*
+ * Template for a supported cipher.  Ciphers register with the
+ * crypto code and are typically loaded as separate modules
+ * (the null cipher is always present).
+ * XXX may need refcnts
+ */
+struct ieee80211_cipher {
+	const char *ic_name;		/* printable name */
+	u_int	ic_cipher;		/* IEEE80211_CIPHER_* */
+	u_int	ic_header;		/* size of privacy header (bytes) */
+	u_int	ic_trailer;		/* size of privacy trailer (bytes) */
+	u_int	ic_miclen;		/* size of mic trailer (bytes) */
+	void*	(*ic_attach)(struct ieee80211com *, struct ieee80211_key *);
+	void	(*ic_detach)(struct ieee80211_key *);
+	int	(*ic_setkey)(struct ieee80211_key *);
+	int	(*ic_encap)(struct ieee80211_key *, struct sk_buff *,
+			u_int8_t keyid);
+	int	(*ic_decap)(struct ieee80211_key *, struct sk_buff *);
+	int	(*ic_enmic)(struct ieee80211_key *, struct sk_buff *);
+	int	(*ic_demic)(struct ieee80211_key *, struct sk_buff *);
+};
+extern	const struct ieee80211_cipher ieee80211_cipher_none;
+
+extern	void ieee80211_crypto_register(const struct ieee80211_cipher *);
+extern	void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
+
+extern	struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *,
+		struct ieee80211_node *, struct sk_buff *);
+extern	struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *,
+		struct ieee80211_node *, struct sk_buff *);
+
+/*
+ * Check and remove any MIC.
+ */
+static inline int
+ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
+	struct sk_buff *skb)
+{
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	return (cip->ic_miclen > 0 ? (*cip->ic_demic)(k, skb) : 1);
+}
+
+/*
+ * Add any MIC.
+ */
+static inline int
+ieee80211_crypto_enmic(struct ieee80211com *ic,
+	struct ieee80211_key *k, struct sk_buff *skb)
+{
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	return (cip->ic_miclen > 0 ? (*cip->ic_enmic)(k, skb) : 1);
+}
+
+/* 
+ * Reset key state to an unused state.  The crypto
+ * key allocation mechanism insures other state (e.g.
+ * key data) is properly setup before a key is used.
+ */
+static inline void
+ieee80211_crypto_resetkey(struct ieee80211com *ic,
+	struct ieee80211_key *k, u_int16_t ix)
+{
+	k->wk_cipher = &ieee80211_cipher_none;;
+	k->wk_private = k->wk_cipher->ic_attach(ic, k);
+	k->wk_keyix = ix;
+	k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+}
+
+/*
+ * Crypt-related notification methods.
+ */
+extern	void ieee80211_notify_replay_failure(struct ieee80211com *,
+		const struct ieee80211_frame *, const struct ieee80211_key *,
+		u_int64_t rsc);
+extern	void ieee80211_notify_michael_failure(struct ieee80211com *,
+		const struct ieee80211_frame *, u_int keyix);
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c	2005-02-24 13:06:17.301131848 -0800
@@ -0,0 +1,528 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * IEEE 802.11i AES-CCMP crypto support.
+ *
+ * Part of this module is derived from similar code in the Host
+ * AP driver. The code is used with the consent of the author and
+ * it's license is included below.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/init.h>
+
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+#define AES_BLOCK_LEN 16
+
+struct ccmp_ctx {
+	struct ieee80211com *cc_ic;	/* for diagnostics */
+	struct crypto_tfm   *cc_tfm;
+};
+
+static	void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void ccmp_detach(struct ieee80211_key *);
+static	int ccmp_setkey(struct ieee80211_key *);
+static	int ccmp_encap(struct ieee80211_key *k, struct sk_buff *skb,
+		u_int8_t keyid);
+static	int ccmp_decap(struct ieee80211_key *, struct sk_buff *);
+static	int ccmp_enmic(struct ieee80211_key *, struct sk_buff *);
+static	int ccmp_demic(struct ieee80211_key *, struct sk_buff *);
+
+static const struct ieee80211_cipher ccmp = {
+	.ic_name	= "AES-CCM",
+	.ic_cipher	= IEEE80211_CIPHER_AES_CCM,
+	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+			  IEEE80211_WEP_EXTIVLEN,
+	.ic_trailer	= IEEE80211_WEP_MICLEN,
+	.ic_miclen	= 0,
+	.ic_attach	= ccmp_attach,
+	.ic_detach	= ccmp_detach,
+	.ic_setkey	= ccmp_setkey,
+	.ic_encap	= ccmp_encap,
+	.ic_decap	= ccmp_decap,
+	.ic_enmic	= ccmp_enmic,
+	.ic_demic	= ccmp_demic,
+};
+
+static	int ccmp_encrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen);
+static	int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
+		struct sk_buff *, int hdrlen);
+
+static void *
+ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx;
+
+	_MOD_INC_USE(THIS_MODULE, return NULL);
+
+	MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ctx == NULL) {
+		ic->ic_stats.is_crypto_nomem++;
+		_MOD_DEC_USE(THIS_MODULE);
+		return NULL;
+	}
+
+	ctx->cc_ic = ic;
+	ctx->cc_tfm = crypto_alloc_tfm("aes", 0);
+	if (ctx->cc_tfm == NULL) {
+		FREE(ctx, M_DEVBUF);
+		_MOD_DEC_USE(THIS_MODULE);
+		return NULL;
+	}
+	return ctx;
+}
+
+static void
+ccmp_detach(struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+
+	if (ctx->cc_tfm != NULL)
+		crypto_free_tfm(ctx->cc_tfm);
+	FREE(ctx, M_DEVBUF);
+
+	_MOD_DEC_USE(THIS_MODULE);
+}
+
+static int
+ccmp_setkey(struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+
+	if (k->wk_keylen != (128/NBBY)) {
+		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+			("%s: Invalid key length %u, expecting %u\n",
+			__func__, k->wk_keylen, 128/NBBY));
+		return 0;
+	}
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+		crypto_cipher_setkey(ctx->cc_tfm, k->wk_key, k->wk_keylen);
+	return 1;
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+ccmp_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid)
+{
+	u_int8_t *ivp;
+	int hdrlen;
+
+	hdrlen = ieee80211_hdrsize(skb->data);
+
+	/*
+	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+	 */
+	ivp = skb_push(skb, ccmp.ic_header);
+	memmove(ivp, ivp + ccmp.ic_header, hdrlen);
+	ivp += hdrlen;
+
+	k->wk_keytsc++;		/* XXX wrap at 48 bits */
+	ivp[0] = k->wk_keytsc >> 0;		/* PN0 */
+	ivp[1] = k->wk_keytsc >> 8;		/* PN1 */
+	ivp[2] = 0;				/* Reserved */
+	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
+	ivp[4] = k->wk_keytsc >> 16;		/* PN2 */
+	ivp[5] = k->wk_keytsc >> 24;		/* PN3 */
+	ivp[6] = k->wk_keytsc >> 32;		/* PN4 */
+	ivp[7] = k->wk_keytsc >> 40;		/* PN5 */
+
+	/*
+	 * Finally, do software encrypt if neeed.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !ccmp_encrypt(k, skb, hdrlen))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+ccmp_enmic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+
+	return 1;
+}
+
+static inline u_int64_t
+READ_6(u_int8_t b0, u_int8_t b1, u_int8_t b2, u_int8_t b3, u_int8_t b4, u_int8_t b5)
+{
+	u_int32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+	u_int16_t iv16 = (b4 << 0) | (b5 << 8);
+	return (((u_int64_t)iv16) << 32) | iv32;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. The specified key should be correct but
+ * is also verified.
+ */
+static int
+ccmp_decap(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+	struct ieee80211_frame *wh;
+	u_int8_t *ivp;
+	u_int64_t pn;
+	int hdrlen;
+
+	/*
+	 * Header should have extended IV and sequence number;
+	 * verify the former and validate the latter.
+	 */
+	wh = (struct ieee80211_frame *)skb->data;
+	hdrlen = ieee80211_hdrsize(wh);
+	ivp = skb->data + hdrlen;
+	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+		/*
+		 * No extended IV; discard frame.
+		 */
+		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+			("[%s] Missing ExtIV for AES-CCM cipher\n",
+			ether_sprintf(wh->i_addr2)));
+		ctx->cc_ic->ic_stats.is_rx_ccmpformat++;
+		return 0;
+	}
+	/* NB: assume IEEEE80211_WEP_MINLEN covers the extended IV */ 
+	pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+	if (pn <= k->wk_keyrsc) {
+		/*
+		 * Replay violation.
+		 */
+		ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn);
+		ctx->cc_ic->ic_stats.is_rx_ccmpreplay++;
+		return 0;
+	}
+
+	/*
+	 * Check if the device handled the decrypt in hardware.
+	 * If so we just strip the header; otherwise we need to
+	 * handle the decrypt in software.  Note that for the
+	 * latter we leave the header in place for use in the
+	 * decryption work.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !ccmp_decrypt(k, pn, skb, hdrlen))
+		return 0;
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	memmove(skb->data + ccmp.ic_header, skb->data, hdrlen);
+	skb_pull(skb, ccmp.ic_header);
+	skb_trim(skb, skb->len - ccmp.ic_trailer);
+
+	/*
+	 * Ok to update rsc now.
+	 */
+	k->wk_keyrsc = pn;
+
+	return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+ccmp_demic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	return 1;
+}
+
+static inline void
+xor_block(u8 *b, const u8 *a, size_t len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		b[i] ^= a[i];
+}
+
+static void
+rijndael_encrypt(struct crypto_tfm *tfm, const void *src, void *dst)
+{
+	struct scatterlist sg_src;
+	struct scatterlist sg_dst;
+
+	sg_src.page = virt_to_page(src);
+	sg_src.offset = offset_in_page(src);
+	sg_src.length = AES_BLOCK_LEN;
+
+	sg_dst.page = virt_to_page(dst);
+	sg_dst.offset = offset_in_page(dst);
+	sg_dst.length = AES_BLOCK_LEN;
+	crypto_cipher_encrypt(tfm, &sg_dst, &sg_src, AES_BLOCK_LEN);
+}
+
+/*
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ */
+
+static void
+ccmp_init_blocks(struct crypto_tfm *tfm, struct ieee80211_frame *wh,
+	u_int64_t pn, size_t dlen, u8 *b0, u8 *aad, u8 *auth, u8 *s0)
+{
+#define	IS_4ADDRESS(wh) \
+	((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+#define	IS_QOS_DATA(wh)	IEEE80211_QOS_HAS_SEQ(wh)
+
+	/* 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;
+	/* NB: b0[1] set below */
+	IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);
+	b0[8] = pn >> 40;
+	b0[9] = pn >> 32;
+	b0[10] = pn >> 24;
+	b0[11] = pn >> 16;
+	b0[12] = pn >> 8;
+	b0[13] = pn >> 0;
+	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)
+	 */
+	aad[0] = 0;	/* AAD length >> 8 */
+	/* NB: aad[1] set below */
+	aad[2] = wh->i_fc[0] & 0x8f;	/* XXX magic #s */
+	aad[3] = wh->i_fc[1] & 0xc7;	/* XXX magic #s */
+	/* NB: we know 3 addresses are contiguous */
+	memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
+	aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
+	aad[23] = 0; /* all bits masked */
+	/*
+	 * Construct variable-length portion of AAD based
+	 * on whether this is a 4-address frame/QOS frame.
+	 * We always zero-pad to 32 bytes before running it
+	 * through the cipher.
+	 *
+	 * We also fill in the priority bits of the CCM
+	 * initial block as we know whether or not we have
+	 * a QOS frame.
+	 */
+	if (IS_4ADDRESS(wh)) {
+		IEEE80211_ADDR_COPY(aad + 24,
+			((struct ieee80211_frame_addr4 *)wh)->i_addr4);
+		if (IS_QOS_DATA(wh)) {
+			struct ieee80211_qosframe_addr4 *qwh4 =
+				(struct ieee80211_qosframe_addr4 *) wh;
+			aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
+			aad[31] = 0;
+			b0[1] = aad[30];
+			aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
+		} else {
+			*(u_int16_t *)&aad[30] = 0;
+			b0[1] = 0;
+			aad[1] = 22 + IEEE80211_ADDR_LEN;
+		}
+	} else {
+		if (IS_QOS_DATA(wh)) {
+			struct ieee80211_qosframe *qwh =
+				(struct ieee80211_qosframe*) wh;
+			aad[24] = qwh->i_qos[0] & 0x0f;	/* just priority bits */
+			aad[25] = 0;
+			b0[1] = aad[24];
+			aad[1] = 22 + 2;
+		} else {
+			*(u_int16_t *)&aad[24] = 0;
+			b0[1] = 0;
+			aad[1] = 22;
+		}
+		*(u_int16_t *)&aad[26] = 0;
+		*(u_int32_t *)&aad[28] = 0;
+	}
+
+	/* Start with the first block and AAD */
+	rijndael_encrypt(tfm, b0, auth);
+	xor_block(auth, aad, AES_BLOCK_LEN);
+	rijndael_encrypt(tfm, auth, auth);
+	xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+	rijndael_encrypt(tfm, auth, auth);
+	b0[0] &= 0x07;
+	b0[14] = b0[15] = 0;
+	rijndael_encrypt(tfm, b0, s0);
+#undef	IS_QOS_DATA
+#undef	IS_4ADDRESS
+}
+
+static int
+ccmp_encrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen)
+{
+	struct ccmp_ctx *ctx = key->wk_private;
+	struct ieee80211_frame *wh;
+	int data_len, i, blocks, last, len;
+	u8 aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN],
+		e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
+	u8 *mic, *pos;
+
+	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+
+	wh = (struct ieee80211_frame *) skb->data;
+	if (skb_tailroom(skb) < ccmp.ic_trailer) {
+		/* NB: should not happen */
+		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+			("[%s] No room for %s MIC, tailroom %u\n",
+			ether_sprintf(wh->i_addr1), ccmp.ic_name,
+			skb_tailroom(skb)));
+		/* XXX statistic */
+		return 0;
+	}
+	data_len = skb->len - (hdrlen + ccmp.ic_header);
+	ccmp_init_blocks(ctx->cc_tfm, wh, key->wk_keytsc,
+		data_len, b0, aad, b, s0);
+
+	pos = skb->data + hdrlen + ccmp.ic_header;
+	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);
+		rijndael_encrypt(ctx->cc_tfm, b, b);
+		/* Encryption, with counter */
+		b0[14] = (i >> 8) & 0xff;
+		b0[15] = i & 0xff;
+		rijndael_encrypt(ctx->cc_tfm, b0, e);
+		xor_block(pos, e, len);
+		pos += len;
+	}
+
+	mic = skb_put(skb, ccmp.ic_trailer);
+	for (i = 0; i < ccmp.ic_trailer; i++)
+		mic[i] = b[i] ^ s0[i];
+
+	return 1;
+}
+
+static int
+ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct sk_buff *skb, int hdrlen)
+{
+	struct ccmp_ctx *ctx = key->wk_private;
+	struct ieee80211_frame *wh;
+	u8 aad[2 * AES_BLOCK_LEN];
+	u8 b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
+	int i, blocks, last, len;
+	size_t data_len;
+	u8 *pos, *mic;
+
+	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+
+	wh = (struct ieee80211_frame *) skb->data;
+	data_len = skb->len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
+	ccmp_init_blocks(ctx->cc_tfm, wh, pn, data_len, b0, aad, a, b);
+	mic = skb->data + skb->len - ccmp.ic_trailer;
+	xor_block(mic, b, ccmp.ic_trailer);
+
+	pos = skb->data + hdrlen + ccmp.ic_header;
+	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;
+		rijndael_encrypt(ctx->cc_tfm, b0, b);
+		xor_block(pos, b, len);
+		/* Authentication */
+		xor_block(a, pos, len);
+		rijndael_encrypt(ctx->cc_tfm, a, a);
+		pos += len;
+	}
+
+	if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
+		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+			("[%s] AES-CCM decrypt failed; MIC mismatch\n",
+			ether_sprintf(wh->i_addr2)));
+		ctx->cc_ic->ic_stats.is_rx_ccmpmic++;
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: AES-CCM cipher");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static int __init
+init_crypto_ccmp(void)
+{
+	ieee80211_crypto_register(&ccmp);
+	return 0;
+}
+module_init(init_crypto_ccmp);
+
+static void __exit
+exit_crypto_ccmp(void)
+{
+	ieee80211_crypto_unregister(&ccmp);
+}
+module_exit(exit_crypto_ccmp);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_none.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_none.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_none.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_none.c	2005-02-24 13:06:17.301131848 -0800
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto.c,v 1.4 2003/09/23 16:03:46 dyoung Exp $");
+
+/*
+ * IEEE 802.11 NULL crypto support.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+static	void *none_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void none_detach(struct ieee80211_key *);
+static	int none_setkey(struct ieee80211_key *);
+static	int none_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
+static	int none_decap(struct ieee80211_key *, struct sk_buff *);
+static	int none_enmic(struct ieee80211_key *, struct sk_buff *);
+static	int none_demic(struct ieee80211_key *, struct sk_buff *);
+
+const struct ieee80211_cipher ieee80211_cipher_none = {
+	.ic_name	= "NONE",
+	.ic_cipher	= IEEE80211_CIPHER_NONE,
+	.ic_header	= 0,
+	.ic_trailer	= 0,
+	.ic_miclen	= 0,
+	.ic_attach	= none_attach,
+	.ic_detach	= none_detach,
+	.ic_setkey	= none_setkey,
+	.ic_encap	= none_encap,
+	.ic_decap	= none_decap,
+	.ic_enmic	= none_enmic,
+	.ic_demic	= none_demic,
+};
+EXPORT_SYMBOL(ieee80211_cipher_none);
+
+static void *
+none_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	return ic;		/* for diagnostics+stats */
+}
+
+static void
+none_detach(struct ieee80211_key *k)
+{
+	(void) k;
+}
+
+static int
+none_setkey(struct ieee80211_key *k)
+{
+	(void) k;
+	return 1;
+}
+
+static int
+none_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid)
+{
+	struct ieee80211com *ic = k->wk_private;
+	struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
+
+	/*
+	 * The specified key is not setup; this can
+	 * happen, at least, when changing keys.
+	 */
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		("[%s] key id %u is not set (encap)\n",
+		ether_sprintf(wh->i_addr1), keyid>>6));
+	ic->ic_stats.is_tx_badcipher++;
+	return 0;
+}
+
+static int
+none_decap(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct ieee80211com *ic = k->wk_private;
+	struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
+	const u_int8_t *ivp = (const u_int8_t *)&wh[1];
+
+	/*
+	 * The specified key is not setup; this can
+	 * happen, at least, when changing keys.
+	 */
+	/* XXX useful to know dst too */
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		("[%s] key id %u is not set (decap)\n",
+		ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6));
+	ic->ic_stats.is_rx_badkeyid++;
+	return 0;
+}
+
+static int
+none_enmic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct ieee80211com *ic = k->wk_private;
+
+	ic->ic_stats.is_tx_badcipher++;
+	return 0;
+}
+
+static int
+none_demic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct ieee80211com *ic = k->wk_private;
+
+	ic->ic_stats.is_rx_badkeyid++;
+	return 0;
+}
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c	2005-02-24 13:06:17.302131696 -0800
@@ -0,0 +1,958 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * IEEE 802.11i TKIP crypto support.
+ *
+ * Part of this module is derived from similar code in the Host
+ * AP driver. The code is used with the consent of the author and
+ * it's license is included below.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_USE_CRYPTO_API
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include <asm/uaccess.h>
+#endif
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+static	void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void tkip_detach(struct ieee80211_key *);
+static	int tkip_setkey(struct ieee80211_key *);
+static	int tkip_encap(struct ieee80211_key *, struct sk_buff *skb,
+		u_int8_t keyid);
+static	int tkip_enmic(struct ieee80211_key *, struct sk_buff *);
+static	int tkip_decap(struct ieee80211_key *, struct sk_buff *);
+static	int tkip_demic(struct ieee80211_key *, struct sk_buff *);
+
+static const struct ieee80211_cipher tkip  = {
+	.ic_name	= "TKIP",
+	.ic_cipher	= IEEE80211_CIPHER_TKIP,
+	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+			  IEEE80211_WEP_EXTIVLEN,
+	.ic_trailer	= IEEE80211_WEP_CRCLEN,
+	.ic_miclen	= IEEE80211_WEP_MICLEN,
+	.ic_attach	= tkip_attach,
+	.ic_detach	= tkip_detach,
+	.ic_setkey	= tkip_setkey,
+	.ic_encap	= tkip_encap,
+	.ic_decap	= tkip_decap,
+	.ic_enmic	= tkip_enmic,
+	.ic_demic	= tkip_demic,
+};
+
+struct tkip_ctx {
+	struct ieee80211com *tc_ic;	/* for diagnostics */
+
+	u16	tx_ttak[5];
+	int	tx_phase1_done;
+	u8	tx_rc4key[16];		/* XXX for test module; make locals? */
+
+	u16	rx_ttak[5];
+	int	rx_phase1_done;
+	u8	rx_rc4key[16];		/* XXX for test module; make locals? */
+	u_int64_t rx_rsc;		/* held until MIC verified */
+
+#ifdef CONFIG_USE_CRYPTO_API
+	struct crypto_tfm *tfm_michael;
+#endif
+};
+
+static	void michael_mic_hdr(const struct ieee80211_frame *, u8 hdr[16]);
+static	void michael_mic(struct tkip_ctx *, const u8 *key, const u8 hdr[16],
+		const u8 *data, size_t data_len, u8 mic[IEEE80211_WEP_MICLEN]);
+static	int tkip_encrypt(struct tkip_ctx *, struct ieee80211_key *,
+		struct sk_buff *, int hdr_len);
+static	int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *,
+		struct sk_buff *, int hdr_len);
+
+static void *
+tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx;
+
+	_MOD_INC_USE(THIS_MODULE, return NULL);
+
+	MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ctx == NULL) {
+		ic->ic_stats.is_crypto_nomem++;
+		_MOD_DEC_USE(THIS_MODULE);
+		return NULL;
+	}
+
+	ctx->tc_ic = ic;
+	if (k->wk_flags & IEEE80211_KEY_SWMIC) {
+#ifdef CONFIG_USE_CRYPTO_API
+		/* XXX safe for concurrent bidirectional use? */
+		ctx->tfm_michael = crypto_alloc_tfm("michael_mic", 0);
+		if (priv->tfm_michael == NULL)
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    ("%s: Warning, no michael_mic crypto API "
+			    "support, using private version." __func__));
+#endif
+	}
+	return ctx;
+}
+
+static void
+tkip_detach(struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+#ifdef CONFIG_USE_CRYPTO_API
+	if (ctx->tfm_michael != NULL)
+		crypto_free_tfm(ctx->tfm_michael);
+#endif
+	FREE(ctx, M_DEVBUF);
+
+	_MOD_DEC_USE(THIS_MODULE);
+}
+
+static int
+tkip_setkey(struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+	if (k->wk_keylen != (128/NBBY)) {
+		IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+			("%s: Invalid key length %u, expecting %u\n",
+			__func__, k->wk_keylen, 128/NBBY));
+		return 0;
+	}
+	k->wk_keytsc = 1;		/* TSC starts at 1 */
+	return 1;
+}
+
+/*
+ * Add privacy headers and do any s/w encryption required.
+ */
+static int
+tkip_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->tc_ic;
+	u_int8_t *ivp;
+	int hdrlen;
+
+	/*
+	 * Handle TKIP counter measures requirement.
+	 */
+	if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+		struct ieee80211_frame *wh =
+			(struct ieee80211_frame *) skb->data;
+
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			("[%s] Discard frame due to countermeasures (%s)\n",
+			ether_sprintf(wh->i_addr2), __func__));
+		ic->ic_stats.is_crypto_tkipcm++;
+		return 0;
+	}
+	hdrlen = ieee80211_hdrsize(skb->data);
+
+	/*
+	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+	 */
+	ivp = skb_push(skb, tkip.ic_header);
+	memmove(ivp, ivp + tkip.ic_header, hdrlen);
+	ivp += hdrlen;
+
+	ivp[0] = k->wk_keytsc >> 8;		/* TSC1 */
+	ivp[1] = (ivp[0] | 0x20) & 0x7f;	/* WEP seed */
+	ivp[2] = k->wk_keytsc >> 0;		/* TSC0 */
+	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
+	ivp[4] = k->wk_keytsc >> 16;		/* TSC2 */
+	ivp[5] = k->wk_keytsc >> 24;		/* TSC3 */
+	ivp[6] = k->wk_keytsc >> 32;		/* TSC4 */
+	ivp[7] = k->wk_keytsc >> 40;		/* TSC5 */
+
+	/*
+	 * Finally, do software encrypt if neeed.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+		if (!tkip_encrypt(ctx, k, skb, hdrlen))
+			return 0;
+		/* NB: tkip_encrypt handles wk_keytsc */
+	} else
+		k->wk_keytsc++;		/* XXX wrap at 48 bits */
+
+	return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+tkip_enmic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	u8 hdr[16];
+	int hdrlen;
+
+	if (k->wk_flags & IEEE80211_KEY_SWMIC) {
+		struct ieee80211_frame *wh =
+			(struct ieee80211_frame *) skb->data;
+		u_int8_t *mic;
+
+		ctx->tc_ic->ic_stats.is_crypto_tkipenmic++;
+
+		if (skb_tailroom(skb) < tkip.ic_miclen) {
+			/* NB: should not happen */
+			IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+				("[%s] No room for Michael MIC, tailroom %u\n",
+				ether_sprintf(wh->i_addr1), skb_tailroom(skb)));
+			/* XXX statistic */
+			return 0;
+		}
+
+		hdrlen = ieee80211_hdrsize(wh);
+
+		michael_mic_hdr(wh, hdr);
+		mic = skb_put(skb, tkip.ic_miclen);
+		michael_mic(ctx, k->wk_txmic, hdr,
+			skb->data + hdrlen,
+			skb->len - (hdrlen + tkip.ic_miclen),
+			mic);
+	}
+	return 1;
+}
+
+static inline u_int64_t
+READ_6(u_int8_t b0, u_int8_t b1, u_int8_t b2, u_int8_t b3, u_int8_t b4, u_int8_t b5)
+{
+	u_int32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+	u_int16_t iv16 = (b4 << 0) | (b5 << 8);
+	return (((u_int64_t)iv16) << 32) | iv32;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame.  If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+tkip_decap(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->tc_ic;
+	struct ieee80211_frame *wh;
+	u_int8_t *ivp;
+	int hdrlen;
+
+	/*
+	 * Header should have extended IV and sequence number;
+	 * verify the former and validate the latter.
+	 */
+	wh = (struct ieee80211_frame *)skb->data;
+	hdrlen = ieee80211_hdrsize(wh);
+	ivp = skb->data + hdrlen;
+	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+		/*
+		 * No extended IV; discard frame.
+		 */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			("[%s] Missing ExtIV for TKIP cipher\n",
+			ether_sprintf(wh->i_addr2)));
+		ic->ic_stats.is_rx_tkipformat++;
+		return 0;
+	}
+	/*
+	 * Handle TKIP counter measures requirement.
+	 */
+	if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			("[%s] Discard frame due to countermeasures (%s)\n",
+			ether_sprintf(wh->i_addr2), __func__));
+		ic->ic_stats.is_crypto_tkipcm++;
+		return 0;
+	}
+
+	/* NB: assume IEEEE80211_WEP_MINLEN covers the extended IV */ 
+	ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5],
+		ivp[6], ivp[7]);
+	if (ctx->rx_rsc <= k->wk_keyrsc) {
+		/*
+		 * Replay violation; notify upper layer.
+		 */
+		ieee80211_notify_replay_failure(ic, wh, k, ctx->rx_rsc);
+		ic->ic_stats.is_rx_tkipreplay++;
+		return 0;
+	}
+	/*
+	 * NB: We can't update the rsc in the key until MIC is verified.
+	 *
+	 * We assume we are not preempted between doing the check above
+	 * and updating wk_keyrsc when stripping the MIC in tkip_demic.
+	 * Otherwise we might process another packet and discard it as
+	 * a replay.
+	 */
+
+	/*
+	 * Check if the device handled the decrypt in hardware.
+	 * If so we just strip the header; otherwise we need to
+	 * handle the decrypt in software.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !tkip_decrypt(ctx, k, skb, hdrlen))
+		return 0;
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	memmove(skb->data + tkip.ic_header, skb->data, hdrlen);
+	skb_pull(skb, tkip.ic_header);
+	skb_trim(skb, skb->len - tkip.ic_trailer);
+
+	return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+tkip_demic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	u8 hdr[16];
+	u8 mic[IEEE80211_WEP_MICLEN];
+	int hdrlen;
+
+	if (k->wk_flags & IEEE80211_KEY_SWMIC) {
+		struct ieee80211_frame *wh =
+			(struct ieee80211_frame *) skb->data;
+
+		ctx->tc_ic->ic_stats.is_crypto_tkipdemic++;
+
+		hdrlen = ieee80211_hdrsize(wh);
+
+		michael_mic_hdr(wh, hdr);
+		michael_mic(ctx, k->wk_rxmic, hdr,
+			skb->data + hdrlen,
+			skb->len - (hdrlen + tkip.ic_miclen),
+			mic);
+		if (memcmp(mic, skb->data+skb->len - tkip.ic_miclen, tkip.ic_miclen)) {
+			/* NB: 802.11 layer handles statistic and debug msg */
+			ieee80211_notify_michael_failure(ctx->tc_ic, wh,
+				k->wk_keyix);
+			return 0;
+		}
+	}
+	/*
+	 * Strip MIC from the tail.
+	 */
+	skb_trim(skb, skb->len - tkip.ic_miclen);
+
+	/*
+	 * Ok to update rsc now that MIC has been verified.
+	 */
+	k->wk_keyrsc = ctx->rx_rsc;
+
+	return 1;
+}
+
+/*
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ */
+
+static const __u32 crc32_table[256] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
+
+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;
+	}
+}
+
+#ifndef _BYTE_ORDER
+#error "Don't know native byte order"
+#endif
+
+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);
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+	{
+		int i;
+		for (i = 0; i < 6; i++)
+			PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+	}
+#endif
+}
+
+static void
+wep_encrypt(u8 *key, u8 *buf, size_t buflen, u8 *icv)
+{
+	u32 i, j, k, crc;
+	u8 S[256];
+	u8 *pos;
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[i & 0x0f]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Compute CRC32 over unencrypted data and apply RC4 to data */
+	crc = ~0;
+	i = j = 0;
+	pos = buf;
+	for (k = 0; k < buflen; k++) {
+		crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		*pos++ ^= S[(S[i] + S[j]) & 0xff];
+	}
+	crc = ~crc;
+
+	/* Append little-endian CRC32 and encrypt it to produce ICV */
+	pos = icv;
+	pos[0] = crc;
+	pos[1] = crc >> 8;
+	pos[2] = crc >> 16;
+	pos[3] = crc >> 24;
+	for (k = 0; k < 4; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		*pos++ ^= S[(S[i] + S[j]) & 0xff];
+	}
+}
+
+static int
+wep_decrypt(u8 *key, u8 *buf, size_t plen)
+{
+	u32 i, j, k, crc;
+	u8 S[256];
+	u8 *pos, icv[4];
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[i & 0x0f]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Apply RC4 to data and compute CRC32 over decrypted data */
+	pos = buf;
+	crc = ~0;
+	i = j = 0;
+	for (k = 0; k < plen; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		*pos ^= S[(S[i] + S[j]) & 0xff];
+		crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+		pos++;
+	}
+	crc = ~crc;
+
+	/* Encrypt little-endian CRC32 and verify that it matches with the
+	 * received ICV */
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+	for (k = 0; k < 4; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
+			/* ICV mismatch - drop frame */
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static inline u32 rotl(u32 val, int bits)
+{
+	return (val << bits) | (val >> (32 - bits));
+}
+
+
+static inline u32 rotr(u32 val, int bits)
+{
+	return (val >> bits) | (val << (32 - bits));
+}
+
+
+static inline u32 xswap(u32 val)
+{
+	return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
+}
+
+
+#define michael_block(l, r)	\
+do {				\
+	r ^= rotl(l, 17);	\
+	l += r;			\
+	r ^= xswap(l);		\
+	l += r;			\
+	r ^= rotl(l, 3);	\
+	l += r;			\
+	r ^= rotr(l, 2);	\
+	l += r;			\
+} while (0)
+
+
+static inline u32 get_le32(const u8 *p)
+{
+	return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+
+static inline void put_le32(u8 *p, u32 v)
+{
+	p[0] = v;
+	p[1] = v >> 8;
+	p[2] = v >> 16;
+	p[3] = v >> 24;
+}
+
+
+static void michael_mic_private(struct tkip_ctx *ctx, const u8 *key, const u8 hdr[16],
+		       const u8 *data, size_t data_len, u8 mic[IEEE80211_WEP_MICLEN])
+{
+	u32 l, r;
+	int i, blocks, last;
+
+	l = get_le32(key);
+	r = get_le32(key + 4);
+
+	/* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+	l ^= get_le32(hdr);
+	michael_block(l, r);
+	l ^= get_le32(&hdr[4]);
+	michael_block(l, r);
+	l ^= get_le32(&hdr[8]);
+	michael_block(l, r);
+	l ^= get_le32(&hdr[12]);
+	michael_block(l, r);
+
+	/* 32-bit blocks of data */
+	blocks = data_len / 4;
+	last = data_len % 4;
+	for (i = 0; i < blocks; i++) {
+		l ^= get_le32(&data[4 * i]);
+		michael_block(l, r);
+	}
+
+	/* Last block and padding (0x5a, 4..7 x 0) */
+	switch (last) {
+	case 0:
+		l ^= 0x5a;
+		break;
+	case 1:
+		l ^= data[4 * i] | 0x5a00;
+		break;
+	case 2:
+		l ^= data[4 * i] | (data[4 * i + 1] << 8) | 0x5a0000;
+		break;
+	case 3:
+		l ^= data[4 * i] | (data[4 * i + 1] << 8) |
+			(data[4 * i + 2] << 16) | 0x5a000000;
+		break;
+	}
+	michael_block(l, r);
+	/* l ^= 0; */
+	michael_block(l, r);
+
+	put_le32(mic, l);
+	put_le32(mic + 4, r);
+}
+
+static void
+michael_mic(struct tkip_ctx *ctx, const u8 *key, const u8 hdr[16],
+		       const u8 *data, size_t data_len, u8 mic[IEEE80211_WEP_MICLEN])
+{
+#ifdef CONFIG_USE_CRYPTO_API
+	if (ctx->tfm_michael != NULL) {
+		struct scatterlist sg[2];
+
+		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(ctx->tfm_michael);
+		crypto_digest_setkey(ctx->tfm_michael, key, 8);
+		crypto_digest_update(ctx->tfm_michael, sg, 2);
+		crypto_digest_final(ctx->tfm_michael, mic);
+	} else
+#endif /* CONFIG_USE_CRYPTO_API */
+		michael_mic_private(ctx, key, hdr, data, data_len, mic);
+}
+
+/*
+ * Craft pseudo header used to calculate the MIC.
+ */
+static void
+michael_mic_hdr(const struct ieee80211_frame *wh0, u8 hdr[16])
+{
+	const struct ieee80211_frame_addr4 *wh =
+		(struct ieee80211_frame_addr4 *) wh0;
+
+	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+	case IEEE80211_FC1_DIR_NODS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
+		break;
+	case IEEE80211_FC1_DIR_TODS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
+		break;
+	case IEEE80211_FC1_DIR_FROMDS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr3);
+		break;
+	case IEEE80211_FC1_DIR_DSTODS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr4);
+		break;
+	}
+
+	hdr[12] = 0; /* XXX qos priority */
+	hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+static int
+tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
+	struct sk_buff *skb, int hdrlen)
+{
+	struct ieee80211_frame *wh;
+	u8 *icv;
+
+	ctx->tc_ic->ic_stats.is_crypto_tkip++;
+
+	wh = (struct ieee80211_frame *) skb->data;
+	if (skb_tailroom(skb) < tkip.ic_trailer) {
+		/* NB: should not happen */
+		IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+			("[%s] No room for %s ICV, tailroom %u\n",
+			ether_sprintf(wh->i_addr1), tkip.ic_name,
+			skb_tailroom(skb)));
+		/* XXX statistic */
+		return 0;
+	}
+	if (!ctx->tx_phase1_done) {
+		tkip_mixing_phase1(ctx->tx_ttak, key->wk_key, wh->i_addr2,
+				   (u32)(key->wk_keytsc >> 16));
+		ctx->tx_phase1_done = 1;
+	}
+	tkip_mixing_phase2(ctx->tx_rc4key, key->wk_key, ctx->tx_ttak,
+		(u16) key->wk_keytsc);
+
+	icv = skb_put(skb, tkip.ic_trailer);
+	wep_encrypt(ctx->tx_rc4key,
+		skb->data + hdrlen + tkip.ic_header,
+		skb->len - (hdrlen + tkip.ic_header + tkip.ic_trailer),
+		icv);
+
+	key->wk_keytsc++;		/* XXX wrap at 48 bits */
+	if ((u16)(key->wk_keytsc) == 0)
+		ctx->tx_phase1_done = 0;
+	return 1;
+}
+
+static int
+tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
+	struct sk_buff *skb, int hdrlen)
+{
+	struct ieee80211_frame *wh;
+	u32 iv32;
+	u16 iv16;
+
+	ctx->tc_ic->ic_stats.is_crypto_tkip++;
+
+	wh = (struct ieee80211_frame *) skb->data;
+	/* NB: tkip_decap already verified header and left seq in rx_rsc */
+	iv16 = (u16) ctx->rx_rsc;
+	iv32 = (u32) (ctx->rx_rsc >> 16);
+
+	if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) {
+		tkip_mixing_phase1(ctx->rx_ttak, key->wk_key,
+			wh->i_addr2, iv32);
+		ctx->rx_phase1_done = 1;
+	}
+	tkip_mixing_phase2(ctx->rx_rc4key, key->wk_key, ctx->rx_ttak, iv16);
+
+	/* NB: skb is unstripped; deduct headers + ICV to get payload */
+	if (wep_decrypt(ctx->rx_rc4key,
+		skb->data + hdrlen + tkip.ic_header,
+	        skb->len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) {
+		if (iv32 != (u32)(key->wk_keyrsc >> 16)) {
+			/* Previously cached Phase1 result was already lost, so
+			 * it needs to be recalculated for the next packet. */
+			ctx->rx_phase1_done = 0;
+		}
+		IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+		    ("[%s] TKIP ICV mismatch on decrypt\n",
+		    ether_sprintf(wh->i_addr2)));
+		ctx->tc_ic->ic_stats.is_rx_tkipicv++;
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: TKIP cipher");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static int __init
+init_crypto_tkip(void)
+{
+	ieee80211_crypto_register(&tkip);
+	return 0;
+}
+module_init(init_crypto_tkip);
+
+static void __exit
+exit_crypto_tkip(void)
+{
+	ieee80211_crypto_unregister(&tkip);
+}
+module_exit(exit_crypto_tkip);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_wep.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_wep.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_wep.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_wep.c	2005-02-24 13:06:17.303131544 -0800
@@ -0,0 +1,407 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * IEEE 802.11 WEP crypto support.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/init.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+#include "rc4.h"
+#define	arc4_ctxlen()			sizeof (struct rc4_state)
+#define	arc4_setkey(_c,_k,_l)		rc4_init(_c,_k,_l)
+#define	arc4_encrypt(_c,_d,_s,_l)	rc4_crypt_skip(_c,_s,_d,_l,0)
+#define	arc4_decrypt(_c,_d,_s,_l)	rc4_crypt_skip(_c,_s,_d,_l,0)
+
+static	void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void wep_detach(struct ieee80211_key *);
+static	int wep_setkey(struct ieee80211_key *);
+static	int wep_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t keyid);
+static	int wep_decap(struct ieee80211_key *, struct sk_buff *);
+static	int wep_enmic(struct ieee80211_key *, struct sk_buff *);
+static	int wep_demic(struct ieee80211_key *, struct sk_buff *);
+
+static const struct ieee80211_cipher wep = {
+	.ic_name	= "WEP",
+	.ic_cipher	= IEEE80211_CIPHER_WEP,
+	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
+	.ic_trailer	= IEEE80211_WEP_CRCLEN,
+	.ic_miclen	= 0,
+	.ic_attach	= wep_attach,
+	.ic_detach	= wep_detach,
+	.ic_setkey	= wep_setkey,
+	.ic_encap	= wep_encap,
+	.ic_decap	= wep_decap,
+	.ic_enmic	= wep_enmic,
+	.ic_demic	= wep_demic,
+};
+
+static	int wep_encrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen);
+static	int wep_decrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen);
+
+struct wep_ctx_hw {			/* for use with h/w support */
+	struct ieee80211com *wc_ic;	/* for diagnostics */
+	u_int32_t	wc_iv;		/* initial vector for crypto */
+};
+
+struct wep_ctx {
+	struct ieee80211com *wc_ic;	/* for diagnostics */
+	u_int32_t	wc_iv;		/* initial vector for crypto */
+	struct rc4_state wc_rc4;	/* rc4 state */
+};
+
+static void *
+wep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct wep_ctx *ctx;
+
+	_MOD_INC_USE(THIS_MODULE, return NULL);
+
+	/* NB: only allocate rc4 state when doing s/w crypto */
+	MALLOC(ctx, struct wep_ctx *,
+		k->wk_flags & IEEE80211_KEY_SWCRYPT ?
+			sizeof(struct wep_ctx) : sizeof(struct wep_ctx_hw),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ctx == NULL) {
+		ic->ic_stats.is_crypto_nomem++;
+		_MOD_DEC_USE(THIS_MODULE);
+		return NULL;
+	}
+
+	ctx->wc_ic = ic;
+	get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
+	return ctx;
+}
+
+static void
+wep_detach(struct ieee80211_key *k)
+{
+	struct wep_ctx *ctx = k->wk_private;
+
+	FREE(ctx, M_DEVBUF);
+
+	_MOD_DEC_USE(THIS_MODULE);
+}
+
+static int
+wep_setkey(struct ieee80211_key *k)
+{
+	return k->wk_keylen >= 40/NBBY;
+}
+
+#ifndef _BYTE_ORDER
+#error "Don't know native byte order"
+#endif
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+wep_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid)
+{
+	struct wep_ctx *ctx = k->wk_private;
+	u_int32_t iv;
+	u_int8_t *ivp;
+	int hdrlen;
+
+	hdrlen = ieee80211_hdrsize(skb->data);
+
+	/*
+	 * Copy down 802.11 header and add the IV + KeyID.
+	 */
+	ivp = skb_push(skb, wep.ic_header);
+	memmove(ivp, ivp + wep.ic_header, hdrlen);
+	ivp += hdrlen;
+
+	/*
+	 * XXX
+	 * IV must not duplicate during the lifetime of the key.
+	 * But no mechanism to renew keys is defined in IEEE 802.11
+	 * for WEP.  And the IV may be duplicated at other stations
+	 * because the session key itself is shared.  So we use a
+	 * pseudo random IV for now, though it is not the right way.
+	 *
+	 * NB: Rather than use a strictly random IV we select a
+	 * random one to start and then increment the value for
+	 * each frame.  This is an explicit tradeoff between
+	 * overhead and security.  Given the basic insecurity of
+	 * WEP this seems worthwhile.
+	 */
+
+	/*
+	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
+	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
+	 */
+	iv = ctx->wc_iv;
+	if ((iv & 0xff00) == 0xff00) {
+		int B = (iv & 0xff0000) >> 16;
+		if (3 <= B && B < 16)
+			iv += 0x0100;
+	}
+	ctx->wc_iv = iv + 1;
+
+	/*
+	 * NB: Preserve byte order of IV for packet
+	 *     sniffers; it doesn't matter otherwise.
+	 */
+#if _BYTE_ORDER == _BIG_ENDIAN
+	ivp[0] = iv >> 0;
+	ivp[1] = iv >> 8;
+	ivp[2] = iv >> 16;
+#else
+	ivp[2] = iv >> 0;
+	ivp[1] = iv >> 8;
+	ivp[0] = iv >> 16;
+#endif
+	ivp[3] = keyid;
+
+	/*
+	 * Finally, do software encrypt if neeed.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !wep_encrypt(k, skb, hdrlen))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+wep_enmic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+
+	return 1;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame.  If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+wep_decap(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	struct wep_ctx *ctx = k->wk_private;
+	struct ieee80211_frame *wh;
+	int hdrlen;
+
+	wh = (struct ieee80211_frame *)skb->data;
+	hdrlen = ieee80211_hdrsize(wh);
+
+	/*
+	 * Check if the device handled the decrypt in hardware.
+	 * If so we just strip the header; otherwise we need to
+	 * handle the decrypt in software.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !wep_decrypt(k, skb, hdrlen)) {
+		IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
+		    ("[%s] WEP ICV mismatch on decrypt\n",
+		    ether_sprintf(wh->i_addr2)));
+		ctx->wc_ic->ic_stats.is_rx_wepfail++;
+		return 0;
+	}
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	memmove(skb->data + wep.ic_header, skb->data, hdrlen);
+	skb_pull(skb, wep.ic_header);
+	skb_trim(skb, skb->len - wep.ic_trailer);
+
+	return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+wep_demic(struct ieee80211_key *k, struct sk_buff *skb)
+{
+	return 1;
+}
+
+/*
+ * CRC 32 -- routine from RFC 2083
+ */
+
+/* Table of CRCs of all 8-bit messages */
+static u_int32_t crc_table[256];
+
+/* Make the table for a fast CRC. */
+static void
+crc_init(void)
+{
+	u_int32_t c;
+	int n, k;
+
+	for (n = 0; n < 256; n++) {
+		c = (u_int32_t)n;
+		for (k = 0; k < 8; k++) {
+			if (c & 1)
+				c = 0xedb88320UL ^ (c >> 1);
+			else
+				c = c >> 1;
+		}
+		crc_table[n] = c;
+	}
+}
+
+/*
+ * Update a running CRC with the bytes buf[0..len-1]--the CRC
+ * should be initialized to all 1's, and the transmitted value
+ * is the 1's complement of the final running CRC
+ */
+
+static u_int32_t
+crc_update(u_int32_t crc, u_int8_t *buf, int len)
+{
+	u_int8_t *endbuf;
+
+	for (endbuf = buf + len; buf < endbuf; buf++)
+		crc = crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+	return crc;
+}
+
+static int
+wep_encrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen)
+{
+	struct wep_ctx *ctx = key->wk_private;
+	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+	u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
+	u_int8_t *icv;
+	u_int32_t crc;
+
+	KASSERT(key->wk_flags & IEEE80211_KEY_SWCRYPT, ("No s/w crypto state"));
+
+	ctx->wc_ic->ic_stats.is_crypto_wep++;
+
+	if (skb_tailroom(skb) < wep.ic_trailer) {
+		struct ieee80211_frame *wh =
+			(struct ieee80211_frame *) skb->data;
+		/* NB: should not happen */
+		IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
+			("[%s] No room for %s ICV, tailroom %u\n",
+			ether_sprintf(wh->i_addr1), wep.ic_name,
+			skb_tailroom(skb)));
+		/* XXX statistic */
+		return 0;
+	}
+	memcpy(rc4key, skb->data + hdrlen, IEEE80211_WEP_IVLEN);
+	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
+	arc4_setkey(&ctx->wc_rc4, rc4key, IEEE80211_WEP_IVLEN + key->wk_keylen);
+
+	/* calculate CRC over unencrypted data */
+	crc = crc_update(~0,
+		skb->data + hdrlen + wep.ic_header,
+		skb->len - (hdrlen + wep.ic_header));
+	/* encrypt data */
+	arc4_encrypt(&ctx->wc_rc4,
+		skb->data + hdrlen + wep.ic_header,
+		skb->data + hdrlen + wep.ic_header,
+		skb->len - (hdrlen + wep.ic_header));
+	/* tack on ICV */
+	*(u_int32_t *)crcbuf = htole32(~crc);
+	icv = skb_put(skb, IEEE80211_WEP_CRCLEN);
+	arc4_encrypt(&ctx->wc_rc4, icv, crcbuf, IEEE80211_WEP_CRCLEN);
+
+	return 1;
+}
+
+static int
+wep_decrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen)
+{
+	struct wep_ctx *ctx = key->wk_private;
+	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+	u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
+	u_int8_t *icv;
+	u_int32_t crc;
+
+	KASSERT(key->wk_flags & IEEE80211_KEY_SWCRYPT, ("No s/w crypto state"));
+
+	ctx->wc_ic->ic_stats.is_crypto_wep++;
+
+	memcpy(rc4key, skb->data + hdrlen, IEEE80211_WEP_IVLEN);
+	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
+	arc4_setkey(&ctx->wc_rc4, rc4key, IEEE80211_WEP_IVLEN + key->wk_keylen);
+
+	/* decrypt data */
+	arc4_decrypt(&ctx->wc_rc4,
+		skb->data + hdrlen + wep.ic_header,
+		skb->data + hdrlen + wep.ic_header,
+		skb->len - (hdrlen + wep.ic_header + wep.ic_trailer));
+	/* calculate CRC over unencrypted data */
+	crc = crc_update(~0,
+		skb->data + hdrlen + wep.ic_header,
+		skb->len - (hdrlen + wep.ic_header + wep.ic_trailer));
+	/* decrypt ICV and compare to CRC */
+	icv = skb->data + (skb->len - IEEE80211_WEP_CRCLEN);
+	arc4_decrypt(&ctx->wc_rc4, crcbuf, icv, IEEE80211_WEP_CRCLEN);
+
+	return (crc == ~le32toh(*(u_int32_t *)crcbuf));
+}
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: WEP cipher");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static int __init
+init_crypto_wep(void)
+{
+	ieee80211_crypto_register(&wep);
+	crc_init();
+	return 0;
+}
+module_init(init_crypto_wep);
+
+static void __exit
+exit_crypto_wep(void)
+{
+	ieee80211_crypto_unregister(&wep);
+}
+module_exit(exit_crypto_wep);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.c	2005-02-24 13:06:17.304131392 -0800
@@ -0,0 +1,1799 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+/*
+ * 802.1x+WPA authenticator protocol handling and state machine.
+ *
+ * This support is optional; it is only used when the 802.11 layer's
+ * authentication mode is set to use 802.1x or WPA is enabled separately
+ * (for WPA-PSK).  If compiled as a module this code does not need
+ * to be present unless 802.1x/WPA-PSK is in use.
+ *
+ * The authenticator hooks into the 802.11 layer through callbacks
+ * that are invoked when stations join and leave (associate and
+ * deassociate).  Authentication state is managed separately from the
+ * 802.11 layer's node state.  State is synchronized with a single
+ * lock.  This scheme is also used by the optional radius client.
+ *
+ * It might be possible to generalize the 802.1x support to handle
+ * non-802.11 devices but for now it is tightly integrated with the
+ * 802.11 code.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/sysctl.h>
+#include <linux/in.h>
+
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include <linux/random.h>
+
+#include "if_media.h"
+#include "if_llc.h"
+#include "if_ethersubr.h"
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_dot1x.h>
+#include <net80211/ieee80211_radius.h>
+
+#ifndef TRUE
+enum { TRUE = 1, FALSE = 0 };		/* for consistency with spec */
+#endif
+
+/*
+ * Global settings that can be modified with sysctl's.
+ */
+u_int	eapol_idletimo = 30;		/* 30 seconds */
+u_int	eapol_reauthlimit = 5;
+u_int	eapol_reauthmin = 60;		/* 60 seconds */
+u_int	eapol_reauthtimo = 5*60;	/* 5 minutes */
+u_int	eapol_txtimo = 3;		/* 3 seconds */
+u_int	eapol_supptimo = 30;		/* 3O seconds */
+u_int	eapol_servtimo = 3;		/* 3 seconds */
+u_int	eapol_suppreqlimit = 2;
+u_int	eapol_servreqlimit = 2;
+
+u_int	eapol_keytxenabled = TRUE;	/* ena/dis EAPOL key trasmission */
+u_int	eapol_reauthenabled = TRUE;	/* ena/dis reauthentication */
+
+#ifdef IEEE80211_DEBUG
+static const char *eapol_as_states[] = {
+	"EAPOL_AS_INIT",
+	"EAPOL_AS_DISCONNECTED",
+	"EAPOL_AS_CONNECTING",
+	"EAPOL_AS_AUTHENTICATING",
+	"EAPOL_AS_AUTHENTICATED",
+	"EAPOL_AS_ABORTING",
+	"EAPOL_AS_HELD",
+	"EAPOL_AS_FORCE_AUTH",
+	"EAPOL_AS_FORCE_UNAUTH",
+};
+static const char *eapol_abs_states[] = {
+	"EAPOL_ABS_INIT",
+	"EAPOL_ABS_IDLE",
+	"EAPOL_ABS_REQUEST",
+	"EAPOL_ABS_RESPONSE",
+	"EAPOL_ABS_SUCCESS",
+	"EAPOL_ABS_FAIL",
+	"EAPOL_ABS_TIMEOUT",
+};
+static const char *eapol_aks_states[] = {
+	"EAPOL_AKS_NO_KEY",
+	"EAPOL_AKS_KEY",
+};
+static const char *eapol_ars_states[] = {
+	"EAPOL_ARS_INIT",
+	"EAPOL_ARS_REAUTH",
+};
+#endif /* IEEE80211_DEBUG */
+
+MALLOC_DEFINE(M_EAPOL_NODE, "node", "802.1x node state");
+
+struct eapolcom *eapolcom = NULL;	/* ``the'' authenticator */
+struct eapolstats eapolstats = { 0 };
+EXPORT_SYMBOL(eapolstats);
+
+static	struct eapol_auth_node *eapol_new_node(struct eapolcom *,
+		struct ieee80211com *, struct ieee80211_node *);
+static	void txCannedFail(struct eapol_auth_node *);
+static	void txCannedSuccess(struct eapol_auth_node *);
+static	void txReqId(struct eapol_auth_node *);
+static	void eap_send_simple(struct eapol_node *, u_int8_t code, u_int8_t id);
+
+/*
+ * Input handling (messages from the supplicant).
+ */
+
+/*
+ * Process an EAP frame; we're only interested in
+ * EAP Response messages.
+ */
+static int
+eapol_auth_input_eap(struct eapol_auth_node *ean, struct eap_hdr *eap,
+	struct sk_buff *skb)
+{
+	struct eapolcom *ec;
+
+	EAPOL_LOCK_ASSERT(ean->ean_ec);
+
+	if (eap->eap_id != ean->ean_currentId) {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+			("[%s] EAP id mismatch, got %u, expecting %u\n",
+			ether_sprintf(ean->ean_node->ni_macaddr),
+			eap->eap_id, ean->ean_currentId));
+		eapolstats.eas_idmismatch++;
+		goto out;
+	}
+	if (eap->eap_code != EAP_CODE_RESPONSE) {
+		/* unexpected response from station */
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+			("[%s] unexpected EAP code %u\n",
+			ether_sprintf(ean->ean_node->ni_macaddr),
+			eap->eap_code));
+		eapolstats.eas_badcode++;
+		goto out;
+	}
+	ec = ean->ean_ec;
+	if (eap->eap_type == EAP_TYPE_IDENTITY) {
+		/*
+		 * Record identity string.
+		 */
+		/* NB: known to be in host byte order */
+		if (eap->eap_len > sizeof(struct eap_hdr)+EAP_IDENTITY_MAXLEN) {
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+				("[%s] EAP Identity len %u too big\n",
+				ether_sprintf(ean->ean_node->ni_macaddr),
+				eap->eap_len));
+			eapolstats.eas_badidlen++;
+			goto out;
+		}
+		ean->ean_idlen = eap->eap_len - sizeof(struct eap_hdr);
+		memcpy(ean->ean_id, &eap[1], ean->ean_idlen);
+		ean->ean_id[ean->ean_idlen] = '\0';
+		ean->ean_rxRespId = TRUE;
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+			("[%s] RECV EAP Identity \"%s\"\n",
+			ether_sprintf(ean->ean_node->ni_macaddr),
+			ean->ean_id));
+		/*
+		 * Let radius client reset it's state.
+		 */
+		if (ec->ec_radius)
+			(*ec->ec_radius->rc_identity_input)(ean, skb);
+	} else {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+			("[%s] RECV EAP msg type %u len %u\n",
+			ether_sprintf(ean->ean_node->ni_macaddr),
+			eap->eap_type, eap->eap_len));
+		if (eap->eap_type != EAP_TYPE_NAK)
+			ean->ean_backendNonNakResponsesForSupplicant++;
+		ean->ean_rxResp = TRUE;
+	}
+	/*
+	 * Save the frame contents for later.
+	 */
+	if (ean->ean_skb)
+		kfree_skb(ean->ean_skb);
+	ean->ean_skb = skb;
+
+	eapol_fsm_run(ean);
+	return 0;
+out:
+	kfree_skb(skb);
+	return 0;
+}
+
+/*
+ * Process an EAPOL Key frame.
+ */
+static int
+eapol_auth_input_key(struct eapol_auth_node *ean, struct eapol_hdr *eapol,
+	struct sk_buff *skb)
+{
+
+	EAPOL_LOCK_ASSERT(ean->ean_ec);
+
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+		("[%s] RECV EAPOL WPA-Key\n",
+		ether_sprintf(ean->ean_node->ni_macaddr)));
+
+	kfree_skb(skb);
+	return 0;
+}
+
+/*
+ * Receive an EAPOL frame from the device layer.  This
+ * routine is installed as a packet handler for EAPOL
+ * Ethernet frames when the authenticator is attached.
+ */
+int
+eapol_input(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
+{
+	struct eapolcom *ec;
+	struct ieee80211com *ic;
+	struct ieee80211_node *ni;
+	struct eapol_hdr *eapol;
+	struct eapol_auth_node *ean;
+	struct eap_hdr *eap;
+	int error = 0;
+
+	if (eapolcom == NULL) {
+		eapolstats.eps_noauth++;
+		goto out;
+	}
+	ec = eapolcom;
+	if (!pskb_may_pull(skb, sizeof(struct eapol_hdr))) {
+		eapolstats.eps_tooshort++;
+		goto out;
+	}
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (skb == NULL) {
+		eapolstats.eps_sharecheck++;
+		goto out;
+	}
+	eapol = (struct eapol_hdr *)skb->data;
+	(void) skb_pull(skb, sizeof(struct eapol_hdr));
+	/* NB: XXX give netfilter a chance? */
+
+	/*
+	 * NB: The spec says backwards compatibility for packet
+	 *     types is preserved.  So we are safe to accept
+	 *     packets with a version number >= to our version
+	 *     and still be able to correctly process the packets
+	 *     we know/care about.
+	 */
+	if (eapol->eapol_ver < EAPOL_VERSION) {
+		eapolstats.eps_badver++;
+		goto out;
+	}
+	/*
+	 * Find session based on sender's MAC address.
+	 */
+	/* NB: must release reference */
+	ic = ieee80211_find_vap(skb->mac.ethernet->h_dest);
+	if (ic == NULL) {
+		eapolstats.eps_noinstance++;
+		goto out;
+	}
+	ni = ieee80211_find_node(ic, skb->mac.ethernet->h_source);
+	if (ni == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+		    ("[%s] EAPOL msg type %u with no 802.11 node\n",
+		    ether_sprintf(skb->mac.ethernet->h_source),
+		    eapol->eapol_type));
+		eapolstats.eps_nonode++;
+		goto out;
+	}
+	EAPOL_LOCK(ec);
+	ean = eapolcom->ec_table[IEEE80211_NODE_AID(ni)];
+	if (ean == NULL && eapol->eapol_type != EAPOL_TYPE_START) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+		    ("[%s] EAPOL msg type %u with no session\n",
+		    ether_sprintf(ni->ni_macaddr), eapol->eapol_type));
+		eapolstats.eps_nosession++;
+		goto unlock;
+	}
+	/* put length in host byte order */
+	eapol->eapol_len = ntohs(eapol->eapol_len);
+
+	switch (eapol->eapol_type) {
+	case EAPOL_TYPE_EAP:
+		if (!pskb_may_pull(skb, sizeof(struct eap_hdr_short))) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+			    ("[%s] EAP msg too short, eapol len %u\n",
+			    ether_sprintf(ean->ean_node->ni_macaddr),
+			    eapol->eapol_len));
+			eapolstats.eap_tooshort++;
+			goto unlock;
+		}
+		eap = (struct eap_hdr *)skb->data;
+		eap->eap_len = ntohs(eap->eap_len);
+		if (eap->eap_len < eapol->eapol_len) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+			    ("[%s] EAP length invalid, %u < eapol len %u\n",
+			    ether_sprintf(ean->ean_node->ni_macaddr),
+			    eap->eap_len, eapol->eapol_len));
+			eapolstats.eap_lenmismatch++;
+			goto unlock;
+		}
+
+		switch (eap->eap_code) {
+		case EAP_CODE_RESPONSE:
+			if (!pskb_may_pull(skb, sizeof(struct eap_hdr))) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+				    ("[%s] EAP msg too short, len %u\n",
+				    ether_sprintf(ean->ean_node->ni_macaddr),
+				    skb->len));
+				eapolstats.eap_tooshort++;
+				goto unlock;
+			}
+			error = eapol_auth_input_eap(ean, eap, skb);
+			skb = NULL;		/* consumed */
+			break;
+		case EAP_CODE_SUCCESS:
+		case EAP_CODE_FAILURE:
+		case EAP_CODE_REQUEST:
+			/* XXX no need for these */
+			/* fall thru... */
+		default:
+			eapolstats.eap_badcode++;
+			goto unlock;
+		}
+		break;
+	case EAPOL_TYPE_START:
+		if (ean == NULL) {
+			ean = eapol_new_node(eapolcom, ic, ni);
+			if (ean == NULL)
+				goto unlock;
+		}
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+			("[%s] RECV EAPOL Start\n",
+			ether_sprintf(ean->ean_node->ni_macaddr)));
+		ean->ean_eapStart = TRUE;
+		eapol_fsm_run(ean);
+		break;
+	case EAPOL_TYPE_LOGOFF:
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+			("[%s] RECV EAPOL Logoff\n",
+			ether_sprintf(ean->ean_node->ni_macaddr)));
+		ean->ean_eapLogoff = TRUE;
+		eapol_fsm_run(ean);
+		break;
+	case EAPOL_TYPE_KEY:
+		if (!pskb_may_pull(skb, sizeof(struct eapol_wpa_key))) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+			    ("[%s] EAPOL KEY msg too short, eapol len %u\n",
+			    ether_sprintf(ean->ean_node->ni_macaddr),
+			    eapol->eapol_len));
+			eapolstats.eap_keytooshort++;
+			goto unlock;
+		}
+		error = eapol_auth_input_key(ean, eapol, skb);
+		skb = NULL;		/* consumed */
+		eapol_fsm_run(ean);
+		break;
+	case EAPOL_TYPE_EASFA:
+		/* XXX not handled */
+		/* fall thru... */
+	default:
+		eapolstats.eps_badtype++;
+		goto unlock;
+	}
+unlock:
+	/* NB: unref is safe 'cuz ean holds a ref */
+	ieee80211_unref_node(&ni);
+	EAPOL_UNLOCK(ec);
+out:
+	if (skb != NULL)
+		kfree_skb(skb);
+	return error;
+}
+
+/*
+ * 802.1x+WPA state machine support.  We follow the 802.1x and
+ * WPA specs by defining independent state machines for the
+ * supplicant-authenticator, authenticator-backend, reauthentication
+ * timer, and key transmit handling.  These machines are ``clocked''
+ * together when various events take place (receipt of EAPOL messages
+ * from the supplicant, radius messages from the backened, timers, etc.)
+ */
+
+#define	STATE_DECL(type,state) \
+	static void eapol_auth_##type##_##state(struct eapol_auth_node *ean)
+#define	STATE_ENTER(type,state, ean)	eapol_auth_##type##_##state(ean)
+
+/*
+ * Authenticator state machine.
+ */
+#define	AS_STATE_DECL(s)	STATE_DECL(AS,s)
+#define	AS_STATE_ENTER(s,ean)	STATE_ENTER(AS,s,ean)
+#ifdef IEEE80211_DEBUG
+#define	AS_STATE_DEBUG(s,ean)					\
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM,	\
+		("[%s] %s -> %s\n",				\
+		ether_sprintf(ean->ean_node->ni_macaddr),	\
+		eapol_as_states[ean->ean_authState],		\
+		eapol_as_states[EAPOL_AS_##s]))
+#define	AS_STATE_OPT_DEBUG(s,ean) do {				\
+	if (ean->ean_authState != EAPOL_AS_##s)			\
+		AS_STATE_DEBUG(s,ean);				\
+} while (0)
+#else
+#define	AS_STATE_DEBUG(s,ean)
+#define	AS_STATE_OPT_DEBUG(s,ean)
+#endif
+
+AS_STATE_DECL(INIT)
+{
+	struct eapolcom *ec = ean->ean_ec;
+
+	AS_STATE_OPT_DEBUG(INIT, ean);
+
+	(*ec->ec_node_reset)(ean);	/* reset non-fsm state */
+	ean->ean_currentId = 0;
+	ean->ean_portMode = EAPOL_PORTMODE_AUTO;
+	ean->ean_authState = EAPOL_AS_INIT;
+}
+
+AS_STATE_DECL(DISCONNECTED)
+{
+	AS_STATE_DEBUG(DISCONNECTED, ean);
+
+	ean->ean_portStatus = EAPOL_PORTSTATUS_UNAUTH;
+	ieee80211_node_unauthorize(ean->ean_ic, ean->ean_node);
+
+	ean->ean_eapLogoff = FALSE;
+	ean->ean_reAuthCount = 0;
+	if (ean->ean_authState != EAPOL_AS_INIT) {
+		txCannedFail(ean);
+		ean->ean_currentId++;
+	}
+	ean->ean_authState = EAPOL_AS_DISCONNECTED;
+	/* XXX remove station keys */
+}
+
+AS_STATE_DECL(HELD)
+{
+
+	AS_STATE_DEBUG(HELD, ean);
+
+	ean->ean_portStatus = EAPOL_PORTSTATUS_UNAUTH;
+	ieee80211_node_unauthorize(ean->ean_ic, ean->ean_node);
+
+	ean->ean_quietWhile = eapol_idletimo;
+	ean->ean_eapLogoff = FALSE;
+	ean->ean_currentId++;
+	ean->ean_authState = EAPOL_AS_HELD;
+}
+
+AS_STATE_DECL(CONNECTING)
+{
+
+	AS_STATE_DEBUG(CONNECTING, ean);
+
+	ean->ean_eapStart = FALSE;
+	ean->ean_reAuthenticate = FALSE;
+	ean->ean_txWhen = eapol_txtimo;
+	ean->ean_rxRespId = FALSE;
+	txReqId(ean);
+	ean->ean_reAuthCount++;
+	ean->ean_authState = EAPOL_AS_CONNECTING;
+}
+
+AS_STATE_DECL(AUTHENTICATED)
+{
+	AS_STATE_DEBUG(AUTHENTICATED, ean);
+
+	ean->ean_portStatus = EAPOL_PORTSTATUS_AUTH;
+	ieee80211_node_authorize(ean->ean_ic, ean->ean_node);
+
+	ean->ean_reAuthCount = 0;
+	ean->ean_currentId++;
+	ean->ean_authState = EAPOL_AS_AUTHENTICATED;
+}
+
+AS_STATE_DECL(AUTHENTICATING)
+{
+	AS_STATE_DEBUG(AUTHENTICATING, ean);
+
+	ean->ean_authSuccess = FALSE;
+	ean->ean_authFail = FALSE;
+	ean->ean_authTimeout = FALSE;
+	ean->ean_authStart = TRUE;
+	ean->ean_authState = EAPOL_AS_AUTHENTICATING;
+}
+
+AS_STATE_DECL(ABORTING)
+{
+	AS_STATE_DEBUG(ABORTING, ean);
+
+	ean->ean_currentId++;
+	ean->ean_authAbort = TRUE;
+	ean->ean_authState = EAPOL_AS_ABORTING;
+}
+
+AS_STATE_DECL(FORCE_AUTH)
+{
+	AS_STATE_DEBUG(FORCE_AUTH, ean);
+
+	ean->ean_portStatus = EAPOL_PORTSTATUS_AUTH;
+	ieee80211_node_authorize(ean->ean_ic, ean->ean_node);
+
+	ean->ean_portMode = EAPOL_PORTMODE_FORCEAUTH;
+	ean->ean_eapStart = FALSE;
+	txCannedSuccess(ean);
+	ean->ean_currentId++;
+	ean->ean_authState = EAPOL_AS_FORCE_AUTH;
+}
+
+AS_STATE_DECL(FORCE_UNAUTH)
+{
+	AS_STATE_DEBUG(FORCE_UNAUTH, ean);
+
+	ean->ean_portStatus = EAPOL_PORTSTATUS_UNAUTH;
+	ieee80211_node_unauthorize(ean->ean_ic, ean->ean_node);
+
+	ean->ean_portMode = EAPOL_PORTMODE_FORCEUNAUTH;
+	ean->ean_eapStart = FALSE;
+	txCannedFail(ean);
+	ean->ean_currentId++;
+	ean->ean_authState = EAPOL_AS_FORCE_UNAUTH;
+}
+
+/*
+ * Carry out a state transition in the authentication
+ * state machine. This routine encapsulates Figure 8-8
+ * in the spec.
+ */
+static void
+eapol_auth_step(struct eapol_auth_node *ean)
+{
+
+	EAPOL_LOCK_ASSERT(ean->ean_ec);
+
+	/* NB: portEnabled is implicit */
+	if ((ean->ean_portControl == EAPOL_PORTCONTROL_AUTO &&
+	    ean->ean_portMode != ean->ean_portControl) ||
+	    ean->ean_initialize) {
+		AS_STATE_ENTER(INIT, ean);
+		return;
+	}
+	switch (ean->ean_authState) {
+	case EAPOL_AS_INIT:
+		AS_STATE_ENTER(DISCONNECTED, ean);
+		break;
+	case EAPOL_AS_DISCONNECTED:
+		AS_STATE_ENTER(CONNECTING, ean);
+		break;
+	case EAPOL_AS_HELD:
+		if (ean->ean_quietWhile == 0)
+			AS_STATE_ENTER(CONNECTING, ean);
+		break;
+	case EAPOL_AS_CONNECTING:
+		if (ean->ean_reAuthCount <= eapol_reauthlimit) {
+			if (ean->ean_rxRespId) {
+				ean->ean_txWhen = 0;
+				ean->ean_authEntersAuthenticating++;
+				AS_STATE_ENTER(AUTHENTICATING, ean);
+			} else if (ean->ean_txWhen == 0 || ean->ean_eapStart ||
+			    ean->ean_reAuthenticate) {
+				AS_STATE_ENTER(CONNECTING, ean);
+			} else if (ean->ean_eapLogoff) {
+				AS_STATE_ENTER(DISCONNECTED, ean);
+			}
+		} else {
+			/*
+			 * Forcibly deauthenticate station and reset
+			 * state.  The spec says we go back to a
+			 * DISCONNECTED state but the station is no
+			 * longer associated at this point so we do
+			 * not maintain state.
+			 */
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM,
+				("[%s] reauth limit reached, deauth station\n",
+				ether_sprintf(ean->ean_node->ni_macaddr)));
+			IEEE80211_SEND_MGMT(ean->ean_ic, ean->ean_node,
+				IEEE80211_FC0_SUBTYPE_DEAUTH,
+				IEEE80211_REASON_AUTH_EXPIRE);
+			ean->ean_gone = TRUE;
+		}
+		break;
+	case EAPOL_AS_AUTHENTICATED:
+		if (ean->ean_eapStart || ean->ean_reAuthenticate) {
+			if (ean->ean_eapStart)
+				ean->ean_authAuthEapStartsWhileAuthenticated++;
+			if (ean->ean_reAuthenticate)
+				ean->ean_authAuthReauthsWhileAuthenticated++;
+			AS_STATE_ENTER(CONNECTING, ean);
+		} else if (ean->ean_eapLogoff) {
+			ean->ean_authAuthEapLogoffWhileAuthenticated++;
+			AS_STATE_ENTER(DISCONNECTED, ean);
+		}
+		break;
+	case EAPOL_AS_AUTHENTICATING:
+		if (ean->ean_authSuccess) {
+			ean->ean_authAuthSuccessesWhileAuthenticating++;
+			AS_STATE_ENTER(AUTHENTICATED, ean);
+		} else if (ean->ean_authFail) {
+			ean->ean_authAuthFailWhileAuthenticating++;
+			AS_STATE_ENTER(HELD, ean);
+		} else if (ean->ean_reAuthenticate || ean->ean_eapStart ||
+		    ean->ean_eapLogoff || ean->ean_authTimeout) {
+			if (ean->ean_reAuthenticate)
+				ean->ean_authAuthReauthsWhileAuthenticating++;
+			if (ean->ean_eapStart)
+				ean->ean_authAuthEapStartsWhileAuthenticating++;
+			if (ean->ean_eapLogoff)
+				ean->ean_authAuthEapLogoffWhileAuthenticating++;
+			if (ean->ean_authTimeout)
+				ean->ean_authAuthTimeoutsWhileAuthenticating++;
+			AS_STATE_ENTER(ABORTING, ean);
+		}
+		break;
+	case EAPOL_AS_ABORTING:
+		if (ean->ean_eapLogoff && !ean->ean_authAbort)
+			AS_STATE_ENTER(DISCONNECTED, ean);
+		else if (!ean->ean_eapLogoff && !ean->ean_authAbort)
+			AS_STATE_ENTER(CONNECTING, ean);
+		break;
+	case EAPOL_AS_FORCE_AUTH:
+		if (ean->ean_eapStart)
+			AS_STATE_ENTER(FORCE_AUTH, ean);
+		break;
+	case EAPOL_AS_FORCE_UNAUTH:
+		if (ean->ean_eapStart)
+			AS_STATE_ENTER(FORCE_UNAUTH, ean);
+		break;
+	default:
+		eapolstats.eps_badauthfsm++;
+		break;
+	}
+}
+#undef AS_STATE_DECL
+#undef AS_STATE_ENTER
+#undef AS_STATE_DEBUG
+
+/*
+ * Backend state machine.
+ */
+#define	ABS_STATE_DECL(s)	STATE_DECL(ABS,s)
+#define	ABS_STATE_ENTER(s,ean)	STATE_ENTER(ABS,s,ean)
+#ifdef IEEE80211_DEBUG
+#define	ABS_STATE_DEBUG(s,ean)					\
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM,	\
+		("[%s] %s -> %s\n",				\
+		ether_sprintf(ean->ean_node->ni_macaddr),	\
+		eapol_abs_states[ean->ean_backendState],	\
+		eapol_abs_states[EAPOL_ABS_##s]))
+#define	ABS_STATE_OPT_DEBUG(s,ean) do {				\
+	if (ean->ean_backendState != EAPOL_ABS_##s)		\
+		ABS_STATE_DEBUG(s,ean);				\
+} while (0)
+#else
+#define	ABS_STATE_DEBUG(s,ean)
+#define	ABS_STATE_OPT_DEBUG(s,ean)
+#endif
+
+ABS_STATE_DECL(INIT)
+{
+	ABS_STATE_OPT_DEBUG(INIT, ean);
+
+	/* abort authentication XXX */
+	ean->ean_aWhile = 0;
+	ean->ean_authAbort = FALSE;
+	ean->ean_keyAvailable = FALSE;		/* XXX key fsm */
+	ean->ean_backendState = EAPOL_ABS_INIT;
+}
+
+ABS_STATE_DECL(IDLE)
+{
+	ABS_STATE_OPT_DEBUG(IDLE, ean);
+
+	ean->ean_authStart = FALSE;	/* XXX */
+	ean->ean_reqCount = 0;
+	ean->ean_reqSrvCount = 0;
+	ean->ean_backendState = EAPOL_ABS_IDLE;
+}
+
+ABS_STATE_DECL(REQUEST)
+{
+#define	txReq(ean)		(*ean->ean_ec->ec_radius->rc_txreq)(ean)
+	ABS_STATE_DEBUG(REQUEST, ean);
+
+	ean->ean_currentId = ean->ean_idFromServer;
+	txReq(ean);
+	ean->ean_aWhile = eapol_supptimo;
+	ean->ean_reqCount++;
+	ean->ean_reqSrvCount = 0;
+	ean->ean_backendState = EAPOL_ABS_REQUEST;
+#undef txReq
+}
+
+ABS_STATE_DECL(RESPONSE)
+{
+#define	sendRespToServer(ean)	(*ean->ean_ec->ec_radius->rc_sendsrvr)(ean)
+	ABS_STATE_DEBUG(RESPONSE, ean);
+
+	ean->ean_aReq = FALSE;
+	ean->ean_aSuccess = FALSE;
+	ean->ean_authTimeout = FALSE;
+	ean->ean_rxResp = FALSE;
+	ean->ean_aFail = FALSE;
+	ean->ean_aWhile = eapol_servtimo;
+	ean->ean_reqCount = 0;
+	sendRespToServer(ean);
+	ean->ean_reqSrvCount++;
+	ean->ean_backendState = EAPOL_ABS_RESPONSE;
+#undef sendRespToServer
+}
+
+ABS_STATE_DECL(SUCCESS)
+{
+	ABS_STATE_DEBUG(SUCCESS, ean);
+
+	ean->ean_aWhile = 0;
+	ean->ean_currentId = ean->ean_idFromServer;
+	txCannedSuccess(ean);
+	ean->ean_authSuccess = TRUE;
+	ean->ean_backendState = EAPOL_ABS_SUCCESS;
+}
+
+ABS_STATE_DECL(FAIL)
+{
+	ABS_STATE_DEBUG(FAIL, ean);
+
+	ean->ean_aWhile = 0;
+	ean->ean_currentId = ean->ean_idFromServer;
+	txCannedFail(ean);
+	ean->ean_authFail = TRUE;
+	ean->ean_backendState = EAPOL_ABS_FAIL;
+}
+
+ABS_STATE_DECL(TIMEOUT)
+{
+	ABS_STATE_DEBUG(TIMEOUT, ean);
+
+	if (ean->ean_portStatus == EAPOL_PORTSTATUS_UNAUTH)
+		txCannedFail(ean);
+	ean->ean_authTimeout = TRUE;
+	ean->ean_backendState = EAPOL_ABS_TIMEOUT;
+}
+
+/*
+ * Carry out a state transition in the backend state machine.
+ * This routine encapsulates Figure 8-12 in the spec.
+ */
+static void
+eapol_backend_step(struct eapol_auth_node *ean)
+{
+
+	EAPOL_LOCK_ASSERT(ean->ean_ec);
+
+	if (ean->ean_portControl != EAPOL_PORTCONTROL_AUTO ||
+	    ean->ean_initialize || ean->ean_authAbort) {
+		ABS_STATE_ENTER(INIT, ean);
+		return;
+	}
+	switch (ean->ean_backendState) {
+	case EAPOL_ABS_INIT:
+		ABS_STATE_ENTER(IDLE, ean);
+		break;
+	case EAPOL_ABS_IDLE:
+		if (ean->ean_authStart)
+			ABS_STATE_ENTER(RESPONSE, ean);
+		break;
+	case EAPOL_ABS_REQUEST:
+		if (ean->ean_rxResp)
+			ABS_STATE_ENTER(RESPONSE, ean);
+		else if (ean->ean_aWhile == 0) {
+			if (ean->ean_reqCount < eapol_suppreqlimit)
+				ABS_STATE_ENTER(REQUEST, ean);
+			else
+				ABS_STATE_ENTER(TIMEOUT, ean);
+		}
+		break;
+	case EAPOL_ABS_RESPONSE:
+		if (ean->ean_aReq)
+			ABS_STATE_ENTER(REQUEST, ean);
+		else if (ean->ean_aFail)
+			ABS_STATE_ENTER(FAIL, ean);
+		else if (ean->ean_aSuccess)
+			ABS_STATE_ENTER(SUCCESS, ean);
+		else if (ean->ean_aWhile == 0) {
+			if (ean->ean_reqSrvCount < eapol_servreqlimit)
+				ABS_STATE_ENTER(RESPONSE, ean);
+			else
+				ABS_STATE_ENTER(TIMEOUT, ean);
+		}
+		break;
+	case EAPOL_ABS_SUCCESS:
+	case EAPOL_ABS_FAIL:
+	case EAPOL_ABS_TIMEOUT:
+		ABS_STATE_ENTER(IDLE, ean);
+		break;
+	default:
+		eapolstats.eps_badbackendfsm++;
+		break;
+	}
+}
+#undef ABS_STATE_DECL
+#undef ABS_STATE_ENTER
+#undef ABS_STATE_DEBUG
+
+/*
+ * Reauthentication timer state machine.
+ */
+#define	REAUTH_STATE_DECL(s)		STATE_DECL(ARS,s)
+#define	REAUTH_STATE_ENTER(s,ean)	STATE_ENTER(ARS,s,ean)
+#ifdef IEEE80211_DEBUG
+#define	REAUTH_STATE_DEBUG(s,ean)				\
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM,	\
+		("[%s] %s -> %s\n",				\
+		ether_sprintf(ean->ean_node->ni_macaddr),	\
+		eapol_ars_states[ean->ean_reAuthState],		\
+		eapol_ars_states[EAPOL_ARS_##s]))
+#define	REAUTH_STATE_OPT_DEBUG(s,ean) do {			\
+	if (ean->ean_reAuthState != EAPOL_ARS_##s)		\
+		REAUTH_STATE_DEBUG(s,ean);			\
+} while (0)
+#else
+#define	REAUTH_STATE_DEBUG(s,ean)
+#define	REAUTH_STATE_OPT_DEBUG(s,ean)
+#endif
+
+REAUTH_STATE_DECL(INIT)
+{
+	REAUTH_STATE_OPT_DEBUG(INIT, ean);
+
+	ean->ean_reAuthWhen = eapol_reauthtimo;
+	ean->ean_reAuthState = EAPOL_ARS_INIT;
+}
+
+REAUTH_STATE_DECL(REAUTH)
+{
+	REAUTH_STATE_DEBUG(REAUTH, ean);
+
+	ean->ean_reAuthenticate = TRUE;
+	ean->ean_reAuthState = EAPOL_ARS_REAUTH;
+}
+
+/*
+ * Carry out a state transition in the reauthentication
+ * timer state machine. This routine encapsulates
+ * Figure 8-11 in the spec.
+ */
+static void
+eapol_reauth_step(struct eapol_auth_node *ean)
+{
+
+	EAPOL_LOCK_ASSERT(ean->ean_ec);
+
+	if (ean->ean_portControl != EAPOL_PORTCONTROL_AUTO ||
+	    ean->ean_initialize ||
+	    ean->ean_portStatus == EAPOL_PORTSTATUS_UNAUTH ||
+	    !eapol_reauthenabled) {
+		REAUTH_STATE_ENTER(INIT, ean);
+		return;
+	}
+	switch (ean->ean_reAuthState) {
+	case EAPOL_ARS_INIT:
+		if (ean->ean_reAuthWhen == 0)
+			REAUTH_STATE_ENTER(REAUTH, ean);
+		break;
+	case EAPOL_ARS_REAUTH:
+		REAUTH_STATE_ENTER(INIT, ean);
+		break;
+	default:
+		eapolstats.eps_badreauthfsm++;
+		break;
+	}
+}
+#undef REAUTH_STATE_DECL
+#undef REAUTH_STATE_ENTER
+#undef REAUTH_STATE_DEBUG
+
+/*
+ * Key transmit state machine.
+ */
+#define	KEY_STATE_DECL(s)	STATE_DECL(AKS,s)
+#define	KEY_STATE_ENTER(s,ean)	STATE_ENTER(AKS,s,ean)
+#ifdef IEEE80211_DEBUG
+#define	KEY_STATE_DEBUG(s,ean)					\
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM,	\
+		("[%s] %s -> %s\n",				\
+		ether_sprintf(ean->ean_node->ni_macaddr),	\
+		eapol_aks_states[ean->ean_keyState],		\
+		eapol_aks_states[EAPOL_AKS_##s]))
+#define	KEY_STATE_OPT_DEBUG(s,ean) do {				\
+	if (ean->ean_keyState != EAPOL_AKS_##s)			\
+		KEY_STATE_DEBUG(s,ean);				\
+} while (0)
+#else
+#define	KEY_STATE_DEBUG(s,ean)
+#define	KEY_STATE_OPT_DEBUG(s,ean)
+#endif
+
+KEY_STATE_DECL(NO_KEY)
+{
+	KEY_STATE_OPT_DEBUG(NO_KEY, ean);
+
+	ean->ean_keyState = EAPOL_AKS_NO_KEY;
+}
+
+KEY_STATE_DECL(KEY)
+{
+#define	txKey(ean)	(*ean->ean_ec->ec_radius->rc_txkey)(ean)
+	KEY_STATE_DEBUG(KEY, ean);
+
+	txKey(ean);
+	ean->ean_keyAvailable = FALSE;
+	ean->ean_keyState = EAPOL_AKS_KEY;
+#undef txKey
+}
+
+/*
+ * Carry out a state transition in the key transmit
+ * state machine. This routine encapsulates
+ * Figure 8-9 in the spec.
+ */
+static void
+eapol_key_step(struct eapol_auth_node *ean)
+{
+
+	EAPOL_LOCK_ASSERT(ean->ean_ec);
+
+	if (ean->ean_portControl != EAPOL_PORTCONTROL_AUTO ||
+	    ean->ean_initialize) {
+		KEY_STATE_ENTER(NO_KEY, ean);
+		return;
+	}
+	switch (ean->ean_keyState) {
+	case EAPOL_AKS_NO_KEY:
+		if (eapol_keytxenabled && ean->ean_keyAvailable)
+			KEY_STATE_ENTER(KEY, ean);
+		break;
+	case EAPOL_AKS_KEY:
+		if (!eapol_keytxenabled ||
+		    ean->ean_authFail || ean->ean_eapLogoff)
+			KEY_STATE_ENTER(NO_KEY, ean);
+		else if (ean->ean_keyAvailable)
+			KEY_STATE_ENTER(KEY, ean);
+		break;
+	default:
+		eapolstats.eps_badkeyfsm++;
+		break;
+	}
+}
+#undef KEY_STATE_DECL
+#undef KEY_STATE_ENTER
+#undef KEY_STATE_DEBUG
+
+/*
+ * Run the state machines so long as there are state transitions.
+ */
+void
+eapol_fsm_run(struct eapol_auth_node *ean)
+{
+	int ostate, statechange;
+
+	do {
+		statechange = FALSE;
+
+		ostate = ean->ean_authState;
+		eapol_auth_step(ean);
+		if (ean->ean_gone) {
+			struct ieee80211com *ic = ean->ean_ic;
+			struct ieee80211_node *ni = ean->ean_node;
+
+			/*
+			 * Delayed handling of node reclamation when the
+			 * reauthentication timer expires.  We must be
+			 * careful as the call to ieee80211_node_leave
+			 * will reclaim our state so we cannot reference
+			 * it past that point.  However this happens the
+			 * caller must also be careful to not reference state.
+			 */
+			ieee80211_node_leave(ic, ni);
+			ic->ic_stats.is_node_timeout++;
+			return;
+		}
+		statechange |= ostate != ean->ean_authState;
+
+		ostate = ean->ean_backendState;
+		eapol_backend_step(ean);
+		statechange |= ostate != ean->ean_backendState;
+
+		ostate = ean->ean_keyState;
+		eapol_key_step(ean);
+		statechange |= ostate != ean->ean_keyState;
+
+		ostate = ean->ean_reAuthState;
+		eapol_reauth_step(ean);
+		statechange |= ostate != ean->ean_reAuthState;
+	} while (statechange);
+}
+EXPORT_SYMBOL(eapol_fsm_run);
+
+/*
+ * Process timers for an authenticator's nodes.
+ */
+static void
+eapol_check_timers(unsigned long arg)
+{
+	struct eapolcom *ec = (struct eapolcom *)arg;
+	struct eapol_auth_node *ean;
+	int i, inuse, timeout;
+
+	/*
+	 * NB: this is very simplistic, but if the table is large
+	 * the time spent here may be noticeable and we may want
+	 * to optimize the work.
+	 */
+	EAPOL_LOCK(ec);
+	inuse = ec->ec_inuse;
+	for (i = 0; i < ec->ec_maxaid && inuse; i++) {
+		ean = ec->ec_table[i];
+		if (ean == NULL)
+			continue;
+		timeout = FALSE;
+		if (ean->ean_aWhile && --ean->ean_aWhile == 0) {
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+				("[%s] TIMEOUT aWhile, %s\n",
+				ether_sprintf(ean->ean_node->ni_macaddr),
+				eapol_abs_states[ean->ean_backendState]));
+			timeout = TRUE;
+		}
+		if (ean->ean_quietWhile && --ean->ean_quietWhile == 0) {
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+				("[%s] TIMEOUT quietWhile, %s\n",
+				ether_sprintf(ean->ean_node->ni_macaddr),
+				eapol_as_states[ean->ean_authState]));
+			timeout = TRUE;
+		}
+		if (ean->ean_reAuthWhen && --ean->ean_reAuthWhen == 0) {
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+				("[%s] TIMEOUT reAuthWhen, %s\n",
+				ether_sprintf(ean->ean_node->ni_macaddr),
+				eapol_ars_states[ean->ean_reAuthState]));
+			timeout = TRUE;
+		}
+		if (ean->ean_txWhen && --ean->ean_txWhen == 0) {
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+				("[%s] TIMEOUT txWhen, %s\n",
+				ether_sprintf(ean->ean_node->ni_macaddr),
+				eapol_as_states[ean->ean_authState]));
+			timeout = TRUE;
+		}
+		if (timeout)
+			eapol_fsm_run(ean);
+		inuse--;
+	}
+	if (ec->ec_inuse) {
+		ec->ec_timer.expires = jiffies + HZ;	/* once 1 second */
+		add_timer(&ec->ec_timer);
+	}
+	EAPOL_UNLOCK(ec);
+}
+
+/*
+ * Output routines to send EAP frames to the supplicant.
+ */
+
+/*
+ * Send an EAP Failure packet to the supplicant.
+ */
+static void
+txCannedFail(struct eapol_auth_node *ean)
+{
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+		("[%s] txCannedFail currentId %u\n",
+		ether_sprintf(ean->ean_node->ni_macaddr),
+		ean->ean_currentId));
+	eap_send_simple(&ean->ean_base, EAP_CODE_FAILURE, ean->ean_currentId);
+}
+
+/*
+ * Send an EAP Success packet to the supplicant.
+ */
+static void
+txCannedSuccess(struct eapol_auth_node *ean)
+{
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+		("[%s] txCannedSuccess currentId %u\n",
+		ether_sprintf(ean->ean_node->ni_macaddr),
+		ean->ean_currentId));
+	eap_send_simple(&ean->ean_base, EAP_CODE_SUCCESS, ean->ean_currentId);
+}
+
+void
+eapol_hmac_md5(struct eapolcom *ec, void *data, u_int datalen,
+	void *key, u_int keylen, u_int8_t hash[16])
+{
+	struct scatterlist sg;
+
+	sg.page = virt_to_page(data);
+	sg.offset = offset_in_page(data);
+	sg.length = datalen;
+
+	crypto_hmac(ec->ec_md5, key, &keylen, &sg, 1, hash);
+}
+EXPORT_SYMBOL(eapol_hmac_md5);
+
+/*
+ * Allocate a buffer large enough to hold the specified
+ * payload preceded by an EAPOL header and the other
+ * headers required to send the result as an 802.11 frame.
+ */
+struct sk_buff *
+eapol_alloc_skb(u_int payload)
+{
+	/* NB: these will never be encrypted */
+	const int overhead =
+		  sizeof(struct ieee80211_qosframe)
+		+ sizeof(struct llc)
+		+ sizeof(struct ether_header)
+		+ sizeof(struct eapol_hdr)
+		;
+	struct sk_buff *skb;
+
+	skb = dev_alloc_skb(overhead + payload);
+	if (skb != NULL)
+		skb_reserve(skb, overhead);
+	else
+		eapolstats.eps_nobuf++;
+	return skb;
+}
+EXPORT_SYMBOL(eapol_alloc_skb);
+
+/*
+ * Send an EAP Request/Identity packet to the supplicant.
+ */
+static void
+txReqId(struct eapol_auth_node *ean)
+{
+	struct eap_hdr *eap;
+	struct sk_buff *skb;
+
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+		("[%s] %s currentId %u\n",
+		ether_sprintf(ean->ean_node->ni_macaddr), __func__,
+		ean->ean_currentId));
+	skb = eapol_alloc_skb(sizeof(struct eap_hdr));
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY,
+			("[%s] could not allocate sk_buff (%s) \n",
+			ether_sprintf(ean->ean_node->ni_macaddr), __func__));
+		return;
+	}
+	eap = (struct eap_hdr *)skb_put(skb, sizeof(struct eap_hdr));
+	eap->eap_code = EAP_CODE_REQUEST;
+	eap->eap_id = ean->ean_currentId;
+	eap->eap_len = htons(sizeof(struct eap_hdr));
+	eap->eap_type = EAP_TYPE_IDENTITY;
+
+	eapol_send(&ean->ean_base, skb, EAPOL_TYPE_EAP);
+
+	ean->ean_txWhen = eapol_txtimo;
+}
+
+/*
+ * Set the re-authentication timer based on a setting
+ * received from the backend server.  We sanity check
+ * it and install it in the node for use if/when the
+ * reauthentication timer is started.
+ */
+void
+eapol_reauth_setperiod(struct eapol_auth_node *ean, int timeout)
+{
+
+	if (timeout == 0)
+		ean->ean_reAuthPeriod = eapol_reauthtimo;
+	else if (timeout < eapol_reauthmin)
+		ean->ean_reAuthPeriod = eapol_reauthmin;
+	else
+		ean->ean_reAuthPeriod = timeout;
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X,
+		("[%s] reauthentication period set to %u secs\n",
+		ether_sprintf(ean->ean_node->ni_macaddr),
+		ean->ean_reAuthPeriod));
+}
+EXPORT_SYMBOL(eapol_reauth_setperiod);
+
+/*
+ * Node management support.
+ */
+
+/*
+ * Node management callbacks.  All of these are
+ * subclassed when the radius client is used.
+ */
+
+/*
+ * Allocate a node on station join/discovery.
+ */
+static struct eapol_auth_node *
+eapol_node_alloc(struct eapolcom *ec)
+{
+	struct eapol_auth_node *ean;
+
+	MALLOC(ean, struct eapol_auth_node *, sizeof(struct eapol_auth_node),
+		M_EAPOL_NODE, M_NOWAIT | M_ZERO);
+	if (ean == NULL)
+		eapolstats.eps_nonodemem++;
+	return ean;
+}
+
+/*
+ * Reclaim a node on station leave or shutdown.
+ */
+static void
+eapol_node_free(struct eapol_auth_node *ean)
+{
+	FREE(ean, M_EAPOL_NODE);
+}
+
+/*
+ * Reset an existing node's non-state machine
+ * state when a station reassociates.
+ */
+static void
+eapol_node_reset(struct eapol_auth_node *ean)
+{
+	if (ean->ean_skb != NULL) {
+		kfree_skb(ean->ean_skb);
+		ean->ean_skb = NULL;
+	}
+	ean->ean_gone = FALSE;
+
+	/* reset timers, aWhile is reset by backend fsm */
+	ean->ean_quietWhile = 0;
+	ean->ean_reAuthWhen = 0;
+	ean->ean_txWhen = 0;
+	ean->ean_reKeyWhen = 0;
+	ean->ean_gReKeyWhen = 0;
+
+	ean->ean_portControl = EAPOL_PORTCONTROL_AUTO;
+}
+
+/*
+ * Create an entry for a client.
+ */
+static struct eapol_auth_node *
+eapol_new_node(struct eapolcom *ec,
+	struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct eapol_auth_node *ean;
+
+	/*
+	 * Allocate and initialize the node state.
+	 */
+	ean = (*ec->ec_node_alloc)(ec);
+	if (ean == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("[%s] no memory for EAPOL node\n",
+			ether_sprintf(ni->ni_macaddr)));
+		return NULL;
+	}
+	ean->ean_ec = ec;
+	ean->ean_ic = ic;
+	ean->ean_node = ni;
+
+	/*
+	 * Add the new entry to the table.
+	 */
+	EAPOL_LOCK(ec);
+	KASSERT(ec->ec_table[IEEE80211_NODE_AID(ni)] == NULL,
+	    ("entry already present for association id %u!",
+	    IEEE80211_NODE_AID(ni)));
+	ec->ec_table[IEEE80211_NODE_AID(ni)] = ean;
+	if (ec->ec_inuse++ == 0) {
+		/* start timer on first node */
+		ec->ec_timer.expires = jiffies + HZ;
+		add_timer(&ec->ec_timer);
+	}
+	EAPOL_UNLOCK(ec);
+
+	return ean;
+}
+
+/*
+ * Create an entry in response to a station associating
+ * reassociating.  This also triggers the state machine
+ * which results in the supplicant receiving a message.
+ */
+static void
+eapol_node_join(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct eapolcom *ec = ic->ic_ec;
+	struct eapol_auth_node *ean;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+		("[%s] 802.1x node join\n", ether_sprintf(ni->ni_macaddr)));
+	ean = ec->ec_table[IEEE80211_NODE_AID(ni)];
+	if (ean == NULL) {
+		/*
+		 * Bump the reference count so long as we hold a reference.
+		 */
+		ieee80211_ref_node(ni);
+		ean = eapol_new_node(ec, ic, ni);
+		if (ean == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+				("[%s] 802.1x node join FAILED\n",
+				ether_sprintf(ni->ni_macaddr)));
+			ieee80211_unref_node(&ni);
+			return;
+		}
+	}
+	/*
+	 * Start/Restart state machine.  Note that we crank
+	 * the state machine(s) twice because the initialize
+	 * handling will prematurely terminate stepping.
+	 * This should be safe to do.
+	 */
+	EAPOL_LOCK(ec);
+	ean->ean_initialize = TRUE;
+	eapol_fsm_run(ean);
+	ean->ean_initialize = FALSE;
+#if 0
+	eapol_fsm_run(ean);
+#else
+	/* XXX delay initial Identity request by 1 second */
+	ean->ean_txWhen = 1;
+#endif
+	EAPOL_UNLOCK(ec);
+}
+
+/* 
+ * Remove a node from the table and reclaim its resources.
+ */
+static void
+eapol_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct eapolcom *ec = ic->ic_ec;
+	struct eapol_auth_node *ean;
+
+	EAPOL_LOCK(ec);
+	ean = ec->ec_table[IEEE80211_NODE_AID(ni)];
+	if (ean == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+			("[%s] 802.1x node leave, but not found, aid %u\n",
+			ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_AID(ni)));
+		/* XXX statistic */
+	} else {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X,
+			("[%s] 802.1x node leave (%s)\n",
+			ether_sprintf(ni->ni_macaddr),
+			eapol_as_states[ean->ean_authState]));
+		/* 
+		 * Remove the entry from the table.
+		 */
+		ec->ec_table[IEEE80211_NODE_AID(ni)] = NULL;
+		if (--ec->ec_inuse == 0) {
+			/* clear timer on last node */
+			del_timer(&ec->ec_timer);
+		}
+		EAPOL_UNLOCK(ec);
+		/*
+		 * Delete the reference to the node and free our memory.
+		 */
+		ieee80211_free_node(ean->ean_ic, ean->ean_node);
+		if (ean->ean_skb)
+			kfree_skb(ean->ean_skb);
+		(*ec->ec_node_free)(ean);
+	}
+}
+
+/* 
+ * Remove all nodes from the table and reclaim their resources.
+ */
+static void
+eapol_delete_all_nodes(struct eapolcom *ec)
+{
+	struct eapol_auth_node *ean;
+	int i;
+
+	EAPOL_LOCK(ec);
+	for (i = 0; i < ec->ec_maxaid; i++) {
+		ean = ec->ec_table[i];
+		if (ean) {
+			ec->ec_table[i] = NULL;
+			ieee80211_free_node(ean->ean_ic, ean->ean_node);
+			if (ean->ean_skb)
+				kfree_skb(ean->ean_skb);
+			(*ec->ec_node_free)(ean);
+		}
+	}
+	ec->ec_inuse = 0;
+	del_timer(&ec->ec_timer);	/* clear timer */
+	EAPOL_UNLOCK(ec);
+}
+
+/*
+ * Send a formulated EAPOL packet.
+ */
+void
+eapol_send_raw(struct eapol_node *en, struct sk_buff *skb)
+{
+	struct net_device *dev = en->en_ic->ic_dev;
+
+	/*
+	 * Fill the device header for the EAPOL frame
+	 */
+	skb->dev = dev;
+	skb->protocol = __constant_htons(ETH_P_EAPOL);
+	skb->mac.raw = skb->nh.raw = skb->data;
+	if (dev->hard_header &&
+	    dev->hard_header(skb, dev, ETH_P_EAPOL, en->en_node->ni_macaddr, dev->dev_addr, skb->len) < 0)
+		goto out;
+
+	/* Send it off, maybe filter it using firewalling first.  */
+	NF_HOOK(NF_EAPOL, NF_EAPOL_OUT, skb, NULL, dev, dev_queue_xmit);
+	return;
+out:
+	kfree_skb(skb);
+}
+EXPORT_SYMBOL(eapol_send_raw);
+
+/*
+ * Create and send an EAPOL packet.
+ */
+void
+eapol_send(struct eapol_node *en, struct sk_buff *skb, u_int8_t type)
+{
+	struct eapol_hdr *eapol;
+
+	/*
+	 * Craft the EAPOL header.
+	 */
+	eapol = (struct eapol_hdr *)skb_push(skb, sizeof(*eapol));
+	eapol->eapol_ver = EAPOL_VERSION;
+	eapol->eapol_type = type;
+	eapol->eapol_len = htons(skb->len - sizeof(struct eapol_hdr));
+
+	eapol_send_raw(en, skb);
+}
+EXPORT_SYMBOL(eapol_send);
+
+#if 0
+/*
+ * Create and send a simple EAPOL packet.
+ */
+void
+eapol_send_simple(struct eapol_node *en, u_int8_t type)
+{
+	struct sk_buff *skb;
+
+	skb = eapol_alloc_skb(0);
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(en->en_ic, IEEE80211_MSG_ANY,
+			("[%s] could not allocate sk_buff (%s) \n",
+			ether_sprintf(en->en_node->ni_macaddr), __func__));
+		return;
+	}
+	eapol_send(en, skb, type);
+}
+#endif
+
+/*
+ * Create and send a simple EAP packet.
+ */
+static void
+eap_send_simple(struct eapol_node *en, u_int8_t code, u_int8_t id)
+{
+	struct eap_hdr_short *eap;
+	struct sk_buff *skb;
+
+	skb = eapol_alloc_skb(sizeof(struct eap_hdr_short));
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(en->en_ic, IEEE80211_MSG_ANY,
+			("[%s] could not allocate sk_buff (%s) \n",
+			ether_sprintf(en->en_node->ni_macaddr), __func__));
+		return;
+	}
+	eap = (struct eap_hdr_short *)
+		skb_put(skb, sizeof(struct eap_hdr_short));
+	eap->eap_code = code;
+	eap->eap_id = id;
+	eap->eap_len = __constant_htons(sizeof(struct eap_hdr_short));
+
+	eapol_send(en, skb, EAPOL_TYPE_EAP);
+}
+
+static void
+eapol_cleanup(struct eapolcom *ec)
+{
+	EAPOL_LOCK_DESTROY(ec);
+	if (ec->ec_md5 != NULL)
+		crypto_free_tfm(ec->ec_md5);
+	if (ec->ec_table != NULL)
+		FREE(ec->ec_table, M_DEVBUF);
+	FREE(ec, M_DEVBUF);
+}
+
+static struct eapolcom *
+eapol_setup(void)
+{
+	struct eapolcom *ec;
+	struct crypto_tfm *tfm;
+
+	/* XXX WAITOK? */
+	MALLOC(ec, struct eapolcom *, sizeof(*ec), M_DEVBUF, M_WAITOK | M_ZERO);
+	if (ec == NULL) {
+		printk("%s: no memory for state block!\n", __func__);
+		return NULL;
+	}
+	EAPOL_LOCK_INIT(ec, "eapol");
+
+	tfm = crypto_alloc_tfm("md5", 0);
+	if (tfm == NULL) {
+		/* XXX fallback on internal implementation? */
+		printf("%s: unable to allocate md5 crypto state\n", __func__);
+		/* XXX statistic */
+		goto bad;
+	}
+	ec->ec_md5 = tfm;
+
+	ec->ec_node_alloc = eapol_node_alloc;
+	ec->ec_node_free = eapol_node_free;
+	ec->ec_node_reset = eapol_node_reset;
+
+	return ec;
+bad:
+	eapol_cleanup(ec);
+	return NULL;
+}
+
+static struct packet_type eapol_packet_type = {
+	.type =	__constant_htons(ETH_P_EAPOL),
+	.func =	eapol_input,
+};
+
+/*
+ * Attach an authenticator.
+ */
+static int
+eapol_authenticator_attach(struct ieee80211com *ic)
+{
+	struct eapolcom *ec;
+
+	_MOD_INC_USE(THIS_MODULE, return FALSE);
+
+	if (eapolcom == NULL) {
+		/* XXX cannot happen */
+		printk(KERN_ERR "%s: no eapolcom!\n", __func__);
+		_MOD_DEC_USE(THIS_MODULE);
+		return FALSE;
+	}
+	ec = eapolcom;
+
+	ec->ec_maxaid = ic->ic_max_aid;
+	MALLOC(ec->ec_table, struct eapol_auth_node **,
+		sizeof(struct eapol_auth_node *) * ic->ic_max_aid,
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ec->ec_table == NULL) {
+		printk("%s: no memory for aid table!\n", __func__);
+		_MOD_DEC_USE(THIS_MODULE);
+		return FALSE;
+	}
+
+	/*
+	 * Startup radius client.
+	 */
+	ec->ec_backend = ieee80211_authenticator_backend_get("radius");
+	if (ec->ec_backend == NULL ||
+	    !ec->ec_backend->iab_attach(ec))
+		goto bad;
+
+	init_timer(&ec->ec_timer);
+	ec->ec_timer.data = (unsigned long) ec;
+	ec->ec_timer.function = eapol_check_timers;
+
+	/*
+	 * Bind to the 802.11 layer.
+	 */
+	ic->ic_ec = ec;
+
+	dev_add_pack(&eapol_packet_type);
+	printk(KERN_INFO "802.1x authenticator started\n");
+	return TRUE;
+bad:
+	FREE(ec->ec_table, M_DEVBUF);
+	ec->ec_table = NULL;
+	_MOD_DEC_USE(THIS_MODULE);
+	return FALSE;
+}
+
+/*
+ * Detach an authenticator.
+ */
+static void
+eapol_authenticator_detach(struct ieee80211com *ic)
+{
+	struct eapolcom *ec = ic->ic_ec;
+
+	if (ec != NULL) {
+		dev_remove_pack(&eapol_packet_type);
+		/*
+		 * Detach from 802.11 layer.
+		 */
+		ic->ic_ec = NULL;
+
+		/*
+		 * NB: must do this before detaching radius support
+		 *     as node management methods are reset on detach.
+		 */
+		eapol_delete_all_nodes(ec);
+		if (ec->ec_radius != NULL)
+			ec->ec_backend->iab_detach(ec);
+		if (ec->ec_table != NULL) {
+			FREE(ec->ec_table, M_DEVBUF);
+			ec->ec_table = NULL;
+		}
+		printk(KERN_INFO "802.1x authenticator stopped\n");
+	}
+	_MOD_DEC_USE(THIS_MODULE);
+}
+
+#define	CTL_AUTO	-2	/* cannot be CTL_ANY or CTL_NONE */
+
+/* XXX validate/minmax settings */
+static ctl_table dot1x_sysctls[] = {
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "reauthenabled",
+	  .data		= &eapol_reauthenabled,
+	  .maxlen	= sizeof(eapol_reauthenabled),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "keytxenabled",
+	  .data		= &eapol_keytxenabled,
+	  .maxlen	= sizeof(eapol_keytxenabled),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "idletimo",
+	  .data		= &eapol_idletimo,
+	  .maxlen	= sizeof(eapol_idletimo),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "reauthlimit",
+	  .data		= &eapol_reauthlimit,
+	  .maxlen	= sizeof(eapol_reauthlimit),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "reauthmin",
+	  .data		= &eapol_reauthmin,
+	  .maxlen	= sizeof(eapol_reauthmin),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "reauthtimo",
+	  .data		= &eapol_reauthtimo,
+	  .maxlen	= sizeof(eapol_reauthtimo),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "txtimo",
+	  .data		= &eapol_txtimo,
+	  .maxlen	= sizeof(eapol_txtimo),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "supptimo",
+	  .data		= &eapol_supptimo,
+	  .maxlen	= sizeof(eapol_supptimo),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "servtimo",
+	  .data		= &eapol_servtimo,
+	  .maxlen	= sizeof(eapol_servtimo),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "suppreqlimit",
+	  .data		= &eapol_suppreqlimit,
+	  .maxlen	= sizeof(eapol_suppreqlimit),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "servreqlimit",
+	  .data		= &eapol_servreqlimit,
+	  .maxlen	= sizeof(eapol_servreqlimit),
+	  .mode		= 0644,
+	  .proc_handler	= proc_dointvec
+	},
+	{ 0 }
+};
+static ctl_table dot1x_table[] = {
+	{ .ctl_name	= NET_8021X,
+	  .procname	= "8021x",
+	  .mode		= 0555,
+	  .child	= dot1x_sysctls
+	}, { 0 }
+};
+static ctl_table net_table[] = {
+#ifdef CONFIG_PROC_FS
+	{ .ctl_name	= CTL_NET,
+	  .procname	= "net",
+	  .mode		= 0555,
+	  .child	= dot1x_table
+	},
+#endif /* CONFIG_PROC_FS */
+	{ 0 }
+};
+
+static struct ctl_table_header *eapol_sysctls;
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: 802.1x authenticator");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+/*
+ * One module handles everything for now.  May want
+ * to split things up for embedded applications.
+ */
+static const struct ieee80211_authenticator dot1x = {
+	.ia_name	= "802.1x",
+	.ia_attach	= eapol_authenticator_attach,
+	.ia_detach	= eapol_authenticator_detach,
+	.ia_node_join	= eapol_node_join,
+	.ia_node_leave	= eapol_node_leave,
+};
+
+static int __init
+init_ieee80211_auth(void)
+{
+	eapolcom = eapol_setup();
+	if (eapolcom == NULL)
+		return -1;		/* XXX?? */
+	eapol_sysctls = register_sysctl_table(net_table, 0);
+	ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &dot1x);
+	return 0;
+}
+module_init(init_ieee80211_auth);
+
+static void __exit
+exit_ieee80211_auth(void)
+{
+	if (eapolcom != NULL)
+		eapol_cleanup(eapolcom);
+	if (eapol_sysctls)
+		unregister_sysctl_table(eapol_sysctls);
+	ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X);
+}
+module_exit(exit_ieee80211_auth);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.h	2005-02-24 13:06:17.304131392 -0800
@@ -0,0 +1,330 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.11 2004/01/15 08:44:27 onoe Exp $
+ */
+#ifndef _NET80211_DOT1X_H_
+#define _NET80211_DOT1X_H_
+
+/*
+ * IEEE 802.1x authenticator.
+ */
+#include <net80211/eapol.h>
+
+/*
+ * Authenticators handle authentication requests from supplicants.
+ * Typically this is done by communicating with a backend server
+ * using the Radius protocol. When configured in this way an
+ * authenticator does little more than pass frames between the
+ * supplicant and the backend server.  The exception to this is
+ * when authentication is followed by cryptographic key setup; in
+ * which case the authenticator must relay the key state and
+ * arrange for the keys to be installed prior to passing this
+ * information on the supplicant.  This is the basis for 802.1x.
+ */
+
+/*
+ * Authenticator PAE state machine.
+ */
+enum {
+	EAPOL_AS_INIT		= 0,
+	EAPOL_AS_DISCONNECTED	= 1,
+	EAPOL_AS_CONNECTING	= 2,
+	EAPOL_AS_AUTHENTICATING	= 3,
+	EAPOL_AS_AUTHENTICATED	= 4,
+	EAPOL_AS_ABORTING	= 5,
+	EAPOL_AS_HELD		= 6,
+	EAPOL_AS_FORCE_AUTH	= 7,
+	EAPOL_AS_FORCE_UNAUTH	= 8,
+};
+
+/*
+ * Authenticator backend state machine.
+ */
+enum {
+	EAPOL_ABS_INIT		= 0,
+	EAPOL_ABS_IDLE		= 1,
+	EAPOL_ABS_REQUEST	= 2,
+	EAPOL_ABS_RESPONSE	= 3,
+	EAPOL_ABS_SUCCESS	= 4,
+	EAPOL_ABS_FAIL		= 5,
+	EAPOL_ABS_TIMEOUT	= 6,
+};
+
+/*
+ * Reauthentication timer state machine.
+ */
+enum {
+	EAPOL_ARS_INIT		= 0,
+	EAPOL_ARS_REAUTH	= 1,
+};
+
+/*
+ * Authenticator key transmit state machine.
+ */
+enum {
+	EAPOL_AKS_NO_KEY	= 0,
+	EAPOL_AKS_KEY		= 1,
+};
+
+/*
+ * Port-related definitions.
+ */
+enum {
+	EAPOL_PORTCONTROL_AUTO	= 0,
+};
+enum {
+	EAPOL_PORTMODE_AUTO	= 0,
+	EAPOL_PORTMODE_FORCEAUTH= 1,
+	EAPOL_PORTMODE_FORCEUNAUTH= 2,
+};
+enum {
+	EAPOL_PORTSTATUS_AUTH	= 0,	/* authenticated */
+	EAPOL_PORTSTATUS_UNAUTH	= 1,	/* unauthenticated */
+};
+
+struct eapolcom;
+struct ieee80211com;
+struct ieee80211_node;
+
+/*
+ * Base class in case we decide to add supplicant support.
+ */
+struct eapol_node {
+	struct eapolcom		*en_ec;		/* associated authenticator */
+	struct ieee80211com	*en_ic;		/* associated device */
+	struct ieee80211_node	*en_node;	/* associated 802.11 state */
+	u_int8_t		en_id[EAP_IDENTITY_MAXLEN];
+	u_int16_t		en_idlen;
+};
+
+/*
+ * Per-station authentication state.  Variable names are
+ * chosen to mimic those used in the 802.1x specification
+ * (maybe should group like state into structures).
+ */
+struct eapol_auth_node {
+	struct eapol_node	ean_base;	/* base class */
+	u_int			ean_scangen;	/* scan generation # */
+	u_int8_t		ean_gone;	/* node needs delayed reclaim */
+	struct sk_buff		*ean_skb;	/* from supplicant */
+
+	/* authenticator timers 8.5.2.1 */
+	u_int32_t		ean_aWhile;	/* with supplicant or AS */
+	u_int32_t		ean_quietWhile;	/* delay before acquiring sup */
+	u_int32_t		ean_reAuthWhen;	/* re-authentication interval */
+	u_int32_t		ean_txWhen;	/* xmit timeout */
+	u_int32_t		ean_reKeyWhen;	/* station re-key timer */
+	u_int32_t		ean_gReKeyWhen;	/* group re-key timer */
+	/* global variables 8.5.2.2 */
+	u_int			ean_authAbort	: 1,
+				ean_authFail	: 1,
+				ean_authStart	: 1,
+				ean_authTimeout	: 1,
+				ean_authSuccess	: 1,
+				ean_initialize	: 1,
+				ean_portControl	: 2,
+				ean_portStatus	: 2,
+				ean_reAuthenticate : 1;
+	u_int8_t		ean_currentId;	/* current id */
+	u_int8_t		ean_receivedId;	/* most recent Identifier rx */
+	/* authentication variables 8.5.4.1 */
+	u_int8_t		ean_authState;
+	u_int8_t		ean_eapLogoff	: 1,
+				ean_eapStart	: 1,
+				ean_portMode	: 2,
+				ean_rxRespId	: 1;
+	u_int8_t		ean_reAuthCount;
+	/* authentication counters 8.5.4.2 */
+	u_int			ean_authEntersConnecting;
+	u_int			ean_authEapLogoffsWhileConnecting;
+	u_int			ean_authEntersAuthenticating;
+	u_int			ean_authAuthSuccessesWhileAuthenticating;
+	u_int			ean_authAuthTimeoutsWhileAuthenticating;
+	u_int			ean_authAuthFailWhileAuthenticating;
+	u_int			ean_authAuthReauthsWhileAuthenticating;
+	u_int			ean_authAuthEapStartsWhileAuthenticating;
+	u_int			ean_authAuthEapLogoffWhileAuthenticating;
+	u_int			ean_authAuthReauthsWhileAuthenticated;
+	u_int			ean_authAuthEapStartsWhileAuthenticated;
+	u_int			ean_authAuthEapLogoffWhileAuthenticated;
+	/* key state variables 8.5.5 */
+	u_int8_t		ean_keyState;
+	u_int			ean_keyAvailable : 1;
+	/* reauthentication variables */
+	u_int8_t		ean_reAuthState;
+	u_int			ean_reAuthPeriod;	/* not part of spec */
+	/* backend authentication variables 8.5.8.1.1 */
+	u_int8_t		ean_backendState;
+	u_int8_t		ean_reqCount;
+	u_int8_t		ean_rxResp	: 1,
+				ean_aSuccess	: 1,
+				ean_aFail	: 1,
+				ean_aReq	: 1;
+	u_int8_t		ean_idFromServer;
+	u_int8_t		ean_reqSrvCount;	/* not part of spec */
+	/* backend counters 8.5.8.2 */
+	u_int			ean_backendResponses;
+	u_int			ean_backendAccessChallenges;
+	u_int			ean_backendOtherRequestsToSupplicant;
+	u_int			ean_backendNonNakResponsesForSupplicant;
+	u_int			ean_backendAuthSuccesses;
+	u_int			ean_backendAuthFails;
+};
+#define	EAPOL_AUTHNODE(_x)	((struct eapol_auth_node *)(_x))
+
+/* write-arounds for base class members */
+#define	ean_ec		ean_base.en_ec
+#define	ean_ic		ean_base.en_ic
+#define	ean_node	ean_base.en_node
+#define	ean_id		ean_base.en_id
+#define	ean_idlen	ean_base.en_idlen
+#define	ean_lock	ean_base.en_lock
+
+struct crypto_tfm;
+
+/*
+ * State for each authenticator instance.  We only support
+ * one at the moment and it's not clear that more than one
+ * per-machine is desirable.
+ */
+struct eapolcom {
+	struct eapol_auth_node	**ec_table;	/* indexed by association id */
+	u_int16_t		ec_maxaid;	/* copy of ic_max_aid */
+	u_int16_t		ec_inuse;	/* number of entries in use */
+	eapol_lock_t		ec_lock;	/* on eapolcom/node table */
+	struct timer_list	ec_timer;	/* state machine timers */
+
+	struct crypto_tfm	*ec_md5;
+
+	/* backend state and related methods */
+	const struct ieee80211_authenticator_backend *ec_backend;
+	struct radiuscom	*ec_radius;
+	struct eapol_auth_node	*(*ec_node_alloc)(struct eapolcom *);
+	void			(*ec_node_free)(struct eapol_auth_node *);
+	void			(*ec_node_reset)(struct eapol_auth_node *);
+};
+
+/*
+ * Statistics.  We define the radius client statistics
+ * here too so only one structure needs to be understood
+ * by applications that use it.
+ * XXX no way to tell whether a radius client is active
+ */
+struct eapolstats {
+	u_int32_t	eap_badcode;
+	u_int32_t	eap_lenmismatch;
+	u_int32_t	eap_tooshort;
+	u_int32_t	eas_badcode;
+	u_int32_t	eas_badidlen;
+	u_int32_t	eas_badtype;
+	u_int32_t	eas_idmismatch;
+	u_int32_t	eak_keynotrequest;
+	u_int32_t	eak_badkeytype;
+	u_int32_t	eak_replay;
+	u_int32_t	eak_nononce;
+	u_int32_t	eak_micfailed;
+	u_int32_t	eap_keydiscard;
+	u_int32_t	eap_keytooshort;
+	u_int32_t	eap_keynotwpa;
+	u_int32_t	eps_badalloc;
+	u_int32_t	eps_badauthfsm;
+	u_int32_t	eps_badauthlogoff;
+	u_int32_t	eps_badauthstart;
+	u_int32_t	eps_badauthtimeout;
+	u_int32_t	eps_badbackendfsm;
+	u_int32_t	eps_badbackendtimeout;
+	u_int32_t	eps_badkeyfsm;
+	u_int32_t	eps_badreauthfsm;
+	u_int32_t	eps_badtype;
+	u_int32_t	eps_badver;
+	u_int32_t	eps_noauth;
+	u_int32_t	eps_nobuf;
+	u_int32_t	eps_noinstance;
+	u_int32_t	eps_nonode;
+	u_int32_t	eps_nonodemem;
+	u_int32_t	eps_nosession;
+	u_int32_t	eps_sharecheck;
+	u_int32_t	eps_tooshort;
+	/* radius server-specific statistics */
+	u_int32_t	rs_nosecret;
+	u_int32_t	rs_addrmismatch;
+	u_int32_t	rs_badattrlen;
+	u_int32_t	rs_badcode;
+	u_int32_t	rs_badid;
+	u_int32_t	rs_badlen;
+	u_int32_t	rs_badmode;
+	u_int32_t	rs_badmsgauth;
+	u_int32_t	rs_badrespauth;
+	u_int32_t	rs_cannotbind;
+	u_int32_t	rs_emptyreply;
+	u_int32_t	rs_lenmismatch;
+	u_int32_t	rs_noclone;
+	u_int32_t	rs_nomsg;
+	u_int32_t	rs_nocrypto;
+	u_int32_t	rs_nomem;
+	u_int32_t	rs_nomsgauth;
+	u_int32_t	rs_nosocket;
+	u_int32_t	rs_nothread;
+	u_int32_t	rs_request;
+	u_int32_t	rs_tooshort;
+	u_int32_t	rs_vkeybadsalt;
+	u_int32_t	rs_vkeydupsalt;
+	u_int32_t	rs_vkeybadvid;
+	u_int32_t	rs_vkeybadlen;
+	u_int32_t	rs_vkeytooshort;
+	u_int32_t	rs_vkeytoolong;
+};
+
+#ifdef __linux__
+#define	NF_EAPOL	0
+#define	NF_EAPOL_OUT	1
+#define	NF_EAPOL_IN	2
+
+enum {
+	NET_8021X	= 18,			/* XXX */
+};
+#endif
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+extern	void eapol_fsm_run(struct eapol_auth_node *);
+extern	void eapol_reauth_setperiod(struct eapol_auth_node *, int timeout);
+extern	void eapol_send_raw(struct eapol_node *, struct sk_buff *);
+extern	void eapol_send(struct eapol_node *, struct sk_buff *, u_int8_t type);
+extern	struct sk_buff *eapol_alloc_skb(u_int payload);
+extern	void eapol_hmac_md5(struct eapolcom *ec, void *data, u_int datalen,
+		void *key, u_int keylen, u_int8_t hash[16]);
+
+extern	struct eapolstats eapolstats;
+
+MALLOC_DECLARE(M_EAPOL_NODE);
+#endif
+
+#endif /* _NET80211_DOT1X_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_input.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_input.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_input.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_input.c	2005-02-24 13:06:17.306131088 -0800
@@ -0,0 +1,2360 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.13 2004/01/15 08:44:27 onoe Exp $");
+
+/*
+ * IEEE 802.11 input handling.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/if_vlan.h>
+
+#include "if_llc.h"
+#include "if_ethersubr.h"
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+static struct sk_buff *ieee80211_defrag(struct ieee80211com *,
+	struct ieee80211_node *, struct sk_buff *);
+static struct sk_buff *ieee80211_decap(struct ieee80211com *, struct sk_buff *);
+static void ieee80211_recv_pspoll(struct ieee80211com *,
+	struct ieee80211_node *, struct sk_buff *);
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled.  This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211com *ic, int subtype)
+{
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_BEACON:
+		return (ic->ic_state == IEEE80211_S_SCAN);
+	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+		return (ic->ic_opmode == IEEE80211_M_IBSS);
+	}
+	return 1;
+}
+#endif
+
+/*
+ * Process a received frame.  The node associated with the sender
+ * should be supplied.  If nothing was found in the node table then
+ * the caller is assumed to supply a reference to ic_bss instead.
+ * The RSSI and a timestamp are also supplied.  The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''.  The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+void
+ieee80211_input(struct ieee80211com *ic, struct sk_buff *skb,
+	struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+{
+#define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
+#define	HAS_SEQ(type)	((type & 0x4) == 0)
+	struct net_device *dev = ic->ic_dev;
+	struct ieee80211_frame *wh;
+	struct ieee80211_key *key;
+	struct ether_header *eh;
+	int len;
+	u_int8_t dir, type, subtype;
+	u_int8_t *bssid;
+	u_int16_t rxseq;
+
+	KASSERT(ni != NULL, ("null node"));
+	KASSERT(skb->len >= sizeof(struct ieee80211_frame_min),
+		("frame length too short: %u", skb->len));
+
+	/*
+	 * In monitor mode, send everything directly to bpf.
+	 * Also do not process frames w/o i_addr2 any further.
+	 * XXX may want to include the CRC
+	 */
+	if (ic->ic_opmode == IEEE80211_M_MONITOR)
+		goto out;
+
+	if (skb->len < sizeof(struct ieee80211_frame_min)) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: frame too short, len %u\n",
+			__func__, skb->len));
+		ic->ic_stats.is_rx_tooshort++;
+		goto out;
+	}
+	/*
+	 * Bit of a cheat here, we use a pointer for a 3-address
+	 * frame format but don't reference fields past outside
+	 * ieee80211_frame_min w/o first validating the data is
+	 * present.
+	 */
+	wh = (struct ieee80211_frame *)skb->data;
+
+	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+	    IEEE80211_FC0_VERSION_0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("receive packet with wrong version: %x\n",
+			wh->i_fc[0]));
+		ic->ic_stats.is_rx_badversion++;
+		goto err;
+	}
+
+	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+	if (ic->ic_state != IEEE80211_S_SCAN) {
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			bssid = wh->i_addr2;
+			if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
+				/* not interested in */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+					("[%s] discard frame not to bss\n",
+					ether_sprintf(bssid)));
+				ic->ic_stats.is_rx_wrongbss++;
+				goto out;
+			}
+			break;
+		case IEEE80211_M_IBSS:
+		case IEEE80211_M_AHDEMO:
+		case IEEE80211_M_HOSTAP:
+			if (dir != IEEE80211_FC1_DIR_NODS)
+				bssid = wh->i_addr1;
+			else if (type == IEEE80211_FC0_TYPE_CTL)
+				bssid = wh->i_addr1;
+			else {
+				if (skb->len < sizeof(struct ieee80211_frame)) {
+					IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+					    ("%s: frame too short, len %u\n",
+					    __func__, skb->len));
+					ic->ic_stats.is_rx_tooshort++;
+					goto out;
+				}
+				bssid = wh->i_addr3;
+			}
+			if (type == IEEE80211_FC0_TYPE_DATA &&
+			    !IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
+			    !IEEE80211_ADDR_EQ(bssid, dev->broadcast)) {
+				/* not interested in */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+				    ("[%s] discard data frame not to bss\n",
+				    ether_sprintf(bssid)));
+				ic->ic_stats.is_rx_wrongbss++;
+				goto out;
+			}
+			break;
+		default:
+			/* XXX catch bad values */
+			goto out;
+		}
+		ni->ni_rssi = rssi;
+		ni->ni_rstamp = rstamp;
+		if (HAS_SEQ(type)) {
+			rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+			if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+			    SEQ_LEQ(rxseq, ni->ni_rxseq)) {
+				/* duplicate, discard */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+				    ("[%s] discard duplicate frame, "
+				    "seqno <%u,%u> fragno <%u,%u>\n",
+				    ether_sprintf(bssid),
+				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+				    ni->ni_rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+				    rxseq & IEEE80211_SEQ_FRAG_MASK,
+				    ni->ni_rxseq & IEEE80211_SEQ_FRAG_MASK));
+				/* XXX per-station stat */
+				ic->ic_stats.is_rx_dup++;
+				goto out;
+			}
+			ni->ni_rxseq = rxseq;
+		}
+	}
+
+	/*
+	 * Check for ps-poll state change for the station.
+	 * XXX is there a response when pspoll is not supported?
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    ic->ic_set_tim != NULL &&
+	    ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+	    (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) {
+		/* XXX statistics? */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+			("[%s] power save mode %s\n",
+			ether_sprintf(wh->i_addr2),
+			(wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "on" : "off")));
+		if ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == 0) {
+			/* turn off power save mode, dequeue stored packets */
+			ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+			(*ic->ic_set_tim)(ic, ni->ni_associd, 0);
+			while (!_IF_QLEN(&ni->ni_savedq) != 0) {
+				struct sk_buff *skb0;
+				IF_DEQUEUE(&ni->ni_savedq, skb0);
+				/* XXX need different driver interface */
+				(*dev->hard_start_xmit)(skb0, dev);/* XXX??? */
+			}
+		} else {
+			/* turn on power save mode */
+			ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+		}
+	}
+
+	switch (type) {
+	case IEEE80211_FC0_TYPE_DATA:
+		if (skb->len < sizeof(struct ieee80211_frame)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: data frame too short, len %u\n",
+				__func__, skb->len));
+			ic->ic_stats.is_rx_tooshort++;
+			goto out;		/* XXX */
+		}
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			if (dir != IEEE80211_FC1_DIR_FROMDS) {
+				ic->ic_stats.is_rx_wrongdir++;
+				goto out;
+			}
+			if ((dev->flags & IFF_MULTICAST) &&
+			    IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+			    IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
+				/*
+				 * In IEEE802.11 network, multicast packet
+				 * sent from me is broadcasted from AP.
+				 * It should be silently discarded for
+				 * SIMPLEX interface.
+				 *
+				 * NB: Linux has no IFF_ flag to indicate
+				 *     if an interface is SIMPLEX or not;
+				 *     so we always assume it to be true.
+				 */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+					("discard multicast echo\n"));
+				ic->ic_stats.is_rx_mcastecho++;
+				goto out;
+			}
+			break;
+		case IEEE80211_M_IBSS:
+		case IEEE80211_M_AHDEMO:
+			if (dir != IEEE80211_FC1_DIR_NODS) {
+				ic->ic_stats.is_rx_wrongdir++;
+				goto out;
+			}
+			break;
+		case IEEE80211_M_HOSTAP:
+			if (dir != IEEE80211_FC1_DIR_TODS) {
+				ic->ic_stats.is_rx_wrongdir++;
+				goto out;
+			}
+			/* check if source STA is associated */
+			if (ni == ic->ic_bss) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+					("[%s] discard data from unknown src\n",
+					ether_sprintf(wh->i_addr2)));
+ieee80211_dump_nodes(ic);/*XXX*/
+				/* NB: caller deals with reference to ic_bss */
+				ni = ieee80211_dup_bss(ic, wh->i_addr2);
+				if (ni != NULL) {
+					IEEE80211_SEND_MGMT(ic, ni,
+					    IEEE80211_FC0_SUBTYPE_DEAUTH,
+					    IEEE80211_REASON_NOT_AUTHED);
+					ieee80211_free_node(ic, ni);
+				}
+				ic->ic_stats.is_rx_notassoc++;
+				goto err;
+			}
+			if (ni->ni_associd == 0) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+					("[%s] discard data from unassoc src\n",
+					ether_sprintf(wh->i_addr2)));
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DISASSOC,
+				    IEEE80211_REASON_NOT_ASSOCED);
+				ic->ic_stats.is_rx_notassoc++;
+				goto err;
+			}
+			break;
+		default:
+			/* XXX here to keep compiler happy */
+			goto out;
+		}
+
+		/*
+		 * Handle privacy requirements.  Note that we
+		 * must not be preempted from here until after
+		 * we (potentially) call ieee80211_crypto_demic;
+		 * otherwise we may violate assumptions in the
+		 * crypto cipher modules used to do delayed update
+		 * of replay sequence numbers.
+		 */
+		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				/*
+				 * Discard encrypted frames when privacy is off.
+				 */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+					("[%s] discard WEP data frame 'cuz "
+					"PRIVACY off\n",
+					ether_sprintf(wh->i_addr2)));
+				ic->ic_stats.is_rx_noprivacy++;
+				goto out;
+			}
+			key = ieee80211_crypto_decap(ic, ni, skb);
+			if (key == NULL) {
+				/* NB: stats+msgs handled in crypto_decap */
+				goto out;
+			}
+		} else {
+			key = NULL;
+		}
+
+		/*
+		 * Next up, any fragmentation.
+		 */
+		skb = ieee80211_defrag(ic, ni, skb);
+		if (skb == NULL) {
+			/* XXX statistic */
+			/* Fragment dropped or frame not complete yet */
+			goto out;
+		}
+		wh = NULL;		/* no longer valid, catch any uses */
+
+		/*
+		 * Next strip any MSDU crypto bits.
+		 */
+		if (key != NULL && !ieee80211_crypto_demic(ic, key, skb)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+				("%s: discard frame on demic error\n",
+				__func__));
+			/* XXX statistic? */
+			goto out;
+		}
+
+		/*
+		 * Finally, strip the 802.11 header.
+		 */
+		skb = ieee80211_decap(ic, skb);
+		if (skb == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+				("%s: decapsulation error\n", __func__));
+			ic->ic_stats.is_rx_decap++;
+			goto err;
+		}
+		eh = (struct ether_header *) skb->data;
+		if ((ni->ni_flags & IEEE80211_NODE_AUTH) == 0) {
+			/*
+			 * Deny any non-PAE frames received prior to
+			 * authorization.  For open/shared-key
+			 * authentication the port is mark authorized
+			 * after authentication completes.  For 802.1x
+			 * the port is not marked authorized by the
+			 * authenticator until the handshake has completed.
+			 */
+			if (eh->ether_type != __constant_htons(ETHERTYPE_PAE)) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+				    ("[%s] discard data (ether type 0x%x len %u)"
+				    " on unauthorized port\n",
+				    ether_sprintf(eh->ether_shost),
+				    eh->ether_type, skb->len));
+				ic->ic_stats.is_rx_unauth++;
+				/* XXX node statistic */
+				goto err;
+			}
+			ni->ni_inact = ic->ic_inact_auth;
+		} else {
+			/*
+			 * When denying unencrypted frames, discard
+			 * any non-PAE frames received without encryption.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
+			    key == NULL &&
+			    eh->ether_type != __constant_htons(ETHERTYPE_PAE)) {
+				/*
+				 * Drop unencrypted frames.
+				 */
+				ic->ic_stats.is_rx_unencrypted++;
+				goto out;
+			}
+			ni->ni_inact = ic->ic_inact_run;
+		}
+		ic->ic_devstats->rx_packets++;
+		ic->ic_devstats->rx_bytes += skb->len;
+
+		/* perform as a bridge within the AP */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		    (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
+			struct sk_buff *skb1 = NULL;
+
+			if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+				skb1 = skb_copy(skb, GFP_ATOMIC);
+			} else {
+				/* XXX this dups work done in ieee80211_encap */
+				/* check if destination is associated */
+				struct ieee80211_node *ni1 =
+				    ieee80211_find_node(ic, eh->ether_dhost);
+				if (ni1 != NULL) {
+					if (ni1->ni_associd != 0) {
+						skb1 = skb;
+						skb = NULL;
+					}
+					/* XXX statistic? */
+					ieee80211_free_node(ic, ni1);
+				}
+			}
+			if (skb1 != NULL) {
+				len = skb1->len;
+				skb1->dev = dev;
+				skb1->mac.raw = skb1->data;
+				skb1->nh.raw = skb1->data + 
+					sizeof(struct ether_header);
+				skb1->protocol = __constant_htons(ETH_P_802_2);
+				dev_queue_xmit(skb1);
+			}
+		}
+		if (skb != NULL) {
+			skb->dev = dev;
+			skb->protocol = eth_type_trans(skb, dev);
+			if (ni->ni_vlan != 0 && ic->ic_vlgrp != NULL) {
+				/* attach vlan tag */
+				vlan_hwaccel_receive_skb(skb, ic->ic_vlgrp,
+					ni->ni_vlan);
+			} else {
+				netif_rx(skb);
+			}
+			dev->last_rx = jiffies;
+		}
+		return;
+
+	case IEEE80211_FC0_TYPE_MGT:
+		if (dir != IEEE80211_FC1_DIR_NODS) {
+			ic->ic_stats.is_rx_wrongdir++;
+			goto err;
+		}
+		if (skb->len < sizeof(struct ieee80211_frame)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: mgt data frame too short, len %u\n",
+				__func__, skb->len));
+			ic->ic_stats.is_rx_tooshort++;
+			goto out;
+		}
+#ifdef IEEE80211_DEBUG
+		if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
+		    ieee80211_msg_dumppkts(ic)) {
+			if_printf(ic->ic_dev, "[%s] received %s rssi %d\n",
+			    ether_sprintf(wh->i_addr2),
+			    ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT], rssi);
+		}
+#endif
+		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+				/*
+				 * Only shared key auth frames with a challenge
+				 * should be encrypted, discard all others.
+				 */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+					("[%s] discard %s with WEP\n",
+					ether_sprintf(wh->i_addr2),
+					ieee80211_mgt_subtype_name[subtype >>
+					    IEEE80211_FC0_SUBTYPE_SHIFT]));
+				ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
+				goto out;
+			}
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				/*
+				 * Discard encrypted frames when privacy is off.
+				 */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
+					("[%s] discard WEP mgt frame 'cuz "
+					"PRIVACY off\n",
+					ether_sprintf(wh->i_addr2)));
+				ic->ic_stats.is_rx_noprivacy++;
+				goto out;
+			}
+			key = ieee80211_crypto_decap(ic, ni, skb);
+			if (key == NULL) {
+				/* NB: stats+msgs handled in crypto_decap */
+				goto out;
+			}
+		}
+		(*ic->ic_recv_mgmt)(ic, skb, ni, subtype, rssi, rstamp);
+		dev_kfree_skb(skb);
+		return;
+
+	case IEEE80211_FC0_TYPE_CTL:
+		ic->ic_stats.is_rx_ctl++;
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+			goto out;
+		switch (subtype) {
+		case IEEE80211_FC0_SUBTYPE_PS_POLL:
+			/* XXX statistic */
+			/* Dump out a single packet from the host */
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+				("got power save probe from %s\n",
+				ether_sprintf(wh->i_addr2)));
+			ieee80211_recv_pspoll(ic, ni, skb);
+			break;
+		}
+		goto out;
+
+	default:
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: bad frame type %x\n", __func__, type));
+		/* should not come here */
+		break;
+	}
+err:
+	ic->ic_devstats->rx_errors++;
+out:
+	if (skb != NULL)
+		dev_kfree_skb(skb);
+#undef HAS_SEQ
+#undef SEQ_LEQ
+}
+EXPORT_SYMBOL(ieee80211_input);
+
+/*
+ * This function reassemble fragments using the skb of the 1st fragment,
+ * if large enough. If not, a new skb is allocated to hold incoming
+ * fragments.
+ *
+ * Fragments are copied at the end of the previous fragment.  A different
+ * strategy could have been used, where a non-linear skb is allocated and
+ * fragments attached to that skb.
+ */
+static struct sk_buff *
+ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct sk_buff *skb)
+{
+	struct ieee80211_frame *wh = (struct ieee80211_frame *) skb->data;
+	u_int16_t rxseq, last_rxseq;
+	u_int8_t fragno, last_fragno;
+	u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
+
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+		/* Do not keep fragments of multicast frames */
+		return skb;
+	}
+		
+	rxseq = le16_to_cpu(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+	fragno = le16_to_cpu(*(u_int16_t *)wh->i_seq) & IEEE80211_SEQ_FRAG_MASK;
+
+	/* Quick way out, if there's nothing to defragment */
+	if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
+		return skb;
+
+	/*
+	 * Use this lock to make sure ni->ni_rxfrag[0] is
+	 * not freed by the timer process while we use it.
+	 * XXX bogus
+	 */
+	IEEE80211_NODE_LOCK(ic);
+
+	/*
+	 * Update the time stamp.  As a side effect, it
+	 * also makes sure that the timer will not change
+	 * ni->ni_rxfrag[0] for at least 1 second, or in
+	 * other words, for the remaining of this function.
+	 */
+	ni->ni_rxfragstamp = jiffies;
+
+	IEEE80211_NODE_UNLOCK(ic);
+
+	/*
+	 * Validate that fragment is in order and
+	 * related to the previous ones.
+	 */
+	if (ni->ni_rxfrag[0]) {
+		struct ieee80211_frame *lwh;
+
+		lwh = (struct ieee80211_frame *) ni->ni_rxfrag[0]->data;
+		last_rxseq = le16_to_cpu(*(u_int16_t *)lwh->i_seq) >>
+			IEEE80211_SEQ_SEQ_SHIFT;
+		last_fragno = le16_to_cpu(*(u_int16_t *)lwh->i_seq) &
+			IEEE80211_SEQ_FRAG_MASK;
+		if (rxseq != last_rxseq
+		    || fragno != last_fragno + 1
+		    || (!IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1))
+		    || (!IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2))
+		    || (ni->ni_rxfrag[0]->end - ni->ni_rxfrag[0]->tail <
+				skb->len)) {
+			/*
+			 * Unrelated fragment or no space for it,
+			 * clear current fragments
+			 */
+			dev_kfree_skb(ni->ni_rxfrag[0]);
+			ni->ni_rxfrag[0] = NULL;
+		}
+	}
+
+	/* If this is the first fragment */
+ 	if (ni->ni_rxfrag[0] == NULL && fragno == 0) {
+		ni->ni_rxfrag[0] = skb;
+		/* If more frags are coming */
+		if (more_frag) {
+			if (skb_is_nonlinear(skb)) {
+				/*
+				 * We need a continous buffer to
+				 * assemble fragments
+				 */
+				ni->ni_rxfrag[0] = skb_copy(skb, GFP_ATOMIC);
+				dev_kfree_skb(skb);
+			}
+			/*
+			 * Check that we have enough space to hold
+			 * incoming fragments
+			 */
+			else if (skb->end - skb->head < ic->ic_dev->mtu +
+				sizeof(sizeof(struct ieee80211_frame))) {
+				ni->ni_rxfrag[0] = skb_copy_expand(skb, 0,
+					(ic->ic_dev->mtu +
+					 sizeof(sizeof(struct ieee80211_frame)))
+				         - (skb->end - skb->head), GFP_ATOMIC);
+				dev_kfree_skb(skb);
+			}
+		}
+	} else {
+		if (ni->ni_rxfrag[0]) {
+			struct ieee80211_frame *lwh = (struct ieee80211_frame *)
+				ni->ni_rxfrag[0]->data;
+
+			/*
+			 * We know we have enough space to copy,
+			 * we've verified that before
+			 */
+			/* Copy current fragment at end of previous one */
+			memcpy(ni->ni_rxfrag[0]->tail,
+			       skb->data + sizeof(struct ieee80211_frame),
+			       skb->len - sizeof(struct ieee80211_frame)
+			);
+			/* Update tail and length */
+			skb_put(ni->ni_rxfrag[0],
+				skb->len - sizeof(struct ieee80211_frame));
+			/* Keep a copy of last sequence and fragno */
+			*(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq;
+		}
+		/* we're done with the fragment */
+		dev_kfree_skb(skb);
+	}
+		
+	if (more_frag) {
+		/* More to come */
+		skb = NULL;
+	} else {
+		/* Last fragment received, we're done! */
+		skb = ni->ni_rxfrag[0];
+		ni->ni_rxfrag[0] = NULL;
+	}
+	return skb;
+}
+
+static struct sk_buff *
+ieee80211_decap(struct ieee80211com *ic, struct sk_buff *skb)
+{
+	struct ether_header *eh;
+	struct ieee80211_frame wh;
+	struct llc *llc;
+	u_short ether_type = 0;
+
+	memcpy(&wh, skb->data, sizeof(struct ieee80211_frame));
+	llc = (struct llc *) skb_pull(skb, sizeof(struct ieee80211_frame));
+	if (skb->len >= sizeof(struct llc) &&
+	    llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP &&
+	    llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 &&
+	    llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) {
+		ether_type = llc->llc_un.type_snap.ether_type;
+		skb_pull(skb, sizeof(struct llc));
+		llc = NULL;
+	}
+	eh = (struct ether_header *) skb_push(skb, sizeof(struct ether_header));
+	switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+	case IEEE80211_FC1_DIR_NODS:
+		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
+		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
+		break;
+	case IEEE80211_FC1_DIR_TODS:
+		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
+		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
+		break;
+	case IEEE80211_FC1_DIR_FROMDS:
+		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
+		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
+		break;
+	case IEEE80211_FC1_DIR_DSTODS:
+		/* not yet supported */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: discard DS to DS frame\n", __func__));
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	if (!ALIGNED_POINTER(skb->data + sizeof(*eh), u_int32_t)) {
+		struct sk_buff *n;
+
+		/* XXX does this always work? */
+		n = skb_copy(skb, GFP_ATOMIC);
+		dev_kfree_skb(skb);
+		if (n == NULL)
+			return NULL;
+		skb = n;
+		eh = (struct ether_header *) skb->data;
+	}
+	if (llc != NULL)
+		eh->ether_type = htons(skb->len - sizeof(*eh));
+	else
+		eh->ether_type = ether_type;
+	return skb;
+}
+
+/*
+ * Install received rate set information in the node's state block.
+ */
+static int
+ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
+	u_int8_t *rates, u_int8_t *xrates, int flags)
+{
+	struct ieee80211_rateset *rs = &ni->ni_rates;
+
+	memset(rs, 0, sizeof(*rs));
+	rs->rs_nrates = rates[1];
+	memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
+	if (xrates != NULL) {
+		u_int8_t nxrates;
+		/*
+		 * Tack on 11g extended supported rate element.
+		 */
+		nxrates = xrates[1];
+		if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
+			nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
+				("%s: extended rate set too large;"
+				" only using %u of %u rates\n",
+				__func__, nxrates, xrates[1]));
+			ic->ic_stats.is_rx_rstoobig++;
+		}
+		memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
+		rs->rs_nrates += nxrates;
+	}
+	return ieee80211_fix_rate(ic, ni, flags);
+}
+
+static void
+ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
+    struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq,
+    u_int16_t status)
+{
+	int allocbs;
+
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_IBSS:
+		if (ic->ic_state != IEEE80211_S_RUN ||
+		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		ieee80211_new_state(ic, IEEE80211_S_AUTH,
+		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+		break;
+
+	case IEEE80211_M_AHDEMO:
+		/* should not come here */
+		break;
+
+	case IEEE80211_M_HOSTAP:
+		if (ic->ic_state != IEEE80211_S_RUN ||
+		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		/* always accept open authentication requests */
+		if (ni == ic->ic_bss) {
+			ni = ieee80211_dup_bss(ic, wh->i_addr2);
+			if (ni == NULL)
+				return;
+			allocbs = 1;
+		} else
+			allocbs = 0;
+		ni->ni_rssi = rssi;
+		ni->ni_rstamp = rstamp;
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+			("station %s %s authenticated (open)\n",
+			ether_sprintf(ni->ni_macaddr),
+			(allocbs ? "newly" : "already")));
+		break;
+
+	case IEEE80211_M_STA:
+		if (ic->ic_state != IEEE80211_S_AUTH ||
+		    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		if (status != 0) {
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+			    ("open authentication failed (reason %d) for %s\n",
+			    status,
+			    ether_sprintf(wh->i_addr3)));
+			/* XXX can this happen? */
+			if (ni != ic->ic_bss)
+				ni->ni_fails++;
+			ic->ic_stats.is_rx_auth_fail++;
+			return;
+		}
+		ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+		break;
+	case IEEE80211_M_MONITOR:
+		break;
+	}
+}
+
+static int
+alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	if (ni->ni_challenge == NULL)
+		MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN,
+		    M_DEVBUF, M_NOWAIT);
+	if (ni->ni_challenge == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+			("%s: challenge alloc failed\n", __func__));
+		/* XXX statistic */
+	}
+	return (ni->ni_challenge != NULL);
+}
+
+/* XXX TODO: add statistics */
+static void
+ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
+    u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi,
+    u_int32_t rstamp, u_int16_t seq, u_int16_t status)
+{
+	u_int8_t *challenge;
+	int allocbs, estatus;
+
+	/*
+	 * NB: this can happen as we allow pre-shared key
+	 * authentication to be enabled w/o wep being turned
+	 * on so that configuration of these can be done
+	 * in any order.  It may be better to enforce the
+	 * ordering in which case this check would just be
+	 * for sanity/consistency.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			("%s: WEP is off\n", __func__));
+		estatus = IEEE80211_STATUS_ALG;
+		goto bad;
+	}
+	/*
+	 * Pre-shared key authentication is evil; accept
+	 * it only if explicitly configured (it is supported
+	 * mainly for compatibility with clients like OS X).
+	 */
+	if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+	    ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			("%s: operating in %u mode, reject\n",
+			 __func__, ni->ni_authmode));
+		ic->ic_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
+		estatus = IEEE80211_STATUS_ALG;
+		goto bad;
+	}
+
+	challenge = NULL;
+	if (frm + 1 < efrm) {
+		if ((frm[1] + 2) > (efrm - frm)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				("%s: elt %d %d bytes too long\n", __func__,
+				frm[0], (frm[1] + 2) - (efrm - frm)));
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+		if (*frm == IEEE80211_ELEMID_CHALLENGE)
+			challenge = frm;
+		frm += frm[1] + 2;
+	}
+	switch (seq) {
+	case IEEE80211_AUTH_SHARED_CHALLENGE:
+	case IEEE80211_AUTH_SHARED_RESPONSE:
+		if (challenge == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				("%s: no challenge sent\n", __func__));
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				("%s: bad challenge len %d\n",
+				__func__, challenge[1]));
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+	default:
+		break;
+	}
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_MONITOR:
+	case IEEE80211_M_AHDEMO:
+	case IEEE80211_M_IBSS:
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			("%s: unexpected operating mode\n", __func__));
+		return;
+	case IEEE80211_M_HOSTAP:
+		if (ic->ic_state != IEEE80211_S_RUN) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				("%s: not running\n", __func__));
+			estatus = IEEE80211_STATUS_ALG;	/* XXX */
+			goto bad;
+		}
+		switch (seq) {
+		case IEEE80211_AUTH_SHARED_REQUEST:
+			if (ni == ic->ic_bss) {
+				ni = ieee80211_dup_bss(ic, wh->i_addr2);
+				if (ni == NULL) {
+					/* NB: no way to return an error */
+					return;
+				}
+				allocbs = 1;
+			} else
+				allocbs = 0;
+			ni->ni_rssi = rssi;
+			ni->ni_rstamp = rstamp;
+			if (!alloc_challenge(ic, ni)) {
+				/* NB: don't return error so they rexmit */
+				return;
+			}
+			get_random_bytes(ni->ni_challenge,
+				IEEE80211_CHALLENGE_LEN);
+			IEEE80211_DPRINTF(ic,
+				IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+				("shared key %sauth request from station %s\n",
+				(allocbs ? "" : "re"),
+				ether_sprintf(ni->ni_macaddr)));
+			break;
+		case IEEE80211_AUTH_SHARED_RESPONSE:
+			if (ni == ic->ic_bss) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+					("%s: unknown STA\n", __func__));
+				/* NB: don't send a response */
+				return;
+			}
+			if (ni->ni_challenge == NULL) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				    ("%s: no challenge recorded\n", __func__));
+				ic->ic_stats.is_rx_bad_auth++;
+				estatus = IEEE80211_STATUS_CHALLENGE;
+				goto bad;
+			}
+			if (memcmp(ni->ni_challenge, &challenge[2],
+			           challenge[1]) != 0) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+					("%s: challenge mismatch\n", __func__));
+				ic->ic_stats.is_rx_auth_fail++;
+				estatus = IEEE80211_STATUS_CHALLENGE;
+				goto bad;
+			}
+			IEEE80211_DPRINTF(ic,
+				IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+				("station %s authenticated (shared key)\n",
+				ether_sprintf(ni->ni_macaddr)));
+			break;
+		default:
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				("%s: bad shared key auth seq %d from %s\n",
+				__func__, seq, ether_sprintf(wh->i_addr2)));
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_SEQUENCE;
+			goto bad;
+		}
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+		break;
+
+	case IEEE80211_M_STA:
+		if (ic->ic_state != IEEE80211_S_AUTH)
+			return;
+		switch (seq) {
+		case IEEE80211_AUTH_SHARED_PASS:
+			if (ni->ni_challenge != NULL) {
+				FREE(ni->ni_challenge, M_DEVBUF);
+				ni->ni_challenge = NULL;
+			}
+			if (status != 0) {
+				IEEE80211_DPRINTF(ic,
+				    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+				    ("%s: auth failed (reason %d) for %s\n",
+				    __func__, status,
+				    ether_sprintf(wh->i_addr3)));
+				/* XXX can this happen? */
+				if (ni != ic->ic_bss)
+					ni->ni_fails++;
+				ic->ic_stats.is_rx_auth_fail++;
+				return;
+			}
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			break;
+		case IEEE80211_AUTH_SHARED_CHALLENGE:
+			if (!alloc_challenge(ic, ni))
+				return;
+			/* XXX could optimize by passing recvd challenge */
+			memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
+			IEEE80211_SEND_MGMT(ic, ni,
+				IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+			break;
+		default:
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			    ("%s: bad seq %d from %s\n",
+			    __func__, seq, ether_sprintf(wh->i_addr2)));
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		break;
+	}
+	return;
+bad:
+	/*
+	 * Send an error response; but only when operating as an AP.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		/* XXX hack to workaround calling convention */
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH,
+			(seq + 1) | (estatus<<16));
+	}
+}
+
+/* Verify the existence and length of __elem or get out. */
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {			\
+	if ((__elem) == NULL) {						\
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID,		\
+			("%s: no " #__elem "in %s frame\n",		\
+			__func__, ieee80211_mgt_subtype_name[subtype >>	\
+				IEEE80211_FC0_SUBTYPE_SHIFT]));		\
+		ic->ic_stats.is_rx_elem_missing++;			\
+		return;							\
+	}								\
+	if ((__elem)[1] > (__maxlen)) {					\
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID,		\
+			("%s: bad " #__elem " len %d in %s frame from %s\n",\
+			__func__, (__elem)[1],				\
+			ieee80211_mgt_subtype_name[subtype >>		\
+				IEEE80211_FC0_SUBTYPE_SHIFT],		\
+			ether_sprintf(wh->i_addr2)));			\
+		ic->ic_stats.is_rx_elem_toobig++;			\
+		return;							\
+	}								\
+} while (0)
+
+#define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
+	if ((_len) < (_minlen)) {					\
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID,		\
+			("%s: %s frame too short from %s\n",		\
+			__func__,					\
+			ieee80211_mgt_subtype_name[subtype >>		\
+				IEEE80211_FC0_SUBTYPE_SHIFT],		\
+			ether_sprintf(wh->i_addr2)));			\
+		ic->ic_stats.is_rx_elem_toosmall++;			\
+		return;							\
+	}								\
+} while (0)
+
+#ifdef IEEE80211_DEBUG
+static void
+ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
+	u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid)
+{
+	printf("[%s] %s req ssid mismatch: ", ether_sprintf(mac), tag);
+	ieee80211_print_essid(ssid + 2, ssid[1]);
+	printf("\n");
+}
+
+#define	IEEE80211_VERIFY_SSID(_ni, _ssid, _packet_type) do {		\
+	if ((_ssid)[1] != 0 &&						\
+	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
+		if (ieee80211_msg_input(ic))				\
+			ieee80211_ssid_mismatch(ic, _packet_type,	\
+				wh->i_addr2, _ssid);			\
+		ic->ic_stats.is_rx_ssidmismatch++;			\
+		return;							\
+	}								\
+} while (0)
+#else /* !IEEE80211_DEBUG */
+#define	IEEE80211_VERIFY_SSID(_ni, _ssid, _packet_type) do {		\
+	if ((_ssid)[1] != 0 &&						\
+	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
+		ic->ic_stats.is_rx_ssidmismatch++;			\
+		return;							\
+	}								\
+} while (0)
+#endif /* !IEEE80211_DEBUG */
+
+/* unalligned little endian access */     
+#define LE_READ_2(p)							\
+	((u_int16_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)							\
+	((u_int32_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8) |	\
+	  (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
+
+static int __inline
+iswpaoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+isatherosoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+/*
+ * Convert a WPA cipher selector OUI to an internal
+ * cipher algorithm.  Where appropriate we also
+ * record any key length.
+ */
+static int
+wpa_cipher(u_int8_t *sel, u_int8_t *keylen)
+{
+#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case WPA_SEL(WPA_CSE_NULL):
+		return IEEE80211_CIPHER_NONE;
+	case WPA_SEL(WPA_CSE_WEP40):
+		if (keylen)
+			*keylen = 40 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case WPA_SEL(WPA_CSE_WEP104):
+		if (keylen)
+			*keylen = 104 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case WPA_SEL(WPA_CSE_TKIP):
+		return IEEE80211_CIPHER_TKIP;
+	case WPA_SEL(WPA_CSE_CCMP):
+		return IEEE80211_CIPHER_AES_CCM;
+	}
+	return 32;		/* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert a WPA key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+wpa_keymgmt(u_int8_t *sel)
+{
+#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
+		return WPA_ASE_8021X_UNSPEC;
+	case WPA_SEL(WPA_ASE_8021X_PSK):
+		return WPA_ASE_8021X_PSK;
+	case WPA_SEL(WPA_ASE_NONE):
+		return WPA_ASE_NONE;
+	}
+	return 0;		/* NB: so is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Parse a WPA information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, struct ieee80211_rsnparms *rsn)
+{
+	u_int8_t len = frm[1];
+	u_int32_t w;
+	int n;
+
+	/*
+	 * Check the length once for fixed parts: OUI, type,
+	 * version, mcast cipher, and 2 selector counts.
+	 * Other, variable-length data, must be checked separately.
+	 */
+	KASSERT(ic->ic_flags & IEEE80211_F_WPA1,
+		("not WPA, flags 0x%x", ic->ic_flags));
+	if (len < 14) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: length %u too short\n", __func__, len));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 6, len -= 4;		/* NB: len is payload only */
+	/* NB: iswapoui already validated the OUI and type */
+	w = LE_READ_2(frm);
+	if (w != WPA_VERSION) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: bad version %u\n", __func__, w));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2, len -= 2;
+
+	/* multicast/group cipher */
+	w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+	if (w != rsn->rsn_mcastcipher) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: mcast cipher mismatch; got %u, expected %u\n",
+				__func__, w, rsn->rsn_mcastcipher));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 4, len -= 4;
+
+	/* unicast ciphers */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4+2) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: not enough data for ucast ciphers; len %u, n %u\n",
+				__func__, len, n));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_ucastcipherset;
+	if (w == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: ucast cipher set empty\n", __func__));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & (1<<IEEE80211_CIPHER_TKIP))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+	else
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+	/* key management algorithms */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: not enough data for key mgmt algorithms; len %u, n %u\n",
+				__func__, len, n));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= wpa_keymgmt(frm);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_keymgmtset;
+	if (w == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: no acceptable key mgmt algorithms\n", __func__));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & WPA_ASE_8021X_UNSPEC)
+		rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
+	else
+		rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+	if (len > 2)		/* optional capabilities */
+		rsn->rsn_caps = LE_READ_2(frm);
+
+	return 0;
+}
+
+/*
+ * Convert an RSN cipher selector OUI to an internal
+ * cipher algorithm.  Where appropriate we also
+ * record any key length.
+ */
+static int
+rsn_cipher(u_int8_t *sel, u_int8_t *keylen)
+{
+#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case RSN_SEL(RSN_CSE_NULL):
+		return IEEE80211_CIPHER_NONE;
+	case RSN_SEL(RSN_CSE_WEP40):
+		if (keylen)
+			*keylen = 40 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case RSN_SEL(RSN_CSE_WEP104):
+		if (keylen)
+			*keylen = 104 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case RSN_SEL(RSN_CSE_TKIP):
+		return IEEE80211_CIPHER_TKIP;
+	case RSN_SEL(RSN_CSE_CCMP):
+		return IEEE80211_CIPHER_AES_CCM;
+	case RSN_SEL(RSN_CSE_WRAP):
+		return IEEE80211_CIPHER_AES_OCB;
+	}
+	return 32;		/* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert an RSN key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+rsn_keymgmt(u_int8_t *sel)
+{
+#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
+		return RSN_ASE_8021X_UNSPEC;
+	case RSN_SEL(RSN_ASE_8021X_PSK):
+		return RSN_ASE_8021X_PSK;
+	case RSN_SEL(RSN_ASE_NONE):
+		return RSN_ASE_NONE;
+	}
+	return 0;		/* NB: so is discarded */
+#undef RSN_SEL
+}
+
+/*
+ * Parse a WPA/RSN information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, struct ieee80211_rsnparms *rsn)
+{
+	u_int8_t len = frm[1];
+	u_int32_t w;
+	int n;
+
+	/*
+	 * Check the length once for fixed parts: 
+	 * version, mcast cipher, and 2 selector counts.
+	 * Other, variable-length data, must be checked separately.
+	 */
+	KASSERT(ic->ic_flags & IEEE80211_F_WPA2,
+		("not RSN, flags 0x%x", ic->ic_flags));
+	if (len < 10) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: length %u too short\n", __func__, len));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2;		/* skip id+len */
+	w = LE_READ_2(frm);
+	if (w != RSN_VERSION) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: bad version %u\n", __func__, w));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2, len -= 2;
+
+	/* multicast/group cipher */
+	w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+	if (w != rsn->rsn_mcastcipher) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: mcast cipher mismatch; got %u, expected %u\n",
+				__func__, w, rsn->rsn_mcastcipher));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 4, len -= 4;
+
+	/* unicast ciphers */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4+2) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: not enough data for ucast ciphers; len %u, n %u\n",
+				__func__, len, n));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_ucastcipherset;
+	if (w == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: ucast cipher set empty\n", __func__));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & (1<<IEEE80211_CIPHER_TKIP))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+	else
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+	/* key management algorithms */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: not enough data for key mgmt algorithms; len %u, n %u\n",
+				__func__, len, n));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= rsn_keymgmt(frm);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_keymgmtset;
+	if (w == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+			("%s: no acceptable key mgmt algorithms\n", __func__));
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & RSN_ASE_8021X_UNSPEC)
+		rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
+	else
+		rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
+
+	/* optional RSN capabilities */
+	if (len > 2)
+		rsn->rsn_caps = LE_READ_2(frm);
+	/* XXXPMKID */
+
+	return 0;
+}
+
+static void
+ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie)
+{
+	u_int ielen = ie[1]+2;
+	/*
+	 * Record information element for later use.
+	 */
+	if (*iep == NULL || (*iep)[1] != ie[1]) {
+		if (*iep != NULL)
+			FREE(*iep, M_DEVBUF);
+		MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT);
+	}
+	if (*iep != NULL)
+		memcpy(*iep, ie, ielen);
+}
+
+#ifdef IEEE80211_DEBUG
+static void
+dump_probe_beacon(u_int8_t subtype, int isnew,
+	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	u_int8_t chan, u_int8_t bchan, u_int16_t capinfo, u_int16_t bintval,
+	u_int8_t erp, u_int8_t *ssid, u_int8_t *country)
+{
+	printf("[%s] %s%s on chan %u (bss chan %u) ",
+	    ether_sprintf(mac), isnew ? "new " : "",
+	    (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) ? "probe response" : "beacon",
+	    chan, bchan);
+	ieee80211_print_essid(ssid + 2, ssid[1]);
+	printf("\n");
+
+	if (isnew) {
+		printf("[%s] caps 0x%x bintval %u erp 0x%x", 
+			ether_sprintf(mac), capinfo, bintval, erp);
+		if (country) {
+#ifdef __FreeBSD__
+			printf(" country info %*D", country[1], country+2, " ");
+#else
+			int i;
+			printf(" country info");
+			for (i = 0; i < country[1]; i++)
+				printf(" %02x", country[i+2]);
+#endif
+		}
+		printf("\n");
+	}
+}
+#endif /* IEEE80211_DEBUG */
+
+void
+ieee80211_recv_mgmt(struct ieee80211com *ic, struct sk_buff *skb,
+	struct ieee80211_node *ni,
+	int subtype, int rssi, u_int32_t rstamp)
+{
+#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
+	struct ieee80211_frame *wh;
+	u_int8_t *frm, *efrm;
+	u_int8_t *ssid, *rates, *xrates, *wpa;
+	int reassoc, resp, allocbs;
+
+	wh = (struct ieee80211_frame *) skb->data;
+	frm = (u_int8_t *)&wh[1];
+	efrm = skb->data + skb->len;
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+	case IEEE80211_FC0_SUBTYPE_BEACON: {
+		u_int8_t *tstamp, *country, *wpa;
+		u_int8_t chan, bchan, fhindex, erp;
+		u_int16_t capinfo, bintval, timoff;
+		u_int16_t fhdwell;
+
+		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+			/*
+			 * Count beacon frames specially, some drivers
+			 * use this info to do things like update LED's.
+			 */
+			ic->ic_stats.is_rx_beacon++;
+		}
+		/*
+		 * We process beacon/probe response frames for:
+		 *    o station mode when associated: to collect state
+		 *      updates such as 802.11g slot time
+		 *    o adhoc mode: to discover neighbors
+		 *    o when scanning
+		 * Frames otherwise received are discarded.
+		 */ 
+		if (!((ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd != 0)
+		    || ic->ic_opmode == IEEE80211_M_IBSS
+		    || ic->ic_state == IEEE80211_S_SCAN)) {
+			/* XXX: may be useful for background scan */
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+		/*
+		 * beacon/probe response frame format
+		 *	[8] time stamp
+		 *	[2] beacon interval
+		 *	[2] capability information
+		 *	[tlv] ssid
+		 *	[tlv] supported rates
+		 *	[tlv] country information
+		 *	[tlv] parameter set (FH/DS)
+		 *	[tlv] erp information
+		 *	[tlv] extended supported rates
+		 *	[tlv] WPA or RSN
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+		tstamp  = frm;				frm += 8;
+		bintval = le16toh(*(u_int16_t *)frm);	frm += 2;
+		capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
+		ssid = rates = xrates = country = wpa = NULL;
+		bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+		chan = bchan;
+		fhdwell = 0;
+		fhindex = 0;
+		erp = 0;
+		timoff = 0;
+		while (frm < efrm) {
+			switch (*frm) {
+			case IEEE80211_ELEMID_SSID:
+				ssid = frm;
+				break;
+			case IEEE80211_ELEMID_RATES:
+				rates = frm;
+				break;
+			case IEEE80211_ELEMID_COUNTRY:
+				country = frm;
+				break;
+			case IEEE80211_ELEMID_FHPARMS:
+				if (ic->ic_phytype == IEEE80211_T_FH) {
+					fhdwell = LE_READ_2(&frm[2]);
+					chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+					fhindex = frm[6];
+				}
+				break;
+			case IEEE80211_ELEMID_DSPARMS:
+				/*
+				 * XXX hack this since depending on phytype
+				 * is problematic for multi-mode devices.
+				 */
+				if (ic->ic_phytype != IEEE80211_T_FH)
+					chan = frm[2];
+				break;
+			case IEEE80211_ELEMID_TIM:
+				/* XXX ATIM? */
+				timoff = frm - skb->data;
+				break;
+			case IEEE80211_ELEMID_IBSSPARMS:
+				break;
+			case IEEE80211_ELEMID_XRATES:
+				xrates = frm;
+				break;
+			case IEEE80211_ELEMID_ERP:
+				if (frm[1] != 1) {
+					IEEE80211_DPRINTF(ic,
+						IEEE80211_MSG_ELEMID,
+						("%s: invalid ERP element; "
+						"length %u, expecting 1\n",
+						__func__, frm[1]));
+					ic->ic_stats.is_rx_elem_toobig++;
+					break;
+				}
+				erp = frm[2];
+				break;
+			case IEEE80211_ELEMID_RSN:
+				wpa = frm;
+				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswpaoui(frm))
+					wpa = frm;
+				/* XXX Atheros OUI support */
+				break;
+			default:
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID,
+					("%s: element id %u/len %u ignored\n",
+					__func__, *frm, frm[1]));
+				ic->ic_stats.is_rx_elem_unknown++;
+				break;
+			}
+			frm += frm[1] + 2;
+		}
+		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
+		if (
+#if IEEE80211_CHAN_MAX < 255
+		    chan > IEEE80211_CHAN_MAX ||
+#endif
+		    isclr(ic->ic_chan_active, chan)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID,
+				("%s: ignore %s with invalid channel %u\n",
+				__func__,
+				ISPROBE(subtype) ? "probe response" : "beacon",
+				chan));
+			ic->ic_stats.is_rx_badchan++;
+			return;
+		}
+		if (chan != bchan && ic->ic_phytype != IEEE80211_T_FH) {
+			/*
+			 * Frame was received on a channel different from the
+			 * one indicated in the DS params element id;
+			 * silently discard it.
+			 *
+			 * NB: this can happen due to signal leakage.
+			 *     But we should take it for FH phy because
+			 *     the rssi value should be correct even for
+			 *     different hop pattern in FH.
+			 */
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID,
+				("%s: ignore %s on channel %u marked "
+				"for channel %u\n", __func__,
+				ISPROBE(subtype) ? "probe response" : "beacon",
+				bchan, chan));
+			ic->ic_stats.is_rx_chanmismatch++;
+			return;
+		}
+
+		/*
+		 * Station mode, check for state updates.  We
+		 * consider only 11g stuff right now.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd != 0) {
+			if (ni->ni_erp != erp) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    ("erp change from %s: was 0x%x, now 0x%x\n",
+				    ether_sprintf(wh->i_addr2),
+				    ni->ni_erp, erp));
+				if (erp & IEEE80211_ERP_USE_PROTECTION)
+					ic->ic_flags |= IEEE80211_F_USEPROT;
+				else
+					ic->ic_flags &= ~IEEE80211_F_USEPROT;
+				ni->ni_erp = erp;
+				/* XXX statistic */
+			}
+			if ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    ("capabilities change from %s: before 0x%x,"
+				     " now 0x%x\n", ether_sprintf(wh->i_addr2),
+				     ni->ni_capinfo, capinfo));
+				/*
+				 * NB: we assume short preamble doesn't
+				 *     change dynamically
+				 */
+				ieee80211_set_shortslottime(ic,
+					ic->ic_curmode == IEEE80211_MODE_11A ||
+					(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+				ni->ni_capinfo = capinfo;
+				/* XXX statistic */
+			}
+			return;
+		}
+
+		/*
+		 * Use mac and channel for lookup so we collect all
+		 * potential AP's when scanning.  Otherwise we may
+		 * see the same AP on multiple channels and will only
+		 * record the last one.  We could filter APs here based
+		 * on rssi, etc. but leave that to the end of the scan
+		 * so we can keep the selection criteria in one spot.
+		 * This may result in a bloat of the scanned AP list but
+		 * it shouldn't be too much.
+		 */
+		ni = ieee80211_find_node_with_channel(ic, wh->i_addr2,
+				&ic->ic_channels[chan]);
+		if (ni == NULL) {
+#ifdef IEEE80211_DEBUG
+			if (ieee80211_msg_scan(ic))
+				dump_probe_beacon(subtype, 1,
+				    wh->i_addr2, chan, bchan, capinfo,
+				    bintval, erp, ssid, country);
+#endif
+			ni = ieee80211_dup_bss(ic, wh->i_addr2);
+			if (ni == NULL)
+				return;
+			ni->ni_esslen = ssid[1];
+			memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
+			memcpy(ni->ni_essid, ssid + 2, ssid[1]);
+		} else if (ssid[1] != 0 &&
+		    (ISPROBE(subtype) || ni->ni_esslen == 0)) {
+			/*
+			 * Update ESSID at probe response to adopt hidden AP by
+			 * Lucent/Cisco, which announces null ESSID in beacon.
+			 */
+			ni->ni_esslen = ssid[1];
+			memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
+			memcpy(ni->ni_essid, ssid + 2, ssid[1]);
+#ifdef IEEE80211_DEBUG
+			if (ieee80211_msg_scan(ic) || ieee80211_msg_debug(ic))
+				dump_probe_beacon(subtype, 0,
+				    wh->i_addr2, chan, bchan, capinfo,
+				    bintval, erp, ssid, country);
+#endif
+		}
+		IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+		ni->ni_rssi = rssi;
+		ni->ni_rstamp = rstamp;
+		memcpy(ni->ni_tstamp.data, tstamp, sizeof(ni->ni_tstamp));
+		ni->ni_intval = bintval;
+		ni->ni_capinfo = capinfo;
+		/* XXX validate channel # */
+		ni->ni_chan = &ic->ic_channels[chan];
+		ni->ni_fhdwell = fhdwell;
+		ni->ni_fhindex = fhindex;
+		ni->ni_erp = erp;
+		/*
+		 * Record the byte offset from the mac header to
+		 * the start of the TIM information element for
+		 * use by hardware and/or to speedup software
+		 * processing of beacon frames.
+		 */
+		ni->ni_timoff = timoff;
+		/*
+		 * Record optional information elements that might be
+		 * used by applications or drivers.
+		 */
+		if (wpa != NULL)
+			ieee80211_saveie(&ni->ni_wpa_ie, wpa);
+		/* NB: must be after ni_chan is setup */
+		ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
+		ieee80211_unref_node(&ni);	/* NB: do not free */
+		break;
+	}
+
+	case IEEE80211_FC0_SUBTYPE_PROBE_REQ: {
+		u_int8_t rate;
+
+		if (ic->ic_opmode == IEEE80211_M_STA ||
+		    ic->ic_state != IEEE80211_S_RUN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+
+		/*
+		 * prreq frame format
+		 *	[tlv] ssid
+		 *	[tlv] supported rates
+		 *	[tlv] extended supported rates
+		 */
+		ssid = rates = xrates = NULL;
+		while (frm < efrm) {
+			switch (*frm) {
+			case IEEE80211_ELEMID_SSID:
+				ssid = frm;
+				break;
+			case IEEE80211_ELEMID_RATES:
+				rates = frm;
+				break;
+			case IEEE80211_ELEMID_XRATES:
+				xrates = frm;
+				break;
+			}
+			frm += frm[1] + 2;
+		}
+		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
+		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid, "probe");
+
+		if (ni == ic->ic_bss) {
+			ni = ieee80211_dup_bss(ic, wh->i_addr2);
+			if (ni == NULL)
+				return;
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				("%s: new probe req from %s\n",
+				__func__, ether_sprintf(wh->i_addr2)));
+			allocbs = 1;
+		} else
+			allocbs = 0;
+		ni->ni_rssi = rssi;
+		ni->ni_rstamp = rstamp;
+		rate = ieee80211_setup_rates(ic, ni, rates, xrates,
+				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
+				| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+		if (rate & IEEE80211_RATE_BASIC) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
+				("%s: rate negotiation failed: %s\n",
+				__func__,ether_sprintf(wh->i_addr2)));
+		} else {
+			IEEE80211_SEND_MGMT(ic, ni,
+				IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
+		}
+		if (allocbs) {
+			/*
+			 * When operating as an AP we discard the node's
+			 * state until the station requests authentication.
+			 * This may be better done by holding it and setting
+			 * a short timer for reclaiming it but reduces the
+			 * possibility of stations flooding us with probe
+			 * requests causing our memory use to grow quickly
+			 * (though this can still happen if they send
+			 * authentication requests).  When operating in ibss
+			 * mode we hold the node but with a zero reference
+			 * count; this is the current convention (XXX).
+			 */
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+				ieee80211_free_node(ic, ni);
+			else
+				ieee80211_unref_node(&ni);
+		}
+		break;
+	}
+
+	case IEEE80211_FC0_SUBTYPE_AUTH: {
+		u_int16_t algo, seq, status;
+		/*
+		 * auth frame format
+		 *	[2] algorithm
+		 *	[2] sequence
+		 *	[2] status
+		 *	[tlv*] challenge
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
+		algo   = le16toh(*(u_int16_t *)frm);
+		seq    = le16toh(*(u_int16_t *)(frm + 2));
+		status = le16toh(*(u_int16_t *)(frm + 4));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			("%s: algorithm %d seq %d from %s\n",
+			__func__, algo, seq, ether_sprintf(wh->i_addr2)));
+
+		/*
+		 * Consult the ACL policy module if setup.
+		 */
+		if (ic->ic_acl != NULL &&
+		    !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL,
+			    ("[%s] reject auth request by station due to ACL\n",
+			    ether_sprintf(wh->i_addr2)));
+			ic->ic_stats.is_rx_acl++;
+			return;
+		}
+		if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+			/* XXX only in ap mode? */
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+			    ("[%s] reject auth request by station due to TKIP "
+			    "countermeasures\n",
+			    ether_sprintf(wh->i_addr2)));
+			ic->ic_stats.is_rx_auth_countermeasures++;
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				IEEE80211_SEND_MGMT(ic, ni,
+					IEEE80211_FC0_SUBTYPE_AUTH,
+					IEEE80211_REASON_MIC_FAILURE);
+			}
+			return;
+		}
+		if (algo == IEEE80211_AUTH_ALG_SHARED)
+			ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
+			    rstamp, seq, status);
+		else if (algo == IEEE80211_AUTH_ALG_OPEN)
+			ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq,
+			    status);
+		else {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: unsupported auth algorithm %d from %s\n",
+				__func__, algo, ether_sprintf(wh->i_addr2)));
+			ic->ic_stats.is_rx_auth_unsupported++;
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				/* XXX not right */
+				IEEE80211_SEND_MGMT(ic, ni,
+					IEEE80211_FC0_SUBTYPE_AUTH,
+					(seq+1) | (IEEE80211_STATUS_ALG<<16));
+			}
+			return;
+		} 
+		break;
+	}
+
+	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
+		u_int16_t capinfo, bintval;
+		struct ieee80211_rsnparms rsn;
+		u_int8_t reason;
+
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
+		    ic->ic_state != IEEE80211_S_RUN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+
+		if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+			reassoc = 1;
+			resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
+		} else {
+			reassoc = 0;
+			resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
+		}
+		/*
+		 * asreq frame format
+		 *	[2] capability information
+		 *	[2] listen interval
+		 *	[6*] current AP address (reassoc only)
+		 *	[tlv] ssid
+		 *	[tlv] supported rates
+		 *	[tlv] extended supported rates
+		 *	[tlv] WPA or RSN
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
+		if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: ignore assoc request with bss %s not "
+				"our own\n",
+				__func__, ether_sprintf(wh->i_addr2)));
+			ic->ic_stats.is_rx_assoc_bss++;
+			return;
+		}
+		capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
+		bintval = le16toh(*(u_int16_t *)frm);	frm += 2;
+		if (reassoc)
+			frm += 6;	/* ignore current AP info */
+		ssid = rates = xrates = wpa = NULL;
+		while (frm < efrm) {
+			switch (*frm) {
+			case IEEE80211_ELEMID_SSID:
+				ssid = frm;
+				break;
+			case IEEE80211_ELEMID_RATES:
+				rates = frm;
+				break;
+			case IEEE80211_ELEMID_XRATES:
+				xrates = frm;
+				break;
+			/* XXX verify only one of RSN and WPA ie's? */
+			case IEEE80211_ELEMID_RSN:
+				wpa = frm;
+				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswpaoui(frm)) {
+					if (ic->ic_flags & IEEE80211_F_WPA1)
+						wpa = frm;
+				}
+				/* XXX Atheros OUI support */
+				break;
+			}
+			frm += frm[1] + 2;
+		}
+		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
+		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid, 
+			reassoc ? "reassoc" : "assoc");
+
+		if (ni == ic->ic_bss) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			    ("[%s] deny %sassoc, not authenticated\n",
+			    ether_sprintf(wh->i_addr2), reassoc ? "re" : ""));
+			ni = ieee80211_dup_bss(ic, wh->i_addr2);
+			if (ni != NULL) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH,
+				    IEEE80211_REASON_ASSOC_NOT_AUTHED);
+				ieee80211_free_node(ic, ni);
+			}
+			ic->ic_stats.is_rx_assoc_notauth++;
+			return;
+		}
+		if (wpa != NULL) {
+			/*
+			 * Parse WPA information element.  Note that
+			 * we initialize the param block from the node
+			 * state so that information in the IE overrides
+			 * our defaults.  The resulting parameters are
+			 * installed below after the association is assured.
+			 */
+			rsn = ni->ni_rsn;
+			if (wpa[0] != IEEE80211_ELEMID_RSN)
+				reason = ieee80211_parse_wpa(ic, wpa, &rsn);
+			else
+				reason = ieee80211_parse_rsn(ic, wpa, &rsn);
+			if (reason != 0) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				    ("%s: bad %s ie from %s, reason %u\n",
+				    __func__, wpa[0] != IEEE80211_ELEMID_RSN ?
+						"WPA" : "RSN",
+				    ether_sprintf(wh->i_addr2), reason));
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+				ieee80211_node_leave(ic, ni);
+				/* XXX distinguish WPA/RSN? */
+				ic->ic_stats.is_rx_assoc_badwpaie++;
+				return;
+			}
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+				("%s: %s ie from %s, "
+				"mc %u/%u uc %u/%u key %u caps 0x%x\n",
+				__func__, wpa[0] != IEEE80211_ELEMID_RSN ?
+					"WPA" : "RSN",
+				ether_sprintf(wh->i_addr2),
+				rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen,
+				rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen,
+				rsn.rsn_keymgmt, rsn.rsn_caps));
+		}
+		/* discard challenge after association */
+		if (ni->ni_challenge != NULL) {
+			FREE(ni->ni_challenge, M_DEVBUF);
+			ni->ni_challenge = NULL;
+		}
+		/* XXX some stations use the privacy bit for handling APs
+		       that suport both encrypted and unencrypted traffic */
+		/* NB: PRIVACY flag bits are assumed to match */
+		if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||
+		    (capinfo & IEEE80211_CAPINFO_PRIVACY) ^
+		    (ic->ic_flags & IEEE80211_F_PRIVACY)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: capability mismatch 0x%x for %s\n",
+				__func__, capinfo, ether_sprintf(wh->i_addr2)));
+			IEEE80211_SEND_MGMT(ic, ni, resp,
+				IEEE80211_STATUS_CAPINFO);
+			ieee80211_node_leave(ic, ni);
+			ic->ic_stats.is_rx_assoc_capmismatch++;
+			return;
+		}
+		ieee80211_setup_rates(ic, ni, rates, xrates,
+				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+		if (ni->ni_rates.rs_nrates == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: rate mismatch for %s\n",
+				__func__, ether_sprintf(wh->i_addr2)));
+			IEEE80211_SEND_MGMT(ic, ni, resp,
+				IEEE80211_STATUS_BASIC_RATE);
+			ieee80211_node_leave(ic, ni);
+			ic->ic_stats.is_rx_assoc_norate++;
+			return;
+		}
+		ni->ni_rssi = rssi;
+		ni->ni_rstamp = rstamp;
+		ni->ni_intval = bintval;
+		ni->ni_capinfo = capinfo;
+		ni->ni_chan = ic->ic_bss->ni_chan;
+		ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
+		ni->ni_fhindex = ic->ic_bss->ni_fhindex;
+		if (wpa != NULL) {
+			/*
+			 * Record WPA/RSN parameters for station, mark
+			 * node as using WPA and record information element
+			 * for applications that require it.
+			 */
+			ni->ni_rsn = rsn;
+			ieee80211_saveie(&ni->ni_wpa_ie, wpa);
+		}
+		ieee80211_node_join(ic, ni, resp);
+		break;
+	}
+
+	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
+		u_int16_t capinfo, associd;
+		u_int16_t status;
+
+		if (ic->ic_opmode != IEEE80211_M_STA ||
+		    ic->ic_state != IEEE80211_S_ASSOC) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+
+		/*
+		 * asresp frame format
+		 *	[2] capability information
+		 *	[2] status
+		 *	[2] association ID
+		 *	[tlv] supported rates
+		 *	[tlv] extended supported rates
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
+		ni = ic->ic_bss;
+		capinfo = le16toh(*(u_int16_t *)frm);
+		frm += 2;
+		status = le16toh(*(u_int16_t *)frm);
+		frm += 2;
+		if (status != 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				("%sassociation failed (reason %d) for %s\n",
+				ISREASSOC(subtype) ?  "re" : "",
+				status, ether_sprintf(wh->i_addr3)));
+			if (ni != ic->ic_bss)	/* XXX never true? */
+				ni->ni_fails++;
+			ic->ic_stats.is_rx_auth_fail++;	/* XXX */
+			return;
+		}
+		associd = le16toh(*(u_int16_t *)frm);
+		frm += 2;
+
+		rates = xrates = NULL;
+		while (frm < efrm) {
+			switch (*frm) {
+			case IEEE80211_ELEMID_RATES:
+				rates = frm;
+				break;
+			case IEEE80211_ELEMID_XRATES:
+				xrates = frm;
+				break;
+			}
+			frm += frm[1] + 2;
+		}
+
+		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+		ieee80211_setup_rates(ic, ni, rates, xrates,
+				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+		if (ni->ni_rates.rs_nrates == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				("%sassociation failed (rate set mismatch) "
+				"for %s\n",
+				ISREASSOC(subtype) ?  "re" : "",
+				ether_sprintf(wh->i_addr3)));
+			if (ni != ic->ic_bss)	/* XXX never true? */
+				ni->ni_fails++;
+			ic->ic_stats.is_rx_assoc_norate++;
+			return;
+		}
+
+		ni->ni_capinfo = capinfo;
+		ni->ni_associd = associd;
+		/*
+		 * Configure state now that we are associated.
+		 *
+		 * XXX may need different/additional driver callbacks?
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_11A ||
+		    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+		} else {
+			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+			ic->ic_flags |= IEEE80211_F_USEBARKER;
+		}
+		ieee80211_set_shortslottime(ic,
+			ic->ic_curmode == IEEE80211_MODE_11A ||
+			(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+		/*
+		 * Honor ERP protection.
+		 *
+		 * NB: ni_erp should zero for non-11g operation.
+		 * XXX check ic_curmode anyway?
+		 */
+		if (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)
+			ic->ic_flags |= IEEE80211_F_USEPROT;
+		else
+			ic->ic_flags &= ~IEEE80211_F_USEPROT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			("%sassociate with %s: %s preamble, %s slot time%s\n",
+			ISREASSOC(subtype) ?  "re" : "",
+			ether_sprintf(wh->i_addr2),
+			ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+			ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+			ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "")
+		);
+		ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
+		break;
+	}
+
+	case IEEE80211_FC0_SUBTYPE_DEAUTH: {
+		u_int16_t reason;
+
+		if (ic->ic_state == IEEE80211_S_SCAN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+		/*
+		 * deauth frame format
+		 *	[2] reason
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
+		reason = le16toh(*(u_int16_t *)frm);
+		ic->ic_stats.is_rx_deauth++;
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			ieee80211_new_state(ic, IEEE80211_S_AUTH,
+			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			break;
+		case IEEE80211_M_HOSTAP:
+			if (ni != ic->ic_bss) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+					("station %s deauthenticated by "
+					"peer (reason %d)\n",
+					ether_sprintf(ni->ni_macaddr), reason));
+				ieee80211_node_leave(ic, ni);
+			}
+			break;
+		default:
+			ic->ic_stats.is_rx_mgtdiscard++;
+			break;
+		}
+		break;
+	}
+
+	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+		u_int16_t reason;
+
+		if (ic->ic_state != IEEE80211_S_RUN &&
+		    ic->ic_state != IEEE80211_S_AUTH) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+		/*
+		 * disassoc frame format
+		 *	[2] reason
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
+		reason = le16toh(*(u_int16_t *)frm);
+		ic->ic_stats.is_rx_disassoc++;
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			break;
+		case IEEE80211_M_HOSTAP:
+			if (ni != ic->ic_bss) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+					("station %s disassociated by "
+					"peer (reason %d)\n",
+					ether_sprintf(ni->ni_macaddr), reason));
+				ieee80211_node_leave(ic, ni);
+			}
+			break;
+		default:
+			ic->ic_stats.is_rx_mgtdiscard++;
+			break;
+		}
+		break;
+	}
+	default:
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: mgmt frame with subtype 0x%x not handled\n",
+			__func__, subtype));
+		ic->ic_stats.is_rx_badsubtype++;
+		break;
+	}
+#undef ISREASSOC
+#undef ISPROBE
+}
+#undef IEEE80211_VERIFY_LENGTH
+#undef IEEE80211_VERIFY_ELEMENT
+
+static void
+ieee80211_recv_pspoll(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct sk_buff *skb0)
+{
+	struct ieee80211_frame_min *wh;
+	struct sk_buff *skb;
+	u_int16_t aid;
+
+	if (ic->ic_set_tim == NULL)	/* No powersaving functionality */
+		return;
+	if (ni == ic->ic_bss) {
+		wh = (struct ieee80211_frame_min *)skb0->data;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+			("station %s sent bogus power save poll\n",
+			ether_sprintf(wh->i_addr2)));
+		return;
+	}
+
+	wh = (struct ieee80211_frame_min *)skb0->data;
+	memcpy(&aid, wh->i_dur, sizeof(wh->i_dur));
+	if ((aid & 0xc000) != 0xc000) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+			("station %s sent bogus aid %x\n",
+			ether_sprintf(wh->i_addr2), aid));
+		/* XXX statistic */
+		return;
+	}
+	if (aid != ni->ni_associd) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+			("station %s aid %x doesn't match pspoll aid %x\n",
+			ether_sprintf(wh->i_addr2), ni->ni_associd, aid));
+		/* XXX statistic */
+		return;
+	}
+
+	/* Okay, take the first queued packet and put it out... */
+	IF_DEQUEUE(&ni->ni_savedq, skb);
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+			("station %s sent pspoll, but no packets are saved\n",
+			ether_sprintf(wh->i_addr2)));
+		/* XXX statistic */
+		return;
+	}
+	/* 
+	 * If this is the last packet, turn off the TIM fields.
+	 * If there are more packets, set the more packets bit.
+	 */
+	if (_IF_QLEN(&ni->ni_savedq) == 0) {
+		if (ic->ic_set_tim) 
+			(*ic->ic_set_tim)(ic, ni->ni_associd, 0);
+	} else {
+		wh = (struct ieee80211_frame_min *) skb->data;
+		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+	}
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		("enqueued power saving packet for station %s\n",
+		 ether_sprintf(ni->ni_macaddr)));
+	/* XXX need different driver interface */
+	(*ic->ic_dev->hard_start_xmit)(skb, ic->ic_dev);
+}
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_ioctl.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_ioctl.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_ioctl.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_ioctl.h	2005-02-24 13:06:17.308130784 -0800
@@ -0,0 +1,389 @@
+/*	$NetBSD: ieee80211_ioctl.h,v 1.5 2003/10/13 04:16:59 dyoung Exp $	*/
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.4 2003/10/17 23:15:30 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_IOCTL_H_
+#define _NET80211_IEEE80211_IOCTL_H_
+
+/*
+ * IEEE 802.11 ioctls.
+ */
+
+/*
+ * Per/node (station) statistics available when operating as an AP.
+ */
+struct ieee80211_nodestats {
+	u_int32_t	ns_rx_data;		/* rx data frames */
+	u_int32_t	ns_rx_mgmt;		/* rx management frames */
+	u_int32_t	ns_rx_ctrl;		/* rx control frames */
+	u_int32_t	ns_rx_ucast;		/* rx unicast frames */
+	u_int32_t	ns_rx_mcast;		/* rx multi/broadcast frames */
+	u_int64_t	ns_rx_bytes;		/* rx data count (bytes) */
+
+	u_int32_t	ns_rx_dup;		/* rx discard 'cuz dup */
+	u_int32_t	ns_rx_noprivacy;	/* rx w/ wep but privacy off */
+	u_int32_t	ns_rx_wepfail;		/* rx wep processing failed */
+	u_int32_t	ns_rx_decap;		/* rx decapsulation failed */
+	u_int32_t	ns_rx_disassoc;		/* rx disassociation */
+	u_int32_t	ns_rx_deauth;		/* rx deauthentication */
+	u_int32_t	ns_rx_decryptcrc;	/* rx decrypt failed on crc */
+	u_int32_t	ns_rx_unauth;		/* rx on unauthorized port */
+
+	u_int32_t	ns_tx_data;		/* tx data frames */
+	u_int32_t	ns_tx_mgmt;		/* tx management frames */
+	u_int32_t	ns_tx_ucast;		/* tx unicast frames */
+	u_int32_t	ns_tx_mcast;		/* tx multi/broadcast frames */
+	u_int64_t	ns_tx_bytes;		/* tx data count (bytes) */
+
+	u_int32_t	ns_tx_novlantag;	/* tx discard 'cuz no tag */
+	u_int32_t	ns_tx_vlanmismatch;	/* tx discard 'cuz bad tag */
+
+	/* MIB-related state */
+	u_int32_t	ns_mib_assoc;		/* [re]associations */
+	u_int32_t	ns_mib_assoc_fail;	/* [re]association failures */
+	u_int32_t	ns_mib_auth;		/* [re]authentications */
+	u_int32_t	ns_mib_auth_fail;	/* [re]authentication failures*/
+	u_int32_t	ns_mib_deauth;		/* deauthentications */
+	u_int32_t	ns_mib_deauth_code;	/* last deauth reason */
+	u_int32_t	ns_mib_disassoc;	/* disassociations */
+	u_int32_t	ns_mib_disassoc_code;	/* last disassociation reason */
+};
+
+/*
+ * Summary statistics.
+ */
+struct ieee80211_stats {
+	u_int32_t	is_rx_badversion;	/* rx frame with bad version */
+	u_int32_t	is_rx_tooshort;		/* rx frame too short */
+	u_int32_t	is_rx_wrongbss;		/* rx from wrong bssid */
+	u_int32_t	is_rx_dup;		/* rx discard 'cuz dup */
+	u_int32_t	is_rx_wrongdir;		/* rx w/ wrong direction */
+	u_int32_t	is_rx_mcastecho;	/* rx discard 'cuz mcast echo */
+	u_int32_t	is_rx_notassoc;		/* rx discard 'cuz sta !assoc */
+	u_int32_t	is_rx_noprivacy;	/* rx w/ wep but privacy off */
+	u_int32_t	is_rx_unencrypted;	/* rx w/o wep and privacy on */
+	u_int32_t	is_rx_wepfail;		/* rx wep processing failed */
+	u_int32_t	is_rx_decap;		/* rx decapsulation failed */
+	u_int32_t	is_rx_mgtdiscard;	/* rx discard mgt frames */
+	u_int32_t	is_rx_ctl;		/* rx discard ctrl frames */
+	u_int32_t	is_rx_beacon;		/* rx beacon frames */
+	u_int32_t	is_rx_rstoobig;		/* rx rate set truncated */
+	u_int32_t	is_rx_elem_missing;	/* rx required element missing*/
+	u_int32_t	is_rx_elem_toobig;	/* rx element too big */
+	u_int32_t	is_rx_elem_toosmall;	/* rx element too small */
+	u_int32_t	is_rx_elem_unknown;	/* rx element unknown */
+	u_int32_t	is_rx_badchan;		/* rx frame w/ invalid chan */
+	u_int32_t	is_rx_chanmismatch;	/* rx frame chan mismatch */
+	u_int32_t	is_rx_nodealloc;	/* rx frame dropped */
+	u_int32_t	is_rx_ssidmismatch;	/* rx frame ssid mismatch  */
+	u_int32_t	is_rx_auth_unsupported;	/* rx w/ unsupported auth alg */
+	u_int32_t	is_rx_auth_fail;	/* rx sta auth failure */
+	u_int32_t	is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */
+	u_int32_t	is_rx_assoc_bss;	/* rx assoc from wrong bssid */
+	u_int32_t	is_rx_assoc_notauth;	/* rx assoc w/o auth */
+	u_int32_t	is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
+	u_int32_t	is_rx_assoc_norate;	/* rx assoc w/ no rate match */
+	u_int32_t	is_rx_assoc_badwpaie;	/* rx assoc w/ bad WPA IE */
+	u_int32_t	is_rx_deauth;		/* rx deauthentication */
+	u_int32_t	is_rx_disassoc;		/* rx disassociation */
+	u_int32_t	is_rx_badsubtype;	/* rx frame w/ unknown subtype*/
+	u_int32_t	is_rx_nobuf;		/* rx failed for lack of buf */
+	u_int32_t	is_rx_decryptcrc;	/* rx decrypt failed on crc */
+	u_int32_t	is_rx_ahdemo_mgt;	/* rx discard ahdemo mgt frame*/
+	u_int32_t	is_rx_bad_auth;		/* rx bad auth request */
+	u_int32_t	is_rx_unauth;		/* rx on unauthorized port */
+	u_int32_t	is_rx_badkeyid;		/* rx w/ incorrect keyid */
+	u_int32_t	is_rx_ccmpreplay;	/* rx seq# violation (CCMP) */
+	u_int32_t	is_rx_ccmpformat;	/* rx format bad (CCMP) */
+	u_int32_t	is_rx_ccmpmic;		/* rx MIC check failed (CCMP) */
+	u_int32_t	is_rx_tkipreplay;	/* rx seq# violation (TKIP) */
+	u_int32_t	is_rx_tkipformat;	/* rx format bad (TKIP) */
+	u_int32_t	is_rx_tkipmic;		/* rx MIC check failed (TKIP) */
+	u_int32_t	is_rx_tkipicv;		/* rx ICV check failed (TKIP) */
+	u_int32_t	is_rx_badcipher;	/* rx failed 'cuz key type */
+	u_int32_t	is_rx_nocipherctx;	/* rx failed 'cuz key !setup */
+	u_int32_t	is_rx_acl;		/* rx discard 'cuz acl policy */
+	u_int32_t	is_tx_nobuf;		/* tx failed for lack of buf */
+	u_int32_t	is_tx_nonode;		/* tx failed for no node */
+	u_int32_t	is_tx_unknownmgt;	/* tx of unknown mgt frame */
+	u_int32_t	is_tx_badcipher;	/* tx failed 'cuz key type */
+	u_int32_t	is_tx_nodefkey;		/* tx failed 'cuz no defkey */
+	u_int32_t	is_tx_noheadroom;	/* tx failed 'cuz no space */
+	u_int32_t	is_scan_active;		/* active scans started */
+	u_int32_t	is_scan_passive;	/* passive scans started */
+	u_int32_t	is_node_timeout;	/* nodes timed out inactivity */
+	u_int32_t	is_crypto_nomem;	/* no memory for crypto ctx */
+	u_int32_t	is_crypto_tkip;		/* tkip crypto done in s/w */
+	u_int32_t	is_crypto_tkipenmic;	/* tkip en-MIC done in s/w */
+	u_int32_t	is_crypto_tkipdemic;	/* tkip de-MIC done in s/w */
+	u_int32_t	is_crypto_tkipcm;	/* tkip counter measures */
+	u_int32_t	is_crypto_ccmp;		/* ccmp crypto done in s/w */
+	u_int32_t	is_crypto_wep;		/* wep crypto done in s/w */
+	u_int32_t	is_crypto_setkey_cipher;/* cipher rejected key */
+	u_int32_t	is_crypto_setkey_nokey;	/* no key index for setkey */
+	u_int32_t	is_crypto_delkey;	/* driver key delete failed */
+	u_int32_t	is_crypto_badcipher;	/* unknown cipher */
+	u_int32_t	is_crypto_nocipher;	/* cipher not available */
+	u_int32_t	is_crypto_attachfail;	/* cipher attach failed */
+	u_int32_t	is_crypto_swfallback;	/* cipher fallback to s/w */
+	u_int32_t	is_crypto_keyfail;	/* driver key alloc failed */
+};
+
+/*
+ * Max size of optional information elements.  We artificially
+ * constrain this; it's limited only by the max frame size (and
+ * the max parameter size of the wireless extensions).
+ */
+#define	IEEE80211_MAX_OPT_IE	256
+
+/*
+ * WPA/RSN get/set key request.  Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving.  The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap).  They key length
+ * must include any MIC key data; otherwise it should be no
+ more than IEEE80211_KEYBUF_SIZE.
+ */
+struct ieee80211req_key {
+	u_int8_t	ik_type;	/* key/cipher type */
+	u_int8_t	ik_pad;
+	u_int16_t	ik_keyix;	/* key index */
+	u_int8_t	ik_keylen;	/* key length in bytes */
+	u_int8_t	ik_flags;
+/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */
+#define	IEEE80211_KEY_DEFAULT	0x80	/* default xmit key */
+	u_int8_t	ik_macaddr[IEEE80211_ADDR_LEN];
+	u_int64_t	ik_keyrsc;	/* key receive sequence counter */
+	u_int64_t	ik_keytsc;	/* key transmit sequence counter */
+	u_int8_t	ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+};
+
+/*
+ * Delete a key either by index or address.  Set the index
+ * to IEEE80211_KEYIX_NONE when deleting a unicast key.
+ */
+struct ieee80211req_del_key {
+	u_int8_t	idk_keyix;	/* key index */
+	u_int8_t	idk_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/*
+ * MLME state manipulation request.  IEEE80211_MLME_ASSOC
+ * only makes sense when operating as a station.  The other
+ * requests can be used when operating as a station or an
+ * ap (to effect a station).
+ */
+struct ieee80211req_mlme {
+	u_int8_t	im_op;		/* operation to perform */
+#define	IEEE80211_MLME_ASSOC		1	/* associate station */
+#define	IEEE80211_MLME_DISASSOC		2	/* disassociate station */
+#define	IEEE80211_MLME_DEAUTH		3	/* deauthenticate station */
+#define	IEEE80211_MLME_AUTHORIZE	4	/* authorize station */
+#define	IEEE80211_MLME_UNAUTHORIZE	5	/* unauthorize station */
+	u_int16_t	im_reason;	/* 802.11 reason code */
+	u_int8_t	im_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/* 
+ * MAC ACL operations.
+ */
+enum {
+	IEEE80211_MACCMD_POLICY_OPEN	= 0,	/* set policy: no ACL's */
+	IEEE80211_MACCMD_POLICY_ALLOW	= 1,	/* set policy: allow traffic */
+	IEEE80211_MACCMD_POLICY_DENY	= 2,	/* set policy: deny traffic */
+	IEEE80211_MACCMD_FLUSH		= 3,	/* flush ACL database */
+	IEEE80211_MACCMD_DETACH		= 4,	/* detach ACL policy */
+};
+
+/*
+ * Set the active channel list.  Note this list is
+ * intersected with the available channel list in
+ * calculating the set of channels actually used in
+ * scanning.
+ */
+struct ieee80211req_chanlist {
+	u_int8_t	ic_channels[32];	/* 256 channels */
+};
+
+/*
+ * Retrieve the WPA/RSN information element for an associated station.
+ */
+struct ieee80211req_wpaie {
+	u_int8_t	wpa_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	wpa_ie[IEEE80211_MAX_OPT_IE];
+};
+
+#ifdef __FreeBSD__
+/*
+ * FreeBSD-style ioctls.
+ */
+/* the first member must be matched with struct ifreq */
+struct ieee80211req {
+	char		i_name[IFNAMSIZ];	/* if_name, e.g. "wi0" */
+	u_int16_t	i_type;			/* req type */
+	int16_t		i_val;			/* Index or simple value */
+	int16_t		i_len;			/* Index or simple value */
+	void		*i_data;		/* Extra data */
+};
+#define	SIOCS80211		 _IOW('i', 234, struct ieee80211req)
+#define	SIOCG80211		_IOWR('i', 235, struct ieee80211req)
+
+#define IEEE80211_IOC_SSID		1
+#define IEEE80211_IOC_NUMSSIDS		2
+#define IEEE80211_IOC_WEP		3
+#define 	IEEE80211_WEP_NOSUP	-1
+#define 	IEEE80211_WEP_OFF	0
+#define 	IEEE80211_WEP_ON	1
+#define 	IEEE80211_WEP_MIXED	2
+#define IEEE80211_IOC_WEPKEY		4
+#define IEEE80211_IOC_NUMWEPKEYS	5
+#define IEEE80211_IOC_WEPTXKEY		6
+#define IEEE80211_IOC_AUTHMODE		7
+#define IEEE80211_IOC_STATIONNAME	8
+#define IEEE80211_IOC_CHANNEL		9
+#define IEEE80211_IOC_POWERSAVE		10
+#define 	IEEE80211_POWERSAVE_NOSUP	-1
+#define 	IEEE80211_POWERSAVE_OFF		0
+#define 	IEEE80211_POWERSAVE_CAM		1
+#define 	IEEE80211_POWERSAVE_PSP		2
+#define 	IEEE80211_POWERSAVE_PSP_CAM	3
+#define 	IEEE80211_POWERSAVE_ON		IEEE80211_POWERSAVE_CAM
+#define IEEE80211_IOC_POWERSAVESLEEP	11
+#define	IEEE80211_IOC_RTSTHRESHOLD	12
+#define IEEE80211_IOC_PROTMODE		13
+#define 	IEEE80211_PROTMODE_OFF		0
+#define 	IEEE80211_PROTMODE_CTS		1
+#define 	IEEE80211_PROTMODE_RTSCTS	2
+#define	IEEE80211_IOC_TXPOWER		14
+#define	IEEE80211_IOC_BSSID		15
+#define	IEEE80211_IOC_ROAMING		16
+#define	IEEE80211_IOC_PRIVACY		17
+#define	IEEE80211_IOC_DROP_UNENCRYPTED	18
+#define	IEEE80211_IOC_WPAKEY		19
+#define	IEEE80211_IOC_DELKEY		20
+#define	IEEE80211_IOC_MLME		21
+#define	IEEE80211_IOC_OPTIE		22
+#define	IEEE80211_IOC_SCAN_REQ		23
+#define	IEEE80211_IOC_SCAN_RESULTS	24
+#define	IEEE80211_IOC_COUNTERMEASURES	25
+#define	IEEE80211_IOC_WPA		26
+#define	IEEE80211_IOC_CHANLIST		27
+#define	IEEE80211_IOC_WME		28
+#define	IEEE80211_IOC_HIDESSID		29
+#define	IEEE80211_IOC_APBRIDGE		30
+
+#ifndef IEEE80211_CHAN_ANY
+#define	IEEE80211_CHAN_ANY	0xffff		/* token for ``any channel'' */
+#endif
+
+struct ieee80211req_scan_req {
+	u_int8_t	isq_ssid_len;			/* SSID length */
+	u_int8_t	isq_ssid[IEEE80211_NWID_LEN];
+};
+
+struct ieee80211req_scan_result {
+	u_int16_t	isr_len;			/* length (mult of 4) */
+	u_int16_t	isr_freq;			/* MHz */
+	u_int16_t	isr_flags;			/* channel flags */
+	u_int8_t	isr_noise;
+	u_int8_t	isr_rssi;
+	u_int8_t	isr_intval;			/* beacon interval */
+	u_int8_t	isr_capinfo;			/* capabilities */
+	u_int8_t	isr_erp;			/* ERP element */
+	u_int8_t	isr_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	isr_nrates;
+	u_int8_t	isr_rates[15];			/* XXX */
+	u_int8_t	isr_ssid_len;			/* SSID length */
+	u_int8_t	isr_ie_len;			/* IE length */
+	u_int8_t	isr_scangen;			/* scan generation # */
+	/* variable length SSID followed by IE data */
+};
+
+#define	SIOCG80211STATS		_IOWR('i', 236, struct ifreq)
+#endif /* __FreeBSD__ */
+
+#ifdef __linux__
+/*
+ * Wireless Extensions API, private ioctl interfaces.
+ *
+ * NB: Even-numbered ioctl numbers have set semantics and are privileged!
+ *     (regardless of the incorrect comment in wireless.h!)
+ */
+#define	IEEE80211_IOCTL_SETPARAM	(SIOCIWFIRSTPRIV+0)
+#define	IEEE80211_IOCTL_GETPARAM	(SIOCIWFIRSTPRIV+1)
+#define	IEEE80211_IOCTL_SETKEY		(SIOCIWFIRSTPRIV+2)
+#define	IEEE80211_IOCTL_DELKEY		(SIOCIWFIRSTPRIV+4)
+#define	IEEE80211_IOCTL_SETMLME		(SIOCIWFIRSTPRIV+6)
+#define	IEEE80211_IOCTL_SETOPTIE	(SIOCIWFIRSTPRIV+8)
+#define	IEEE80211_IOCTL_GETOPTIE	(SIOCIWFIRSTPRIV+9)
+#define	IEEE80211_IOCTL_ADDMAC		(SIOCIWFIRSTPRIV+10)
+#define	IEEE80211_IOCTL_DELMAC		(SIOCIWFIRSTPRIV+12)
+#define	IEEE80211_IOCTL_CHANLIST	(SIOCIWFIRSTPRIV+14)
+
+enum {
+	IEEE80211_PARAM_TURBO		= 1,	/* turbo mode */
+	IEEE80211_PARAM_MODE		= 2,	/* phy mode (11a, 11b, etc.) */
+	IEEE80211_PARAM_AUTHMODE	= 3,	/* authentication mode */
+	IEEE80211_PARAM_PROTMODE	= 4,	/* 802.11g protection */
+	IEEE80211_PARAM_MCASTCIPHER	= 5,	/* multicast/default cipher */
+	IEEE80211_PARAM_MCASTKEYLEN	= 6,	/* multicast key length */
+	IEEE80211_PARAM_UCASTCIPHERS	= 7,	/* unicast cipher suites */
+	IEEE80211_PARAM_UCASTCIPHER	= 8,	/* unicast cipher */
+	IEEE80211_PARAM_UCASTKEYLEN	= 9,	/* unicast key length */
+	IEEE80211_PARAM_WPA		= 10,	/* WPA mode (0,1,2) */
+	IEEE80211_PARAM_ROAMING		= 12,	/* roaming mode */
+	IEEE80211_PARAM_PRIVACY		= 13,	/* privacy invoked */
+	IEEE80211_PARAM_COUNTERMEASURES	= 14,	/* WPA/TKIP countermeasures */
+	IEEE80211_PARAM_DROPUNENCRYPTED	= 15,	/* discard unencrypted frames */
+	IEEE80211_PARAM_DRIVER_CAPS	= 16,	/* driver capabilities */
+	IEEE80211_PARAM_MACCMD		= 17,	/* MAC ACL operation */
+	IEEE80211_PARAM_WME		= 18,	/* WME mode (on, off) */
+	IEEE80211_PARAM_HIDESSID	= 19,	/* hide SSID mode (on, off) */
+	IEEE80211_PARAM_APBRIDGE	= 20,	/* AP inter-sta bridging */
+	IEEE80211_PARAM_KEYMGTALGS	= 21,	/* key management algorithms */
+	IEEE80211_PARAM_RSNCAPS		= 22,	/* RSN capabilities */
+	IEEE80211_PARAM_INACT		= 23,	/* station inactivity timeout */
+	IEEE80211_PARAM_INACT_AUTH	= 24,	/* station auth inact timeout */
+	IEEE80211_PARAM_INACT_INIT	= 25,	/* station init inact timeout */
+};
+
+#define	SIOCG80211STATS		(SIOCDEVPRIVATE+2)
+/* NB: require in+out parameters so cannot use wireless extensions, yech */
+#define	IEEE80211_IOCTL_GETKEY		(SIOCDEVPRIVATE+3)
+#define	IEEE80211_IOCTL_GETWPAIE	(SIOCDEVPRIVATE+4)
+
+#endif /* __linux__ */
+
+#endif /* _NET80211_IEEE80211_IOCTL_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.c	2005-02-24 13:06:17.308130784 -0800
@@ -0,0 +1,500 @@
+/*-
+ * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+/*
+ * IEEE 802.11 support (Linux-specific code)
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/sysctl.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+
+#include <net/iw_handler.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>		/* XXX for ARPHRD_* */
+
+#include <asm/uaccess.h>
+
+#include "if_media.h"
+#include "if_ethersubr.h"
+
+#include <net80211/ieee80211_var.h>
+
+/*
+ * Print a console message with the device name prepended.
+ */
+void
+if_printf(struct net_device *dev, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[512];		/* XXX */
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+
+	printk("%s: %s", dev->name, buf);
+}
+
+/*
+ * Allocate and setup a management frame of the specified
+ * size.  We return the sk_buff and a pointer to the start
+ * of the contiguous data area that's been reserved based
+ * on the packet length.  The data area is forced to 32-bit
+ * alignment and the buffer length to a multiple of 4 bytes.
+ * This is done mainly so beacon frames (that require this)
+ * can use this interface too.
+ */
+struct sk_buff *
+ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
+{
+	const u_int align = sizeof(u_int32_t);
+	struct ieee80211_cb *cb;
+	struct sk_buff *skb;
+	u_int len;
+
+	len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
+	skb = dev_alloc_skb(len + align-1);
+	if (skb != NULL) {
+		u_int off = ((unsigned long) skb->data) % align;
+		if (off != 0)
+			skb_reserve(skb, align - off);
+
+		cb = (struct ieee80211_cb *)skb->cb;
+		cb->ni = NULL;
+		cb->flags = 0;
+
+		skb_reserve(skb, sizeof(struct ieee80211_frame));
+		*frm = skb_put(skb, pktlen);
+	}
+	return skb;
+}
+
+/*
+ * Drain a queue of sk_buff's.
+ */
+void
+skb_queue_drain(struct sk_buff_head *q)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->lock, flags);
+	while ((skb = __skb_dequeue(q)) != NULL)
+		dev_kfree_skb(skb);
+	spin_unlock_irqrestore(&q->lock, flags);
+}
+
+#if IEEE80211_VLAN_TAG_USED
+/*
+ * VLAN support.
+ */
+
+/*
+ * Register a vlan group.
+ */
+void
+ieee80211_vlan_register(struct ieee80211com *ic, struct vlan_group *grp)
+{
+	ic->ic_vlgrp = grp;
+}
+EXPORT_SYMBOL(ieee80211_vlan_register);
+
+/*
+ * Kill (i.e. delete) a vlan identifier.
+ */
+void
+ieee80211_vlan_kill_vid(struct ieee80211com *ic, unsigned short vid)
+{
+	if (ic->ic_vlgrp)
+		ic->ic_vlgrp->vlan_devices[vid] = NULL;
+}
+EXPORT_SYMBOL(ieee80211_vlan_kill_vid);
+#endif /* IEEE80211_VLAN_TAG_USED */
+
+void
+ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
+{
+	union iwreq_data wreq;
+
+	if (ni == ic->ic_bss) {
+		if (newassoc)
+			netif_carrier_on(ic->ic_dev);
+		memset(&wreq, 0, sizeof(wreq));
+		IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_bssid);
+		wreq.addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(ic->ic_dev, SIOCGIWAP, &wreq, NULL);
+	} else if (newassoc) {
+		/* fire off wireless event only for new station */
+		memset(&wreq, 0, sizeof(wreq));
+		IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
+		wreq.addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(ic->ic_dev, IWEVREGISTERED, &wreq, NULL);
+	}
+}
+
+void
+ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	union iwreq_data wreq;
+
+	if (ni == ic->ic_bss) {
+		netif_carrier_off(ic->ic_dev);
+		memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN);
+		wreq.ap_addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(ic->ic_dev, SIOCGIWAP, &wreq, NULL);
+	} else {
+		/* fire off wireless event station leaving */
+		memset(&wreq, 0, sizeof(wreq));
+		IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
+		wreq.addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(ic->ic_dev, IWEVEXPIRED, &wreq, NULL);
+	}
+}
+
+void
+ieee80211_notify_scan_done(struct ieee80211com *ic)
+{
+	union iwreq_data wreq;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		("%s: notify scan done\n", ic->ic_dev->name));
+
+	/* dispatch wireless event indicating scan completed */
+	wreq.data.length = 0;
+	wreq.data.flags = 0;
+	wireless_send_event(ic->ic_dev, SIOCGIWSCAN, &wreq, NULL);
+}
+
+void
+ieee80211_notify_replay_failure(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh, const struct ieee80211_key *k,
+	u_int64_t rsc)
+{
+	static const char * tag = "MLME-REPLAYFAILURE.indication";
+	union iwreq_data wrqu;
+	char buf[128];
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		("[%s] %s replay detected <rsc %llu, csc %llu>\n",
+		ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
+		rsc, k->wk_keyrsc));
+
+	if (ic->ic_dev == NULL)		/* NB: for cipher test modules */
+		return;
+
+	/* TODO: needed parameters: count, keyid, key type, src address, TSC */
+	snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag,
+	    k->wk_keyix, IEEE80211_IS_MULTICAST(wh->i_addr1) ?  "broad" : "uni",
+	    ether_sprintf(wh->i_addr1));
+	memset(&wrqu, 0, sizeof(wrqu));
+	wrqu.data.length = strlen(buf);
+	wireless_send_event(ic->ic_dev, IWEVCUSTOM, &wrqu, buf);
+}
+EXPORT_SYMBOL(ieee80211_notify_replay_failure);
+
+void
+ieee80211_notify_michael_failure(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh, u_int keyix)
+{
+	static const char * tag = "MLME-MICHAELMICFAILURE.indication";
+	union iwreq_data wrqu;
+	char buf[128];
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		("[%s] Michael MIC verification failed <keyidx %d>\n",
+	       ether_sprintf(wh->i_addr2), keyix));
+	ic->ic_stats.is_rx_tkipmic++;
+
+	if (ic->ic_dev == NULL)		/* NB: for cipher test modules */
+		return;
+
+	/* TODO: needed parameters: count, keyid, key type, src address, TSC */
+	snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag,
+	    keyix, IEEE80211_IS_MULTICAST(wh->i_addr1) ?  "broad" : "uni",
+	    ether_sprintf(wh->i_addr1));
+	memset(&wrqu, 0, sizeof(wrqu));
+	wrqu.data.length = strlen(buf);
+	wireless_send_event(ic->ic_dev, IWEVCUSTOM, &wrqu, buf);
+}
+EXPORT_SYMBOL(ieee80211_notify_michael_failure);
+
+#ifdef CONFIG_SYSCTL
+#include <linux/ctype.h>
+
+static int
+proc_read_node(char *page, int space, struct ieee80211com *ic, void *arg)
+{
+	TAILQ_HEAD(, ieee80211_node) *head = arg;
+	char *p = page;
+	struct ieee80211_node *ni;
+	struct ieee80211_rateset *rs;
+	int i;
+
+	IEEE80211_NODE_LOCK_BH(ic);
+	TAILQ_FOREACH(ni, head, ni_list) {
+		/* Assume each node needs 300 bytes */ 
+		if (p - page > space - 300)
+			break;
+
+		p += sprintf(p, "\nmacaddr: <%s>\n", ether_sprintf(ni->ni_macaddr));
+		p += sprintf(p, "  rssi: %d dBm\n",
+			(*ic->ic_node_getrssi)(ic, ni));
+
+		p += sprintf(p, "  capinfo:");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_ESS)
+			p += sprintf(p, " ess");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)
+			p += sprintf(p, " ibss");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
+			p += sprintf(p, " cfpollable");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
+			p += sprintf(p, " cfpollreq");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
+			p += sprintf(p, " privacy");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
+			p += sprintf(p, " chanagility");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_PBCC)
+			p += sprintf(p, " pbcc");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
+			p += sprintf(p, " shortpreamble");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
+			p += sprintf(p, " shortslot");
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_DSSSOFDM)
+			p += sprintf(p, " dsssofdm");
+		p += sprintf(p, "\n");
+
+		p += sprintf(p, "  freq: %d MHz (channel %d)\n",
+			ni->ni_chan->ic_freq,
+			ieee80211_mhz2ieee(ni->ni_chan->ic_freq, 0));
+
+		p += sprintf(p, "  opmode:");
+		if (IEEE80211_IS_CHAN_A(ni->ni_chan))
+			p += sprintf(p, " a");
+		if (IEEE80211_IS_CHAN_B(ni->ni_chan))
+			p += sprintf(p, " b");
+		if (IEEE80211_IS_CHAN_PUREG(ni->ni_chan))
+			p += sprintf(p, " pureg");
+		if (IEEE80211_IS_CHAN_G(ni->ni_chan))
+			p += sprintf(p, " g");
+		p += sprintf(p, "\n");
+
+		rs = &ni->ni_rates;
+		if (ni->ni_txrate >= 0 && ni->ni_txrate < rs->rs_nrates) {
+			p += sprintf(p, "  txrate: ");
+			for (i = 0; i < rs->rs_nrates; i++) {
+				p += sprintf(p, "%s%d%sMbps",
+					(i != 0 ? " " : ""),
+					(rs->rs_rates[i] & IEEE80211_RATE_VAL) / 2,
+					((rs->rs_rates[i] & 0x1) != 0 ? ".5" : ""));
+				if (i == ni->ni_txrate)
+					p += sprintf(p, "*"); /* current rate */
+			}
+			p += sprintf(p, "\n");
+		} else
+			p += sprintf(p, "  txrate: %d ? (rs_nrates: %d)\n",
+					ni->ni_txrate, ni->ni_rates.rs_nrates);
+
+		p += sprintf(p, "  txseq: %d  rxseq: %d\n",
+				ni->ni_txseq, ni->ni_rxseq);
+		p += sprintf(p, "  fails: %d  inact: %d\n",
+				ni->ni_fails, ni->ni_inact);
+
+	}
+	IEEE80211_NODE_UNLOCK_BH(ic);
+	return (p - page);
+}
+
+static int
+IEEE80211_SYSCTL_DECL(ieee80211_sysctl_stations, ctl, write, filp, buffer,
+		lenp, ppos)
+{
+	struct ieee80211com *ic = ctl->extra1;
+	int len = *lenp;
+
+	if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+	    ic->ic_opmode != IEEE80211_M_IBSS)
+		return -EINVAL;
+	if (len && filp->f_pos == 0) {
+		*lenp = proc_read_node(buffer, len, ic, &ic->ic_node);
+		filp->f_pos += *lenp;
+	} else {
+		*lenp = 0;
+	}
+	return 0;
+}
+
+
+static int
+IEEE80211_SYSCTL_DECL(ieee80211_sysctl_debug, ctl, write, filp, buffer,
+		lenp, ppos)
+{
+	struct ieee80211com *ic = ctl->extra1;
+	u_int val;
+	int ret;
+
+	ctl->data = &val;
+	ctl->maxlen = sizeof(val);
+	if (write) {
+		ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
+				lenp, ppos);
+		if (ret == 0)
+			ic->msg_enable = val;
+	} else {
+		val = ic->msg_enable;
+		ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
+				lenp, ppos);
+	}
+	return ret;
+}
+
+#define	CTL_AUTO	-2	/* cannot be CTL_ANY or CTL_NONE */
+
+static const ctl_table ieee80211_sysctl_template[] = {
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "debug",
+	  .mode		= 0644,
+	  .proc_handler	= ieee80211_sysctl_debug
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "associated_sta",
+	  .mode		= 0444,
+	  .proc_handler	= ieee80211_sysctl_stations
+	},
+	{ 0 }
+};
+
+void
+ieee80211_sysctl_register(struct ieee80211com *ic)
+{
+	const char *cp;
+	int i, space;
+
+	space = 5*sizeof(struct ctl_table) + sizeof(ieee80211_sysctl_template);
+	ic->ic_sysctls = kmalloc(space, GFP_KERNEL);
+	if (ic->ic_sysctls == NULL) {
+		printk("%s: no memory for sysctl table!\n", __func__);
+		return;
+	}
+
+	/* setup the table */
+	memset(ic->ic_sysctls, 0, space);
+	ic->ic_sysctls[0].ctl_name = CTL_NET;
+	ic->ic_sysctls[0].procname = "net";
+	ic->ic_sysctls[0].mode = 0555;
+	ic->ic_sysctls[0].child = &ic->ic_sysctls[2];
+	/* [1] is NULL terminator */
+	ic->ic_sysctls[2].ctl_name = CTL_AUTO;
+	for (cp = ic->ic_dev->name; *cp && !isdigit(*cp); cp++)
+		;
+	snprintf(ic->ic_procname, sizeof(ic->ic_procname), "wlan%s", cp);
+	ic->ic_sysctls[2].procname = ic->ic_procname;
+	ic->ic_sysctls[2].mode = 0555;
+	ic->ic_sysctls[2].child = &ic->ic_sysctls[4];
+	/* [3] is NULL terminator */
+	/* copy in pre-defined data */
+	memcpy(&ic->ic_sysctls[4], ieee80211_sysctl_template,
+		sizeof(ieee80211_sysctl_template));
+
+	/* add in dynamic data references */
+	for (i = 4; ic->ic_sysctls[i].ctl_name; i++)
+		if (ic->ic_sysctls[i].extra1 == NULL)
+			ic->ic_sysctls[i].extra1 = ic;
+
+	/* and register everything */
+	ic->ic_sysctl_header = register_sysctl_table(ic->ic_sysctls, 1);
+	if (!ic->ic_sysctl_header) {
+		printk("%s: failed to register sysctls!\n", ic->ic_procname);
+		kfree(ic->ic_sysctls);
+		ic->ic_sysctls = NULL;
+	}
+}
+EXPORT_SYMBOL(ieee80211_sysctl_register);
+
+void
+ieee80211_sysctl_unregister(struct ieee80211com *ic)
+{
+	if (ic->ic_sysctl_header) {
+		unregister_sysctl_table(ic->ic_sysctl_header);
+		ic->ic_sysctl_header = NULL;
+	}
+	if (ic->ic_sysctls) {
+		kfree(ic->ic_sysctls);
+		ic->ic_sysctls = NULL;
+	}
+}
+EXPORT_SYMBOL(ieee80211_sysctl_unregister);
+#endif /* CONFIG_SYSCTL */
+
+/*
+ * Format an Ethernet MAC for printing.
+ */
+const char*
+ether_sprintf(const u_int8_t *mac)
+{
+	static char etherbuf[18];
+	snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	return etherbuf;
+}
+EXPORT_SYMBOL(ether_sprintf);		/* XXX */
+
+/*
+ * Module glue.
+ */
+#include "version.h"
+static	char *version = WLAN_VERSION " (EXPERIMENTAL)";
+static	char *dev_info = "wlan";
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless LAN protocol support");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static int __init
+init_wlan(void)
+{
+	printk(KERN_INFO "%s: %s\n", dev_info, version);
+	return 0;
+}
+module_init(init_wlan);
+
+static void __exit
+exit_wlan(void)
+{
+	printk(KERN_INFO "%s: driver unloaded\n", dev_info);
+}
+module_exit(exit_wlan);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.h	2005-02-24 13:06:17.309130632 -0800
@@ -0,0 +1,347 @@
+/*-
+ * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *	$Id: ieee80211_linux.h,v 1.6 2005/02/16 16:08:47 samleffler Exp $
+ */
+#ifndef _NET80211_IEEE80211_LINUX_H_
+#define _NET80211_IEEE80211_LINUX_H_
+
+/*
+ * Node locking definitions.
+ */
+typedef rwlock_t ieee80211_node_lock_t;
+#define	IEEE80211_NODE_LOCK_INIT(_ic, _name)	rwlock_init(&(_ic)->ic_nodelock)
+#define	IEEE80211_NODE_LOCK_DESTROY(_ic)
+#define	IEEE80211_NODE_LOCK(_ic)	write_lock(&(_ic)->ic_nodelock)
+#define	IEEE80211_NODE_UNLOCK(_ic)	write_unlock(&(_ic)->ic_nodelock)
+#define	IEEE80211_NODE_LOCK_BH(_ic)	write_lock_bh(&(_ic)->ic_nodelock)
+#define	IEEE80211_NODE_UNLOCK_BH(_ic)	write_unlock_bh(&(_ic)->ic_nodelock)
+/* NB: beware, *_is_locked() are boguly defined for UP+!PREEMPT */
+#if (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)) && defined(rwlock_is_locked)
+#define	IEEE80211_NODE_LOCK_ASSERT(_ic) \
+	KASSERT(rwlock_is_locked(&(_ic)->ic_nodelock), \
+		("802.11 node not locked!"))
+#else
+#define	IEEE80211_NODE_LOCK_ASSERT(_ic)
+#endif
+
+/*
+ * 802.1x state locking definitions.
+ */
+typedef spinlock_t eapol_lock_t;
+#define	EAPOL_LOCK_INIT(_ec, _name)	spin_lock_init(&(_ec)->ec_lock)
+#define	EAPOL_LOCK_DESTROY(_ec)
+#define	EAPOL_LOCK(_ec)			spin_lock_bh(&(_ec)->ec_lock)
+#define	EAPOL_UNLOCK(_ec)		spin_unlock_bh(&(_ec)->ec_lock)
+/* NB: beware, *_is_locked() are boguly defined for UP+!PREEMPT */
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
+#define	EAPOL_LOCK_ASSERT(_ec) \
+	KASSERT(spin_is_locked(&(_ec)->ec_lock), ("EAPOL not locked!"))
+#else
+#define	EAPOL_LOCK_ASSERT(_ec)
+#endif
+
+/*
+ * 802.1x MAC ACL database locking definitions.
+ */
+typedef spinlock_t acl_lock_t;
+#define	ACL_LOCK_INIT(_as, _name)	spin_lock_init(&(_as)->as_lock)
+#define	ACL_LOCK_DESTROY(_as)
+#define	ACL_LOCK(_as)			spin_lock_bh(&(_as)->as_lock)
+#define	ACL_UNLOCK(_as)			spin_unlock_bh(&(_as)->as_lock)
+/* NB: beware, *_is_locked() are boguly defined for UP+!PREEMPT */
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
+#define	ACL_LOCK_ASSERT(_as) \
+	KASSERT(spin_is_locked(&(_as)->as_lock), ("ACL not locked!"))
+#else
+#define	ACL_LOCK_ASSERT(_as)
+#endif
+
+#define	M_LINK0		0x01		/* frame needs WEP encryption */
+
+/*
+ * Node reference counting definitions.
+ *
+ * ieee80211_node_initref	initialize the reference count to 1
+ * ieee80211_node_incref	add a reference
+ * ieee80211_node_decref	remove a reference
+ * ieee80211_node_dectestref	remove a reference and return 1 if this
+ *				is the last reference, otherwise 0
+ * ieee80211_node_refcnt	reference count for printing (only)
+ */
+#define ieee80211_node_initref(_ni) \
+	atomic_set(&(_ni)->ni_refcnt, 1)
+#define ieee80211_node_incref(_ni) \
+	atomic_inc(&(_ni)->ni_refcnt)
+#define	ieee80211_node_decref(_ni) \
+	atomic_dec(&(_ni)->ni_refcnt)
+#define	ieee80211_node_dectestref(_ni) \
+	atomic_dec_and_test(&(_ni)->ni_refcnt)
+#define	ieee80211_node_refcnt(_ni)	(_ni)->ni_refcnt.counter
+
+#define	le16toh(_x)	le16_to_cpu(_x)
+#define	htole16(_x)	cpu_to_le16(_x)
+#define	le32toh(_x)	le32_to_cpu(_x)
+#define	htole32(_x)	cpu_to_le32(_x)
+#define	be32toh(_x)	be32_to_cpu(_x)
+#define	htobe32(_x)	cpu_to_be32(_x)
+
+/*
+ * Linux has no equivalents to malloc types so null these out.
+ */
+#define	MALLOC_DEFINE(type, shortdesc, longdesc)
+#define	MALLOC_DECLARE(type)
+
+/*
+ * flags to malloc.
+ */
+#define	M_NOWAIT	0x0001		/* do not block */
+#define	M_WAITOK	0x0002		/* ok to block */
+#define	M_ZERO		0x0100		/* bzero the allocation */
+
+static inline void *
+ieee80211_malloc(size_t size, int flags)
+{
+	void *p = kmalloc(size, flags & M_NOWAIT ? GFP_ATOMIC : GFP_KERNEL);
+	if (p && (flags & M_ZERO))
+		memset(p, 0, size);
+	return p;
+}
+#define	MALLOC(_ptr, cast, _size, _type, _flags) \
+	((_ptr) = (cast)ieee80211_malloc(_size, _flags))
+#define	FREE(addr, type)	kfree((addr))
+
+/*
+ * This unlikely to be popular but it dramatically reduces diffs.
+ */
+#define	printf	printk
+struct ieee80211com;
+extern	void if_printf(struct net_device *, const char *, ...);
+extern	const char *ether_sprintf(const u_int8_t *);
+
+/*
+ * Queue write-arounds and support routines.
+ */
+extern	struct sk_buff *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen);
+#define	IF_ENQUEUE(_q,_skb)	skb_queue_tail(_q,_skb)
+#define	IF_DEQUEUE(_q,_skb)	(_skb = skb_dequeue(_q))
+#define	_IF_QLEN(_q)		skb_queue_len(_q)
+#define	IF_DRAIN(_q)		skb_queue_drain(_q)
+extern	void skb_queue_drain(struct sk_buff_head *q);
+
+extern	struct net_device_stats *ieee80211_getstats(struct net_device *);
+
+#ifndef __MOD_INC_USE_COUNT
+#define	_MOD_INC_USE(_m, _err)						\
+	if (!try_module_get(_m)) {					\
+		printk(KERN_WARNING "%s: try_module_get failed\n",	\
+			__func__); \
+		_err;							\
+	}
+#define	_MOD_DEC_USE(_m)		module_put(_m)
+#else
+#define	_MOD_INC_USE(_m, _err)	MOD_INC_USE_COUNT
+#define	_MOD_DEC_USE(_m)	MOD_DEC_USE_COUNT
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static inline u_int64_t
+get_jiffies_64(void)
+{
+	return (u_int64_t) jiffies;		/* XXX not right */
+}
+#endif
+
+#ifndef CLONE_KERNEL
+/*
+ * List of flags we want to share for kernel threads,
+ * if only because they are not used by them anyway.
+ */
+#define CLONE_KERNEL	(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
+#endif
+
+#ifndef offset_in_page
+#define	offset_in_page(p) ((unsigned long) (p) & ~PAGE_MASK)
+#endif
+
+#ifndef module_put_and_exit
+#define module_put_and_exit(code) do {	\
+	_MOD_DEC_USE(THIS_MODULE);	\
+	do_exit(code);			\
+} while (0)
+#endif
+
+/*
+ * Linux uses __BIG_ENDIAN and __LITTLE_ENDIAN while BSD uses _foo
+ * and an explicit _BYTE_ORDER.  Sorry, BSD got there first--define
+ * things in the BSD way...
+ */
+#define	_LITTLE_ENDIAN	1234	/* LSB first: i386, vax */
+#define	_BIG_ENDIAN	4321	/* MSB first: 68000, ibm, net */
+#include <asm/byteorder.h>
+#if defined(__LITTLE_ENDIAN)
+#define	_BYTE_ORDER	_LITTLE_ENDIAN
+#elif defined(__BIG_ENDIAN)
+#define	_BYTE_ORDER	_BIG_ENDIAN
+#else
+#error "Please fix asm/byteorder.h"
+#endif
+
+#ifdef CONFIG_SYSCTL
+/*
+ * Deal with the sysctl handler api changing.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+#define	IEEE80211_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
+	f(ctl_table *ctl, int write, struct file *filp, void *buffer, \
+		size_t *lenp)
+#define	IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+	proc_dointvec(ctl, write, filp, buffer, lenp)
+#else
+#define	IEEE80211_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
+	f(ctl_table *ctl, int write, struct file *filp, void *buffer,\
+		size_t *lenp, loff_t *ppos)
+#define	IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+	proc_dointvec(ctl, write, filp, buffer, lenp, ppos)
+#endif
+
+extern	void ieee80211_sysctl_register(struct ieee80211com *);
+extern	void ieee80211_sysctl_unregister(struct ieee80211com *);
+#endif /* CONFIG_SYSCTL */
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define IEEE80211_VLAN_TAG_USED 1
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)
+#define	vlan_hwaccel_receive_skb(skb, grp, tag)	vlan_hwaccel_rx(skb, grp, tag)
+#endif
+
+extern	void ieee80211_vlan_register(struct ieee80211com *, struct vlan_group*);
+extern	void ieee80211_vlan_kill_vid(struct ieee80211com *, unsigned short);
+#else
+#define IEEE80211_VLAN_TAG_USED 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define	free_netdev(dev)	kfree(dev)
+#endif
+
+#ifdef CONFIG_NET_WIRELESS
+struct iw_statistics;
+extern	void ieee80211_iw_getstats(struct ieee80211com*, struct iw_statistics*);
+struct iw_request_info;
+struct iw_point;
+extern	int ieee80211_ioctl_giwname(struct ieee80211com *,
+		struct iw_request_info *, char *name, char *extra);
+extern	int ieee80211_ioctl_siwencode(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+extern	int ieee80211_ioctl_giwencode(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *key);
+struct iw_param;
+extern	int ieee80211_ioctl_siwrate(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_giwrate(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_siwsens(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_giwsens(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_siwrts(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_giwrts(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_siwfrag(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_giwfrag(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+struct sockaddr;
+extern	int ieee80211_ioctl_siwap(struct ieee80211com *,
+		struct iw_request_info *, struct sockaddr *, char *);
+extern	int ieee80211_ioctl_giwap(struct ieee80211com *,
+		struct iw_request_info *, struct sockaddr *, char *);
+extern	int ieee80211_ioctl_siwnickn(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+extern	int ieee80211_ioctl_giwnickn(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+struct iw_freq;
+extern	int ieee80211_ioctl_siwfreq(struct ieee80211com *,
+		struct iw_request_info *, struct iw_freq *, char *);
+extern	int ieee80211_ioctl_giwfreq(struct ieee80211com *,
+		struct iw_request_info *, struct iw_freq *, char *);
+extern	int ieee80211_ioctl_siwessid(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+extern	int ieee80211_ioctl_giwessid(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+extern	int ieee80211_ioctl_giwrange(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+extern	int ieee80211_ioctl_siwmode(struct ieee80211com *,
+		struct iw_request_info *, __u32 *, char *);
+extern	int ieee80211_ioctl_giwmode(struct ieee80211com *,
+		struct iw_request_info *, __u32 *, char *);
+extern	int ieee80211_ioctl_siwpower(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_giwpower(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_siwretry(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_giwretry(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_siwtxpow(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_giwtxpow(struct ieee80211com *,
+		struct iw_request_info *, struct iw_param *, char *);
+extern	int ieee80211_ioctl_iwaplist(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+extern	int ieee80211_ioctl_siwscan(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+extern	int ieee80211_ioctl_giwscan(struct ieee80211com *,
+		struct iw_request_info *, struct iw_point *, char *);
+
+extern	int ieee80211_ioctl_setparam(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_getparam(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_setoptie(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_getoptie(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_setkey(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_delkey(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_setmlme(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_addmac(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_delmac(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+extern	int ieee80211_ioctl_chanlist(struct ieee80211com *,
+		struct iw_request_info *, void *, char *);
+
+extern	void ieee80211_ioctl_iwsetup(struct iw_handler_def *);
+#endif
+
+#endif /* _NET80211_IEEE80211_LINUX_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.c	2005-02-24 13:06:17.317129416 -0800
@@ -0,0 +1,1273 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.13 2003/11/09 23:36:46 sam Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_node.c,v 1.8 2003/11/02 01:29:05 dyoung Exp $");
+
+/*
+ * IEEE 802.11 node handling support.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
+static void node_cleanup(struct ieee80211com *, struct ieee80211_node *);
+static void node_free(struct ieee80211com *, struct ieee80211_node *);
+static void ieee80211_node_copy(struct ieee80211com *,
+		struct ieee80211_node *, const struct ieee80211_node *);
+static u_int8_t ieee80211_node_getrssi(struct ieee80211com *,
+		struct ieee80211_node *);
+
+static void ieee80211_setup_node(struct ieee80211com *ic,
+		struct ieee80211_node *ni, u_int8_t *macaddr);
+static void _ieee80211_free_node(struct ieee80211com *,
+		struct ieee80211_node *);
+
+MALLOC_DEFINE(M_80211_NODE, "node", "802.11 node state");
+
+void
+ieee80211_node_attach(struct ieee80211com *ic)
+{
+
+	/* XXX need unit */
+	IEEE80211_NODE_LOCK_INIT(ic, ic->ic_ifp->if_xname);
+	TAILQ_INIT(&ic->ic_node);
+	ic->ic_node_alloc = ieee80211_node_alloc;
+	ic->ic_node_free = node_free;
+	ic->ic_node_cleanup = node_cleanup;
+	ic->ic_node_copy = ieee80211_node_copy;
+	ic->ic_node_getrssi = ieee80211_node_getrssi;
+	ic->ic_scangen = 1;
+	/* default inactivity timer setings */
+	ic->ic_inact_init = IEEE80211_INACT_INIT;
+	ic->ic_inact_auth = IEEE80211_INACT_AUTH;
+	ic->ic_inact_run = IEEE80211_INACT_RUN;
+
+	if (ic->ic_max_aid == 0)
+		ic->ic_max_aid = IEEE80211_AID_DEF;
+	else if (ic->ic_max_aid > IEEE80211_AID_MAX)
+		ic->ic_max_aid = IEEE80211_AID_MAX;
+	MALLOC(ic->ic_aid_bitmap, u_int32_t *,
+		howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ic->ic_aid_bitmap == NULL) {
+		/* XXX no way to recover */
+		printf("%s: no memory for AID bitmap!\n", __func__);
+		ic->ic_max_aid = 0;
+	}
+}
+
+void
+ieee80211_node_lateattach(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211_rsnparms *rsn;
+
+	ni = ic->ic_node_alloc(ic);
+	KASSERT(ni != NULL, ("unable to setup inital BSS node"));
+	/*
+	 * Setup "global settings" in the bss node so that
+	 * each new station automatically inherits them.
+	 */
+	rsn = &ni->ni_rsn;
+	/* WEP, TKIP, and AES-CCM are always supported */
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_WEP;
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP;
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_CCM;
+	if (ic->ic_caps & IEEE80211_C_AES)
+		rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_OCB;
+	if (ic->ic_caps & IEEE80211_C_CKIP)
+		rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_CKIP;
+	/*
+	 * Default unicast cipher to WEP for 802.1x use.  If
+	 * WPA is enabled the management code will set these
+	 * values to reflect.
+	 */
+	rsn->rsn_ucastcipher = IEEE80211_CIPHER_WEP;
+	rsn->rsn_ucastkeylen = 104 / NBBY;
+	/*
+	 * WPA says the multicast cipher is the lowest unicast
+	 * cipher supported.  But we skip WEP which would
+	 * otherwise be used based on this criteria.
+	 */
+	rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP;
+	rsn->rsn_mcastkeylen = 128 / NBBY;
+
+	/*
+	 * We support both WPA-PSK and 802.1x; the one used
+	 * is determined by the authentication mode and the
+	 * setting of the PSK state.
+	 */
+	rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK;
+	rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+	ni->ni_chan = IEEE80211_CHAN_ANYC;
+	ni->ni_authmode = IEEE80211_AUTH_OPEN;
+	ni->ni_txpower = IEEE80211_TXPOWER_MAX;
+	ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+	ic->ic_bss = ni;
+	ic->ic_auth = ieee80211_authenticator_get(ni->ni_authmode);
+}
+
+void
+ieee80211_node_detach(struct ieee80211com *ic)
+{
+
+	if (ic->ic_bss != NULL) {
+		ic->ic_node_free(ic, ic->ic_bss);
+		ic->ic_bss = NULL;
+	}
+	ieee80211_free_allnodes(ic);
+	IEEE80211_NODE_LOCK_DESTROY(ic);
+	if (ic->ic_aid_bitmap != NULL)
+		FREE(ic->ic_aid_bitmap, M_DEVBUF);
+}
+
+/* 
+ * Port authorize/unauthorize interfaces for use by an authenticator.
+ */
+
+void
+ieee80211_node_authorize(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	ni->ni_flags |= IEEE80211_NODE_AUTH;
+}
+EXPORT_SYMBOL(ieee80211_node_authorize);
+
+void
+ieee80211_node_unauthorize(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	ni->ni_flags &= ~IEEE80211_NODE_AUTH;
+}
+EXPORT_SYMBOL(ieee80211_node_unauthorize);
+
+/*
+ * Set/change the channel.  The rate set is also updated as
+ * to insure a consistent view by drivers.
+ */
+static inline void
+ieee80211_set_chan(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct ieee80211_channel *chan)
+{
+	ni->ni_chan = chan;
+	ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
+}
+
+/*
+ * AP scanning support.
+ */
+
+#ifdef IEEE80211_DEBUG
+static void
+dump_chanlist(const u_char chans[])
+{
+	const char *sep;
+	int i;
+
+	sep = " ";
+	for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+		if (isset(chans, i)) {
+			printf("%s%u", sep, i);
+			sep = ", ";
+		}
+}
+#endif /* IEEE80211_DEBUG */
+
+/*
+ * Initialize the active channel set based on the set
+ * of available channels and the current PHY mode.
+ */
+static void
+ieee80211_reset_scan(struct ieee80211com *ic)
+{
+
+	/* XXX ic_des_chan should be handled with ic_chan_active */
+	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+		memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan));
+		setbit(ic->ic_chan_scan,
+			ieee80211_chan2ieee(ic, ic->ic_des_chan));
+	} else
+		memcpy(ic->ic_chan_scan, ic->ic_chan_active,
+			sizeof(ic->ic_chan_active));
+	/* NB: hack, setup so next_scan starts with the first channel */
+	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
+		ieee80211_set_chan(ic, ic->ic_bss,
+			&ic->ic_channels[IEEE80211_CHAN_MAX]);
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic)) {
+		printf("%s: scan set:", __func__);
+		dump_chanlist(ic->ic_chan_scan);
+		printf(" start chan %u\n",
+			ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
+	}
+#endif /* IEEE80211_DEBUG */
+}
+
+/*
+ * Begin an active scan.
+ */
+void
+ieee80211_begin_scan(struct ieee80211com *ic, int reset)
+{
+
+	/*
+	 * In all but hostap mode scanning starts off in
+	 * an active mode before switching to passive.
+	 */
+	if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+		ic->ic_flags |= IEEE80211_F_ASCAN;
+		ic->ic_stats.is_scan_active++;
+	} else
+		ic->ic_stats.is_scan_passive++;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, ("begin %s scan\n",
+		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive"));
+	/*
+	 * Clear scan state and flush any previously seen
+	 * AP's.  Note that the latter assumes we don't act
+	 * as both an AP and a station, otherwise we'll
+	 * potentially flush state of stations associated
+	 * with us.
+	 */
+	ieee80211_reset_scan(ic);
+	if (reset)
+		ieee80211_free_allnodes(ic);
+
+	/* Scan the next channel. */
+	ieee80211_next_scan(ic);
+}
+
+/*
+ * Switch to the next channel marked for scanning.
+ */
+int
+ieee80211_next_scan(struct ieee80211com *ic)
+{
+	struct ieee80211_channel *chan;
+
+	/*
+	 * Insure any previous mgt frame timeouts don't fire.
+	 * This assumes the driver does the right thing in
+	 * flushing anything queued in the driver and below.
+	 */
+	ic->ic_mgt_timer = 0;
+
+	chan = ic->ic_bss->ni_chan;
+	do {
+		if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX])
+			chan = &ic->ic_channels[0];
+		if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) {
+			clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			    ("%s: chan %d->%d\n", __func__,
+			    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
+			    ieee80211_chan2ieee(ic, chan)));
+			ieee80211_set_chan(ic, ic->ic_bss, chan);
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+			return 1;
+		}
+	} while (chan != ic->ic_bss->ni_chan);
+	ieee80211_end_scan(ic);
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_next_scan);
+
+void
+ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
+{
+	struct ieee80211_node *ni;
+
+	ni = ic->ic_bss;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, ("creating ibss\n"));
+	ic->ic_flags |= IEEE80211_F_SIBSS;
+	ieee80211_set_chan(ic, ic->ic_bss, chan);
+	IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
+	IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
+	if (ic->ic_opmode == IEEE80211_M_IBSS)
+		ni->ni_bssid[0] |= 0x02;	/* local bit for IBSS */
+	ni->ni_esslen = ic->ic_des_esslen;
+	memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
+	ni->ni_rssi = 0;
+	ni->ni_rstamp = 0;
+	ni->ni_tstamp.tsf = 0;
+	ni->ni_intval = ic->ic_lintval;
+	ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if (ic->ic_phytype == IEEE80211_T_FH) {
+		ni->ni_fhdwell = 200;	/* XXX */
+		ni->ni_fhindex = 1;
+	}
+	ni->ni_erp = 0;
+	ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+}
+
+static int
+ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+        u_int8_t rate;
+        int fail;
+
+	fail = 0;
+	if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
+		fail |= 0x01;
+	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+	    ni->ni_chan != ic->ic_des_chan)
+		fail |= 0x01;
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+			fail |= 0x02;
+	} else {
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
+			fail |= 0x02;
+	}
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+			fail |= 0x04;
+	} else {
+		/* XXX does this mean privacy is supported or required? */
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
+			fail |= 0x04;
+	}
+	rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
+	if (rate & IEEE80211_RATE_BASIC)
+		fail |= 0x08;
+	if (ic->ic_des_esslen != 0 &&
+	    (ni->ni_esslen != ic->ic_des_esslen ||
+	     memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0))
+		fail |= 0x10;
+	if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+		fail |= 0x20;
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic)) {
+		printf(" %c %s", fail ? '-' : '+',
+		    ether_sprintf(ni->ni_macaddr));
+		printf(" %s%c", ether_sprintf(ni->ni_bssid),
+		    fail & 0x20 ? '!' : ' ');
+		printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
+			fail & 0x01 ? '!' : ' ');
+		printf(" %+4d", ni->ni_rssi);
+		printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
+		    fail & 0x08 ? '!' : ' ');
+		printf(" %4s%c",
+		    (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
+		    (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
+		    "????",
+		    fail & 0x02 ? '!' : ' ');
+		printf(" %3s%c ",
+		    (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
+		    "wep" : "no",
+		    fail & 0x04 ? '!' : ' ');
+		ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
+		printf("%s\n", fail & 0x10 ? "!" : "");
+	}
+#endif
+	return fail;
+}
+
+/*
+ * Complete a scan of potential channels.
+ */
+void
+ieee80211_end_scan(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni, *nextbs, *selbs;
+	int i, fail;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, ("end %s scan\n",
+		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive"));
+
+	ic->ic_flags &= ~IEEE80211_F_ASCAN;
+	ni = TAILQ_FIRST(&ic->ic_node);
+
+	ieee80211_notify_scan_done(ic);
+
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		/* XXX off stack? */
+		u_char occupied[IEEE80211_CHAN_BYTES];
+		/*
+		 * The passive scan to look for existing AP's completed,
+		 * select a channel to camp on.  Identify the channels
+		 * that already have one or more AP's and try to locate
+		 * an unnoccupied one.  If that fails, pick a random
+		 * channel from the active set.
+		 */
+		for (; ni != NULL; ni = nextbs) {
+			ieee80211_ref_node(ni);
+			nextbs = TAILQ_NEXT(ni, ni_list);
+			setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan));
+			ieee80211_free_node(ic, ni);
+		}
+		for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+			if (isset(ic->ic_chan_active, i) && isclr(occupied, i))
+				break;
+		if (i == IEEE80211_CHAN_MAX) {
+			get_random_bytes(&fail, sizeof(fail));
+			fail &= 3;		/* random 0-3 */
+			for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+				if (isset(ic->ic_chan_active, i) && fail-- == 0)
+					break;
+		}
+		ieee80211_create_ibss(ic, &ic->ic_channels[i]);
+		return;
+	}
+
+	/*
+	 * When manually sequencing the state machine; scan just once
+	 * regardless of whether we have a candidate or not.  The
+	 * controlling application is expected to setup state and
+	 * initiate an association.
+	 */
+	if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL)
+		return;
+	/*
+	 * Automatic sequencing; look for a candidate and
+	 * if found join the network.
+	 */
+	if (ni == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			("%s: no scan candidate\n", __func__));
+  notfound:
+		if (ic->ic_opmode == IEEE80211_M_IBSS &&
+		    (ic->ic_flags & IEEE80211_F_IBSSON) &&
+		    ic->ic_des_esslen != 0) {
+			ieee80211_create_ibss(ic, ic->ic_ibss_chan);
+			return;
+		}
+		/*
+		 * Reset the list of channels to scan and start again.
+		 */
+		ieee80211_reset_scan(ic);
+		ieee80211_next_scan(ic);
+		return;
+	}
+	selbs = NULL;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		("\tmacaddr          bssid         chan  rssi rate flag  wep  essid\n"));
+	for (; ni != NULL; ni = nextbs) {
+		ieee80211_ref_node(ni);
+		nextbs = TAILQ_NEXT(ni, ni_list);
+		if (ni->ni_fails) {
+			/*
+			 * The configuration of the access points may change
+			 * during my scan.  So delete the entry for the AP
+			 * and retry to associate if there is another beacon.
+			 */
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+				("%s: skip scan candidate %s, fails %u\n",
+				__func__, ether_sprintf(ni->ni_macaddr),
+				ni->ni_fails));
+			if (ni->ni_fails++ > 2)
+				ieee80211_free_node(ic, ni);
+			continue;
+		}
+		if (ieee80211_match_bss(ic, ni) == 0) {
+			if (selbs == NULL)
+				selbs = ni;
+			else if (ni->ni_rssi > selbs->ni_rssi) {
+				ieee80211_unref_node(&selbs);
+				selbs = ni;
+			} else
+				ieee80211_unref_node(&ni);
+		} else {
+			ieee80211_unref_node(&ni);
+		}
+	}
+	if (selbs == NULL)
+		goto notfound;
+	if (!ieee80211_sta_join(ic, selbs)) {
+		ieee80211_unref_node(&selbs);
+		goto notfound;
+	}
+}
+
+/*
+ * Join the specified IBSS/BSS network.  The node is assumed to
+ * be passed in with a held reference.
+ */
+int
+ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs)
+{
+
+	/* XXX leak existing state in ic_bss? */
+	ic->ic_node_copy(ic, ic->ic_bss, selbs);
+	/*
+	 * Set the erp state (mostly the slot time) to deal with
+	 * the auto-select case; this should be redundant if the
+	 * mode is locked.
+	 */ 
+	ic->ic_curmode = ieee80211_chan2mode(ic, ic->ic_bss->ni_chan);
+	ieee80211_reset_erp(ic);
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE |
+		    IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+		if (ic->ic_bss->ni_rates.rs_nrates == 0) {
+			selbs->ni_fails++;
+			return 0;
+		}
+		ieee80211_unref_node(&selbs);
+		/*
+		 * Discard scan set; the nodes have a refcnt of zero
+		 * and have not asked the driver to setup private
+		 * node state.  Let them be repopulated on demand either
+		 * through transmission (ieee80211_find_txnode) or receipt
+		 * of a probe response (to be added).
+		 */
+		ieee80211_free_allnodes(ic);
+		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	} else {
+		ieee80211_unref_node(&selbs);
+		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
+	}
+	return 1;
+}
+
+/*
+ * Leave the specified IBSS/BSS network.  The node is assumed to
+ * be passed in with a held reference.
+ */
+void
+ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	ic->ic_node_cleanup(ic, ni);
+	ieee80211_notify_node_leave(ic, ni);
+}
+
+static struct ieee80211_node *
+ieee80211_node_alloc(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni;
+	MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node),
+		M_80211_NODE, M_NOWAIT | M_ZERO);
+	return ni;
+}
+
+/*
+ * Reclaim any resources in a node and reset any critical
+ * state.  Typically nodes are free'd immediately after,
+ * but in some cases the storage may be reused so we need
+ * to insure consistent state (should probably fix that).
+ */
+static void
+node_cleanup(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	int i;
+
+	ni->ni_flags = 0;
+	ni->ni_associd = 0;
+	ni->ni_esslen = 0;
+	/* XXX ni_savedq */
+	if (ni->ni_challenge != NULL) {
+		FREE(ni->ni_challenge, M_DEVBUF);
+		ni->ni_challenge = NULL;
+	}
+	if (ni->ni_wpa_ie) {
+		FREE(ni->ni_wpa_ie, M_DEVBUF);
+		ni->ni_wpa_ie = NULL;
+	}
+	for (i = 0; i < N(ni->ni_rxfrag); i++)
+		if (ni->ni_rxfrag[i] != NULL) {
+			kfree_skb(ni->ni_rxfrag[i]);
+			ni->ni_rxfrag[i] = NULL;
+		}
+	ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+#undef N
+}
+
+static void
+node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	ic->ic_node_cleanup(ic, ni);
+	FREE(ni, M_80211_NODE);
+}
+
+/*
+ * Copy state from one node to another.  The semantics are
+ * unclear for non-refcnt'd data; we leave them in the src
+ * and clear the dst.  This is what is needed for the one
+ * (current) use of this operation (setting up ic_bss after
+ * an AP scan).  In practice the fields in question should
+ * always be unused.
+ */
+static void
+ieee80211_node_copy(struct ieee80211com *ic,
+	struct ieee80211_node *dst, const struct ieee80211_node *src)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	int i;
+
+	ic->ic_node_cleanup(ic, dst);
+	*dst = *src;
+	dst->ni_challenge = NULL;
+	dst->ni_wpa_ie = NULL;
+	for (i = 0; i < N(dst->ni_rxfrag); i++)
+		dst->ni_rxfrag[i] = NULL;
+	/* NB: unicast key index reset by ieee80211_crypto_delkey */
+#undef N
+}
+
+static u_int8_t
+ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	return ni->ni_rssi;
+}
+
+static void
+ieee80211_setup_node(struct ieee80211com *ic,
+	struct ieee80211_node *ni, u_int8_t *macaddr)
+{
+	int hash;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		("%s %s\n", __func__, ether_sprintf(macaddr)));
+	IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
+	hash = IEEE80211_NODE_HASH(macaddr);
+	skb_queue_head_init(&ni->ni_savedq);
+	ieee80211_node_initref(ni);		/* mark referenced */
+	ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+	ni->ni_inact = ic->ic_inact_init;
+
+	IEEE80211_NODE_LOCK_BH(ic);
+	TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
+	LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
+	/* 
+	 * Note we don't enable the inactive timer when acting
+	 * as a station.  Nodes created in this mode represent
+	 * AP's identified while scanning.  If we time them out
+	 * then several things happen: we can't return the data
+	 * to users to show the list of AP's we encountered, and
+	 * more importantly, we'll incorrectly deauthenticate
+	 * ourself because the inactivity timer will kick us off. 
+	 */
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
+	IEEE80211_NODE_UNLOCK_BH(ic);
+}
+
+struct ieee80211_node *
+ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+	struct ieee80211_node *ni = ic->ic_node_alloc(ic);
+	if (ni != NULL)
+		ieee80211_setup_node(ic, ni, macaddr);
+	else
+		ic->ic_stats.is_rx_nodealloc++;
+	return ni;
+}
+
+struct ieee80211_node *
+ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+	struct ieee80211_node *ni = ic->ic_node_alloc(ic);
+	if (ni != NULL) {
+		ieee80211_setup_node(ic, ni, macaddr);
+		/*
+		 * Inherit from ic_bss.
+		 */
+		ni->ni_authmode = ic->ic_bss->ni_authmode;
+		ni->ni_txpower = ic->ic_bss->ni_txpower;
+		ni->ni_vlan = ic->ic_bss->ni_vlan;	/* XXX?? */
+		IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
+		ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+		ni->ni_rsn = ic->ic_bss->ni_rsn;
+	} else
+		ic->ic_stats.is_rx_nodealloc++;
+	return ni;
+}
+
+struct ieee80211_node *
+_ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+	struct ieee80211_node *ni;
+	int hash;
+
+	IEEE80211_NODE_LOCK_ASSERT(ic);
+
+	hash = IEEE80211_NODE_HASH(macaddr);
+	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
+			ieee80211_node_incref(ni); /* mark referenced */
+			return ni;
+		}
+	}
+	return NULL;
+}
+
+struct ieee80211_node *
+ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+	struct ieee80211_node *ni;
+
+	IEEE80211_NODE_LOCK(ic);
+	ni = _ieee80211_find_node(ic, macaddr);
+	IEEE80211_NODE_UNLOCK(ic);
+	return ni;
+}
+EXPORT_SYMBOL(ieee80211_find_node);
+
+/*
+ * Return a reference to the appropriate node for sending
+ * a data frame.  This handles node discovery in adhoc networks.
+ */
+struct ieee80211_node *
+ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+	struct ieee80211_node *ni;
+
+	/*
+	 * The destination address should be in the node table
+	 * unless we are operating in station mode or this is a
+	 * multicast/broadcast frame.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
+		return ic->ic_bss;
+
+	/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
+	IEEE80211_NODE_LOCK(ic);
+	ni = _ieee80211_find_node(ic, macaddr);
+	IEEE80211_NODE_UNLOCK(ic);
+	if (ni == NULL &&
+	    (ic->ic_opmode == IEEE80211_M_IBSS ||
+	     ic->ic_opmode == IEEE80211_M_AHDEMO)) {
+		/*
+		 * Fake up a node; this handles node discovery in
+		 * adhoc mode.  Note that for the driver's benefit
+		 * we we treat this like an association so the driver
+		 * has an opportunity to setup it's private state.
+		 *
+		 * XXX need better way to handle this; issue probe
+		 *     request so we can deduce rate set, etc.
+		 */
+		ni = ieee80211_dup_bss(ic, macaddr);
+		if (ni != NULL) {
+			/* XXX no rate negotiation; just dup */
+			ni->ni_rates = ic->ic_bss->ni_rates;
+			if (ic->ic_newassoc)
+				(*ic->ic_newassoc)(ic, ni, 1);
+			/* XXX not sure how 802.1x works w/ IBSS */
+			ieee80211_node_authorize(ic, ni);
+		}
+	}
+	return ni;
+}
+
+/*
+ * Like find but search based on the channel too.
+ */
+struct ieee80211_node *
+ieee80211_find_node_with_channel(struct ieee80211com *ic, u_int8_t *macaddr,
+	struct ieee80211_channel *chan)
+{
+	struct ieee80211_node *ni;
+	int hash;
+
+	hash = IEEE80211_NODE_HASH(macaddr);
+	IEEE80211_NODE_LOCK(ic);
+	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
+		    ni->ni_chan == chan) {
+			ieee80211_node_incref(ni);/* mark referenced */
+			break;
+		}
+	}
+	IEEE80211_NODE_UNLOCK(ic);
+	return ni;
+}
+
+/*
+ * Like find but search based on the ssid too.
+ */
+struct ieee80211_node *
+ieee80211_find_node_with_ssid(struct ieee80211com *ic, u_int8_t *macaddr,
+	u_int ssidlen, const u_int8_t *ssid)
+{
+	struct ieee80211_node *ni;
+	int hash;
+
+	hash = IEEE80211_NODE_HASH(macaddr);
+	IEEE80211_NODE_LOCK(ic);
+	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
+		    ni->ni_esslen == ic->ic_des_esslen &&
+		    memcmp(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen) == 0) {
+			ieee80211_node_incref(ni);/* mark referenced */
+			break;
+		}
+	}
+	IEEE80211_NODE_UNLOCK(ic);
+	return ni;
+}
+
+static void
+_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	KASSERT(ni != ic->ic_bss, ("freeing bss node"));
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		("%s %s\n", __func__, ether_sprintf(ni->ni_macaddr)));
+	IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+	TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
+	if (TAILQ_EMPTY(&ic->ic_node))
+		ic->ic_inact_timer = 0;
+	LIST_REMOVE(ni, ni_hash);
+	if (_IF_QLEN(&ni->ni_savedq) != 0) {
+		/*
+		 * Drain power save queue.
+		 */
+		IF_DRAIN(&ni->ni_savedq);
+		if (ic->ic_set_tim)
+			(*ic->ic_set_tim)(ic, ni->ni_associd, 0);
+	}
+	ic->ic_node_free(ic, ni);
+}
+
+void
+ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	KASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		("%s %s refcnt %d\n", __func__,
+		 ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)));
+	if (ieee80211_node_dectestref(ni)) {
+		IEEE80211_NODE_LOCK_BH(ic);
+		_ieee80211_free_node(ic, ni);
+		IEEE80211_NODE_UNLOCK_BH(ic);
+	}
+}
+EXPORT_SYMBOL(ieee80211_free_node);
+
+void
+ieee80211_free_allnodes(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, ("free all nodes\n"));
+	IEEE80211_NODE_LOCK_BH(ic);
+	while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) {
+		if (ni->ni_associd != 0) {
+			if (ic->ic_auth->ia_node_leave != NULL)
+				ic->ic_auth->ia_node_leave(ic, ni);
+			IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+		}
+		_ieee80211_free_node(ic, ni);
+	}
+	ieee80211_reset_erp(ic);
+	IEEE80211_NODE_UNLOCK_BH(ic);
+
+	if (ic->ic_bss != NULL)
+		ic->ic_node_cleanup(ic, ic->ic_bss);	/* for station mode */
+}
+
+/*
+ * Timeout inactive nodes.  Note that we cannot hold the node
+ * lock while sending a frame as this would lead to a LOR.
+ * Instead we use a generation number to mark nodes that we've
+ * scanned and drop the lock and restart a scan if we have to
+ * time out a node.  Since we are single-threaded by virtue of
+ * controlling the inactivity timer we can be sure this will
+ * process each node only once.
+ */
+void
+ieee80211_timeout_nodes(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni;
+	u_int gen = ic->ic_scangen++;		/* NB: ok 'cuz single-threaded*/
+
+restart:
+	IEEE80211_NODE_LOCK(ic);
+	TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+		if (ni->ni_scangen == gen)	/* previously handled */
+			continue;
+		ni->ni_scangen = gen;
+		/*
+		 * Free fragment skb if not needed anymore
+		 * (last fragment older than 1s).
+		 * XXX doesn't belong here
+		 */
+		if (ni->ni_rxfrag[0] && jiffies > ni->ni_rxfragstamp + HZ) {
+			kfree_skb(ni->ni_rxfrag[0]);
+			ni->ni_rxfrag[0] = NULL;
+		}
+		if (ni->ni_inact && --ni->ni_inact <= 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+			    ("station %s timed out due to inactivity\n",
+			    ether_sprintf(ni->ni_macaddr)));
+			/*
+			 * Send a deauthenticate frame.
+			 *
+			 * Drop the node lock before sending the
+			 * deauthentication frame in case the driver takes     
+			 * a lock, as this will result in a LOR between the     
+			 * node lock and the driver lock.
+			 */
+			IEEE80211_NODE_UNLOCK(ic);
+			IEEE80211_SEND_MGMT(ic, ni,
+			    IEEE80211_FC0_SUBTYPE_DEAUTH,
+			    IEEE80211_REASON_AUTH_EXPIRE);
+			ieee80211_node_leave(ic, ni);
+			ic->ic_stats.is_node_timeout++;
+			goto restart;
+		}
+	}
+	if (!TAILQ_EMPTY(&ic->ic_node))
+		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
+	IEEE80211_NODE_UNLOCK(ic);
+}
+
+void
+ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg)
+{
+	struct ieee80211_node *ni;
+
+	IEEE80211_NODE_LOCK(ic);
+	TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+		(*f)(arg, ni);
+	IEEE80211_NODE_UNLOCK(ic);
+}
+EXPORT_SYMBOL(ieee80211_iterate_nodes);
+
+void
+ieee80211_dump_node(struct ieee80211_node *ni)
+{
+	printf("0x%p: mac %s refcnt %d\n", ni,
+		ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni));
+	printf("\tscangen %u authmode %u flags 0x%x\n",
+		ni->ni_scangen, ni->ni_authmode, ni->ni_flags);
+	printf("\tassocid 0x%x txpower %u vlan %u\n",
+		ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
+	printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
+		ni->ni_txseq,
+		ni->ni_rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+		ni->ni_rxseq & IEEE80211_SEQ_FRAG_MASK,
+		ni->ni_rxfragstamp);
+	printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n",
+		ni->ni_rstamp, ni->ni_rssi, ni->ni_intval, ni->ni_capinfo);
+	printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n",
+		ether_sprintf(ni->ni_bssid),
+		ni->ni_esslen, ni->ni_essid,
+		ni->ni_chan->ic_freq, ni->ni_chan->ic_flags);
+	printf("\tfails %u inact %u txrate %u\n",
+		ni->ni_fails, ni->ni_inact, ni->ni_txrate);
+}
+
+void
+ieee80211_dump_nodes(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni;
+
+	IEEE80211_NODE_LOCK(ic);
+	TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+		ieee80211_dump_node(ni);
+	IEEE80211_NODE_UNLOCK(ic);
+}
+
+/*
+ * Handle a station joining an 11g network.
+ */
+static void
+ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	/*
+	 * Station isn't capable of short slot time.  Bump
+	 * the count of long slot time stations and disable
+	 * use of short slot time.  Note that the actual switch
+	 * over to long slot time use may not occur until the
+	 * next beacon transmission (per sec. 7.3.1.4 of 11g).
+	 */
+	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+		ic->ic_longslotsta++;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			("station %s needs long slot time, "
+			"count %d\n",
+			ether_sprintf(ni->ni_macaddr),
+			ic->ic_longslotsta));
+		/* XXX vap's w/ conflicting needs won't work */
+		ieee80211_set_shortslottime(ic, 0);
+	}
+	/*
+	 * If the new station is not an ERP station
+	 * then bump the counter and enable protection
+	 * if configured.
+	 */
+	if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
+		ic->ic_nonerpsta++;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			("station %s is !ERP, %d non-ERP stations "
+			"associated\n",
+			ether_sprintf(ni->ni_macaddr),
+			ic->ic_nonerpsta));
+		/*
+		 * If protection is configured, enable it.
+		 */
+		if (ic->ic_protmode != IEEE80211_PROT_NONE) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    ("enable use of protection\n"));
+			ic->ic_flags |= IEEE80211_F_USEPROT;
+		}
+		/*
+		 * If station does not support short preamble
+		 * then we must enable use of Barker preamble.
+		 */
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    ("station %s needs long preamble\n",
+			    ether_sprintf(ni->ni_macaddr)));
+			ic->ic_flags |= IEEE80211_F_USEBARKER;
+			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+		}
+	} else
+		ni->ni_flags |= IEEE80211_NODE_ERP;
+}
+
+void
+ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp)
+{
+	int newassoc;
+
+	if (ni->ni_associd == 0) {
+		u_int16_t aid;
+
+		/*
+		 * It would be good to search the bitmap
+		 * more efficiently, but this will do for now.
+		 */
+		for (aid = 1; aid < ic->ic_max_aid; aid++) {
+			if (!IEEE80211_AID_ISSET(aid,
+			    ic->ic_aid_bitmap))
+				break;
+		}
+		if (aid >= ic->ic_max_aid) {
+			IEEE80211_SEND_MGMT(ic, ni, resp,
+			    IEEE80211_REASON_ASSOC_TOOMANY);
+			ieee80211_node_leave(ic, ni);
+			return;
+		}
+		ni->ni_associd = aid | 0xc000;
+		IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
+		newassoc = 1;
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			ieee80211_node_join_11g(ic, ni);
+	} else
+		newassoc = 0;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
+		("station %s %s associated at aid %d\n",
+		ether_sprintf(ni->ni_macaddr),
+		newassoc ? "newly" : "already",
+		IEEE80211_NODE_AID(ni)));
+
+	/* give driver a chance to setup state like ni_txrate */
+	if (ic->ic_newassoc)
+		(*ic->ic_newassoc)(ic, ni, newassoc);
+	IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+	/* tell the authenticator about new station */
+	if (ic->ic_auth->ia_node_join != NULL)
+		(*ic->ic_auth->ia_node_join)(ic, ni);
+	ieee80211_notify_node_join(ic, ni, newassoc);
+}
+
+/*
+ * Handle a station leaving an 11g network.
+ */
+static void
+ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	KASSERT(ic->ic_curmode == IEEE80211_MODE_11G,
+		("not in 11g, curmode %x", ic->ic_curmode));
+
+	/*
+	 * If a long slot station do the slot time bookkeeping.
+	 */
+	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+		KASSERT(ic->ic_longslotsta > 0,
+		    ("bogus long slot station count %d", ic->ic_longslotsta));
+		ic->ic_longslotsta--;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			("long slot time station %s leaves, count now %d\n",
+			ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta));
+		if (ic->ic_longslotsta == 0) {
+			/*
+			 * Re-enable use of short slot time if supported
+			 * and not operating in IBSS mode (per spec).
+			 */
+			if ((ic->ic_caps & IEEE80211_C_SHSLOT) &&
+			    ic->ic_opmode != IEEE80211_M_IBSS) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+					("re-enable use of short slot time\n"));
+				ieee80211_set_shortslottime(ic, 1);
+			}
+		}
+	}
+	/*
+	 * If a non-ERP station do the protection-related bookkeeping.
+	 */
+	if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) {
+		KASSERT(ic->ic_nonerpsta > 0,
+		    ("bogus non-ERP station count %d", ic->ic_nonerpsta));
+		ic->ic_nonerpsta--;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			("non-ERP station %s leaves, count now %d\n",
+			ether_sprintf(ni->ni_macaddr), ic->ic_nonerpsta));
+		if (ic->ic_nonerpsta == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				("disable use of protection\n"));
+			ic->ic_flags &= ~IEEE80211_F_USEPROT;
+			/* XXX verify mode? */
+			if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+					("re-enable use of short preamble\n"));
+				ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+				ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+			}
+		}
+	}
+}
+
+/*
+ * Handle bookkeeping for station deauthentication/disassociation
+ * when operating as an ap.
+ */
+void
+ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP,
+		("not in ap mode, mode %u", ic->ic_opmode));
+	/*
+	 * If node wasn't previously associated all
+	 * we need to do is reclaim the reference.
+	 */
+	if (ni->ni_associd == 0)
+		goto done;
+	/*
+	 * Tell the authenticator the station is leaving.
+	 * Note that we must do this before yanking the
+	 * association id as the authenticator uses the
+	 * associd to locate it's state block.
+	 */
+	if (ic->ic_auth->ia_node_leave != NULL)
+		(*ic->ic_auth->ia_node_leave)(ic, ni);
+	IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+	ni->ni_associd = 0;
+
+	if (ic->ic_curmode == IEEE80211_MODE_11G)
+		ieee80211_node_leave_11g(ic, ni);
+	/*
+	 * Cleanup station state.  In particular clear various
+	 * state that might otherwise be reused if the node
+	 * is reused before the reference count goes to zero
+	 * (and memory is reclaimed).
+	 */
+	ieee80211_sta_leave(ic, ni);
+done:
+	ieee80211_free_node(ic, ni);
+}
+EXPORT_SYMBOL(ieee80211_node_leave);
+
+u_int8_t
+ieee80211_getrssi(struct ieee80211com *ic)
+{
+#define	NZ(x)	((x) == 0 ? 1 : (x))
+	u_int32_t rssi_samples, rssi_total;
+	struct ieee80211_node *ni;
+
+	rssi_total = 0;
+	rssi_samples = 0;
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_IBSS:		/* average of all ibss neighbors */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+			if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) {
+				rssi_samples++;
+				rssi_total += ic->ic_node_getrssi(ic, ni);
+			}
+		break;
+	case IEEE80211_M_AHDEMO:	/* average of all neighbors */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+			rssi_samples++;
+			rssi_total += ic->ic_node_getrssi(ic, ni);
+		}
+		break;
+	case IEEE80211_M_HOSTAP:	/* average of all associated stations */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+			if (IEEE80211_AID(ni->ni_associd) != 0) {
+				rssi_samples++;
+				rssi_total += ic->ic_node_getrssi(ic, ni);
+			}
+		break;
+	case IEEE80211_M_MONITOR:	/* XXX */
+	case IEEE80211_M_STA:		/* use stats from associated ap */
+	default:
+		if (ic->ic_bss != NULL)
+			rssi_total = ic->ic_node_getrssi(ic, ic->ic_bss);
+		rssi_samples = 1;
+		break;
+	}
+	return rssi_total / NZ(rssi_samples);
+#undef NZ
+}
+EXPORT_SYMBOL(ieee80211_getrssi);
+
+/*
+ * Set the short slot time state and notify the driver.
+ */
+void
+ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
+{
+	if (onoff)
+		ic->ic_flags |= IEEE80211_F_SHSLOT;
+	else
+		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+	/* notify driver */
+	if (ic->ic_updateslot != NULL)
+		ic->ic_updateslot(ic->ic_dev);
+}
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.h	2005-02-24 13:06:17.318129264 -0800
@@ -0,0 +1,220 @@
+/*	$NetBSD: ieee80211_node.h,v 1.7 2003/10/29 21:50:57 dyoung Exp $	*/
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.7 2003/10/17 21:41:52 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_NODE_H_
+#define _NET80211_IEEE80211_NODE_H_
+
+#include <net80211/ieee80211_ioctl.h>		/* for ieee80211_nodestats */
+
+/*
+ * Each ieee80211com instance has a single timer that fires once a
+ * second.  This is used to initiate various work depending on the
+ * state of the instance: scanning (passive or active), ``transition''
+ * (waiting for a response to a management frame when operating
+ * as a station), and node inactivity processing (when operating
+ * as an AP).  For inactivity processing each node has a timeout
+ * set in it's ni_intval field that is decremented on each timeout
+ * and the node is reclaimed when the counter goes to zero.  We
+ * use different inactivity timeout values depending on whether
+ * the node is associated and authorized (either by 802.1x or
+ * open/shared key authentication) or associated but yet to be
+ * authorized.  The latter timeout is shorter to more aggressively
+ * reclaim nodes that leave part way through the 802.1x exchange.
+ */
+#define	IEEE80211_PSCAN_WAIT 	5		/* passive scan intvl (secs) */
+#define	IEEE80211_TRANS_WAIT 	5		/* transition interval (secs) */
+#define	IEEE80211_INACT_WAIT	5		/* inactivity interval (secs) */
+#define	IEEE80211_INACT_INIT	(30/IEEE80211_INACT_WAIT)	/* initial */
+#define	IEEE80211_INACT_AUTH	(180/IEEE80211_INACT_WAIT)	/* associated but not authorized */
+#define	IEEE80211_INACT_RUN	(300/IEEE80211_INACT_WAIT)	/* authorized */
+
+#define	IEEE80211_NODE_HASHSIZE	32
+/* simple hash is enough for variation of macaddr */
+#define	IEEE80211_NODE_HASH(addr)	\
+	(((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE)
+
+#define	IEEE80211_RATE_SIZE	8		/* 802.11 standard */
+#define	IEEE80211_RATE_MAXSIZE	15		/* max rates we'll handle */
+
+struct ieee80211_rateset {
+	u_int8_t		rs_nrates;
+	u_int8_t		rs_rates[IEEE80211_RATE_MAXSIZE];
+};
+
+struct ieee80211_rsnparms {
+	u_int8_t	rsn_mcastcipher;	/* mcast/group cipher */
+	u_int8_t	rsn_mcastkeylen;	/* mcast key length */
+	u_int8_t	rsn_ucastcipherset;	/* unicast cipher set */
+	u_int8_t	rsn_ucastcipher;	/* selected unicast cipher */
+	u_int8_t	rsn_ucastkeylen;	/* unicast key length */
+	u_int8_t	rsn_keymgmtset;		/* key mangement algorithms */
+	u_int8_t	rsn_keymgmt;		/* selected key mgmt algo */
+	u_int16_t	rsn_caps;		/* capabilities */
+};
+
+/*
+ * Node specific information.  Note that drivers are expected
+ * to derive from this structure to add device-specific per-node
+ * state.  This is done by overriding the ic_node_* methods in
+ * the ieee80211com structure.
+ */
+struct ieee80211_node {
+	TAILQ_ENTRY(ieee80211_node)	ni_list;
+	LIST_ENTRY(ieee80211_node)	ni_hash;
+	atomic_t		ni_refcnt;
+	u_int			ni_scangen;	/* gen# for timeout scan */
+	u_int8_t		ni_authmode;	/* authentication algorithm */
+	u_int16_t		ni_flags;	/* special-purpose state */
+#define	IEEE80211_NODE_AUTH	0x0001		/* authorized for data */
+#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
+#define	IEEE80211_NODE_ERP	0x0004		/* ERP enabled */
+/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */
+#define	IEEE80211_NODE_PWR_MGT	0x0010		/* power save mode enabled */
+	u_int16_t		ni_associd;	/* assoc response */
+	u_int16_t		ni_txpower;	/* current transmit power */
+	u_int16_t		ni_vlan;	/* vlan tag */
+	u_int32_t		*ni_challenge;	/* shared-key challenge */
+	u_int8_t		*ni_wpa_ie;	/* captured WPA/RSN ie */
+	u_int16_t		ni_txseq;	/* seq to be transmitted */
+	u_int16_t		ni_rxseq;	/* seq previous received */
+	u_int16_t		ni_rxseqs[16];	/* seq previous for qos frames*/
+	u_int32_t		ni_rxfragstamp;	/* time stamp of last rx frag */
+	struct sk_buff		*ni_rxfrag[3];	/* rx frag reassembly */
+	struct ieee80211_rsnparms ni_rsn;	/* RSN/WPA parameters */
+	struct ieee80211_key	ni_ucastkey;	/* unicast key */
+
+	/* hardware */
+	u_int32_t		ni_rstamp;	/* recv timestamp */
+	u_int8_t		ni_rssi;	/* recv ssi */
+
+	/* header */
+	u_int8_t		ni_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t		ni_bssid[IEEE80211_ADDR_LEN];
+
+	/* beacon, probe response */
+	union {
+		u_int8_t	data[8];
+		u_int64_t	tsf;
+	} ni_tstamp;				/* from last rcv'd beacon */
+	u_int16_t		ni_intval;	/* beacon interval */
+	u_int16_t		ni_capinfo;	/* capabilities */
+	u_int8_t		ni_esslen;
+	u_int8_t		ni_essid[IEEE80211_NWID_LEN];
+	struct ieee80211_rateset ni_rates;	/* negotiated rate set */
+	struct ieee80211_channel *ni_chan;
+	u_int16_t		ni_fhdwell;	/* FH only */
+	u_int8_t		ni_fhindex;	/* FH only */
+	u_int8_t		ni_erp;		/* ERP from beacon/probe resp */
+	u_int16_t		ni_timoff;	/* byte offset to TIM ie */
+
+	/* others */
+	struct sk_buff_head	ni_savedq;	/* packets queued for pspoll */
+	u_int32_t		ni_pwrsavedrops;/* pspoll packets dropped */
+	int			ni_fails;	/* failure count to associate */
+	int			ni_inact;	/* current inactivity count */
+	int			ni_txrate;	/* index to ni_rates[] */
+	struct ieee80211_nodestats ni_stats;	/* per-node statistics */
+};
+MALLOC_DECLARE(M_80211_NODE);
+
+#define	IEEE80211_NODE_AID(ni)	IEEE80211_AID(ni->ni_associd)
+
+static __inline struct ieee80211_node *
+ieee80211_ref_node(struct ieee80211_node *ni)
+{
+	ieee80211_node_incref(ni);
+	return ni;
+}
+
+static __inline void
+ieee80211_unref_node(struct ieee80211_node **ni)
+{
+	ieee80211_node_decref(*ni);
+	*ni = NULL;			/* guard against use */
+}
+
+struct ieee80211com;
+
+extern	void ieee80211_node_attach(struct ieee80211com *);
+extern	void ieee80211_node_lateattach(struct ieee80211com *);
+extern	void ieee80211_node_detach(struct ieee80211com *);
+
+extern	void ieee80211_node_authorize(struct ieee80211com *,
+		struct ieee80211_node *);
+extern	void ieee80211_node_unauthorize(struct ieee80211com *,
+		struct ieee80211_node *);
+
+extern	void ieee80211_begin_scan(struct ieee80211com *, int);
+extern	int ieee80211_next_scan(struct ieee80211com *);
+extern	void ieee80211_create_ibss(struct ieee80211com*,
+		struct ieee80211_channel *);
+extern	void ieee80211_end_scan(struct ieee80211com *);
+extern	int ieee80211_sta_join(struct ieee80211com *,
+		struct ieee80211_node *);
+extern	void ieee80211_sta_leave(struct ieee80211com *,
+		struct ieee80211_node *);
+
+extern	struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *,
+		u_int8_t *);
+extern	struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *,
+		u_int8_t *);
+extern	struct ieee80211_node *ieee80211_find_node(struct ieee80211com *,
+		u_int8_t *);
+extern	struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
+		u_int8_t *);
+extern	struct ieee80211_node *ieee80211_find_node_with_channel(
+		struct ieee80211com *, u_int8_t *macaddr,
+		struct ieee80211_channel *);
+extern	struct ieee80211_node *ieee80211_find_node_with_ssid(
+		struct ieee80211com *, u_int8_t *macaddr, u_int ssidlen,
+		const u_int8_t *ssid);
+extern	void ieee80211_free_node(struct ieee80211com *,
+		struct ieee80211_node *);
+extern	void ieee80211_free_allnodes(struct ieee80211com *);
+
+typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
+extern	void ieee80211_iterate_nodes(struct ieee80211com *,
+		ieee80211_iter_func *, void *);
+
+extern	void ieee80211_dump_node(struct ieee80211_node *);
+extern	void ieee80211_dump_nodes(struct ieee80211com *);
+extern	void ieee80211_timeout_nodes(struct ieee80211com *);
+
+extern	void ieee80211_node_join(struct ieee80211com *,
+		struct ieee80211_node *, int);
+extern	void ieee80211_node_leave(struct ieee80211com *,
+		struct ieee80211_node *);
+extern  u_int8_t ieee80211_getrssi(struct ieee80211com *);
+extern	void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
+#endif /* _NET80211_IEEE80211_NODE_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_output.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_output.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_output.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_output.c	2005-02-24 13:06:17.319129112 -0800
@@ -0,0 +1,1162 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.9 2003/10/17 23:15:30 sam Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_output.c,v 1.9 2003/11/02 00:17:27 dyoung Exp $");
+
+/*
+ * IEEE 802.11 output handling.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+
+#include "if_llc.h"
+#include "if_ethersubr.h"
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Decide if an outbound management frame should be
+ * printed when debugging is enabled.  This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211com *ic, int subtype)
+{
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		return (ic->ic_opmode == IEEE80211_M_IBSS);
+	}
+	return 1;
+}
+#endif
+
+/*
+ * Send a management frame to the specified node.  The node pointer
+ * must have a reference as the pointer will be passed to the driver
+ * and potentially held for a long time.  If the frame is successfully
+ * dispatched to the driver, then it is responsible for freeing the
+ * reference (and potentially free'ing up any associated storage).
+ */
+static void
+ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
+    struct sk_buff *skb, int type)
+{
+	struct ieee80211_frame *wh;
+	struct ieee80211_cb *cb = (struct ieee80211_cb *)skb->cb;
+
+	KASSERT(ni != NULL, ("null node"));
+	ni->ni_inact = ic->ic_inact_auth;
+
+	/*
+	 * Yech, hack alert!  We want to pass the node down to the
+	 * driver's start routine.  If we don't do so then the start
+	 * routine must immediately look it up again and that can
+	 * cause a lock order reversal if, for example, this frame
+	 * is being sent because the station is being timedout and
+	 * the frame being sent is a DEAUTH message. 
+	 */
+	cb->ni = ni;
+
+	wh = (struct ieee80211_frame *)
+		skb_push(skb, sizeof(struct ieee80211_frame));
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
+	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+	*(u_int16_t *)&wh->i_dur[0] = 0;
+	*(u_int16_t *)&wh->i_seq[0] =
+	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+	ni->ni_txseq++;
+	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+
+	if ((cb->flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
+		cb->flags &= ~M_LINK0;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			("%s: encrypting frame for %s\n",
+			__func__, ether_sprintf(wh->i_addr1)));
+		wh->i_fc[1] |= IEEE80211_FC1_WEP;
+	}
+#ifdef IEEE80211_DEBUG
+	/* avoid printing too many frames */
+	if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
+	    ieee80211_msg_dumppkts(ic)) {
+		if_printf(ic->ic_dev, "sending %s to %s on channel %u\n",
+		    ieee80211_mgt_subtype_name[
+		    (type & IEEE80211_FC0_SUBTYPE_MASK)
+		    >> IEEE80211_FC0_SUBTYPE_SHIFT],
+		    ether_sprintf(ni->ni_macaddr),
+		    ieee80211_chan2ieee(ic, ni->ni_chan));
+	}
+#endif
+	(void) (*ic->ic_mgtstart)(ic, skb);
+}
+
+/*
+ * Insure there is sufficient headroom and tailroom to
+ * encapsulate the 802.11 data frame.  If room isn't
+ * already there, reallocate so there is enough space.
+ * Drivers and cipher modules assume we have done the
+ * necessary work and fail rudely if they don't find
+ * the space they need.
+ */
+static struct sk_buff *
+ieee80211_skbhdr_adjust(struct ieee80211com *ic, struct ieee80211_key *key,
+	struct sk_buff *skb)
+{
+	/* XXX too conservative, e.g. qos frame */
+	/* XXX pre-calculate per node? */
+	int need_headroom = sizeof(struct ieee80211_qosframe)
+		 + sizeof(struct llc)
+		 + IEEE80211_ADDR_LEN
+		 ;
+	int need_tailroom = 0;
+
+	if (key != NULL) {
+		const struct ieee80211_cipher *cip = key->wk_cipher;
+		/*
+		 * Adjust for crypto needs.  When hardware crypto is
+		 * being used we assume the hardware/driver will deal
+		 * with any padding (on the fly, without needing to
+		 * expand the frame contents).  When software crypto
+		 * is used we need to insure room is available at the
+		 * front and back and also for any per-MSDU additions.
+		 */
+		/* XXX belongs in crypto code? */
+		need_headroom += cip->ic_header;
+		/* XXX pre-calculate per key */
+		if (key->wk_flags & IEEE80211_KEY_SWCRYPT)
+			need_tailroom += cip->ic_trailer;
+		/* XXX frags */
+		if (key->wk_flags & IEEE80211_KEY_SWMIC)
+			need_tailroom += cip->ic_miclen;
+	}
+
+	skb = skb_unshare(skb, GFP_ATOMIC);
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+		    ("%s: cannot unshare for encapsulation\n", __func__));
+		ic->ic_stats.is_tx_nobuf++;
+	} else if (skb_tailroom(skb) < need_tailroom) {
+		int headroom = skb_headroom(skb) < need_headroom ?
+			need_headroom - skb_headroom(skb) : 0;
+		if (pskb_expand_head(skb, headroom,
+			need_tailroom - skb_tailroom(skb), GFP_ATOMIC)) {
+			dev_kfree_skb(skb);
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+			    ("%s: cannot expand storage (tail)\n", __func__));
+			ic->ic_stats.is_tx_nobuf++;
+			skb = NULL;
+		}
+	} else if (skb_headroom(skb) < need_headroom) {
+		struct sk_buff *tmp = skb;
+		skb = skb_realloc_headroom(skb, need_headroom);
+		dev_kfree_skb(tmp);
+		if (skb == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+			    ("%s: cannot expand storage (head)\n", __func__));
+			ic->ic_stats.is_tx_nobuf++;
+		}
+	}
+	return skb;
+}
+
+/*
+ * Return the transmit key to use in sending a frame to
+ * the specified destination. Multicast traffic always
+ * uses the group key.  Otherwise if a unicast key is
+ * set we use that.  When no unicast key is set we fall
+ * back to the default transmit key.
+ */ 
+static inline struct ieee80211_key *
+ieee80211_crypto_getkey(struct ieee80211com *ic,
+	const u_int8_t mac[IEEE80211_ADDR_LEN], struct ieee80211_node *ni)
+{
+#define	KEY_UNDEFINED(k)	((k).wk_cipher == &ieee80211_cipher_none)
+	if (IEEE80211_IS_MULTICAST(mac) || KEY_UNDEFINED(ni->ni_ucastkey)) {
+		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
+		    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+			    ("%s: no transmit key, def_txkey %u\n",
+			    __func__, ic->ic_def_txkey));
+			/* XXX statistic */
+			return NULL;
+		}
+		return &ic->ic_nw_keys[ic->ic_def_txkey];
+	} else {
+		return &ni->ni_ucastkey;
+	}
+#undef KEY_UNDEFINED
+}
+
+/*
+ * Encapsulate an outbound data frame.  The mbuf chain is updated and
+ * a reference to the destination node is returned.  If an error is
+ * encountered NULL is returned and the node reference will also be NULL.
+ * 
+ * NB: The caller is responsible for free'ing a returned node reference.
+ *     The convention is ic_bss is not reference counted; the caller must
+ *     maintain that.
+ */
+struct sk_buff *
+ieee80211_encap(struct ieee80211com *ic, struct sk_buff *skb,
+	struct ieee80211_node **pni)
+{
+	struct ether_header eh;
+	struct ieee80211_frame *wh;
+	struct ieee80211_node *ni;
+	struct ieee80211_key *key;
+	struct llc *llc;
+
+	memcpy(&eh, skb->data, sizeof(struct ether_header));
+	skb_pull(skb, sizeof(struct ether_header));
+
+	ni = ieee80211_find_txnode(ic, eh.ether_dhost);
+	if (ni == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+			("%s: no node for dst %s, discard frame\n",
+			__func__, ether_sprintf(eh.ether_dhost)));
+		ic->ic_stats.is_tx_nonode++; 
+		goto bad;
+	}
+
+	/* 
+	 * If node has a vlan tag then all traffic
+	 * to it must have a matching tag.
+	 */
+	if (ni->ni_vlan != 0) {
+		if (ic->ic_vlgrp == NULL || !vlan_tx_tag_present(skb)) {
+			ni->ni_stats.ns_tx_novlantag++;
+			goto bad;
+		}
+		if (vlan_tx_tag_get(skb) != ni->ni_vlan) {
+			ni->ni_stats.ns_tx_vlanmismatch++;
+			goto bad;
+		}
+	}
+
+	/*
+	 * Insure space for additional headers.  First
+	 * identify transmit key to use in calculating any
+	 * buffer adjustments required.  This is also used
+	 * below to do privacy encapsulation work.
+	 *
+	 * Note key may be NULL if we fall back to the default
+	 * transmit key and that is not set.  In that case the
+	 * buffer may not be expanded as needed by the cipher
+	 * routines, but they will/should discard it.
+	 */
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		key = ieee80211_crypto_getkey(ic, eh.ether_dhost, ni);
+	else
+		key = NULL;
+	skb = ieee80211_skbhdr_adjust(ic, key, skb);
+	if (skb == NULL) {
+		/* NB: ieee80211_skbhdr_adjust handles msgs+statistics */
+		goto bad;
+	}
+
+	llc = (struct llc *) skb_push(skb, sizeof(struct llc));
+	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+	llc->llc_control = LLC_UI;
+	llc->llc_snap.org_code[0] = 0;
+	llc->llc_snap.org_code[1] = 0;
+	llc->llc_snap.org_code[2] = 0;
+	llc->llc_snap.ether_type = eh.ether_type;
+
+	wh = (struct ieee80211_frame *)
+		skb_push(skb, sizeof(struct ieee80211_frame));;
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
+	*(u_int16_t *)&wh->i_dur[0] = 0;
+	*(u_int16_t *)&wh->i_seq[0] =
+	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+	ni->ni_txseq++;
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_STA:
+		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
+		IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
+		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+		IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+		break;
+	case IEEE80211_M_IBSS:
+	case IEEE80211_M_AHDEMO:
+		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+		IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+		break;
+	case IEEE80211_M_HOSTAP:
+		wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+		IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
+		IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
+		break;
+	case IEEE80211_M_MONITOR:
+		goto bad;
+	}
+	if (eh.ether_type != __constant_htons(ETHERTYPE_PAE) ||
+	    (key != NULL && (ic->ic_flags & IEEE80211_F_WPA))) {
+		/*
+		 * IEEE 802.1X: send EAPOL frames always in the clear.
+		 * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
+		 */
+		if (key != NULL) {
+			wh->i_fc[1] |= IEEE80211_FC1_WEP;
+			/* XXX do fragmentation */
+			if (!ieee80211_crypto_enmic(ic, key, skb)) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				    ("[%s] enmic failed, discard frame\n",
+				    ether_sprintf(eh.ether_dhost)));
+				/* XXX statistic */
+				goto bad;
+			}
+		}
+	}
+	if (eh.ether_type != __constant_htons(ETHERTYPE_PAE)) {
+		/*
+		 * Reset the inactivity timer only for non-PAE traffic
+		 * to avoid a problem where the station leaves w/o
+		 * notice while we're requesting Identity.  In this
+		 * situation the 802.1x state machine will continue
+		 * to retransmit the requests because it assumes the
+		 * station will be timed out for inactivity, but our
+		 * retransmits will reset the inactivity timer.
+		 */ 
+		ni->ni_inact = ic->ic_inact_run;
+	}
+	*pni = ni;
+	return skb;
+bad:
+	if (skb != NULL)
+		dev_kfree_skb(skb);
+	if (ni && ni != ic->ic_bss)
+		ieee80211_free_node(ic, ni);
+	*pni = NULL;
+	return NULL;
+}
+EXPORT_SYMBOL(ieee80211_encap);
+
+/*
+ * Add a supported rates element id to a frame.
+ */
+static u_int8_t *
+ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+{
+	int nrates;
+
+	*frm++ = IEEE80211_ELEMID_RATES;
+	nrates = rs->rs_nrates;
+	if (nrates > IEEE80211_RATE_SIZE)
+		nrates = IEEE80211_RATE_SIZE;
+	*frm++ = nrates;
+	memcpy(frm, rs->rs_rates, nrates);
+	return frm + nrates;
+}
+
+/*
+ * Add an extended supported rates element id to a frame.
+ */
+static u_int8_t *
+ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+{
+	/*
+	 * Add an extended supported rates element if operating in 11g mode.
+	 */
+	if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
+		int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE;
+		*frm++ = IEEE80211_ELEMID_XRATES;
+		*frm++ = nrates;
+		memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates);
+		frm += nrates;
+	}
+	return frm;
+}
+
+/* 
+ * Add an ssid elemet to a frame.
+ */
+static u_int8_t *
+ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
+{
+	*frm++ = IEEE80211_ELEMID_SSID;
+	*frm++ = len;
+	memcpy(frm, ssid, len);
+	return frm + len;
+}
+
+/*
+ * Add an erp element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
+{
+	u_int8_t erp;
+
+	*frm++ = IEEE80211_ELEMID_ERP;
+	*frm++ = 1;
+	erp = 0;
+	if (ic->ic_nonerpsta != 0)
+		erp |= IEEE80211_ERP_NON_ERP_PRESENT;
+	if (ic->ic_flags & IEEE80211_F_USEPROT)
+		erp |= IEEE80211_ERP_USE_PROTECTION;
+	if (ic->ic_flags & IEEE80211_F_USEBARKER)
+		erp |= IEEE80211_ERP_LONG_PREAMBLE;
+	*frm++ = erp;
+	return frm;
+}
+
+static u_int8_t *
+ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie)
+{
+#define	WPA_OUI_BYTES		0x00, 0x50, 0xf2
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+#define	ADDSELECTOR(frm, sel) do {		\
+	memcpy(frm, sel, 4);			\
+	frm += 4;				\
+} while (0)
+	static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
+	static const u_int8_t cipher_suite[][4] = {
+		{ WPA_OUI_BYTES, WPA_CSE_WEP40 },	/* NB: 40-bit */
+		{ WPA_OUI_BYTES, WPA_CSE_TKIP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX WRAP */
+		{ WPA_OUI_BYTES, WPA_CSE_CCMP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
+		{ WPA_OUI_BYTES, WPA_CSE_NULL },
+	};
+	static const u_int8_t wep104_suite[4] =
+		{ WPA_OUI_BYTES, WPA_CSE_WEP104 };
+	static const u_int8_t key_mgt_unspec[4] =
+		{ WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
+	static const u_int8_t key_mgt_psk[4] =
+		{ WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	u_int8_t *frm = ie;
+	u_int8_t *selcnt;
+
+	*frm++ = IEEE80211_ELEMID_VENDOR;
+	*frm++ = 0;				/* length filled in below */
+	memcpy(frm, oui, sizeof(oui));		/* WPA OUI */
+	frm += sizeof(oui);
+	ADDSHORT(frm, WPA_VERSION);
+
+	/* XXX filter out CKIP */
+
+	/* multicast cipher */
+	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
+	    rsn->rsn_mcastkeylen >= 13)
+		ADDSELECTOR(frm, wep104_suite);
+	else
+		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
+
+	/* unicast cipher list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
+	}
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
+	}
+
+	/* authenticator selector list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_unspec);
+	}
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_psk);
+	}
+
+	/* optional capabilities */
+	if (rsn->rsn_caps != 0)
+		ADDSHORT(frm, rsn->rsn_caps);
+
+	/* calculate element length */
+	ie[1] = frm - ie - 2;
+	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
+		("WPA IE too big, %u > %u",
+		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+	return frm;
+#undef ADDSHORT
+#undef ADDSELECTOR
+#undef WPA_OUI_BYTES
+}
+
+static u_int8_t *
+ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie)
+{
+#define	RSN_OUI_BYTES		0x00, 0x0f, 0xac
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+#define	ADDSELECTOR(frm, sel) do {		\
+	memcpy(frm, sel, 4);			\
+	frm += 4;				\
+} while (0)
+	static const u_int8_t cipher_suite[][4] = {
+		{ RSN_OUI_BYTES, RSN_CSE_WEP40 },	/* NB: 40-bit */
+		{ RSN_OUI_BYTES, RSN_CSE_TKIP },
+		{ RSN_OUI_BYTES, RSN_CSE_WRAP },
+		{ RSN_OUI_BYTES, RSN_CSE_CCMP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
+		{ RSN_OUI_BYTES, RSN_CSE_NULL },
+	};
+	static const u_int8_t wep104_suite[4] =
+		{ RSN_OUI_BYTES, RSN_CSE_WEP104 };
+	static const u_int8_t key_mgt_unspec[4] =
+		{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
+	static const u_int8_t key_mgt_psk[4] =
+		{ RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	u_int8_t *frm = ie;
+	u_int8_t *selcnt;
+
+	*frm++ = IEEE80211_ELEMID_RSN;
+	*frm++ = 0;				/* length filled in below */
+	ADDSHORT(frm, RSN_VERSION);
+
+	/* XXX filter out CKIP */
+
+	/* multicast cipher */
+	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
+	    rsn->rsn_mcastkeylen >= 13)
+		ADDSELECTOR(frm, wep104_suite);
+	else
+		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
+
+	/* unicast cipher list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
+	}
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
+	}
+
+	/* authenticator selector list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_unspec);
+	}
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_psk);
+	}
+
+	/* optional capabilities */
+	if (rsn->rsn_caps != 0)
+		ADDSHORT(frm, rsn->rsn_caps);
+	/* XXX PMKID */
+
+	/* calculate element length */
+	ie[1] = frm - ie - 2;
+	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
+		("RSN IE too big, %u > %u",
+		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+	return frm;
+#undef ADDSELECTOR
+#undef ADDSHORT
+#undef RSN_OUI_BYTES
+}
+
+/*
+ * Add a WPA/RSN element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic)
+{
+
+	KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
+	if (ic->ic_flags & IEEE80211_F_WPA2)
+		frm = ieee80211_setup_rsn_ie(ic, frm);
+	if (ic->ic_flags & IEEE80211_F_WPA1)
+		frm = ieee80211_setup_wpa_ie(ic, frm);
+	return frm;
+}
+
+/*
+ * Send a management frame.  The node is for the destination (or ic_bss
+ * when in station mode).  Nodes other than ic_bss have their reference
+ * count bumped to reflect our use for an indeterminant time.
+ */
+int
+ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+	int type, int arg)
+{
+#define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+	struct sk_buff *skb;
+	u_int8_t *frm;
+	enum ieee80211_phymode mode;
+	u_int16_t capinfo;
+	int has_challenge, is_shared_key, ret, timer, status;
+
+	KASSERT(ni != NULL, ("null node"));
+
+	/*
+	 * Hold a reference on the node so it doesn't go away until after
+	 * the xmit is complete all the way in the driver.  On error we
+	 * will remove our reference.
+	 */
+	if (ni != ic->ic_bss)
+		ieee80211_ref_node(ni);
+	timer = 0;
+	switch (type) {
+	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+		/*
+		 * prreq frame format
+		 *	[tlv] ssid
+		 *	[tlv] supported rates
+		 *	[tlv] extended supported rates
+		 *	[tlv] user-specified ie's
+		 */
+		skb = ieee80211_getmgtframe(&frm,
+			 2 + ic->ic_des_esslen
+		       + 2 + IEEE80211_RATE_SIZE
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0));
+		if (skb == NULL)
+			senderr(ENOMEM, is_tx_nobuf);
+
+		frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
+		mode = ieee80211_chan2mode(ic, ni->ni_chan);
+		frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
+		frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
+		if (ic->ic_opt_ie != NULL) {
+			memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
+			frm += ic->ic_opt_ie_len;
+		}
+		skb_trim(skb, frm - skb->data);
+
+		timer = IEEE80211_TRANS_WAIT;
+		break;
+
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		/*
+		 * probe response frame format
+		 *	[8] time stamp
+		 *	[2] beacon interval
+		 *	[2] cabability information
+		 *	[tlv] ssid
+		 *	[tlv] supported rates
+		 *	[tlv] parameter set (FH/DS)
+		 *	[tlv] parameter set (IBSS)
+		 *	[tlv] extended rate phy (ERP)
+		 *	[tlv] extended supported rates
+		 *	[tlv] WPA
+		 */
+		skb = ieee80211_getmgtframe(&frm,
+			 8 + 2 + 2 + 2
+		       + 2 + ni->ni_esslen
+		       + 2 + IEEE80211_RATE_SIZE
+		       + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3)
+		       + 6
+		       + (ic->ic_curmode == IEEE80211_MODE_11G ? 3 : 0)
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + (ic->ic_flags & IEEE80211_F_WPA ?
+				2*sizeof(struct ieee80211_ie_wpa) : 0)
+		);
+		if (skb == NULL)
+			senderr(ENOMEM, is_tx_nobuf);
+
+		memset(frm, 0, 8);	/* timestamp should be filled later */
+		frm += 8;
+		*(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
+		frm += 2;
+		if (ic->ic_opmode == IEEE80211_M_IBSS)
+			capinfo = IEEE80211_CAPINFO_IBSS;
+		else
+			capinfo = IEEE80211_CAPINFO_ESS;
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
+			capinfo |= IEEE80211_CAPINFO_PRIVACY;
+		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+		*(u_int16_t *)frm = htole16(capinfo);
+		frm += 2;
+
+		frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
+				ic->ic_bss->ni_esslen);
+		frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
+
+		if (ic->ic_phytype == IEEE80211_T_FH) {
+                        *frm++ = IEEE80211_ELEMID_FHPARMS;
+                        *frm++ = 5;
+                        *frm++ = ni->ni_fhdwell & 0x00ff;
+                        *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
+                        *frm++ = IEEE80211_FH_CHANSET(
+			    ieee80211_chan2ieee(ic, ni->ni_chan));
+                        *frm++ = IEEE80211_FH_CHANPAT(
+			    ieee80211_chan2ieee(ic, ni->ni_chan));
+                        *frm++ = ni->ni_fhindex;
+		} else {
+			*frm++ = IEEE80211_ELEMID_DSPARMS;
+			*frm++ = 1;
+			*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+		}
+
+		if (ic->ic_opmode == IEEE80211_M_IBSS) {
+			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
+			*frm++ = 2;
+			*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
+		}
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			frm = ieee80211_add_wpa(frm, ic);
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			frm = ieee80211_add_erp(frm, ic);
+		frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
+		skb_trim(skb, frm - skb->data);
+		break;
+
+	case IEEE80211_FC0_SUBTYPE_AUTH:
+		status = arg >> 16;
+		arg &= 0xffff;
+		has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
+		    arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
+		    ni->ni_challenge != NULL);
+
+		/*
+		 * Deduce whether we're doing open authentication or
+		 * shared key authentication.  We do the latter if
+		 * we're in the middle of a shared key authentication
+		 * handshake or if we're initiating an authentication
+		 * request and configured to use shared key.
+		 */
+		is_shared_key = has_challenge ||
+		     arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
+		     (arg == IEEE80211_AUTH_SHARED_REQUEST &&
+		      ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
+
+		skb = ieee80211_getmgtframe(&frm,
+			  3 * sizeof(u_int16_t)
+			+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
+				sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0));
+		if (skb == NULL)
+			senderr(ENOMEM, is_tx_nobuf);
+
+		((u_int16_t *)frm)[0] =
+		    (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED)
+		                    : htole16(IEEE80211_AUTH_ALG_OPEN);
+		((u_int16_t *)frm)[1] = htole16(arg);	/* sequence number */
+		((u_int16_t *)frm)[2] = htole16(status);/* status */
+
+		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
+			((u_int16_t *)frm)[3] =
+			    htole16((IEEE80211_CHALLENGE_LEN << 8) |
+			    IEEE80211_ELEMID_CHALLENGE);
+			memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
+			    IEEE80211_CHALLENGE_LEN);
+			if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
+				struct ieee80211_cb *cb =
+					(struct ieee80211_cb *)skb->cb;
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				    ("%s: request encrypt frame\n", __func__));
+				cb->flags |= M_LINK0; /* WEP-encrypt, please */
+			}
+		}
+		/*
+		 * When 802.1x is not in use mark the port
+		 * authorized at this point so traffic can flow.
+		 * XXX shared key auth
+		 */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		    status == IEEE80211_STATUS_SUCCESS &&
+		    ni->ni_authmode != IEEE80211_AUTH_8021X)
+			ieee80211_node_authorize(ic, ni);
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			timer = IEEE80211_TRANS_WAIT;
+		break;
+
+	case IEEE80211_FC0_SUBTYPE_DEAUTH:
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			("send station %s deauthenticate (reason %d)\n",
+			ether_sprintf(ni->ni_macaddr), arg));
+		skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
+		if (skb == NULL)
+			senderr(ENOMEM, is_tx_nobuf);
+		*(u_int16_t *)frm = htole16(arg);	/* reason */
+		ieee80211_node_unauthorize(ic, ni);	/* port closed */
+		break;
+
+	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+		/*
+		 * asreq frame format
+		 *	[2] capability information
+		 *	[2] listen interval
+		 *	[6*] current AP address (reassoc only)
+		 *	[tlv] ssid
+		 *	[tlv] supported rates
+		 *	[tlv] extended supported rates
+		 *	[tlv] user-specified ie's
+		 */
+		skb = ieee80211_getmgtframe(&frm,
+			 sizeof(capinfo)
+		       + sizeof(u_int16_t)
+		       + IEEE80211_ADDR_LEN
+		       + 2 + ni->ni_esslen
+		       + 2 + IEEE80211_RATE_SIZE
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0));
+		if (skb == NULL)
+			senderr(ENOMEM, is_tx_nobuf);
+
+		capinfo = 0;
+		if (ic->ic_opmode == IEEE80211_M_IBSS)
+			capinfo |= IEEE80211_CAPINFO_IBSS;
+		else		/* IEEE80211_M_STA */
+			capinfo |= IEEE80211_CAPINFO_ESS;
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
+			capinfo |= IEEE80211_CAPINFO_PRIVACY;
+		/*
+		 * NB: Some 11a AP's reject the request when
+		 *     short premable is set.
+		 */
+		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
+		    (ic->ic_caps & IEEE80211_C_SHSLOT))
+			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+		*(u_int16_t *)frm = htole16(capinfo);
+		frm += 2;
+
+		*(u_int16_t *)frm = htole16(ic->ic_lintval);
+		frm += 2;
+
+		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+			IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
+			frm += IEEE80211_ADDR_LEN;
+		}
+
+		frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
+		frm = ieee80211_add_rates(frm, &ni->ni_rates);
+		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+		if (ic->ic_opt_ie != NULL) {
+			memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
+			frm += ic->ic_opt_ie_len;
+		}
+		skb_trim(skb, frm - skb->data);
+
+		timer = IEEE80211_TRANS_WAIT;
+		break;
+
+	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+		/*
+		 * asreq frame format
+		 *	[2] capability information
+		 *	[2] status
+		 *	[2] association ID
+		 *	[tlv] supported rates
+		 *	[tlv] extended supported rates
+		 */
+		skb = ieee80211_getmgtframe(&frm,
+			 sizeof(capinfo)
+		       + sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
+		       + 2 + IEEE80211_RATE_SIZE
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		if (skb == NULL)
+			senderr(ENOMEM, is_tx_nobuf);
+
+		capinfo = IEEE80211_CAPINFO_ESS;
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
+			capinfo |= IEEE80211_CAPINFO_PRIVACY;
+		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+		*(u_int16_t *)frm = htole16(capinfo);
+		frm += 2;
+
+		*(u_int16_t *)frm = htole16(arg);	/* status */
+		frm += 2;
+
+		if (arg == IEEE80211_STATUS_SUCCESS)
+			*(u_int16_t *)frm = htole16(ni->ni_associd);
+		frm += 2;
+
+		frm = ieee80211_add_rates(frm, &ni->ni_rates);
+		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+		skb_trim(skb, frm - skb->data);
+		break;
+
+	case IEEE80211_FC0_SUBTYPE_DISASSOC:
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			("send station %s disassociate (reason %d)\n",
+			ether_sprintf(ni->ni_macaddr), arg));
+		skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
+		if (skb == NULL)
+			senderr(ENOMEM, is_tx_nobuf);
+		*(u_int16_t *)frm = htole16(arg);	/* reason */
+		break;
+
+	default:
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: invalid mgmt frame type %u\n", __func__, type));
+		senderr(EINVAL, is_tx_unknownmgt);
+		/* NOTREACHED */
+	}
+
+	ieee80211_mgmt_output(ic, ni, skb, type);
+	if (timer)
+		ic->ic_mgt_timer = timer;
+	return 0;
+bad:
+	if (ni != ic->ic_bss)		/* remove ref we added */
+		ieee80211_free_node(ic, ni);
+	return ret;
+#undef senderr
+}
+
+/*
+ * Allocate a beacon frame and fillin the appropriate bits.
+ */
+struct sk_buff *
+ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct ieee80211_beacon_offsets *bo)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ieee80211_frame *wh;
+	struct sk_buff *skb;
+	int pktlen;
+	u_int8_t *frm, *efrm;
+	u_int16_t capinfo;
+	struct ieee80211_rateset *rs;
+
+	/*
+	 * beacon frame format
+	 *	[8] time stamp
+	 *	[2] beacon interval
+	 *	[2] cabability information
+	 *	[tlv] ssid
+	 *	[tlv] supported rates
+	 *	[3] parameter set (DS)
+	 *	[tlv] parameter set (IBSS/TIM)
+	 *	[tlv] extended rate phy (ERP)
+	 *	[tlv] extended supported rates
+	 *	[tlv] WPA/RSN parameters
+	 * XXX WME, etc.
+	 * XXX Vendor-specific OIDs (e.g. Atheros)
+	 */
+	rs = &ni->ni_rates;
+	/* XXX may be better to just allocate a max-sized buffer */
+	pktlen =   8					/* time stamp */
+		 + sizeof(u_int16_t)			/* beacon interval */
+		 + sizeof(u_int16_t)			/* capabilities */
+		 + 2 + ni->ni_esslen			/* ssid */
+	         + 2 + IEEE80211_RATE_SIZE		/* supported rates */
+	         + 2 + 1				/* DS parameters */
+		 + 2 + 4				/* DTIM/IBSSPARMS */
+		 + 2 + 1				/* ERP */
+	         + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		 + 2*sizeof(struct ieee80211_ie_wpa)	/* WPA 1+2 */
+		 ;
+	skb = ieee80211_getmgtframe(&frm, pktlen);
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("%s: cannot get buf; size %u\n", __func__, pktlen));
+		ic->ic_stats.is_tx_nobuf++;
+		return NULL;
+	}
+
+	memset(frm, 0, 8);	/* XXX timestamp is set by hardware */
+	frm += 8;
+	*(u_int16_t *)frm = cpu_to_le16(ni->ni_intval);
+	frm += 2;
+	if (ic->ic_opmode == IEEE80211_M_IBSS)
+		capinfo = IEEE80211_CAPINFO_IBSS;
+	else
+		capinfo = IEEE80211_CAPINFO_ESS;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+	bo->bo_caps = (u_int16_t *)frm;
+	*(u_int16_t *)frm = cpu_to_le16(capinfo);
+	frm += 2;
+	*frm++ = IEEE80211_ELEMID_SSID;
+	if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
+		*frm++ = ni->ni_esslen;
+		memcpy(frm, ni->ni_essid, ni->ni_esslen);
+		frm += ni->ni_esslen;
+	} else
+		*frm++ = 0;
+	frm = ieee80211_add_rates(frm, rs);
+	if (ic->ic_curmode != IEEE80211_MODE_FH) {
+		*frm++ = IEEE80211_ELEMID_DSPARMS;
+		*frm++ = 1;
+		*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+	}
+	bo->bo_tim = frm;
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
+		*frm++ = 2;
+		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
+		bo->bo_tim_len = 4;
+	} else {
+		/* TODO: TIM */
+		*frm++ = IEEE80211_ELEMID_TIM;
+		*frm++ = 4;	/* length */
+		*frm++ = 0;	/* DTIM count */ 
+		*frm++ = 1;	/* DTIM period */
+		*frm++ = 0;	/* bitmap control */
+		*frm++ = 0;	/* Partial Virtual Bitmap (variable length) */
+		bo->bo_tim_len = 6;
+	}
+	bo->bo_trailer = frm;
+	if (ic->ic_flags & IEEE80211_F_WPA)
+		frm = ieee80211_add_wpa(frm, ic);
+	if (ic->ic_curmode == IEEE80211_MODE_11G)
+		frm = ieee80211_add_erp(frm, ic);
+	efrm = ieee80211_add_xrates(frm, rs);
+	bo->bo_trailer_len = efrm - bo->bo_trailer;
+	skb_trim(skb, efrm - skb->data);
+
+	wh = (struct ieee80211_frame *)
+		skb_push(skb, sizeof(struct ieee80211_frame));
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
+	    IEEE80211_FC0_SUBTYPE_BEACON;
+	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+	*(u_int16_t *)wh->i_dur = 0;
+	IEEE80211_ADDR_COPY(wh->i_addr1, dev->broadcast);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+	*(u_int16_t *)wh->i_seq = 0;
+
+	return skb;
+}
+EXPORT_SYMBOL(ieee80211_beacon_alloc);
+
+/*
+ * Update the dynamic parts of a beacon frame based on the current state.
+ */
+int
+ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct ieee80211_beacon_offsets *bo, struct sk_buff **skb0)
+{
+	u_int16_t capinfo;
+
+	/* XXX lock out changes */
+	/* XXX only update as needed */
+	/* XXX faster to recalculate entirely or just changes? */
+	if (ic->ic_opmode == IEEE80211_M_IBSS)
+		capinfo = IEEE80211_CAPINFO_IBSS;
+	else
+		capinfo = IEEE80211_CAPINFO_ESS;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+	*bo->bo_caps = htole16(capinfo);
+
+	if (ic->ic_flags & IEEE80211_F_TIMUPDATE) {
+		/* 
+		 * ATIM/DTIM needs updating.  If it fits in the
+		 * current space allocated then just copy in the
+		 * new bits.  Otherwise we need to move any extended
+		 * rate set the follows and, possibly, allocate a
+		 * new buffer if the this current one isn't large
+		 * enough.  XXX It may be better to just allocate
+		 * a max-sized buffer so we don't re-allocate.
+		 */
+		/* XXX fillin */
+		ic->ic_flags &= ~IEEE80211_F_TIMUPDATE;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_beacon_update);
+
+/*
+ * Save an outbound packet for a node in power-save sleep state.
+ * The new packet is placed on the node's saved queue, and the TIM
+ * is changed, if necessary.
+ */
+void
+ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, 
+		  struct sk_buff *skb)
+{
+	if (_IF_QLEN(&ni->ni_savedq) == 0)
+		(*ic->ic_set_tim)(ic, ni->ni_associd, 1);
+	if (_IF_QLEN(&ni->ni_savedq) >= IEEE80211_PS_MAX_QUEUE) {
+		ni->ni_pwrsavedrops++;	/* XXX atomic_inc */
+		dev_kfree_skb(skb);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			("station %s pwr save q overflow of size %d drops %d\n",
+			ether_sprintf(ni->ni_macaddr), 
+		        IEEE80211_PS_MAX_QUEUE, ni->ni_pwrsavedrops));
+	} else {
+		struct ieee80211_cb *cb = (struct ieee80211_cb *)skb->cb;
+		/*
+		 * Similar to ieee80211_mgmt_output, save the node in
+		 * the packet for use by the driver start routine.
+		 */
+		/* XXX do we know we have a reference? */
+		cb->ni = ni;
+		IF_ENQUEUE(&ni->ni_savedq, skb);
+	}
+}
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.c	2005-02-24 13:06:17.341125768 -0800
@@ -0,0 +1,714 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.6 2003/10/31 18:32:09 brooks Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_proto.c,v 1.5 2003/10/13 04:23:56 dyoung Exp $");
+
+/*
+ * IEEE 802.11 protocol support.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_dot1x.h>
+
+#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
+
+const char *ieee80211_mgt_subtype_name[] = {
+	"assoc_req",	"assoc_resp",	"reassoc_req",	"reassoc_resp",
+	"probe_req",	"probe_resp",	"reserved#6",	"reserved#7",
+	"beacon",	"atim",		"disassoc",	"auth",
+	"deauth",	"reserved#13",	"reserved#14",	"reserved#15"
+};
+EXPORT_SYMBOL(ieee80211_mgt_subtype_name);
+const char *ieee80211_ctl_subtype_name[] = {
+	"reserved#0",	"reserved#1",	"reserved#2",	"reserved#3",
+	"reserved#3",	"reserved#5",	"reserved#6",	"reserved#7",
+	"reserved#8",	"reserved#9",	"ps_poll",	"rts",
+	"cts",		"ack",		"cf_end",	"cf_end_ack"
+};
+EXPORT_SYMBOL(ieee80211_ctl_subtype_name);
+const char *ieee80211_state_name[IEEE80211_S_MAX] = {
+	"INIT",		/* IEEE80211_S_INIT */
+	"SCAN",		/* IEEE80211_S_SCAN */
+	"AUTH",		/* IEEE80211_S_AUTH */
+	"ASSOC",	/* IEEE80211_S_ASSOC */
+	"RUN"		/* IEEE80211_S_RUN */
+};
+EXPORT_SYMBOL(ieee80211_state_name);
+
+#define	IEEE80211_AUTH_MAX	(IEEE80211_AUTH_WPA+1)
+/* XXX well-known names */
+static const char *auth_modnames[IEEE80211_AUTH_MAX] = {
+	"wlan_internal",	/* IEEE80211_AUTH_NONE */
+	"wlan_internal",	/* IEEE80211_AUTH_OPEN */
+	"wlan_internal",	/* IEEE80211_AUTH_SHARED */
+	"wlan_xauth",		/* IEEE80211_AUTH_8021X	 */
+	"wlan_internal",	/* IEEE80211_AUTH_AUTO */
+	"wlan_xauth",		/* IEEE80211_AUTH_WPA */
+};
+static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX];
+
+static const struct ieee80211_authenticator auth_internal = {
+	.ia_name		= "wlan_internal",
+	.ia_attach		= NULL,
+	.ia_detach		= NULL,
+	.ia_node_join		= NULL,
+	.ia_node_leave		= NULL,
+};
+
+static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
+
+void
+ieee80211_proto_attach(struct ieee80211com *ic)
+{
+
+#ifdef notdef
+	ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
+#else
+	ic->ic_rtsthreshold = IEEE80211_RTS_MAX;
+#endif
+	ic->ic_fragthreshold = 2346;		/* XXX not used yet */
+	ic->ic_fixed_rate = -1;			/* no fixed rate */
+	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
+	ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+	/* protocol state change handler */
+	ic->ic_newstate = ieee80211_newstate;
+
+	/* initialize management frame handlers */
+	ic->ic_recv_mgmt = ieee80211_recv_mgmt;
+	ic->ic_send_mgmt = ieee80211_send_mgmt;
+
+	ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal);
+	ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal);
+	ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal);
+}
+
+void
+ieee80211_proto_detach(struct ieee80211com *ic)
+{
+	/*
+	 * This should not be needed as we detach when reseting
+	 * the state but be conservative here since the
+	 * authenticator may do things like spawn kernel threads.
+	 */
+	if (ic->ic_auth->ia_detach)
+		ic->ic_auth->ia_detach(ic);
+
+	ieee80211_authenticator_unregister(IEEE80211_AUTH_OPEN);
+	ieee80211_authenticator_unregister(IEEE80211_AUTH_SHARED);
+	ieee80211_authenticator_unregister(IEEE80211_AUTH_AUTO);
+
+	/*
+	 * Detach any ACL'ator.
+	 */
+	if (ic->ic_acl != NULL)
+		ic->ic_acl->iac_detach(ic);
+}
+
+/*
+ * Simple-minded authenticator module support.
+ */
+
+const struct ieee80211_authenticator *
+ieee80211_authenticator_get(int auth)
+{
+	if (auth >= IEEE80211_AUTH_MAX)
+		return NULL;
+	if (authenticators[auth] == NULL)
+		request_module(auth_modnames[auth]);
+	return authenticators[auth];
+}
+
+void
+ieee80211_authenticator_register(int type,
+	const struct ieee80211_authenticator *auth)
+{
+	if (type >= IEEE80211_AUTH_MAX)
+		return;
+	authenticators[type] = auth;
+}
+EXPORT_SYMBOL(ieee80211_authenticator_register);
+
+void
+ieee80211_authenticator_unregister(int type)
+{
+
+	if (type >= IEEE80211_AUTH_MAX)
+		return;
+	authenticators[type] = NULL;
+}
+EXPORT_SYMBOL(ieee80211_authenticator_unregister);
+
+/*
+ * Very simple-minded authenticator backend module support.
+ */
+/* XXX just one for now */
+static	const struct ieee80211_authenticator_backend *backend = NULL;
+
+void
+ieee80211_authenticator_backend_register(
+	const struct ieee80211_authenticator_backend *be)
+{
+	printk(KERN_INFO "wlan: %s backend registered\n", be->iab_name);
+	backend = be;
+}
+EXPORT_SYMBOL(ieee80211_authenticator_backend_register);
+
+void
+ieee80211_authenticator_backend_unregister(
+	const struct ieee80211_authenticator_backend * be)
+{
+	if (backend == be)
+		backend = NULL;
+	printk(KERN_INFO "wlan: %s backend unregistered\n",
+		be->iab_name);
+}
+EXPORT_SYMBOL(ieee80211_authenticator_backend_unregister);
+
+const struct ieee80211_authenticator_backend *
+ieee80211_authenticator_backend_get(const char *name)
+{
+	if (backend == NULL)
+		request_module("wlan_radius");
+	return backend && strcmp(backend->iab_name, name) == 0 ? backend : NULL;
+}
+EXPORT_SYMBOL(ieee80211_authenticator_backend_get);
+
+/*
+ * Very simple-minded ACL module support.
+ */
+/* XXX just one for now */
+static	const struct ieee80211_aclator *acl = NULL;
+
+void
+ieee80211_aclator_register(const struct ieee80211_aclator *iac)
+{
+	printk(KERN_INFO "wlan: %s acl policy registered\n", iac->iac_name);
+	acl = iac;
+}
+EXPORT_SYMBOL(ieee80211_aclator_register);
+
+void
+ieee80211_aclator_unregister(const struct ieee80211_aclator *iac)
+{
+	if (acl == iac)
+		acl = NULL;
+	printk(KERN_INFO "wlan: %s acl policy unregistered\n", iac->iac_name);
+}
+EXPORT_SYMBOL(ieee80211_aclator_unregister);
+
+const struct ieee80211_aclator *
+ieee80211_aclator_get(const char *name)
+{
+	if (acl == NULL)
+		request_module("wlan_acl");
+	return acl && strcmp(acl->iac_name, name) == 0 ? acl : NULL;
+}
+EXPORT_SYMBOL(ieee80211_aclator_get);
+
+void
+ieee80211_print_essid(const u_int8_t *essid, int len)
+{
+	int i;
+	const u_int8_t *p; 
+
+	if (len > IEEE80211_NWID_LEN)
+		len = IEEE80211_NWID_LEN;
+	/* determine printable or not */
+	for (i = 0, p = essid; i < len; i++, p++) {
+		if (*p < ' ' || *p > 0x7e)
+			break;
+	}
+	if (i == len) {
+		printf("\"");
+		for (i = 0, p = essid; i < len; i++, p++)
+			printf("%c", *p);
+		printf("\"");
+	} else {
+		printf("0x");
+		for (i = 0, p = essid; i < len; i++, p++)
+			printf("%02x", *p);
+	}
+}
+
+void
+ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi)
+{
+	struct ieee80211_frame *wh;
+	int i;
+
+	wh = (struct ieee80211_frame *)buf;
+	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+	case IEEE80211_FC1_DIR_NODS:
+		printf("NODS %s", ether_sprintf(wh->i_addr2));
+		printf("->%s", ether_sprintf(wh->i_addr1));
+		printf("(%s)", ether_sprintf(wh->i_addr3));
+		break;
+	case IEEE80211_FC1_DIR_TODS:
+		printf("TODS %s", ether_sprintf(wh->i_addr2));
+		printf("->%s", ether_sprintf(wh->i_addr3));
+		printf("(%s)", ether_sprintf(wh->i_addr1));
+		break;
+	case IEEE80211_FC1_DIR_FROMDS:
+		printf("FRDS %s", ether_sprintf(wh->i_addr3));
+		printf("->%s", ether_sprintf(wh->i_addr1));
+		printf("(%s)", ether_sprintf(wh->i_addr2));
+		break;
+	case IEEE80211_FC1_DIR_DSTODS:
+		printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
+		printf("->%s", ether_sprintf(wh->i_addr3));
+		printf("(%s", ether_sprintf(wh->i_addr2));
+		printf("->%s)", ether_sprintf(wh->i_addr1));
+		break;
+	}
+	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+	case IEEE80211_FC0_TYPE_DATA:
+		printf(" data");
+		break;
+	case IEEE80211_FC0_TYPE_MGT:
+		printf(" %s", ieee80211_mgt_subtype_name[
+		    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
+		    >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+		break;
+	default:
+		printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
+		break;
+	}
+	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		int i;
+		printf(" WEP [IV");
+		for (i = 0; i < IEEE80211_WEP_IVLEN; i++)
+			printf(" %.02x", buf[sizeof(*wh)+i]);
+		printf(" KID %u]", buf[sizeof(*wh)+i] >> 6);
+	}
+	if (rate >= 0)
+		printf(" %dM", rate / 2);
+	if (rssi >= 0)
+		printf(" +%d", rssi);
+	printf("\n");
+	if (len > 0) {
+		for (i = 0; i < len; i++) {
+			if ((i & 1) == 0)
+				printf(" ");
+			printf("%02x", buf[i]);
+		}
+		printf("\n");
+	}
+}
+EXPORT_SYMBOL(ieee80211_dump_pkt);
+
+int
+ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags)
+{
+#define	RV(v)	((v) & IEEE80211_RATE_VAL)
+	int i, j, ignore, error;
+	int okrate, badrate, fixedrate;
+	struct ieee80211_rateset *srs, *nrs;
+	u_int8_t r;
+
+	/*
+	 * If the fixed rate check was requested but no
+	 * fixed has been defined then just remove it.
+	 */
+	if ((flags & IEEE80211_F_DOFRATE) && ic->ic_fixed_rate < 0)
+		flags &= ~IEEE80211_F_DOFRATE;
+	error = 0;
+	okrate = badrate = fixedrate = 0;
+	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
+	nrs = &ni->ni_rates;
+	for (i = 0; i < nrs->rs_nrates; ) {
+		ignore = 0;
+		if (flags & IEEE80211_F_DOSORT) {
+			/*
+			 * Sort rates.
+			 */
+			for (j = i + 1; j < nrs->rs_nrates; j++) {
+				if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) {
+					r = nrs->rs_rates[i];
+					nrs->rs_rates[i] = nrs->rs_rates[j];
+					nrs->rs_rates[j] = r;
+				}
+			}
+		}
+		r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
+		badrate = r;
+		if (flags & IEEE80211_F_DOFRATE) {
+			/*
+			 * Check any fixed rate is included. 
+			 */
+			if (r == RV(srs->rs_rates[ic->ic_fixed_rate]))
+				fixedrate = r;
+		}
+		if (flags & IEEE80211_F_DONEGO) {
+			/*
+			 * Check against supported rates.
+			 */
+			for (j = 0; j < srs->rs_nrates; j++) {
+				if (r == RV(srs->rs_rates[j])) {
+					/*
+					 * Overwrite with the supported rate
+					 * value so any basic rate bit is set.
+					 * This insures that response we send
+					 * to stations have the necessary basic
+					 * rate bit set.
+					 */
+					nrs->rs_rates[i] = srs->rs_rates[j];
+					break;
+				}
+			}
+			if (j == srs->rs_nrates) {
+				/*
+				 * A rate in the node's rate set is not
+				 * supported.  If this is a basic rate and we
+				 * are operating as an AP then this is an error.
+				 * Otherwise we just discard/ignore the rate.
+				 * Note that this is important for 11b stations
+				 * when they want to associate with an 11g AP.
+				 */
+				if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+				    (nrs->rs_rates[i] & IEEE80211_RATE_BASIC))
+					error++;
+				ignore++;
+			}
+		}
+		if (flags & IEEE80211_F_DODEL) {
+			/*
+			 * Delete unacceptable rates.
+			 */
+			if (ignore) {
+				nrs->rs_nrates--;
+				for (j = i; j < nrs->rs_nrates; j++)
+					nrs->rs_rates[j] = nrs->rs_rates[j + 1];
+				nrs->rs_rates[j] = 0;
+				continue;
+			}
+		}
+		if (!ignore)
+			okrate = nrs->rs_rates[i];
+		i++;
+	}
+	if (okrate == 0 || error != 0 ||
+	    ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
+		return badrate | IEEE80211_RATE_BASIC;
+	else
+		return RV(okrate);
+#undef RV
+}
+
+/*
+ * Check if the specified rate set supports ERP.
+ * NB: the rate set is assumed to be sorted.
+ */
+int
+ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
+{
+#define N(a)	(sizeof(a) / sizeof(a[0]))
+	static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
+	int i, j;
+
+	if (rs->rs_nrates < N(rates))
+		return 0;
+	for (i = 0; i < N(rates); i++) {
+		for (j = 0; j < rs->rs_nrates; j++) {
+			int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
+			if (rates[i] == r)
+				goto next;
+			if (r > rates[i])
+				return 0;
+		}
+		return 0;
+	next:
+		;
+	}
+	return 1;
+#undef N
+}
+
+static int
+ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ieee80211_node *ni;
+	enum ieee80211_state ostate;
+
+	ostate = ic->ic_state;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, ("%s: %s -> %s\n", __func__,
+		ieee80211_state_name[ostate], ieee80211_state_name[nstate]));
+	ic->ic_state = nstate;			/* state transition */
+	ni = ic->ic_bss;			/* NB: no reference held */
+	switch (nstate) {
+	case IEEE80211_S_INIT:
+		switch (ostate) {
+		case IEEE80211_S_INIT:
+			break;
+		case IEEE80211_S_RUN:
+			switch (ic->ic_opmode) {
+			case IEEE80211_M_STA:
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DISASSOC,
+				    IEEE80211_REASON_ASSOC_LEAVE);
+				ieee80211_sta_leave(ic, ni);
+				break;
+			case IEEE80211_M_HOSTAP:
+				IEEE80211_NODE_LOCK(ic);
+				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+					if (ni->ni_associd == 0)
+						continue;
+					IEEE80211_SEND_MGMT(ic, ni,
+					    IEEE80211_FC0_SUBTYPE_DISASSOC,
+					    IEEE80211_REASON_ASSOC_LEAVE);
+				}
+				IEEE80211_NODE_UNLOCK(ic);
+				break;
+			default:
+				break;
+			}
+			goto reset;
+		case IEEE80211_S_ASSOC:
+			switch (ic->ic_opmode) {
+			case IEEE80211_M_STA:
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH,
+				    IEEE80211_REASON_AUTH_LEAVE);
+				break;
+			case IEEE80211_M_HOSTAP:
+				IEEE80211_NODE_LOCK(ic);
+				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+					IEEE80211_SEND_MGMT(ic, ni,
+					    IEEE80211_FC0_SUBTYPE_DEAUTH,
+					    IEEE80211_REASON_AUTH_LEAVE);
+				}
+				IEEE80211_NODE_UNLOCK(ic);
+				break;
+			default:
+				break;
+			}
+			goto reset;
+		case IEEE80211_S_AUTH:
+		case IEEE80211_S_SCAN:
+		reset:
+			ic->ic_mgt_timer = 0;
+			ic->ic_inact_timer = 0;
+			ieee80211_free_allnodes(ic);
+			break;
+		}
+		if (ic->ic_auth->ia_detach != NULL)
+			ic->ic_auth->ia_detach(ic);
+		break;
+	case IEEE80211_S_SCAN:
+		ic->ic_flags &= ~IEEE80211_F_SIBSS;
+		/* initialize bss for probe request */
+		IEEE80211_ADDR_COPY(ni->ni_macaddr, dev->broadcast);
+		IEEE80211_ADDR_COPY(ni->ni_bssid, dev->broadcast);
+		ni->ni_rates = ic->ic_sup_rates[
+			ieee80211_chan2mode(ic, ni->ni_chan)];
+		ni->ni_associd = 0;
+		ni->ni_rstamp = 0;
+		switch (ostate) {
+		case IEEE80211_S_INIT:
+			if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+			     ic->ic_opmode == IEEE80211_M_IBSS ||
+			     ic->ic_opmode == IEEE80211_M_AHDEMO) &&
+			    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+				/*
+				 * AP operation and we already have a channel;
+				 * bypass the scan and startup immediately.
+				 */
+				ieee80211_create_ibss(ic, ic->ic_des_chan);
+			} else {
+				ieee80211_begin_scan(ic, arg);
+			}
+			break;
+		case IEEE80211_S_SCAN:
+			/*
+			 * Scan next. If doing an active scan and the
+			 * channel is not marked passive-only then send
+			 * a probe request.  Otherwise just listen for
+			 * beacons on the channel.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
+			    (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+			}
+			break;
+		case IEEE80211_S_RUN:
+			/* beacon miss */
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE,
+				("no recent beacons from %s; rescanning\n",
+				ether_sprintf(ic->ic_bss->ni_bssid)));
+			ieee80211_sta_leave(ic, ni);
+			/* XXX this clears the scan set */
+			ieee80211_free_allnodes(ic);
+			/* FALLTHRU */
+		case IEEE80211_S_AUTH:
+		case IEEE80211_S_ASSOC:
+			/* XXX doesn't work, ni_macaddr rewritten above */
+			/* timeout restart scan */
+			ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
+			if (ni != NULL) {
+				ni->ni_fails++;
+				ieee80211_unref_node(&ni);
+			}
+			ieee80211_begin_scan(ic, arg);
+			break;
+		}
+		break;
+	case IEEE80211_S_AUTH:
+		switch (ostate) {
+		case IEEE80211_S_INIT:
+		case IEEE80211_S_SCAN:
+			IEEE80211_SEND_MGMT(ic, ni,
+			    IEEE80211_FC0_SUBTYPE_AUTH, 1);
+			break;
+		case IEEE80211_S_AUTH:
+		case IEEE80211_S_ASSOC:
+			switch (arg) {
+			case IEEE80211_FC0_SUBTYPE_AUTH:
+				/* ??? */
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
+				break;
+			case IEEE80211_FC0_SUBTYPE_DEAUTH:
+				/* ignore and retry scan on timeout */
+				break;
+			}
+			break;
+		case IEEE80211_S_RUN:
+			switch (arg) {
+			case IEEE80211_FC0_SUBTYPE_AUTH:
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
+				ic->ic_state = ostate;	/* stay RUN */
+				break;
+			case IEEE80211_FC0_SUBTYPE_DEAUTH:
+				/* try to reauth */
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_AUTH, 1);
+				ieee80211_sta_leave(ic, ni);
+				break;
+			}
+			break;
+		}
+		break;
+	case IEEE80211_S_ASSOC:
+		switch (ostate) {
+		case IEEE80211_S_INIT:
+		case IEEE80211_S_SCAN:
+		case IEEE80211_S_ASSOC:
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: invalid transition\n", __func__));
+			break;
+		case IEEE80211_S_AUTH:
+			IEEE80211_SEND_MGMT(ic, ni,
+			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+			break;
+		case IEEE80211_S_RUN:
+			IEEE80211_SEND_MGMT(ic, ni,
+			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+			ieee80211_sta_leave(ic, ni);
+			break;
+		}
+		break;
+	case IEEE80211_S_RUN:
+		if (ic->ic_flags & IEEE80211_F_WPA) {
+			/* XXX validate prerequisites */
+		}
+		switch (ostate) {
+		case IEEE80211_S_INIT:
+			if (ic->ic_opmode == IEEE80211_M_MONITOR)
+				break;
+			/* fall thru... */
+		case IEEE80211_S_AUTH:
+		case IEEE80211_S_RUN:
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				("%s: invalid transition\n", __func__));
+			break;
+		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
+		case IEEE80211_S_ASSOC:		/* infra mode */
+			KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
+				("%s: bogus xmit rate %u setup\n", __func__,
+					ni->ni_txrate));
+			if (ieee80211_msg_debug(ic)) {
+				if_printf(ic->ic_dev, " ");
+				if (ic->ic_opmode == IEEE80211_M_STA)
+					printf("associated ");
+				else
+					printf("synchronized ");
+				printf("with %s ssid ",
+				    ether_sprintf(ni->ni_bssid));
+				ieee80211_print_essid(ic->ic_bss->ni_essid,
+				    ni->ni_esslen);
+				printf(" channel %d start %uMb\n",
+					ieee80211_chan2ieee(ic, ni->ni_chan),
+					IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
+			}
+			ic->ic_mgt_timer = 0;
+			if (ic->ic_opmode == IEEE80211_M_STA)
+				ieee80211_notify_node_join(ic, ni, 
+					arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+			break;
+		}
+		/*
+		 * Start/stop the authenticator when operating as an
+		 * AP.  We delay until here to allow configuration to
+		 * happen out of order.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */
+		    ic->ic_auth->ia_attach != NULL) {
+			/* XXX check failure */
+			ic->ic_auth->ia_attach(ic);
+		} else if (ic->ic_auth->ia_detach != NULL) {
+			ic->ic_auth->ia_detach(ic);
+		}
+		/*
+		 * When 802.1x is not in use mark the port authorized
+		 * at this point so traffic can flow.
+		 */
+		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+			ieee80211_node_authorize(ic, ni);
+		break;
+	}
+	return 0;
+}
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.h	2005-02-24 13:06:17.342125616 -0800
@@ -0,0 +1,229 @@
+/*	$NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $	*/
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.4 2003/08/19 22:17:03 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_PROTO_H_
+#define _NET80211_IEEE80211_PROTO_H_
+
+/*
+ * 802.11 protocol implementation definitions.
+ */
+
+enum ieee80211_state {
+	IEEE80211_S_INIT	= 0,	/* default state */
+	IEEE80211_S_SCAN	= 1,	/* scanning */
+	IEEE80211_S_AUTH	= 2,	/* try to authenticate */
+	IEEE80211_S_ASSOC	= 3,	/* try to assoc */
+	IEEE80211_S_RUN		= 4,	/* associated */
+};
+#define	IEEE80211_S_MAX		(IEEE80211_S_RUN+1)
+
+#define	IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
+	((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
+
+/*
+ * Transmitted frames have the following information
+ * held in the sk_buff control buffer.  This is used to
+ * communicate various inter-procedural state that needs
+ * to be associated with the frame for the duration of
+ * it's existence.
+ */
+struct ieee80211_cb {
+	struct ieee80211_node	*ni;
+	struct ieee80211_key	*key;
+	u_int8_t		flags;
+};
+
+extern	const char *ieee80211_mgt_subtype_name[];
+extern	const char *ieee80211_ctl_subtype_name[];
+
+extern	void ieee80211_proto_attach(struct ieee80211com *);
+extern	void ieee80211_proto_detach(struct ieee80211com *);
+
+struct ieee80211_node;
+extern	void ieee80211_input(struct ieee80211com *, struct sk_buff *,
+		struct ieee80211_node *, int, u_int32_t);
+extern	void ieee80211_recv_mgmt(struct ieee80211com *, struct sk_buff *,
+		struct ieee80211_node *, int, int, u_int32_t);
+extern	int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
+		int, int);
+extern	void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, 
+		struct sk_buff *);
+extern	struct sk_buff *ieee80211_encap(struct ieee80211com *, struct sk_buff *,
+		struct ieee80211_node **);
+
+/*
+ * Return the size of the 802.11 header for a management or data frame.
+ */
+static inline int
+ieee80211_hdrsize(const void *data)
+{
+	const struct ieee80211_frame *wh = data;
+	int size = sizeof(struct ieee80211_frame);
+
+	/* NB: we don't handle control frames */
+	KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL,
+		("%s: control frame", __func__));
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+		size += IEEE80211_ADDR_LEN;
+	if (IEEE80211_QOS_HAS_SEQ(wh))
+		size += sizeof(u_int16_t);
+	return size;
+}
+
+/*
+ * Return the size of the 802.11 header; handles any type of frame.
+ */
+static inline int
+ieee80211_anyhdrsize(const void *data)
+{
+	const struct ieee80211_frame *wh = data;
+
+	if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+		switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
+		case IEEE80211_FC0_SUBTYPE_CTS:
+		case IEEE80211_FC0_SUBTYPE_ACK:
+			return sizeof(struct ieee80211_frame_ack);
+		}
+		return sizeof(struct ieee80211_frame_min);
+	} else
+		return ieee80211_hdrsize(data);
+}
+
+/*
+ * Template for an in-kernel authenticator.  Authenticators
+ * register with the protocol code and are typically loaded
+ * as separate modules as needed.
+ */
+struct ieee80211_authenticator {
+	const char *ia_name;		/* printable name */
+	int	(*ia_attach)(struct ieee80211com *);
+	void	(*ia_detach)(struct ieee80211com *);
+	void	(*ia_node_join)(struct ieee80211com *,
+				struct ieee80211_node *);
+	void	(*ia_node_leave)(struct ieee80211com *,
+				struct ieee80211_node *);
+};
+extern	void ieee80211_authenticator_register(int type,
+		const struct ieee80211_authenticator *);
+extern	void ieee80211_authenticator_unregister(int type);
+extern	const struct ieee80211_authenticator *
+		ieee80211_authenticator_get(int auth);
+
+struct eapolcom;
+/*
+ * Template for an in-kernel authenticator backend.  Backends
+ * register with the protocol code and are typically loaded
+ * as separate modules as needed.
+ */
+struct ieee80211_authenticator_backend {
+	const char *iab_name;		/* printable name */
+	int	(*iab_attach)(struct eapolcom *);
+	void	(*iab_detach)(struct eapolcom *);
+};
+extern	void ieee80211_authenticator_backend_register(
+		const struct ieee80211_authenticator_backend *);
+extern	void ieee80211_authenticator_backend_unregister(
+		const struct ieee80211_authenticator_backend *);
+extern	const struct ieee80211_authenticator_backend *
+	ieee80211_authenticator_backend_get(const char *name);
+
+/*
+ * Template for an MAC ACL policy module.  Such modules
+ * register with the protocol code and are passed the sender's
+ * address of each received frame for validation.
+ */
+struct ieee80211_aclator {
+	const char *iac_name;		/* printable name */
+	int	(*iac_attach)(struct ieee80211com *);
+	void	(*iac_detach)(struct ieee80211com *);
+	int	(*iac_check)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_add)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_remove)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_flush)(struct ieee80211com *);
+	int	(*iac_setpolicy)(struct ieee80211com *, int);
+	int	(*iac_getpolicy)(struct ieee80211com *);
+};
+extern	void ieee80211_aclator_register(const struct ieee80211_aclator *);
+extern	void ieee80211_aclator_unregister(const struct ieee80211_aclator *);
+extern	const struct ieee80211_aclator *ieee80211_aclator_get(const char *name);
+
+/* flags for ieee80211_fix_rate() */
+#define	IEEE80211_F_DOSORT	0x00000001	/* sort rate list */
+#define	IEEE80211_F_DOFRATE	0x00000002	/* use fixed rate */
+#define	IEEE80211_F_DONEGO	0x00000004	/* calc negotiated rate */
+#define	IEEE80211_F_DODEL	0x00000008	/* delete ignore rate */
+extern	int ieee80211_fix_rate(struct ieee80211com *,
+		struct ieee80211_node *, int);
+
+extern	int ieee80211_iserp_rateset(struct ieee80211com *,
+		struct ieee80211_rateset *);
+#define	ieee80211_new_state(_ic, _nstate, _arg) \
+	(((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
+extern	void ieee80211_print_essid(const u_int8_t *, int);
+extern	void ieee80211_dump_pkt(const u_int8_t *, int, int, int);
+
+extern	const char *ieee80211_state_name[IEEE80211_S_MAX];
+
+/*
+ * Beacon frames constructed by ieee80211_beacon_alloc
+ * have the following structure filled in so drivers
+ * can update the frame later w/ minimal overhead.
+ */
+struct ieee80211_beacon_offsets {
+	u_int16_t	*bo_caps;	/* capabilities */
+	u_int8_t	*bo_tim;	/* start of atim/dtim */
+	u_int8_t	*bo_trailer;	/* start of fixed-size trailer */
+	u_int16_t	bo_tim_len;	/* atim/dtim length in bytes */
+	u_int16_t	bo_trailer_len;	/* trailer length in bytes */
+};
+extern	struct sk_buff *ieee80211_beacon_alloc(struct ieee80211com *,
+		struct ieee80211_node *, struct ieee80211_beacon_offsets *);
+extern	int ieee80211_beacon_update(struct ieee80211com *,
+		struct ieee80211_node *, struct ieee80211_beacon_offsets *,
+		struct sk_buff **);
+
+/*
+ * Notification methods called from the 802.11 state machine.
+ * Note that while these are defined here, their implementation
+ * is OS-specific.
+ */
+extern	void ieee80211_notify_node_join(struct ieee80211com *,
+		struct ieee80211_node *, int newassoc);
+extern	void ieee80211_notify_node_leave(struct ieee80211com *,
+		struct ieee80211_node *);
+extern	void ieee80211_notify_scan_done(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radiotap.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radiotap.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radiotap.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radiotap.h	2005-02-24 13:06:17.342125616 -0800
@@ -0,0 +1,202 @@
+/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.2 2003/12/28 06:57:28 sam Exp $ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.3 2003/11/16 09:02:42 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DAVID
+ * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+#ifndef _NET_IF_IEEE80211RADIOTAP_H_
+#define _NET_IF_IEEE80211RADIOTAP_H_
+
+/* A generic radio capture format is desirable. There is one for
+ * Linux, but it is neither rigidly defined (there were not even
+ * units given for some fields) nor easily extensible.
+ *
+ * I suggest the following extensible radio capture format. It is
+ * based on a bitmap indicating which fields are present.
+ *
+ * I am trying to describe precisely what the application programmer
+ * should expect in the following, and for that reason I tell the
+ * units and origin of each measurement (where it applies), or else I
+ * use sufficiently weaselly language ("is a monotonically nondecreasing
+ * function of...") that I cannot set false expectations for lawyerly
+ * readers.
+ */
+#if defined(__KERNEL__) || defined(_KERNEL)
+#ifndef DLT_IEEE802_11_RADIO
+#define	DLT_IEEE802_11_RADIO	127	/* 802.11 plus WLAN header */
+#endif
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+
+/* The radio capture header precedes the 802.11 header. */
+struct ieee80211_radiotap_header {
+	u_int8_t	it_version;	/* Version 0. Only increases
+					 * for drastic changes,
+					 * introduction of compatible
+					 * new fields does not count.
+					 */
+	u_int8_t	it_pad;
+	u_int16_t       it_len;         /* length of the whole
+					 * header in bytes, including
+					 * it_version, it_pad,
+					 * it_len, and data fields.
+					 */
+	u_int32_t       it_present;     /* A bitmap telling which
+					 * fields are present. Set bit 31
+					 * (0x80000000) to extend the
+					 * bitmap by another 32 bits.
+					 * Additional extensions are made
+					 * by setting bit 31.
+					 */
+} __attribute__((__packed__));
+
+/* Name                                 Data type       Units
+ * ----                                 ---------       -----
+ *
+ * IEEE80211_RADIOTAP_TSFT              u_int64_t       microseconds
+ *
+ *      Value in microseconds of the MAC's 64-bit 802.11 Time
+ *      Synchronization Function timer when the first bit of the
+ *      MPDU arrived at the MAC. For received frames, only.
+ *
+ * IEEE80211_RADIOTAP_CHANNEL           2 x u_int16_t   MHz, bitmap
+ *
+ *      Tx/Rx frequency in MHz, followed by flags (see below).
+ *
+ * IEEE80211_RADIOTAP_FHSS              u_int16_t       see below
+ *
+ *      For frequency-hopping radios, the hop set (first byte)
+ *      and pattern (second byte).
+ *
+ * IEEE80211_RADIOTAP_RATE              u_int8_t        500kb/s
+ *
+ *      Tx/Rx data rate
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     int8_t          decibels from
+ *                                                      one milliwatt (dBm)
+ *
+ *      RF signal power at the antenna, decibel difference from
+ *      one milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE      int8_t          decibels from
+ *                                                      one milliwatt (dBm)
+ *
+ *      RF noise power at the antenna, decibel difference from one
+ *      milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u_int8_t        decibel (dB)
+ *
+ *      RF signal power at the antenna, decibel difference from an
+ *      arbitrary, fixed reference.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTNOISE       u_int8_t        decibel (dB)
+ *
+ *      RF noise power at the antenna, decibel difference from an
+ *      arbitrary, fixed reference point.
+ *
+ * IEEE80211_RADIOTAP_BARKER_CODE_LOCK  u_int16_t       unitless
+ *
+ *      Quality of Barker code lock. Unitless. Monotonically
+ *      nondecreasing with "better" lock strength. Called "Signal
+ *      Quality" in datasheets.  (Is there a standard way to measure
+ *      this?)
+ *
+ * IEEE80211_RADIOTAP_TX_ATTENUATION    u_int16_t       unitless
+ *
+ *      Transmit power expressed as unitless distance from max
+ *      power set at factory calibration.  0 is max power.
+ *      Monotonically nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u_int16_t       decibels (dB)
+ *
+ *      Transmit power expressed as decibel distance from max power
+ *      set at factory calibration.  0 is max power.  Monotonically
+ *      nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DBM_TX_POWER      int8_t          decibels from
+ *                                                      one milliwatt (dBm)
+ *
+ *      Transmit power expressed as dBm (decibels from a 1 milliwatt
+ *      reference). This is the absolute power level measured at
+ *      the antenna port.
+ *
+ * IEEE80211_RADIOTAP_FLAGS             u_int8_t        bitmap
+ *
+ *      Properties of transmitted and received frames. See flags
+ *      defined below.
+ *
+ * IEEE80211_RADIOTAP_ANTENNA           u_int8_t        antenna index
+ *
+ *      Unitless indication of the Rx/Tx antenna for this packet.
+ *      The first antenna is antenna 0.
+ */
+enum ieee80211_radiotap_type {
+	IEEE80211_RADIOTAP_TSFT = 0,
+	IEEE80211_RADIOTAP_FLAGS = 1,
+	IEEE80211_RADIOTAP_RATE = 2,
+	IEEE80211_RADIOTAP_CHANNEL = 3,
+	IEEE80211_RADIOTAP_FHSS = 4,
+	IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+	IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+	IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+	IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+	IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+	IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+	IEEE80211_RADIOTAP_ANTENNA = 11,
+	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+	IEEE80211_RADIOTAP_EXT = 31,
+};
+
+#if !defined(__KERNEL__) && !defined(_KERNEL))
+/* Channel flags. */
+#define IEEE80211_CHAN_TURBO    0x0010  /* Turbo channel */
+#define IEEE80211_CHAN_CCK      0x0020  /* CCK channel */
+#define IEEE80211_CHAN_OFDM     0x0040  /* OFDM channel */
+#define IEEE80211_CHAN_2GHZ     0x0080  /* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_5GHZ     0x0100  /* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_PASSIVE  0x0200  /* Only passive scan allowed */
+#define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
+#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+#endif /* !defined(__KERNEL__) && !defined(_KERNEL) */
+
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define	IEEE80211_RADIOTAP_F_CFP	0x01	/* sent/received
+						 * during CFP
+						 */
+#define	IEEE80211_RADIOTAP_F_SHORTPRE	0x02	/* sent/received
+						 * with short
+						 * preamble
+						 */
+#define	IEEE80211_RADIOTAP_F_WEP	0x04	/* sent/received
+						 * with WEP encryption
+						 */
+#define	IEEE80211_RADIOTAP_F_FRAG	0x08	/* sent/received
+						 * with fragmentation
+						 */
+
+#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.c	2005-02-24 13:06:17.392118016 -0800
@@ -0,0 +1,1834 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+/*
+ * Radius client support for the 802.1x+WPA authenticator.
+ *
+ * This support is optional (it is not present when the authenticator
+ * uses only pre-shared keys).  We override the authenticator's node
+ * management methods to allocate additional space for the radius client
+ * and supply methods for the 802.1x backend state machine to communicate
+ * with a radius server.  This module creates one thread for receiving
+ * messages from the radius server.  All other communication happens
+ * asynchronously through callbacks from the 802.1x authenticator code.
+ * Likewise we communicate state changes to the authenticator by setting
+ * the state variables in a node's state block and then running the
+ * authenticator state machine for that node.
+ *
+ * To use this support the radius server state must be configured before
+ * enabling 802.1x authentication.  Specifically you must set the radius
+ * server's IP address and port and the shared secret used to communicate.
+ * These are all set using sysctl's (or through /proc).  To change any
+ * of these settings while the client is running the device must be marked
+ * down and up again, or similar (e.g. change authentication mode).
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/sysctl.h>
+#include <linux/in.h>
+#include <linux/utsname.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include <asm/uaccess.h>
+#include <linux/random.h>
+
+#include <net/sock.h>			/* XXX for sk_allocation/allocation */
+
+#include "if_media.h"
+#include "if_ethersubr.h"		/* for ETHER_MAX_LEN */
+#include "if_llc.h"			/* for LLC_SNAPFRAMELEN */
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_dot1x.h>
+#include <net80211/ieee80211_radius.h>
+
+#include "rc4.h"		/* XXX */
+#define	arc4_ctxlen()			sizeof (struct rc4_state)
+#define	arc4_setkey(_c,_k,_l)		rc4_init(_c,_k,_l)
+#define	arc4_encrypt(_c,_d,_s,_l)	rc4_crypt_skip(_c,_s,_d,_l,0)
+
+struct auth_hdr {
+	u_int8_t	ah_code;
+	u_int8_t	ah_id;
+	u_int16_t	ah_len;
+	u_int8_t	ah_auth[16];	/* MD5 hash */
+	/* variable length attributes follow */
+} __attribute__((__packed__));
+
+enum {
+	RAD_ACCESS_REQUEST	= 1,
+	RAD_ACCESS_ACCEPT	= 2,
+	RAD_ACCESS_REJECT	= 3,
+	RAD_ACCESS_CHALLENGE	= 11,
+};
+enum {
+	RAD_ATTR_USER_NAME		= 1,
+	RAD_ATTR_USER_PASSWD		= 2,
+	RAD_ATTR_NAS_IP_ADDRESS		= 4,
+	RAD_ATTR_NAS_PORT		= 5,
+	RAD_ATTR_SERVICE_TYPE		= 6,
+#define	RAD_SERVICE_TYPE_FRAMED		2
+	RAD_ATTR_FRAMED_MTU		= 12,
+	RAD_ATTR_REPLY_MESSAGE		= 18,
+	RAD_ATTR_STATE			= 24,
+	RAD_ATTR_CLASS			= 25,
+	RAD_ATTR_VENDOR_SPECIFIC	= 26,
+	RAD_ATTR_SESSION_TIMEOUT	= 27,
+	RAD_ATTR_CALLED_STATION_ID	= 30,
+	RAD_ATTR_CALLING_STATION_ID	= 31,
+	RAD_ATTR_NAS_IDENTIFIER		= 32,
+	RAD_ATTR_NAS_PORT_TYPE		= 61,
+#define	RAD_NAS_PORT_TYPE_WIRELESS	19	/* IEEE 802.11 */
+	RAD_ATTR_CONNECT_INFO		= 77,
+	RAD_ATTR_EAP_MESSAGE		= 79,
+	RAD_ATTR_MESSAGE_AUTHENTICATOR	= 80,
+	RAD_ATTR_NAS_PORT_ID		= 87,
+};
+
+#define	RAD_MAX_ATTR_LEN	(255 - 2)
+#define	RAD_MAX_SECRET		48	/* NB: must be a multiple of 16 */
+
+#ifndef TRUE
+enum { TRUE = 1, FALSE = 0 };		/* for consistency with spec */
+#endif
+
+static	struct sockaddr_in radius_serveraddr;	/* IP address of radius server */
+static	struct sockaddr_in radius_clientaddr;	/* IP address of client */
+static	u_int8_t radius_secret[RAD_MAX_SECRET+1];/* backend shared secret */
+static	u_int	radius_secretlen = 0;
+
+static	int radius_check_auth(struct radiuscom *,
+		struct eapol_auth_radius_node *, struct auth_hdr *);
+static	int radius_check_msg_auth(struct radiuscom *,
+		struct eapol_auth_radius_node *, struct auth_hdr *);
+static	struct sk_buff *radius_create_reply(struct radiuscom *,
+		struct auth_hdr *, struct eapol_auth_node *);
+static	int radius_extract_key(struct radiuscom *,
+		struct eapol_auth_radius_node *, struct auth_hdr *);
+static	void radius_dumppkt(const char *tag, const struct eapol_auth_node *,
+		const struct auth_hdr *);
+static	u_int8_t * radius_find_attr(struct auth_hdr *, int attr);
+static	void sendRespToServer(struct eapol_auth_node *);
+static	struct eapol_auth_radius_node *radius_get_reply(struct radiuscom *,
+		u_int8_t id);
+static	void radius_add_reply(struct radiuscom *,
+		struct eapol_auth_radius_node *);
+static	void radius_cleanup(struct radiuscom *);
+
+#ifdef IEEE80211_DEBUG
+static	void radius_dumpbytes(const char *tag, const void *data, u_int len);
+#endif
+
+static int
+radius_get_integer(u_int8_t *ap)
+{
+	u_int32_t v;
+
+	if (ap[1] >= 2+sizeof(v))
+		memcpy(&v, ap+2, sizeof(v));
+	else
+		v = 0;
+	return ntohl(v);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+
+#include <linux/smp_lock.h>
+
+static void
+_daemonize(const char *procname)
+{
+	lock_kernel();
+
+	daemonize();
+	current->tty = NULL;
+	strcpy(current->comm, procname);
+
+	unlock_kernel();
+}
+
+static int
+allow_signal(int sig)
+{
+	if (sig < 1 || sig > _NSIG)
+		return -EINVAL;
+
+#ifdef INIT_SIGHAND
+	/*
+	 * "The test on INIT_SIGHAND is not perfect but will at
+	 *  least allow this to compile on RedHat kernels."
+	 *
+	 * http://www.mail-archive.com/jfs-discussion@www-124.ibm.com/msg00803.html
+	 */
+	spin_lock_irq(&current->sighand->siglock);
+	sigdelset(&current->blocked, sig);
+	/* XXX current->mm? */
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+#else
+	spin_lock_irq(&current->sigmask_lock);
+	sigdelset(&current->blocked, sig);
+	/* XXX current->mm? */
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+#endif
+
+	return 0;
+}
+#else
+#define	_daemonize(s)	daemonize(s)
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) */
+
+/*
+ * Thread to handle responses from the radius server.
+ * This just listens for packets, decodes them, and
+ * applies their contents to the appropriate session
+ * state, then kicks the state machine as necessary.
+ */
+static int
+radiusd(void *arg)
+{
+	struct radiuscom *rc = arg;
+	struct eapolcom *ec = rc->rc_ec;
+	mm_segment_t oldfs;
+	struct auth_hdr *ahp;
+	struct msghdr msg;
+	struct iovec iov;
+	struct sockaddr_in sin;
+	struct eapol_auth_node *ean;
+	struct eapol_auth_radius_node *ern;
+	u_int8_t *ap;
+	int len;
+
+	/* XXX return what */
+	_MOD_INC_USE(THIS_MODULE, return -1);
+
+	_daemonize("kradiusd");
+	allow_signal(SIGKILL);
+
+	for (;;) {
+		msg.msg_name = &sin;
+		msg.msg_namelen = sizeof(sin);
+		iov.iov_base = rc->rc_buf;
+		iov.iov_len = sizeof(rc->rc_buf);
+		msg.msg_iov = &iov;
+		msg.msg_iovlen = 1;
+		msg.msg_control = NULL;
+		msg.msg_controllen = 0;
+		msg.msg_flags = 0;
+		/*
+		 * Gag, override task limits to tell sock_recvmsg & co
+		 * that our recv buffer is in kernel space and not user!
+		 */
+		oldfs = get_fs();
+		set_fs(KERNEL_DS);
+		len = sock_recvmsg(rc->rc_sock, &msg, iov.iov_len, 0);
+		set_fs(oldfs);
+		/*
+		 * We receive a SIGKILL to detach; ignore any
+		 * message that might have been received.
+		 */
+		if (signal_pending(current))
+			break;
+		if (len <= 0) {
+			printf("%s: sock_recvmsg returns %d\n", __func__, len);
+			continue;
+		}
+		/*
+		 * Warn about msgs not from the server (note
+		 * this is not meant to be security measure...).
+		 */
+		if (msg.msg_namelen != sizeof(sin) ||
+		    sin.sin_addr.s_addr != rc->rc_server.sin_addr.s_addr) {
+			/*
+			 * NB: some versions of Linux apparently
+			 * do not return the sender's address in
+			 * which case we'll want to conditionally
+			 * disable this complaint.
+			 */
+			sin.sin_addr.s_addr = ntohl(sin.sin_addr.s_addr);
+			printf("%s: Warning, bad sender address, "
+			    "len %u addr %u.%u.%u.%u\n",
+			    __func__, msg.msg_namelen,
+			    (sin.sin_addr.s_addr>>24)&0xff,
+			    (sin.sin_addr.s_addr>>16)&0xff,
+			    (sin.sin_addr.s_addr>> 8)&0xff,
+			    (sin.sin_addr.s_addr>> 0)&0xff);
+			eapolstats.rs_addrmismatch++;
+			/* fall thru... */
+		}
+		/*
+		 * Process the radius packet.
+		 */
+		if (len < sizeof(struct auth_hdr)) {
+			printf("%s: msg too short, len %u, discarded\n",
+				__func__, len);
+			eapolstats.rs_tooshort++;
+			continue;
+		}
+		ahp = (struct auth_hdr *)rc->rc_buf;
+		if (ahp->ah_code == RAD_ACCESS_REQUEST) {
+			printf("%s: bad code %u, msg discarded\n",
+				__func__, ahp->ah_code);
+			eapolstats.rs_request++;
+			continue;
+		}
+		/*
+		 * NB: many places we assume the packet contents
+		 *     are not altered so don't do "optimizations"
+		 *     like fixing the byte order of ah_len.
+		 */
+		if (ntohs(ahp->ah_len) > len) {
+			printf("%s: auth len %u > msg len %u, msg discarded\n",
+				__func__, ahp->ah_len, len);
+			eapolstats.rs_badlen++;
+			continue;
+		}
+		/*
+		 * We use the EAPOL state lock to synchronize our
+		 * work (which should not take long to complete).
+		 */
+		EAPOL_LOCK(ec);
+		/*
+		 * Check the radius server response against the
+		 * list of outstanding requests.  Only one should
+		 * have a matching id.
+		 */
+		ern = radius_get_reply(rc, ahp->ah_id);
+		if (ern == NULL) {
+			EAPOL_UNLOCK(ec);
+			printf("%s: RADIUS msg id %u has no pending request\n",
+				__func__, ahp->ah_id);
+			eapolstats.rs_badid++;
+			continue;
+		}
+		ean = &ern->ern_base;
+#ifdef IEEE80211_DEBUG
+		if (ieee80211_msg_dumpradius(ean->ean_ic))
+			radius_dumppkt("RECV", ean, ahp);
+#endif
+		/*
+		 * Check the response authenticator.
+		 */
+		if (!radius_check_auth(rc, ern, ahp)) {
+			EAPOL_UNLOCK(ec);
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+				("[%s] BAD response authenticator\n",
+				ether_sprintf(ean->ean_node->ni_macaddr)));
+			eapolstats.rs_badrespauth++;
+			/* XXX put ean back on reply list? */
+			continue;
+		}
+		if (ahp->ah_code != RAD_ACCESS_REJECT) {
+			/*
+			 * Check the Message-Authenticator hash
+			 * before trusting the message contents.
+			 */
+			if (!radius_check_msg_auth(rc, ern, ahp)) {
+				EAPOL_UNLOCK(ec);
+				IEEE80211_DPRINTF(ean->ean_ic,
+				    IEEE80211_MSG_RADIUS,
+				    ("[%s] BAD message-auth hash\n",
+				    ether_sprintf(ean->ean_node->ni_macaddr)));
+				eapolstats.rs_badmsgauth++;
+				continue;
+			}
+			ap = radius_find_attr(ahp, RAD_ATTR_STATE);
+			if (ap != NULL) {
+				ern->ern_statelen = ap[1] - 2;
+				memcpy(ern->ern_state, ap+2, ern->ern_statelen);
+			} else
+				ern->ern_statelen = 0;
+			ap = radius_find_attr(ahp, RAD_ATTR_CLASS);
+			if (ap != NULL) {
+				ern->ern_classlen = ap[1] - 2;
+				memcpy(ern->ern_class, ap+2, ern->ern_classlen);
+			} else
+				ern->ern_classlen = 0;
+			/*
+			 * Construct EAPOL reply frame based on the
+			 * contents of the radius message just received.
+			 * radius_create_reply will only return a reply
+			 * buffer if the radius message has at least one EAP
+			 * message in it; this is used below.
+			 */
+			if (ern->ern_msg != NULL)
+				kfree_skb(ern->ern_msg);
+			ern->ern_msg = radius_create_reply(rc, ahp, ean);
+		} else
+			ern->ern_msg = NULL;
+
+		switch (ahp->ah_code) {
+		case RAD_ACCESS_ACCEPT:
+			if (ern->ern_msg == NULL) {
+				IEEE80211_DPRINTF(ean->ean_ic,
+				    IEEE80211_MSG_RADIUS,
+				    ("[%s] RECV RAD_ACCESS_ACCEPT, no EAP\n",
+				    ether_sprintf(ean->ean_node->ni_macaddr)));
+				ean->ean_backendAuthFails++;
+				break;
+			}
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+				("[%s] RECV RAD_ACCESS_ACCEPT, success\n",
+				ether_sprintf(ean->ean_node->ni_macaddr)));
+			/* 
+			 * Setup the re-authentication timer based on
+			 * any supplied timeout.  Any value we supply
+			 * from the packet is sanity-checked before use.
+			 */
+			ap = radius_find_attr(ahp, RAD_ATTR_SESSION_TIMEOUT);
+			eapol_reauth_setperiod(ean, ap ?
+				radius_get_integer(ap) : 0);
+			/*
+			 * Collect key state from the message.
+			 */
+			if (radius_extract_key(rc, ern, ahp)) {
+				IEEE80211_DPRINTF(ean->ean_ic,
+				    IEEE80211_MSG_RADIUS,
+				    ("[%s] KEYS available\n",
+				    ether_sprintf(ean->ean_node->ni_macaddr)));
+				ean->ean_keyAvailable = TRUE;
+			}
+			ean->ean_aFail = FALSE;
+			ean->ean_aSuccess = TRUE;
+			ean->ean_backendAuthSuccesses++;
+			eapol_fsm_run(ean);
+			break;
+		case RAD_ACCESS_REJECT:
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+				("[%s] RECV RAD_ACCESS_REJECT\n",
+				ether_sprintf(ean->ean_node->ni_macaddr)));
+			ean->ean_aFail = TRUE;
+			ean->ean_aSuccess = FALSE;
+			ean->ean_backendAuthFails++;
+			/* NB: it's ok for a reject to omit EAP */
+			if (ern->ern_msg != NULL)
+				ean->ean_currentId = ean->ean_idFromServer;
+			eapol_fsm_run(ean);
+			break;
+		case RAD_ACCESS_CHALLENGE:
+			if (ern->ern_msg == NULL) {
+				IEEE80211_DPRINTF(ean->ean_ic,
+				    IEEE80211_MSG_RADIUS,
+				    ("[%s] RECV RAD_ACCESS_CHALLENGE, no EAP\n",
+				    ether_sprintf(ean->ean_node->ni_macaddr)));
+				/* XXX */
+				break;
+			}
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+				("[%s] RECV RAD_ACCESS_CHALLENGE, ok\n",
+				ether_sprintf(ean->ean_node->ni_macaddr)));
+			ean->ean_currentId = ean->ean_idFromServer;
+			ean->ean_aReq = TRUE;
+			ean->ean_backendAccessChallenges++;
+			eapol_fsm_run(ean);
+			break;
+		default:
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+				("[%s] RECV RADIUS msg code %u ignored\n",
+				ether_sprintf(ean->ean_node->ni_macaddr),
+				ahp->ah_code));
+			if (ern->ern_msg != NULL) {
+				kfree_skb(ern->ern_msg);
+				ern->ern_msg = NULL;
+			}
+			eapolstats.rs_badcode++;
+			break;
+		}
+		EAPOL_UNLOCK(ec);
+	}
+	/*
+	 * ieee80211_radius_detach leaves the final
+	 * cleanup work to us to avoid race conditions.
+	 */
+	radius_cleanup(rc);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	module_put_and_exit(0);
+#else
+	return 0;
+#endif
+}
+
+/*
+ * Write-around for bogus crypto API; requiring the address
+ * address be specified twice can easily cause mistakes.
+ */
+static __inline void
+digest_update(struct crypto_tfm *md5, void *data, u_int len)
+{
+	struct scatterlist sg;
+
+	sg.page = virt_to_page(data);
+	sg.offset = offset_in_page(data);
+	sg.length = len;
+	crypto_digest_update(md5, &sg, 1);
+}
+
+/*
+ * Verify the authenticator hash in each reply using
+ * the shared secret and the hash calculated for the
+ * associated request.  The frame contents are assumed
+ * to have been checked that they have a valid length.
+ */
+static int
+radius_check_auth(struct radiuscom *rc, struct eapol_auth_radius_node *ern,
+	struct auth_hdr *ahp)
+{
+	u_int8_t orighash[16];
+	u_int8_t hash[16];
+	struct crypto_tfm *md5 = rc->rc_ec->ec_md5;
+
+	crypto_digest_init(md5);
+
+	memcpy(orighash, ahp->ah_auth, sizeof(ahp->ah_auth));
+	memcpy(ahp->ah_auth, ern->ern_reqauth, sizeof(ern->ern_reqauth));
+
+	digest_update(md5, ahp, ntohs(ahp->ah_len));
+	digest_update(md5, rc->rc_secret, rc->rc_secretlen);
+	crypto_digest_final(md5, hash);
+
+	memcpy(ahp->ah_auth, orighash, sizeof(ahp->ah_auth));
+	return (memcmp(ahp->ah_auth, hash, sizeof(hash)) == 0);
+}
+
+/*
+ * Validate the Message-Authenticator hash.
+ */
+static int
+radius_check_msg_auth(struct radiuscom *rc, struct eapol_auth_radius_node *ern,
+	struct auth_hdr *ahp)
+{
+	u_int8_t ahash[16], hash[16];
+	u_int8_t *ap;
+	int authlen;
+
+	ap = radius_find_attr(ahp, RAD_ATTR_MESSAGE_AUTHENTICATOR);
+	if (ap == NULL) {
+		eapolstats.rs_nomsgauth++;
+		return FALSE;
+	}
+	authlen = ap[1] - 2;
+	if (authlen != sizeof(ahp->ah_auth)) {
+		eapolstats.rs_lenmismatch++;
+		return FALSE;
+	}
+	memcpy(ahash, ap+2, authlen);	/* save hash */
+	memset(ap+2, 0, authlen);	/* zero it for calculations */
+	/* NB: we smash ah_auth and don't repair it... */
+	memcpy(ahp->ah_auth, ern->ern_reqauth, authlen);
+
+	eapol_hmac_md5(rc->rc_ec, ahp, ntohs(ahp->ah_len),
+		rc->rc_secret, rc->rc_secretlen, hash);
+	if (memcmp(ahash, hash, sizeof(hash)) == 0) {
+		memcpy(ap+2, ahash, authlen);	/* restore original data */
+		return TRUE;			/* pass */
+	} else {
+		return FALSE;			/* fail */
+	}
+}
+
+/*
+ * Create an EAPOL/EAP reply message for the supplicant
+ * based on a message from the radius server.  We reserve
+ * space at the front for the EAPOL header and then copy
+ * all the EAP message items from the radius packet.
+ */
+static struct sk_buff *
+radius_create_reply(struct radiuscom *rc,
+	struct auth_hdr *ahp, struct eapol_auth_node *ean)
+{
+	int len = ntohs(ahp->ah_len);
+	const u_int8_t *ap = (const u_int8_t *)&ahp[1];
+	const u_int8_t *ep = ap + (len - sizeof(*ahp));
+	struct sk_buff *skb;
+
+	/*
+	 * Use the received length to size the payload
+	 * in the reply.  This is likely an overestimate but
+	 * simplifies things. Note also that eapol_alloc_skb
+	 * reserves headroom for the headers, including the
+	 * EAPOL header.
+	 */
+	skb = eapol_alloc_skb(len);
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY,
+		    ("[%s] could not alloc sk_buff (%s)\n",
+		    ether_sprintf(ean->ean_node->ni_macaddr), __func__));
+		return NULL;
+	}
+	/*
+	 * Copy EAP messages from radius message to the sk_buff.
+	 */
+	for (; ap < ep; ap += ap[1]) {
+		/* validate attribute length */
+		if (ap[1] < 2 || ap + ap[1] > ep) {
+			IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+			    ("[%s] bad attribute length %u (%s)\n",
+			    ether_sprintf(ean->ean_node->ni_macaddr),
+			    ap[1], __func__));
+			eapolstats.rs_badattrlen++;
+			goto bad;
+		}
+		if (ap[0] == RAD_ATTR_EAP_MESSAGE) {
+			int alen = ap[1] - 2;
+			memcpy(skb_put(skb, alen), ap+2, alen);
+		}
+	}
+	/*
+	 * Check result.
+	 */
+	if (skb->len < sizeof(struct eap_hdr_short)) {
+		/* no EAP messages were found, discard the buf */
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+		    ("[%s] no EAP msg found (%s)\n",
+		    ether_sprintf(ean->ean_node->ni_macaddr), __func__));
+		eapolstats.rs_emptyreply++;
+bad:
+		dev_kfree_skb(skb);
+		return NULL;
+	} else {
+		/* extract EAP identifier for later use */
+		struct eap_hdr_short *eap = (struct eap_hdr_short *)skb->data;
+		ean->ean_idFromServer = eap->eap_id;
+		return skb;
+	}
+}
+
+#define	RAD_VENDOR_MICROSOFT	311
+#define	RAD_KEYTYPE_SEND	16	/* MPPE-Send-Key */
+#define	RAD_KEYTYPE_RECV	17	/* MPPE-Recv-Key */
+
+/*
+ * Microsoft Vendor-specific radius attributes used for
+ * distributing keys.  See MS-MPPE-Send-Key and
+ * MS-MPPE-Recv-Key in RFC 2548 for documentation on the
+ * handling of this data.
+ */
+struct vendor_key {
+	u_int32_t	vk_vid;		/* vendor id */
+	u_int8_t	vk_type;	/* key type */
+	u_int8_t	vk_len;		/* length of type+len+salt+key */
+	u_int16_t	vk_salt;
+	/* variable length key data follows */
+} __attribute__((__packed__));
+
+/*
+ * Extract a station transmit/receive key by mixing the
+ * MPPE key with an md5 hash of the shared secret, request
+ * authenticator, salt, and MPPE key contents.
+ */
+static int
+radius_extract_mppe(struct radiuscom *rc, struct eapol_auth_radius_node *ern,
+	struct vendor_key *vk, struct eapol_radius_key *key)
+{
+	u_int8_t hash[16], keybuf[sizeof(key->rk_data)];
+	u_int8_t *vkey;
+	u_int8_t *dkey;
+	int keylen, total, i, j;
+	struct crypto_tfm *md5 = rc->rc_ec->ec_md5;
+
+	/*
+	 * vk_len is the vendor attribute payload size, so deduct
+	 * the header (including salt) to get the key length.  If
+	 * the key length is not a multiple of the md5 hash size;
+	 * then it is zero-padded (the rfc talks about an optional
+	 * padding sub-field but we assume clients may not include
+	 * it--this may be wrong if they include it and do not use
+	 * zero for padding, in practice everyone so far just sends
+	 * keys that are a multiple of 16).  Also, verify the result
+	 * fits in the storage we have set aside for it.
+	 */
+	keylen = vk->vk_len - (sizeof(vk->vk_type) +
+		sizeof(vk->vk_len) + sizeof(vk->vk_salt));
+	/* XXX is keylen == 0 ok? */
+	total = roundup(keylen, 16);
+	if (total > sizeof(keybuf)) {
+		IEEE80211_DPRINTF(ern->ern_ic, IEEE80211_MSG_ANY,
+		    ("[%s] MPPE key too large, total %u (%s)\n",
+		    ether_sprintf(ern->ern_node->ni_macaddr),
+		    total, __func__));
+		/* XXX should we just truncate it? */
+		eapolstats.rs_vkeytoolong++;
+		return FALSE;
+	}
+
+	/*
+	 * Create intermediate, potentially zero-padded, copy.
+	 */
+	vkey = (u_int8_t *)&vk[1];
+	memcpy(keybuf, vkey, keylen);
+	if (keylen != total)		/* zero-pad key */
+		memset(keybuf+keylen, 0, total-keylen);
+
+	/*
+	 * The first 16 bytes are xor'd with a hash constructed
+	 * from (shared secret | req-authenticator | salt).
+	 * Subsequent 16-byte chunks of the key are xor'd with
+	 * the hash of (shared secret | key).
+	 */
+	dkey = key->rk_data;
+	for (i = 0; i < total; i += 16) {
+		crypto_digest_init(md5);
+		digest_update(md5, rc->rc_secret, rc->rc_secretlen);
+		if (i == 0) {
+			digest_update(md5,
+				ern->ern_reqauth, sizeof(ern->ern_reqauth));
+			digest_update(md5, &vk->vk_salt, sizeof(vk->vk_salt));
+		} else {
+			digest_update(md5, &keybuf[i-16], 16);
+		}
+		crypto_digest_final(md5, hash);
+
+		for (j = 0; j < 16; j++)
+			dkey[i+j] = keybuf[i+j] ^ hash[j];
+	}
+	return TRUE;
+}
+
+/*
+ * Extract Microsoft MPPE key state from the frame.
+ */
+static int
+radius_extract_key(struct radiuscom *rc,
+	struct eapol_auth_radius_node *ern, struct auth_hdr *ahp)
+{
+#define	ALLKEYS	(RAD_KEYTYPE_SEND+RAD_KEYTYPE_RECV)
+	int len = ntohs(ahp->ah_len);
+	u_int8_t *ap = (u_int8_t *)&ahp[1];
+	u_int8_t *ep = ap + (len - sizeof(*ahp));
+	struct vendor_key *vk;
+	int found = 0;
+
+	for (; ap < ep && found != ALLKEYS; ap += ap[1]) {
+		/* validate attribute length */
+		if (ap[1] < 2 || ap + ap[1] > ep) {
+			eapolstats.rs_badattrlen++;
+			break;
+		}
+		if (ap[0] != RAD_ATTR_VENDOR_SPECIFIC)
+			continue;
+		if (ap[1] < 2+sizeof(struct vendor_key)) {
+			eapolstats.rs_vkeytooshort++;
+			continue;
+		}
+		vk = (struct vendor_key *)&ap[2];
+		/* XXX ntohl alignment */
+		if (ntohl(vk->vk_vid) != RAD_VENDOR_MICROSOFT) {
+			eapolstats.rs_vkeybadvid++;
+			continue;
+		}
+		if ((ntohs(vk->vk_salt) & 0x8000) == 0) {
+			eapolstats.rs_vkeybadsalt++;
+			continue;
+		}
+		switch (vk->vk_type) {
+		case RAD_KEYTYPE_SEND:
+			if (found && vk->vk_salt == ern->ern_rxkey.rk_salt) {
+				eapolstats.rs_vkeydupsalt++;
+				continue;
+			}
+			if (radius_extract_mppe(rc, ern, vk, &ern->ern_txkey)) {
+#ifdef IEEE80211_DEBUG
+				if (ieee80211_msg_dumpradkeys(ern->ern_ic))
+					radius_dumpbytes("send key",
+						&ern->ern_txkey.rk_data[1],
+						ern->ern_txkey.rk_len);
+#endif
+				ern->ern_txkey.rk_salt = vk->vk_salt;
+				found += RAD_KEYTYPE_SEND;
+			}
+			break;
+		case RAD_KEYTYPE_RECV:
+			if (found && vk->vk_salt == ern->ern_txkey.rk_salt) {
+				eapolstats.rs_vkeydupsalt++;
+				continue;
+			}
+			if (radius_extract_mppe(rc, ern, vk, &ern->ern_rxkey)) {
+#ifdef IEEE80211_DEBUG
+				if (ieee80211_msg_dumpradkeys(ern->ern_ic))
+					radius_dumpbytes("recv key",
+						&ern->ern_rxkey.rk_data[1],
+						ern->ern_rxkey.rk_len);
+#endif
+				ern->ern_rxkey.rk_salt = vk->vk_salt;
+				found += RAD_KEYTYPE_RECV;
+			}
+			break;
+		}
+	}
+	return (found == ALLKEYS);
+#undef ALLKEYS
+}
+
+/*
+ * Locate an attribute in the message and sanity
+ * check the length field so the caller can assume
+ * it's minimally valid (i.e. it covers the attribute
+ * itself and any data is within the bound of the
+ * message as specified in the header.
+ */
+static u_int8_t *
+radius_find_attr(struct auth_hdr *ahp, int attr)
+{
+	int len = ntohs(ahp->ah_len);
+	u_int8_t *ap = (u_int8_t *)&ahp[1];
+	u_int8_t *ep = ap + (len - sizeof(*ahp));
+
+	for (; ap < ep; ap += ap[1]) {
+		/* validate attribute length */
+		if (ap[1] < 2 || ap + ap[1] > ep) {
+			eapolstats.rs_badattrlen++;
+			return NULL;
+		}
+		if (ap[0] == attr)
+			return ap;
+	}
+	return NULL;
+}
+
+/*
+ * Helper functions for constructing a radius message.
+ */
+
+static u_int8_t *
+radius_add_bytes(u_int8_t *dp, int attr, const void *val, u_int len)
+{
+	dp[0] = attr;
+	dp[1] = 2 + len;
+	memcpy(&dp[2], val, len);
+	return dp + dp[1];
+}
+
+static u_int8_t *
+radius_add_space(u_int8_t *dp, int attr, u_int len)
+{
+	dp[0] = attr;
+	dp[1] = 2 + len;
+	memset(&dp[2], 0, len);
+	return dp + dp[1];
+}
+
+static u_int8_t *
+radius_add_integer(u_int8_t *dp, int attr, u_int32_t val)
+{
+	val = htonl(val);
+	return radius_add_bytes(dp, attr, &val, sizeof(val));
+}
+
+static u_int8_t *
+radius_add_string(u_int8_t *dp, int attr, const char *val)
+{
+	return radius_add_bytes(dp, attr, val, strlen(val));
+}
+
+static u_int8_t *
+radius_add_stationid(u_int8_t *dp, int attr, struct eapol_auth_node *ean)
+{
+	struct ieee80211com *ic = ean->ean_ic;
+	const u_int8_t *mac = ic->ic_bss->ni_macaddr;
+	char buf[255];
+
+	snprintf(buf, sizeof(buf), "%02x-%02x-%02x-%02x-%02x-%02x:%.*s",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+		ic->ic_des_esslen, ic->ic_des_essid);
+	return radius_add_string(dp, attr, buf);
+}
+
+static u_int8_t *
+radius_add_macaddr(u_int8_t *dp, int attr, struct eapol_auth_node *ean)
+{
+	const u_int8_t *mac = ean->ean_node->ni_macaddr;
+	char buf[20];
+
+	snprintf(buf, sizeof(buf), "%02x-%02x-%02x-%02x-%02x-%02x",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	return radius_add_string(dp, attr, buf);
+}
+
+static u_int8_t *
+radius_add_nasport(u_int8_t *dp, int port)
+{
+	char buf[32];
+	int len = snprintf(buf, sizeof(buf), "STA port # %d", port);
+	return radius_add_bytes(dp, RAD_ATTR_NAS_PORT_ID, buf, len);
+}
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Return the name for certain attributes.
+ */
+static const char *
+radius_attrname(int attr)
+{
+	static char buf[8];
+
+	switch (attr) {
+	case RAD_ATTR_USER_NAME:	return "User-Name";
+	case RAD_ATTR_USER_PASSWD:	return "User-Passwd";
+	case RAD_ATTR_NAS_IP_ADDRESS:	return "NAS-Ip-Address";
+	case RAD_ATTR_NAS_PORT:		return "NAS-Port";
+	case RAD_ATTR_SERVICE_TYPE:	return "Service-Type";
+	case RAD_ATTR_FRAMED_MTU:	return "Framed-MTU";
+	case RAD_ATTR_REPLY_MESSAGE:	return "Reply-Message";
+	case RAD_ATTR_STATE:		return "State";
+	case RAD_ATTR_CLASS:		return "Class";
+	case RAD_ATTR_VENDOR_SPECIFIC:	return "Vendor-Specific";
+	case RAD_ATTR_SESSION_TIMEOUT:	return "Session-Timeout";
+	case RAD_ATTR_CALLED_STATION_ID:return "Called-Station-ID";
+	case RAD_ATTR_CALLING_STATION_ID:return "Calling-Station-ID";
+	case RAD_ATTR_NAS_IDENTIFIER:	return "NAS-Identifier";
+	case RAD_ATTR_NAS_PORT_TYPE:	return "NAS-Port-Type";
+	case RAD_ATTR_CONNECT_INFO:	return "Connect-Info";
+	case RAD_ATTR_EAP_MESSAGE:	return "EAP-Message";
+	case RAD_ATTR_MESSAGE_AUTHENTICATOR: return "Message-Authenticator";
+	case RAD_ATTR_NAS_PORT_ID:	return "NAS-Port-ID";
+	}
+	snprintf(buf, sizeof(buf), "%u", attr);
+	return buf;
+};
+
+static void
+radius_dumpbytes(const char *tag, const void *data, u_int len)
+{
+	const u_int8_t *tp;
+
+	printf("%s = 0x", tag);
+	for (tp = data; len > 0; tp++, len--)
+		printf("%02x", *tp);
+	printf("\n");
+}
+
+/*
+ * Dump the contents of a RADIUS msg to the console.
+ */
+static void
+radius_dumppkt(const char *tag, const struct eapol_auth_node *ean,
+	const struct auth_hdr *ahp)
+{
+	int len = ntohs(ahp->ah_len);
+	const u_int8_t *ap = (const u_int8_t *)&ahp[1];
+	const u_int8_t *ep = ap + (len - sizeof(*ahp));
+	const char *attrname;
+	u_int32_t v;
+
+	printf("[%s] %s RADIUS msg: code %u id %u len %u\n",
+		ether_sprintf(ean->ean_node->ni_macaddr),
+		tag, ahp->ah_code, ahp->ah_id, len);
+	for (; ap < ep; ap += ap[1]) {
+		if (ap + ap[1] > ep) {
+			printf("%s: bogus attribute length %u for attr %u\n",
+				__func__, ap[1], ap[0]);
+			return;
+		}
+		attrname = radius_attrname(ap[0]);
+		len = ap[1] - 2;
+		switch (ap[0]) {
+		case RAD_ATTR_SERVICE_TYPE:
+		case RAD_ATTR_FRAMED_MTU:
+		case RAD_ATTR_NAS_PORT_TYPE:
+		case RAD_ATTR_NAS_PORT:
+			memcpy(&v, ap+2, sizeof(v));
+			v = ntohl(v);
+			printf("\t%s = %u (0x%x)\n", attrname, v, v);
+			break;
+		case RAD_ATTR_USER_NAME:
+		case RAD_ATTR_CALLED_STATION_ID:
+		case RAD_ATTR_CALLING_STATION_ID:
+		case RAD_ATTR_NAS_IDENTIFIER:
+		case RAD_ATTR_CONNECT_INFO:
+		case RAD_ATTR_NAS_PORT_ID:
+			printf("\t%s = \"%.*s\"\n", attrname, len, ap+2);
+			break;
+		case RAD_ATTR_NAS_IP_ADDRESS:
+			memcpy(&v, ap+2, sizeof(v));
+			v = ntohl(v);
+			printf("\t%s = %u.%u.%u.%u\n", attrname,
+				(v>>24)&0xff, (v>>16)&0xff,
+				(v>>8)&0xff, (v>>0)&0xff);
+			break;
+		default:
+			printf("\t");
+			radius_dumpbytes(attrname, ap+2, len);
+			break;
+		}
+	}
+}
+#endif /* IEEE80211_DEBUG */
+
+/*
+ * Create a response msg for transmission to the radius server.
+ * The response is an EAP-encapsulated version of the supplicant
+ * request that is left in an outbound packet buffer.
+ *
+ * XXX bounds checking
+ */
+int
+radius_make_response(struct radiuscom *rc, struct eapol_auth_radius_node *ern)
+{
+	struct eapol_auth_node *ean = &ern->ern_base;
+	struct sk_buff *skb = ean->ean_skb;
+	struct eap_hdr *eap;
+	u_int8_t *dp, *cp, *map;
+	u_int8_t msgauth[16];
+	struct auth_hdr *ahp;
+	u_int len, cc;
+
+	if (skb == NULL) {
+		/* XXX something is wrong */
+		return FALSE;
+	}
+	eap = (struct eap_hdr *)skb->data;
+
+	if (ern->ern_radmsg == NULL) {
+		/* XXX 4096 */
+		MALLOC(ern->ern_radmsg, u_int8_t *, 4096, M_DEVBUF, M_NOWAIT);
+		if (ern->ern_radmsg == NULL) {
+			eapolstats.rs_nomem++;	/* XXX unique stat */
+			return FALSE;
+		}
+	}
+	ahp = (struct auth_hdr *)ern->ern_radmsg;
+
+	ahp->ah_code = RAD_ACCESS_REQUEST;
+	ahp->ah_id = eap->eap_id;		/* copy from supplicant */
+	/*
+	 * Save authenticator hash for use in validating replies
+	 * and for extracting MPPE keys returned by the server.
+	 */
+	get_random_bytes(ern->ern_reqauth, sizeof(ern->ern_reqauth));
+	memcpy(ahp->ah_auth, ern->ern_reqauth, sizeof(ern->ern_reqauth));
+
+	/*
+	 * Add the attribute-value pairs.
+	 */
+	dp = (u_int8_t *)&ahp[1];
+	map = dp;		/* hold a pointer for fixup later */
+	dp = radius_add_space(dp, RAD_ATTR_MESSAGE_AUTHENTICATOR, 16);
+	dp = radius_add_integer(dp, RAD_ATTR_SERVICE_TYPE,
+		RAD_SERVICE_TYPE_FRAMED);
+	dp = radius_add_bytes(dp, RAD_ATTR_USER_NAME,
+		ean->ean_id, ean->ean_idlen);
+	/* NB: use Ethernet MTU rather than 802.11 MTU */
+	dp = radius_add_integer(dp, RAD_ATTR_FRAMED_MTU,
+		ETHERMTU - LLC_SNAPFRAMELEN - sizeof(struct eapol_hdr));
+	if (ern->ern_statelen != 0)
+		dp = radius_add_bytes(dp, RAD_ATTR_STATE,
+			ern->ern_state, ern->ern_statelen);
+	if (ern->ern_classlen != 0)
+		dp = radius_add_bytes(dp, RAD_ATTR_CLASS,
+			ern->ern_class, ern->ern_classlen);
+	dp = radius_add_stationid(dp, RAD_ATTR_CALLED_STATION_ID, ean);
+	dp = radius_add_macaddr(dp, RAD_ATTR_CALLING_STATION_ID, ean);
+	dp = radius_add_string(dp, RAD_ATTR_NAS_IDENTIFIER,
+		system_utsname.nodename);
+	dp = radius_add_integer(dp, RAD_ATTR_NAS_PORT_TYPE,
+		RAD_NAS_PORT_TYPE_WIRELESS);
+	switch (ean->ean_ic->ic_curmode) {
+	case IEEE80211_MODE_11A:
+	case IEEE80211_MODE_TURBO:
+		dp = radius_add_string(dp, RAD_ATTR_CONNECT_INFO,
+			"CONNECT 54Mbps 802.11a");
+		break;
+	case IEEE80211_MODE_11B:
+		dp = radius_add_string(dp, RAD_ATTR_CONNECT_INFO,
+			"CONNECT 11Mbps 802.11b");
+		break;
+	case IEEE80211_MODE_11G:
+		dp = radius_add_string(dp, RAD_ATTR_CONNECT_INFO,
+			"CONNECT 54Mbps 802.11g");
+		break;
+	default:
+		/* XXX bitch */
+		eapolstats.rs_badmode++;
+		break;
+	}
+
+	/*
+	 * Copy in the original EAP message.  Note that
+	 * we have to byte swap the length field in the
+	 * header as it is put in host order on receipt.
+	 */
+	len = eap->eap_len;
+	eap->eap_len = htons(len);
+	for (cp = (u_int8_t *)eap; len > 0; cp += cc, len -= cc) {
+		cc = len;
+		if (cc > RAD_MAX_ATTR_LEN)
+			cc = RAD_MAX_ATTR_LEN;
+		dp = radius_add_bytes(dp, RAD_ATTR_EAP_MESSAGE, cp, cc);
+	}
+	eap->eap_len = ntohs(eap->eap_len);
+
+	dp = radius_add_bytes(dp, RAD_ATTR_NAS_IP_ADDRESS,
+		&rc->rc_local.sin_addr.s_addr, sizeof(struct in_addr));
+	dp = radius_add_integer(dp, RAD_ATTR_NAS_PORT,
+		IEEE80211_NODE_AID(ean->ean_node));
+	dp = radius_add_nasport(dp, IEEE80211_NODE_AID(ean->ean_node));
+
+	ern->ern_radmsglen = dp - ern->ern_radmsg;
+
+	/*
+	 * Now patch up stuff that required the message be
+	 * formulated and calculate the message authenticator
+	 * hash.
+	 */
+	ahp->ah_len = htons(ern->ern_radmsglen);
+	eapol_hmac_md5(rc->rc_ec, ern->ern_radmsg, ern->ern_radmsglen,
+		rc->rc_secret, rc->rc_secretlen, msgauth);
+	memcpy(map+2, msgauth, sizeof(msgauth));
+
+	return TRUE;
+}
+
+/*
+ * Callback from the 802.1x state machine on
+ * receiving an Identity packet from the supplicant.
+ */
+static void
+radius_identity_input(struct eapol_auth_node *ean, struct sk_buff *skb)
+{
+	struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean);
+
+	memset(ern->ern_state, 0, sizeof(ern->ern_state));
+	ern->ern_statelen = 0;
+	memset(ern->ern_class, 0, sizeof(ern->ern_class));
+	ern->ern_classlen = 0;
+}
+
+/*
+ * Callback from the 802.1x backend state machine to
+ * send/resend a response message to the server.
+ */
+static void
+sendRespToServer(struct eapol_auth_node *ean)
+{
+	struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean);
+	struct radiuscom *rc = ean->ean_ec->ec_radius;
+	struct msghdr msg;
+	struct iovec iov;
+
+	if (ean->ean_reqSrvCount == 0) {
+		/*
+		 * Not a retry; formulate the message from scratch
+		 * and add an entry to the reply list.  This entry
+		 * stays until a response is received from the server
+		 * or the node is reset/reclaimed.
+		 */
+		radius_make_response(rc, ern);
+		radius_add_reply(rc, ern);
+	}
+	IEEE80211_DPRINTF(ean->ean_ic,
+		IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X,
+		("[%s] %s buf 0x%p len %u\n",
+		ether_sprintf(ean->ean_node->ni_macaddr),
+		__func__, ern->ern_radmsg, ern->ern_radmsglen));
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_dumpradius(ean->ean_ic))
+		radius_dumppkt("SEND", ean, (struct auth_hdr *)ern->ern_radmsg);
+#endif
+	msg.msg_name = &rc->rc_server;
+	msg.msg_namelen = sizeof(rc->rc_server);
+	iov.iov_base = ern->ern_radmsg;
+	iov.iov_len = ern->ern_radmsglen;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = 0;
+	/* XXXX check return */
+	(void) sock_sendmsg(rc->rc_sock, &msg, iov.iov_len);
+}
+
+/*
+ * Callback from the 802.1x backend state machine to
+ * transmit/retransmit the EAP message from the radius sever
+ * to the supplicant.  The message should have been setup by
+ * the thread that receives messages from the radius server
+ * (see radius_create_reply).
+ */
+static void
+txReq(struct eapol_auth_node *ean)
+{
+	struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean);
+	struct sk_buff *skb;
+
+	if (ern->ern_msg == NULL) {
+		IEEE80211_DPRINTF(ean->ean_ic,
+			IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X,
+			("[%s] no msg to send (%s)!\n",
+			ether_sprintf(ean->ean_node->ni_macaddr), __func__));
+		eapolstats.rs_nomsg++;
+		return;
+	}
+	skb = skb_clone(ern->ern_msg, GFP_ATOMIC);
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(ean->ean_ic,
+			IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X,
+			("[%s] could not clone msg for xmit (%s)\n",
+			ether_sprintf(ean->ean_node->ni_macaddr), __func__));
+		eapolstats.rs_noclone++;
+	} else {
+		IEEE80211_DPRINTF(ean->ean_ic,
+			IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X,
+			("[%s] %s msg 0x%p len %u\n",
+			ether_sprintf(ean->ean_node->ni_macaddr),
+			__func__, skb, skb->len));
+		eapol_send(&ean->ean_base, skb, EAPOL_TYPE_EAP);
+	}
+}
+
+/*
+ * Construct and send an EAPOL RC4 Key message to the supplicant.
+ */
+static void
+radius_txkey(struct eapol_auth_radius_node *ern,
+	int keytype, int keyix, struct ieee80211_key *key)
+{
+	struct eapol_auth_node *ean = &ern->ern_base;
+	struct radiuscom *rc = ean->ean_ec->ec_radius;
+	struct sk_buff *skb;
+	struct eapol_key *kp;
+	struct eapol_hdr *eapol;
+	struct rc4_state ctx;
+	u_int8_t *keydata, rc4key[16+64], hash[16];
+	struct crypto_tfm *md5 = rc->rc_ec->ec_md5;
+
+	KASSERT(keyix != IEEE80211_KEYIX_NONE, ("no key index"));
+	if (key->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY,
+		    ("[%s] key type %u not WEP\n",
+		    ether_sprintf(ean->ean_node->ni_macaddr),
+		    key->wk_cipher->ic_cipher));
+		return;
+	}
+	if (key->wk_keylen != 16 && key->wk_keylen != 13 && key->wk_keylen != 5) {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY,
+		    ("[%s] bad key length %u\n",
+		    ether_sprintf(ean->ean_node->ni_macaddr), key->wk_keylen));
+		return;
+	}
+	skb = eapol_alloc_skb(sizeof(struct eapol_key) + key->wk_keylen);
+	if (skb == NULL) {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY,
+		    ("[%s] could not alloc sk_buff (%s)\n",
+		    ether_sprintf(ean->ean_node->ni_macaddr), __func__));
+		return;
+	}
+	kp = (struct eapol_key *)skb_put(skb, sizeof(struct eapol_key));
+	memset(kp, 0, sizeof(struct eapol_key));
+	kp->ek_type = EAPOL_KEY_TYPE_RC4;
+	kp->ek_length = htons(key->wk_keylen);
+	/* NB: there is no htonll */
+	kp->ek_replay = cpu_to_be64(get_jiffies_64());
+
+	get_random_bytes(kp->ek_iv, sizeof(kp->ek_iv));
+	crypto_digest_init(md5);
+	digest_update(md5, kp->ek_iv, sizeof(kp->ek_iv));
+	crypto_digest_final(md5, kp->ek_iv);
+
+	kp->ek_index = keyix | keytype;
+
+	/*
+	 * NB: this ``cannot happen'' as the rxkey has already been
+	 *     verified to fit in ern_rxkey.
+	 */
+	if (sizeof(kp->ek_iv)+ern->ern_rxkey.rk_len > sizeof(rc4key)) {
+		IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY,
+		    ("[%s] intermediate key buf too small, key len %u (%s)\n",
+		    ether_sprintf(ean->ean_node->ni_macaddr),
+		    ern->ern_rxkey.rk_len, __func__));
+		return;
+	}
+
+	/* encrypt the station key with rc4 key of (iv | rxkey) */
+	memcpy(rc4key, kp->ek_iv, sizeof(kp->ek_iv));
+	memcpy(rc4key+sizeof(kp->ek_iv), &ern->ern_rxkey.rk_data[1],
+		ern->ern_rxkey.rk_len);
+
+	keydata = (u_int8_t *)skb_put(skb, key->wk_keylen);
+	arc4_setkey(&ctx, rc4key, sizeof(kp->ek_iv) + ern->ern_rxkey.rk_len);
+	arc4_encrypt(&ctx, keydata, key->wk_key, key->wk_keylen);
+	memset(rc4key, 0, sizeof(rc4key));
+
+	/*
+	 * Finally, sign the message.  To do this we must
+	 * formulate the EAPOL header so we can calculate
+	 * the signature that goes in the key payload.  This
+	 * violates the normal layering (sigh, who designs
+	 * these protocols).
+	 */
+	eapol = (struct eapol_hdr *)skb_push(skb, sizeof(struct eapol_hdr));
+	eapol->eapol_ver = EAPOL_VERSION;
+	eapol->eapol_type = EAPOL_TYPE_KEY;
+	eapol->eapol_len = htons(skb->len - sizeof(struct eapol_hdr));
+
+	eapol_hmac_md5(rc->rc_ec, eapol, skb->len,
+		&ern->ern_txkey.rk_data[1], ern->ern_txkey.rk_len, hash);
+	memcpy(kp->ek_sig, hash, sizeof(hash));
+
+	IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS,
+	    ("[%s] SEND %s EAPOL-Key ix %u len %u bits\n",
+	    ether_sprintf(ean->ean_node->ni_macaddr),
+	    keytype == EAPOL_KEY_BCAST ? "bcast" : "ucast",
+	    keyix, key->wk_keylen * NBBY));
+
+	eapol_send_raw(&ean->ean_base, skb);
+}
+
+/*
+ * Construct unicast key for station.
+ *
+ * XXX just use random data for now; probably want something better
+ * XXX cipher suites
+ */
+static int
+setUnicastKey(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct ieee80211_key *key = &ni->ni_ucastkey;
+	int ok = 1;
+
+	ieee80211_key_update_begin(ic);
+	key->wk_keylen = ni->ni_rsn.rsn_ucastkeylen;
+	get_random_bytes(key->wk_key, key->wk_keylen);
+	if (!ieee80211_crypto_newkey(ic, ni->ni_rsn.rsn_ucastcipher, key)) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_RADIUS,
+		    ("[%s] problem creating unicast key for station\n",
+		    ether_sprintf(ni->ni_macaddr)));
+		ok = 0;
+	} else if (!ieee80211_crypto_setkey(ic, key, ni->ni_macaddr)) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_RADIUS,
+		    ("[%s] problem installing unicast key for station\n",
+		    ether_sprintf(ni->ni_macaddr)));
+		/* XXX recovery? */
+		ok = 0;
+	}
+	ieee80211_key_update_end(ic);
+
+	return ok;
+}
+
+/*
+ * Callback from the 802.1x backend state machine to transmit
+ * broadcast+unicast keys to the supplicant. The keys are
+ * constructed from the key material received from the radius
+ * server when access is granted.
+ *
+ * NB: there must be a multicast key; the unicast key is optional.
+ */
+static void
+txKey(struct eapol_auth_node *ean)
+{
+	struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean);
+	struct ieee80211com *ic = ean->ean_ic;
+	struct ieee80211_node *ni = ean->ean_node;
+
+	KASSERT(ean->ean_keyAvailable == TRUE, ("no keys!"));
+
+	/*
+	 * Send multicast key with index > 0.
+	 */
+	if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_RADIUS,
+		    ("[%s] no multicast key for station\n",
+		    ether_sprintf(ni->ni_macaddr)));
+		return;
+	}
+	radius_txkey(ern, EAPOL_KEY_BCAST,
+		ic->ic_def_txkey, &ic->ic_nw_keys[ic->ic_def_txkey]);
+	/*
+	 * Construct unicast key for station and send it with index 0.
+	 */
+	if (setUnicastKey(ic, ni))
+		radius_txkey(ern, EAPOL_KEY_UCAST, 0, &ni->ni_ucastkey);
+}
+
+/*
+ * Look for a session waiting for a reply from the
+ * radius server.  If found the session is taken off
+ * the list.
+ */
+static struct eapol_auth_radius_node *
+radius_get_reply(struct radiuscom *rc, u_int8_t id)
+{
+	struct eapol_auth_radius_node *ern;
+
+	EAPOL_LOCK_ASSERT(rc->rc_ec);
+
+	LIST_FOREACH(ern, &rc->rc_replies, ern_next)
+		if (ern->ern_base.ean_currentId == id) {
+			LIST_REMOVE(ern, ern_next);
+			ern->ern_onreply = FALSE;
+			return ern;
+		}
+	return NULL;
+}
+
+static void
+radius_remove_reply(struct radiuscom *rc, struct eapol_auth_radius_node *p)
+{
+	struct eapol_auth_radius_node *ern;
+
+	EAPOL_LOCK_ASSERT(rc->rc_ec);
+
+	LIST_FOREACH(ern, &rc->rc_replies, ern_next)
+		if (ern == p) {
+			LIST_REMOVE(ern, ern_next);
+			ern->ern_onreply = FALSE;
+			break;
+		}
+}
+
+/*
+ * Add a session to the list of those waiting for a
+ * reply from the radius server.
+ */
+static void
+radius_add_reply(struct radiuscom *rc, struct eapol_auth_radius_node *ern)
+{
+
+	EAPOL_LOCK_ASSERT(rc->rc_ec);
+
+	if (!ern->ern_onreply) {
+		LIST_INSERT_HEAD(&rc->rc_replies, ern, ern_next);
+		ern->ern_onreply = TRUE;
+	}
+}
+
+/*
+ * Radius-specific node allocate/free routines that
+ * allocate the larger nodes needed when communicating
+ * with a radius server.  This routine is installed
+ * when we attach to the authenticator
+ */
+static struct eapol_auth_node *
+radius_node_alloc(struct eapolcom *ec)
+{
+	struct eapol_auth_radius_node *ern;
+
+	MALLOC(ern, struct eapol_auth_radius_node *,
+		sizeof(struct eapol_auth_radius_node),
+		M_EAPOL_NODE, M_NOWAIT | M_ZERO);
+	return ern ? &ern->ern_base : NULL;
+}
+
+/*
+ * Reclaim resources specific to the radius server.  This
+ * routine is installed when we attach to the authenticator.
+ */
+static void
+radius_node_free(struct eapol_auth_node *ean)
+{
+	struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean);
+	struct radiuscom *rc = ean->ean_ec->ec_radius;
+
+	/*
+	 * NB: we're called from eapol_node_leave which drops it's
+	 * EAPOL lock before calling us as it's already removed the
+	 * reference to us from the table and needs to drop the lock
+	 * before calling back in to the 802.11 layer.  However we
+	 * need to grab the lock again to safeguard removing any
+	 * reference to use on the reply list.  There's probably
+	 * a better way to do this.
+	 */
+	EAPOL_LOCK(ean->ean_ec);
+	radius_remove_reply(rc, ern);
+	EAPOL_UNLOCK(ean->ean_ec);
+
+	if (ern->ern_msg != NULL)
+		kfree_skb(ern->ern_msg);
+	if (ern->ern_radmsg != NULL)
+		FREE(ern->ern_radmsg, M_DEVBUF);
+	(*rc->rc_node_free)(ean);
+}
+
+/*
+ * Reset non-fsm state for the specified node.  This
+ * routine is installed when we attach to the authenticator.
+ */
+static void
+radius_node_reset(struct eapol_auth_node *ean)
+{
+	struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean);
+	struct radiuscom *rc = ean->ean_ec->ec_radius;
+
+	EAPOL_LOCK_ASSERT(rc->rc_ec);
+
+	radius_remove_reply(rc, ern);
+	if (ern->ern_msg != NULL) {
+		kfree_skb(ern->ern_msg);
+		ern->ern_msg = NULL;
+	}
+	(*rc->rc_node_reset)(ean);
+}
+
+/*
+ * Cleanup radius client state.
+ */
+static void
+radius_cleanup(struct radiuscom *rc)
+{
+	if (rc->rc_sock != NULL)
+		sock_release(rc->rc_sock);
+	if (rc->rc_secret != NULL)
+		FREE(rc->rc_secret, M_DEVBUF);
+	FREE(rc, M_DEVBUF);
+
+	printk(KERN_INFO "802.1x radius client stopped\n");
+}
+
+/*
+ * Detach a radius client from an authenticator.
+ */
+static void
+ieee80211_radius_detach(struct eapolcom *ec)
+{
+	struct radiuscom *rc = ec->ec_radius;
+
+	if (rc != NULL) {
+		printk(KERN_INFO "802.1x radius client stopping\n");
+		ec->ec_radius = NULL;
+		/* restore original methods */
+		rc->rc_ec->ec_node_alloc = rc->rc_node_alloc;
+		rc->rc_ec->ec_node_free = rc->rc_node_free;
+		rc->rc_ec->ec_node_reset = rc->rc_node_reset;
+		/*
+		 * If we started the receiver thread then just signal
+		 * it and let it complete the cleanup work when it's
+		 * dropped out of the receive loop.  Otherwise we must
+		 * do the cleanup directly.
+		 *
+		 * XXX wrong, must wait to remove module reference
+		 */
+		if (rc->rc_pid != -1)
+			kill_proc(rc->rc_pid, SIGKILL, 1);
+		else
+			radius_cleanup(rc);
+
+		_MOD_DEC_USE(THIS_MODULE);
+	}
+}
+
+/*
+ * Attach a radius client to an authenticator.  We setup
+ * private state, override the node allocation alloc/free
+ * methods, and start a thread to receive messages from
+ * the radius server.  We also setup callsbacks used by
+ * the 802.1x backend state machine.
+ */
+static int
+ieee80211_radius_attach(struct eapolcom *ec)
+{
+	struct radiuscom *rc;
+	int error, secretlen;
+
+	_MOD_INC_USE(THIS_MODULE, return FALSE);
+
+	/* XXX require master key be setup? */
+	secretlen = strlen(radius_secret);
+	if (secretlen == 0) {
+		printf("%s: no radius server shared secret setup", __func__);
+		eapolstats.rs_nosecret++;
+		_MOD_DEC_USE(THIS_MODULE);
+		return FALSE;
+	}
+	MALLOC(rc, struct radiuscom *, sizeof(struct radiuscom),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (rc == NULL) {
+		printf("%s: unable to allocate memory for client state\n",
+			__func__);
+		eapolstats.rs_nomem++;
+		_MOD_DEC_USE(THIS_MODULE);
+		return FALSE;
+	}
+	LIST_INIT(&rc->rc_replies);
+	rc->rc_pid = -1;
+	MALLOC(rc->rc_secret, u_int8_t*, radius_secretlen, M_DEVBUF, M_NOWAIT);
+	if (rc->rc_secret == NULL) {
+		eapolstats.rs_nomem++;
+		printf("%s: unable to allocate memory for shared secret\n",
+			__func__);
+		goto bad;
+	}
+	memcpy(rc->rc_secret, radius_secret, radius_secretlen);
+	rc->rc_secretlen = radius_secretlen;
+
+	rc->rc_local = radius_clientaddr;
+	error = sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &rc->rc_sock);
+	if (error < 0) {
+		printf("%s: cannot create socket, error %u\n",
+			__func__, -error);
+		eapolstats.rs_nosocket++;
+		goto bad;
+	}
+	/*
+	 * Force atomic allocation since we sometimes send
+	 * messages from interrupt level.
+	 */
+/* XXX 2.6.0 is just a guess */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	rc->rc_sock->sk->allocation = GFP_ATOMIC;
+#else
+	rc->rc_sock->sk->sk_allocation = GFP_ATOMIC;
+#endif
+	error = (*rc->rc_sock->ops->bind)(rc->rc_sock,
+	    (struct sockaddr *)&rc->rc_local, sizeof(rc->rc_local));
+	if (error < 0) {
+		/* XXX */
+		printf("%s: cannot bind local address, error %u\n",
+			__func__, -error);
+		eapolstats.rs_cannotbind++;
+		goto bad;
+	}
+	/* XXX check result */
+
+	rc->rc_server = radius_serveraddr;
+	rc->rc_pid = kernel_thread(radiusd, rc, CLONE_KERNEL);
+	if (rc->rc_pid < 0) {
+		printf("%s: cannot start radiusd thread; error %d\n",
+			__func__, -rc->rc_pid);
+		eapolstats.rs_nothread++;
+		goto bad;
+	}
+	rc->rc_identity_input = radius_identity_input;
+	rc->rc_txreq = txReq;
+	rc->rc_sendsrvr = sendRespToServer;
+	rc->rc_txkey = txKey;
+	/*
+	 * Override node management methods so we have the
+	 * extra space needed for the radius client and so
+	 * we properly manage our private state.
+	 */
+	rc->rc_node_alloc = ec->ec_node_alloc;
+	ec->ec_node_alloc = radius_node_alloc;
+	rc->rc_node_free = ec->ec_node_free;
+	ec->ec_node_free = radius_node_free;
+	rc->rc_node_reset = ec->ec_node_reset;
+	ec->ec_node_reset = radius_node_reset;
+
+	rc->rc_ec = ec;
+	ec->ec_radius = rc;
+
+	printk(KERN_INFO "802.1x radius client started\n");
+	return TRUE;
+bad:
+	ieee80211_radius_detach(ec);		/* NB: does _MOD_DEC_USE */
+	return FALSE;
+}
+
+/*
+ * Handle a sysctl that read/writes an IP address.
+ */
+static int
+radius_sysctl_ipaddr(ctl_table *ctl, int write, struct file *filp,
+	void *buf, size_t *lenp)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *) ctl->data;
+
+	if (write) {
+		u_int a1, a2, a3, a4;
+		if (sscanf(buf, "%d.%d.%d.%d", &a1, &a2, &a3, &a4) != 4)
+			return -EINVAL;
+		sin->sin_addr.s_addr = htonl(
+			  ((a1 & 0xff) << 24) | ((a2 & 0xff) << 16)
+			| ((a3 & 0xff) <<  8) | ((a4 & 0xff) <<  0));
+	} else if (*lenp && filp->f_pos == 0) {
+		u_int32_t v = ntohl(sin->sin_addr.s_addr);
+		*lenp = snprintf(buf, *lenp, "%d.%d.%d.%d\n",
+			(v >> 24) & 0xff, (v >> 16) & 0xff,
+			(v >>  8) & 0xff, (v >>  0) & 0xff);
+		filp->f_pos += *lenp;
+	} else {
+		*lenp = 0;
+	}
+	return 0;
+}
+
+/*
+ * Handle a sysctl that reads/writes an IP port number.
+ */
+static int
+radius_sysctl_ipport(ctl_table *ctl, int write, struct file *filp,
+	void *buf, size_t *lenp)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *) ctl->data;
+
+	if (write) {
+		u_int a1;
+		if (sscanf(buf, "%d", &a1) != 1)
+			return -EINVAL;
+		sin->sin_port = htons(a1 & 0xffff);
+	} else if (*lenp && filp->f_pos == 0) {
+		*lenp = snprintf(buf, *lenp, "%d\n", ntohs(sin->sin_port));
+		filp->f_pos += *lenp;
+	} else {
+		*lenp = 0;
+	}
+	return 0;
+}
+
+/*
+ * Handle a sysctl to read/write the radius server shared secret.
+ */
+static int
+radius_sysctl_secret(ctl_table *ctl, int write, struct file *filp,
+	void *buf, size_t *lenp)
+{
+	int len = *lenp;
+
+	if (write) {
+		if (len > RAD_MAX_SECRET)	/* should not happen */
+			len = RAD_MAX_SECRET;
+		memset(radius_secret, 0, sizeof(radius_secret));
+		memcpy(radius_secret, buf, len);
+		radius_secret[RAD_MAX_SECRET] = '\0';
+		radius_secretlen = len;
+	} else if (*lenp && filp->f_pos == 0) {
+		if (len >= radius_secretlen)
+			len = strlen(radius_secret);
+		memcpy(buf, radius_secret, len);
+		*lenp = len;
+		filp->f_pos += *lenp;
+	} else {
+		*lenp = 0;
+	}
+	return 0;
+}
+
+#define	CTL_AUTO	-2	/* cannot be CTL_ANY or CTL_NONE */
+
+static ctl_table radius_sysctls[] = {
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "serveraddr",
+	  .data		= &radius_serveraddr,
+	  .mode		= 0644,
+	  .proc_handler	= radius_sysctl_ipaddr
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "serverport",
+	  .data		= &radius_serveraddr,
+	  .mode		= 0644,
+	  .proc_handler	= radius_sysctl_ipport
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "clientaddr",
+	  .data		= &radius_clientaddr,
+	  .mode		= 0644,
+	  .proc_handler	= radius_sysctl_ipaddr
+	},
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "secret",
+	  .maxlen	= RAD_MAX_SECRET,
+	  .mode		= 0600,
+	  .proc_handler	= radius_sysctl_secret
+	},
+	{ 0 }
+};
+static ctl_table radius_table[] = {
+	{ .ctl_name	= CTL_AUTO,
+	  .procname	= "radius",
+	  .mode		= 0555,
+	  .child	= radius_sysctls
+	}, { 0 }
+};
+static ctl_table dot1x_table[] = {
+	{ .ctl_name	= NET_8021X,
+	  .procname	= "8021x",
+	  .mode		= 0555,
+	  .child	= radius_table
+	}, { 0 }
+};
+static ctl_table net_table[] = {
+#ifdef CONFIG_PROC_FS
+	{ .ctl_name	= CTL_NET,
+	  .procname	= "net",
+	  .mode		= 0555,
+	  .child	= dot1x_table
+	},
+#endif /* CONFIG_PROC_FS */
+	{ 0 }
+};
+
+static struct ctl_table_header *radius_sys;
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: radius backend ");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static const struct ieee80211_authenticator_backend radius = {
+	.iab_name	= "radius",
+	.iab_attach	= ieee80211_radius_attach,
+	.iab_detach	= ieee80211_radius_detach,
+};
+
+static int __init
+init_ieee80211_radius(void)
+{
+#ifndef __linux__
+	radius_clientaddr.sin_len = sizeof(radius_clientaddr);
+#endif
+	radius_clientaddr.sin_family = AF_INET;
+#ifndef __linux__
+	radius_serveraddr.sin_len = sizeof(radius_serveraddr);
+#endif
+	radius_serveraddr.sin_family = AF_INET;
+	radius_serveraddr.sin_port = htons(1812);	/* default port */
+	radius_sys = register_sysctl_table(net_table, 0);
+
+	ieee80211_authenticator_backend_register(&radius);
+	return 0;
+}
+module_init(init_ieee80211_radius);
+
+static void __exit
+exit_ieee80211_radius(void)
+{
+	if (radius_sys)
+		unregister_sysctl_table(radius_sys);
+
+	ieee80211_authenticator_backend_unregister(&radius);
+}
+module_exit(exit_ieee80211_radius);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.h	2005-02-24 13:06:17.398117104 -0800
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _NET80211_RADIUS_H_
+#define _NET80211_RADIUS_H_
+
+/*
+ * IEEE 802.1x radius client support.
+ *
+ * Radius client support for the 802.1x authenticator.
+ * This could be a user process that communicates with
+ * the authenticator through messages; but for now its
+ * in the kernel where it's easy to share state with the
+ * authenticator state machine.  We structure this as a
+ * module so it is not always resident.  Also we instantiate
+ * this support only as needed so the cost of having it
+ * is only incurred when in use (e.g. not for stations).
+ */
+
+struct eapol_radius_key {
+	u_int8_t	rk_data[64];	/* decoded key length+data */
+#define	rk_len	rk_data[0]		/* decoded key length */
+	u_int16_t	rk_salt;	/* salt from server */
+} __attribute__((__packed__));
+
+struct eapol_auth_radius_node {
+	struct eapol_auth_node	ern_base;
+	LIST_ENTRY(eapol_auth_radius_node) ern_next;	/* reply list */
+	struct sk_buff		*ern_msg;	/* supplicant reply */
+	u_int8_t		*ern_radmsg;	/* radius server message */
+	u_int16_t		ern_radmsglen;	/* length of server message */
+	u_int8_t		ern_onreply;	/* on reply pending list */
+	u_int8_t		ern_reqauth[16];/* request authenticator */
+	u_int8_t		ern_state[255];
+	u_int8_t		ern_statelen;
+	u_int8_t		ern_class[255];
+	u_int8_t		ern_classlen;
+	struct eapol_radius_key	ern_txkey;
+	struct eapol_radius_key	ern_rxkey;
+};
+#define	EAPOL_RADIUSNODE(_x)	((struct eapol_auth_radius_node *)(_x))
+
+#define	ern_ic		ern_base.ean_ic
+#define	ern_node	ern_base.ean_node
+
+#define	RAD_MAXMSG	4096			/* max message size */
+
+struct crypto_tfm;
+
+struct radiuscom {
+	struct eapolcom	*rc_ec;			/* back reference */
+	struct socket	*rc_sock;		/* open socket */
+	pid_t		rc_pid;			/* pid of client thread */
+	struct sockaddr_in rc_server;		/* server's address */
+	struct sockaddr_in rc_local;		/* local address */
+	u_int8_t	*rc_secret;		/* shared secret */
+	u_int		rc_secretlen;		/* length of shared secret */
+	u_int8_t	rc_buf[RAD_MAXMSG];	/* recv thread msg buffer */
+	ATH_LIST_HEAD(, eapol_auth_radius_node) rc_replies;
+	/* saved copies of eapolcom methods */
+	struct eapol_auth_node *(*rc_node_alloc)(struct eapolcom *);
+	void		(*rc_node_free)(struct eapol_auth_node *);
+	void		(*rc_node_reset)(struct eapol_auth_node *);
+	/* callbacks used by the main .1x code */
+	void		(*rc_identity_input)(struct eapol_auth_node *,
+				struct sk_buff *);
+	void		(*rc_txreq)(struct eapol_auth_node *);
+	void		(*rc_sendsrvr)(struct eapol_auth_node *);
+	void		(*rc_txkey)(struct eapol_auth_node *);
+};
+#endif /* _NET80211_RADIUS_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_var.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_var.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_var.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_var.h	2005-02-24 13:06:17.406115888 -0800
@@ -0,0 +1,442 @@
+/*	$NetBSD: ieee80211_var.h,v 1.4 2003/10/13 04:27:40 dyoung Exp $	*/
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.11 2004/01/15 08:44:27 onoe Exp $
+ */
+#ifndef _NET80211_IEEE80211_VAR_H_
+#define _NET80211_IEEE80211_VAR_H_
+
+/*
+ * Definitions for IEEE 802.11 drivers.
+ */
+
+/* NB: portability glue must go first */
+#ifdef __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#elif __FreeBSD__
+#include <net80211/ieee80211_freebsd.h>
+#elif __linux__
+#include <net80211/ieee80211_linux.h>
+#else
+#error	"No support for your operating system!"
+#endif
+
+#include <sys/queue.h>
+
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h>		/* for ieee80211_stats */
+#include <net80211/ieee80211_node.h>
+#include <net80211/ieee80211_proto.h>
+
+#define	IEEE80211_CHAN_MAX	255
+#define	IEEE80211_CHAN_BYTES	howmany(IEEE80211_CHAN_MAX, NBBY)
+#define	IEEE80211_CHAN_ANY	0xffff	/* token for ``any channel'' */
+#define	IEEE80211_CHAN_ANYC \
+	((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
+
+#define	IEEE80211_TXPOWER_MAX	100	/* .5 dbM (XXX units?) */
+#define	IEEE80211_TXPOWER_MIN	0	/* kill radio */
+
+enum ieee80211_phytype {
+	IEEE80211_T_DS,			/* direct sequence spread spectrum */
+	IEEE80211_T_FH,			/* frequency hopping */
+	IEEE80211_T_OFDM,		/* frequency division multiplexing */
+	IEEE80211_T_TURBO,		/* high rate OFDM, aka turbo mode */
+};
+#define	IEEE80211_T_CCK	IEEE80211_T_DS	/* more common nomenclature */
+
+/* XXX not really a mode; there are really multiple PHY's */
+enum ieee80211_phymode {
+	IEEE80211_MODE_AUTO	= 0,	/* autoselect */
+	IEEE80211_MODE_11A	= 1,	/* 5GHz, OFDM */
+	IEEE80211_MODE_11B	= 2,	/* 2GHz, CCK */
+	IEEE80211_MODE_11G	= 3,	/* 2GHz, OFDM */
+	IEEE80211_MODE_FH	= 4,	/* 2GHz, GFSK */
+	IEEE80211_MODE_TURBO	= 5,	/* 5GHz, OFDM, 2x clock */
+};
+#define	IEEE80211_MODE_MAX	(IEEE80211_MODE_TURBO+1)
+
+enum ieee80211_opmode {
+	IEEE80211_M_STA		= 1,	/* infrastructure station */
+	IEEE80211_M_IBSS 	= 0,	/* IBSS (adhoc) station */
+	IEEE80211_M_AHDEMO	= 3,	/* Old lucent compatible adhoc demo */
+	IEEE80211_M_HOSTAP	= 6,	/* Software Access Point */
+	IEEE80211_M_MONITOR	= 8	/* Monitor mode */
+};
+
+/*
+ * 802.11g protection mode.
+ */
+enum ieee80211_protmode {
+	IEEE80211_PROT_NONE	= 0,	/* no protection */
+	IEEE80211_PROT_CTSONLY	= 1,	/* CTS to self */
+	IEEE80211_PROT_RTSCTS	= 2,	/* RTS-CTS */
+};
+
+/*
+ * Roaming mode is effectively who controls the operation
+ * of the 802.11 state machine when operating as a station.
+ * State transitions are controlled either by the driver
+ * (typically when management frames are processed by the
+ * hardware/firmware), the host (auto/normal operation of
+ * the 802.11 layer), or explicitly through ioctl requests
+ * when applications like wpa_supplicant want control.
+ */
+enum ieee80211_roamingmode {
+	IEEE80211_ROAMING_DEVICE= 0,	/* driver/hardware control */
+	IEEE80211_ROAMING_AUTO	= 1,	/* 802.11 layer control */
+	IEEE80211_ROAMING_MANUAL= 2,	/* application control */
+};
+
+/*
+ * Channels are specified by frequency and attributes.
+ */
+struct ieee80211_channel {
+	u_int16_t	ic_freq;	/* setting in Mhz */
+	u_int16_t	ic_flags;	/* see below */
+};
+
+/* bits 0-3 are for private use by drivers */
+/* channel attributes */
+#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */
+#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */
+#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */
+#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */
+#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */
+#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
+#define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
+#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+
+/*
+ * Useful combinations of channel characteristics.
+ */
+#define	IEEE80211_CHAN_FHSS \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
+#define	IEEE80211_CHAN_A \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
+#define	IEEE80211_CHAN_B \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
+#define	IEEE80211_CHAN_PUREG \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
+#define	IEEE80211_CHAN_G \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
+#define	IEEE80211_CHAN_T \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+
+#define	IEEE80211_IS_CHAN_FHSS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
+#define	IEEE80211_IS_CHAN_A(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
+#define	IEEE80211_IS_CHAN_B(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
+#define	IEEE80211_IS_CHAN_PUREG(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
+#define	IEEE80211_IS_CHAN_G(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
+#define	IEEE80211_IS_CHAN_T(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+
+#define	IEEE80211_IS_CHAN_2GHZ(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
+#define	IEEE80211_IS_CHAN_5GHZ(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
+#define	IEEE80211_IS_CHAN_OFDM(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0)
+#define	IEEE80211_IS_CHAN_CCK(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0)
+#define	IEEE80211_IS_CHAN_GFSK(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
+
+/* ni_chan encoding for FH phy */
+#define	IEEE80211_FH_CHANMOD	80
+#define	IEEE80211_FH_CHAN(set,pat)	(((set)-1)*IEEE80211_FH_CHANMOD+(pat))
+#define	IEEE80211_FH_CHANSET(chan)	((chan)/IEEE80211_FH_CHANMOD+1)
+#define	IEEE80211_FH_CHANPAT(chan)	((chan)%IEEE80211_FH_CHANMOD)
+
+#define	IEEE80211_PS_SLEEP	0x1	/* STA is in power saving mode */
+
+#define	IEEE80211_PS_MAX_QUEUE	50	/* maximum saved packets */
+
+struct vlan_group;
+struct eapolcom;
+struct ieee80211_aclator;
+
+struct ieee80211com {
+	SLIST_ENTRY(ieee80211com) ic_next;
+	struct net_device	*ic_dev;	/* associated device */
+	struct net_device_stats	*ic_devstats;	/* interface statistics */
+	u_int32_t		msg_enable;	/* interface message flags */
+	struct timer_list	ic_slowtimo;	/* mgmt/inactivity timer */
+	struct ieee80211_stats	ic_stats;	/* private statistics */
+#ifdef CONFIG_SYSCTL
+	char			ic_procname[12];/* e.g. wlan%d */
+	struct ctl_table_header	*ic_sysctl_header;
+	struct ctl_table	*ic_sysctls;
+#endif
+	struct vlan_group	*ic_vlgrp;	/* vlan group state */
+
+	int			(*ic_init)(struct net_device *);
+	int			(*ic_reset)(struct net_device *);
+	int			(*ic_mgtstart)(struct ieee80211com *,
+					struct sk_buff *);
+	void			(*ic_recv_mgmt)(struct ieee80211com *,
+				    struct sk_buff *, struct ieee80211_node *,
+				    int, int, u_int32_t);
+	int			(*ic_send_mgmt)(struct ieee80211com *,
+				    struct ieee80211_node *, int, int);
+	int			(*ic_newstate)(struct ieee80211com *,
+				    enum ieee80211_state, int);
+	void			(*ic_newassoc)(struct ieee80211com *,
+				    struct ieee80211_node *, int);
+	void			(*ic_updateslot)(struct net_device *);
+	int			(*ic_set_tim)(struct ieee80211com *, int, int);
+	u_int8_t		ic_myaddr[IEEE80211_ADDR_LEN];
+	struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+	struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
+	u_int8_t		ic_chan_avail[IEEE80211_CHAN_BYTES];
+	u_int8_t		ic_chan_active[IEEE80211_CHAN_BYTES];
+	u_int8_t		ic_chan_scan[IEEE80211_CHAN_BYTES];
+	u_int32_t		ic_flags;	/* state flags */
+	u_int32_t		ic_caps;	/* capabilities */
+	u_int16_t		ic_modecaps;	/* set of mode capabilities */
+	u_int16_t		ic_curmode;	/* current mode */
+	enum ieee80211_phytype	ic_phytype;	/* XXX wrong for multi-mode */
+	enum ieee80211_opmode	ic_opmode;	/* operation mode */
+	enum ieee80211_state	ic_state;	/* 802.11 state */
+	enum ieee80211_protmode	ic_protmode;	/* 802.11g protection mode */
+	enum ieee80211_roamingmode ic_roaming;	/* roaming mode */
+	u_int32_t		*ic_aid_bitmap;
+	u_int16_t		ic_max_aid;
+	struct ifmedia		ic_media;	/* interface media config */
+#if NBPFILTER > 0
+	struct bpf_if		*ic_rawbpf;	/* packet filter structure */
+#endif
+	struct ieee80211_node	*ic_bss;	/* information for this node */
+	struct ieee80211_channel *ic_ibss_chan;
+	int			ic_fixed_rate;	/* index to ic_sup_rates[] */
+	u_int16_t		ic_rtsthreshold;
+	u_int16_t		ic_fragthreshold;
+	ieee80211_node_lock_t	ic_nodelock;	/* on node table */
+	u_int			ic_scangen;	/* gen# for timeout scan */
+	struct ieee80211_node	*(*ic_node_alloc)(struct ieee80211com *);
+	void			(*ic_node_free)(struct ieee80211com *,
+					struct ieee80211_node *);
+	void			(*ic_node_cleanup)(struct ieee80211com *,
+					struct ieee80211_node *);
+	void			(*ic_node_copy)(struct ieee80211com *,
+					struct ieee80211_node *,
+					const struct ieee80211_node *);
+	u_int8_t		(*ic_node_getrssi)(struct ieee80211com *,
+					struct ieee80211_node *);
+	TAILQ_HEAD(, ieee80211_node) ic_node;	/* information of all nodes */
+	ATH_LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE];
+	u_int16_t		ic_lintval;	/* listen interval */
+	u_int16_t		ic_holdover;	/* PM hold over duration */
+	u_int16_t		ic_txmin;	/* min tx retry count */
+	u_int16_t		ic_txmax;	/* max tx retry count */
+	u_int16_t		ic_txlifetime;	/* tx lifetime */
+	u_int16_t		ic_bmisstimeout;/* beacon miss threshold (ms) */
+	u_int16_t		ic_nonerpsta;	/* # non-ERP stations */
+	u_int16_t		ic_longslotsta;	/* # long slot time stations */
+	int			ic_mgt_timer;	/* mgmt timeout */
+	int			ic_inact_timer;	/* inactivity timer wait */
+	int			ic_nicknamelen;
+	u_int8_t		ic_nickname[IEEE80211_NWID_LEN];
+	int			ic_des_esslen;
+	u_int8_t		ic_des_essid[IEEE80211_NWID_LEN];
+	struct ieee80211_channel *ic_des_chan;	/* desired channel */
+	u_int8_t		ic_des_bssid[IEEE80211_ADDR_LEN];
+	void			*ic_opt_ie;	/* user-specified IE's */
+	u_int16_t		ic_opt_ie_len;	/* length of ni_opt_ie */
+	/*
+	 * Inactivity timer settings for nodes.
+	 */
+	int			ic_inact_init;	/* initial setting */
+	int			ic_inact_auth;	/* assoc but not auth setting */
+	int			ic_inact_run;	/* authorized setting */
+
+	/*
+	 * Cipher state/configuration.
+	 */
+	struct ieee80211_crypto_state ic_crypto;
+#define	ic_nw_keys	ic_crypto.cs_nw_keys	/* XXX compatibility */
+#define	ic_def_txkey	ic_crypto.cs_def_txkey	/* XXX compatibility */
+
+	/*
+	 * 802.1x glue.  When an authenticator attaches it
+	 * fills in this section.  We assume that when ic_ec
+	 * is setup that the methods are safe to call.
+	 */
+	const struct ieee80211_authenticator *ic_auth;
+	struct eapolcom		*ic_ec;	
+
+	/*
+	 * Access control glue.  When a control agent attaches
+	 * it fills in this section.  We assume that when ic_ac
+	 * is setup that the methods are safe to call.
+	 */
+	const struct ieee80211_aclator *ic_acl;
+	void			*ic_as;
+};
+
+#define	IEEE80211_ADDR_EQ(a1,a2)	(memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
+#define	IEEE80211_ADDR_COPY(dst,src)	memcpy(dst,src,IEEE80211_ADDR_LEN)
+
+/* ic_flags */
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */
+#define	IEEE80211_F_PRIVACY	0x00000010	/* CONF: privacy enabled */
+#define	IEEE80211_F_ASCAN	0x00000100	/* STATUS: active scan */
+#define	IEEE80211_F_SIBSS	0x00000200	/* STATUS: start IBSS */
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */
+#define	IEEE80211_F_SHSLOT	0x00000400	/* STATUS: use short slot time*/
+#define	IEEE80211_F_PMGTON	0x00000800	/* CONF: Power mgmt enable */
+#define	IEEE80211_F_DESBSSID	0x00001000	/* CONF: des_bssid is set */
+#define	IEEE80211_F_WME		0x00002000	/* CONF: enable WME use */
+#define	IEEE80211_F_ROAMING	0x00004000	/* CONF: roaming enabled */
+#define	IEEE80211_F_SWRETRY	0x00008000	/* CONF: sw tx retry enabled */
+#define IEEE80211_F_TXPOW_FIXED	0x00010000	/* TX Power: fixed rate */
+#define	IEEE80211_F_IBSSON	0x00020000	/* CONF: IBSS creation enable */
+#define	IEEE80211_F_SHPREAMBLE	0x00040000	/* STATUS: use short preamble */
+#define	IEEE80211_F_DATAPAD	0x00080000	/* CONF: do alignment pad */
+#define	IEEE80211_F_USEPROT	0x00100000	/* STATUS: protection enabled */
+#define	IEEE80211_F_USEBARKER	0x00200000	/* STATUS: use barker preamble*/
+#define	IEEE80211_F_TIMUPDATE	0x00400000	/* STATUS: update beacon tim */
+#define	IEEE80211_F_WPA1	0x00800000	/* CONF: WPA enabled */
+#define	IEEE80211_F_WPA2	0x01000000	/* CONF: WPA2 enabled */
+#define	IEEE80211_F_WPA		0x01800000	/* CONF: WPA/WPA2 enabled */
+#define	IEEE80211_F_DROPUNENC	0x02000000	/* CONF: drop unencrypted */
+#define	IEEE80211_F_COUNTERM	0x04000000	/* CONF: TKIP countermeasures */
+#define	IEEE80211_F_HIDESSID	0x08000000	/* CONF: hide SSID in beacon */
+#define	IEEE80211_F_NOBRIDGE	0x10000000	/* CONF: dis. internal bridge */
+
+/* ic_caps */
+#define	IEEE80211_C_WEP		0x00000001	/* CAPABILITY: WEP available */
+#define	IEEE80211_C_TKIP	0x00000002	/* CAPABILITY: TKIP available */
+#define	IEEE80211_C_AES		0x00000004	/* CAPABILITY: AES OCB avail */
+#define	IEEE80211_C_AES_CCM	0x00000008	/* CAPABILITY: AES CCM avail */
+#define	IEEE80211_C_CKIP	0x00000020	/* CAPABILITY: CKIP available */
+#define	IEEE80211_C_IBSS	0x00000100	/* CAPABILITY: IBSS available */
+#define	IEEE80211_C_PMGT	0x00000200	/* CAPABILITY: Power mgmt */
+#define	IEEE80211_C_HOSTAP	0x00000400	/* CAPABILITY: HOSTAP avail */
+#define	IEEE80211_C_AHDEMO	0x00000800	/* CAPABILITY: Old Adhoc Demo */
+#define	IEEE80211_C_SWRETRY	0x00001000	/* CAPABILITY: sw tx retry */
+#define	IEEE80211_C_TXPMGT	0x00002000	/* CAPABILITY: tx power mgmt */
+#define	IEEE80211_C_SHSLOT	0x00004000	/* CAPABILITY: short slottime */
+#define	IEEE80211_C_SHPREAMBLE	0x00008000	/* CAPABILITY: short preamble */
+#define	IEEE80211_C_MONITOR	0x00010000	/* CAPABILITY: monitor mode */
+#define	IEEE80211_C_TKIPMIC	0x00020000	/* CAPABILITY: TKIP MIC avail */
+/* XXX protection/barker? */
+
+#define	IEEE80211_C_CRYPTO	0x0000002f	/* CAPABILITY: crypto alg's */
+
+int	ieee80211_ifattach(struct ieee80211com *);
+void	ieee80211_ifdetach(struct ieee80211com *);
+void	ieee80211_announce(struct ieee80211com *);
+void	ieee80211_media_init(struct ieee80211com *, ifm_change_cb_t, ifm_stat_cb_t);
+struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]);
+int	ieee80211_media_change(struct net_device *);
+void	ieee80211_media_status(struct net_device *, struct ifmediareq *);
+int	ieee80211_ioctl(struct ieee80211com *, struct ifreq *, int);
+int	ieee80211_rate2media(struct ieee80211com *, int,
+		enum ieee80211_phymode);
+int	ieee80211_media2rate(int);
+u_int	ieee80211_mhz2ieee(u_int, u_int);
+u_int	ieee80211_chan2ieee(struct ieee80211com *, struct ieee80211_channel *);
+u_int	ieee80211_ieee2mhz(u_int, u_int);
+int	ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
+void	ieee80211_reset_erp(struct ieee80211com *);
+enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,
+		struct ieee80211_channel *);
+
+/* 
+ * Key update synchronization methods.  XXX should not be visible.
+ */
+static inline void
+ieee80211_key_update_begin(struct ieee80211com *ic)
+{
+	ic->ic_crypto.cs_key_update_begin(ic);
+}
+static inline void
+ieee80211_key_update_end(struct ieee80211com *ic)
+{
+	ic->ic_crypto.cs_key_update_end(ic);
+}
+
+#define	IEEE80211_MSG_DEBUG	0x40000000	/* IFF_DEBUG equivalent */
+#define	IEEE80211_MSG_DUMPPKTS	0x20000000	/* IFF_LINK2 equivalant */
+#define	IEEE80211_MSG_CRYPTO	0x10000000	/* crypto work */
+#define	IEEE80211_MSG_INPUT	0x08000000	/* input handling */
+#define	IEEE80211_MSG_XRATE	0x04000000	/* rate set handling */
+#define	IEEE80211_MSG_ELEMID	0x02000000	/* element id parsing */
+#define	IEEE80211_MSG_NODE	0x01000000	/* node handling */
+#define	IEEE80211_MSG_ASSOC	0x00800000	/* association handling */
+#define	IEEE80211_MSG_AUTH	0x00400000	/* authentication handling */
+#define	IEEE80211_MSG_SCAN	0x00200000	/* scanning */
+#define	IEEE80211_MSG_OUTPUT	0x00100000	/* output handling */
+#define	IEEE80211_MSG_STATE	0x00080000	/* state machine */
+#define	IEEE80211_MSG_POWER	0x00040000	/* power save handling */
+#define	IEEE80211_MSG_DOT1X	0x00020000	/* 802.1x authenticator */
+#define	IEEE80211_MSG_DOT1XSM	0x00010000	/* 802.1x state machine */
+#define	IEEE80211_MSG_RADIUS	0x00008000	/* 802.1x radius client */
+#define	IEEE80211_MSG_RADDUMP	0x00004000	/* dump 802.1x radius packets */
+#define	IEEE80211_MSG_RADKEYS	0x00002000	/* dump 802.1x keys */
+#define	IEEE80211_MSG_WPA	0x00001000	/* WPA/RSN protocol */
+#define	IEEE80211_MSG_ACL	0x00000800	/* ACL handling */
+
+#define	IEEE80211_MSG_ANY	0xffffffff	/* anything */
+
+#define	IEEE80211_DEBUG
+#ifdef IEEE80211_DEBUG
+#define	IEEE80211_DPRINTF(_ic, _m, _args) do {		\
+	if (_ic->msg_enable & (_m))			\
+		printk _args;				\
+} while (0)
+#define	ieee80211_msg_debug(_ic) \
+	((_ic)->msg_enable & IEEE80211_MSG_DEBUG)
+#define	ieee80211_msg_dumppkts(_ic) \
+	((_ic)->msg_enable & IEEE80211_MSG_DUMPPKTS)
+#define	ieee80211_msg_input(_ic) \
+	((_ic)->msg_enable & IEEE80211_MSG_INPUT)
+#define	ieee80211_msg_radius(_ic) \
+	((_ic)->msg_enable & IEEE80211_MSG_RADIUS)
+#define	ieee80211_msg_dumpradius(_ic) \
+	((_ic)->msg_enable & IEEE80211_MSG_RADDUMP)
+#define	ieee80211_msg_dumpradkeys(_ic) \
+	((_ic)->msg_enable & IEEE80211_MSG_RADKEYS)
+#define	ieee80211_msg_scan(_ic) \
+	((_ic)->msg_enable & IEEE80211_MSG_SCAN)
+#else
+#define	IEEE80211_DPRINTF(_ic, _fmt, ...)
+#endif
+
+#endif /* _NET80211_IEEE80211_VAR_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_wireless.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_wireless.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_wireless.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_wireless.c	2005-02-24 13:06:17.408115584 -0800
@@ -0,0 +1,2206 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: ieee80211_wireless.c,v 1.19 2005/02/16 16:09:03 samleffler Exp $
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+#ifdef CONFIG_NET_WIRELESS
+/*
+ * Wireless extensions support for 802.11 common code.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/utsname.h>
+#include <linux/if_arp.h>		/* XXX for ARPHRD_ETHER */
+#include <net/iw_handler.h>
+
+#include <asm/uaccess.h>
+
+#include "if_media.h"
+
+#include <net80211/ieee80211_var.h>
+
+#define	IS_UP(_dev) \
+	(((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+#define	IS_UP_AUTO(_ic) \
+	(IS_UP((_ic)->ic_dev) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
+
+/*
+ * Units are in db above the noise floor. That means the
+ * rssi values reported in the tx/rx descriptors in the
+ * driver are the SNR expressed in db.
+ *
+ * If you assume that the noise floor is -95, which is an
+ * excellent assumption 99.5 % of the time, then you can
+ * derive the absolute signal level (i.e. -95 + rssi). 
+ * There are some other slight factors to take into account
+ * depending on whether the rssi measurement is from 11b,
+ * 11g, or 11a.   These differences are at most 2db and
+ * can be documented.
+ *
+ * NB: various calculations are based on the orinoco/wavelan
+ *     drivers for compatibility
+ */
+static void
+set_quality(struct iw_quality *iq, u_int rssi)
+{
+	iq->qual = rssi;
+	/* NB: max is 94 because noise is hardcoded to 161 */
+	if (iq->qual > 94)
+		iq->qual = 94;
+
+	iq->noise = 161;		/* -95dBm */
+	iq->level = iq->noise + iq->qual;
+	iq->updated = 7;
+}
+
+void
+ieee80211_iw_getstats(struct ieee80211com *ic, struct iw_statistics *is)
+{
+#define	NZ(x)	((x) ? (x) : 1)
+
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_STA:
+		/* use stats from associated ap */
+		if (ic->ic_bss)
+			set_quality(&is->qual,
+				(*ic->ic_node_getrssi)(ic, ic->ic_bss));
+		else
+			set_quality(&is->qual, 0);
+		break;
+	case IEEE80211_M_IBSS:
+	case IEEE80211_M_AHDEMO:
+	case IEEE80211_M_HOSTAP: {
+		struct ieee80211_node* ni;
+		u_int32_t rssi_samples = 0;
+		u_int32_t rssi_total = 0;
+
+		/* average stats from all nodes */
+		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+			rssi_samples++;
+			rssi_total += (*ic->ic_node_getrssi)(ic, ni);
+		}
+		set_quality(&is->qual, rssi_total / NZ(rssi_samples));
+		break;
+	}
+	case IEEE80211_M_MONITOR:
+	default:
+		/* no stats */
+		set_quality(&is->qual, 0);
+		break;
+	}
+	is->status = ic->ic_state;
+	is->discard.nwid = ic->ic_stats.is_rx_wrongbss
+			 + ic->ic_stats.is_rx_ssidmismatch;
+	is->discard.code = ic->ic_stats.is_rx_wepfail
+			 + ic->ic_stats.is_rx_decryptcrc;
+	is->discard.fragment = 0;
+	is->discard.retries = 0;
+	is->discard.misc = 0;
+
+	is->miss.beacon = 0;
+#undef NZ
+}
+EXPORT_SYMBOL(ieee80211_iw_getstats);
+
+int
+ieee80211_ioctl_giwname(struct ieee80211com *ic,
+		   struct iw_request_info *info,
+		   char *name, char *extra)
+{
+
+	/* XXX should use media status but IFM_AUTO case gets tricky */
+	switch (ic->ic_curmode) {
+	case IEEE80211_MODE_11A:
+		strncpy(name, "IEEE 802.11a", IFNAMSIZ);
+		break;
+	case IEEE80211_MODE_11B:
+		strncpy(name, "IEEE 802.11b", IFNAMSIZ);
+		break;
+	case IEEE80211_MODE_11G:
+		strncpy(name, "IEEE 802.11g", IFNAMSIZ);
+		break;
+	case IEEE80211_MODE_TURBO:
+		strncpy(name, "IEEE 802.11-TURBO", IFNAMSIZ);
+		break;
+	default:
+		strncpy(name, "IEEE 802.11", IFNAMSIZ);
+		break;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwname);
+
+/*
+ * Get a key index from a request.  If nothing is
+ * specified in the request we use the current xmit
+ * key index.  Otherwise we just convert the index
+ * to be base zero.
+ */
+static int
+getiwkeyix(struct ieee80211com *ic, const struct iw_point* erq, int *kix)
+{
+	int kid;
+
+	kid = erq->flags & IW_ENCODE_INDEX;
+	if (kid < 1 || kid > IEEE80211_WEP_NKID) {
+		kid = ic->ic_def_txkey;
+		if (kid == IEEE80211_KEYIX_NONE)
+			kid = 0;
+	} else
+		--kid;
+	if (0 <= kid && kid < IEEE80211_WEP_NKID) {
+		*kix = kid;
+		return 0;
+	} else
+		return EINVAL;
+}
+
+int
+ieee80211_ioctl_siwencode(struct ieee80211com *ic,
+			  struct iw_request_info *info,
+			  struct iw_point *erq, char *keybuf)
+{
+	int kid, error;
+	int wepchange = 0;
+
+	if ((erq->flags & IW_ENCODE_DISABLED) == 0) {
+		/*
+		 * Enable crypto, set key contents, and
+		 * set the default transmit key.
+		 */
+		error = getiwkeyix(ic, erq, &kid);
+		if (error)
+			return -error;
+		if (erq->length > IEEE80211_KEYBUF_SIZE)
+			return -EINVAL;
+		/* XXX no way to install 0-length key */
+		ieee80211_key_update_begin(ic);
+		if (erq->length > 0) {
+			struct ieee80211_key *k = &ic->ic_nw_keys[kid];
+
+			/*
+			 * Set key contents.  This interface only supports WEP.
+			 */
+			if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, k)) {
+				k->wk_keylen = erq->length;
+				/* NB: preserve flags set by newkey */
+				k->wk_flags |=
+					IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+				memcpy(k->wk_key, keybuf, erq->length);
+				memset(k->wk_key + erq->length, 0,
+					IEEE80211_KEYBUF_SIZE - erq->length);
+				if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
+					error = -EINVAL;		/* XXX */
+			} else {
+				error = -EINVAL;
+			}
+		} else {
+			/*
+			 * When the length is zero the request only changes
+			 * the default transmit key.  Verify the new key has
+			 * a non-zero length.
+			 */
+			if (ic->ic_nw_keys[kid].wk_keylen == 0)
+				error = -EINVAL;
+		}
+		if (error == 0) {
+			/*
+			 * The default transmit key is only changed when:
+			 * 1. Privacy is enabled and no key matter is
+			 *    specified.
+			 * 2. Privacy is currently disabled.
+			 * This is deduced from the iwconfig man page.
+			 */
+			if (erq->length == 0 ||
+			    (ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+				ic->ic_def_txkey = kid;
+			wepchange = (ic->ic_flags & IEEE80211_F_PRIVACY) == 0;
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+		}
+		ieee80211_key_update_end(ic);
+	} else {
+		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+			return 0;
+		ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+		wepchange = 1;
+		error = 0;
+	}
+	if (error == 0) {
+		if (erq->flags & IW_ENCODE_OPEN)
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+		else
+			ic->ic_flags |= IEEE80211_F_DROPUNENC;
+	}
+	if (error == 0 && IS_UP(ic->ic_dev)) {
+		/*
+		 * Device is up and running; we must kick it to
+		 * effect the change.  If we're enabling/disabling
+		 * crypto use then we must re-initialize the device
+		 * so the 802.11 state machine is reset.  Otherwise
+		 * the key state should have been updated above.
+		 */
+		if (wepchange && ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+			error = -(*ic->ic_init)(ic->ic_dev);
+	}
+	return error;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwencode);
+
+int
+ieee80211_ioctl_giwencode(struct ieee80211com *ic,
+			  struct iw_request_info *info,
+			  struct iw_point *erq, char *key)
+{
+	struct ieee80211_key *k;
+	int error, kid;
+
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		error = getiwkeyix(ic, erq, &kid);
+		if (error != 0)
+			return -error;
+		k = &ic->ic_nw_keys[kid];
+		/* XXX no way to return cipher/key type */
+
+		erq->flags = kid + 1;			/* NB: base 1 */
+		if (erq->length > k->wk_keylen)
+			erq->length = k->wk_keylen;
+		memcpy(key, k->wk_key, erq->length);
+		erq->flags |= IW_ENCODE_ENABLED;
+	} else {
+		erq->length = 0;
+		erq->flags = IW_ENCODE_DISABLED;
+	}
+	if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+		erq->flags |= IW_ENCODE_RESTRICTED;
+	else
+		erq->flags |= IW_ENCODE_OPEN;
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwencode);
+
+#ifndef ifr_media
+#define	ifr_media	ifr_ifru.ifru_ivalue
+#endif
+
+int
+ieee80211_ioctl_siwrate(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_param *rrq, char *extra)
+{
+	struct ifreq ifr;
+	int rate;
+
+	if (!ic->ic_media.ifm_cur)
+		return -EINVAL;
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media &~ IFM_TMASK;
+	if (rrq->fixed) {
+		/* XXX fudge checking rates */
+		rate = ieee80211_rate2media(ic, 2 * rrq->value / 1000000,
+				ic->ic_curmode);
+		if (rate == IFM_AUTO)		/* NB: unknown rate */
+			return -EINVAL;
+	} else
+		rate = IFM_AUTO;
+	ifr.ifr_media |= IFM_SUBTYPE(rate);
+
+	return -ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA);
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwrate);
+
+int
+ieee80211_ioctl_giwrate(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_param *rrq, char *extra)
+{
+	struct ifmediareq imr;
+	int rate;
+
+	memset(&imr, 0, sizeof(imr));
+	(*ic->ic_media.ifm_status)(ic->ic_dev, &imr);
+
+	rrq->fixed = IFM_SUBTYPE(ic->ic_media.ifm_media) != IFM_AUTO;
+	/* media status will have the current xmit rate if available */
+	rate = ieee80211_media2rate(imr.ifm_active);
+	if (rate == -1)		/* IFM_AUTO */
+		rate = 0;
+	rrq->value = 1000000 * (rate / 2);
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwrate);
+
+int
+ieee80211_ioctl_siwsens(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_param *sens, char *extra)
+{
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwsens);
+
+int
+ieee80211_ioctl_giwsens(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_param *sens, char *extra)
+{
+	sens->value = 0;
+	sens->fixed = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwsens);
+
+int
+ieee80211_ioctl_siwrts(struct ieee80211com *ic,
+		       struct iw_request_info *info,
+		       struct iw_param *rts, char *extra)
+{
+	u16 val;
+
+	if (rts->disabled)
+		val = IEEE80211_RTS_MAX;
+	else if (IEEE80211_RTS_MIN <= rts->value &&
+	    rts->value <= IEEE80211_RTS_MAX)
+		val = rts->value;
+	else
+		return -EINVAL;
+	if (val != ic->ic_rtsthreshold) {
+		ic->ic_rtsthreshold = val;
+		if (IS_UP(ic->ic_dev))
+			return -(*ic->ic_reset)(ic->ic_dev);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwrts);
+
+int
+ieee80211_ioctl_giwrts(struct ieee80211com *ic,
+		       struct iw_request_info *info,
+		       struct iw_param *rts, char *extra)
+{
+
+	rts->value = ic->ic_rtsthreshold;
+	rts->disabled = (rts->value == IEEE80211_RTS_MAX);
+	rts->fixed = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwrts);
+
+int
+ieee80211_ioctl_siwfrag(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_param *rts, char *extra)
+{
+	u16 val;
+
+	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 */
+	if (val != ic->ic_fragthreshold) {
+		ic->ic_fragthreshold = val;
+		if (IS_UP(ic->ic_dev))
+			return -(*ic->ic_reset)(ic->ic_dev);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwfrag);
+
+int
+ieee80211_ioctl_giwfrag(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_param *rts, char *extra)
+{
+
+	rts->value = ic->ic_fragthreshold;
+	rts->disabled = (rts->value == 2346);
+	rts->fixed = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwfrag);
+
+int
+ieee80211_ioctl_siwap(struct ieee80211com *ic,
+		      struct iw_request_info *info,
+		      struct sockaddr *ap_addr, char *extra)
+{
+	static const u_int8_t zero_bssid[IEEE80211_ADDR_LEN];
+
+	/* NB: should only be set when in STA mode */
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		return -EINVAL;
+	IEEE80211_ADDR_COPY(ic->ic_des_bssid, &ap_addr->sa_data);
+	/* looks like a zero address disables */
+	if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zero_bssid))
+		ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+	else
+		ic->ic_flags |= IEEE80211_F_DESBSSID;
+	return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwap);
+
+int
+ieee80211_ioctl_giwap(struct ieee80211com *ic,
+		      struct iw_request_info *info,
+		      struct sockaddr *ap_addr, char *extra)
+{
+
+	if (ic->ic_flags & IEEE80211_F_DESBSSID)
+		IEEE80211_ADDR_COPY(&ap_addr->sa_data, ic->ic_des_bssid);
+	else
+		IEEE80211_ADDR_COPY(&ap_addr->sa_data, ic->ic_bss->ni_bssid);
+	ap_addr->sa_family = ARPHRD_ETHER;
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwap);
+
+int
+ieee80211_ioctl_siwnickn(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_point *data, char *nickname)
+{
+
+	if (data->length > IEEE80211_NWID_LEN)
+		return -EINVAL;
+
+	memset(ic->ic_nickname, 0, IEEE80211_NWID_LEN);
+	memcpy(ic->ic_nickname, nickname, data->length);
+	ic->ic_nicknamelen = data->length;
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwnickn);
+
+int
+ieee80211_ioctl_giwnickn(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_point *data, char *nickname)
+{
+
+	if (data->length > ic->ic_nicknamelen + 1)
+		data->length = ic->ic_nicknamelen + 1;
+	if (data->length > 0) {
+		memcpy(nickname, ic->ic_nickname, data->length-1);
+		nickname[data->length-1] = '\0';
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwnickn);
+
+static struct ieee80211_channel *
+getcurchan(struct ieee80211com *ic)
+{
+	switch (ic->ic_state) {
+	case IEEE80211_S_INIT:
+	case IEEE80211_S_SCAN:
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			return ic->ic_des_chan;
+		break;
+	default:
+		break;
+	}
+	return ic->ic_ibss_chan;
+}
+
+int
+ieee80211_ioctl_siwfreq(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_freq *freq, char *extra)
+{
+	struct ieee80211_channel *c;
+	int i;
+	
+	if (freq->e > 1)
+		return -EINVAL;
+	if (freq->e == 1)
+		i = ieee80211_mhz2ieee(freq->m / 100000, 0);
+	else
+		i = freq->m;
+	if (i != 0) {
+		if (i > IEEE80211_CHAN_MAX || isclr(ic->ic_chan_active, i))
+			return -EINVAL;
+		c = &ic->ic_channels[i];
+		if (c == getcurchan(ic)) {	/* no change, just return */
+			ic->ic_des_chan = c;	/* XXX */
+			return 0;
+		}
+		ic->ic_des_chan = c;
+		if (c != IEEE80211_CHAN_ANYC)
+			ic->ic_ibss_chan = c;
+	} else {
+		/*
+		 * Intepret channel 0 to mean "no desired channel";
+		 * otherwise there's no way to undo fixing the desired
+		 * channel.
+		 */
+		if (ic->ic_des_chan == IEEE80211_CHAN_ANYC)
+			return 0;
+		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+	}
+	if (ic->ic_opmode == IEEE80211_M_MONITOR)
+		return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0;
+	else
+		return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwfreq);
+
+int
+ieee80211_ioctl_giwfreq(struct ieee80211com *ic,
+				struct iw_request_info *info,
+				struct iw_freq *freq, char *extra)
+{
+
+	if (!ic->ic_ibss_chan)
+		return -EINVAL;
+
+	freq->m = ic->ic_ibss_chan->ic_freq * 100000;
+	freq->e = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwfreq);
+
+int
+ieee80211_ioctl_siwessid(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_point *data, char *ssid)
+{
+
+	if (data->flags == 0) {		/* ANY */
+		memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
+		ic->ic_des_esslen = 0;
+	} else {
+		if (data->length > sizeof(ic->ic_des_essid))
+			data->length = sizeof(ic->ic_des_essid);
+		memcpy(ic->ic_des_essid, ssid, data->length);
+		ic->ic_des_esslen = data->length;
+		/*
+		 * Deduct a trailing \0 since iwconfig passes a string
+		 * length that includes this.  Unfortunately this means
+		 * that specifying a string with multiple trailing \0's
+		 * won't be handled correctly.  Not sure there's a good
+		 * solution; the API is botched (the length should be
+		 * exactly those bytes that are meaningful and not include
+		 * extraneous stuff).
+		 */
+		if (ic->ic_des_esslen > 0 &&
+		    ic->ic_des_essid[ic->ic_des_esslen-1] == '\0')
+			ic->ic_des_esslen--;
+	}
+	return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwessid);
+
+int
+ieee80211_ioctl_giwessid(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_point *data, char *essid)
+{
+
+	data->flags = 1;		/* active */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		if (data->length > ic->ic_des_esslen)
+			data->length = ic->ic_des_esslen;
+		memcpy(essid, ic->ic_des_essid, data->length);
+	} else {
+		if (ic->ic_des_esslen == 0) {
+			if (data->length > ic->ic_bss->ni_esslen)
+				data->length = ic->ic_bss->ni_esslen;
+			memcpy(essid, ic->ic_bss->ni_essid, data->length);
+		} else {
+			if (data->length > ic->ic_des_esslen)
+				data->length = ic->ic_des_esslen;
+			memcpy(essid, ic->ic_des_essid, data->length);
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwessid);
+
+int
+ieee80211_ioctl_giwrange(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_point *data, char *extra)
+{
+	struct ieee80211_node *ni = ic->ic_bss;
+	struct iw_range *range = (struct iw_range *) extra;
+	struct ieee80211_rateset *rs;
+	int i, r;
+
+	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 (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_IBSS) {
+		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 = 13;
+
+	range->retry_capa = IW_RETRY_LIMIT;
+	range->retry_flags = IW_RETRY_LIMIT;
+	range->min_retry = 0;
+	range->max_retry = 255;
+
+	range->num_channels = IEEE80211_CHAN_MAX;	/* XXX */
+
+	range->num_frequency = 0;
+	for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+		if (isset(ic->ic_chan_active, i)) {
+			range->freq[range->num_frequency].i = i;
+			range->freq[range->num_frequency].m =
+				ic->ic_channels[i].ic_freq * 100000;
+			range->freq[range->num_frequency].e = 1;
+			if (++range->num_frequency == IW_MAX_FREQUENCIES)
+				break;
+		}
+
+	/* Max quality is max field value minus noise floor */
+	range->max_qual.qual  = 0xff - 161;
+
+	/*
+	 * In order to use dBm measurements, 'level' must be lower
+	 * than any possible measurement (see iw_print_stats() in
+	 * wireless tools).  It's unclear how this is meant to be
+	 * done, but setting zero in these values forces dBm and
+	 * the actual numbers are not used.
+	 */
+	range->max_qual.level = 0;
+	range->max_qual.noise = 0;
+
+	range->sensitivity = 3;
+
+	range->max_encoding_tokens = IEEE80211_WEP_NKID;
+	/* XXX query driver to find out supported key sizes */
+	range->num_encoding_sizes = 3;
+	range->encoding_size[0] = 5;		/* 40-bit */
+	range->encoding_size[1] = 13;		/* 104-bit */
+	range->encoding_size[2] = 16;		/* 128-bit */
+
+	/* XXX this only works for station mode */
+	rs = &ni->ni_rates;
+	range->num_bitrates = rs->rs_nrates;
+	if (range->num_bitrates > IW_MAX_BITRATES)
+		range->num_bitrates = IW_MAX_BITRATES;
+	for (i = 0; i < range->num_bitrates; i++) {
+		r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+		range->bitrate[i] = (r * 1000000) / 2;
+	}
+
+	/* estimated maximum TCP throughput values (bps) */
+	range->throughput = 5500000;
+
+	range->min_rts = 0;
+	range->max_rts = 2347;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwrange);
+
+int
+ieee80211_ioctl_siwmode(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			__u32 *mode, char *extra)
+{
+	struct ifreq ifr;
+
+	if (!ic->ic_media.ifm_cur)
+		return -EINVAL;
+	memset(&ifr, 0, sizeof(ifr));
+	/* NB: remove any fixed-rate at the same time */
+	ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media &~
+		(IFM_OMASK | IFM_TMASK);
+	switch (*mode) {
+	case IW_MODE_INFRA:
+		/* NB: this is the default */
+		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+		break;
+	case IW_MODE_ADHOC:
+		ifr.ifr_media |= IFM_IEEE80211_ADHOC;
+		break;
+	case IW_MODE_MASTER:
+		ifr.ifr_media |= IFM_IEEE80211_HOSTAP;
+		break;
+#if WIRELESS_EXT >= 15
+	case IW_MODE_MONITOR:
+		ifr.ifr_media |= IFM_IEEE80211_MONITOR;
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+	return -ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA);
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwmode);
+
+int
+ieee80211_ioctl_giwmode(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			__u32 *mode, char *extra)
+{
+	struct ifmediareq imr;
+
+	memset(&imr, 0, sizeof(imr));
+	(*ic->ic_media.ifm_status)(ic->ic_dev, &imr);
+
+	if (imr.ifm_active & IFM_IEEE80211_HOSTAP)
+		*mode = IW_MODE_MASTER;
+#if WIRELESS_EXT >= 15
+	else if (imr.ifm_active & IFM_IEEE80211_MONITOR)
+		*mode = IW_MODE_MONITOR;
+#endif
+	else if (imr.ifm_active & IFM_IEEE80211_ADHOC)
+		*mode = IW_MODE_ADHOC;
+	else
+		*mode = IW_MODE_INFRA;
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwmode);
+
+int
+ieee80211_ioctl_siwpower(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_param *wrq, char *extra)
+{
+
+	if (wrq->disabled) {
+		if (ic->ic_flags & IEEE80211_F_PMGTON) {
+			ic->ic_flags &= ~IEEE80211_F_PMGTON;
+			goto done;
+		}
+		return 0;
+	}
+
+	if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
+		return -EOPNOTSUPP;
+	switch (wrq->flags & IW_POWER_MODE) {
+	case IW_POWER_UNICAST_R:
+	case IW_POWER_ALL_R:
+	case IW_POWER_ON:
+		ic->ic_flags |= IEEE80211_F_PMGTON;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (wrq->flags & IW_POWER_TIMEOUT) {
+		ic->ic_holdover = wrq->value / 1024;
+		ic->ic_flags |= IEEE80211_F_PMGTON;
+	}
+	if (wrq->flags & IW_POWER_PERIOD) {
+		ic->ic_lintval = wrq->value / 1024;
+		ic->ic_flags |= IEEE80211_F_PMGTON;
+	}
+done:
+	return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwpower);
+
+int
+ieee80211_ioctl_giwpower(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_param *rrq, char *extra)
+{
+
+	rrq->disabled = (ic->ic_flags & IEEE80211_F_PMGTON) == 0;
+	if (!rrq->disabled) {
+		switch (rrq->flags & IW_POWER_TYPE) {
+		case IW_POWER_TIMEOUT:
+			rrq->flags = IW_POWER_TIMEOUT;
+			rrq->value = ic->ic_holdover * 1024;
+			break;
+		case IW_POWER_PERIOD:
+			rrq->flags = IW_POWER_PERIOD;
+			rrq->value = ic->ic_lintval * 1024;
+			break;
+		}
+		rrq->flags |= IW_POWER_ALL_R;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwpower);
+
+int
+ieee80211_ioctl_siwretry(struct ieee80211com *ic,
+				 struct iw_request_info *info,
+				 struct iw_param *rrq, char *extra)
+{
+
+	if (rrq->disabled) {
+		if (ic->ic_flags & IEEE80211_F_SWRETRY) {
+			ic->ic_flags &= ~IEEE80211_F_SWRETRY;
+			goto done;
+		}
+		return 0;
+	}
+
+	if ((ic->ic_caps & IEEE80211_C_SWRETRY) == 0)
+		return -EOPNOTSUPP;
+	if (rrq->flags == IW_RETRY_LIMIT) {
+		if (rrq->value >= 0) {
+			ic->ic_txmin = rrq->value;
+			ic->ic_txmax = rrq->value;	/* XXX */
+			ic->ic_txlifetime = 0;		/* XXX */
+			ic->ic_flags |= IEEE80211_F_SWRETRY;
+		} else {
+			ic->ic_flags &= ~IEEE80211_F_SWRETRY;
+		}
+		return 0;
+	}
+done:
+	return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwretry);
+
+int
+ieee80211_ioctl_giwretry(struct ieee80211com *ic,
+				 struct iw_request_info *info,
+				 struct iw_param *rrq, char *extra)
+{
+
+	rrq->disabled = (ic->ic_flags & IEEE80211_F_SWRETRY) == 0;
+	if (!rrq->disabled) {
+		switch (rrq->flags & IW_RETRY_TYPE) {
+		case IW_RETRY_LIFETIME:
+			rrq->flags = IW_RETRY_LIFETIME;
+			rrq->value = ic->ic_txlifetime * 1024;
+			break;
+		case IW_RETRY_LIMIT:
+			rrq->flags = IW_RETRY_LIMIT;
+			switch (rrq->flags & IW_RETRY_MODIFIER) {
+			case IW_RETRY_MIN:
+				rrq->flags |= IW_RETRY_MAX;
+				rrq->value = ic->ic_txmin;
+				break;
+			case IW_RETRY_MAX:
+				rrq->flags |= IW_RETRY_MAX;
+				rrq->value = ic->ic_txmax;
+				break;
+			}
+			break;
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwretry);
+
+int
+ieee80211_ioctl_siwtxpow(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_param *rrq, char *extra)
+{
+	int fixed, disabled;
+
+	fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED);
+	disabled = (fixed && ic->ic_bss->ni_txpower == 0);
+	if (rrq->disabled) {
+		if (!disabled) {
+			if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+				return -EOPNOTSUPP;
+			ic->ic_flags |= IEEE80211_F_TXPOW_FIXED;
+			ic->ic_bss->ni_txpower = 0;
+			goto done;
+		}
+		return 0;
+	}
+
+	if (rrq->fixed) {
+		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+			return -EOPNOTSUPP;
+		if (rrq->flags != IW_TXPOW_DBM)
+			return -EOPNOTSUPP;
+		ic->ic_bss->ni_txpower = 2*rrq->value;
+		ic->ic_flags |= IEEE80211_F_TXPOW_FIXED;
+	} else {
+		if (!fixed)		/* no change */
+			return 0;
+		ic->ic_flags &= ~IEEE80211_F_TXPOW_FIXED;
+	}
+done:
+	return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwtxpow);
+
+int
+ieee80211_ioctl_giwtxpow(struct ieee80211com *ic,
+			 struct iw_request_info *info,
+			 struct iw_param *rrq, char *extra)
+{
+
+	rrq->value = ic->ic_bss->ni_txpower/2;
+	rrq->fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED) != 0;
+	rrq->disabled = (rrq->fixed && rrq->value == 0);
+	rrq->flags = IW_TXPOW_DBM;
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwtxpow);
+
+int
+ieee80211_ioctl_iwaplist(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_point *data, char *extra)
+{
+	struct ieee80211_node *ni;
+	struct sockaddr addr[IW_MAX_AP];
+	struct iw_quality qual[IW_MAX_AP];
+	int i;
+
+	i = 0;
+	/* XXX lock node list */
+	TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+		addr[i].sa_family = ARPHRD_ETHER;
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+			IEEE80211_ADDR_COPY(addr[i].sa_data, ni->ni_macaddr);
+		else
+			IEEE80211_ADDR_COPY(addr[i].sa_data, ni->ni_bssid);
+		set_quality(&qual[i], (*ic->ic_node_getrssi)(ic, ni));
+		if (++i >= IW_MAX_AP)
+			break;
+	}
+	data->length = i;
+	memcpy(extra, &addr, i*sizeof(addr[0]));
+	data->flags = 1;		/* signal quality present (sort of) */
+	memcpy(extra + i*sizeof(addr[0]), &qual, i*sizeof(qual[i]));
+
+	return 0;
+
+}
+EXPORT_SYMBOL(ieee80211_ioctl_iwaplist);
+
+#ifdef SIOCGIWSCAN
+int
+ieee80211_ioctl_siwscan(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_point *data, char *extra)
+{
+	u_char *chanlist = ic->ic_chan_active;
+	int i;
+
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+		return -EINVAL;
+	/*
+	 * XXX don't permit a scan to be started unless we
+	 * know the device is ready.  For the moment this means
+	 * the device is marked up as this is the required to
+	 * initialize the hardware.  It would be better to permit
+	 * scanning prior to being up but that'll require some
+	 * changes to the infrastructure.
+	 */
+	if (!IS_UP(ic->ic_dev))
+		return -EINVAL;		/* XXX */
+	if (ic->ic_state == IEEE80211_S_SCAN && (ic->ic_flags & IEEE80211_F_ASCAN))
+		return -EINPROGRESS;
+	if (ic->ic_ibss_chan == NULL ||
+	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
+		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+			if (isset(chanlist, i)) {
+				ic->ic_ibss_chan = &ic->ic_channels[i];
+				goto found;
+			}
+		return -EINVAL;			/* no active channels */
+found:
+		;
+	}
+	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
+	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
+		ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+	/*
+	 * We force the state to INIT before calling ieee80211_new_state
+	 * to get ieee80211_begin_scan called.  We really want to scan w/o
+	 * altering the current state but that's not possible right now.
+	 */
+	/* XXX handle proberequest case */
+	if (ic->ic_state != IEEE80211_S_INIT)
+		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+	ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_siwscan);
+
+#if WIRELESS_EXT > 14
+/*
+ * Encode a WPA or RSN information element as a custom
+ * element using the hostap format.
+ */
+static u_int
+encode_ie(void *buf, size_t bufsize,
+	const u_int8_t *ie, size_t ielen,
+	const char *leader, size_t leader_len)
+{
+	u_int8_t *p;
+	int i;
+
+	if (bufsize < leader_len)
+		return 0;
+	p = buf;
+	memcpy(p, leader, leader_len);
+	bufsize -= leader_len;
+	p += leader_len;
+	for (i = 0; i < ielen && bufsize > 2; i++)
+		p += sprintf(p, "%02x", ie[i]);
+	return (i == ielen ? p - (u_int8_t *)buf : 0);
+}
+#endif /* WIRELESS_EXT > 14 */
+
+int
+ieee80211_ioctl_giwscan(struct ieee80211com *ic,
+			struct iw_request_info *info,
+			struct iw_point *data, char *extra)
+{
+	struct ieee80211_node *ni;
+	char *current_ev = extra;
+	char *end_buf = extra + IW_SCAN_MAX_DATA;
+	char *current_val;
+	struct iw_event iwe;
+#if WIRELESS_EXT > 14
+	char buf[64*2 + 30];
+#endif
+	int j, startmode, mode;
+
+	/* XXX use generation number and always return current results */
+	if (ic->ic_state == IEEE80211_S_SCAN && (ic->ic_flags & IEEE80211_F_ASCAN)) {
+		/*
+		 * Still scanning, indicate the caller should try again.
+		 */
+		return -EAGAIN;
+	}
+
+	/*
+	 * Do two passes to insure WPA/non-WPA scan candidates
+	 * are sorted to the front.  This is a hack to deal with
+	 * the wireless extensions capping scan results at
+	 * IW_SCAN_MAX_DATA bytes.  In densely populated environments
+	 * it's easy to overflow this buffer (especially with WPA/RSN
+	 * information elements).  Note this sorting hack does not
+	 * guarantee we won't overflow anyway.
+	 */
+	startmode = ic->ic_flags & IEEE80211_F_WPA;
+	mode = startmode;
+again:
+	/*
+	 * Translate data to WE format.
+	 */
+	TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+		if (current_ev >= end_buf)
+			goto done;
+		/* WPA/!WPA sort criteria */
+		if ((mode != 0) ^ (ni->ni_wpa_ie != NULL))
+			continue;
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+			IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, ni->ni_macaddr);
+		else
+			IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, ni->ni_bssid);
+		current_ev = iwe_stream_add_event(current_ev,
+			end_buf, &iwe, IW_EV_ADDR_LEN);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.flags = 1;
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			iwe.u.data.length = ic->ic_des_esslen;
+			current_ev = iwe_stream_add_point(current_ev,
+					end_buf, &iwe, ic->ic_des_essid);
+		} else {
+			iwe.u.data.length = ni->ni_esslen;
+			current_ev = iwe_stream_add_point(current_ev,
+					end_buf, &iwe, ni->ni_essid);
+		}
+
+		if (ni->ni_capinfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) {
+			memset(&iwe, 0, sizeof(iwe));
+			iwe.cmd = SIOCGIWMODE;
+			iwe.u.mode = ni->ni_capinfo & IEEE80211_CAPINFO_ESS ?
+				IW_MODE_MASTER : IW_MODE_ADHOC;
+			current_ev = iwe_stream_add_event(current_ev,
+					end_buf, &iwe, IW_EV_UINT_LEN);
+		}
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWFREQ;
+		iwe.u.freq.m = ni->ni_chan->ic_freq * 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 = IWEVQUAL;
+		set_quality(&iwe.u.qual, (*ic->ic_node_getrssi)(ic, ni));
+		current_ev = iwe_stream_add_event(current_ev,
+			end_buf, &iwe, IW_EV_QUAL_LEN);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWENCODE;
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		iwe.u.data.length = 0;
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWRATE;
+		current_val = current_ev + IW_EV_LCP_LEN;
+		for (j = 0; j < ni->ni_rates.rs_nrates; j++) {
+			if (ni->ni_rates.rs_rates[j]) {
+				iwe.u.bitrate.value = ((ni->ni_rates.rs_rates[j] &
+				    IEEE80211_RATE_VAL) / 2) * 1000000;
+				current_val = iwe_stream_add_value(current_ev,
+					current_val, end_buf, &iwe,
+					IW_EV_PARAM_LEN);
+			}
+		}
+		/* remove fixed header if no rates were added */
+		if ((current_val - current_ev) > IW_EV_LCP_LEN)
+			current_ev = current_val;
+
+#if WIRELESS_EXT > 14
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = IWEVCUSTOM;
+		snprintf(buf, sizeof(buf), "bcn_int=%d", ni->ni_intval);
+		iwe.u.data.length = strlen(buf);
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+
+		if (ni->ni_wpa_ie != NULL) {
+			static const char rsn_leader[] = "rsn_ie=";
+			static const char wpa_leader[] = "wpa_ie=";
+
+			memset(&iwe, 0, sizeof(iwe));
+			iwe.cmd = IWEVCUSTOM;
+			if (ni->ni_wpa_ie[0] == IEEE80211_ELEMID_RSN)
+				iwe.u.data.length = encode_ie(buf, sizeof(buf),
+					ni->ni_wpa_ie, ni->ni_wpa_ie[1]+2,
+					rsn_leader, sizeof(rsn_leader)-1);
+			else
+				iwe.u.data.length = encode_ie(buf, sizeof(buf),
+					ni->ni_wpa_ie, ni->ni_wpa_ie[1]+2,
+					wpa_leader, sizeof(wpa_leader)-1);
+			if (iwe.u.data.length != 0)
+				current_ev = iwe_stream_add_point(current_ev, end_buf,
+					&iwe, buf);
+		}
+#endif /* WIRELESS_EXT > 14 */
+	}
+	if (mode == startmode) {
+		mode = mode ? 0 : IEEE80211_F_WPA;
+		goto again;		/* sort of an Algol-style for loop */
+	}
+done:
+	data->length = current_ev - extra;
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_giwscan);
+#endif /* SIOCGIWSCAN */
+
+static int
+cipher2cap(int cipher)
+{
+	switch (cipher) {
+	case IEEE80211_CIPHER_WEP:	return IEEE80211_C_WEP;
+	case IEEE80211_CIPHER_AES_OCB:	return IEEE80211_C_AES;
+	case IEEE80211_CIPHER_AES_CCM:	return IEEE80211_C_AES_CCM;
+	case IEEE80211_CIPHER_CKIP:	return IEEE80211_C_CKIP;
+	case IEEE80211_CIPHER_TKIP:	return IEEE80211_C_TKIP;
+	}
+	return 0;
+}
+
+int
+ieee80211_ioctl_setparam(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	int *i = (int *) extra;
+	int param = i[0];		/* parameter id is 1st */
+	int value = i[1];		/* NB: most values are TYPE_INT */
+	struct ifreq ifr;
+	int retv = 0;
+	int j, caps;
+	const struct ieee80211_authenticator *auth;
+	const struct ieee80211_aclator *acl;
+
+	switch (param) {
+	case IEEE80211_PARAM_TURBO:
+		if (!ic->ic_media.ifm_cur)
+			return -EINVAL;
+		memset(&ifr, 0, sizeof(ifr));
+		ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media;
+		if (value)
+			ifr.ifr_media |= IFM_IEEE80211_TURBO;
+		else
+			ifr.ifr_media &= ~IFM_IEEE80211_TURBO;
+		retv = ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA);
+		break;
+	case IEEE80211_PARAM_MODE:
+		if (!ic->ic_media.ifm_cur)
+			return -EINVAL;
+		memset(&ifr, 0, sizeof(ifr));
+		ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media &~ IFM_MMASK;
+		ifr.ifr_media |= IFM_MAKEMODE(value);
+		retv = ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA);
+		break;
+	case IEEE80211_PARAM_AUTHMODE:
+		switch (value) {
+		case IEEE80211_AUTH_WPA:	/* WPA */
+		case IEEE80211_AUTH_8021X:	/* 802.1x */
+		case IEEE80211_AUTH_OPEN:	/* open */
+		case IEEE80211_AUTH_SHARED:	/* shared-key */
+		case IEEE80211_AUTH_AUTO:	/* auto */
+			auth = ieee80211_authenticator_get(value);
+			if (auth == NULL)
+				return -EINVAL;
+			break;
+		default:
+			return -EINVAL;
+		}
+		switch (value) {
+		case IEEE80211_AUTH_WPA:	/* WPA w/ 802.1x */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			value = IEEE80211_AUTH_8021X;
+			break;
+		case IEEE80211_AUTH_OPEN:	/* open */
+			ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
+			break;
+		case IEEE80211_AUTH_SHARED:	/* shared-key */
+		case IEEE80211_AUTH_8021X:	/* 802.1x */
+			ic->ic_flags &= ~IEEE80211_F_WPA;
+			/* both require a key so mark the PRIVACY capability */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			break;
+		case IEEE80211_AUTH_AUTO:	/* auto */
+			ic->ic_flags &= ~IEEE80211_F_WPA;
+			/* XXX PRIVACY handling? */
+			/* XXX what's the right way to do this? */
+			break;
+		}
+		/* NB: authenticator attach/detach happens on state change */
+		ic->ic_bss->ni_authmode = value;
+		/* XXX mixed/mode/usage? */
+		ic->ic_auth = auth;
+		retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_PROTMODE:
+		if (value > IEEE80211_PROT_RTSCTS)
+			return -EINVAL;
+		ic->ic_protmode = value;
+		/* NB: if not operating in 11g this can wait */
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_MCASTCIPHER:
+		/* XXX s/w implementations */
+		if ((ic->ic_caps & cipher2cap(value)) == 0)
+			return -EINVAL;
+		rsn->rsn_mcastcipher = value;
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_MCASTKEYLEN:
+		if (!(0 < value && value < IEEE80211_KEYBUF_SIZE))
+			return -EINVAL;
+		/* XXX no way to verify driver capability */
+		rsn->rsn_mcastkeylen = value;
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_UCASTCIPHERS:
+		/*
+		 * Convert cipher set to equivalent capabilities.
+		 * NB: this logic intentionally ignores unknown and
+		 * unsupported ciphers so folks can specify 0xff or
+		 * similar and get all available ciphers.
+		 */
+		caps = 0;
+		for (j = 1; j < 32; j++)	/* NB: skip WEP */
+			if (value & (1<<j))
+				caps |= cipher2cap(j);
+		/* XXX s/w implementations */
+		caps &= ic->ic_caps;	/* restrict to supported ciphers */
+		/* XXX verify ciphers ok for unicast use? */
+		/* XXX disallow if running as it'll have no effect */
+		rsn->rsn_ucastcipherset = caps;
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_UCASTCIPHER:
+		if ((rsn->rsn_ucastcipherset & cipher2cap(value)) == 0)
+			return -EINVAL;
+		rsn->rsn_ucastcipher = value;
+		break;
+	case IEEE80211_PARAM_UCASTKEYLEN:
+		if (!(0 < value && value < IEEE80211_KEYBUF_SIZE))
+			return -EINVAL;
+		/* XXX no way to verify driver capability */
+		rsn->rsn_ucastkeylen = value;
+		break;
+	case IEEE80211_PARAM_KEYMGTALGS:
+		/* XXX check */
+		rsn->rsn_keymgmtset = value;
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_RSNCAPS:
+		/* XXX check */
+		rsn->rsn_caps = value;
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_WPA:
+		if (value > 3)
+			return -EINVAL;
+		/* XXX verify ciphers available */
+		ic->ic_flags &= ~IEEE80211_F_WPA;
+		switch (value) {
+		case 1:
+			ic->ic_flags |= IEEE80211_F_WPA1;
+			break;
+		case 2:
+			ic->ic_flags |= IEEE80211_F_WPA2;
+			break;
+		case 3:
+			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
+			break;
+		}
+		retv = ENETRESET;		/* XXX? */
+		break;
+	case IEEE80211_PARAM_ROAMING:
+		if (!(IEEE80211_ROAMING_DEVICE <= value &&
+		    value <= IEEE80211_ROAMING_MANUAL))
+			return -EINVAL;
+		ic->ic_roaming = value;
+		break;
+	case IEEE80211_PARAM_PRIVACY:
+		if (value) {
+			/* XXX check for key state? */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+		break;
+	case IEEE80211_PARAM_DROPUNENCRYPTED:
+		if (value)
+			ic->ic_flags |= IEEE80211_F_DROPUNENC;
+		else
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+		break;
+	case IEEE80211_PARAM_COUNTERMEASURES:
+		if (value) {
+			if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
+				return -EINVAL;
+			ic->ic_flags |= IEEE80211_F_COUNTERM;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+		break;
+	case IEEE80211_PARAM_DRIVER_CAPS:
+		ic->ic_caps = value;		/* NB: for testing */
+		break;
+	case IEEE80211_PARAM_MACCMD:
+		acl = ic->ic_acl;
+		switch (value) {
+		case IEEE80211_MACCMD_POLICY_OPEN:
+		case IEEE80211_MACCMD_POLICY_ALLOW:
+		case IEEE80211_MACCMD_POLICY_DENY:
+			if (acl == NULL) {
+				acl = ieee80211_aclator_get("mac");
+				if (acl == NULL || !acl->iac_attach(ic))
+					return -EINVAL;
+				ic->ic_acl = acl;
+			}
+			acl->iac_setpolicy(ic, value);
+			break;
+		case IEEE80211_MACCMD_FLUSH:
+			if (acl != NULL)
+				acl->iac_flush(ic);
+			/* NB: silently ignore when not in use */
+			break;
+		case IEEE80211_MACCMD_DETACH:
+			if (acl != NULL) {
+				ic->ic_acl = NULL;
+				acl->iac_detach(ic);
+			}
+			break;
+		}
+		break;
+	case IEEE80211_PARAM_WME:
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			return -EINVAL;
+		if (value)
+			ic->ic_flags |= IEEE80211_F_WME;
+		else
+			ic->ic_flags &= ~IEEE80211_F_WME;
+		break;
+	case IEEE80211_PARAM_HIDESSID:
+		if (value)
+			ic->ic_flags |= IEEE80211_F_HIDESSID;
+		else
+			ic->ic_flags &= ~IEEE80211_F_HIDESSID;
+		retv = ENETRESET;
+		break;
+	case IEEE80211_PARAM_APBRIDGE:
+		if (value == 0)
+			ic->ic_flags |= IEEE80211_F_NOBRIDGE;
+		else
+			ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
+		break;
+	case IEEE80211_PARAM_INACT:
+		ic->ic_inact_run = value / IEEE80211_INACT_WAIT;
+		break;
+	case IEEE80211_PARAM_INACT_AUTH:
+		ic->ic_inact_auth = value / IEEE80211_INACT_WAIT;
+		break;
+	case IEEE80211_PARAM_INACT_INIT:
+		ic->ic_inact_init = value / IEEE80211_INACT_WAIT;
+		break;
+	default:
+		retv = EOPNOTSUPP;
+		break;
+	}
+	if (retv == ENETRESET)
+		retv = IS_UP_AUTO(ic) ? (*ic->ic_init)(ic->ic_dev) : 0;
+	return -retv;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_setparam);
+
+static int
+cap2cipher(int flag)
+{
+	switch (flag) {
+	case IEEE80211_C_WEP:		return IEEE80211_CIPHER_WEP;
+	case IEEE80211_C_AES:		return IEEE80211_CIPHER_AES_OCB;
+	case IEEE80211_C_AES_CCM:	return IEEE80211_CIPHER_AES_CCM;
+	case IEEE80211_C_CKIP:		return IEEE80211_CIPHER_CKIP;
+	case IEEE80211_C_TKIP:		return IEEE80211_CIPHER_TKIP;
+	}
+	return -1;
+}
+
+int
+ieee80211_ioctl_getparam(struct ieee80211com *ic, struct iw_request_info *info,
+			void *w, char *extra)
+{
+	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	struct ifmediareq imr;
+	int *param = (int *) extra;
+	u_int m;
+
+	switch (param[0]) {
+	case IEEE80211_PARAM_TURBO:
+		(*ic->ic_media.ifm_status)(ic->ic_dev, &imr);
+		param[0] = (imr.ifm_active & IFM_IEEE80211_TURBO) != 0;
+		break;
+	case IEEE80211_PARAM_MODE:
+		(*ic->ic_media.ifm_status)(ic->ic_dev, &imr);
+		switch (IFM_MODE(imr.ifm_active)) {
+		case IFM_IEEE80211_11A:
+			param[0] = 1;
+			break;
+		case IFM_IEEE80211_11B:
+			param[0] = 2;
+			break;
+		case IFM_IEEE80211_11G:
+			param[0] = 3;
+			break;
+		case IFM_IEEE80211_FH:
+			param[0] = 4;
+			break;
+		case IFM_AUTO:
+			param[0] = 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IEEE80211_PARAM_AUTHMODE:
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			param[0] = IEEE80211_AUTH_WPA;
+		else
+			param[0] = ic->ic_bss->ni_authmode;
+		break;
+	case IEEE80211_PARAM_PROTMODE:
+		param[0] = ic->ic_protmode;
+		break;
+	case IEEE80211_PARAM_MCASTCIPHER:
+		param[0] = rsn->rsn_mcastcipher;
+		break;
+	case IEEE80211_PARAM_MCASTKEYLEN:
+		param[0] = rsn->rsn_mcastkeylen;
+		break;
+	case IEEE80211_PARAM_UCASTCIPHERS:
+		param[0] = 0;
+		for (m = 0x1; m != 0; m <<= 1)
+			if (rsn->rsn_ucastcipherset & m)
+				param[0] |= 1<<cap2cipher(m);
+		break;
+	case IEEE80211_PARAM_UCASTCIPHER:
+		param[0] = rsn->rsn_ucastcipher;
+		break;
+	case IEEE80211_PARAM_UCASTKEYLEN:
+		param[0] = rsn->rsn_ucastkeylen;
+		break;
+	case IEEE80211_PARAM_KEYMGTALGS:
+		param[0] = rsn->rsn_keymgmtset;
+		break;
+	case IEEE80211_PARAM_RSNCAPS:
+		param[0] = rsn->rsn_caps;
+		break;
+	case IEEE80211_PARAM_WPA:
+		switch (ic->ic_flags & IEEE80211_F_WPA) {
+		case IEEE80211_F_WPA1:
+			param[0] = 1;
+			break;
+		case IEEE80211_F_WPA2:
+			param[0] = 2;
+			break;
+		case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
+			param[0] = 3;
+			break;
+		default:
+			param[0] = 0;
+			break;
+		}
+		break;
+	case IEEE80211_PARAM_ROAMING:
+		param[0] = ic->ic_roaming;
+		break;
+	case IEEE80211_PARAM_PRIVACY:
+		param[0] = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+		break;
+	case IEEE80211_PARAM_DROPUNENCRYPTED:
+		param[0] = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
+		break;
+	case IEEE80211_PARAM_COUNTERMEASURES:
+		param[0] = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
+		break;
+	case IEEE80211_PARAM_DRIVER_CAPS:
+		param[0] = ic->ic_caps;
+		break;
+	case IEEE80211_PARAM_WME:
+		param[0] = (ic->ic_flags & IEEE80211_F_WME) != 0;
+		break;
+	case IEEE80211_PARAM_HIDESSID:
+		param[0] = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
+		break;
+	case IEEE80211_PARAM_APBRIDGE:
+		param[0] = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
+		break;
+	case IEEE80211_PARAM_INACT:
+		param[0] = ic->ic_inact_run * IEEE80211_INACT_WAIT;
+		break;
+	case IEEE80211_PARAM_INACT_AUTH:
+		param[0] = ic->ic_inact_auth * IEEE80211_INACT_WAIT;
+		break;
+	case IEEE80211_PARAM_INACT_INIT:
+		param[0] = ic->ic_inact_init * IEEE80211_INACT_WAIT;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_getparam);
+
+int
+ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	union iwreq_data *u = w;
+	void *ie;
+
+	/*
+	 * NB: Doing this for ap operation could be useful (e.g. for
+	 *     WPA and/or WME) except that it typically is worthless
+	 *     without being able to intervene when processing
+	 *     association response frames--so disallow it for now.
+	 */
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		return -EINVAL;
+	/* NB: data.length is validated by the wireless extensions code */
+	MALLOC(ie, void *, u->data.length, M_DEVBUF, M_WAITOK);
+	if (ie == NULL)
+		return -ENOMEM;
+	memcpy(ie, extra, u->data.length);
+	/* XXX sanity check data? */
+	if (ic->ic_opt_ie != NULL)
+		FREE(ic->ic_opt_ie, M_DEVBUF);
+	ic->ic_opt_ie = ie;
+	ic->ic_opt_ie_len = u->data.length;
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_setoptie);
+
+int
+ieee80211_ioctl_getoptie(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	union iwreq_data *u = w;
+
+	if (ic->ic_opt_ie == NULL)
+		return -EINVAL;
+	if (u->data.length < ic->ic_opt_ie_len)
+		return -EINVAL;
+	u->data.length = ic->ic_opt_ie_len;
+	memcpy(extra, ic->ic_opt_ie, u->data.length);
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_getoptie);
+
+int
+ieee80211_ioctl_setkey(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	struct ieee80211req_key *ik = (struct ieee80211req_key *)extra;
+	struct ieee80211_node *ni;
+	struct ieee80211_key *wk;
+	u_int16_t kid;
+	int error;
+
+	/* NB: cipher support is verified by ieee80211_crypt_newkey */
+	/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
+	if (ik->ik_keylen > sizeof(ik->ik_keydata))
+		return -E2BIG;
+	kid = ik->ik_keyix;
+	if (kid == IEEE80211_KEYIX_NONE) {
+		/* XXX unicast keys currently must be tx/rx */
+		if (ik->ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
+			return -EINVAL;
+		if (ic->ic_opmode == IEEE80211_M_STA) {
+			ni = ic->ic_bss;
+			if (!IEEE80211_ADDR_EQ(ik->ik_macaddr, ni->ni_bssid))
+				return -EADDRNOTAVAIL;
+		} else
+			ni = ieee80211_find_node(ic, ik->ik_macaddr);
+		if (ni == NULL)
+			return -ENOENT;
+		wk = &ni->ni_ucastkey;
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return -EINVAL;
+		wk = &ic->ic_nw_keys[kid];
+		ni = NULL;
+	}
+	error = 0;
+	ieee80211_key_update_begin(ic);
+	if (ieee80211_crypto_newkey(ic, ik->ik_type, wk)) {
+		wk->wk_keylen = ik->ik_keylen;
+		/* NB: MIC presence is implied by cipher type */
+		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
+			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
+		wk->wk_keyrsc = ik->ik_keyrsc;
+		wk->wk_keytsc = 0;			/* new key, reset */
+		wk->wk_flags |=
+			ik->ik_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV);
+		memset(wk->wk_key, 0, sizeof(wk->wk_key));
+		memcpy(wk->wk_key, ik->ik_keydata, ik->ik_keylen);
+		if (!ieee80211_crypto_setkey(ic, wk,
+		    ni != NULL ? ni->ni_macaddr : ik->ik_macaddr))
+			error = -EIO;
+		else if ((ik->ik_flags & IEEE80211_KEY_DEFAULT))
+			ic->ic_def_txkey = kid;
+	} else
+		error = -ENXIO;
+	ieee80211_key_update_end(ic);
+	if (ni != NULL && ni != ic->ic_bss)
+		ieee80211_free_node(ic, ni);
+	return error;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_setkey);
+
+static int
+ieee80211_ioctl_getkey(struct ieee80211com *ic, struct iwreq *iwr)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_key ik;
+	struct ieee80211_key *wk;
+	const struct ieee80211_cipher *cip;
+	u_int kid;
+
+	if (iwr->u.data.length != sizeof(ik))
+		return -EINVAL;
+	if (copy_from_user(&ik, iwr->u.data.pointer, sizeof(ik)))
+		return -EFAULT;
+	kid = ik.ik_keyix;
+	if (kid == IEEE80211_KEYIX_NONE) {
+		ni = ieee80211_find_node(ic, ik.ik_macaddr);
+		if (ni == NULL)
+			return -EINVAL;		/* XXX */
+		wk = &ni->ni_ucastkey;
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return -EINVAL;
+		wk = &ic->ic_nw_keys[kid];
+		IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
+		ni = NULL;
+	}
+	cip = wk->wk_cipher;
+	ik.ik_type = cip->ic_cipher;
+	ik.ik_keylen = wk->wk_keylen;
+	ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
+	if (wk->wk_keyix == ic->ic_def_txkey)
+		ik.ik_flags |= IEEE80211_KEY_DEFAULT;
+	if (capable(CAP_NET_ADMIN)) {
+		/* NB: only root can read key data */
+		ik.ik_keyrsc = wk->wk_keyrsc;
+		ik.ik_keytsc = wk->wk_keytsc;
+		memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
+		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
+			memcpy(ik.ik_keydata+wk->wk_keylen,
+				wk->wk_key + IEEE80211_KEYBUF_SIZE,
+				IEEE80211_MICBUF_SIZE);
+			ik.ik_keylen += IEEE80211_MICBUF_SIZE;
+		}
+	} else {
+		ik.ik_keyrsc = 0;
+		ik.ik_keytsc = 0;
+		memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
+	}
+	if (ni != NULL)
+		ieee80211_free_node(ic, ni);
+	return (copy_to_user(iwr->u.data.pointer, &ik, sizeof(ik)) ?
+			-EFAULT : 0);
+}
+
+int
+ieee80211_ioctl_delkey(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	struct ieee80211req_del_key *dk = (struct ieee80211req_del_key *)extra;
+	int kid;
+
+	kid = dk->idk_keyix;
+	/* XXX u_int8_t -> u_int16_t */
+	if (dk->idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) {
+		struct ieee80211_node *ni =
+			ieee80211_find_node(ic, dk->idk_macaddr);
+		if (ni == NULL)
+			return -EINVAL;		/* XXX */
+		/* XXX error return */
+		ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+		ieee80211_free_node(ic, ni);
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return -EINVAL;
+		/* XXX error return */
+		ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_delkey);
+
+int
+ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	struct ieee80211req_mlme *mlme = (struct ieee80211req_mlme *)extra;
+	struct ieee80211_node *ni;
+
+	switch (mlme->im_op) {
+	case IEEE80211_MLME_ASSOC:
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			return -EINVAL;
+		/* XXX must be in S_SCAN state? */
+
+		if (ic->ic_des_esslen != 0) {
+			/*
+			 * Desired ssid specified; must match both bssid and
+			 * ssid to distinguish ap advertising multiple ssid's.
+			 */
+			ni = ieee80211_find_node_with_ssid(ic, mlme->im_macaddr,
+				ic->ic_des_esslen, ic->ic_des_essid);
+		} else {
+			/*
+			 * Normal case; just match bssid.
+			 */
+			ni = ieee80211_find_node(ic, mlme->im_macaddr);
+		}
+		if (ni == NULL)
+			return -EINVAL;
+		if (!ieee80211_sta_join(ic, ni)) {
+			/* NB: from AP scan; don't free, just unref */
+			ieee80211_unref_node(&ni);
+			return -EINVAL;
+		}
+		break;
+	case IEEE80211_MLME_DISASSOC:
+	case IEEE80211_MLME_DEAUTH:
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			/* XXX not quite right */
+			ieee80211_new_state(ic, IEEE80211_S_INIT,
+				mlme->im_reason);
+			break;
+		case IEEE80211_M_HOSTAP:
+			ni = ieee80211_find_node(ic, mlme->im_macaddr);
+			if (ni == NULL)
+				return -EINVAL;
+			IEEE80211_SEND_MGMT(ic, ni,
+				mlme->im_op == IEEE80211_MLME_DEAUTH ?
+					  IEEE80211_FC0_SUBTYPE_DEAUTH
+					: IEEE80211_FC0_SUBTYPE_DISASSOC,
+				mlme->im_reason);
+			ieee80211_node_leave(ic, ni);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IEEE80211_MLME_AUTHORIZE:
+	case IEEE80211_MLME_UNAUTHORIZE:
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+			return -EINVAL;
+		ni = ieee80211_find_node(ic, mlme->im_macaddr);
+		if (ni == NULL)
+			return -EINVAL;
+		if (mlme->im_op == IEEE80211_MLME_AUTHORIZE)
+			ieee80211_node_authorize(ic, ni);
+		else
+			ieee80211_node_unauthorize(ic, ni);
+		ieee80211_free_node(ic, ni);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_setmlme);
+
+int
+ieee80211_ioctl_addmac(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	struct sockaddr *sa = (struct sockaddr *)extra;
+	const struct ieee80211_aclator *acl = ic->ic_acl;
+
+	if (acl == NULL) {
+		acl = ieee80211_aclator_get("mac");
+		if (acl == NULL || !acl->iac_attach(ic))
+			return -EINVAL;
+		ic->ic_acl = acl;
+	}
+	acl->iac_add(ic, sa->sa_data);
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_addmac);
+
+int
+ieee80211_ioctl_delmac(struct ieee80211com *ic, struct iw_request_info *info,
+		   	 void *w, char *extra)
+{
+	struct sockaddr *sa = (struct sockaddr *)extra;
+	const struct ieee80211_aclator *acl = ic->ic_acl;
+
+	if (acl == NULL) {
+		acl = ieee80211_aclator_get("mac");
+		if (acl == NULL || !acl->iac_attach(ic))
+			return -EINVAL;
+		ic->ic_acl = acl;
+	}
+	acl->iac_remove(ic, sa->sa_data);
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_delmac);
+
+int
+ieee80211_ioctl_chanlist(struct ieee80211com *ic, struct iw_request_info *info,
+			void *w, char *extra)
+{
+	struct ieee80211req_chanlist *list =
+		(struct ieee80211req_chanlist *)extra;
+	u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)];
+	int i, j;
+
+	memset(chanlist, 0, sizeof(chanlist));
+	/*
+	 * Since channel 0 is not available for DS, channel 1
+	 * is assigned to LSB on WaveLAN.
+	 */
+	if (ic->ic_phytype == IEEE80211_T_DS)
+		i = 1;
+	else
+		i = 0;
+	for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
+		/*
+		 * NB: silently discard unavailable channels so users
+		 *     can specify 1-255 to get all available channels.
+		 */
+		if (isset(list->ic_channels, j) && isset(ic->ic_chan_avail, i))
+			setbit(chanlist, i);
+	}
+	if (ic->ic_ibss_chan == NULL ||
+	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
+		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+			if (isset(chanlist, i)) {
+				ic->ic_ibss_chan = &ic->ic_channels[i];
+				goto found;
+			}
+		return EINVAL;			/* no active channels */
+found:
+		;
+	}
+	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
+	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
+	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
+		ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+	return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0;
+}
+EXPORT_SYMBOL(ieee80211_ioctl_chanlist);
+
+static int
+ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct iwreq *iwr)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_wpaie wpaie;
+
+	if (iwr->u.data.length != sizeof(wpaie))
+		return -EINVAL;
+	if (copy_from_user(&wpaie, iwr->u.data.pointer, IEEE80211_ADDR_LEN))
+		return -EFAULT;
+	ni = ieee80211_find_node(ic, wpaie.wpa_macaddr);
+	if (ni == NULL)
+		return -EINVAL;		/* XXX */
+	memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
+	if (ni->ni_wpa_ie != NULL) {
+		int ielen = ni->ni_wpa_ie[1] + 2;
+		if (ielen > sizeof(wpaie.wpa_ie))
+			ielen = sizeof(wpaie.wpa_ie);
+		memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
+	}
+	ieee80211_free_node(ic, ni);
+	return (copy_to_user(iwr->u.data.pointer, &wpaie, sizeof(wpaie)) ?
+			-EFAULT : 0);
+}
+
+#define	IW_PRIV_TYPE_OPTIE	IW_PRIV_TYPE_BYTE | IEEE80211_MAX_OPT_IE
+#define	IW_PRIV_TYPE_KEY \
+	IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_key)
+#define	IW_PRIV_TYPE_DELKEY \
+	IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_del_key)
+#define	IW_PRIV_TYPE_MLME \
+	IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_mlme)
+#define	IW_PRIV_TYPE_CHANLIST \
+	IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_chanlist)
+
+static const struct iw_priv_args ieee80211_priv_args[] = {
+	/* NB: setoptie & getoptie are !IW_PRIV_SIZE_FIXED */
+	{ IEEE80211_IOCTL_SETOPTIE,
+	  IW_PRIV_TYPE_OPTIE, 0,			"setoptie" },
+	{ IEEE80211_IOCTL_GETOPTIE,
+	  0, IW_PRIV_TYPE_OPTIE,			"getoptie" },
+	{ IEEE80211_IOCTL_SETKEY,
+	  IW_PRIV_TYPE_KEY | IW_PRIV_SIZE_FIXED, 0,	"setkey" },
+	{ IEEE80211_IOCTL_DELKEY,
+	  IW_PRIV_TYPE_DELKEY | IW_PRIV_SIZE_FIXED, 0,	"delkey" },
+	{ IEEE80211_IOCTL_SETMLME,
+	  IW_PRIV_TYPE_MLME | IW_PRIV_SIZE_FIXED, 0,	"setmlme" },
+	{ IEEE80211_IOCTL_ADDMAC,
+	  IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"addmac" },
+	{ IEEE80211_IOCTL_DELMAC,
+	  IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"delmac" },
+	{ IEEE80211_IOCTL_CHANLIST,
+	  IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED, 0,"chanlist" },
+#if WIRELESS_EXT >= 12
+	{ IEEE80211_IOCTL_SETPARAM,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setparam" },
+	/*
+	 * These depends on sub-ioctl support which added in version 12.
+	 */
+	{ IEEE80211_IOCTL_GETPARAM,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,	"getparam" },
+
+	/* sub-ioctl handlers */
+	{ IEEE80211_IOCTL_SETPARAM,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" },
+	{ IEEE80211_IOCTL_GETPARAM,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" },
+
+	/* sub-ioctl definitions */
+	{ IEEE80211_PARAM_TURBO,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "turbo" },
+	{ IEEE80211_PARAM_TURBO,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_turbo" },
+	{ IEEE80211_PARAM_MODE,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mode" },
+	{ IEEE80211_PARAM_MODE,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mode" },
+	{ IEEE80211_PARAM_AUTHMODE,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "authmode" },
+	{ IEEE80211_PARAM_AUTHMODE,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_authmode" },
+	{ IEEE80211_PARAM_PROTMODE,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "protmode" },
+	{ IEEE80211_PARAM_PROTMODE,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_protmode" },
+	{ IEEE80211_PARAM_MCASTCIPHER,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcastcipher" },
+	{ IEEE80211_PARAM_MCASTCIPHER,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcastcipher" },
+	{ IEEE80211_PARAM_MCASTKEYLEN,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcastkeylen" },
+	{ IEEE80211_PARAM_MCASTKEYLEN,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcastkeylen" },
+	{ IEEE80211_PARAM_UCASTCIPHERS,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ucastciphers" },
+	{ IEEE80211_PARAM_UCASTCIPHERS,
+	/*
+	 * NB: can't use "get_ucastciphers" 'cuz iwpriv command names
+	 *     must be <IFNAMESIZ which is 16.
+	 */
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_uciphers" },
+	{ IEEE80211_PARAM_UCASTCIPHER,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ucastcipher" },
+	{ IEEE80211_PARAM_UCASTCIPHER,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ucastcipher" },
+	{ IEEE80211_PARAM_UCASTKEYLEN,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ucastkeylen" },
+	{ IEEE80211_PARAM_UCASTKEYLEN,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ucastkeylen" },
+	{ IEEE80211_PARAM_KEYMGTALGS,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "keymgtalgs" },
+	{ IEEE80211_PARAM_KEYMGTALGS,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_keymgtalgs" },
+	{ IEEE80211_PARAM_RSNCAPS,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rsncaps" },
+	{ IEEE80211_PARAM_RSNCAPS,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rsncaps" },
+	{ IEEE80211_PARAM_ROAMING,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "roaming" },
+	{ IEEE80211_PARAM_ROAMING,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_roaming" },
+	{ IEEE80211_PARAM_PRIVACY,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy" },
+	{ IEEE80211_PARAM_PRIVACY,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_privacy" },
+	{ IEEE80211_PARAM_COUNTERMEASURES,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "countermeasures" },
+	{ IEEE80211_PARAM_COUNTERMEASURES,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_countermeas" },
+	{ IEEE80211_PARAM_DROPUNENCRYPTED,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dropunencrypted" },
+	{ IEEE80211_PARAM_DROPUNENCRYPTED,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dropunencry" },
+	{ IEEE80211_PARAM_WPA,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" },
+	{ IEEE80211_PARAM_WPA,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wpa" },
+	{ IEEE80211_PARAM_DRIVER_CAPS,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "driver_caps" },
+	{ IEEE80211_PARAM_DRIVER_CAPS,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_driver_caps" },
+	{ IEEE80211_PARAM_MACCMD,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" },
+	{ IEEE80211_PARAM_WME,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wme" },
+	{ IEEE80211_PARAM_WME,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wme" },
+	{ IEEE80211_PARAM_HIDESSID,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hide_ssid" },
+	{ IEEE80211_PARAM_HIDESSID,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_hide_ssid" },
+	{ IEEE80211_PARAM_APBRIDGE,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_bridge" },
+	{ IEEE80211_PARAM_APBRIDGE,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ap_bridge" },
+	{ IEEE80211_PARAM_INACT,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact" },
+	{ IEEE80211_PARAM_INACT,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact" },
+	{ IEEE80211_PARAM_INACT_AUTH,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact_auth" },
+	{ IEEE80211_PARAM_INACT_AUTH,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact_auth" },
+	{ IEEE80211_PARAM_INACT_INIT,
+	  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact_init" },
+	{ IEEE80211_PARAM_INACT_INIT,
+	  0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact_init" },
+#endif /* WIRELESS_EXT >= 12 */
+};
+
+void
+ieee80211_ioctl_iwsetup(struct iw_handler_def *def)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	def->private_args = (struct iw_priv_args *) ieee80211_priv_args;
+	def->num_private_args = N(ieee80211_priv_args);
+#undef N
+}
+EXPORT_SYMBOL(ieee80211_ioctl_iwsetup);
+
+/*
+ * Handle private ioctl requests.
+ */
+int
+ieee80211_ioctl(struct ieee80211com *ic, struct ifreq *ifr, int cmd)
+{
+
+	switch (cmd) {
+	case SIOCG80211STATS:
+		return copy_to_user(ifr->ifr_data, &ic->ic_stats,
+				sizeof (ic->ic_stats)) ? -EFAULT : 0;
+	case IEEE80211_IOCTL_GETKEY:
+		return ieee80211_ioctl_getkey(ic, (struct iwreq *) ifr);
+	case IEEE80211_IOCTL_GETWPAIE:
+		return ieee80211_ioctl_getwpaie(ic, (struct iwreq *) ifr);
+	}
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(ieee80211_ioctl);
+#endif /* CONFIG_NET_WIRELESS */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_xauth.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_xauth.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_xauth.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_xauth.c	2005-02-24 13:06:17.409115432 -0800
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2004 Video54 Technologies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+/*
+ * External authenticator placeholder module.
+ *
+ * This support is optional; it is only used when the 802.11 layer's
+ * authentication mode is set to use 802.1x or WPA is enabled separately
+ * (for WPA-PSK).  If compiled as a module this code does not need
+ * to be present unless 802.1x/WPA is in use.
+ *
+ * The authenticator hooks into the 802.11 layer.  At present we use none
+ * of the available callbacks--the user mode authenticator process works
+ * entirely from messages about stations joining and leaving.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/sysctl.h>
+#include <linux/in.h>
+
+#include "if_media.h"
+#include "if_llc.h"
+#include "if_ethersubr.h"
+
+#include <net80211/ieee80211_var.h>
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: external (user mode) authenticator");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+/*
+ * One module handles everything for now.  May want
+ * to split things up for embedded applications.
+ */
+static const struct ieee80211_authenticator xauth = {
+	.ia_name	= "external",
+	.ia_attach	= NULL,
+	.ia_detach	= NULL,
+	.ia_node_join	= NULL,
+	.ia_node_leave	= NULL,
+};
+
+static int __init
+init_ieee80211_xauth(void)
+{
+	ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth);
+	ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth);
+	return 0;
+}
+module_init(init_ieee80211_xauth);
+
+static void __exit
+exit_ieee80211_xauth(void)
+{
+	ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X);
+	ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA);
+}
+module_exit(exit_ieee80211_xauth);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_ethersubr.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_ethersubr.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_ethersubr.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_ethersubr.h	2005-02-24 13:06:17.409115432 -0800
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_ethersubr.h,v 1.3 2005/02/16 16:09:04 samleffler Exp $
+ */
+
+#ifndef _NET_IF_ETHERSUBR_H_
+#define _NET_IF_ETHERSUBR_H_
+
+#define	ETHER_ADDR_LEN		6	/* length of an Ethernet address */
+#define	ETHER_TYPE_LEN		2	/* length of the Ethernet type field */
+#define	ETHER_CRC_LEN		4	/* length of the Ethernet CRC */
+#define	ETHER_HDR_LEN		(ETHER_ADDR_LEN*2+ETHER_TYPE_LEN)
+#define	ETHER_MAX_LEN		1518
+
+#define	ETHERMTU	(ETHER_MAX_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN)
+
+/*
+ * Structure of a 10Mb/s Ethernet header.
+ */
+struct	ether_header {
+	u_char	ether_dhost[ETHER_ADDR_LEN];
+	u_char	ether_shost[ETHER_ADDR_LEN];
+	u_short	ether_type;
+};
+
+#ifndef ETHERTYPE_PAE
+#define	ETHERTYPE_PAE	0x888e		/* EAPOL PAE/802.1x */
+#endif
+#ifndef ETHERTYPE_IP
+#define	ETHERTYPE_IP	0x0800		/* IP protocol */
+#endif
+
+/*
+ * Structure of a 48-bit Ethernet address.
+ */
+struct	ether_addr {
+	u_char octet[ETHER_ADDR_LEN];
+};
+
+#define	ETHER_IS_MULTICAST(addr) (*(addr) & 0x01) /* is address mcast/bcast? */
+
+#endif /* _NET_IF_ETHERSUBR_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_llc.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_llc.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_llc.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_llc.h	2005-02-24 13:06:17.410115280 -0800
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_llc.h,v 1.3 2005/02/16 16:09:09 samleffler Exp $
+ * $NetBSD: if_llc.h,v 1.12 1999/11/19 20:41:19 thorpej Exp $
+ * $Id: if_llc.h,v 1.3 2005/02/16 16:09:09 samleffler Exp $
+ */
+
+/*
+ * Copyright (c) 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)if_llc.h	8.1 (Berkeley) 6/10/93
+ * $FreeBSD: src/sys/net/if_llc.h,v 1.9 2002/09/23 06:25:08 alfred Exp $
+ */
+
+#ifndef _NET_IF_LLC_H_
+#define _NET_IF_LLC_H_
+
+/*
+ * IEEE 802.2 Link Level Control headers, for use in conjunction with
+ * 802.{3,4,5} media access control methods.
+ *
+ * Headers here do not use bit fields due to shortcommings in many
+ * compilers.
+ */
+
+struct llc {
+	u_int8_t llc_dsap;
+	u_int8_t llc_ssap;
+	union {
+	    struct {
+		u_int8_t control;
+		u_int8_t format_id;
+		u_int8_t class;
+		u_int8_t window_x2;
+	    } type_u __packed;
+	    struct {
+		u_int8_t num_snd_x2;
+		u_int8_t num_rcv_x2;
+	    } type_i __packed;
+	    struct {
+		u_int8_t control;
+		u_int8_t num_rcv_x2;
+	    } type_s __packed;
+	    struct {
+	        u_int8_t control;
+		/*
+		 * We cannot put the following fields in a structure because
+		 * the structure rounding might cause padding.
+		 */
+		u_int8_t frmr_rej_pdu0;
+		u_int8_t frmr_rej_pdu1;
+		u_int8_t frmr_control;
+		u_int8_t frmr_control_ext;
+		u_int8_t frmr_cause;
+	    } type_frmr __packed;
+	    struct {
+		u_int8_t  control;
+		u_int8_t  org_code[3];
+		u_int16_t ether_type;
+	    } type_snap __packed;
+	    struct {
+		u_int8_t control;
+		u_int8_t control_ext;
+	    } type_raw __packed;
+	} llc_un /* XXX __packed ??? */;
+} __packed;
+
+struct frmrinfo {
+	u_int8_t frmr_rej_pdu0;
+	u_int8_t frmr_rej_pdu1;
+	u_int8_t frmr_control;
+	u_int8_t frmr_control_ext;
+	u_int8_t frmr_cause;
+} __packed;
+
+#define	llc_control		llc_un.type_u.control
+#define	llc_control_ext		llc_un.type_raw.control_ext
+#define	llc_fid			llc_un.type_u.format_id
+#define	llc_class		llc_un.type_u.class
+#define	llc_window		llc_un.type_u.window_x2
+#define	llc_frmrinfo 		llc_un.type_frmr.frmr_rej_pdu0
+#define	llc_frmr_pdu0		llc_un.type_frmr.frmr_rej_pdu0
+#define	llc_frmr_pdu1		llc_un.type_frmr.frmr_rej_pdu1
+#define	llc_frmr_control	llc_un.type_frmr.frmr_control
+#define	llc_frmr_control_ext	llc_un.type_frmr.frmr_control_ext
+#define	llc_frmr_cause		llc_un.type_frmr.frmr_cause
+#define	llc_snap		llc_un.type_snap
+
+/*
+ * Don't use sizeof(struct llc_un) for LLC header sizes
+ */
+#define LLC_ISFRAMELEN 4
+#define LLC_UFRAMELEN  3
+#define LLC_FRMRLEN    7
+#define LLC_SNAPFRAMELEN 8
+
+/*
+ * Unnumbered LLC format commands
+ */
+#define LLC_UI		0x3
+#define LLC_UI_P	0x13
+#define LLC_DISC	0x43
+#define	LLC_DISC_P	0x53
+#define LLC_UA		0x63
+#define LLC_UA_P	0x73
+#define LLC_TEST	0xe3
+#define LLC_TEST_P	0xf3
+#define LLC_FRMR	0x87
+#define	LLC_FRMR_P	0x97
+#define LLC_DM		0x0f
+#define	LLC_DM_P	0x1f
+#define LLC_XID		0xaf
+#define LLC_XID_P	0xbf
+#define LLC_SABME	0x6f
+#define LLC_SABME_P	0x7f
+
+/*
+ * Supervisory LLC commands
+ */
+#define	LLC_RR		0x01
+#define	LLC_RNR		0x05
+#define	LLC_REJ		0x09
+
+/*
+ * Info format - dummy only
+ */
+#define	LLC_INFO	0x00
+
+/*
+ * ISO PDTR 10178 contains among others
+ */
+#define LLC_X25_LSAP	0x7e
+#define LLC_SNAP_LSAP	0xaa
+#define LLC_ISO_LSAP	0xfe
+
+#endif /* _NET_IF_LLC_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.c	2005-02-24 13:06:17.411115128 -0800
@@ -0,0 +1,520 @@
+/*	$NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $	*/
+/* $FreeBSD: src/sys/net/if_media.c,v 1.17 2002/06/26 03:34:50 ken Exp $ */
+/*	$Id: if_media.c,v 1.2 2004/08/05 17:34:54 samleffler Exp $	*/
+
+/*
+ * Copyright (c) 1997
+ *	Jonathan Stone and Jason R. Thorpe.  All rights reserved.
+ *
+ * This software is derived from information provided by Matt Thomas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Jonathan Stone
+ *	and Jason R. Thorpe for the NetBSD Project.
+ * 4. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * BSD/OS-compatible network interface media selection.
+ *
+ * Where it is safe to do so, this code strays slightly from the BSD/OS
+ * design.  Software which uses the API (device drivers, basically)
+ * shouldn't notice any difference.
+ *
+ * Many thanks to Matt Thomas for providing the information necessary
+ * to implement this interface.
+ */
+
+#ifndef EXPORT_SYMTAB
+#define	EXPORT_SYMTAB
+#endif
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#ifndef ifr_media
+#define	ifr_media	ifr_ifru.ifru_ivalue
+#endif
+
+#include <asm/uaccess.h>
+
+#include "if_media.h"
+
+/*
+ * Compile-time options:
+ * IFMEDIA_DEBUG:
+ *	turn on implementation-level debug printfs.
+ * 	Useful for debugging newly-ported  drivers.
+ */
+
+struct ifmedia_entry *ifmedia_match(struct ifmedia *ifm, int flags, int mask);
+
+#ifdef IFMEDIA_DEBUG
+int	ifmedia_debug = 0;
+static	void ifmedia_printword(int);
+#endif
+
+/*
+ * Initialize if_media struct for a specific interface instance.
+ */
+void
+ifmedia_init(
+	struct ifmedia *ifm,
+	int dontcare_mask,
+	ifm_change_cb_t change_callback,
+	ifm_stat_cb_t status_callback)
+{
+
+	LIST_INIT(&ifm->ifm_list);
+	ifm->ifm_cur = NULL;
+	ifm->ifm_media = 0;
+	ifm->ifm_mask = dontcare_mask;		/* IF don't-care bits */
+	ifm->ifm_change = change_callback;
+	ifm->ifm_status = status_callback;
+}
+
+void
+ifmedia_removeall(struct ifmedia *ifm)
+{
+	struct ifmedia_entry *entry;
+
+	for (entry = LIST_FIRST(&ifm->ifm_list); entry;
+	     entry = LIST_FIRST(&ifm->ifm_list)) {
+		LIST_REMOVE(entry, ifm_list);
+		kfree(entry);
+	}
+}
+
+/*
+ * Add a media configuration to the list of supported media
+ * for a specific interface instance.
+ */
+void
+ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux)
+{
+	register struct ifmedia_entry *entry;
+
+#ifdef IFMEDIA_DEBUG
+	if (ifmedia_debug) {
+		if (ifm == NULL) {
+			printk("ifmedia_add: null ifm\n");
+			return;
+		}
+		printk("Adding entry for ");
+		ifmedia_printword(mword);
+	}
+#endif
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (entry == NULL)
+		panic("ifmedia_add: can't malloc entry");
+
+	entry->ifm_media = mword;
+	entry->ifm_data = data;
+	entry->ifm_aux = aux;
+
+	LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list);
+}
+
+/*
+ * Add an array of media configurations to the list of
+ * supported media for a specific interface instance.
+ */
+void
+ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data,
+		    lp[i].ifm_aux);
+}
+
+/*
+ * Set the default active media. 
+ *
+ * Called by device-specific code which is assumed to have already
+ * selected the default media in hardware.  We do _not_ call the
+ * media-change callback.
+ */
+void
+ifmedia_set(struct ifmedia *ifm, int target)
+
+{
+	struct ifmedia_entry *match;
+
+	match = ifmedia_match(ifm, target, ifm->ifm_mask);
+
+	if (match == NULL) {
+		printk("ifmedia_set: no match for 0x%x/0x%x\n",
+		    target, ~ifm->ifm_mask);
+		panic("ifmedia_set");
+	}
+	ifm->ifm_cur = match;
+
+#ifdef IFMEDIA_DEBUG
+	if (ifmedia_debug) {
+		printk("ifmedia_set: target ");
+		ifmedia_printword(target);
+		printk("ifmedia_set: setting to ");
+		ifmedia_printword(ifm->ifm_cur->ifm_media);
+	}
+#endif
+}
+
+/*
+ * Device-independent media ioctl support function.
+ */
+int
+ifmedia_ioctl(struct net_device *dev, struct ifreq *ifr,
+	struct ifmedia *ifm, u_long cmd)
+{
+	struct ifmedia_entry *match;
+	struct ifmediareq *ifmr = (struct ifmediareq *) ifr;
+	int error = 0, sticky;
+
+	if (dev == NULL || ifr == NULL || ifm == NULL)
+		return(EINVAL);
+
+	switch (cmd) {
+
+	/*
+	 * Set the current media.
+	 */
+	case  SIOCSIFMEDIA:
+	{
+		struct ifmedia_entry *oldentry;
+		int oldmedia;
+		int newmedia = ifr->ifr_media;
+
+		match = ifmedia_match(ifm, newmedia, ifm->ifm_mask);
+		if (match == NULL) {
+#ifdef IFMEDIA_DEBUG
+			if (ifmedia_debug) {
+				printk(
+				    "ifmedia_ioctl: no media found for 0x%x\n", 
+				    newmedia);
+			}
+#endif
+			return (ENXIO);
+		}
+
+		/*
+		 * If no change, we're done.
+		 * XXX Automedia may invole software intervention.
+		 *     Keep going in case the the connected media changed.
+		 *     Similarly, if best match changed (kernel debugger?).
+		 */
+		if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) &&
+		    (newmedia == ifm->ifm_media) &&
+		    (match == ifm->ifm_cur))
+			return 0;
+
+		/*
+		 * We found a match, now make the driver switch to it.
+		 * Make sure to preserve our old media type in case the
+		 * driver can't switch.
+		 */
+#ifdef IFMEDIA_DEBUG
+		if (ifmedia_debug) {
+			printk("ifmedia_ioctl: switching %s to ", dev->name);
+			ifmedia_printword(match->ifm_media);
+		}
+#endif
+		oldentry = ifm->ifm_cur;
+		oldmedia = ifm->ifm_media;
+		ifm->ifm_cur = match;
+		ifm->ifm_media = newmedia;
+		error = (*ifm->ifm_change)(dev);
+		if (error) {
+			ifm->ifm_cur = oldentry;
+			ifm->ifm_media = oldmedia;
+		}
+		break;
+	}
+
+	/*
+	 * Get list of available media and current media on interface.
+	 */
+	case  SIOCGIFMEDIA: 
+	{
+		struct ifmedia_entry *ep;
+		int *kptr, count;
+		int usermax;	/* user requested max */
+
+		kptr = NULL;		/* XXX gcc */
+
+		ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
+		    ifm->ifm_cur->ifm_media : IFM_NONE;
+		ifmr->ifm_mask = ifm->ifm_mask;
+		ifmr->ifm_status = 0;
+		(*ifm->ifm_status)(dev, ifmr);
+
+		count = 0;
+		usermax = 0;
+
+		/*
+		 * If there are more interfaces on the list, count
+		 * them.  This allows the caller to set ifmr->ifm_count
+		 * to 0 on the first call to know how much space to
+		 * allocate.
+		 */
+		LIST_FOREACH(ep, &ifm->ifm_list, ifm_list)
+			usermax++;
+
+		/*
+		 * Don't allow the user to ask for too many
+		 * or a negative number.
+		 */
+		if (ifmr->ifm_count > usermax)
+			ifmr->ifm_count = usermax;
+		else if (ifmr->ifm_count < 0)
+			return (EINVAL);
+
+		if (ifmr->ifm_count != 0) {
+			kptr = (int *)kmalloc(ifmr->ifm_count * sizeof(int),
+			    GFP_KERNEL);
+
+			if (kptr == NULL)
+				return (ENOMEM);
+			/*
+			 * Get the media words from the interface's list.
+			 */
+			ep = LIST_FIRST(&ifm->ifm_list);
+			for (; ep != NULL && count < ifmr->ifm_count;
+			    ep = LIST_NEXT(ep, ifm_list), count++)
+				kptr[count] = ep->ifm_media;
+
+			if (ep != NULL)
+				error = E2BIG;	/* oops! */
+		} else {
+			count = usermax;
+		}
+
+		/*
+		 * We do the copyout on E2BIG, because that's
+		 * just our way of telling userland that there
+		 * are more.  This is the behavior I've observed
+		 * under BSD/OS 3.0
+		 */
+		sticky = error;
+		if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) {
+			error = copy_to_user((caddr_t)ifmr->ifm_ulist,
+				(caddr_t)kptr, ifmr->ifm_count * sizeof(int));
+		}
+
+		if (error == 0)
+			error = sticky;
+
+		if (ifmr->ifm_count != 0)
+			kfree(kptr);
+
+		ifmr->ifm_count = count;
+		break;
+	}
+
+	default:
+		return (EINVAL);
+	}
+
+	return (error);
+}
+
+/*
+ * Find media entry matching a given ifm word.
+ *
+ */
+struct ifmedia_entry *
+ifmedia_match(struct ifmedia *ifm, int target, int mask)
+{
+	struct ifmedia_entry *match, *next;
+
+	match = NULL;
+	mask = ~mask;
+
+	LIST_FOREACH(next, &ifm->ifm_list, ifm_list) {
+		if ((next->ifm_media & mask) == (target & mask)) {
+#if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC)
+			if (match) {
+				printk("ifmedia_match: multiple match for "
+				    "0x%x/0x%x\n", target, mask);
+			}
+#endif
+			match = next;
+		}
+	}
+
+	return match;
+}
+
+#ifdef IFMEDIA_DEBUG
+struct ifmedia_description ifm_type_descriptions[] =
+    IFM_TYPE_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
+    IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
+    IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_tokenring_descriptions[] =
+    IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
+    IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_fddi_descriptions[] =
+    IFM_SUBTYPE_FDDI_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
+    IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_ieee80211_descriptions[] =
+    IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] =
+    IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] =
+    IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_shared_descriptions[] =
+    IFM_SUBTYPE_SHARED_DESCRIPTIONS;
+
+struct ifmedia_description ifm_shared_option_descriptions[] =
+    IFM_SHARED_OPTION_DESCRIPTIONS;
+
+struct ifmedia_type_to_subtype {
+	struct ifmedia_description *subtypes;
+	struct ifmedia_description *options;
+	struct ifmedia_description *modes;
+};
+
+/* must be in the same order as IFM_TYPE_DESCRIPTIONS */
+struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
+	{
+	  &ifm_subtype_ethernet_descriptions[0],
+	  &ifm_subtype_ethernet_option_descriptions[0],
+	  NULL,
+	},
+	{
+	  &ifm_subtype_tokenring_descriptions[0],
+	  &ifm_subtype_tokenring_option_descriptions[0],
+	  NULL,
+	},
+	{
+	  &ifm_subtype_fddi_descriptions[0],
+	  &ifm_subtype_fddi_option_descriptions[0],
+	  NULL,
+	},
+	{
+	  &ifm_subtype_ieee80211_descriptions[0],
+	  &ifm_subtype_ieee80211_option_descriptions[0],
+	  &ifm_subtype_ieee80211_mode_descriptions[0]
+	},
+};
+
+/*
+ * print a media word.
+ */
+static void
+ifmedia_printword(int ifmw)
+{
+	struct ifmedia_description *desc;
+	struct ifmedia_type_to_subtype *ttos;
+	int seen_option = 0;
+
+	/* Find the top-level interface type. */
+	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+	    desc->ifmt_string != NULL; desc++, ttos++)
+		if (IFM_TYPE(ifmw) == desc->ifmt_word)
+			break;
+	if (desc->ifmt_string == NULL) {
+		printk("<unknown type>\n");
+		return;
+	}
+	printk(desc->ifmt_string);
+
+	/* Any mode. */
+	for (desc = ttos->modes; desc && desc->ifmt_string != NULL; desc++)
+		if (IFM_MODE(ifmw) == desc->ifmt_word) {
+			if (desc->ifmt_string != NULL)
+				printk(" mode %s", desc->ifmt_string);
+			break;
+		}
+
+	/*
+	 * Check for the shared subtype descriptions first, then the
+	 * type-specific ones.
+	 */
+	for (desc = ifm_subtype_shared_descriptions;
+	    desc->ifmt_string != NULL; desc++)
+		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
+			goto got_subtype;
+
+	for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++)
+		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
+			break;
+	if (desc->ifmt_string == NULL) {
+		printk(" <unknown subtype>\n");
+		return;
+	}
+
+ got_subtype:
+	printk(" %s", desc->ifmt_string);
+
+	/*
+	 * Look for shared options.
+	 */
+	for (desc = ifm_shared_option_descriptions;
+	    desc->ifmt_string != NULL; desc++) {
+		if (ifmw & desc->ifmt_word) {
+			if (seen_option == 0)
+				printk(" <");
+			printk("%s%s", seen_option++ ? "," : "",
+			    desc->ifmt_string);
+		}
+	}
+
+	/*
+	 * Look for subtype-specific options.
+	 */
+	for (desc = ttos->options; desc->ifmt_string != NULL; desc++) {
+		if (ifmw & desc->ifmt_word) {
+			if (seen_option == 0)
+				printk(" <");
+			printk("%s%s", seen_option++ ? "," : "",
+			    desc->ifmt_string); 
+		}
+	}
+	printk("%s\n", seen_option ? ">" : "");
+}
+#endif /* IFMEDIA_DEBUG */
+
+EXPORT_SYMBOL(ifmedia_ioctl);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.h	2005-02-24 13:06:17.411115128 -0800
@@ -0,0 +1,478 @@
+/*	$NetBSD: if_media.h,v 1.3 1997/03/26 01:19:27 thorpej Exp $	*/
+/* $FreeBSD: src/sys/net/if_media.h,v 1.18 2002/07/14 21:58:19 kbyanc Exp $ */
+/*	$Id: if_media.h,v 1.2 2004/08/05 17:34:54 samleffler Exp $	*/
+
+/*
+ * Copyright (c) 1997
+ *	Jonathan Stone and Jason R. Thorpe.  All rights reserved.
+ *
+ * This software is derived from information provided by Matt Thomas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Jonathan Stone
+ *	and Jason R. Thorpe for the NetBSD Project.
+ * 4. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_IF_MEDIA_H_
+#define _NET_IF_MEDIA_H_
+
+/*
+ * Prototypes and definitions for BSD/OS-compatible network interface
+ * media selection.
+ *
+ * Where it is safe to do so, this code strays slightly from the BSD/OS
+ * design.  Software which uses the API (device drivers, basically)
+ * shouldn't notice any difference.
+ *
+ * Many thanks to Matt Thomas for providing the information necessary
+ * to implement this interface.
+ */
+
+struct ifmediareq {
+	char	ifm_name[IFNAMSIZ];	/* if name, e.g. "en0" */
+	int	ifm_current;		/* current media options */
+	int	ifm_mask;		/* don't care mask */
+	int	ifm_status;		/* media status */
+	int	ifm_active;		/* active options */
+	int	ifm_count;		/* # entries in ifm_ulist array */
+	int	*ifm_ulist;		/* media words */
+};
+#define	SIOCSIFMEDIA	_IOWR('i', 55, struct ifreq)	/* set net media */
+#define	SIOCGIFMEDIA	_IOWR('i', 56, struct ifmediareq) /* get net media */
+
+#ifdef __KERNEL__
+
+#include <sys/queue.h>
+
+/*
+ * Driver callbacks for media status and change requests.
+ */
+struct net_device;
+typedef	int (*ifm_change_cb_t)(struct net_device *);
+typedef	void (*ifm_stat_cb_t)(struct net_device *, struct ifmediareq *req);
+
+/*
+ * In-kernel representation of a single supported media type.
+ */
+struct ifmedia_entry {
+	LIST_ENTRY(ifmedia_entry) ifm_list;
+	int	ifm_media;	/* description of this media attachment */
+	int	ifm_data;	/* for driver-specific use */
+	void	*ifm_aux;	/* for driver-specific use */
+};
+
+/*
+ * One of these goes into a network interface's softc structure.
+ * It is used to keep general media state.
+ */
+struct ifmedia {
+	int	ifm_mask;	/* mask of changes we don't care about */
+	int	ifm_media;	/* current user-set media word */
+	struct ifmedia_entry *ifm_cur;	/* currently selected media */
+	ATH_LIST_HEAD(, ifmedia_entry) ifm_list; /* list of all supported media */
+	ifm_change_cb_t	ifm_change;	/* media change driver callback */
+	ifm_stat_cb_t	ifm_status;	/* media status driver callback */
+};
+
+/* Initialize an interface's struct if_media field. */
+void	ifmedia_init(struct ifmedia *ifm, int dontcare_mask,
+	    ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback);
+
+/* Remove all mediums from a struct ifmedia.  */
+void	ifmedia_removeall( struct ifmedia *ifm);
+
+/* Add one supported medium to a struct ifmedia. */
+void	ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux);
+
+/* Add an array (of ifmedia_entry) media to a struct ifmedia. */
+void	ifmedia_list_add(struct ifmedia *mp, struct ifmedia_entry *lp,
+	    int count);
+
+/* Set default media type on initialization. */
+void	ifmedia_set(struct ifmedia *ifm, int mword);
+
+/* Common ioctl function for getting/setting media, called by driver. */
+int	ifmedia_ioctl(struct net_device *, struct ifreq *ifr,
+	    struct ifmedia *ifm, u_long cmd);
+
+#endif /*_KERNEL */
+
+/*
+ * if_media Options word:
+ *	Bits	Use
+ *	----	-------
+ *	0-4	Media variant
+ *	5-7	Media type
+ *	8-15	Type specific options
+ *	16-18	Mode (for multi-mode devices)
+ *	19	RFU
+ *	20-27	Shared (global) options
+ *	28-31	Instance
+ */
+
+/*
+ * Ethernet
+ */
+#define	IFM_ETHER	0x00000020
+#define	IFM_10_T	3		/* 10BaseT - RJ45 */
+#define	IFM_10_2	4		/* 10Base2 - Thinnet */
+#define	IFM_10_5	5		/* 10Base5 - AUI */
+#define	IFM_100_TX	6		/* 100BaseTX - RJ45 */
+#define	IFM_100_FX	7		/* 100BaseFX - Fiber */
+#define	IFM_100_T4	8		/* 100BaseT4 - 4 pair cat 3 */
+#define	IFM_100_VG	9		/* 100VG-AnyLAN */
+#define	IFM_100_T2	10		/* 100BaseT2 */
+#define	IFM_1000_SX	11		/* 1000BaseSX - multi-mode fiber */
+#define	IFM_10_STP	12		/* 10BaseT over shielded TP */
+#define	IFM_10_FL	13		/* 10BaseFL - Fiber */
+#define	IFM_1000_LX	14		/* 1000baseLX - single-mode fiber */
+#define	IFM_1000_CX	15		/* 1000baseCX - 150ohm STP */
+#define	IFM_1000_T	16		/* 1000baseT - 4 pair cat 5 */
+#define	IFM_HPNA_1	17		/* HomePNA 1.0 (1Mb/s) */
+/* note 31 is the max! */
+
+#define	IFM_ETH_MASTER	0x00000100	/* master mode (1000baseT) */
+
+/*
+ * Token ring
+ */
+#define	IFM_TOKEN	0x00000040
+#define	IFM_TOK_STP4	3		/* Shielded twisted pair 4m - DB9 */
+#define	IFM_TOK_STP16	4		/* Shielded twisted pair 16m - DB9 */
+#define	IFM_TOK_UTP4	5		/* Unshielded twisted pair 4m - RJ45 */
+#define	IFM_TOK_UTP16	6		/* Unshielded twisted pair 16m - RJ45 */
+#define	IFM_TOK_STP100  7		/* Shielded twisted pair 100m - DB9 */
+#define	IFM_TOK_UTP100  8		/* Unshielded twisted pair 100m - RJ45 */
+#define	IFM_TOK_ETR	0x00000200	/* Early token release */
+#define	IFM_TOK_SRCRT	0x00000400	/* Enable source routing features */
+#define	IFM_TOK_ALLR	0x00000800	/* All routes / Single route bcast */
+#define	IFM_TOK_DTR	0x00002000	/* Dedicated token ring */
+#define	IFM_TOK_CLASSIC	0x00004000	/* Classic token ring */
+#define	IFM_TOK_AUTO	0x00008000	/* Automatic Dedicate/Classic token ring */
+
+/*
+ * FDDI
+ */
+#define	IFM_FDDI	0x00000060
+#define	IFM_FDDI_SMF	3		/* Single-mode fiber */
+#define	IFM_FDDI_MMF	4		/* Multi-mode fiber */
+#define	IFM_FDDI_UTP	5		/* CDDI / UTP */
+#define	IFM_FDDI_DA	0x00000100	/* Dual attach / single attach */
+
+/*
+ * IEEE 802.11 Wireless
+ */
+#define	IFM_IEEE80211	0x00000080
+/* NB: 0,1,2 are auto, manual, none defined below */
+#define	IFM_IEEE80211_FH1	3	/* Frequency Hopping 1Mbps */
+#define	IFM_IEEE80211_FH2	4	/* Frequency Hopping 2Mbps */
+#define	IFM_IEEE80211_DS1	5	/* Direct Sequence 1Mbps */
+#define	IFM_IEEE80211_DS2	6	/* Direct Sequence 2Mbps */
+#define	IFM_IEEE80211_DS5	7	/* Direct Sequence 5.5Mbps */
+#define	IFM_IEEE80211_DS11	8	/* Direct Sequence 11Mbps */
+#define	IFM_IEEE80211_DS22	9	/* Direct Sequence 22Mbps */
+#define	IFM_IEEE80211_OFDM6	10	/* OFDM 6Mbps */
+#define	IFM_IEEE80211_OFDM9	11	/* OFDM 9Mbps */
+#define	IFM_IEEE80211_OFDM12	12	/* OFDM 12Mbps */
+#define	IFM_IEEE80211_OFDM18	13	/* OFDM 18Mbps */
+#define	IFM_IEEE80211_OFDM24	14	/* OFDM 24Mbps */
+#define	IFM_IEEE80211_OFDM36	15	/* OFDM 36Mbps */
+#define	IFM_IEEE80211_OFDM48	16	/* OFDM 48Mbps */
+#define	IFM_IEEE80211_OFDM54	17	/* OFDM 54Mbps */
+#define	IFM_IEEE80211_OFDM72	18	/* OFDM 72Mbps */
+
+#define	IFM_IEEE80211_ADHOC	0x00000100	/* Operate in Adhoc mode */
+#define	IFM_IEEE80211_HOSTAP	0x00000200	/* Operate in Host AP mode */
+#define	IFM_IEEE80211_IBSS	0x00000400	/* Operate in IBSS mode */
+#define	IFM_IEEE80211_IBSSMASTER 0x00000800	/* Operate as an IBSS master */
+#define	IFM_IEEE80211_TURBO	0x00001000	/* Operate in turbo mode */
+#define	IFM_IEEE80211_MONITOR	0x00002000	/* Operate in monitor mode */
+
+/* operating mode for multi-mode devices */
+#define	IFM_IEEE80211_11A	0x00010000	/* 5Ghz, OFDM mode */
+#define	IFM_IEEE80211_11B	0x00020000	/* Direct Sequence mode */
+#define	IFM_IEEE80211_11G	0x00030000	/* 2Ghz, CCK mode */
+#define	IFM_IEEE80211_FH	0x00040000	/* 2Ghz, GFSK mode */
+
+/*
+ * Shared media sub-types
+ */
+#define	IFM_AUTO	0		/* Autoselect best media */
+#define	IFM_MANUAL	1		/* Jumper/dipswitch selects media */
+#define	IFM_NONE	2		/* Deselect all media */
+
+/*
+ * Shared options
+ */
+#define	IFM_FDX		0x00100000	/* Force full duplex */
+#define	IFM_HDX		0x00200000	/* Force half duplex */
+#define	IFM_FLAG0	0x01000000	/* Driver defined flag */
+#define	IFM_FLAG1	0x02000000	/* Driver defined flag */
+#define	IFM_FLAG2	0x04000000	/* Driver defined flag */
+#define	IFM_LOOP	0x08000000	/* Put hardware in loopback */
+
+/*
+ * Masks
+ */
+#define	IFM_NMASK	0x000000e0	/* Network type */
+#define	IFM_TMASK	0x0000001f	/* Media sub-type */
+#define	IFM_IMASK	0xf0000000	/* Instance */
+#define	IFM_ISHIFT	28		/* Instance shift */
+#define	IFM_OMASK	0x0000ff00	/* Type specific options */
+#define	IFM_MMASK	0x00070000	/* Mode */
+#define	IFM_MSHIFT	16		/* Mode shift */
+#define	IFM_GMASK	0x0ff00000	/* Global options */
+
+/*
+ * Status bits
+ */
+#define	IFM_AVALID	0x00000001	/* Active bit valid */
+#define	IFM_ACTIVE	0x00000002	/* Interface attached to working net */
+
+/*
+ * Macros to extract various bits of information from the media word.
+ */
+#define	IFM_TYPE(x)         ((x) & IFM_NMASK)
+#define	IFM_SUBTYPE(x)      ((x) & IFM_TMASK)
+#define	IFM_TYPE_OPTIONS(x) ((x) & IFM_OMASK)
+#define	IFM_INST(x)         (((x) & IFM_IMASK) >> IFM_ISHIFT)
+#define	IFM_OPTIONS(x)	((x) & (IFM_OMASK|IFM_GMASK))
+#define	IFM_MODE(x)	    ((x) & IFM_MMASK)
+
+#define	IFM_INST_MAX	IFM_INST(IFM_IMASK)
+
+/*
+ * Macro to create a media word.
+ */
+#define	IFM_MAKEWORD(type, subtype, options, instance)			\
+	((type) | (subtype) | (options) | ((instance) << IFM_ISHIFT))
+#define	IFM_MAKEMODE(mode) \
+	(((mode) << IFM_MSHIFT) & IFM_MMASK)
+
+/*
+ * NetBSD extension not defined in the BSDI API.  This is used in various
+ * places to get the canonical description for a given type/subtype.
+ *
+ * NOTE: all but the top-level type descriptions must contain NO whitespace!
+ * Otherwise, parsing these in ifconfig(8) would be a nightmare.
+ */
+struct ifmedia_description {
+	int	ifmt_word;		/* word value; may be masked */
+	const char *ifmt_string;	/* description */
+};
+
+#define	IFM_TYPE_DESCRIPTIONS {						\
+	{ IFM_ETHER,		"Ethernet" },				\
+	{ IFM_TOKEN,		"Token ring" },				\
+	{ IFM_FDDI,		"FDDI" },				\
+	{ IFM_IEEE80211,	"IEEE 802.11 Wireless Ethernet" },	\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_ETHERNET_DESCRIPTIONS {				\
+	{ IFM_10_T,	"10baseT/UTP" },				\
+	{ IFM_10_2,	"10base2/BNC" },				\
+	{ IFM_10_5,	"10base5/AUI" },				\
+	{ IFM_100_TX,	"100baseTX" },					\
+	{ IFM_100_FX,	"100baseFX" },					\
+	{ IFM_100_T4,	"100baseT4" },					\
+	{ IFM_100_VG,	"100baseVG" },					\
+	{ IFM_100_T2,	"100baseT2" },					\
+	{ IFM_10_STP,	"10baseSTP" },					\
+	{ IFM_10_FL,	"10baseFL" },					\
+	{ IFM_1000_SX,	"1000baseSX" },					\
+	{ IFM_1000_LX,	"1000baseLX" },					\
+	{ IFM_1000_CX,	"1000baseCX" },					\
+	{ IFM_1000_T,	"1000baseTX" },					\
+	{ IFM_1000_T,	"1000baseT" },					\
+	{ IFM_HPNA_1,	"homePNA" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_ETHERNET_ALIASES {					\
+	{ IFM_10_T,	"UTP" },					\
+	{ IFM_10_T,	"10UTP" },					\
+	{ IFM_10_2,	"BNC" },					\
+	{ IFM_10_2,	"10BNC" },					\
+	{ IFM_10_5,	"AUI" },					\
+	{ IFM_10_5,	"10AUI" },					\
+	{ IFM_100_TX,	"100TX" },					\
+	{ IFM_100_T4,	"100T4" },					\
+	{ IFM_100_VG,	"100VG" },					\
+	{ IFM_100_T2,	"100T2" },					\
+	{ IFM_10_STP,	"10STP" },					\
+	{ IFM_10_FL,	"10FL" },					\
+	{ IFM_1000_SX,	"1000SX" },					\
+	{ IFM_1000_LX,	"1000LX" },					\
+	{ IFM_1000_CX,	"1000CX" },					\
+	{ IFM_1000_T,	"1000TX" },					\
+	{ IFM_1000_T,	"1000T" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS {			\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_TOKENRING_DESCRIPTIONS {				\
+	{ IFM_TOK_STP4,	"DB9/4Mbit" },					\
+	{ IFM_TOK_STP16, "DB9/16Mbit" },				\
+	{ IFM_TOK_UTP4,	"UTP/4Mbit" },					\
+	{ IFM_TOK_UTP16, "UTP/16Mbit" },				\
+	{ IFM_TOK_STP100, "STP/100Mbit" },				\
+	{ IFM_TOK_UTP100, "UTP/100Mbit" },				\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_TOKENRING_ALIASES {					\
+	{ IFM_TOK_STP4,	"4STP" },					\
+	{ IFM_TOK_STP16, "16STP" },					\
+	{ IFM_TOK_UTP4,	"4UTP" },					\
+	{ IFM_TOK_UTP16, "16UTP" },					\
+	{ IFM_TOK_STP100, "100STP" },					\
+	{ IFM_TOK_UTP100, "100UTP" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS {			\
+	{ IFM_TOK_ETR,	"EarlyTokenRelease" },				\
+	{ IFM_TOK_SRCRT, "SourceRouting" },				\
+	{ IFM_TOK_ALLR,	"AllRoutes" },					\
+	{ IFM_TOK_DTR,	"Dedicated" },					\
+	{ IFM_TOK_CLASSIC,"Classic" },					\
+	{ IFM_TOK_AUTO,	" " },						\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_FDDI_DESCRIPTIONS {					\
+	{ IFM_FDDI_SMF, "Single-mode" },				\
+	{ IFM_FDDI_MMF, "Multi-mode" },					\
+	{ IFM_FDDI_UTP, "UTP" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_FDDI_ALIASES {					\
+	{ IFM_FDDI_SMF,	"SMF" },					\
+	{ IFM_FDDI_MMF,	"MMF" },					\
+	{ IFM_FDDI_UTP,	"CDDI" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS {				\
+	{ IFM_FDDI_DA, "Dual-attach" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_IEEE80211_DESCRIPTIONS {				\
+	{ IFM_IEEE80211_FH1, "FH/1Mbps" },				\
+	{ IFM_IEEE80211_FH2, "FH/2Mbps" },				\
+	{ IFM_IEEE80211_DS1, "DS/1Mbps" },				\
+	{ IFM_IEEE80211_DS2, "DS/2Mbps" },				\
+	{ IFM_IEEE80211_DS5, "DS/5.5Mbps" },				\
+	{ IFM_IEEE80211_DS11, "DS/11Mbps" },				\
+	{ IFM_IEEE80211_DS22, "DS/22Mbps" },				\
+	{ IFM_IEEE80211_OFDM6, "OFDM/6Mbps" },				\
+	{ IFM_IEEE80211_OFDM9, "OFDM/9Mbps" },				\
+	{ IFM_IEEE80211_OFDM12, "OFDM/12Mbps" },			\
+	{ IFM_IEEE80211_OFDM18, "OFDM/18Mbps" },			\
+	{ IFM_IEEE80211_OFDM24, "OFDM/24Mbps" },			\
+	{ IFM_IEEE80211_OFDM36, "OFDM/36Mbps" },			\
+	{ IFM_IEEE80211_OFDM48, "OFDM/48Mbps" },			\
+	{ IFM_IEEE80211_OFDM54, "OFDM/54Mbps" },			\
+	{ IFM_IEEE80211_OFDM72, "OFDM/72Mbps" },			\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_IEEE80211_ALIASES {					\
+	{ IFM_IEEE80211_FH1, "FH1" },					\
+	{ IFM_IEEE80211_FH2, "FH2" },					\
+	{ IFM_IEEE80211_FH1, "FrequencyHopping/1Mbps" },		\
+	{ IFM_IEEE80211_FH2, "FrequencyHopping/2Mbps" },		\
+	{ IFM_IEEE80211_DS1, "DS1" },					\
+	{ IFM_IEEE80211_DS2, "DS2" },					\
+	{ IFM_IEEE80211_DS5, "DS5.5" },					\
+	{ IFM_IEEE80211_DS11, "DS11" },					\
+	{ IFM_IEEE80211_DS22, "DS22" },					\
+	{ IFM_IEEE80211_DS1, "DirectSequence/1Mbps" },			\
+	{ IFM_IEEE80211_DS2, "DirectSequence/2Mbps" },			\
+	{ IFM_IEEE80211_DS5, "DirectSequence/5.5Mbps" },		\
+	{ IFM_IEEE80211_DS11, "DirectSequence/11Mbps" },		\
+	{ IFM_IEEE80211_DS22, "DirectSequence/22Mbps" },		\
+	{ IFM_IEEE80211_OFDM6, "OFDM6" },				\
+	{ IFM_IEEE80211_OFDM9, "OFDM9" },				\
+	{ IFM_IEEE80211_OFDM12, "OFDM12" },				\
+	{ IFM_IEEE80211_OFDM18, "OFDM18" },				\
+	{ IFM_IEEE80211_OFDM24, "OFDM24" },				\
+	{ IFM_IEEE80211_OFDM36, "OFDM36" },				\
+	{ IFM_IEEE80211_OFDM48, "OFDM48" },				\
+	{ IFM_IEEE80211_OFDM54, "OFDM54" },				\
+	{ IFM_IEEE80211_OFDM72, "OFDM72" },				\
+	{ IFM_IEEE80211_DS1, "CCK1" },					\
+	{ IFM_IEEE80211_DS2, "CCK2" },					\
+	{ IFM_IEEE80211_DS5, "CCK5.5" },				\
+	{ IFM_IEEE80211_DS11, "CCK11" },				\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS {			\
+	{ IFM_IEEE80211_ADHOC, "adhoc" },				\
+	{ IFM_IEEE80211_HOSTAP, "hostap" },				\
+	{ IFM_IEEE80211_IBSS, "ibss" },					\
+	{ IFM_IEEE80211_IBSSMASTER, "ibss-master" },			\
+	{ IFM_IEEE80211_TURBO, "turbo" },				\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS {			\
+	{ IFM_IEEE80211_11A, "11a" },					\
+	{ IFM_IEEE80211_11B, "11b" },					\
+	{ IFM_IEEE80211_11G, "11g" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_SHARED_DESCRIPTIONS {				\
+	{ IFM_AUTO,	"autoselect" },					\
+	{ IFM_MANUAL,	"manual" },					\
+	{ IFM_NONE,	"none" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SUBTYPE_SHARED_ALIASES {					\
+	{ IFM_AUTO,	"auto" },					\
+	{ 0, NULL },							\
+}
+
+#define	IFM_SHARED_OPTION_DESCRIPTIONS {				\
+	{ IFM_FDX,	"full-duplex" },				\
+	{ IFM_HDX,	"half-duplex" },				\
+	{ IFM_FLAG0,	"flag0" },					\
+	{ IFM_FLAG1,	"flag1" },					\
+	{ IFM_FLAG2,	"flag2" },					\
+	{ IFM_LOOP,	"hw-loopback" },				\
+	{ 0, NULL },							\
+}
+
+#endif	/* _NET_IF_MEDIA_H_ */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_wavelan_ieee.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_wavelan_ieee.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_wavelan_ieee.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_wavelan_ieee.h	2005-02-24 13:06:17.412114976 -0800
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 1997, 1998, 1999
+ *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/wi/if_wavelan_ieee.h,v 1.17 2003/01/15 20:11:31 sam Exp $
+ * $Id: if_wavelan_ieee.h,v 1.2 2004/08/05 17:34:54 samleffler Exp $
+ */
+
+#ifndef _IF_WAVELAN_IEEE_H
+#define _IF_WAVELAN_IEEE_H
+
+/*
+ * This header defines a simple command interface to the FreeBSD
+ * WaveLAN/IEEE driver (wi) driver, which is used to set certain
+ * device-specific parameters which can't be easily managed through
+ * ifconfig(8). No, sysctl(2) is not the answer. I said a _simple_
+ * interface, didn't I.
+ */
+
+#ifndef SIOCSWAVELAN
+#define SIOCSWAVELAN	SIOCSIFGENERIC
+#endif
+
+#ifndef SIOCGWAVELAN
+#define SIOCGWAVELAN	SIOCGIFGENERIC
+#endif
+
+/*
+ * Technically I don't think there's a limit to a record
+ * length. The largest record is the one that contains the CIS
+ * data, which is 240 words long, so 256 should be a safe
+ * value.
+ */
+#define WI_MAX_DATALEN	512
+
+struct wi_req {
+	u_int16_t	wi_len;
+	u_int16_t	wi_type;
+	u_int16_t	wi_val[WI_MAX_DATALEN];
+};
+
+/*
+ * Private LTV records (interpreted only by the driver). This is
+ * a minor kludge to allow reading the interface statistics from
+ * the driver.
+ */
+#define WI_RID_IFACE_STATS	0x0100
+#define WI_RID_MGMT_XMIT	0x0200
+#define WI_RID_ZERO_CACHE	0x0300
+#define WI_RID_READ_CACHE	0x0400
+#define WI_RID_FWDOWNLOAD	0x0500
+#define WI_RID_MONITOR_MODE	0x0600
+#define WI_RID_MIF		0x0700
+#define	WI_RID_SCAN_APS		0x0800
+#define	WI_RID_READ_APS		0x0900
+
+struct wi_80211_hdr {
+	u_int16_t		frame_ctl;
+	u_int16_t		dur_id;
+	u_int8_t		addr1[6];
+	u_int8_t		addr2[6];
+	u_int8_t		addr3[6];
+	u_int16_t		seq_ctl;
+	u_int8_t		addr4[6];
+};
+
+#define WI_FCTL_VERS		0x0002
+#define WI_FCTL_FTYPE		0x000C
+#define WI_FCTL_STYPE		0x00F0
+#define WI_FCTL_TODS		0x0100
+#define WI_FCTL_FROMDS		0x0200
+#define WI_FCTL_MOREFRAGS	0x0400
+#define WI_FCTL_RETRY		0x0800
+#define WI_FCTL_PM		0x1000
+#define WI_FCTL_MOREDATA	0x2000
+#define WI_FCTL_WEP		0x4000
+#define WI_FCTL_ORDER		0x8000
+
+#define WI_FTYPE_MGMT		0x0000
+#define WI_FTYPE_CTL		0x0004
+#define WI_FTYPE_DATA		0x0008
+
+#define WI_STYPE_MGMT_ASREQ	0x0000	/* association request */
+#define WI_STYPE_MGMT_ASRESP	0x0010	/* association response */
+#define WI_STYPE_MGMT_REASREQ	0x0020	/* reassociation request */
+#define WI_STYPE_MGMT_REASRESP	0x0030	/* reassociation response */
+#define WI_STYPE_MGMT_PROBEREQ	0x0040	/* probe request */
+#define WI_STYPE_MGMT_PROBERESP	0x0050	/* probe response */
+#define WI_STYPE_MGMT_BEACON	0x0080	/* beacon */
+#define WI_STYPE_MGMT_ATIM	0x0090	/* announcement traffic ind msg */
+#define WI_STYPE_MGMT_DISAS	0x00A0	/* disassociation */
+#define WI_STYPE_MGMT_AUTH	0x00B0	/* authentication */
+#define WI_STYPE_MGMT_DEAUTH	0x00C0	/* deauthentication */
+
+#define WI_STYPE_CTL_PSPOLL     0x00A0
+#define WI_STYPE_CTL_RTS        0x00B0
+#define WI_STYPE_CTL_CTS        0x00C0
+#define WI_STYPE_CTL_ACK        0x00D0
+#define WI_STYPE_CTL_CFEND      0x00E0
+#define WI_STYPE_CTL_CFENDACK   0x00F0
+
+struct wi_mgmt_hdr {
+	u_int16_t		frame_ctl;
+	u_int16_t		duration;
+	u_int8_t		dst_addr[6];
+	u_int8_t		src_addr[6];
+	u_int8_t		bssid[6];
+	u_int16_t		seq_ctl;
+};
+
+/* 
+ * Lucent/wavelan IEEE signal strength cache
+ *
+ * driver keeps cache of last
+ * MAXWICACHE packets to arrive including signal strength info.
+ * daemons may read this via ioctl
+ *
+ * Each entry in the wi_sigcache has a unique macsrc.
+ */
+struct wi_sigcache {
+	char	macsrc[6];	/* unique MAC address for entry */
+	int	ipsrc;		/* ip address associated with packet */
+	int	signal;		/* signal strength of the packet */
+	int	noise;		/* noise value */
+	int	quality;	/* quality of the packet */
+};
+
+/*
+ * Firmware downloading API.  We support downloading into RAM and into
+ * flash.  We copy the entire .hex file for both the primary and secondary
+ * firmware into the kernel, which is minorly gross, but matches the
+ * format of the compiled in firmware.
+ */
+struct wi_fwdownload {
+	int	type;		/* What type of download. */
+#define WI_FW_RAM	1
+#define WI_FW_FLASH	2
+	size_t	pri_len;	/* Primary firmware length */
+	size_t	sec_len;	/* Secondary firmware length */
+	caddr_t	pri_data;	/* Pointer (user) to primary data */
+	caddr_t sec_data;	/* Pointer (user) to secondary data */
+};
+
+struct wi_counters {
+	u_int32_t		wi_tx_unicast_frames;
+	u_int32_t		wi_tx_multicast_frames;
+	u_int32_t		wi_tx_fragments;
+	u_int32_t		wi_tx_unicast_octets;
+	u_int32_t		wi_tx_multicast_octets;
+	u_int32_t		wi_tx_deferred_xmits;
+	u_int32_t		wi_tx_single_retries;
+	u_int32_t		wi_tx_multi_retries;
+	u_int32_t		wi_tx_retry_limit;
+	u_int32_t		wi_tx_discards;
+	u_int32_t		wi_rx_unicast_frames;
+	u_int32_t		wi_rx_multicast_frames;
+	u_int32_t		wi_rx_fragments;
+	u_int32_t		wi_rx_unicast_octets;
+	u_int32_t		wi_rx_multicast_octets;
+	u_int32_t		wi_rx_fcs_errors;
+	u_int32_t		wi_rx_discards_nobuf;
+	u_int32_t		wi_tx_discards_wrong_sa;
+	u_int32_t		wi_rx_WEP_cant_decrypt;
+	u_int32_t		wi_rx_msg_in_msg_frags;
+	u_int32_t		wi_rx_msg_in_bad_msg_frags;
+};
+
+/*
+ * Network parameters, static configuration entities.
+ */
+#define WI_RID_PORTTYPE		0xFC00 /* Connection control characteristics */
+#define WI_RID_MAC_NODE		0xFC01 /* MAC address of this station */
+#define WI_RID_DESIRED_SSID	0xFC02 /* Service Set ID for connection */
+#define WI_RID_OWN_CHNL		0xFC03 /* Comm channel for BSS creation */
+#define WI_RID_OWN_SSID		0xFC04 /* IBSS creation ID */
+#define WI_RID_OWN_ATIM_WIN	0xFC05 /* ATIM window time for IBSS creation */
+#define WI_RID_SYSTEM_SCALE	0xFC06 /* scale that specifies AP density */
+#define WI_RID_MAX_DATALEN	0xFC07 /* Max len of MAC frame body data */
+#define WI_RID_MAC_WDS		0xFC08 /* MAC addr of corresponding WDS node */
+#define WI_RID_PM_ENABLED	0xFC09 /* ESS power management enable */
+#define WI_RID_PM_EPS		0xFC0A /* PM EPS/PS mode */
+#define WI_RID_MCAST_RX		0xFC0B /* ESS PM mcast reception */
+#define WI_RID_MAX_SLEEP	0xFC0C /* max sleep time for ESS PM */
+#define WI_RID_HOLDOVER		0xFC0D /* holdover time for ESS PM */
+#define WI_RID_NODENAME		0xFC0E /* ID name of this node for diag */
+#define WI_RID_DTIM_PERIOD	0xFC10 /* beacon interval between DTIMs */
+#define WI_RID_WDS_ADDR1	0xFC11 /* port 1 MAC of WDS link node */
+#define WI_RID_WDS_ADDR2	0xFC12 /* port 1 MAC of WDS link node */
+#define WI_RID_WDS_ADDR3	0xFC13 /* port 1 MAC of WDS link node */
+#define WI_RID_WDS_ADDR4	0xFC14 /* port 1 MAC of WDS link node */
+#define WI_RID_WDS_ADDR5	0xFC15 /* port 1 MAC of WDS link node */
+#define WI_RID_WDS_ADDR6	0xFC16 /* port 1 MAC of WDS link node */
+#define WI_RID_MCAST_PM_BUF	0xFC17 /* PM buffering of mcast */
+#define WI_RID_ENCRYPTION	0xFC20 /* enable/disable WEP */
+#define WI_RID_AUTHTYPE		0xFC21 /* specify authentication type */
+#define WI_RID_P2_TX_CRYPT_KEY	0xFC23
+#define WI_RID_P2_CRYPT_KEY0	0xFC24
+#define WI_RID_P2_CRYPT_KEY1	0xFC25
+#define WI_RID_MICROWAVE_OVEN	0xFC25
+#define WI_RID_P2_CRYPT_KEY2	0xFC26
+#define WI_RID_P2_CRYPT_KEY3	0xFC27
+#define WI_RID_P2_ENCRYPTION	0xFC28
+#define	 PRIVACY_INVOKED	0x01
+#define	 EXCLUDE_UNENCRYPTED	0x02
+#define	 HOST_ENCRYPT		0x10
+#define	 IV_EVERY_FRAME		0x00	/* IV = Initialization Vector */
+#define	 IV_EVERY10_FRAME	0x20	/* every 10 frame IV reuse */
+#define	 IV_EVERY50_FRAME	0x40	/* every 50 frame IV reuse */
+#define	 IV_EVERY100_FRAME	0x60	/* every 100 frame IV reuse */
+#define	 HOST_DECRYPT		0x80
+#define WI_RID_WEP_MAPTABLE	0xFC29
+#define WI_RID_CNFAUTHMODE	0xFC2A
+#define WI_RID_ROAMING_MODE	0xFC2D
+#define WI_RID_OWN_BEACON_INT	0xFC33 /* beacon xmit time for BSS creation */
+#define WI_RID_CNF_DBM_ADJUST	0xFC46
+#define WI_RID_DBM_ADJUST	0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */
+#define WI_RID_BASIC_RATE	0xFCB3
+#define WI_RID_SUPPORT_RATE	0xFCB4
+
+/*
+ * Network parameters, dynamic configuration entities
+ */
+#define WI_RID_MCAST_LIST	0xFC80 /* list of multicast addrs */
+#define WI_RID_CREATE_IBSS	0xFC81 /* create IBSS */
+#define WI_RID_FRAG_THRESH	0xFC82 /* frag len, unicast msg xmit */
+#define WI_RID_RTS_THRESH	0xFC83 /* frame len for RTS/CTS handshake */
+#define WI_RID_TX_RATE		0xFC84 /* data rate for message xmit
+ 					* 0 == Fixed 1mbps
+ 					* 1 == Fixed 2mbps
+ 					* 2 == auto fallback
+					*/
+#define WI_RID_PROMISC		0xFC85 /* enable promisc mode */
+#define WI_RID_FRAG_THRESH0	0xFC90
+#define WI_RID_FRAG_THRESH1	0xFC91
+#define WI_RID_FRAG_THRESH2	0xFC92
+#define WI_RID_FRAG_THRESH3	0xFC93
+#define WI_RID_FRAG_THRESH4	0xFC94
+#define WI_RID_FRAG_THRESH5	0xFC95
+#define WI_RID_FRAG_THRESH6	0xFC96
+#define WI_RID_RTS_THRESH0	0xFC97
+#define WI_RID_RTS_THRESH1	0xFC98
+#define WI_RID_RTS_THRESH2	0xFC99
+#define WI_RID_RTS_THRESH3	0xFC9A
+#define WI_RID_RTS_THRESH4	0xFC9B
+#define WI_RID_RTS_THRESH5	0xFC9C
+#define WI_RID_RTS_THRESH6	0xFC9D
+#define WI_RID_TX_RATE0		0xFC9E
+#define WI_RID_TX_RATE1		0xFC9F
+#define WI_RID_TX_RATE2		0xFCA0
+#define WI_RID_TX_RATE3		0xFCA1
+#define WI_RID_TX_RATE4		0xFCA2
+#define WI_RID_TX_RATE5		0xFCA3
+#define WI_RID_TX_RATE6		0xFCA4
+#define WI_RID_DEFLT_CRYPT_KEYS	0xFCB0
+#define WI_RID_TX_CRYPT_KEY	0xFCB1
+#define WI_RID_TICK_TIME	0xFCE0
+
+struct wi_key {
+	u_int16_t		wi_keylen;
+	u_int8_t		wi_keydat[14];
+};
+
+#define WI_NLTV_KEYS 4
+struct wi_ltv_keys {
+	u_int16_t		wi_len;
+	u_int16_t		wi_type;
+	struct wi_key		wi_keys[WI_NLTV_KEYS];
+};
+
+/*
+ * NIC information
+ */
+#define WI_RID_DNLD_BUF		0xFD01
+#define WI_RID_MEMSZ		0xFD02 /* memory size info (XXX Lucent) */
+					/* Looks like on lucnet pri firm too */
+#define	WI_RID_PRI_IDENTITY	0xFD02 /* primary funcs firmware ident (PRISM2) */
+#define WI_RID_PRI_SUP_RANGE	0xFD03 /* primary supplier compatibility */
+#define WI_RID_CIF_ACT_RANGE	0xFD04 /* controller sup. compatibility */
+#define WI_RID_SERIALNO		0xFD0A /* card serial number */
+#define WI_RID_CARD_ID		0xFD0B /* card identification */
+#define WI_RID_MFI_SUP_RANGE	0xFD0C /* modem supplier compatibility */
+#define WI_RID_CFI_SUP_RANGE	0xFD0D /* controller sup. compatibility */
+#define WI_RID_CHANNEL_LIST	0xFD10 /* allowd comm. frequencies. */
+#define WI_RID_REG_DOMAINS	0xFD11 /* list of intendted regulatory doms */
+#define WI_RID_TEMP_TYPE	0xFD12 /* hw temp range code */
+#define WI_RID_CIS		0xFD13 /* PC card info struct */
+#define WI_RID_STA_IDENTITY	0xFD20 /* station funcs firmware ident */
+#define WI_RID_STA_SUP_RANGE	0xFD21 /* station supplier compat */
+#define WI_RID_MFI_ACT_RANGE	0xFD22
+#define WI_RID_SYMBOL_IDENTITY	0xFD24
+#define WI_RID_CFI_ACT_RANGE	0xFD33
+#define WI_RID_COMMQUAL		0xFD43
+#define WI_RID_SCALETHRESH	0xFD46
+#define WI_RID_PCF		0xFD87
+
+/*
+ * MAC information
+ */
+#define WI_RID_PORT_STAT	0xFD40 /* actual MAC port con control stat */
+#define WI_RID_CURRENT_SSID	0xFD41 /* ID of actually connected SS */
+#define WI_RID_CURRENT_BSSID	0xFD42 /* ID of actually connected BSS */
+#define WI_RID_COMMS_QUALITY	0xFD43 /* quality of BSS connection */
+#define WI_RID_CUR_TX_RATE	0xFD44 /* current TX rate */
+#define WI_RID_CUR_BEACON_INT	0xFD45 /* current beacon interval */
+#define WI_RID_CUR_SCALE_THRESH	0xFD46 /* actual system scane thresh setting */
+#define WI_RID_PROT_RESP_TIME	0xFD47 /* time to wait for resp to req msg */
+#define WI_RID_SHORT_RTR_LIM	0xFD48 /* max tx attempts for short frames */
+#define WI_RID_LONG_RTS_LIM	0xFD49 /* max tx attempts for long frames */
+#define WI_RID_MAX_TX_LIFE	0xFD4A /* max tx frame handling duration */
+#define WI_RID_MAX_RX_LIFE	0xFD4B /* max rx frame handling duration */
+#define WI_RID_CF_POLL		0xFD4C /* contention free pollable ind */
+#define WI_RID_AUTH_ALGS	0xFD4D /* auth algorithms available */
+#define WI_RID_AUTH_TYPE	0xFD4E /* availanle auth types */
+#define WI_RID_WEP_AVAIL	0xFD4F /* WEP privacy option available */
+#define WI_RID_DBM_COMMS_QUAL	0xFD51 /* CommQuality normalized to dBm */
+#define WI_RID_CUR_TX_RATE1	0xFD80
+#define WI_RID_CUR_TX_RATE2	0xFD81
+#define WI_RID_CUR_TX_RATE3	0xFD82
+#define WI_RID_CUR_TX_RATE4	0xFD83
+#define WI_RID_CUR_TX_RATE5	0xFD84
+#define WI_RID_CUR_TX_RATE6	0xFD85
+#define WI_RID_OWN_MAC		0xFD86 /* unique local MAC addr */
+#define WI_RID_PCI_INFO		0xFD87 /* point coordination func cap */
+
+/*
+ * Scan Information
+ */
+#define	WI_RID_BCAST_SCAN_REQ	0xFCAB /* Broadcast Scan request (Symbol) */
+#define	 BSCAN_5SEC		0x01
+#define	 BSCAN_ONETIME		0x02
+#define	 BSCAN_PASSIVE		0x40
+#define	 BSCAN_BCAST		0x80
+#define WI_RID_SCAN_REQ		0xFCE1 /* Scan request (STA only) */
+#define WI_RID_JOIN_REQ		0xFCE2 /* Join request (STA only) */
+#define	WI_RID_AUTH_STATION	0xFCE3 /* Authenticates Station (AP) */
+#define	WI_RID_CHANNEL_REQ	0xFCE4 /* Channel Information Request (AP) */
+#define WI_RID_SCAN_RESULTS	0xFD88 /* Scan Results Table */
+
+struct wi_apinfo {
+	int			scanreason;	/* ScanReason */
+	char			bssid[6];	/* BSSID (mac address) */
+	int			channel;	/* Channel */
+	int			signal;		/* Signal level */
+	int			noise;		/* Average Noise Level*/
+	int			quality;	/* Quality */
+	int			namelen;	/* Length of SSID string */
+	char			name[32];	/* SSID string */
+	int			capinfo;	/* Capability info. */ 
+	int			interval;	/* BSS Beacon Interval */
+	int			rate;		/* Data Rate */
+};
+
+/*
+ * Modem information
+ */
+#define WI_RID_PHY_TYPE		0xFDC0 /* phys layer type indication */
+#define WI_RID_CURRENT_CHAN	0xFDC1 /* current frequency */
+#define WI_RID_PWR_STATE	0xFDC2 /* pwr consumption status */
+#define WI_RID_CCA_MODE		0xFDC3 /* clear chan assess mode indication */
+#define WI_RID_CCA_TIME		0xFDC4 /* clear chan assess time */
+#define WI_RID_MAC_PROC_DELAY	0xFDC5 /* MAC processing delay time */
+#define WI_RID_DATA_RATES	0xFDC6 /* supported data rates */
+
+/*
+ * bsd-airtools v0.2 - source-mods v0.2 [common.h]
+ * by h1kari - (c) Dachb0den Labs 2001
+ */
+
+/*
+ * Copyright (c) 2001 Dachb0den Labs.
+ *      David Hulton <h1kari@dachb0den.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by David Hulton.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Hulton AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL David Hulton OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * standard hermes recieve frame used by wavelan/prism2 cards
+ */
+struct wi_rx_frame {
+	/*
+	 * hermes prefix header. supplies information on the current status of
+	 * the network and various other statistics gathered from the
+	 * management/control frames as used internally.
+	 */
+	u_int16_t	wi_status;
+	u_int16_t	wi_ts0;
+	u_int16_t	wi_ts1;
+	u_int8_t	wi_silence;
+	u_int8_t	wi_signal;
+	u_int8_t	wi_rate;
+	u_int8_t	wi_rx_flow;
+	u_int16_t	wi_rsvd0;
+	u_int16_t	wi_rsvd1;
+	/*
+	 * standard 80211 frame header. all packets have to use this header as
+	 * per the AN9900 from intersil, even management/control. for
+	 * management packets, they just threw the header into the data field,
+	 * but for control packets the headers are lost in translation and
+	 * therefore not all control packet info can be displayed.
+	 */
+	u_int16_t	wi_frame_ctl;
+	u_int16_t	wi_id;
+	u_int8_t	wi_addr1[6];
+	u_int8_t	wi_addr2[6];
+	u_int8_t	wi_addr3[6];
+	u_int16_t	wi_seq_ctl;
+	u_int8_t	wi_addr4[6];
+	u_int16_t	wi_dat_len;
+	/*
+	 * another wierdity with the drivers. they append a 802.3 header which
+	 * is somewhat redundant, since all the same data is provided in the
+	 * 802.11 header.
+	 */
+	u_int8_t	wi_dst_addr[6];
+	u_int8_t	wi_src_addr[6];
+	u_int16_t	wi_len;
+};
+#define WI_DATA_HDRLEN		WI_802_11_OFFSET
+#define WI_MGMT_HDRLEN		WI_802_11_OFFSET_RAW
+#define WI_CTL_HDRLEN		WI_802_11_OFFSET_RAW
+
+
+/*
+ * all data packets have a snap (sub-network access protocol) header that
+ * isn't entirely definied, but added for ethernet compatibility.
+ */
+struct wi_snap_frame {
+	u_int16_t	wi_dat[3];
+	u_int16_t	wi_type;
+};
+
+
+/*
+ * management frame headers
+ * note: all management frames consist of a static header and variable length
+ * fields.
+ */
+
+/*
+ * variable length field structure
+ */
+struct wi_mgmt_var_hdr {
+	u_int8_t	wi_code;
+	u_int8_t	wi_len;
+	u_int8_t	wi_data[256];
+};
+
+/*
+ * management beacon frame prefix
+ */
+struct wi_mgmt_beacon_hdr {
+	u_int32_t	wi_ts0;
+	u_int32_t	wi_ts1;
+	u_int16_t	wi_interval;
+	u_int16_t	wi_capinfo;
+};
+
+/*
+ * ibss announcement traffic indication message (atim) frame
+ * note: no parameters
+ */
+
+/*
+ * management disassociation frame
+ */
+struct wi_mgmt_disas_hdr {
+	u_int16_t	wi_reason;
+};
+
+/*
+ * management association request frame prefix
+ */
+struct wi_mgmt_asreq_hdr {
+	u_int16_t	wi_capinfo;
+	u_int16_t	wi_interval;
+};
+
+/*
+ * management association response frame prefix
+ */
+struct wi_mgmt_asresp_hdr {
+	u_int16_t	wi_capinfo;
+	u_int16_t	wi_status;
+	u_int16_t	wi_aid;
+};
+
+/*
+ * management reassociation request frame prefix
+ */
+struct wi_mgmt_reasreq_hdr {
+	u_int16_t	wi_capinfo;
+	u_int16_t	wi_interval;
+	u_int8_t	wi_currap[6];
+};
+
+/*
+ * management reassociation response frame prefix
+ */
+struct wi_mgmt_reasresp_hdr {
+	u_int16_t	wi_capinfo;
+	u_int16_t	wi_status;
+	u_int16_t	wi_aid;
+};
+
+/*
+ * management probe request frame prefix
+ * note: no static parameters, only variable length
+ */
+
+/*
+ * management probe response frame prefix
+ */
+struct wi_mgmt_proberesp_hdr {
+	u_int32_t	wi_ts0;
+	u_int32_t	wi_ts1;
+	u_int16_t	wi_interval;
+	u_int16_t	wi_capinfo;
+};
+
+/*
+ * management authentication frame prefix
+ */
+struct wi_mgmt_auth_hdr {
+	u_int16_t	wi_algo;
+	u_int16_t	wi_seq;
+	u_int16_t	wi_status;
+};
+
+/*
+ * management deauthentication frame
+ */
+struct wi_mgmt_deauth_hdr {
+	u_int16_t	wi_reason;
+};
+
+
+/*
+ * rid configuration register definitions
+ */
+#define WI_RID_SCAN_REQ		0xFCE1 /* scan request information */
+#define WI_RID_SCAN_RES		0xFD88 /* scan result information */
+
+#define WI_RID_PROCFRAME	0x3137 /* Return full frame information */
+#define WI_RID_PRISM2		0x3138 /* tell if we're a prism2 card or not */
+
+
+/*
+ * 802.11 definitions
+ */
+#define WI_STAT_BADCRC		0x0001
+#define WI_STAT_UNDECRYPTABLE	0x0002
+#define WI_STAT_ERRSTAT		0x0003
+#define WI_STAT_MAC_PORT	0x0700
+#define WI_STAT_1042		0x2000
+#define WI_STAT_TUNNEL		0x4000
+#define WI_STAT_WMP_MSG		0x6000
+#define WI_RXSTAT_MSG_TYPE	0xE000
+
+#define WI_FCTL_OPT_MASK	0xFF00
+#define WI_AID_SET		0xC000
+#define WI_AID_MASK		0x3FFF
+#define WI_SCTL_FRAGNUM_MASK	0x000F
+#define WI_SCTL_SEQNUM_MASK	0xFFF0
+
+#define WI_STAT_UNSPEC_FAIL	1
+#define WI_STAT_CAPINFO_FAIL	10
+#define WI_STAT_REAS_DENY	11
+#define WI_STAT_ASSOC_DENY	12
+#define WI_STAT_ALGO_FAIL	13
+#define WI_STAT_SEQ_FAIL	14
+#define WI_STAT_CHAL_FAIL	15
+#define WI_STAT_TOUT_FAIL	16
+#define WI_STAT_OVERL_DENY	17
+#define WI_STAT_RATE_DENY	18
+
+#define WI_FTYPE_MGMT		0x0000
+#define WI_FTYPE_CTL		0x0004
+#define WI_FTYPE_DATA		0x0008
+
+#define WI_FCTL_VERS		0x0002
+#define WI_FCTL_FTYPE		0x000C
+#define WI_FCTL_STYPE		0x00F0
+#define WI_FCTL_TODS		0x0100
+#define WI_FCTL_FROMDS		0x0200
+#define WI_FCTL_MOREFRAGS	0x0400
+#define WI_FCTL_RETRY		0x0800
+#define WI_FCTL_PM		0x1000
+#define WI_FCTL_MOREDATA	0x2000
+#define WI_FCTL_WEP		0x4000
+#define WI_FCTL_ORDER		0x8000
+
+#define WI_FCS_LEN		0x4 /* checksum length */
+
+
+/*
+ * management definitions
+ */
+#define WI_STYPE_MGMT_ASREQ	0x0000
+#define WI_STYPE_MGMT_ASRESP	0x0010
+#define WI_STYPE_MGMT_REASREQ	0x0020
+#define WI_STYPE_MGMT_REASRESP	0x0030
+#define WI_STYPE_MGMT_PROBEREQ	0x0040
+#define WI_STYPE_MGMT_PROBERESP	0x0050
+#define WI_STYPE_MGMT_BEACON	0x0080
+#define WI_STYPE_MGMT_ATIM	0x0090
+#define WI_STYPE_MGMT_DISAS	0x00A0
+#define WI_STYPE_MGMT_AUTH	0x00B0
+#define WI_STYPE_MGMT_DEAUTH	0x00C0
+
+#define WI_CAPINFO_ESS		0x01
+#define WI_CAPINFO_IBSS		0x02
+#define WI_CAPINFO_CFPOLL	0x04
+#define WI_CAPINFO_CFPOLLREQ	0x08
+#define WI_CAPINFO_PRIV		0x10
+
+#define WI_REASON_UNSPEC	1
+#define WI_REASON_AUTH_INVALID	2
+#define WI_REASON_DEAUTH_LEAVE	3
+#define WI_REASON_DISAS_INACT	4
+#define WI_REASON_DISAS_OVERL	5
+#define WI_REASON_CLASS2	6
+#define WI_REASON_CLASS3	7
+#define WI_REASON_DISAS_LEAVE	8
+#define WI_REASON_NOAUTH	9
+
+#define WI_VAR_SSID		0
+#define WI_VAR_SRATES		1
+#define WI_VAR_FH		2
+#define WI_VAR_DS		3
+#define WI_VAR_CF		4
+#define WI_VAR_TIM		5
+#define WI_VAR_IBSS		6
+#define WI_VAR_CHAL		16
+
+#define WI_VAR_SRATES_MASK	0x7F
+
+
+/*
+ * control definitions
+ */
+#define WI_STYPE_CTL_PSPOLL	0x00A0
+#define WI_STYPE_CTL_RTS	0x00B0
+#define WI_STYPE_CTL_CTS	0x00C0
+#define WI_STYPE_CTL_ACK	0x00D0
+#define WI_STYPE_CTL_CFEND	0x00E0
+#define WI_STYPE_CTL_CFENDCFACK	0x00F0
+
+
+/*
+ * ap scanning structures
+ */
+struct wi_scan_res {
+	u_int16_t	wi_chan;
+	u_int16_t	wi_noise;
+	u_int16_t	wi_signal;
+	u_int8_t	wi_bssid[6];
+	u_int16_t	wi_interval;
+	u_int16_t	wi_capinfo;
+	u_int16_t	wi_ssid_len;
+	u_int8_t	wi_ssid[32];
+	u_int8_t	wi_srates[10];
+	u_int8_t	wi_rate;
+	u_int8_t	wi_rsvd;
+};
+#define WI_WAVELAN_RES_SIZE	50
+
+struct wi_scan_p2_hdr {
+	u_int16_t	wi_rsvd;
+	u_int16_t	wi_reason;
+};
+#define WI_PRISM2_RES_SIZE	62
+
+
+/*
+ * prism2 debug mode definitions
+ */
+#define SIOCSPRISM2DEBUG	_IOW('i', 137, struct ifreq)
+#define SIOCGPRISM2DEBUG	_IOWR('i', 138, struct ifreq)
+
+#define WI_DEBUG_RESET		0x00
+#define WI_DEBUG_INIT		0x01
+#define WI_DEBUG_SLEEP		0x02
+#define WI_DEBUG_WAKE		0x03
+#define WI_DEBUG_CHAN		0x08
+#define WI_DEBUG_DELAYSUPP	0x09
+#define WI_DEBUG_TXSUPP		0x0A
+#define WI_DEBUG_MONITOR	0x0B
+#define WI_DEBUG_LEDTEST	0x0C
+#define WI_DEBUG_CONTTX		0x0E
+#define WI_DEBUG_STOPTEST	0x0F
+#define WI_DEBUG_CONTRX		0x10
+#define WI_DEBUG_SIGSTATE	0x11
+#define WI_DEBUG_CALENABLE	0x13
+#define WI_DEBUG_CONFBITS	0x15
+
+#endif
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.c	2005-02-24 13:06:17.412114976 -0800
@@ -0,0 +1,353 @@
+/*
+ * MD5 hash implementation and interface functions
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/byteorder.h>
+
+#if defined(__BIG_ENDIAN)
+#define WORDS_BIGENDIAN
+#endif
+
+#include "md5.h"
+
+
+void md5_mac(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac)
+{
+	MD5_CTX context;
+	MD5Init(&context);
+	MD5Update(&context, key, key_len);
+	MD5Update(&context, data, data_len);
+	MD5Update(&context, key, key_len);
+	MD5Final(mac, &context);
+}
+
+
+/* HMAC code is based on RFC 2104 */
+void hmac_md5_vector(u8 *key, size_t key_len, size_t num_elem,
+		     u8 *addr[], size_t *len, u8 *mac)
+{
+	MD5_CTX context;
+	u8 k_ipad[65]; /* inner padding - key XORd with ipad */
+	u8 k_opad[65]; /* outer padding - key XORd with opad */
+	u8 tk[16];
+	int i;
+
+        /* if key is longer than 64 bytes reset it to key = MD5(key) */
+        if (key_len > 64) {
+		MD5Init(&context);
+		MD5Update(&context, key, key_len);
+		MD5Final(tk, &context);
+
+		key = tk;
+		key_len = 16;
+        }
+
+	/* the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in pads */
+	memset(k_ipad, 0, sizeof(k_ipad));
+	memset(k_opad, 0, sizeof(k_opad));
+	memcpy(k_ipad, key, key_len);
+	memcpy(k_opad, key, key_len);
+
+	/* XOR key with ipad and opad values */
+	for (i = 0; i < 64; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/* perform inner MD5 */
+	MD5Init(&context);                   /* init context for 1st pass */
+	MD5Update(&context, k_ipad, 64);     /* start with inner pad */
+	/* then text of datagram; all fragments */
+	for (i = 0; i < num_elem; i++) {
+		MD5Update(&context, addr[i], len[i]);
+	}
+	MD5Final(mac, &context);             /* finish up 1st pass */
+
+	/* perform outer MD5 */
+	MD5Init(&context);                   /* init context for 2nd pass */
+	MD5Update(&context, k_opad, 64);     /* start with outer pad */
+	MD5Update(&context, mac, 16);        /* then results of 1st hash */
+	MD5Final(mac, &context);             /* finish up 2nd pass */
+}
+
+
+void hmac_md5(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac)
+{
+	hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len)	/* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    u32 t;
+    do {
+	t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(u32 *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    u32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    memcpy(p, buf, len);
+	    return;
+	}
+	memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((u32 *) ctx->in)[14] = ctx->bits[0];
+    ((u32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (u32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(u32 buf[4], u32 const in[16])
+{
+    register u32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
+/* ===== end - public domain MD5 implementation ===== */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.h	2005-02-24 13:06:17.413114824 -0800
@@ -0,0 +1,26 @@
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+struct MD5Context {
+	u32 buf[4];
+	u32 bits[2];
+	u8 in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+	       unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(u32 buf[4], u32 const in[16]);
+
+typedef struct MD5Context MD5_CTX;
+
+
+void md5_mac(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac);
+void hmac_md5_vector(u8 *key, size_t key_len, size_t num_elem,
+		     u8 *addr[], size_t *len, u8 *mac);
+void hmac_md5(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac);
+
+#endif /* MD5_H */
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.c	2005-02-24 13:06:17.413114824 -0800
@@ -0,0 +1,122 @@
+/*
+ * rc4.c
+ *
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * All rights reserved.
+ * 
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ *    copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ *    Communications, Inc. trademarks, including the mark "WHISTLE
+ *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ *    such appears in the above copyright notice or in the software.
+ * 
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/crypto/rc4/rc4.c,v 1.4 2003/01/15 19:55:16 sam Exp $
+ * $Id: rc4.c,v 1.2 2004/08/05 17:34:54 samleffler Exp $
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include "rc4.h"
+
+static __inline void
+swap_bytes(u_char *a, u_char *b)
+{
+	u_char temp;
+
+	temp = *a;
+	*a = *b;
+	*b = temp;
+}
+
+/*
+ * Initialize an RC4 state buffer using the supplied key,
+ * which can have arbitrary length.
+ */
+void
+rc4_init(struct rc4_state *const state, const u_char *key, int keylen)
+{
+	u_char j;
+	int i;
+
+	/* Initialize state with identity permutation */
+	for (i = 0; i < 256; i++)
+		state->perm[i] = (u_char)i; 
+	state->index1 = 0;
+	state->index2 = 0;
+  
+	/* Randomize the permutation using key data */
+	for (j = i = 0; i < 256; i++) {
+		j += state->perm[i] + key[i % keylen]; 
+		swap_bytes(&state->perm[i], &state->perm[j]);
+	}
+}
+EXPORT_SYMBOL(rc4_init);
+
+/*
+ * Encrypt some data using the supplied RC4 state buffer.
+ * The input and output buffers may be the same buffer.
+ * Since RC4 is a stream cypher, this function is used
+ * for both encryption and decryption.
+ */
+void
+rc4_crypt_skip(struct rc4_state *const state,
+	const u_char *inbuf, u_char *outbuf, int buflen, int skip)
+{
+	int i;
+	u_char j;
+
+	for (i = 0; i < skip; i++) {
+
+		/* Update modification indicies */
+		state->index1++;
+		state->index2 += state->perm[state->index1];
+
+		/* Modify permutation */
+		swap_bytes(&state->perm[state->index1],
+		    &state->perm[state->index2]);
+
+		/* Encrypt/decrypt next byte */
+		j = state->perm[state->index1] + state->perm[state->index2];
+	}
+	for (i = 0; i < buflen; i++) {
+
+		/* Update modification indicies */
+		state->index1++;
+		state->index2 += state->perm[state->index1];
+
+		/* Modify permutation */
+		swap_bytes(&state->perm[state->index1],
+		    &state->perm[state->index2]);
+
+		/* Encrypt/decrypt next byte */
+		j = state->perm[state->index1] + state->perm[state->index2];
+		outbuf[i] = inbuf[i] ^ state->perm[j];
+	}
+}
+EXPORT_SYMBOL(rc4_crypt_skip);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.h	2005-02-24 13:06:17.413114824 -0800
@@ -0,0 +1,53 @@
+/*
+ * rc4.h
+ *
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * All rights reserved.
+ * 
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ *    copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ *    Communications, Inc. trademarks, including the mark "WHISTLE
+ *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ *    such appears in the above copyright notice or in the software.
+ * 
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/crypto/rc4/rc4.h,v 1.3 2000/07/16 05:53:14 peter Exp $
+ * $Id: rc4.h,v 1.2 2004/08/05 17:34:54 samleffler Exp $
+ */
+
+#ifndef _SYS_CRYPTO_RC4_RC4_H_
+#define _SYS_CRYPTO_RC4_RC4_H_
+
+struct rc4_state {
+	u_char	perm[256];
+	u_char	index1;
+	u_char	index2;
+};
+
+extern void rc4_init(struct rc4_state *state, const u_char *key, int keylen);
+extern void rc4_crypt_skip(struct rc4_state *state,
+		const u_char *inbuf, u_char *outbuf, int buflen, int skip);
+#endif
+
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_ccmp.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_ccmp.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_ccmp.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_ccmp.c	2005-02-24 13:06:17.414114672 -0800
@@ -0,0 +1,726 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * CCMP test module.
+ *
+ * Test vectors come from section I.7.4 of P802.11i/D7.0, October 2003.
+ *
+ * To use this tester load the net80211 layer (either as a module or
+ * by statically configuring it into your kernel), then insmod this
+ * module.  It should automatically run all test cases and print
+ * information for each.  To run one or more tests you can specify a
+ * tests parameter to the module that is a bit mask of the set of tests
+ * you want; e.g. insmod ccmp_test tests=7 will run only test mpdu's
+ * 1, 2, and 3.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+
+#include <linux/netdevice.h>
+#include "if_media.h"
+#include <net80211/ieee80211_var.h>
+
+/*
+==== CCMP test mpdu   1 ====
+
+-- MPDU Fields
+
+7  Version  = 0
+8  Type     = 2   SubType  = 0  Data
+9  ToDS     = 0   FromDS   = 0
+10  MoreFrag = 0   Retry    = 1
+11  PwrMgt   = 0   moreData = 0
+12  Encrypt  = 1
+13  Order    = 0
+14  Duration = 11459
+15  A1 = 0f-d2-e1-28-a5-7c    DA
+16  A2 = 50-30-f1-84-44-08    SA
+17  A3 = ab-ae-a5-b8-fc-ba    BSSID
+18  SC = 0x3380
+19  seqNum = 824 (0x0338)  fraqNum = 0 (0x00)
+20  Algorithm = AES_CCM
+21  Key ID = 0
+22  TK = c9 7c 1f 67 ce 37 11 85  51 4a 8a 19 f2 bd d5 2f
+23  PN = 199027030681356  (0xB5039776E70C)
+24  802.11 Header =  08 48 c3 2c 0f d2 e1 28 a5 7c 50 30 f1 84 44 08
+25  	ab ae a5 b8 fc ba 80 33
+26  Muted 802.11 Header =  08 40 0f d2 e1 28 a5 7c 50 30 f1 84 44 08
+27  	ab ae a5 b8 fc ba 00 00
+28  CCMP Header =  0c e7 00 20 76 97 03 b5
+29  CCM Nonce = 00 50 30 f1 84 44 08 b5  03 97 76 e7 0c
+30 Plaintext Data = f8 ba 1a 55 d0 2f 85 ae 96 7b b6 2f b6 cd a8 eb
+1	7e 78 a0 50
+2  CCM MIC =  78 45 ce 0b 16 f9 76 23
+3  -- Encrypted MPDU with FCS
+4  08 48 c3 2c 0f d2 e1 28 a5 7c 50 30 f1 84 44 08 ab ae a5 b8 fc ba
+5  80 33 0c e7 00 20 76 97 03 b5 f3 d0 a2 fe 9a 3d bf 23 42 a6 43 e4
+6  32 46 e8 0c 3c 04 d0 19 78 45 ce 0b 16 f9 76 23 1d 99 f0 66
+*/
+static const u_int8_t test1_key[] = {		/* TK */
+	0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,  0x51, 0x4a, 0x8a,
+	0x19, 0xf2, 0xbd, 0xd5, 0x2f
+};
+static const u_int8_t test1_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,	/* 802.11 Header */
+	0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+	0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33, 
+	0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,	/* Plaintext Data */
+	0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+	0x7e, 0x78, 0xa0, 0x50, 
+};
+static const u_int8_t test1_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+	0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+	0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+	0x0c, 0xe7, 0x00, 0x20, 0x76, 0x97, 0x03, 0xb5,
+	0xf3, 0xd0, 0xa2, 0xfe, 0x9a, 0x3d, 0xbf, 0x23,
+	0x42, 0xa6, 0x43, 0xe4, 0x32, 0x46, 0xe8, 0x0c,
+	0x3c, 0x04, 0xd0, 0x19, 0x78, 0x45, 0xce, 0x0b,
+	0x16, 0xf9, 0x76, 0x23,
+};
+
+/*
+==== CCMP test mpdu   2 ====
+
+-- MPDU Fields
+
+ 9  Version  = 0
+10  Type     = 2   SubType  = 3  Data+CF-Ack+CF-Poll
+11  ToDS     = 0   FromDS   = 0
+12  MoreFrag = 0   Retry    = 0
+13  PwrMgt   = 0   moreData = 0
+14  Encrypt  = 1
+15  Order    = 1
+16  Duration = 20842
+17  A1 = ea-10-0c-84-68-50    DA
+18  A2 = ee-c1-76-2c-88-de    SA
+19  A3 = af-2e-e9-f4-6a-07    BSSID
+20  SC = 0xCCE0
+21  seqNum = 3278 (0x0CCE)  fraqNum = 0 (0x00)
+22  Algorithm = AES_CCM
+23  Key ID = 2
+24  TK = 8f 7a 05 3f a5 77 a5 59  75 29 27 20 97 a6 03 d5
+25  PN = 54923164817386  (0x31F3CBBA97EA)
+26  802.11 Header =  38 c0 6a 51 ea 10 0c 84 68 50 ee c1 76 2c 88 de
+27  	af 2e e9 f4 6a 07 e0 cc
+28  Muted 802.11 Header =  08 c0 ea 10 0c 84 68 50 ee c1 76 2c 88 de
+29  	af 2e e9 f4 6a 07 00 00
+30  CCMP Header =  ea 97 00 a0 ba cb f3 31
+31  CCM Nonce = 00 ee c1 76 2c 88 de 31  f3 cb ba 97 ea
+32  Plaintext Data = 83 a0 63 4b 5e d7 62 7e b9 df 22 5e 05 74 03 42
+33  	de 19 41 17
+34  CCM MIC =  54 2f bf 8d a0 6a a4 ae
+35  -- Encrypted MPDU with FCS
+36  38 c0 6a 51 ea 10 0c 84 68 50 ee c1 76 2c 88 de af 2e e9 f4 6a 07
+37  e0 cc ea 97 00 a0 ba cb f3 31 81 4b 69 65 d0 5b f2 b2 ed 38 d4 be
+38  b0 69 fe 82 71 4a 61 0b 54 2f bf 8d a0 6a a4 ae 25 3c 47 38
+*/
+static const u_int8_t test2_key[] = {		/* TK */
+	0x8f, 0x7a, 0x05, 0x3f, 0xa5, 0x77, 0xa5, 0x59,  0x75, 0x29, 0x27,
+	0x20, 0x97, 0xa6, 0x03, 0xd5
+};
+static const u_int8_t test2_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0x38, 0xc0, 0x6a, 0x51, 0xea, 0x10, 0x0c, 0x84, 0x68, 0x50, 0xee,
+	0xc1, 0x76, 0x2c, 0x88, 0xde, 0xaf, 0x2e, 0xe9, 0xf4, 0x6a, 0x07,
+	0xe0, 0xcc,
+	0x83, 0xa0, 0x63, 0x4b, 0x5e, 0xd7, 0x62, 0x7e, 0xb9, 0xdf, 0x22,
+	0x5e, 0x05, 0x74, 0x03, 0x42, 0xde, 0x19, 0x41, 0x17
+};
+static const u_int8_t test2_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0x38, 0xc0, 0x6a, 0x51, 0xea, 0x10, 0x0c, 0x84, 0x68, 0x50, 0xee,
+	0xc1, 0x76, 0x2c, 0x88, 0xde, 0xaf, 0x2e, 0xe9, 0xf4, 0x6a, 0x07,
+	0xe0, 0xcc, 0xea, 0x97, 0x00, 0xa0, 0xba, 0xcb, 0xf3, 0x31, 0x81,
+	0x4b, 0x69, 0x65, 0xd0, 0x5b, 0xf2, 0xb2, 0xed, 0x38, 0xd4, 0xbe,
+	0xb0, 0x69, 0xfe, 0x82, 0x71, 0x4a, 0x61, 0x0b, 0x54, 0x2f, 0xbf,
+	0x8d, 0xa0, 0x6a, 0xa4, 0xae,
+};
+
+/*
+==== CCMP test mpdu   3 ====
+
+-- MPDU Fields
+
+41  Version  = 0
+42  Type     = 2   SubType  = 11
+43  ToDS     = 0   FromDS   = 0
+44  MoreFrag = 0   Retry    = 1
+45  PwrMgt   = 0   moreData = 0
+46  Encrypt  = 1
+47  Order    = 1
+48  Duration = 25052
+49  A1 = d9-57-7d-f7-63-c8    DA
+50 A2 = b6-a8-8a-df-36-91    SA
+1  A3 = dc-4a-8b-ca-94-dd    BSSID
+2  SC = 0x8260
+3  seqNum = 2086 (0x0826)  fraqNum = 0 (0x00)
+4  QC = 0x0000
+5  MSDU Priority = 0 (0x0)
+6  Algorithm = AES_CCM
+7  Key ID = 2
+8  TK = 40 cf b7 a6 2e 88 01 3b  d6 d3 af fc c1 91 04 1e
+9  PN = 52624639632814  (0x2FDCA0F3A5AE)
+10  802.11 Header =  b8 c8 dc 61 d9 57 7d f7 63 c8 b6 a8 8a df 36 91
+11  	dc 4a 8b ca 94 dd 60 82 20 85
+12  Muted 802.11 Header =  88 c0 d9 57 7d f7 63 c8 b6 a8 8a df 36 91
+13  	dc 4a 8b ca 94 dd 00 00 00 00
+14  CCMP Header =  ae a5 00 a0 f3 a0 dc 2f
+15  CCM Nonce = 00 b6 a8 8a df 36 91 2f dc a0 f3 a5 ae
+16  Plaintext Data  = 2c 1b d0 36 83 1c 95 49 6c 5f 4d bf 3d 55 9e 72
+17  	de 80 2a 18
+18  CCM MIC =  fd 1f 1f 61 a9 fb 4b b3
+19  -- Encrypted MPDU with FCS
+20  b8 c8 dc 61 d9 57 7d f7 63 c8 b6 a8 8a df 36 91 dc 4a 8b ca 94 dd
+21  60 82 20 85 ae a5 00 a0 f3 a0 dc 2f 89 d8 58 03 40 b6 26 a0 b6 d4
+22  d0 13 bf 18 f2 91 b8 96 46 c8 fd 1f 1f 61 a9 fb 4b b3 60 3f 5a ad
+*/
+static const u_int8_t test3_key[] = {		/* TK */
+	0x40, 0xcf, 0xb7, 0xa6, 0x2e, 0x88, 0x01, 0x3b,  0xd6, 0xd3,
+	0xaf, 0xfc, 0xc1, 0x91, 0x04, 0x1e
+};
+static const u_int8_t test3_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0xb8, 0xc8, 0xdc, 0x61, 0xd9, 0x57, 0x7d, 0xf7, 0x63, 0xc8,
+	0xb6, 0xa8, 0x8a, 0xdf, 0x36, 0x91, 0xdc, 0x4a, 0x8b, 0xca,
+	0x94, 0xdd, 0x60, 0x82, 0x20, 0x85,
+	0x2c, 0x1b, 0xd0, 0x36, 0x83, 0x1c, 0x95, 0x49, 0x6c, 0x5f,
+	0x4d, 0xbf, 0x3d, 0x55, 0x9e, 0x72, 0xde, 0x80, 0x2a, 0x18
+};
+static const u_int8_t test3_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0xb8, 0xc8, 0xdc, 0x61, 0xd9, 0x57, 0x7d, 0xf7, 0x63, 0xc8,
+	0xb6, 0xa8, 0x8a, 0xdf, 0x36, 0x91, 0xdc, 0x4a, 0x8b, 0xca,
+	0x94, 0xdd, 0x60, 0x82, 0x20, 0x85, 0xae, 0xa5, 0x00, 0xa0,
+	0xf3, 0xa0, 0xdc, 0x2f, 0x89, 0xd8, 0x58, 0x03, 0x40, 0xb6,
+	0x26, 0xa0, 0xb6, 0xd4, 0xd0, 0x13, 0xbf, 0x18, 0xf2, 0x91,
+	0xb8, 0x96, 0x46, 0xc8, 0xfd, 0x1f, 0x1f, 0x61, 0xa9, 0xfb,
+	0x4b, 0xb3,
+};
+
+/*
+==== CCMP test mpdu  4 ==== 
+
+-- MPDU Fields
+25  Version  = 0
+26  Type     = 2   SubType  = 10
+27  ToDS     = 0   FromDS   = 1
+28  MoreFrag = 0   Retry    = 1
+29  PwrMgt   = 0   moreData = 0
+30  Encrypt  = 1
+31  Order    = 1
+32  Duration = 4410
+33  A1 = 71-2a-9d-df-11-db    DA
+34  A2 = 8e-f8-22-73-47-01    BSSID
+35  A3 = 59-14-0d-d6-46-a2    SA
+36  SC = 0x2FC0
+37  seqNum = 764 (0x02FC)  fraqNum = 0 (0x00)
+38  QC = 0x0007
+39  MSDU Priority = 7 (0x0)
+40  Algorithm = AES_CCM
+41  Key ID = 0
+42  TK = 8c 89 a2 eb c9 6c 76 02  70 7f cf 24 b3 2d 38 33
+43  PN = 270963670912995  (0xF670A55A0FE3)
+44  802.11 Header =  a8 ca 3a 11 71 2a 9d df 11 db 8e f8 22 73 47 01
+45  	59 14 0d d6 46 a2 c0 2f 67 a5
+46  Muted 802.11 Header =  88 c2 71 2a 9d df 11 db 8e f8 22 73 47 01
+47  	59 14 0d d6 46 a2 00 00 07 00
+48  CCMP Header =  e3 0f 00 20 5a a5 70 f6
+49  CCM Nonce = 07 8e f8 22 73 47 01 f6  70 a5 5a 0f e3
+50  Plaintext Data = 4f ad 2b 1c 29 0f a5 eb d8 72 fb c3 f3 a0 74 89
+51  	8f 8b 2f bb
+52  CCM MIC =  31 fc 88 00 4f 35 ee 3d
+-- Encrypted MPDU with FCS
+2  a8 ca 3a 11 71 2a 9d df 11 db 8e f8 22 73 47 01 59 14 0d d6 46 a2
+3  c0 2f 67 a5 e3 0f 00 20 5a a5 70 f6 9d 59 b1 5f 37 14 48 c2 30 f4
+4  d7 39 05 2e 13 ab 3b 1a 7b 10 31 fc 88 00 4f 35 ee 3d 45 a7 4a 30
+*/
+static const u_int8_t test4_key[] = {		/* TK */
+	0x8c, 0x89, 0xa2, 0xeb, 0xc9, 0x6c, 0x76, 0x02,
+	0x70, 0x7f, 0xcf, 0x24, 0xb3, 0x2d, 0x38, 0x33,
+};
+static const u_int8_t test4_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0xa8, 0xca, 0x3a, 0x11, 0x71, 0x2a, 0x9d, 0xdf, 0x11, 0xdb,
+	0x8e, 0xf8, 0x22, 0x73, 0x47, 0x01, 0x59, 0x14, 0x0d, 0xd6,
+	0x46, 0xa2, 0xc0, 0x2f, 0x67, 0xa5,
+	0x4f, 0xad, 0x2b, 0x1c, 0x29, 0x0f, 0xa5, 0xeb, 0xd8, 0x72,
+	0xfb, 0xc3, 0xf3, 0xa0, 0x74, 0x89, 0x8f, 0x8b, 0x2f, 0xbb,
+};
+static const u_int8_t test4_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0xa8, 0xca, 0x3a, 0x11, 0x71, 0x2a, 0x9d, 0xdf, 0x11, 0xdb,
+	0x8e, 0xf8, 0x22, 0x73, 0x47, 0x01, 0x59, 0x14, 0x0d, 0xd6,
+	0x46, 0xa2, 0xc0, 0x2f, 0x67, 0xa5, 0xe3, 0x0f, 0x00, 0x20,
+	0x5a, 0xa5, 0x70, 0xf6, 0x9d, 0x59, 0xb1, 0x5f, 0x37, 0x14,
+	0x48, 0xc2, 0x30, 0xf4, 0xd7, 0x39, 0x05, 0x2e, 0x13, 0xab,
+	0x3b, 0x1a, 0x7b, 0x10, 0x31, 0xfc, 0x88, 0x00, 0x4f, 0x35,
+	0xee, 0x3d,
+};
+
+/*
+==== CCMP test mpdu   5 ====
+
+-- MPDU Fields
+
+7  Version  = 0
+8  Type     = 2   SubType  = 8
+9  ToDS     = 0   FromDS   = 1
+10  MoreFrag = 0   Retry    = 1
+11  PwrMgt   = 1   moreData = 0
+12  Encrypt  = 1
+13  Order    = 1
+14  Duration = 16664
+15  A1 = 45-de-c6-9a-74-80    DA
+16  A2 = f3-51-94-6b-c9-6b    BSSID
+17  A3 = e2-76-fb-e6-c1-27    SA
+18  SC = 0xF280
+19  seqNum = 3880 (0x0F28)  fraqNum = 0 (0x00)
+20  QC = 0x000b
+21  MSDU Priority = 0 (0x0)
+22  Algorithm = AES_CCM
+23  Key ID = 2
+24  TK = a5 74 d5 14 3b b2 5e fd  de ff 30 12 2f df d0 66
+25  PN = 184717420531255  (0xA7FFE03C0E37)
+26  802.11 Header =  88 da 18 41 45 de c6 9a 74 80 f3 51 94 6b c9 6b
+27  	e2 76 fb e6 c1 27 80 f2 4b 19
+28  Muted 802.11 Header =  88 c2 45 de c6 9a 74 80 f3 51 94 6b c9 6b
+29  	e2 76 fb e6 c1 27 00 00 0b 00
+30  CCMP Header =  37 0e 00 a0 3c e0 ff a7
+31  CCM Nonce = 0b f3 51 94 6b c9 6b a7 ff e0 3c 0e 37
+32  Plaintext Data = 28 96 9b 95 4f 26 3a 80 18 a9 ef 70 a8 b0 51 46
+33  	24 81 92 2e
+34  CCM MIC =  ce 0c 3b e1 97 d3 05 eb
+35  -- Encrypted MPDU with FCS
+36  88 da 18 41 45 de c6 9a 74 80 f3 51 94 6b c9 6b e2 76 fb e6 c1 27
+37  80 f2 4b 19 37 0e 00 a0 3c e0 ff a7 eb 4a e4 95 6a 80 1d a9 62 4b
+38  7e 0c 18 b2 3e 61 5e c0 3a f6 ce 0c 3b e1 97 d3 05 eb c8 9e a1 b5
+*/
+static const u_int8_t test5_key[] = {		/* TK */
+	0xa5, 0x74, 0xd5, 0x14, 0x3b, 0xb2, 0x5e, 0xfd,
+	0xde, 0xff, 0x30, 0x12, 0x2f, 0xdf, 0xd0, 0x66,
+};
+static const u_int8_t test5_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0x88, 0xda, 0x18, 0x41, 0x45, 0xde, 0xc6, 0x9a, 0x74, 0x80,
+	0xf3, 0x51, 0x94, 0x6b, 0xc9, 0x6b, 0xe2, 0x76, 0xfb, 0xe6,
+	0xc1, 0x27, 0x80, 0xf2, 0x4b, 0x19,
+	0x28, 0x96, 0x9b, 0x95, 0x4f, 0x26, 0x3a, 0x80, 0x18, 0xa9,
+	0xef, 0x70, 0xa8, 0xb0, 0x51, 0x46, 0x24, 0x81, 0x92, 0x2e,
+};
+static const u_int8_t test5_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0x88, 0xda, 0x18, 0x41, 0x45, 0xde, 0xc6, 0x9a, 0x74, 0x80,
+	0xf3, 0x51, 0x94, 0x6b, 0xc9, 0x6b, 0xe2, 0x76, 0xfb, 0xe6,
+	0xc1, 0x27, 0x80, 0xf2, 0x4b, 0x19, 0x37, 0x0e, 0x00, 0xa0,
+	0x3c, 0xe0, 0xff, 0xa7, 0xeb, 0x4a, 0xe4, 0x95, 0x6a, 0x80,
+	0x1d, 0xa9, 0x62, 0x4b, 0x7e, 0x0c, 0x18, 0xb2, 0x3e, 0x61,
+	0x5e, 0xc0, 0x3a, 0xf6, 0xce, 0x0c, 0x3b, 0xe1, 0x97, 0xd3,
+	0x05, 0xeb,
+};
+
+/*
+==== CCMP test mpdu   6 ====
+
+-- MPDU Fields
+
+41  Version  = 0
+42  Type     = 2   SubType  = 8
+43  ToDS     = 0   FromDS   = 1
+44  MoreFrag = 0   Retry    = 0
+45  PwrMgt   = 1   moreData = 0
+46  Encrypt  = 1
+47  Order    = 0
+48  Duration = 8161
+49  A1 = 5a-f2-84-30-fd-ab    DA
+50  A2 = bf-f9-43-b9-f9-a6    BSSID
+1   A3 = ab-1d-98-c7-fe-73    SA
+2  SC = 0x7150
+3  seqNum = 1813 (0x0715)  fraqNum = 0 (0x00)
+4  QC = 0x000d
+5  PSDU Priority = 13 (0xd)
+6  Algorithm = AES_CCM
+7  Key ID = 1
+8  TK = f7 1e ea 4e 1f 58 80 4b 97 17 23 0a d0 61 46 41
+9  PN    = 118205765159305  (0x6B81ECA48989)
+10  802.11 Header =  88 52 e1 1f 5a f2 84 30 fd ab bf f9 43 b9 f9 a6
+11  	ab 1d 98 c7 fe 73 50 71  3d 6a
+12  Muted 802.11 Header =  88 42 5a f2 84 30 fd ab bf f9 43 b9 f9 a6
+13  	ab 1d 98 c7 fe 73 00 00 0d 00
+14  CCMP Header =  89 89 00 60 a4 ec 81 6b
+15  CCM Nonce = 0d bf f9 43 b9 f9 a6 6b  81 ec a4 89 89
+16  Plaintext Data = ab fd a2 2d 3a 0b fc 9c c1 fc 07 93 63 c2 fc a1
+17  	43 e6 eb 1d
+18  CCM MIC =  30 9a 8d 5c 46 6b bb 71
+19  -- Encrypted MPDU with FCS
+20  88 52 e1 1f 5a f2 84 30 fd ab bf f9 43 b9 f9 a6 ab 1d 98 c7 fe 73
+21  50 71 3d 6a 89 89 00 60 a4 ec 81 6b 9a 70 9b 60 a3 9d 40 b1 df b6
+22  12 e1 8b 5f 11 4b ad b6 cc 86 30 9a 8d 5c 46 6b bb 71 86 c0 4e 97
+*/
+static const u_int8_t test6_key[] = {		/* TK */
+	0xf7, 0x1e, 0xea, 0x4e, 0x1f, 0x58, 0x80, 0x4b,
+	0x97, 0x17, 0x23, 0x0a, 0xd0, 0x61, 0x46, 0x41,
+};
+static const u_int8_t test6_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0x88, 0x52, 0xe1, 0x1f, 0x5a, 0xf2, 0x84, 0x30, 0xfd, 0xab,
+	0xbf, 0xf9, 0x43, 0xb9, 0xf9, 0xa6, 0xab, 0x1d, 0x98, 0xc7,
+	0xfe, 0x73, 0x50, 0x71, 0x3d, 0x6a,
+	0xab, 0xfd, 0xa2, 0x2d, 0x3a, 0x0b, 0xfc, 0x9c, 0xc1, 0xfc,
+	0x07, 0x93, 0x63, 0xc2, 0xfc, 0xa1, 0x43, 0xe6, 0xeb, 0x1d,
+};
+static const u_int8_t test6_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0x88, 0x52, 0xe1, 0x1f, 0x5a, 0xf2, 0x84, 0x30, 0xfd, 0xab,
+	0xbf, 0xf9, 0x43, 0xb9, 0xf9, 0xa6, 0xab, 0x1d, 0x98, 0xc7,
+	0xfe, 0x73, 0x50, 0x71, 0x3d, 0x6a, 0x89, 0x89, 0x00, 0x60,
+	0xa4, 0xec, 0x81, 0x6b, 0x9a, 0x70, 0x9b, 0x60, 0xa3, 0x9d,
+	0x40, 0xb1, 0xdf, 0xb6, 0x12, 0xe1, 0x8b, 0x5f, 0x11, 0x4b,
+	0xad, 0xb6, 0xcc, 0x86, 0x30, 0x9a, 0x8d, 0x5c, 0x46, 0x6b,
+	0xbb, 0x71,
+};
+
+/*
+==== CCMP test mpdu   7 ====
+
+-- MPDU Fields
+
+25  Version  = 0
+26  Type     = 2   SubType  = 1  Data+CF-Ack
+27  ToDS     = 1   FromDS   = 0
+28  MoreFrag = 0   Retry    = 1
+29  PwrMgt   = 1   moreData = 1
+30  Encrypt  = 1
+31  Order    = 0
+32  Duration = 18049
+33  A1 = 9b-50-f4-fd-56-f6    BSSID
+34  A2 = ef-ec-95-20-16-91    SA
+35  A3 = 83-57-0c-4c-cd-ee    DA
+36  SC = 0xA020
+37  seqNum = 2562 (0x0A02)  fraqNum = 0 (0x00)
+38  Algorithm = AES_CCM
+39  Key ID = 3
+40  TK = 1b db 34 98 0e 03 81 24 a1 db 1a 89 2b ec 36 6a
+41  PN = 104368786630435  (0x5EEC4073E723)
+42  Header =  18 79 81 46 9b 50 f4 fd 56 f6 ef ec 95 20 16 91 83 57
+43  	0c 4c cd ee 20 a0
+44  Muted MAC Header =  08 41 9b 50 f4 fd 56 f6 ef ec 95 20 16 91
+45  	83 57 0c 4c cd ee 00 00
+46  CCMP Header =  23 e7 00 e0 73 40 ec 5e
+47  CCM Nonce = 00 ef ec 95 20 16 91 5e ec 40 73 e7 23
+48  Plaintext Data = 98 be ca 86 f4 b3 8d a2 0c fd f2 47 24 c5 8e b8
+49  	35 66 53 39
+50  CCM MIC =  2d 09 57 ec fa be 95 b9
+-- Encrypted MPDU with FCS
+1  18 79 81 46 9b 50 f4 fd 56 f6 ef ec 95 20 16 91 83 57 0c 4c cd ee
+2  20 a0 23 e7 00 e0 73 40 ec 5e 12 c5 37 eb f3 ab 58 4e f1 fe f9 a1
+3  f3 54 7a 8c 13 b3 22 5a 2d 09 57 ec fa be 95 b9 aa fa 0c c8
+*/
+static const u_int8_t test7_key[] = {		/* TK */
+	0x1b, 0xdb, 0x34, 0x98, 0x0e, 0x03, 0x81, 0x24,
+	0xa1, 0xdb, 0x1a, 0x89, 0x2b, 0xec, 0x36, 0x6a,
+};
+static const u_int8_t test7_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0x18, 0x79, 0x81, 0x46, 0x9b, 0x50, 0xf4, 0xfd, 0x56, 0xf6,
+	0xef, 0xec, 0x95, 0x20, 0x16, 0x91, 0x83, 0x57, 0x0c, 0x4c,
+	0xcd, 0xee, 0x20, 0xa0,
+	0x98, 0xbe, 0xca, 0x86, 0xf4, 0xb3, 0x8d, 0xa2, 0x0c, 0xfd,
+	0xf2, 0x47, 0x24, 0xc5, 0x8e, 0xb8, 0x35, 0x66, 0x53, 0x39,
+};
+static const u_int8_t test7_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0x18, 0x79, 0x81, 0x46, 0x9b, 0x50, 0xf4, 0xfd, 0x56, 0xf6,
+	0xef, 0xec, 0x95, 0x20, 0x16, 0x91, 0x83, 0x57, 0x0c, 0x4c,
+	0xcd, 0xee, 0x20, 0xa0, 0x23, 0xe7, 0x00, 0xe0, 0x73, 0x40,
+	0xec, 0x5e, 0x12, 0xc5, 0x37, 0xeb, 0xf3, 0xab, 0x58, 0x4e,
+	0xf1, 0xfe, 0xf9, 0xa1, 0xf3, 0x54, 0x7a, 0x8c, 0x13, 0xb3,
+	0x22, 0x5a, 0x2d, 0x09, 0x57, 0xec, 0xfa, 0xbe, 0x95, 0xb9,
+};
+
+/*
+==== CCMP test mpdu   8 ====
+
+-- MPDU Fields
+
+6  Version  = 0
+7  Type     = 2   SubType  = 11
+8  ToDS     = 1   FromDS   = 0
+9  MoreFrag = 0   Retry    = 1
+10  PwrMgt   = 1   moreData = 0
+11  Encrypt  = 1
+12  Order    = 1
+13  Duration = 29260
+14  A1 = 55-2d-5f-72-bb-70    BSSID
+15  A2 = ca-3f-3a-ae-60-c4    SA
+16  A3 = 8b-a9-b5-f8-2c-2f    DA
+17  SC = 0xEB50
+18  seqNum = 3765 (0x0EB5)  fraqNum = 0 (0x00)
+19  QC = 0x000a
+20  MSDU Priority = 10 (0xa)
+21  Algorithm = AES_CCM
+22  Key ID = 2
+23  TK = 6e ac 1b f5 4b d5 4e db 23 21 75 43 03 02 4c 71
+24  PN    = 227588596223197  (0xCEFD996ECCDD)
+25  802.11 Header =  b8 d9 4c 72 55 2d 5f 72 bb 70 ca 3f 3a ae 60 c4
+26  	8b a9 b5 f8 2c 2f 50 eb 2a 55
+27  Muted 802.11 Header =  88 c1 55 2d 5f 72 bb 70 ca 3f 3a ae 60 c4
+28  	8b a9 b5 f8 2c 2f 00 00 0a 00
+29  CCMP Header =  dd cc 00 a0 6e 99 fd ce
+30  CCM Nonce = 0a ca 3f 3a ae 60 c4 ce fd 99 6e cc dd
+31  Plaintext Data = 57 cb 5c 0e 5f cd 88 5e 9a 42 39 e9 b9 ca d6 0d
+32  	64 37 59 79
+33  CCM MIC =  6d ba 8e f7 f0 80 87 dd
+-- Encrypted MPDU with FCS
+35  b8 d9 4c 72 55 2d 5f 72 bb 70 ca 3f 3a ae 60 c4 8b a9 b5 f8 2c 2f
+36  50 eb 2a 55 dd cc 00 a0 6e 99 fd ce 4b f2 81 ef 8e c7 73 9f 91 59
+37  1b 97 a8 7d c1 4b 3f a1 74 62 6d ba 8e f7 f0 80 87 dd 0c 65 74 3f
+*/
+static const u_int8_t test8_key[] = {		/* TK */
+	0x6e, 0xac, 0x1b, 0xf5, 0x4b, 0xd5, 0x4e, 0xdb,
+	0x23, 0x21, 0x75, 0x43, 0x03, 0x02, 0x4c, 0x71,
+};
+static const u_int8_t test8_plaintext[] = {	/* Plaintext MPDU w/o MIC */
+	0xb8, 0xd9, 0x4c, 0x72, 0x55, 0x2d, 0x5f, 0x72, 0xbb, 0x70,
+	0xca, 0x3f, 0x3a, 0xae, 0x60, 0xc4, 0x8b, 0xa9, 0xb5, 0xf8,
+	0x2c, 0x2f, 0x50, 0xeb, 0x2a, 0x55,
+	0x57, 0xcb, 0x5c, 0x0e, 0x5f, 0xcd, 0x88, 0x5e, 0x9a, 0x42,
+	0x39, 0xe9, 0xb9, 0xca, 0xd6, 0x0d, 0x64, 0x37, 0x59, 0x79,
+};
+static const u_int8_t test8_encrypted[] = {	/* Encrypted MPDU with MIC */
+	0xb8, 0xd9, 0x4c, 0x72, 0x55, 0x2d, 0x5f, 0x72, 0xbb, 0x70,
+	0xca, 0x3f, 0x3a, 0xae, 0x60, 0xc4, 0x8b, 0xa9, 0xb5, 0xf8,
+	0x2c, 0x2f, 0x50, 0xeb, 0x2a, 0x55, 0xdd, 0xcc, 0x00, 0xa0,
+	0x6e, 0x99, 0xfd, 0xce, 0x4b, 0xf2, 0x81, 0xef, 0x8e, 0xc7,
+	0x73, 0x9f, 0x91, 0x59, 0x1b, 0x97, 0xa8, 0x7d, 0xc1, 0x4b,
+	0x3f, 0xa1, 0x74, 0x62, 0x6d, 0xba, 0x8e, 0xf7, 0xf0, 0x80,
+	0x87, 0xdd,
+};
+
+#define	TEST(n,name,cipher,keyix,pn) { \
+	name, IEEE80211_CIPHER_##cipher,keyix, pn##LL, \
+	test##n##_key,   sizeof(test##n##_key), \
+	test##n##_plaintext, sizeof(test##n##_plaintext), \
+	test##n##_encrypted, sizeof(test##n##_encrypted) \
+}
+
+struct ciphertest {
+	const char	*name;
+	int		cipher;
+	int		keyix;
+	u_int64_t	pn;
+	const u_int8_t	*key;
+	size_t		key_len;
+	const u_int8_t	*plaintext;
+	size_t		plaintext_len;
+	const u_int8_t	*encrypted;
+	size_t		encrypted_len;
+} ccmptests[] = {
+	TEST(1, "CCMP test mpdu 1", AES_CCM, 0, 199027030681356),
+	TEST(2, "CCMP test mpdu 2", AES_CCM, 2, 54923164817386),
+	TEST(3, "CCMP test mpdu 3", AES_CCM, 2, 52624639632814),
+	TEST(4, "CCMP test mpdu 4", AES_CCM, 0, 270963670912995),
+	TEST(5, "CCMP test mpdu 5", AES_CCM, 2, 184717420531255),
+	TEST(6, "CCMP test mpdu 6", AES_CCM, 1, 118205765159305),
+	TEST(7, "CCMP test mpdu 7", AES_CCM, 3, 104368786630435),
+	TEST(8, "CCMP test mpdu 8", AES_CCM, 2, 227588596223197),
+};
+
+static void
+dumpdata(const char *tag, const void *p, size_t len)
+{
+	int i;
+
+	printk("%s: 0x%p len %u", tag, p, len);
+	for (i = 0; i < len; i++) {
+		if ((i % 16) == 0)
+			printk("\n%03d:", i);
+		printk(" %02x", ((u_int8_t *)p)[i]);
+	}
+	printk("\n");
+}
+
+static void
+cmpfail(const void *gen, size_t genlen, const void *ref, size_t reflen)
+{
+	int i;
+
+	for (i = 0; i < genlen; i++)
+		if (((u_int8_t *)gen)[i] != ((u_int8_t *)ref)[i]) {
+			printk("first difference at byte %u\n", i);
+			break;
+		}
+	dumpdata("Generated", gen, genlen);
+	dumpdata("Reference", ref, reflen);
+}
+
+int
+runtest(struct ieee80211com *ic, struct ciphertest *t)
+{
+	struct ieee80211_key key;
+	struct sk_buff *skb = NULL;
+	const struct ieee80211_cipher *cip;
+	u_int8_t mac[IEEE80211_ADDR_LEN];
+
+	printk("%s: ", t->name);
+
+	/*
+	 * Setup key.
+	 */
+	memset(&key, 0, sizeof(key));
+	key.wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+	key.wk_cipher = &ieee80211_cipher_none;
+	if (!ieee80211_crypto_newkey(ic, t->cipher, &key)) {
+		printk("FAIL: ieee80211_crypto_newkey failed\n");
+		goto bad;
+	}
+
+	memcpy(key.wk_key, t->key, t->key_len);
+	key.wk_keylen = t->key_len;
+	key.wk_keyrsc = 0;
+	key.wk_keytsc = t->pn-1;	/* PN-1 since we do encap */
+	if (!ieee80211_crypto_setkey(ic, &key, mac)) {
+		printk("FAIL: ieee80211_crypto_setkey failed\n");
+		goto bad;
+	}
+
+	/*
+	 * Craft frame from plaintext data.
+	 */
+	cip = key.wk_cipher;
+	skb = dev_alloc_skb(t->plaintext_len +
+			cip->ic_header + cip->ic_trailer);
+	if (skb == NULL) {
+		printk("FAIL: unable to allocate skbuff\n");
+		goto bad;
+	}
+	skb_reserve(skb, cip->ic_header);
+	memcpy(skb_put(skb, t->plaintext_len),
+		t->plaintext, t->plaintext_len);
+
+	/*
+	 * Encrypt frame w/ MIC.
+	 */
+	if (!(*cip->ic_encap)(&key, skb, t->keyix<<6)) {
+		printk("FAIL: ccmp encap failed\n");
+		goto bad;
+	}
+	/*
+	 * Verify: frame length, frame contents.
+	 */
+	if (skb->len != t->encrypted_len) {
+		printk("FAIL: encap data length mismatch\n");
+		cmpfail(skb->data, skb->len,
+			t->encrypted, t->encrypted_len);
+		goto bad;
+	} else if (memcmp(skb->data, t->encrypted, skb->len)) {
+		printk("FAIL: encrypt data does not compare\n");
+		cmpfail(skb->data, skb->len,
+			t->encrypted, t->encrypted_len);
+		dumpdata("Plaintext", t->plaintext, t->plaintext_len);
+		goto bad;
+	}
+
+	/*
+	 * Decrypt frame; strip MIC.
+	 */
+	if (!(*cip->ic_decap)(&key, skb)) {
+		printk("FAIL: ccmp decap failed\n");
+		cmpfail(skb->data, skb->len,
+			t->plaintext, t->plaintext_len);
+		goto bad;
+	}
+	/*
+	 * Verify: frame length, frame contents.
+	 */
+	if (skb->len != t->plaintext_len) {
+		printk("FAIL: decap botch; length mismatch\n");
+		cmpfail(skb->data, skb->len,
+			t->plaintext, t->plaintext_len);
+		goto bad;
+	} else if (memcmp(skb->data, t->plaintext, t->plaintext_len)) {
+		printk("FAIL: decap botch; data does not compare\n");
+		cmpfail(skb->data, skb->len,
+			t->plaintext, sizeof(t->plaintext));
+		goto bad;
+	}
+	dev_kfree_skb(skb);
+	ieee80211_crypto_delkey(ic, &key);
+	printk("PASS\n");
+	return 1;
+bad:
+	if (skb != NULL)
+		dev_kfree_skb(skb);
+	ieee80211_crypto_delkey(ic, &key);
+	return 0;
+}
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: AES-CCMP cipher tester");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static	int tests = -1;
+MODULE_PARM(tests, "i");
+MODULE_PARM_DESC(tests, "Specify which tests to run");
+
+static	int debug = 0;
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO");
+
+static int __init
+init_crypto_ccmp_test(void)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ieee80211com ic;
+	int i, pass, total;
+
+	memset(&ic, 0, sizeof(ic));
+	if (debug)
+		ic.msg_enable = IEEE80211_MSG_CRYPTO;
+	ieee80211_crypto_attach(&ic);
+
+	pass = 0;
+	total = 0;
+	for (i = 0; i < N(ccmptests); i++)
+		if (tests & (1<<i)) {
+			total++;
+			pass += runtest(&ic, &ccmptests[i]);
+		}
+	printk("%u of %u 802.11i AES-CCMP test vectors passed\n", pass, total);
+	ieee80211_crypto_detach(&ic);
+	return (pass == total ? 0 : -1);
+#undef N
+}
+module_init(init_crypto_ccmp_test);
+
+static void __exit
+exit_crypto_ccmp_test(void)
+{
+}
+module_exit(exit_crypto_ccmp_test);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_tkip.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_tkip.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_tkip.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_tkip.c	2005-02-24 13:06:17.415114520 -0800
@@ -0,0 +1,337 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * TKIP test module.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+
+#include <linux/netdevice.h>
+#include "if_media.h"
+#include <net80211/ieee80211_var.h>
+
+/*
+Key	12 34 56 78 90 12 34 56 78 90 12 34 56 78 90 12
+	34 56 78 90 12 34 56 78 90 12 34 56 78 90 12 34
+PN	0x000000000001
+IV	00 20 01 20 00 00 00 00
+Phase1	bb 58 07 1f 9e 93 b4 38 25 4b
+Phase2	00 20 01 4c fe 67 be d2 7c 86 7b 1b f8 02 8b 1c 
+*/
+
+static const u_int8_t ref_key[] = {
+	0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+	0x34, 0x56, 0x78, 0x90, 0x12,
+
+	0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,		/* TX MIC */
+	/*
+	 * NB: 11i test vector specifies a RX MIC key different
+	 *     from the TX key.  But this doesn't work to enmic,
+	 *     encrypt, then decrypt, demic.  So instead we use
+	 *     the same key for doing the MIC in each direction.
+	 *
+	 * XXX need additional vectors to test alternate MIC keys
+	 */
+#if 0
+	0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34,		/* 11i RX MIC */
+#else
+	0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,		/* TX copy */
+#endif
+};
+static const u_int8_t ref_phase1[] = {
+	0xbb, 0x58, 0x07, 0x1f, 0x9e, 0x93, 0xb4, 0x38, 0x25, 0x4b
+};
+static const u_int8_t ref_phase2[] = {
+	0x00, 0x20, 0x01, 0x4c, 0xfe, 0x67, 0xbe, 0xd2, 0x7c, 0x86,
+	0x7b, 0x1b, 0xf8, 0x02, 0x8b, 0x1c, 
+};
+
+/* Plaintext MPDU with MIC */
+static const u_int8_t ref_plaintext[] = {
+0x08,0x42,0x2c,0x00,0x02,0x03,0x04,0x05,0x06,0x08,0x02,0x03,0x04,0x05,0x06,0x07,
+0x02,0x03,0x04,0x05,0x06,0x07,0xd0,0x02,
+0xaa,0xaa,0x03,0x00,0x00,0x00,0x08,0x00,0x45,0x00,0x00,0x54,0x00,0x00,0x40,0x00,
+0x40,0x01,0xa5,0x55,0xc0,0xa8,0x0a,0x02,0xc0,0xa8,0x0a,0x01,0x08,0x00,0x3a,0xb0,
+0x00,0x00,0x00,0x00,0xcd,0x4c,0x05,0x00,0x00,0x00,0x00,0x00,0x08,0x09,0x0a,0x0b,
+0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,
+0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,
+0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
+/* MIC */ 0x68,0x81,0xa3,0xf3,0xd6,0x48,0xd0,0x3c
+};
+
+/* Encrypted MPDU with MIC and ICV */
+static const u_int8_t ref_encrypted[] = {
+0x08,0x42,0x2c,0x00,0x02,0x03,0x04,0x05,0x06,0x08,0x02,0x03,0x04,0x05,0x06,0x07,
+0x02,0x03,0x04,0x05,0x06,0x07,0xd0,0x02,0x00,0x20,0x01,0x20,0x00,0x00,0x00,0x00,
+0xc0,0x0e,0x14,0xfc,0xe7,0xcf,0xab,0xc7,0x75,0x47,0xe6,0x66,0xe5,0x7c,0x0d,0xac,
+0x70,0x4a,0x1e,0x35,0x8a,0x88,0xc1,0x1c,0x8e,0x2e,0x28,0x2e,0x38,0x01,0x02,0x7a,
+0x46,0x56,0x05,0x5e,0xe9,0x3e,0x9c,0x25,0x47,0x02,0xe9,0x73,0x58,0x05,0xdd,0xb5,
+0x76,0x9b,0xa7,0x3f,0x1e,0xbb,0x56,0xe8,0x44,0xef,0x91,0x22,0x85,0xd3,0xdd,0x6e,
+0x54,0x1e,0x82,0x38,0x73,0x55,0x8a,0xdb,0xa0,0x79,0x06,0x8a,0xbd,0x7f,0x7f,0x50,
+0x95,0x96,0x75,0xac,0xc4,0xb4,0xde,0x9a,0xa9,0x9c,0x05,0xf2,0x89,0xa7,0xc5,0x2f,
+0xee,0x5b,0xfc,0x14,0xf6,0xf8,0xe5,0xf8
+};
+
+struct tkip_ctx {
+	struct ieee80211com *tc_ic;	/* for diagnostics */
+
+	u16	tx_ttak[5];
+	int	tx_phase1_done;
+	u8	tx_rc4key[16];
+
+	u16	rx_ttak[5];
+	int	rx_phase1_done;
+	u8	rx_rc4key[16];
+	u_int64_t rx_rsc;		/* held until MIC verified */
+};
+
+static void
+dumpdata(const char *tag, const void *p, size_t len)
+{
+	int i;
+
+	printk("%s: 0x%p len %u", tag, p, len);
+	for (i = 0; i < len; i++) {
+		if ((i % 16) == 0)
+			printk("\n%03d:", i);
+		printk(" %02x", ((u_int8_t *)p)[i]);
+	}
+	printk("\n");
+}
+
+static void
+cmpfail(const void *gen, size_t genlen, const void *ref, size_t reflen)
+{
+	int i;
+
+	for (i = 0; i < genlen; i++)
+		if (((u_int8_t *)gen)[i] != ((u_int8_t *)ref)[i]) {
+			printk("first difference at byte %u\n", i);
+			break;
+		}
+	dumpdata("Generated", gen, genlen);
+	dumpdata("Reference", ref, reflen);
+}
+
+void
+tkip_test(struct ieee80211com *ic)
+{
+	struct tkip_ctx *ctx;
+	struct ieee80211_key key;
+	struct sk_buff *skb = NULL;
+	const struct ieee80211_cipher *cip;
+	u_int8_t mac[IEEE80211_ADDR_LEN];
+
+	/*
+	 * Setup key.
+	 */
+	memset(&key, 0, sizeof(key));
+	key.wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+	key.wk_cipher = &ieee80211_cipher_none;
+	if (!ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_TKIP, &key)) {
+		printk("ieee80211_crypto_newkey failed\n");
+		goto bad;
+	}
+
+	memcpy(key.wk_key, ref_key, sizeof(ref_key));
+	key.wk_keylen = 128/NBBY;
+	key.wk_keyrsc = 0;
+	key.wk_keytsc = 0;
+	if (!ieee80211_crypto_setkey(ic, &key, mac)) {
+		printk("ieee80211_crypto_setkey failed\n");
+		goto bad;
+	}
+
+	/*
+	 * Craft frame from plaintext data.  Note that
+	 * we leave the MIC off as we'll add it ourself
+	 * and then check it against the reference data.
+	 */
+	cip = key.wk_cipher;
+	skb = dev_alloc_skb(sizeof(ref_plaintext) +
+			cip->ic_miclen + cip->ic_header + cip->ic_trailer);
+	if (skb == NULL) {
+		printk("unable to allocate skbuff\n");
+		goto bad;
+	}
+	skb_reserve(skb, cip->ic_header);
+	memcpy(skb_put(skb, sizeof(ref_plaintext) - cip->ic_miclen),
+		ref_plaintext, sizeof(ref_plaintext) - cip->ic_miclen);
+
+	/*
+	 * Add MIC.
+	 */
+	if (!ieee80211_crypto_enmic(ic, &key, skb)) {
+		printk("tkip enmic failed\n");
+		goto bad;
+	}
+	/*
+	 * Verify: frame length, frame contents.
+	 */
+	if (skb->len != sizeof(ref_plaintext)) {
+		printk("enmic botch; length mismatch\n");
+		cmpfail(skb->data, skb->len,
+			ref_plaintext, sizeof(ref_plaintext));
+		goto bad;
+	}
+	if (memcmp(skb->data, ref_plaintext, sizeof(ref_plaintext))) {
+		printk("enmic botch\n");
+		cmpfail(skb->data, skb->len,
+			ref_plaintext, sizeof(ref_plaintext));
+		goto bad;
+	}
+	/*
+	 * Encrypt frame w/ MIC.
+	 */
+	if (!(*cip->ic_encap)(&key, skb, 0<<6)) {
+		printk("tkip encap failed\n");
+		goto bad;
+	}
+	/*
+	 * Verify: phase1, phase2, frame length, frame contents.
+	 */
+	ctx = key.wk_private;
+	if (memcmp(ctx->tx_ttak, ref_phase1, sizeof(ref_phase1))) {
+		printk("encrypt phase1 botch\n");
+		cmpfail(ctx->tx_ttak, sizeof(ctx->tx_ttak),
+			ref_phase1, sizeof(ref_phase1));
+		goto bad;
+	} else if (memcmp(ctx->tx_rc4key, ref_phase2, sizeof(ref_phase2))) {
+		printf("encrypt phase2 botch\n");
+		cmpfail(ctx->tx_rc4key, sizeof(ctx->tx_rc4key),
+			ref_phase2, sizeof(ref_phase2));
+		goto bad;
+	} else if (skb->len != sizeof(ref_encrypted)) {
+		printk("encrypt data length mismatch\n");
+		cmpfail(skb->data, skb->len,
+			ref_encrypted, sizeof(ref_encrypted));
+		goto bad;
+	} else if (memcmp(skb->data, ref_encrypted, skb->len)) {
+		printk("encrypt data does not compare\n");
+		cmpfail(skb->data, skb->len,
+			ref_encrypted, sizeof(ref_encrypted));
+		dumpdata("Plaintext", ref_plaintext, sizeof(ref_plaintext));
+		goto bad;
+	}
+
+	/*
+	 * Decrypt frame.
+	 */
+	if (!(*cip->ic_decap)(&key, skb)) {
+		printk("tkip decap failed\n");
+		/*
+		 * Check reason for failure: phase1, phase2, frame data (ICV).
+		 */
+		if (memcmp(ctx->rx_ttak, ref_phase1, sizeof(ref_phase1))) {
+			printk("decrypt phase1 botch\n");
+			cmpfail(ctx->rx_ttak, sizeof(ctx->rx_ttak),
+				ref_phase1, sizeof(ref_phase1));
+		} else if (memcmp(ctx->rx_rc4key, ref_phase2, sizeof(ref_phase2))) {
+			printf("decrypt phase2 botch\n");
+			cmpfail(ctx->rx_rc4key, sizeof(ctx->rx_rc4key),
+				ref_phase2, sizeof(ref_phase2));
+		} else {
+			printk("decrypt data does not compare\n");
+			cmpfail(skb->data, skb->len,
+				ref_plaintext, sizeof(ref_plaintext));
+		}
+		goto bad;
+	}
+	/*
+	 * Verify: frame length, frame contents.
+	 */
+	if (skb->len != sizeof(ref_plaintext)) {
+		printk("decap botch; length mismatch\n");
+		cmpfail(skb->data, skb->len,
+			ref_plaintext, sizeof(ref_plaintext));
+		goto bad;
+	}
+	if (memcmp(skb->data, ref_plaintext, sizeof(ref_plaintext))) {
+		printk("decap botch; data does not compare\n");
+		cmpfail(skb->data, skb->len,
+			ref_plaintext, sizeof(ref_plaintext));
+		goto bad;
+	}
+	/*
+	 * De-MIC decrypted frame.
+	 */
+	if (!ieee80211_crypto_demic(ic, &key, skb)) {
+		printk("tkip demic failed\n");
+		goto bad;
+	}
+	/* XXX check frame length and contents... */
+	printk("802.11i TKIP test vectors passed\n");
+bad:
+	if (skb != NULL)
+		dev_kfree_skb(skb);
+	ieee80211_crypto_delkey(ic, &key);
+}
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: TKIP cipher tester");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static	int debug = 0;
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO");
+
+static int __init
+init_crypto_tkip_test(void)
+{
+	struct ieee80211com ic;
+
+	memset(&ic, 0, sizeof(ic));
+	if (debug)
+		ic.msg_enable = IEEE80211_MSG_CRYPTO;
+	ieee80211_crypto_attach(&ic);
+
+	tkip_test(&ic);
+
+	ieee80211_crypto_detach(&ic);
+	return 0;
+}
+module_init(init_crypto_tkip_test);
+
+static void __exit
+exit_crypto_tkip_test(void)
+{
+}
+module_exit(exit_crypto_tkip_test);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_wep.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_wep.c
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_wep.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_wep.c	2005-02-24 13:06:17.415114520 -0800
@@ -0,0 +1,326 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * WEP test module.
+ *
+ * Test vectors come from section I.7.2 of P802.11i/D7.0, October 2003.
+ *
+ * To use this tester load the net80211 layer (either as a module or
+ * by statically configuring it into your kernel), then insmod this
+ * module.  It should automatically run all test cases and print
+ * information for each.  To run one or more tests you can specify a
+ * tests parameter to the module that is a bit mask of the set of tests
+ * you want; e.g. insmod wep_test tests=7 will run only test mpdu's
+ * 1, 2, and 3.
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+
+#include <linux/netdevice.h>
+#include "if_media.h"
+#include <net80211/ieee80211_var.h>
+
+/*
+MPDU data
+ aa aa 03 00 00 00 08 00 45 00 00 4e 66 1a 00 00 80 11 be 64 0a 00 01 22
+ 0a ff ff ff 00 89 00 89 00 3a 00 00 80 a6 01 10 00 01 00 00 00 00 00 00
+ 20 45 43 45 4a 45 48 45 43 46 43 45 50 46 45 45 49 45 46 46 43 43 41 43
+ 41 43 41 43 41 43 41 41 41 00 00 20 00 01
+
+RC4 encryption is performed as follows:
+17
+18  Key  fb 02 9e 30 31 32 33 34 
+Plaintext
+ aa aa 03 00 00 00 08 00 45 00 00 4e 66 1a 00 00 80 11 be 64 0a 00 01
+ 22 0a ff ff ff 00 89 00 89 00 3a 00 00 80 a6 01 10 00 01 00 00 00 00
+ 00 00 20 45 43 45 4a 45 48 45 43 46 43 45 50 46 45 45 49 45 46 46 43
+ 43 41 43 41 43 41 43 41 43 41 41 41 00 00 20 00 01 1b d0 b6 04
+Ciphertext
+ f6 9c 58 06 bd 6c e8 46 26 bc be fb 94 74 65 0a ad 1f 79 09 b0 f6 4d
+ 5f 58 a5 03 a2 58 b7 ed 22 eb 0e a6 49 30 d3 a0 56 a5 57 42 fc ce 14
+ 1d 48 5f 8a a8 36 de a1 8d f4 2c 53 80 80 5a d0 c6 1a 5d 6f 58 f4 10
+ 40 b2 4b 7d 1a 69 38 56 ed 0d 43 98 e7 ae e3 bf 0e 2a 2c a8 f7
+The plaintext consists of the MPDU data, followed by a 4-octet CRC-32
+calculated over the MPDU data.
+19  The expanded MPDU, after WEP encapsulation, is as follows:
+20
+21  IV  fb 02 9e 80
+MPDU  data
+ f6 9c 58 06 bd 6c e8 46 26 bc be fb 94 74 65 0a ad 1f 79 09 b0 f6 4d 5f 58 a5
+ 03 a2 58 b7 ed 22 eb 0e a6 49 30 d3 a0 56 a5 57 42 fc ce 14 1d 48 5f 8a a8 36
+ de a1 8d f4 2c 53 80 80 5a d0 c6 1a 5d 6f 58 f4 10 40 b2 4b 7d 1a 69 38 56 ed
+ 0d 43 98 e7 ae e3 bf 0e
+ICV  2a 2c a8 f7  
+*/
+static const u_int8_t test1_key[] = {		/* TK (w/o IV) */
+	0x30, 0x31, 0x32, 0x33, 0x34, 
+};
+static const u_int8_t test1_plaintext[] = {	/* Plaintext MPDU */
+	0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,	/* 802.11 Header */
+	0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+	0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33, 
+	0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,	/* Plaintext data */
+	0x45, 0x00, 0x00, 0x4e, 0x66, 0x1a, 0x00, 0x00,
+	0x80, 0x11, 0xbe, 0x64, 0x0a, 0x00, 0x01, 0x22,
+	0x0a, 0xff, 0xff, 0xff, 0x00, 0x89, 0x00, 0x89,
+	0x00, 0x3a, 0x00, 0x00, 0x80, 0xa6, 0x01, 0x10,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x20, 0x45, 0x43, 0x45, 0x4a, 0x45, 0x48, 0x45,
+	0x43, 0x46, 0x43, 0x45, 0x50, 0x46, 0x45, 0x45,
+	0x49, 0x45, 0x46, 0x46, 0x43, 0x43, 0x41, 0x43,
+	0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x41,
+	0x41, 0x00, 0x00, 0x20, 0x00, 0x01,
+};
+static const u_int8_t test1_encrypted[] = {	/* Encrypted MPDU */
+	0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+	0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+	0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+	0xfb, 0x02, 0x9e, 0x80, 0xf6, 0x9c, 0x58, 0x06,
+	0xbd, 0x6c, 0xe8, 0x46, 0x26, 0xbc, 0xbe, 0xfb,
+	0x94, 0x74, 0x65, 0x0a, 0xad, 0x1f, 0x79, 0x09,
+	0xb0, 0xf6, 0x4d, 0x5f, 0x58, 0xa5, 0x03, 0xa2,
+	0x58, 0xb7, 0xed, 0x22, 0xeb, 0x0e, 0xa6, 0x49,
+	0x30, 0xd3, 0xa0, 0x56, 0xa5, 0x57, 0x42, 0xfc,
+	0xce, 0x14, 0x1d, 0x48, 0x5f, 0x8a, 0xa8, 0x36,
+	0xde, 0xa1, 0x8d, 0xf4, 0x2c, 0x53, 0x80, 0x80,
+	0x5a, 0xd0, 0xc6, 0x1a, 0x5d, 0x6f, 0x58, 0xf4,
+	0x10, 0x40, 0xb2, 0x4b, 0x7d, 0x1a, 0x69, 0x38,
+	0x56, 0xed, 0x0d, 0x43, 0x98, 0xe7, 0xae, 0xe3,
+	0xbf, 0x0e, 0x2a, 0x2c, 0xa8, 0xf7,
+};
+
+/* XXX fix byte order of iv */
+#define	TEST(n,name,cipher,keyix,iv0,iv1,iv2,iv3) { \
+	name, IEEE80211_CIPHER_##cipher,keyix, { iv2,iv1,iv0,iv3 }, \
+	test##n##_key,   sizeof(test##n##_key), \
+	test##n##_plaintext, sizeof(test##n##_plaintext), \
+	test##n##_encrypted, sizeof(test##n##_encrypted) \
+}
+
+struct ciphertest {
+	const char	*name;
+	int		cipher;
+	int		keyix;
+	u_int8_t	iv[4];
+	const u_int8_t	*key;
+	size_t		key_len;
+	const u_int8_t	*plaintext;
+	size_t		plaintext_len;
+	const u_int8_t	*encrypted;
+	size_t		encrypted_len;
+} weptests[] = {
+	TEST(1, "WEP test mpdu 1", WEP, 2, 0xfb, 0x02, 0x9e, 0x80),
+};
+
+static void
+dumpdata(const char *tag, const void *p, size_t len)
+{
+	int i;
+
+	printk("%s: 0x%p len %u", tag, p, len);
+	for (i = 0; i < len; i++) {
+		if ((i % 16) == 0)
+			printk("\n%03d:", i);
+		printk(" %02x", ((u_int8_t *)p)[i]);
+	}
+	printk("\n");
+}
+
+static void
+cmpfail(const void *gen, size_t genlen, const void *ref, size_t reflen)
+{
+	int i;
+
+	for (i = 0; i < genlen; i++)
+		if (((u_int8_t *)gen)[i] != ((u_int8_t *)ref)[i]) {
+			printk("first difference at byte %u\n", i);
+			break;
+		}
+	dumpdata("Generated", gen, genlen);
+	dumpdata("Reference", ref, reflen);
+}
+
+struct wep_ctx_hw {			/* for use with h/w support */
+	struct ieee80211com *wc_ic;	/* for diagnostics */
+	u_int32_t	wc_iv;		/* initial vector for crypto */
+};
+
+int
+runtest(struct ieee80211com *ic, struct ciphertest *t)
+{
+	struct ieee80211_key key;
+	struct sk_buff *skb = NULL;
+	const struct ieee80211_cipher *cip;
+	u_int8_t mac[IEEE80211_ADDR_LEN];
+	struct wep_ctx_hw *ctx;
+
+	printk("%s: ", t->name);
+
+	/*
+	 * Setup key.
+	 */
+	memset(&key, 0, sizeof(key));
+	key.wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+	key.wk_cipher = &ieee80211_cipher_none;
+	if (!ieee80211_crypto_newkey(ic, t->cipher, &key)) {
+		printk("FAIL: ieee80211_crypto_newkey failed\n");
+		goto bad;
+	}
+
+	memcpy(key.wk_key, t->key, t->key_len);
+	key.wk_keylen = t->key_len;
+	if (!ieee80211_crypto_setkey(ic, &key, mac)) {
+		printk("FAIL: ieee80211_crypto_setkey failed\n");
+		goto bad;
+	}
+	cip = key.wk_cipher;
+
+	/*
+	 * Craft encrypted frame from known data.
+	 */
+	skb = dev_alloc_skb(t->encrypted_len);
+	if (skb == NULL) {
+		printk("FAIL: unable to allocate skbuff\n");
+		goto bad;
+	}
+	memcpy(skb_put(skb, t->encrypted_len), t->encrypted, t->encrypted_len);
+
+	/*
+	 * Decrypt frame.
+	 */
+	if (!(*cip->ic_decap)(&key, skb)) {
+		printk("FAIL: wep decap failed\n");
+		cmpfail(skb->data, skb->len,
+			t->plaintext, t->plaintext_len);
+		goto bad;
+	}
+	/*
+	 * Verify: frame length, frame contents.
+	 */
+	if (skb->len != t->plaintext_len) {
+		printk("FAIL: decap botch; length mismatch\n");
+		cmpfail(skb->data, skb->len,
+			t->plaintext, t->plaintext_len);
+		goto bad;
+	} else if (memcmp(skb->data, t->plaintext, t->plaintext_len)) {
+		printk("FAIL: decap botch; data does not compare\n");
+		cmpfail(skb->data, skb->len,
+			t->plaintext, sizeof(t->plaintext));
+		goto bad;
+	}
+
+	/*
+	 * Encrypt frame.
+	 */
+	ctx = (struct wep_ctx_hw *) key.wk_private;
+	memcpy(&ctx->wc_iv, t->iv, sizeof(t->iv));	/* for encap/encrypt */
+	if (!(*cip->ic_encap)(&key, skb, t->keyix<<6)) {
+		printk("FAIL: wep encap failed\n");
+		goto bad;
+	}
+	/*
+	 * Verify: frame length, frame contents.
+	 */
+	if (skb->len != t->encrypted_len) {
+		printk("FAIL: encap data length mismatch\n");
+		cmpfail(skb->data, skb->len,
+			t->encrypted, t->encrypted_len);
+		goto bad;
+	} else if (memcmp(skb->data, t->encrypted, skb->len)) {
+		printk("FAIL: encrypt data does not compare\n");
+		cmpfail(skb->data, skb->len,
+			t->encrypted, t->encrypted_len);
+		dumpdata("Plaintext", t->plaintext, t->plaintext_len);
+		goto bad;
+	}
+	dev_kfree_skb(skb);
+	ieee80211_crypto_delkey(ic, &key);
+	printk("PASS\n");
+	return 1;
+bad:
+	if (skb != NULL)
+		dev_kfree_skb(skb);
+	ieee80211_crypto_delkey(ic, &key);
+	return 0;
+}
+
+/*
+ * Module glue.
+ */
+
+MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+MODULE_DESCRIPTION("802.11 wireless support: WEP cipher tester");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+static	int tests = -1;
+MODULE_PARM(tests, "i");
+MODULE_PARM_DESC(tests, "Specify which tests to run");
+
+static	int debug = 0;
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO");
+
+static int __init
+init_crypto_wep_test(void)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ieee80211com ic;
+	int i, pass, total;
+
+	memset(&ic, 0, sizeof(ic));
+	if (debug)
+		ic.msg_enable = IEEE80211_MSG_CRYPTO;
+	ieee80211_crypto_attach(&ic);
+	pass = 0;
+	total = 0;
+	for (i = 0; i < N(weptests); i++)
+		if (tests & (1<<i)) {
+			total++;
+			pass += runtest(&ic, &weptests[i]);
+		}
+	printk("%u of %u 802.11i WEP test vectors passed\n", pass, total);
+	ieee80211_crypto_detach(&ic);
+	return (pass == total ? 0 : -1);
+#undef N
+}
+module_init(init_crypto_wep_test);
+
+static void __exit
+exit_crypto_wep_test(void)
+{
+}
+module_exit(exit_crypto_wep_test);
diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/version.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/version.h
--- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/version.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/version.h	2005-02-24 13:06:17.415114520 -0800
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: version.h,v 1.10 2005/02/16 16:09:09 samleffler Exp $
+ */
+#define	WLAN_VERSION	"0.8.4.5"
