diff -Naur linux-2.6.12-rc3-no1/adm8211/Changelog no-sources-new/adm8211/Changelog
--- linux-2.6.12-rc3-no1/adm8211/Changelog	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/Changelog	2005-03-23 11:37:34.000000000 +0000
@@ -0,0 +1,255 @@
+20050323
+- Added private ioctl to tune antenna power, lpf cutoff, and lnags threshold
+- Made txpow ioctl actually work
+- Added documentation for these ioctls
+20050307
+- pci_module_init -> pci_register_driver
+20050226
+- Change docs to reflect adhoc is working well now
+- Don't get confused if LinkOn and LinkOff occur at the same time
+20050208
+- Change linkup timeout to 3 seconds
+20050205
+- Use flags in ieee80211_data structure
+- Rescan if the link doesn't go up in 2 seconds
+20050128
+- Update copyright
+- Optimize adhoc settings on adm8211b with input from Emard
+20050124
+- Remove bogus test for monitor mode
+- Fix monitor mode opps
+20050123
+- Make sure we're not scanning before reporting scan results
+- Use time_after in ieee80211_associated
+- Remove NAPI from todo list. Shouldn't be necessary, I don't think
+- Enable LLTX
+20041227
+- Fix the 2.6.10 fix
+20041225
+- Updated suspend/resume stuff for 2.6.10
+- Adjust whitespace
+20041122
+- Crypto API and ARC4 now used for WEP
+- Documentation updated to reflect ARC4 requirement
+20041121
+- EEPROM better documented
+- Adjust whitespace
+- Add more CSRs
+- Remove procfs debugging code
+- Remove unused ADM8211_CR macros
+- Fix ordering in adm8211 tx header
+- Added some casts to make sparse happy
+20041116
+- 2.6 only release. Now working towards using only 2.6 API and inclusion in the main tree
+- Use __iomem stuff
+- Use a big struct instead of macros to access CSRs
+- Use bitwise stuff like __le32 so sparse can find endianess problems
+20041111
+- Change capab to just short_preamble
+- Fix reauthentication after deauthentication. Thanks to Olaf for finding the source of the bug
+- Detect hidden SSID properly. Thanks to Olaf for input on supporting this
+20041106
+- Reset authentication/association counters after success
+- Tolerate bad beacons/probe resp from stupid APs
+20041021
+- Improve eeprom handling code
+- Fix deauthentication handling in ieee80211 code
+20041011
+- Properly expire old BSSes
+- Use proper errnos
+- Fix locking (hopefully)
+- Avoid unnecessary association
+- Misc cleanups
+20041005
+- Adhoc on ADM8211B and up fixed
+- Whitespace changes
+- MWI/cacheline change
+20041002
+- Restructure code in ieee80211.c to avoid unnecessary authentication and association
+- Remove delay in scanning
+- Prevent stall timer from restarting card when card is shutting down
+- Redo beacon/TSF/link stuff
+20040929
+- Remove unused authentication code in ieee80211.c
+- Show AP MAC addr setting if the interface is down and the user set one
+- Replace TX timeout stall detection with proper dma stall detection
+- Reduce number of arguments to ieee80211_add_bss
+20040927
+- Put write to PAR in the beginning of open
+- Move adm8211_write_bbp function
+- Make switch indenting consistant
+- Remove unnecessary function prototypes in adm8211_hw.c
+- Other cleanups
+20040926
+- Fix bug in hidden SSID support
+- Disable write to PAR for now
+20040923
+- Update notes file to reflect the SMC2635W I have for testing now. Thanks nurey!
+- Use proper max RX duration
+- Fixed bug causing bbp postwrite errors
+- Adjustment to hidden SSID code
+- Add locks for future compatibility with LLTX
+- Better use of register defines for PAR
+- Whitespace changes in adm8211_tx function
+- Other misc. cleanups and changes
+20040916
+- Use RF3000 BBP register defines from adm8211.h
+- Another attempt at hidden SSID support
+20040912
+- Whitespace changes
+- Dead code removed from link change code
+- MAC addr setting code simplified
+- Cleanup tx rate setting IOCTL
+- Remove unneeded code in wep setting IOCTL
+- Use dev->name in private IOCTLs in printk
+20040911
+- Cleanup channel setting code
+- Try to support cloaked ssids
+- Init BBP for adm8211b twice
+20040901
+- Updated Fedora Core 2 HOWTO from author
+- Some modifications of FC2 HOWTO with suggestions from Per Bjornsson
+- Report current TX power
+- Cleanup error bit ignoring for ADM8211B/C + Ad-hoc
+20040831
+- Use DMA_32BIT_MASK macro. yay.
+- Use pci_dma_sync_single_for_device and pci_dma_sync_single_for_cpu
+- Add more messages for ad-hoc mode
+- Uncomment some beacon configuration code
+20040825
+- Replace "select BSSID" message with better message
+- Fix compile on 2.4 by adding compat.h to adm8211_ioctl
+20040821
+- Fix kernel.patch
+- Improve/simplify authentication/association TX/RX printks
+- Suspend/resume tested & fixed
+20040820
+- Changed dev->priv to netdev_priv(dev)
+- Use skb_queue_purge
+- Remove module options for enabling/disabling hardware WEP - no one has reported usage of this.
+- Updated kernel.patch to source Kconfig from this dir
+- TX power IOCTLs added, not yet used
+20040817
+- Change Makefile so it works inside the kernel source tree
+- Added kernel.patch, against 2.6.8.1
+- Updated INSTALL file with info on putting driver into kernel tree
+- Disable BBP warning on adm8211b again
+20040816
+- Remove alternate SYN writing code for rfmd2958
+- Return 1 in ieee80211_data_tx when we're waiting for the card to associate
+- Check MAC for validity, generate a valid one if necessary
+- Add eeprom struct for better code self-documentation
+20040815
+- Fix compile error
+20040814
+- Adjust adm8211_set_rx_mode
+- Separate ieee80211 and adm8211_hw code better
+- Add alternate SYN writing code for rfmd2958
+- Changed BSSID generation in adhoc mode slightly
+- Initial suspend/resume support
+20040812
+- Start RX/TX early
+- Fix memleak and other things in fragment reassembly
+- Make adm8211b and up use error bits if not in adhoc mode
+- Made direct syn writing function for rfmd2948.. not used yet
+20040811
+- Adhoc fixed
+- Remove unnecessary check in ieee80211_encaps
+- Retry limit ioctl finished
+20040810
+- Makefile-2.4 now checks KDIR
+- 2.4 build fixes
+- Move things around in adm8211_probe
+- Added NOTES.FC2, contributed by Kyrre Ness Sjobak <kyrre@solution-forge.net>.
+20040809
+- Move compatibility stuff to compat.h
+- Added watchdog timer for ADM8211B to restart the card if it gets stuck
+- Remove some unnecessary code in ring setup
+- Fix value written to WCSR
+- Disable hardware link off detection for now
+20040804
+- Adjust MWI enabling
+- Really fix monitor mode
+- Cleanup adm8211_interrupt
+- Fix value written to BPLI
+20040731
+- Cleanup in adm8211_rx_skb
+- Bunch of fixes to minimize usage count problem
+20040730
+- Added module parameters to disable hardware wep for RX (soft_rxwep) and TX (soft_txwep). Set to non-zero value to use.
+- Fixed ieee80211_stop deadlock
+20040726
+- Fixed crash after authentication attempt times out 5 times for a single AP
+- register_netdev only when everything is ready
+- Display pci_name when error occurs during probe
+- Disable postwrite warning on adm8211b
+20040724
+- Monitor mode fixed
+- Move the setting of dev->last_rx
+20040719
+- MWI enabled
+- Fix promiscuous mode for WEP. Some WEP disabling code for monitor mode removed, not needed.
+- Cleaned up TX rate selection code
+- Removed some unnecessary code
+- Removed Philips SA2400A - not used anymore
+20040715
+- Hardware WEP fixed
+- RFMD2958 and MAX2420 transceiver support for ADM8211C added. No hardware to test though..
+20040714
+- Clean up ring on device removal
+- Do not receive data packets before association
+- Tiny syn writing cleanup
+- Ensure packet is unicast before doing frag or rts
+- 2.4 compile fix (no SET_NETDEV_DEV)
+20040711
+- Big cleanups to adm8211_tx and all the code that calls it
+20040707
+- More misc cleanups
+- Mode change bug fix
+20040705
+- Fix select_bssid function
+- Bunch of fixes for adhoc so things don't crash
+20040704
+- Improve authentication failure
+- Don't start queue until associated (makes dhcpcd faster..)
+- Started retry count IOCTLs
+20040702
+- Save/use NAR register state in adm8211_priv
+- Possible workaround for SMC 2635W bug
+- Use AVS Capture Frame Format v2 for monitor mode
+20040630
+- Reassemble 802.11 fragments properly
+- Fix WEP cleanup
+- Removed some magic numbers
+- Finished hardware beacon support for adhoc (or at least the hard part)
+- Other misc stuff
+20040626
+- Association request changed to match admtek's code.. need to check if that is correct
+- Do not authenticate with adhoc station when in managed mode, and vice versa
+- Clean up some wep code
+- Eep. Fix bad rfmd2958 writing code (used in rev 0x20). Strange, the AL2210 isn't affected by the same bug..
+- Another silly mistake. MMIRD1 wasn't actually written to for rev 0x20...
+20040624
+- RTS threshold used now. Needs testing
+- Added a simple 2.4 makefile from SuD
+- Improved writing to SRAM
+- PCI ID for 3Com 3CRSHPW796 added. Needs testing
+- Some work on adhoc
+20040623
+- Software WEP fixed
+20040622
+- Added RTS ioctls. Not actually used by the driver yet.
+- Removed some unnecessary/dead code
+- Improved error reporting on association failure
+- Interface doesn't have to be down to switch wireless modes anymore
+20040617
+- Even more (little) changes to try to get adm8211b working
+20040616
+- More adm8211b stuff
+- Makefile changed to support both 2.6 and 2.4 (inspired by a makefile created by SCR <king_903@yahoo.com>)
+- Make link light turn on after association, not anytime before
+20040615
+- Changelog created
+- Locking bug triggered by more than 6 failed association attempts fixed.
+- Other changes that I can't remember
+
diff -Naur linux-2.6.12-rc3-no1/adm8211/INSTALL no-sources-new/adm8211/INSTALL
--- linux-2.6.12-rc3-no1/adm8211/INSTALL	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/INSTALL	2004-11-22 17:12:58.000000000 +0000
@@ -0,0 +1,21 @@
+Installing this driver should be fairly easy if you meet the prerequisites.
+
+For 2.6:
+1. Latest module-init-tools. Ok, you probably don't need the very latest, but I don't want to guess what the minimum version should be, so just make sure you have the latest.
+2. Latest gcc 3.x or 2.95.x. Chances are, many many other versions also work - I'm just not supporting them.
+3. 2.6.9 kernel or newer. You must have your kernel tree installed and configured for kbuild to build your module. Crypto API (CONFIG_CRYPTO) and the ARC4 cipher must be compiled in or as modules or the driver will not work.
+4. Wireless tools. Latest is prefered, but having slightly older versions are very unlikely to cause problems, at least compared to potential problems with the driver itself.
+5. Latest hotplug scripts. This is somewhat optional, but it makes things easier.
+
+To install, just run make install. You can also do make, then make modules_install, which uses the kernel build system to install the modules. However, if you do it the second way, you'll need to run depmod -a manually.
+
+To put the driver inside the kernel source tree (2.6 only, not recommended unless you run a kernel w/o module loading):
+1. Apply kernel.patch
+2. Copy the entire adm8211 directory into $(KERNEL_SOURCE)/drivers/net/wireless
+
+Then you can configure/compile/install the driver like any other driver included with the kernel. Note that the driver is installed to a different place (as a module) than usual when compiled & installed inside the kernel tree, so be careful when upgrading.
+
+To load the driver if it isn't automatically loaded, just run modprobe adm8211.
+
+For 2.4:
+2.4 is no longer supported. 20041111 was the last release that supports both 2.4 and 2.6.
diff -Naur linux-2.6.12-rc3-no1/adm8211/Kconfig no-sources-new/adm8211/Kconfig
--- linux-2.6.12-rc3-no1/adm8211/Kconfig	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/Kconfig	2004-11-22 17:14:32.000000000 +0000
@@ -0,0 +1,27 @@
+config ADM8211
+	tristate "ADMtek ADM8211 support"
+	depends on NET_RADIO && PCI && CRYPTO_ARC4 && EXPERIMENTAL
+	---help---
+	  This driver is for ADM8211A, ADM8211B, and ADM8211C based cards.
+	  These are PCI/mini-PCI/Cardbus 802.11b chips found in cards like:
+
+	  Xterasys Cardbus XN-2411b
+	  Blitz NetWave Point PC
+	  TrendNet 221pc
+	  Belkin F5D6001
+	  SMC 2635W
+	  Linksys WPC11 v1
+	  Fiberline FL-WL-200X
+	  3com Office Connect (3CRSHPW796)
+	  Corega WLPCIB-11
+	  SMC 2602W V2 EU
+	  D-Link DWL-520 Revision C
+
+	  However, some of these cards have been replaced with other chips
+	  like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or
+	  the Ralink RT2400 (SMC2635W) without a model number change.
+
+	  Thanks to Infineon-ADMtek for their support of this driver.
+
+	  The latest version of this driver can be found at:
+	  <http://aluminum.sourmilk.net/adm8211>
diff -Naur linux-2.6.12-rc3-no1/adm8211/Makefile no-sources-new/adm8211/Makefile
--- linux-2.6.12-rc3-no1/adm8211/Makefile	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/Makefile	2004-11-15 18:07:39.000000000 +0000
@@ -0,0 +1,37 @@
+# Makefile for Linux 2.6.x and 2.4.x kbuild
+
+ifneq ($(KERNELRELEASE),)
+
+ifeq ($(CONFIG_ADM8211),)
+	CONFIG_ADM8211=m
+endif
+
+adm8211-objs		:= adm8211_hw.o adm8211_ioctl.o ieee80211.o wep.o
+obj-$(CONFIG_ADM8211)	+= adm8211.o
+
+else
+KDIR	:= /lib/modules/$(shell uname -r)/build
+PWD	:= $(shell pwd)
+
+MODULE_NAME = adm8211.ko
+KBUILD_PARAMS = -C $(KDIR) M=$(PWD)
+
+all: modules
+
+modules:
+	$(MAKE) $(KBUILD_PARAMS) modules
+
+modules_install:
+	$(MAKE) $(KBUILD_PARAMS) modules_install
+
+checkstack:
+	$(MAKE) $(KBUILD_PARAMS) checkstack
+
+install: all
+	cp -f $(MODULE_NAME) /lib/modules/$(shell uname -r)/kernel/drivers/net/wireless/
+	depmod -a
+
+clean:
+	$(MAKE) $(KBUILD_PARAMS) clean
+
+endif
diff -Naur linux-2.6.12-rc3-no1/adm8211/NOTES no-sources-new/adm8211/NOTES
--- linux-2.6.12-rc3-no1/adm8211/NOTES	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/NOTES	2005-03-23 11:43:53.000000000 +0000
@@ -0,0 +1,48 @@
+This driver requires crypto api (CONFIG_CRYPTO) & the ARC4 cipher.
+
+This card has been tested and known to work on:
+	Xterasys Cardbus XN-2411b
+	Blitz - NetWave Point PC
+	TrendNet 221pc
+	Belkin F5D6001
+	SMC 2635W
+	Linksys WPC11 v1
+	Fiberline FL-WL-200X
+	3com office connect card (3CRSHPW796)
+	Corega WLPCIB-11
+	SMC 2602W V2 EU
+	D-Link DWL-520 Revision C
+	Blitz BWP612
+	D-Link DWL-650 rev. L1
+
+Cards made by Runtop (mac addr starts with 00:03:6D) do not seem to work correctly with this driver.
+
+Note that some cards that use ADM8211 are being replaced with RTL8180L without a change to the model number/name. For example, the Xterasys XN-2411b and Belkin F5D6001. Also, a SMC 2635W has been found with a RaLink RT2400 chip. If the driver is installed correctly yet doesn't work, make sure you really have an ADM8211 based card.
+
+PCI Revisions:
+	0x11 (ADM8211A): Should work.
+	0x15 (ADM8211A?): Unknown. Does this exist? If anyone has one of these, let me know. I assume it should work..
+	0x20 (ADM8211B): Commonly found on SMC 2635W cards. nurey sent me a SMC 2635W. AP mode possible. (but not yet implemented)
+	0x30 (ADM8211C): Support is completed. ADMtek sent me two samples of this card with an airoha transceiver. AP mode possible.
+
+Please email me with your card name/info, the kernel messages (dmesg) from the driver about the card (revision, bbp, etc.), and whether or not it worked if it isn't on the list. Note that if the card can't get a dynamic address via dhcp, it's unlikely to work with a staticly assigned address either so there is no need to staticly assign an IP address to your interface unless that's what you already do.
+
+The txpow setting accepts values from 0x0 to 0x3F. For the private wireless ioctls set_lpfcutoff and set_lnagsthresh, values from 0x0 to 0xFF are accepted, but 0xFF disables manual override of eeprom settings. Antenna power (set_antpower) is only used on the ADM8211B, and accepts values from 0x0 to 0x3F. Any value above 0x3F disables manual override of antenna power.
+
+Managed mode is supported and well tested.
+
+Monitor mode is supported and tested.
+
+Adhoc mode is supported and tested.
+
+Suspending/resuming works.
+
+This driver is aiming for integration into the netdev/mm series. Check the TODO file for the current progress.
+
+This driver is tested on the latest stable 2.6 kernel (with various patches like mm/ck/suspend2 sometimes) with gcc 3.4.2.
+
+There are two mailing lists for this driver:
+http://sourmilk.net/mailman/listinfo/adm8211-announce_sourmilk.net
+http://sourmilk.net/mailman/listinfo/adm8211-user_sourmilk.net
+
+Michael Wu <flamingice@sourmilk.net> (current maintainer of this driver. please do not bug Jouni, since he does not work on this driver right now)
diff -Naur linux-2.6.12-rc3-no1/adm8211/NOTES-FC2 no-sources-new/adm8211/NOTES-FC2
--- linux-2.6.12-rc3-no1/adm8211/NOTES-FC2	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/NOTES-FC2	2004-10-30 16:24:53.000000000 +0000
@@ -0,0 +1,86 @@
+(the following is a howto contributed by Kyree Ness Sjøbæk)
+
+The SMC 2635 W HOWTO (ADM8211 chipset) for Fedora Core 2
+
+This howto describes how to install the SMC 2635 W PCMCIA wireless
+adapter on Fedora Core 2. It could probably be adapted to other
+compatible adapters and distributions as well, but this is the equipment
+i have used.
+
+What you need:
+- A wlan pcmcia card with the adm 8211 chipset, such as the SMC 2635
+- A 2.6.6 kernel or better (i used the Redhat binary/source rpm version
+"2.6.7-1.494.2.2", which you can download manually, or with a tool such
+as yum or apt. You need the kernel-source, the kernel (can be generated
+from the source, but that is outside the scope of this document). While
+your at it, grab the documentation as well.
+- GCC and friends. Can be installed from the CD, or using
+"system-config-packages" and installing "development tools" from there.
+- kudzu - the redhat hardware detection and installation tool.
+(installed by default)
+- neat alias system-config-network - the redhat network set-up gui
+(installed by default?)
+- The drivers, which you can download from:
+
+http://aluminum.sourmilk.net/adm8211/index.php?sort=date
+
+There is also a forum (or at least a thread) dedicated to the drivers,
+just take a look at:
+
+http://www.linuxquestions.org/questions/showthread.php?s=&threadid=132161&perpage=15&pagenumber=1
+
+And two (low traffic) mailing-lists - which are found at:
+http://sourmilk.net/mailman/listinfo/adm8211-announce_sourmilk.net -
+announcments of new driver versions etc
+http://sourmilk.net/mailman/listinfo/adm8211-user_sourmilk.net - General
+discussion, support etc.
+
+Okay, so this is what we are doing:
+
+1. Install the new kernel if needed. Install the development-tools.
+Reboot into the new kernel, and make sure everything else works as
+intended. When it does, remove the extra entry for the old kernel in
+/etc/grub.conf so you dont boot it - it will make your card inpossible
+to connect.
+
+2. Get the latest drivers from this site:
+
+http://aluminum.sourmilk.net/adm8211/index.php?sort=date
+
+Make sure you read the "INSTALL" file included - the procedure may have
+changed.
+
+Unzip the driver, and fire up your terminal. Then do:
+
+- Login as root, including its paths:
+        $ su -
+        <type root-password followed by enter>
+- cd into the path where you unzipped the drivers
+        # cd /path/to/drivers/
+- Compile and install the drivers (this may take a minute)
+        # make install
+- If everything went well - load the newly installed driver
+        # modprobe adm8211
+- Then run kudzu in order to set up and configure the new network
+device. Make shure the card is connected and the "PWR/ACT" light is on.
+        # kudzu
+- Log out of the terminal. Press control-D until you are logged out.
+
+The card has now been installed, and you may configure it through the
+"system-config-network" alias "neat" GUI. To open it, run "neat" from
+the run-dialogue, or simply click RedHat-menu -> system-settings ->
+network
+
+You will see your new device there. Select it and push "edit" in order
+to bring up the configuration dialogue.
+
+Tips: If you are using gnome, you can activate the wifi applet on the
+taskbar. In order to do it, right-click the taskbar, click Add to panel
+-> Internet -> Wireless Link Monitor.
+
+Known bugs in the driver (as for 20040831):
+- None AFIC
+
+This howto was written by Kyrre Ness Sjøbæk. For suggestions etc,
+contact me on:
+kyrre [a-with-a-circle] solution-forge [period] net
diff -Naur linux-2.6.12-rc3-no1/adm8211/TODO no-sources-new/adm8211/TODO
--- linux-2.6.12-rc3-no1/adm8211/TODO	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/TODO	2005-03-23 11:44:32.000000000 +0000
@@ -0,0 +1,25 @@
+
+Things that need to be tested
+ - Multicast
+ - RTS
+ - Fragment reassembly
+ - TX fragment.. tested and found to be broken.
+
+Things to be completed before being sent to the mm series
+ - clean up duplicate frame handling
+ - fix ping problem (improving..)
+ - private IOCTLs
+	- clean up
+	- add ioctls to adjust bbp values
+		- antenna
+
+Things scheduled for work after above is completed
+ - WPA support (software only first)
+
+Things scheduled for work in the wireless-2.6 branch (after above is finished)
+ - AP mode
+ - 802.11i support
+ - PCF support
+ - Power Management (beyond software suspend/resume)
+ - WOL
+ - Ethtool support
diff -Naur linux-2.6.12-rc3-no1/adm8211/adm8211.h no-sources-new/adm8211/adm8211.h
--- linux-2.6.12-rc3-no1/adm8211/adm8211.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/adm8211.h	2005-03-23 10:12:37.000000000 +0000
@@ -0,0 +1,449 @@
+#ifndef ADM8211_H
+#define ADM8211_H
+
+#include "ieee80211.h"
+
+/* ADM8211 Registers */
+
+/* CR32 (SIG) signature */
+#define ADM8211_SIG1		0x82011317 /* ADM8211A */
+#define ADM8211_SIG2		0x82111317 /* ADM8211B/ADM8211C */
+
+#define ADM8211_CSR_READ(r) ioread32(&priv->map->r)
+#define ADM8211_CSR_WRITE(r, val) iowrite32((__force u32)val, &priv->map->r)
+
+/* CSR (Host Control and Status Registers) */
+struct adm8211_csr {
+	__le32 PAR;		/* 0x00 CSR0 */
+	__le32 FRCTL;		/* 0x04 CSR0A */
+	__le32 TDR;		/* 0x08 CSR1 */
+	__le32 WTDP;		/* 0x0C CSR1A */
+	__le32 RDR;		/* 0x10 CSR2 */
+	__le32 WRDP;		/* 0x14 CSR2A */
+	__le32 RDB;		/* 0x18 CSR3 */
+	__le32 TDBH;		/* 0x1C CSR3A */
+	__le32 TDBD;		/* 0x20 CSR4 */
+	__le32 TDBP;		/* 0x24 CSR4A */
+	__le32 STSR;		/* 0x28 CSR5 */
+	__le32 TDBB;		/* 0x2C CSR5A */
+	__le32 NAR;		/* 0x30 CSR6 */
+	__le32 CSR6A;		/* reserved */
+	__le32 IER;		/* 0x38 CSR7 */
+	__le32 TKIPSCEP;	/* 0x3C CSR7A */	
+	__le32 LPC;		/* 0x40 CSR8 */
+	__le32 CSR_TEST1;	/* 0x44 CSR8A */
+	__le32 SPR;		/* 0x48 CSR9 */
+	__le32 CSR_TEST0;	/* 0x4C CSR9A */
+	__le32 WCSR;		/* 0x50 CSR10 */
+	__le32 WPDR;		/* 0x54 CSR10A */
+	__le32 GPTMR;		/* 0x58 CSR11 */
+	__le32 GPIO;		/* 0x5C CSR11A */
+	__le32 BBPCTL;		/* 0x60 CSR12 */
+	__le32 SYNCTL;		/* 0x64 CSR12A */
+	__le32 PLCPHD;		/* 0x68 CSR13 */
+	__le32 MMIWA;		/* 0x6C CSR13A */
+	__le32 MMIRD0;		/* 0x70 CSR14 */
+	__le32 MMIRD1;		/* 0x74 CSR14A */
+	__le32 TXBR;		/* 0x78 CSR15 */
+	__le32 SYNDATA;		/* 0x7C CSR15A */
+	__le32 ALCS;		/* 0x80 CSR16 */
+	__le32 TOFS2;		/* 0x84 CSR17 */
+	__le32 CMDR;		/* 0x88 CSR18 */
+	__le32 PCIC;		/* 0x8C CSR19 */
+	__le32 PMCSR;		/* 0x90 CSR20 */
+	u32 PAR0;		/* 0x94 CSR21 */
+	u32 PAR1;		/* 0x98 CSR22 */
+	u32 MAR0;		/* 0x9C CSR23 */
+	u32 MAR1;		/* 0xA0 CSR24 */
+	u32 ATIMDA0;		/* 0xA4 CSR25 */
+	u32 ABDA1;		/* 0xA8 CSR26 */
+	u32 BSSID0;		/* 0xAC CSR27 */
+	__le32 TXLMT;		/* 0xB0 CSR28 */
+	__le32 MIBCNT;		/* 0xB4 CSR29 */
+	__le32 BCNT;		/* 0xB8 CSR30 */
+	__le32 TSFTH;		/* 0xBC CSR31 */
+	__le32 TSC;		/* 0xC0 CSR32 */
+	__le32 SYNRF;		/* 0xC4 CSR33 */
+	__le32 BPLI;		/* 0xC8 CSR34 */
+	__le32 CAP0;		/* 0xCC CSR35 */
+	__le32 CAP1;		/* 0xD0 CSR36 */
+	__le32 RMD;		/* 0xD4 CSR37 */
+	__le32 CFPP;		/* 0xD8 CSR38 */
+	__le32 TOFS0;		/* 0xDC CSR39 */
+	__le32 TOFS1;		/* 0xE0 CSR40 */
+	__le32 IFST;		/* 0xE4 CSR41 */
+	__le32 RSPT;		/* 0xE8 CSR42 */
+	__le32 TSFTL;		/* 0xEC CSR43 */
+	__le32 WEPCTL;		/* 0xF0 CSR44 */
+	__le32 WESK;		/* 0xF4 CSR45 */
+	__le32 WEPCNT;		/* 0xF8 CSR46 */
+	__le32 MACTEST;		/* 0xFC CSR47 */
+	__le32 FER;		/* 0x100 */
+	__le32 FEMR;		/* 0x104 */
+	__le32 FPSR;		/* 0x108 */
+	__le32 FFER;		/* 0x10C */
+} __attribute__ ((packed));
+
+/* CSR0 - PAR (PCI Address Register) */
+#define ADM8211_PAR_MWIE	(1 << 24)
+#define ADM8211_PAR_MRLE	(1 << 23)
+#define ADM8211_PAR_MRME	(1 << 21)
+#define ADM8211_PAR_RAP		((1 << 18) | (1 << 17))
+#define ADM8211_PAR_CAL		((1 << 15) | (1 << 14))
+#define ADM8211_PAR_PBL		0x00003f00
+#define ADM8211_PAR_BLE		(1 << 7)
+#define ADM8211_PAR_DSL		0x0000007c
+#define ADM8211_PAR_BAR		(1 << 1)
+#define ADM8211_PAR_SWR		(1 << 0)
+
+/* CSR1 - FRCTL (Frame Control Register) */
+#define ADM8211_FRCTL_PWRMGT	(1 << 31)
+#define ADM8211_FRCTL_MAXPSP	(1 << 27)
+#define ADM8211_FRCTL_AID	0x0000ffff
+#define ADM8211_FRCTL_AID_ON	0x0000c000
+
+/* CSR5 - STSR (Status Register) */
+#define ADM8211_STSR_PCF	(1 << 31)
+#define ADM8211_STSR_BCNTC	(1 << 30)
+#define ADM8211_STSR_GPINT	(1 << 29)
+#define ADM8211_STSR_LinkOff	(1 << 28)
+#define ADM8211_STSR_ATIMTC	(1 << 27)
+#define ADM8211_STSR_TSFTF	(1 << 26)
+#define ADM8211_STSR_TSCZ	(1 << 25)
+#define ADM8211_STSR_LinkOn	(1 << 24)
+#define ADM8211_STSR_SQL	(1 << 23)
+#define ADM8211_STSR_WEPTD	(1 << 22)
+#define ADM8211_STSR_ATIME	(1 << 21)
+#define ADM8211_STSR_TBTT	(1 << 20)
+#define ADM8211_STSR_NISS	(1 << 16)
+#define ADM8211_STSR_AISS	(1 << 15)
+#define ADM8211_STSR_TEIS	(1 << 14)
+#define ADM8211_STSR_FBE	(1 << 13)
+#define ADM8211_STSR_REIS	(1 << 12)
+#define ADM8211_STSR_GPTT	(1 << 11)
+#define ADM8211_STSR_RPS	(1 << 8)
+#define ADM8211_STSR_RDU	(1 << 7)
+#define ADM8211_STSR_RCI	(1 << 6)
+#define ADM8211_STSR_TUF	(1 << 5)
+#define ADM8211_STSR_TRT	(1 << 4)
+#define ADM8211_STSR_TLT	(1 << 3)
+#define ADM8211_STSR_TDU	(1 << 2)
+#define ADM8211_STSR_TPS	(1 << 1)
+#define ADM8211_STSR_TCI	(1 << 0)
+
+/* CSR6 - NAR (Network Access Register) */
+#define ADM8211_NAR_TXCF	(1 << 31)
+#define ADM8211_NAR_HF		(1 << 30)
+#define ADM8211_NAR_UTR		(1 << 29)
+#define ADM8211_NAR_SQ		(1 << 28)
+#define ADM8211_NAR_CFP		(1 << 27)
+#define ADM8211_NAR_SF		(1 << 21)
+#define ADM8211_NAR_TR		((1 << 15) | (1 << 14))
+#define ADM8211_NAR_ST		(1 << 13)
+#define ADM8211_NAR_OM		((1 << 11) | (1 << 10))
+#define ADM8211_NAR_MM		(1 << 7)
+#define ADM8211_NAR_PR		(1 << 6)
+#define ADM8211_NAR_EA		(1 << 5)
+#define ADM8211_NAR_PB		(1 << 3)
+#define ADM8211_NAR_STPDMA	(1 << 2)
+#define ADM8211_NAR_SR		(1 << 1)
+#define ADM8211_NAR_CTX		(1 << 0)
+
+/* CSR7 - IER (Interrupt Enable Register) */
+#define ADM8211_IER_PCFIE	(1 << 31)
+#define ADM8211_IER_BCNTCIE	(1 << 30)
+#define ADM8211_IER_GPIE	(1 << 29)
+#define ADM8211_IER_LinkOffIE	(1 << 28)
+#define ADM8211_IER_ATIMTCIE	(1 << 27)
+#define ADM8211_IER_TSFTFIE	(1 << 26)
+#define ADM8211_IER_TSCZE	(1 << 25)
+#define ADM8211_IER_LinkOnIE	(1 << 24)
+#define ADM8211_IER_SQLIE	(1 << 23)
+#define ADM8211_IER_WEPIE	(1 << 22)
+#define ADM8211_IER_ATIMEIE	(1 << 21)
+#define ADM8211_IER_TBTTIE	(1 << 20)
+#define ADM8211_IER_NIE		(1 << 16)
+#define ADM8211_IER_AIE		(1 << 15)
+#define ADM8211_IER_TEIE	(1 << 14)
+#define ADM8211_IER_FBEIE	(1 << 13)
+#define ADM8211_IER_REIE	(1 << 12)
+#define ADM8211_IER_GPTIE	(1 << 11)
+#define ADM8211_IER_RSIE	(1 << 8)
+#define ADM8211_IER_RUIE	(1 << 7)
+#define ADM8211_IER_RCIE	(1 << 6)
+#define ADM8211_IER_TUIE	(1 << 5)
+#define ADM8211_IER_TRTIE	(1 << 4)
+#define ADM8211_IER_TLTTIE	(1 << 3)
+#define ADM8211_IER_TDUIE	(1 << 2)
+#define ADM8211_IER_TPSIE	(1 << 1)
+#define ADM8211_IER_TCIE	(1 << 0)
+
+/* CSR9 - SPR (Serial Port Register) */
+#define ADM8211_SPR_SRS		(1 << 11)
+#define ADM8211_SPR_SDO		(1 << 3)
+#define ADM8211_SPR_SDI		(1 << 2)
+#define ADM8211_SPR_SCLK	(1 << 1)
+#define ADM8211_SPR_SCS		(1 << 0)
+
+/* CSR9A - CSR_TEST0 */
+#define ADM8211_CSR_TEST0_EPNE	(1 << 18)
+#define ADM8211_CSR_TEST0_EPSNM	(1 << 17)
+#define ADM8211_CSR_TEST0_EPTYP	(1 << 16)
+#define ADM8211_CSR_TEST0_EPRLD	(1 << 15)
+
+/* CSR11A - GPIO */
+#define ADM8211_CSR_GPIO_EN5	(1 << 17)
+#define ADM8211_CSR_GPIO_EN4	(1 << 16)
+#define ADM8211_CSR_GPIO_EN3	(1 << 15)
+#define ADM8211_CSR_GPIO_EN2	(1 << 14)
+#define ADM8211_CSR_GPIO_EN1	(1 << 13)
+#define ADM8211_CSR_GPIO_EN0	(1 << 12)
+#define ADM8211_CSR_GPIO_O5	(1 << 11)
+#define ADM8211_CSR_GPIO_O4	(1 << 10)
+#define ADM8211_CSR_GPIO_O3	(1 << 9)
+#define ADM8211_CSR_GPIO_O2	(1 << 8)
+#define ADM8211_CSR_GPIO_O1	(1 << 7)
+#define ADM8211_CSR_GPIO_O0	(1 << 6)
+#define ADM8211_CSR_GPIO_IN	0x0000003f
+
+/* CSR12 - BBPCTL (BBP Control port) */
+#define ADM8211_BBPCTL_MMISEL	(1 << 31)
+#define ADM8211_BBPCTL_SPICADD  (0x7F << 24)
+#define ADM8211_BBPCTL_RF3000	(0x20 << 24)
+#define ADM8211_BBPCTL_TXCE	(1 << 23)
+#define ADM8211_BBPCTL_RXCE	(1 << 22)
+#define ADM8211_BBPCTL_CCAP	(1 << 21)
+#define ADM8211_BBPCTL_TYPE	0x001c0000
+#define ADM8211_BBPCTL_WR	(1 << 17)
+#define ADM8211_BBPCTL_RD	(1 << 16)
+#define ADM8211_BBPCTL_ADDR	0x0000ff00
+#define ADM8211_BBPCTL_DATA	0x000000ff
+
+/* CSR12A - SYNCTL (Synthesizer Control port) */
+#define ADM8211_SYNCTL_WR	(1 << 31)
+#define ADM8211_SYNCTL_RD	(1 << 30)
+#define ADM8211_SYNCTL_CS0	(1 << 29)
+#define ADM8211_SYNCTL_CS1	(1 << 28)
+#define ADM8211_SYNCTL_CAL	(1 << 27)
+#define ADM8211_SYNCTL_SELCAL	(1 << 26)
+#define ADM8211_SYNCTL_RFtype	((1 << 24) || (1 << 23) || (1 << 22))
+#define ADM8211_SYNCTL_RFMD	(1 << 22)
+#define ADM8211_SYNCTL_GENERAL	(0x7 << 22)
+/* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */
+
+/* CSR18 - CMDR (Command Register) */
+#define ADM8211_CMDR_PM		(1 << 19)
+#define ADM8211_CMDR_APM	(1 << 18)
+#define ADM8211_CMDR_RTE	(1 << 4)
+#define ADM8211_CMDR_DRT	((1 << 3) | (1 << 2))
+#define ADM8211_CMDR_DRT_8DW	(0x0 << 2)
+#define ADM8211_CMDR_DRT_16DW	(0x1 << 2)
+#define ADM8211_CMDR_DRT_SF	(0x2 << 2)
+
+/* CSR33 - SYNRF (SYNRF direct control) */
+#define ADM8211_SYNRF_SELSYN	(1 << 31)
+#define ADM8211_SYNRF_SELRF	(1 << 30)
+#define ADM8211_SYNRF_LERF	(1 << 29)
+#define ADM8211_SYNRF_LEIF	(1 << 28)
+#define ADM8211_SYNRF_SYNCLK	(1 << 27)
+#define ADM8211_SYNRF_SYNDATA	(1 << 26)
+#define ADM8211_SYNRF_PE1	(1 << 25)
+#define ADM8211_SYNRF_PE2	(1 << 24)
+#define ADM8211_SYNRF_PA_PE	(1 << 23)
+#define ADM8211_SYNRF_TR_SW	(1 << 22)
+#define ADM8211_SYNRF_TR_SWN	(1 << 21)
+#define ADM8211_SYNRF_RADIO	(1 << 20)
+#define ADM8211_SYNRF_CAL_EN	(1 << 19)
+#define ADM8211_SYNRF_PHYRST	(1 << 18)
+
+#define ADM8211_SYNRF_IF_SELECT_0 	(1 << 31)
+#define ADM8211_SYNRF_IF_SELECT_1 	((1 << 31) | (1 << 28))
+#define ADM8211_SYNRF_WRITE_SYNDATA_0	(1 << 31)
+#define ADM8211_SYNRF_WRITE_SYNDATA_1	((1 << 31) | (1 << 26))
+#define ADM8211_SYNRF_WRITE_CLOCK_0	(1 << 31)
+#define ADM8211_SYNRF_WRITE_CLOCK_1	((1 << 31) | (1 << 27))
+
+/* CSR44 - WEPCTL (WEP Control) */
+#define ADM8211_WEPCTL_WEPENABLE   (1 << 31)
+#define ADM8211_WEPCTL_WPAENABLE   (1 << 30)
+#define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29)
+#define ADM8211_WEPCTL_TABLE_WR	(1 << 28)
+#define ADM8211_WEPCTL_TABLE_RD	(1 << 27)
+#define ADM8211_WEPCTL_WEPRXBYP	(1 << 25)
+#define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23)
+#define ADM8211_WEPCTL_ADDR	(0x000001ff)
+
+/* CSR45 - WESK (Data Entry for Share/Individual Key) */
+#define ADM8211_WESK_DATA	(0x0000ffff)
+
+/* FER (Function Event Register) */
+#define ADM8211_FER_INTR_EV_ENT	(1 << 15)
+
+
+/* Si4126 RF Synthesizer - Control Registers */
+#define SI4126_MAIN_CONF	0
+#define SI4126_PHASE_DET_GAIN	1
+#define SI4126_POWERDOWN	2
+#define SI4126_RF1_N_DIV	3 /* only Si4136 */
+#define SI4126_RF2_N_DIV	4
+#define SI4126_IF_N_DIV		5
+#define SI4126_RF1_R_DIV	6 /* only Si4136 */
+#define SI4126_RF2_R_DIV	7
+#define SI4126_IF_R_DIV		8
+
+/* Main Configuration */
+#define SI4126_MAIN_XINDIV2	(1 << 6)
+#define SI4126_MAIN_IFDIV	((1 << 11) | (1 << 10))
+/* Powerdown */
+#define SI4126_POWERDOWN_PDIB	(1 << 1)
+#define SI4126_POWERDOWN_PDRB	(1 << 0)
+
+
+/* RF3000 BBP - Control Port Registers */
+/* 0x00 - reserved */
+#define RF3000_MODEM_CTRL__RX_STATUS 0x01
+#define RF3000_CCA_CTRL 0x02
+#define RF3000_DIVERSITY__RSSI 0x03
+#define RF3000_RX_SIGNAL_FIELD 0x04
+#define RF3000_RX_LEN_MSB 0x05
+#define RF3000_RX_LEN_LSB 0x06
+#define RF3000_RX_SERVICE_FIELD 0x07
+#define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11
+#define RF3000_TX_LEN_MSB 0x12
+#define RF3000_TX_LEN_LSB 0x13
+#define RF3000_LOW_GAIN_CALIB 0x14
+#define RF3000_HIGH_GAIN_CALIB 0x15
+
+/* ADM8211 revisions */
+#define ADM8211_REV_AB 0x11
+#define ADM8211_REV_AF 0x15
+#define ADM8211_REV_BA 0x20
+#define ADM8211_REV_CA 0x30
+
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 16
+
+struct adm8211_ring_info {
+	struct sk_buff *skb;
+	dma_addr_t mapping;
+};
+
+struct adm8211_eeprom {
+	__le16	signature;		/* 0x00 */
+	u8	major_version;		/* 0x02 */
+	u8	minor_version;		/* 0x03 */
+	u8	reserved_1[4];		/* 0x04 */
+	u8	hwaddr[6];		/* 0x08 */
+	u8	reserved_2[8];		/* 0x1E */
+	__le16	cr49;			/* 0x16 */
+	u8	cr03;			/* 0x18 */
+	u8	cr28;			/* 0x19 */
+	u8	cr29;			/* 0x1A */
+	u8	country_code;		/* 0x1B */
+
+/* specific bbp types */
+#define ADM8211_BBP_RFMD3000	0x00
+#define ADM8211_BBP_RFMD3002	0x01
+#define ADM8211_BBP_ADM8011	0x04
+	u8	specific_bbptype;	/* 0x1C */
+	u8	specific_rftype;	/* 0x1D */
+	u8	reserved_3[2];		/* 0x1E */
+	__le16	device_id;		/* 0x20 */
+	__le16	vendor_id;		/* 0x22 */
+	__le16	subsystem_id;		/* 0x24 */
+	__le16	subsystem_vendor_id;	/* 0x26 */
+	u8	maxlat;			/* 0x28 */
+	u8	mingnt;			/* 0x29 */
+	__le16	cis_pointer_low;	/* 0x2A */
+	__le16	cis_pointer_high;	/* 0x2C */
+	__le16	csr18;			/* 0x2E */
+	u8	reserved_4[16];		/* 0x30 */
+	u8	d1_pwrdara;		/* 0x40 */
+	u8	d0_pwrdara;		/* 0x41 */
+	u8	d3_pwrdara;		/* 0x42 */
+	u8	d2_pwrdara;		/* 0x43 */
+	u8	antenna_power[14];	/* 0x44 */
+	__le16	cis_wordcnt;		/* 0x52 */
+	u8	tx_power[14];		/* 0x54 */
+	u8	lpf_cutoff[14];		/* 0x62 */
+	u8	lnags_threshold[14];	/* 0x70 */
+	__le16	checksum;		/* 0x7E */
+	u8	cis_data[0];		/* 0x80, 384 bytes */
+} __attribute__ ((packed));
+
+struct adm8211_priv {
+	struct pci_dev *pdev;
+	spinlock_t lock;
+	struct adm8211_csr __iomem *map;
+	struct adm8211_desc *rx_ring;
+	struct adm8211_desc *tx_ring;
+	dma_addr_t rx_ring_dma;
+	dma_addr_t tx_ring_dma;
+	struct adm8211_ring_info rx_buffers[RX_RING_SIZE];
+	struct adm8211_ring_info tx_buffers[TX_RING_SIZE];
+	unsigned cur_tx, dirty_tx, cur_rx;
+	struct sk_buff_head rx_queue;
+	struct tasklet_struct rx_tasklet;
+#ifdef ADM8211_PROC
+	struct proc_dir_entry *proc;
+#endif
+	int iw_mode; /* operating mode (IW_MODE_*) */
+
+	struct net_device_stats stats;
+	struct iw_statistics wstats;
+	struct ieee80211_data ieee80211;
+
+	int (*eth_header_parse)(struct sk_buff *skb, unsigned char *haddr);
+	struct timer_list timer;
+
+	u8 soft_rx_crc;
+	u8 plcp_signal;
+	u8 retry_limit;
+	u16 rts_thresh;
+	u16 frag_thresh;
+
+	u8 ant_power;
+	u8 tx_power;
+	u8 lpf_cutoff;
+	u8 lnags_threshold;
+	struct adm8211_eeprom *eeprom;
+	size_t eeprom_len;
+
+	u8 revid;
+
+	u32 nar;
+
+#define ADM8211_TYPE_INTERSIL	0x00
+#define ADM8211_TYPE_RFMD	0x01
+#define ADM8211_TYPE_MARVEL	0x02
+#define ADM8211_TYPE_ADMTEK     0x05
+	unsigned int rf_type:3;
+	unsigned int bbp_type:3;
+
+	enum {
+		ADM8211_RFMD2948 = 0x0,
+		ADM8211_RFMD2958 = 0x1,
+		ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2,
+		ADM8211_MAX2820 = 0x8,
+		ADM8211_AL2210L = 0xC,	/* Airoha */
+	} transceiver_type;
+};
+
+static const struct ieee80211_chan_range cranges[] = {
+	{1,  11},	/* FCC */
+	{1,  11},	/* IC */
+	{1,  13},	/* ETSI */
+	{10, 11},	/* SPAIN */
+	{10, 13},	/* FRANCE */
+	{14, 14},	/* MMK */
+	{1,  14},	/* MMK2 */
+};
+
+int adm8211_rf_set_channel(struct net_device *dev, int channel);
+int adm8211_80211_header_parse(struct sk_buff *skb, unsigned char *haddr);
+void adm8211_write_wepkey(struct net_device *dev, int index);
+void adm8211_update_wep(struct net_device *dev);
+void adm8211_update_mode(struct net_device *dev);
+void adm8211_set_beacon(struct net_device *dev);
+
+#endif /* ADM8211_H */
diff -Naur linux-2.6.12-rc3-no1/adm8211/adm8211_hw.c no-sources-new/adm8211/adm8211_hw.c
--- linux-2.6.12-rc3-no1/adm8211/adm8211_hw.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/adm8211_hw.c	2005-03-23 11:12:02.000000000 +0000
@@ -0,0 +1,2636 @@
+
+/*
+ * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP)
+ *
+ * Copyright (c) 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004-2005, Michael Wu <flamingice@sourmilk.net>
+ * Some parts copyright (c) 2003 by David Young <dyoung@pobox.com>
+ * and used with permission.
+ *
+ * Much thanks to Infineon-ADMtek for their support of this driver.
+ *
+ * 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.
+ */
+
+/*
+ * Note!
+ * - this is a pre-release of the driver; it is not yet finished and needs
+ *   more testing.
+ *
+ * FIX:
+ * - properly idle chip in parts that need it (goes w/ above)
+ * - improve signal quality + RSSI stuff
+ * - figure out why ping -s 5913 and up lose packets..
+ *   (can be reduced with a mtu of 920..)
+ * - check FCS for rev 0x20 and up?
+ */
+
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <asm/delay.h>
+#include <asm/unaligned.h>
+#include <asm/types.h>
+
+#include "adm8211.h"
+#include "adm8211_ioctl.h"
+#include "ieee80211.h"
+#include "avs_caphdr.h"
+
+#include "rdate.h"
+
+MODULE_AUTHOR("Jouni Malinen <jkmaline@cc.hut.fi>, Michael Wu <flamingice@sourmilk.net>");
+MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless LAN cards based on ADMtek"
+		   " ADM8211");
+MODULE_SUPPORTED_DEVICE("ADM8211");
+MODULE_LICENSE("GPL");
+
+
+static const char *version = KERN_INFO "adm8211: Copyright 2003, "
+"Jouni Malinen <jkmaline@cc.hut.fi>; "
+"Copyright 2004-2005, Michael Wu <flamingice@sourmilk.net>\n";
+
+
+static struct pci_device_id adm8211_pci_id_table[] __devinitdata = {
+	/* ADMtek ADM8211 */
+	{ 0x10B7, 0x6000, PCI_ANY_ID, PCI_ANY_ID }, /* 3Com 3CRSHPW796 */
+	{ 0x1200, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ? */
+	{ 0x1317, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211A */
+	{ 0x1317, 0x8211, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211B/C */
+	{ 0 }
+};
+
+
+#define ADM8211_INTMASK \
+(ADM8211_IER_NIE | ADM8211_IER_AIE | ADM8211_IER_RCIE | ADM8211_IER_TCIE | \
+ADM8211_IER_TDUIE | ADM8211_IER_GPTIE)
+
+
+struct adm8211_desc {
+	__le32 status;
+	__le32 length;
+	__le32 buffer1;
+	__le32 buffer2;
+};
+
+#define RDES0_STATUS_OWN	(1 << 31)
+#define RDES0_STATUS_ES		(1 << 30)
+#define RDES0_STATUS_SQL	(1 << 29)
+#define RDES0_STATUS_DE		(1 << 28)
+#define RDES0_STATUS_FS		(1 << 27)
+#define RDES0_STATUS_LS		(1 << 26)
+#define RDES0_STATUS_PCF	(1 << 25)
+#define RDES0_STATUS_SFDE	(1 << 24)
+#define RDES0_STATUS_SIGE	(1 << 23)
+#define RDES0_STATUS_CRC16E	(1 << 22)
+#define RDES0_STATUS_RXTOE	(1 << 21)
+#define RDES0_STATUS_CRC32E	(1 << 20)
+#define RDES0_STATUS_ICVE	(1 << 19)
+#define RDES0_STATUS_DA1	(1 << 17)
+#define RDES0_STATUS_DA0	(1 << 16)
+#define RDES0_STATUS_RXDR	((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12))
+#define RDES0_STATUS_FL		(0x00000fff)
+
+#define RDES1_CONTROL_RER	(1 << 25)
+#define RDES1_CONTROL_RCH	(1 << 24)
+#define RDES1_CONTROL_RBS2	(0x00fff000)
+#define RDES1_CONTROL_RBS1	(0x00000fff)
+
+#define RDES1_STATUS_RSSI	(0x0000007f)
+
+
+#define TDES0_CONTROL_OWN	(1 << 31)
+#define TDES0_CONTROL_DONE	(1 << 30)
+#define TDES0_CONTROL_TXDR	(0x0ff00000)
+
+#define TDES0_STATUS_OWN	(1 << 31)
+#define TDES0_STATUS_DONE	(1 << 30)
+#define TDES0_STATUS_ES		(1 << 29)
+#define TDES0_STATUS_TLT	(1 << 28)
+#define TDES0_STATUS_TRT	(1 << 27)
+#define TDES0_STATUS_TUF	(1 << 26)
+#define TDES0_STATUS_TRO	(1 << 25)
+#define TDES0_STATUS_SOFBR	(1 << 24)
+#define TDES0_STATUS_ACR	(0x00000fff)
+
+#define TDES1_CONTROL_IC	(1 << 31)
+#define TDES1_CONTROL_LS	(1 << 30)
+#define TDES1_CONTROL_FS	(1 << 29)
+#define TDES1_CONTROL_TER	(1 << 25)
+#define TDES1_CONTROL_TCH	(1 << 24)
+#define TDES1_CONTROL_RBS2	(0x00fff000)
+#define TDES1_CONTROL_RBS1	(0x00000fff)
+
+
+#define PLCP_SIGNAL_1M		0x0a
+#define PLCP_SIGNAL_2M		0x14
+#define PLCP_SIGNAL_5M5		0x37
+#define PLCP_SIGNAL_11M		0x6e
+
+
+/* RX status - stored in skb->cb so this structure must be 48 bytes or less */
+struct adm8211_rx_status {
+	u8 rssi;
+	u8 rate;
+};
+
+
+struct adm8211_tx_hdr {
+	u8 da[6];
+	u8 signal; /* PLCP signal / TX rate in 100 Kbps */
+	u8 service;
+	__le16 frame_body_size;
+	__le16 frame_control;
+	__le16 plcp_frag_tail_len;
+	__le16 plcp_frag_head_len;
+	__le16 dur_frag_tail;
+	__le16 dur_frag_head;
+	u8 address4[6];
+
+#define ADM8211_TXHDRCTL_SHORT_PREAMBLE		(1 <<  0)
+#define ADM8211_TXHDRCTL_MORE_FRAG		(1 <<  1)
+#define ADM8211_TXHDRCTL_MORE_DATA		(1 <<  2)
+#define ADM8211_TXHDRCTL_FRAG_NO		(1 <<  3) /* ? */
+#define ADM8211_TXHDRCTL_ENABLE_RTS		(1 <<  4)
+#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE	(1 <<  5)
+#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER	(1 << 15) /* ? */
+	__le16 header_control;
+#ifdef BIG_ENDIAN
+	u32 retry_limit:8;
+	u32 reserved_0:8;
+	u32 frag_number:4;
+	u32 frag_threshold:12;
+#else
+	u32 frag_threshold:12;
+	u32 frag_number:4;
+	u32 reserved_0:8;
+	u32 retry_limit:8;
+#endif
+
+	u32 wep2key0;
+	u32 wep2key1;
+	u32 wep2key2;
+	u32 wep2key3;
+
+	u8 keyid;
+	u8 reserved_1[3];
+	u32 reserved_2;
+} __attribute__ ((packed));
+
+
+
+#define RX_COPY_BREAK 128
+
+#define RX_PKT_SIZE 2400
+
+/* SRAM offsets */
+#define ADM8211_SRAM(x) (priv->revid < ADM8211_REV_BA ? \
+        ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x)
+
+#define ADM8211_SRAM_INDIV_KEY   0x0000
+#define ADM8211_SRAM_A_SHARE_KEY 0x0160
+#define ADM8211_SRAM_B_SHARE_KEY 0x00c0
+
+#define ADM8211_SRAM_A_SSID      0x0180
+#define ADM8211_SRAM_B_SSID      0x00d4
+#define ADM8211_SRAM_SSID ADM8211_SRAM(SSID)
+
+#define ADM8211_SRAM_A_SUPP_RATE 0x0191
+#define ADM8211_SRAM_B_SUPP_RATE 0x00dd
+#define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE)
+
+#define ADM8211_SRAM_A_SIZE      0x0200
+#define ADM8211_SRAM_B_SIZE      0x01c0
+#define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE)
+
+/* Serial EEPROM reading for 93C66/93C46 */
+#define EE_ENB		(0x4000 | ADM8211_SPR_SRS | ADM8211_SPR_SCS)
+#define EE_READ_CMD	(6)
+#define eeprom_delay()	ADM8211_CSR_READ(SPR);
+
+
+static u16 adm8211_eeprom_read_word(struct net_device *dev, int addr,
+				    int addr_len)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u16 retval = 0;
+	int read_cmd = addr | (EE_READ_CMD << addr_len);
+
+	ADM8211_CSR_WRITE(SPR, EE_ENB & ~ADM8211_SPR_SCS);
+	eeprom_delay();
+	ADM8211_CSR_WRITE(SPR, EE_ENB);
+	eeprom_delay();
+
+	/* Shift the read command bits out. */
+	for (i = 4 + addr_len; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? ADM8211_SPR_SDI : 0;
+		ADM8211_CSR_WRITE(SPR, EE_ENB | dataval);
+		eeprom_delay();
+		ADM8211_CSR_WRITE(SPR, EE_ENB | dataval | ADM8211_SPR_SCLK);
+		eeprom_delay();
+	}
+
+	ADM8211_CSR_WRITE(SPR, EE_ENB);
+	eeprom_delay();
+
+	for (i = 16; i > 0; i--) {
+		ADM8211_CSR_WRITE(SPR, EE_ENB | ADM8211_SPR_SCLK);
+		eeprom_delay();
+		retval <<= 1;
+		retval |= ((ADM8211_CSR_READ(SPR) & ADM8211_SPR_SDO) ? 1 : 0);
+		ADM8211_CSR_WRITE(SPR, EE_ENB);
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	ADM8211_CSR_WRITE(SPR, EE_ENB & ~ADM8211_SPR_SCS);
+
+	return retval;
+}
+
+
+static int adm8211_read_eeprom(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	int addr_len, words, i;
+
+	reg = ADM8211_CSR_READ(CSR_TEST0);
+
+	if (reg & ADM8211_CSR_TEST0_EPTYP) {
+		printk(KERN_DEBUG "%s (adm8211): EEPROM type: 93C66\n", pci_name(priv->pdev));
+		/* 256 * 16-bit = 512 bytes */
+		addr_len = 8;
+		words = 256;
+	} else {
+		printk(KERN_DEBUG "%s (adm8211): EEPROM type 93C46\n", pci_name(priv->pdev));
+		/* 64 * 16-bit = 128 bytes */
+		addr_len = 6;
+		words = 64;
+	}
+
+	priv->eeprom_len = words * 2;
+	priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL);
+	if (priv->eeprom == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < words; i++)
+		*((u16 *) &((u8 *)priv->eeprom)[i * 2]) =
+			adm8211_eeprom_read_word(dev, i, addr_len);
+
+	priv->rf_type = (le16_to_cpu(priv->eeprom->cr49) & (0x7 << 3)) >> 3;
+	priv->bbp_type = le16_to_cpu(priv->eeprom->cr49) &  0x7;
+
+	/* read country code from eeprom and make sure it's valid */
+	if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) {
+		printk(KERN_WARNING "%s (adm8211): Invalid country code (%d) in EEPROM, assuming ETSI\n",
+		       pci_name(priv->pdev), priv->eeprom->country_code);
+
+		priv->ieee80211.chan_range = cranges[2];
+	} else
+		priv->ieee80211.chan_range = cranges[priv->eeprom->country_code];
+
+	printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n",
+	       pci_name(priv->pdev), (int)priv->ieee80211.chan_range.min, (int)priv->ieee80211.chan_range.max);
+
+	switch (priv->eeprom->specific_rftype) {
+	case ADM8211_RFMD2948:
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+	case ADM8211_MAX2820:
+	case ADM8211_AL2210L:
+		priv->transceiver_type = priv->eeprom->specific_rftype;
+		break;
+
+	default:
+		if (priv->revid == ADM8211_REV_BA)
+			priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER;
+		else if (priv->revid == ADM8211_REV_CA)
+			priv->transceiver_type = ADM8211_AL2210L;
+		else if (priv->revid == ADM8211_REV_AB)
+			priv->transceiver_type = ADM8211_RFMD2948;
+
+		printk(KERN_WARNING "%s (adm8211): Invalid or unsupported transceiver: %d, assuming %d\n",
+		       pci_name(priv->pdev), priv->eeprom->specific_rftype, priv->transceiver_type);
+
+		break;
+	}
+
+	printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d Transceiver=%d\n",
+	       pci_name(priv->pdev), priv->rf_type, priv->bbp_type,
+	       priv->eeprom->specific_bbptype, priv->eeprom->specific_rftype);
+
+	return 0;
+}
+
+static inline void adm8211_write_sram(struct net_device *dev, u32 addr, __le32 data)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(addr | ADM8211_WEPCTL_TABLE_WR |
+			  (priv->revid < ADM8211_REV_BA ?
+			   0 : ADM8211_WEPCTL_SEL_WEPTABLE )) );
+	ADM8211_CSR_READ(WEPCTL);
+	mdelay(1);
+
+	ADM8211_CSR_WRITE(WESK, data);
+	ADM8211_CSR_READ(WESK);
+	mdelay(1);
+}
+
+static void adm8211_write_sram_bytes(struct net_device *dev, int addr, u8 *buf, int len)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg = ADM8211_CSR_READ(WEPCTL);
+	int i;
+
+	if (priv->revid < ADM8211_REV_BA) {
+		for (i = 0; i < len; i += 2) {
+			u16 val = buf[i] | buf[i + 1] << 8;
+			adm8211_write_sram(dev, addr + i / 2, cpu_to_le32(val));
+		}
+	} else {
+		for (i = 0; i < len; i += 4) {
+			u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) |
+				  (buf[i + 2] << 16) | (buf[i + 3] << 24);
+			adm8211_write_sram(dev, addr + i / 4, cpu_to_le32(val));
+		}
+	}
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static void adm8211_clear_sram(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg = ADM8211_CSR_READ(WEPCTL);
+	int addr;
+
+	for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++)
+			adm8211_write_sram(dev, addr, 0);
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static void adm8211_link_change(struct net_device *dev, int link)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+
+	/* Set aid for beacon TIM element decoding */
+	reg = ADM8211_CSR_READ(FRCTL);
+	reg &= ~ADM8211_FRCTL_AID;
+	if (link) {
+		reg |= ADM8211_FRCTL_AID_ON;
+		reg |= priv->ieee80211.aid;
+	}
+	ADM8211_CSR_WRITE(FRCTL, reg);
+}
+
+static struct net_device_stats *adm8211_get_stats(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	return &priv->stats;
+}
+
+static struct iw_statistics *adm8211_get_wireless_stats(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	return &priv->wstats;
+}
+
+static void adm8211_set_rx_mode(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i, bit_nr;
+	__le32 mc_filter[2];
+	struct dev_mc_list *mclist;
+	unsigned long flags;
+
+	if (dev->flags & IFF_PROMISC) {
+		priv->nar |= ADM8211_NAR_PR;
+		priv->nar &= ~ADM8211_NAR_MM;
+		mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
+	} else if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 32)) {
+		priv->nar &= ~ADM8211_NAR_PR;
+		priv->nar |= ADM8211_NAR_MM;
+		mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
+	} else {
+		priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR);
+		mc_filter[1] = mc_filter[0] = 0;
+		mclist = dev->mc_list;
+		for (i = 0; i < dev->mc_count; i+=1) {
+			if (!mclist)
+				break;
+			bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+			bit_nr &= 0x3F;
+                        mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31));
+			mclist = mclist->next;
+		}
+	}
+
+	/* TODO: is this locking necessary? I don't think so.. */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* make sure receive is stopped */
+	if (priv->nar & ADM8211_NAR_SR) {
+	ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR);
+	ADM8211_CSR_READ(NAR);
+	mdelay(20);
+	}
+
+	ADM8211_CSR_WRITE(MAR0, mc_filter[0]);
+	ADM8211_CSR_WRITE(MAR1, mc_filter[1]);
+	ADM8211_CSR_READ(NAR);
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int adm8211_set_mac_address(struct net_device *dev, void *p)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+        struct sockaddr *addr = p;
+	u32 reg;
+
+        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	if (!(dev->flags & IFF_UP))
+		return 0;
+
+	/* sure, we can change the MAC addr while running */
+	reg = ADM8211_CSR_READ(NAR);
+
+	/* make sure receive is stopped */
+	if (priv->nar & ADM8211_NAR_SR) {
+	ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR);
+	mdelay(20);
+	}
+
+	ADM8211_CSR_WRITE(PAR0, *(u32 *)dev->dev_addr);
+	ADM8211_CSR_WRITE(PAR1, *(u16 *)(dev->dev_addr + 4));
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	return 0;
+}
+
+static void adm8211_rx_skb(struct net_device *dev, struct sk_buff *skb)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct ieee80211_hdr *hdr;
+	struct adm8211_rx_status *stat;
+	struct iw_statistics *wstats = &priv->wstats;
+	struct ieee80211_rx_status rx_status;
+	static const u8 rate[] = {10, 20, 55, 110, 220};
+	u16 fc;
+
+	if (skb->len < 14)
+		goto drop;
+
+	/* TODO: Add crc checking here for cards/modes that need it */
+
+	stat = (struct adm8211_rx_status *) skb->cb;
+	memset(&rx_status, 0, sizeof(rx_status));
+	if (priv->revid < ADM8211_REV_CA)
+		rx_status.rssi = stat->rssi;
+	else
+		rx_status.rssi = 100 - stat->rssi;
+
+	if (stat->rate <= 4)
+		rx_status.rate = rate[stat->rate];
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	if (priv->iw_mode == IW_MODE_MONITOR) {
+		struct avs_caphdr *chdr;
+		if (skb_headroom(skb) < sizeof(struct avs_caphdr)) {
+			if (pskb_expand_head(skb, sizeof(struct avs_caphdr), 0,
+					     GFP_ATOMIC)) {
+				printk(KERN_DEBUG "%s: failed to allocate room for prism2 "
+				       "header\n", dev->name);
+				goto drop;
+			}
+		}
+		memset(skb->cb, 0, sizeof(skb->cb));
+
+		chdr = (struct avs_caphdr *) skb_push(skb, sizeof(struct avs_caphdr));
+		chdr->version = cpu_to_be32(0x80211001);
+		chdr->length = cpu_to_be32(sizeof(struct avs_caphdr));
+		chdr->mactime = 0;
+		chdr->hosttime = cpu_to_be64(jiffies);
+		chdr->phytype = cpu_to_be32(4); /* phytype_dsss_dot11_b */
+		chdr->channel = cpu_to_be32(data->channel);
+		chdr->datarate = cpu_to_be32(rx_status.rate);
+		chdr->antenna = 0; /* TODO: once antenna setting is possible.. */
+		chdr->priority = 0; /* hmm, dunno if this is possible.. */
+		chdr->ssi_type = cpu_to_be32(3); /* Raw RSSI */
+		chdr->ssi_signal = cpu_to_be32(rx_status.rssi);
+		chdr->ssi_noise = cpu_to_be32(0xFFFFFFFF);
+		if (skb->len >= 14 + sizeof(struct avs_caphdr))
+			chdr->preamble = cpu_to_be32(
+					 le16_to_cpu(hdr->frame_control) & WLAN_CAPABILITY_SHORT_PREAMBLE
+					 ? 2 : 1);
+		else
+			chdr->preamble = 0;
+		chdr->encoding = cpu_to_be32(1); /* CCK */
+
+		priv->stats.rx_bytes += skb->len;
+		priv->stats.rx_packets += 1;
+
+		skb->pkt_type = PACKET_OTHERHOST;
+		skb->mac.raw = skb->data;
+
+		netif_rx(skb);
+		dev->last_rx = jiffies;
+		return;
+	}
+
+	if (ieee80211_filter_duplicates(&priv->ieee80211, skb, fc))
+		goto drop;
+
+	/* remove FCS */
+	if (dev->flags & IFF_PROMISC)
+		skb_trim(skb, skb->len - FCS_LEN);
+
+	/* Promiscuous mode disables hardware WEP RX decryption */
+	if (priv->revid < ADM8211_REV_BA || dev->flags & IFF_PROMISC) {
+		skb = ieee80211_wep_decode(&priv->ieee80211, skb);
+		if (!skb) {
+			priv->wstats.discard.code += 1;
+			priv->stats.rx_errors += 1;
+			return;
+		}
+	}
+
+	skb = ieee80211_reassemble_frag(&priv->ieee80211, skb);
+	if (!skb)
+		return;
+
+	/* FIX: this is a hack */
+	wstats->qual.qual = stat->rssi;
+	wstats->qual.updated = 1;
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		ieee80211_rx_mgmt(&priv->ieee80211, skb, &rx_status);
+		break;
+	case WLAN_FC_TYPE_DATA:
+		skb = ieee80211_data_decaps(&priv->ieee80211, skb);
+		if (skb) {
+			skb->protocol = eth_type_trans(skb, dev);
+			memset(skb->cb, 0, sizeof(skb->cb));
+			priv->stats.rx_bytes+=skb->len;
+			priv->stats.rx_packets+=1;
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+		}
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		goto drop;
+	}
+
+	return;
+
+ drop:
+	dev_kfree_skb(skb);
+}
+
+
+static void adm8211_rx_tasklet(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *) data;
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&priv->rx_queue)) != NULL)
+		adm8211_rx_skb(dev, skb);
+}
+
+
+static void adm8211_interrupt_tci(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	unsigned dirty_tx;
+
+#if 0
+	printk(KERN_DEBUG "TCI (dirty_tx=%d cur_tx=%d)\n",
+	       priv->dirty_tx, priv->cur_tx);
+#endif
+
+	spin_lock(&priv->lock);
+
+	for (dirty_tx = priv->dirty_tx;
+	     priv->cur_tx - dirty_tx > 0; dirty_tx++) {
+		unsigned entry = dirty_tx % TX_RING_SIZE;
+		u32 status = le32_to_cpu(priv->tx_ring[entry].status);
+		if (status & TDES0_CONTROL_OWN ||
+		    !(status & TDES0_CONTROL_DONE))
+			break;
+
+		if (status & TDES0_STATUS_ES) {
+			priv->stats.tx_errors += 1;
+
+			if (status & (TDES0_STATUS_TUF | TDES0_STATUS_TRO))
+				priv->stats.tx_fifo_errors += 1;
+			if (status & (TDES0_STATUS_TLT | TDES0_STATUS_SOFBR))
+				priv->wstats.discard.misc += 1;
+			if (status & TDES0_STATUS_TRT)
+				priv->wstats.discard.retries += 1;
+		} else {
+			priv->stats.tx_bytes += priv->tx_buffers[entry].skb->len;
+			priv->stats.tx_packets += 1;
+		}
+
+		pci_unmap_single(priv->pdev, priv->tx_buffers[entry].mapping,
+				 priv->tx_buffers[entry].skb->len,
+				 PCI_DMA_TODEVICE);
+		dev_kfree_skb_irq(priv->tx_buffers[entry].skb);
+		priv->tx_buffers[entry].skb = NULL;
+	}
+
+	if (priv->cur_tx - dirty_tx < TX_RING_SIZE - 2)
+		netif_wake_queue(dev);
+
+	priv->dirty_tx = dirty_tx;
+	spin_unlock(&priv->lock);
+}
+
+
+static void adm8211_interrupt_rci(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int entry = priv->cur_rx % RX_RING_SIZE;
+	u32 status;
+	unsigned pktlen;
+	struct sk_buff *skb, *newskb;
+	int limit = RX_RING_SIZE;
+	u8 rssi, rate;
+
+#if 0
+	printk(KERN_DEBUG "RCI\n");
+#endif
+
+	while (!(priv->rx_ring[entry].status &
+		 __constant_cpu_to_le32(RDES0_STATUS_OWN))) {
+		if (--limit < 0)
+			break;
+
+		status = le32_to_cpu(priv->rx_ring[entry].status);
+		rate = (status & RDES0_STATUS_RXDR) >> 12;
+		rssi = le32_to_cpu(priv->rx_ring[entry].length) &
+			RDES1_STATUS_RSSI;
+
+#if 0
+		printk(KERN_DEBUG "%s: RX %02x RXDR=%d FL=%d RSSI=%d "
+		       "%s%s%s%s\n",
+		       dev->name, status, rate,
+		       status & RDES0_STATUS_FL,
+		       rssi,
+		       status & RDES0_STATUS_ES ? "[ES]" : "",
+		       status & RDES0_STATUS_FS ? "[FS]" : "",
+		       status & RDES0_STATUS_LS ? "[LS]" : "",
+		       status & RDES0_STATUS_CRC16E ? "[CRC16E]" : "");
+#endif
+
+		pktlen = status & RDES0_STATUS_FL;
+		if (pktlen > RX_PKT_SIZE) {
+			printk(KERN_DEBUG "%s: too long frame (pktlen=%d)\n",
+			       dev->name, pktlen);
+			pktlen = RX_PKT_SIZE;
+		}
+
+		if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) {
+#if 0
+			printk(KERN_DEBUG "%s: dropped RX frame with error "
+			       "(status=0x%x)\n", dev->name, status);
+#endif
+			skb = NULL; /* old buffer will be reused */
+			priv->stats.rx_errors += 1;
+			if (status & (RDES0_STATUS_SFDE |
+				      RDES0_STATUS_SIGE | RDES0_STATUS_RXTOE))
+				priv->wstats.discard.misc += 1;
+			if (status & RDES0_STATUS_ICVE)
+				priv->wstats.discard.code += 1;
+			if (status & (RDES0_STATUS_CRC16E | RDES0_STATUS_CRC32E))
+				priv->stats.rx_crc_errors += 1;
+
+		} else if (pktlen < RX_COPY_BREAK) {
+			skb = dev_alloc_skb(pktlen);
+			if (skb) {
+				skb->dev = dev;
+				pci_dma_sync_single_for_cpu(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					pktlen, PCI_DMA_FROMDEVICE);
+				memcpy(skb_put(skb, pktlen),
+				       priv->rx_buffers[entry].skb->tail,
+				       pktlen);
+				pci_dma_sync_single_for_device(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+			}
+		} else {
+			newskb = dev_alloc_skb(RX_PKT_SIZE);
+			if (newskb) {
+				newskb->dev = dev;
+				skb = priv->rx_buffers[entry].skb;
+				skb_put(skb, pktlen);
+				pci_unmap_single(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+				priv->rx_buffers[entry].skb = newskb;
+				priv->rx_buffers[entry].mapping =
+					pci_map_single(priv->pdev,
+						       newskb->tail,
+						       RX_PKT_SIZE,
+						       PCI_DMA_FROMDEVICE);
+			} else {
+				skb = NULL;
+				priv->stats.rx_dropped += 1;
+			}
+
+			priv->rx_ring[entry].buffer1 =
+				cpu_to_le32(priv->rx_buffers[entry].mapping);
+		}
+
+		priv->rx_ring[entry].status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL );
+		priv->rx_ring[entry].length =
+			cpu_to_le32(RX_PKT_SIZE |
+				    (entry == RX_RING_SIZE - 1 ?
+				     RDES1_CONTROL_RER : 0));
+
+		if (skb) {
+			struct adm8211_rx_status *stat =
+				(struct adm8211_rx_status *) skb->cb;
+#if 0
+			{
+				int i;
+				printk(KERN_DEBUG "RX[%d/%d]",
+				       pktlen, skb->len);
+				for (i = 0; i < skb->len; i++)
+					printk(" %02x", skb->data[i]);
+				printk("\n");
+			}
+#endif
+			stat->rssi = rssi;
+			stat->rate = rate;
+			skb->mac.raw = skb->data;
+			skb->protocol = (__force unsigned short) __constant_htons(ETH_P_802_2);
+			skb_queue_tail(&priv->rx_queue, skb);
+			tasklet_schedule(&priv->rx_tasklet);
+		}
+
+		entry = (++priv->cur_rx) % RX_RING_SIZE;
+	}
+
+	priv->stats.rx_missed_errors += ADM8211_CSR_READ(LPC) & 0xFFFF;
+}
+
+
+static irqreturn_t adm8211_interrupt(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+#define ADM8211_INT(x) if (stsr & ADM8211_STSR_ ## x) printk(KERN_DEBUG "%s: " #x "\n", dev->name)
+
+	struct net_device *dev = dev_id;
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 stsr;
+	int count = 0;
+
+	do {
+		stsr = ADM8211_CSR_READ(STSR);
+		ADM8211_CSR_WRITE(STSR, stsr);
+		if (stsr == 0xffffffff)
+			return IRQ_HANDLED;
+
+		if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS)))
+			break;
+
+		/*if (stsr & ADM8211_STSR_TBTT) {
+			priv->ieee80211.beacon_sync(dev);
+		}*/
+
+		if (stsr & ADM8211_STSR_RCI)
+			adm8211_interrupt_rci(dev);
+		if (stsr & ADM8211_STSR_TCI)
+			adm8211_interrupt_tci(dev);
+		if (stsr & (ADM8211_STSR_RCI | ADM8211_STSR_TCI))
+			mod_timer(&priv->timer, jiffies + HZ);
+
+		if ((stsr & (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff))
+			 != (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff)) {
+			if (stsr & ADM8211_STSR_LinkOn) {
+				printk(KERN_DEBUG "%s: LinkOn\n", dev->name);
+				priv->ieee80211.flags |= LINK_ON;
+			}
+		
+			if (stsr & ADM8211_STSR_LinkOff) {
+				printk(KERN_DEBUG "%s: LinkOff\n", dev->name);
+				priv->ieee80211.flags &= ~LINK_ON;
+
+				if (dev->flags & IFF_UP)
+					ieee80211_start(&priv->ieee80211);
+			}
+		}
+		
+		ADM8211_INT(PCF);
+		/*ADM8211_INT(BCNTC);*/
+		ADM8211_INT(GPINT);
+		ADM8211_INT(ATIMTC);
+		/*ADM8211_INT(TSFTF);*/
+		ADM8211_INT(TSCZ);
+		ADM8211_INT(SQL);
+		ADM8211_INT(WEPTD);
+		ADM8211_INT(ATIME);
+		/*ADM8211_INT(TBTT);*/
+		ADM8211_INT(TEIS);
+		ADM8211_INT(FBE);
+		ADM8211_INT(REIS);
+		ADM8211_INT(GPTT);
+		ADM8211_INT(RPS);
+		ADM8211_INT(RDU);
+		ADM8211_INT(TUF);
+		/*ADM8211_INT(TRT);*/
+		/*ADM8211_INT(TLT);*/
+		/*ADM8211_INT(TDU);*/
+		ADM8211_INT(TPS);
+
+	} while (count++ < 20);
+
+	return IRQ_RETVAL(count);
+
+#undef ADM8211_INT
+}
+
+static int adm8211_rf_write_syn_max2820 (struct net_device *dev, u8 addr, u16 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg;
+
+	value &= 0xFFF;
+	addr  &= 0xF;
+	value = value | (addr << 12);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 15; i+=1) {
+		if ( value & (1 << (15 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_rf_write_syn_al2210l (struct net_device *dev, u8 addr, u32 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg;
+
+	value &= 0xFFFFF;
+	addr  &= 0xF;
+	value = (value << 4) | addr;
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 23; i+=1) {
+		if ( value & (1 << (23 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_rf_write_syn_rfmd2958 (struct net_device *dev, u16 addr, u32 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg;
+
+	value &= 0x3FFFF;
+	addr  &= 0x1F;
+	value = value | (addr << 18);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 23; i+=1) {
+		if ( value & (1 << (23 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_rf_write_syn_rfmd2948 (struct net_device *dev, u8 addr, u16 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg, bits;
+
+	value &= 0xFFFF;
+	addr  &= 0xF;
+	bits = (value << 4) | addr;
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 21; i+=1) {
+		if ( bits & (1 << (21 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_write_bbp(struct net_device *dev, u8 addr, u8 data)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int timeout;
+	u32 reg;
+
+	timeout = 10;
+	while (timeout > 0) {
+		reg = ADM8211_CSR_READ(BBPCTL);
+		if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD)))
+			break;
+		timeout--;
+		mdelay(2);
+	}
+
+	if (timeout == 0) {
+		printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed prewrite "
+		       "(reg=0x%08x)\n",
+		       dev->name, addr, data, reg);
+		return -ETIMEDOUT;
+	}
+
+	switch (priv->bbp_type) {
+	case ADM8211_TYPE_INTERSIL:
+		reg = ADM8211_BBPCTL_MMISEL;	/* three wire interface */
+		break;
+	case ADM8211_TYPE_RFMD:
+		reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+		      (0x01<<18);
+		break;
+	case ADM8211_TYPE_ADMTEK:
+		reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+		      (0x05<<18);
+		break;
+	}
+	reg |= ADM8211_BBPCTL_WR | (addr << 8) | data;
+
+	ADM8211_CSR_WRITE(BBPCTL, reg);
+
+	timeout=10;
+	while (timeout > 0) {
+		reg = ADM8211_CSR_READ(BBPCTL);
+		if (!(reg & ADM8211_BBPCTL_WR))
+			break;
+		timeout--;
+		mdelay(2);
+	}
+
+	if (timeout == 0) {
+		ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) &
+				  (~ADM8211_BBPCTL_WR));
+		printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed postwrite "
+		       "(reg=0x%08x)\n",
+		       dev->name, addr, data, reg);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int adm8211_rf_set_channel(struct net_device *dev, int channel)
+{
+	static const u32 adm8211_rfmd2958_reg5[] =
+		{0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340,
+		 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7};
+	static const u32 adm8211_rfmd2958_reg6[] =
+		{0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000,
+		 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745};
+
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u8 ant_power = priv->ant_power > 0x3F ?
+		priv->eeprom->antenna_power[channel-1] : priv->ant_power;
+	u8 tx_power = priv->tx_power > 0x3F ?
+		priv->eeprom->tx_power[channel-1] : priv->tx_power;
+	u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ?
+		priv->eeprom->lpf_cutoff[channel-1] : priv->lpf_cutoff;
+	u8 lnags_thresh = priv->lnags_threshold == 0xFF ?
+		priv->eeprom->lnags_threshold[channel-1] : priv->lnags_threshold;
+	u32 reg;
+
+	if (channel < 1 || channel > 14)
+		return -EINVAL;
+
+	/* Stop transmit */
+	ADM8211_CSR_WRITE(NAR, priv->nar & ~(ADM8211_NAR_ST | ADM8211_NAR_SR));
+	ADM8211_CSR_READ(NAR);
+	mdelay(20);
+
+	/* Program synthesizer to new channel */
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033);
+
+		adm8211_rf_write_syn_rfmd2958(dev, 0x05,
+					adm8211_rfmd2958_reg5[channel-1]);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x06,
+					adm8211_rfmd2958_reg6[channel-1]);
+		break;
+
+	case ADM8211_RFMD2948:
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, SI4126_MAIN_XINDIV2);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN,
+				     SI4126_POWERDOWN_PDIB | SI4126_POWERDOWN_PDRB);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV,
+				     (channel == 14 ? 2110 : 2033 + (channel * 5)));
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_rf_write_syn_max2820(dev, 0x3,
+			(channel == 14 ? 0x054 : 0x7 + (channel * 5)));
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_rf_write_syn_al2210l(dev, 0x0,
+			(channel == 14 ? 0x229B4 : 0x22967 + (channel * 5)));
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
+		       dev->name, priv->transceiver_type);
+		break;
+	}
+
+	/* write BBP regs */
+	if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+
+	/* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */
+	/* TODO: remove if SMC 2635W doesn't need this */
+	if (priv->transceiver_type == ADM8211_RFMD2948) {
+		reg = ADM8211_CSR_READ(GPIO);
+		reg &= 0xfffc0000;
+		reg |= ADM8211_CSR_GPIO_EN0;
+		if (channel != 14)
+			reg |= ADM8211_CSR_GPIO_O0;
+		ADM8211_CSR_WRITE(GPIO, reg);
+	}
+
+	if (priv->transceiver_type == ADM8211_RFMD2958) {
+		/* set PCNT2 */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100);
+		/* set PCNT1 P_DESIRED/MID_BIAS */
+		reg = le16_to_cpu(priv->eeprom->cr49);
+		reg >>= 13;
+		reg <<= 15;
+		reg |= ant_power<<9;
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg);
+		/* set TXRX TX_GAIN */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 |
+			(priv->revid < ADM8211_REV_CA ? tx_power : 0));
+	} else {
+		reg = ADM8211_CSR_READ(PLCPHD);
+		reg &= 0xff00ffff;
+		reg |= tx_power<<18;
+		ADM8211_CSR_WRITE(PLCPHD, reg);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | ADM8211_SYNRF_PE1 |
+			  ADM8211_SYNRF_PHYRST);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(30);
+
+	/* RF3000 BBP */
+	if (priv->transceiver_type != ADM8211_RFMD2958)
+		adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT,
+				  tx_power<<2);
+	adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff);
+	adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh);
+	adm8211_write_bbp(dev, 0x1c, priv->revid == ADM8211_REV_BA
+			  ? priv->eeprom->cr28 : 0);
+	adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+
+	} else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) {	/* Nothing to do for ADMtek BBP */
+		printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
+		       dev->name, priv->bbp_type);
+	}
+
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	/* update current channel for adhoc (and maybe AP mode) */
+	reg = ADM8211_CSR_READ(CAP0);
+	reg &= ~0xF;
+	reg |= channel;
+	ADM8211_CSR_WRITE(CAP0, reg);
+
+	return 0;
+}
+
+void adm8211_write_wepkey(struct net_device *dev, int index)
+{
+#define ADM8211_WEP_ENABLE	(1 << 7)
+#define ADM8211_WEP_A_104	(1 << 6)
+#define ADM8211_WEP_B_104	(1 << 4)
+
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int addr;
+	u8 buf[32];
+	memset(buf, 0, sizeof(buf));
+
+	if (priv->revid < ADM8211_REV_BA) {
+		addr = (index*7) + ADM8211_SRAM_A_SHARE_KEY;
+
+		/* control entry */
+		if (data->wep_data[index].len > 5)
+			buf[1] = ADM8211_WEP_ENABLE | ADM8211_WEP_A_104;
+		else if (data->wep_data[index].len > 0)
+			buf[1] = ADM8211_WEP_ENABLE;
+
+		buf[0]=data->wep_data[index].key[0];
+
+		if (data->wep_data[index].len > 0)
+			memcpy(buf+2, &(data->wep_data[index].key[1]), data->wep_data[index].len-1);
+
+		adm8211_write_sram_bytes(dev, addr, buf, 14);
+	} else {
+		addr = (index*5) + ADM8211_SRAM_B_SHARE_KEY;
+
+		/* control entry */
+		if (data->wep_data[index].len > 5)
+			*(__le32 *)buf = cpu_to_le32(ADM8211_WEP_ENABLE | ADM8211_WEP_B_104);
+		else if (data->wep_data[index].len > 0)
+			*(__le32 *)buf = cpu_to_le32(ADM8211_WEP_ENABLE);
+
+		if (data->wep_data[index].len > 0)
+			memcpy(buf+4, data->wep_data[index].key, data->wep_data[index].len);
+
+		adm8211_write_sram_bytes(dev, addr, buf, 17);
+	}
+}
+
+void adm8211_update_wep(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	u32 reg;
+
+	reg = ADM8211_CSR_READ(MACTEST);
+	if (data->flags & WEP_ENABLED) {
+		reg &= ~(3<<20);
+		reg |= (1<<22);
+		reg |= (data->default_key << 20);
+	} else
+		reg &= ~(7<<20);
+	ADM8211_CSR_WRITE(MACTEST, reg);
+
+	reg = ADM8211_CSR_READ(WEPCTL);
+	if (data->flags & WEP_ENABLED)
+		reg |= ADM8211_WEPCTL_WEPENABLE;
+	else
+		reg &= ~ADM8211_WEPCTL_WEPENABLE;
+
+	/* no hardware WEP RX decryption on the ADM8211A */
+	if (priv->revid < ADM8211_REV_BA)
+		reg |= ADM8211_WEPCTL_WEPRXBYP;
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+void adm8211_update_mode(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+
+	/* make sure we're stopped */
+	if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) {
+		ADM8211_CSR_WRITE(NAR, priv->nar & ~(ADM8211_NAR_SR | ADM8211_NAR_ST));
+		ADM8211_CSR_READ(NAR);
+		mdelay(20);
+	}
+
+	priv->soft_rx_crc = 0;
+	switch (priv->iw_mode) {
+	case IW_MODE_INFRA:
+		priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA);
+		priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR;
+		ADM8211_CSR_WRITE(CAP1, data->capab<<16);
+		break;
+	case IW_MODE_ADHOC:
+		priv->nar &= ~ADM8211_NAR_PR;
+		priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR;
+
+		ADM8211_CSR_WRITE(CAP1, data->capab<<16);
+
+		/* don't trust the error bits on rev 0x20 and up in adhoc */
+		if (priv->revid >= ADM8211_REV_BA)
+			priv->soft_rx_crc = 1;
+		break;
+	case IW_MODE_MONITOR:
+		priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST);
+		priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR;
+		ADM8211_CSR_WRITE(CAP1, 0);
+		break;
+	}
+
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+}
+
+static void adm8211_hw_init_syn(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x09,
+			(priv->transceiver_type == ADM8211_RFMD2958
+			? 0x10050 : 0x00050) );
+		adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E);
+		adm8211_rf_write_syn_max2820(dev, 0x2, 0x001);
+		adm8211_rf_write_syn_max2820(dev, 0x3, 0x054);
+		adm8211_rf_write_syn_max2820(dev, 0x4, 0x310);
+		adm8211_rf_write_syn_max2820(dev, 0x5, 0x000);
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C);
+		adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB);
+		adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F);
+		adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9);
+		adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280);
+		adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641);
+		adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130);
+		adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000);
+		adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F);
+		adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C);
+		adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000);
+		adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000);
+		break;
+
+	case ADM8211_RFMD2948:
+	default:
+		break;
+	}
+}
+
+static int adm8211_hw_init_bbp(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+
+	/* write addresses */
+	if (priv->bbp_type == ADM8211_TYPE_INTERSIL) {
+		ADM8211_CSR_WRITE(MMIWA,  0x100E0C0A);
+		ADM8211_CSR_WRITE(MMIRD0, 0x00007c7e);
+		ADM8211_CSR_WRITE(MMIRD1, 0x00100000);
+	} else if (priv->bbp_type == ADM8211_TYPE_RFMD ||
+		   priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+
+	/* check specific BBP type */
+	switch (priv->eeprom->specific_bbptype) {
+	case ADM8211_BBP_RFMD3000:
+	case ADM8211_BBP_RFMD3002:
+		ADM8211_CSR_WRITE(MMIWA,  0x00009101);
+		ADM8211_CSR_WRITE(MMIRD0, 0x00000301);
+		break;
+
+	case ADM8211_BBP_ADM8011:
+		ADM8211_CSR_WRITE(MMIWA,  0x00008903);
+		ADM8211_CSR_WRITE(MMIRD0, 0x00001716);
+
+		reg = ADM8211_CSR_READ(BBPCTL);
+		reg &= ~ADM8211_BBPCTL_TYPE;
+		reg |= 0x5 << 18;
+		ADM8211_CSR_WRITE(BBPCTL, reg);
+		break;
+	}
+
+	switch (priv->revid) {
+	case ADM8211_REV_CA:
+		if (priv->transceiver_type == ADM8211_RFMD2958 ||
+		    priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER ||
+		    priv->transceiver_type == ADM8211_RFMD2948)
+			ADM8211_CSR_WRITE(SYNCTL, (0x1 << 22));
+		else if (priv->transceiver_type == ADM8211_MAX2820 ||
+			 priv->transceiver_type == ADM8211_AL2210L)
+			ADM8211_CSR_WRITE(SYNCTL, (0x3 << 22));
+		break;
+
+	case ADM8211_REV_BA:
+		reg  = ADM8211_CSR_READ(MMIRD1);
+		reg &= 0x0000FFFF;
+		reg |= 0x7e100000;
+		ADM8211_CSR_WRITE(MMIRD1, reg);
+		break;
+
+	case ADM8211_REV_AB:
+	case ADM8211_REV_AF:
+	default:
+		ADM8211_CSR_WRITE(MMIRD1, 0x7e100000);
+		break;
+	}
+
+	/* For RFMD */
+	ADM8211_CSR_WRITE(MACTEST, 0x800);
+	}
+
+	adm8211_hw_init_syn(dev);
+
+	/* Set RF Power control IF pin to PE1+PHYRST# */
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | ADM8211_SYNRF_PE1 |
+			  ADM8211_SYNRF_PHYRST);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(20);
+
+	/* write BBP regs */
+	if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+		/* RF3000 BBP */
+		/* another set:
+		 * 11: c8
+		 * 14: 14
+		 * 15: 50 (chan 1..13; chan 14: d0)
+		 * 1c: 00
+		 * 1d: 84
+		 */
+		adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80);
+		adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); /* antenna selection: diversity */
+		adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74);
+		adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38);
+		adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40);
+
+		if (priv->eeprom->major_version < 2) {
+			adm8211_write_bbp(dev, 0x1c, 0x00);
+			adm8211_write_bbp(dev, 0x1d, 0x80);
+		} else {
+			if (priv->revid == ADM8211_REV_BA)
+				adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28);
+			else
+				adm8211_write_bbp(dev, 0x1c, 0x00);
+
+			adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+		}
+	} else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+	adm8211_write_bbp(dev, 0x00, 0xFF);	/* reset baseband */
+	adm8211_write_bbp(dev, 0x07, 0x0A);	/* antenna selection: diversity */
+
+	/* TODO: find documentation for this */
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x09, 0x00);
+		adm8211_write_bbp(dev, 0x0a, 0x00);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x00);
+		adm8211_write_bbp(dev, 0x0f, 0xAA);
+		adm8211_write_bbp(dev, 0x10, 0x8c);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x40);
+		adm8211_write_bbp(dev, 0x20, 0x23);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x28);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x28, 0x35);
+		adm8211_write_bbp(dev, 0x2a, 0x8c);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x2d, 0x0A);
+		adm8211_write_bbp(dev, 0x29, 0x40);
+		adm8211_write_bbp(dev, 0x60, 0x08);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x09, 0x05);
+		adm8211_write_bbp(dev, 0x0a, 0x02);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x0f);
+		adm8211_write_bbp(dev, 0x0f, 0x55);
+		adm8211_write_bbp(dev, 0x10, 0x8d);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x4a);
+		adm8211_write_bbp(dev, 0x20, 0x20);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x23);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x2a, 0x8c);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x29, 0x4a);
+		adm8211_write_bbp(dev, 0x60, 0x2b);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x07, 0x05);
+		adm8211_write_bbp(dev, 0x08, 0x03);
+		adm8211_write_bbp(dev, 0x09, 0x00);
+		adm8211_write_bbp(dev, 0x0a, 0x00);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x10);
+		adm8211_write_bbp(dev, 0x0f, 0x55);
+		adm8211_write_bbp(dev, 0x10, 0x8d);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x4a);
+		adm8211_write_bbp(dev, 0x20, 0x20);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x23);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x2a, 0xaa);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x29, 0xfa);
+		adm8211_write_bbp(dev, 0x60, 0x2d);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_RFMD2948:
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
+		       dev->name, priv->transceiver_type);
+		break;
+	}
+	} else {
+		printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
+		       dev->name, priv->bbp_type);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+
+	/* Set RF CAL control source to MAC control */
+	reg = ADM8211_CSR_READ(SYNCTL);
+	reg |= ADM8211_SYNCTL_SELCAL;
+	ADM8211_CSR_WRITE(SYNCTL, reg);
+
+	return 0;
+}
+
+static int adm8211_set_rate(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	u32 reg;
+	int i = 0;
+	u8 rate_buf[12] = {0};
+
+	/* write supported rates */
+	if (priv->revid != ADM8211_REV_BA) {
+		rate_buf[0] = data->num_supp_rates;
+		for (i = 0; i < data->num_supp_rates; i+=1)
+			rate_buf[i+1] = data->supp_rates[i] |
+					(data->supp_rates[i] <= data->rate ? 0x80 : 0);
+	} else {
+		/* workaround for rev BA specific bug */
+		rate_buf[0]=4;
+		rate_buf[1]=0x82;
+		rate_buf[2]=0x04;
+		rate_buf[3]=0x0b;
+		rate_buf[4]=0x16;
+	}
+	
+	adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, data->num_supp_rates+1);
+
+	priv->plcp_signal = data->rate * 5;
+
+	/* keep bits 0-23 */
+	reg = ADM8211_CSR_READ(PLCPHD) & 0x00FFFFFF;
+
+	/* short preamble */
+	reg |= (1 << 15);
+
+	reg |= priv->plcp_signal << 24;
+	ADM8211_CSR_WRITE(PLCPHD, reg);
+
+	/* MTMLT   = 512 TU (max TX MSDU lifetime)
+	 * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate)
+	 * SRTYLIM = 224 (short retry limit, value in TX header used by default) */
+	ADM8211_CSR_WRITE(TXLMT, (512<<16) | (priv->plcp_signal<<8) | (224<<0));
+
+	if (priv->revid >= ADM8211_REV_BA)
+		adm8211_set_beacon(dev);
+
+	return 0;
+}
+
+static void adm8211_hw_init(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	u8 cacheline;
+
+	reg = ADM8211_CSR_READ(PAR);
+	reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME;
+	reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL);
+
+	if (!pci_set_mwi(priv->pdev)) {
+		reg |= (0x1<<24);
+		pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cacheline);
+
+		switch (cacheline) {
+		case  0x8: reg |= (0x1<<14);
+			   break;
+		case 0x16: reg |= (0x2<<14);
+			   break;
+		case 0x32: reg |= (0x3<<14);
+			   break;
+		  default: reg |= (0x0<<14);
+			   break;
+		}
+	}
+
+	ADM8211_CSR_WRITE(PAR, reg);
+
+	reg = ADM8211_CSR_READ(CSR_TEST1);
+	reg &= ~(0xF<<28);
+	reg |= ((1 << 28) | (1 << 31));
+	ADM8211_CSR_WRITE(CSR_TEST1, reg);
+
+	reg = (0x07 << 21) | (1 << 20) | (1 << 8);
+	ADM8211_CSR_WRITE(WCSR, reg);
+
+	/* Disable APM, enable receive FIFO threshold, and set drain receive
+	 * threshold to store-and-forward */
+	reg = ADM8211_CSR_READ(CMDR);
+	reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT);
+	reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF;
+	ADM8211_CSR_WRITE(CMDR, reg);
+
+	adm8211_set_rate(dev);
+
+	/* 4-bit values:
+	 * PWR1UP   = 8 * 2 ms
+	 * PWR0PAPE = 8 us or 5 us
+	 * PWR1PAPE = 1 us or 3 us
+	 * PWR0TRSW = 5 us
+	 * PWR1TRSW = 12 us
+	 * PWR0PE2  = 13 us
+	 * PWR1PE2  = 1 us
+	 * PWR0TXPE = 8 or 6 */
+	if (priv->revid < ADM8211_REV_CA)
+		ADM8211_CSR_WRITE(TOFS2, 0x8815cd18);
+	else
+		ADM8211_CSR_WRITE(TOFS2, 0x8535cd16);
+
+	/* Enable store and forward for transmit */
+	priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB;
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	/* Reset RF */
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_RADIO);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(10);
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(5);
+
+	/* Set CFP Max Duration to 0x10 TU */
+	reg = ADM8211_CSR_READ(CFPP);
+	reg &= ~(0xffff<<8);
+	reg |= 0x0010<<8;
+	ADM8211_CSR_WRITE(CFPP, reg);
+
+	/* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us
+	 * TUCNT = 0x3ff - Tu counter 1024 us  */
+	ADM8211_CSR_WRITE(TOFS0, (0x16 << 24) | 0x3ff);
+
+	/* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us),
+	 * DIFS=50 us, EIFS=100 us */
+	if (priv->revid < ADM8211_REV_CA)
+		ADM8211_CSR_WRITE(IFST, (20 << 23) | (110 << 15) |
+					(50 << 9)  | 100);
+	else
+		ADM8211_CSR_WRITE(IFST, (20 << 23) | (24 << 15) |
+					(50 << 9)  | 100);
+
+	/* PCNT = 1 (MAC idle time awake/sleep, unit S)
+	 * RMRD = 2346 * 8 + 1 us (max RX duration)  */
+	ADM8211_CSR_WRITE(RMD, (1 << 16) | 18769);
+
+	/* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */
+	ADM8211_CSR_WRITE(RSPT, 0xffffff00);
+
+	/* Initialize BBP (and SYN) */
+	adm8211_hw_init_bbp(dev);
+
+	/* make sure interrupts are off */
+	ADM8211_CSR_WRITE(IER, 0);
+
+	/* ACK interrupts */
+	ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR));
+
+	/* Setup WEP */
+	adm8211_write_wepkey(dev, 0);
+	adm8211_write_wepkey(dev, 1);
+	adm8211_write_wepkey(dev, 2);
+	adm8211_write_wepkey(dev, 3);
+	adm8211_update_wep(dev);
+}
+
+static int adm8211_hw_reset(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	int timeout = 50;
+
+	/* Power-on issue */
+	ADM8211_CSR_WRITE(FRCTL, 0);
+
+	/* Reset the chip */
+	reg = ADM8211_CSR_READ(PAR);
+	ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR);
+
+	while ((ADM8211_CSR_READ(PAR) & ADM8211_PAR_SWR) && timeout) {
+		mdelay(100);
+		timeout-=1;
+	}
+	if (timeout <= 0)
+		return -ETIMEDOUT;
+
+	ADM8211_CSR_WRITE(PAR, reg);
+
+	if (priv->revid == ADM8211_REV_BA &&
+	    ( priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER
+	   || priv->transceiver_type == ADM8211_RFMD2958)) {
+		reg = ADM8211_CSR_READ(CSR_TEST1);
+		reg |= (1 << 4) | (1 << 5);
+		ADM8211_CSR_WRITE(CSR_TEST1, reg);
+	} else if (priv->revid == ADM8211_REV_CA) {
+		reg = ADM8211_CSR_READ(CSR_TEST1);
+		reg &= ~((1 << 4) | (1 << 5));
+		ADM8211_CSR_WRITE(CSR_TEST1, reg);
+	}
+
+	ADM8211_CSR_WRITE(FRCTL, 0);
+
+	reg = ADM8211_CSR_READ(CSR_TEST0);
+	reg |= ADM8211_CSR_TEST0_EPRLD;
+	ADM8211_CSR_WRITE(CSR_TEST0, reg);
+
+	adm8211_clear_sram(dev);
+
+	return 0;
+}
+
+static void adm8211_set_tbtt (struct net_device *dev, u16 tbtt)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	/* TSFTOFSR (RX TSFT Offset) = 1 us
+	 * TBTTPRE (prediction time) = tbtt TU
+	 * TBTTOFS (Wake up time offset before TBTT) = 20 TU */
+	ADM8211_CSR_WRITE(TOFS1, (1 << 24) | (tbtt << 8) | 20);
+	ADM8211_CSR_READ(TOFS1);
+}
+
+static u64 adm8211_get_tsft (struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 tsftl;
+	u64 tsft;
+
+	tsftl = ADM8211_CSR_READ(TSFTL);
+	tsft = ADM8211_CSR_READ(TSFTH);
+	tsft <<= 32;
+	tsft |= tsftl;
+
+	return tsft;
+}
+
+static void adm8211_set_interval(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	u32 reg;
+
+	/* BP (beacon interval) = data->beacon_interval
+	 * LI (listen interval) = data->listen_interval (in beacon intervals) */
+	reg = (data->beacon_interval << 16) | data->listen_interval;
+	ADM8211_CSR_WRITE(BPLI, reg);
+
+	/* lose link after 7 lost beacons */
+	reg = ADM8211_CSR_READ(WCSR) & ~(0xFF<<21);
+	ADM8211_CSR_WRITE(WCSR, reg | (0x07<<21));
+	ADM8211_CSR_READ(WCSR);
+}
+
+static int adm8211_set_bssid(struct net_device *dev, u8 *bssid)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+
+	reg = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24);
+	ADM8211_CSR_WRITE(BSSID0, reg);
+	reg = ADM8211_CSR_READ(ABDA1);
+	reg &= 0x0000ffff;
+	reg |= (bssid[4] << 16) | (bssid[5] << 24);
+	ADM8211_CSR_WRITE(ABDA1, reg);
+
+	return 0;
+}
+
+static int adm8211_set_ssid(struct net_device *dev, u8 *ssid, size_t ssid_len)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u8 buf[36];
+
+	if (ssid_len > 32)
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = ssid_len;
+	memcpy(buf + 1, ssid, ssid_len);
+	adm8211_write_sram_bytes(dev, ADM8211_SRAM_SSID, buf, 33);
+	adm8211_set_beacon(dev);
+
+	return 0;
+}
+
+void adm8211_set_beacon (struct net_device *dev) {
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	unsigned int blen, len, rem;
+	u32 reg;
+
+	if (priv->iw_mode == IW_MODE_ADHOC)
+		blen = 24 +
+			8 + 2 + 2 + 2 + data->ssid_len + 2 + data->num_supp_rates +
+			3 + 4 + WLAN_FCS_LEN;
+	else
+		blen = 0;
+
+	if (priv->revid < ADM8211_REV_BA) {
+		/* 11M PLCP length */
+		blen *= 8;
+		rem   = blen % 11;
+		len   = blen / 11;
+		if (rem) {
+			len += 1;
+			if (rem <= 3)
+				len |= 1<<7;
+		}
+		reg = len << 16;
+
+		/* 5.5M PLCP length */
+		rem   = (blen*2) % 11;
+		len   = (blen*2) / 11;
+		if (rem)
+			len += 1;
+		reg |= len << 8;
+
+		reg |= blen;
+
+		ADM8211_CSR_WRITE(BCNT, reg);
+	} else {
+		len = blen;
+		rem = (blen*80) % priv->plcp_signal;
+		len = (blen*80) / priv->plcp_signal;
+		if (rem) {
+			len += 1;
+			if (data->rate == 0x16 && ((blen*8) % 11) <= 3)
+				len |= 1<<15;
+		}
+
+		len &= 0xFFFF;
+
+		ADM8211_CSR_WRITE(BCNT, len);
+
+		reg = ADM8211_CSR_READ(MMIRD1);
+		reg &= 0xFFFF0000;
+		reg |= len;
+		ADM8211_CSR_WRITE(MMIRD1, reg);
+	}
+}
+
+static void adm8211_init_rings(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	struct adm8211_desc *desc;
+	struct adm8211_ring_info *info;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		desc = &priv->rx_ring[i];
+		desc->status = 0;
+		desc->length = cpu_to_le32(RX_PKT_SIZE);
+		priv->rx_buffers[i].skb = NULL;
+	}
+	/* Mark the end of RX ring; hw returns to base address after this
+	 * descriptor */
+	desc->length |= cpu_to_le32(RDES1_CONTROL_RER);
+
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		desc = &priv->rx_ring[i];
+		info = &priv->rx_buffers[i];
+
+		info->skb = dev_alloc_skb(RX_PKT_SIZE);
+		if (info->skb == NULL)
+			break;
+		info->mapping = pci_map_single(priv->pdev, info->skb->tail,
+					       RX_PKT_SIZE,
+					       PCI_DMA_FROMDEVICE);
+		info->skb->dev = dev;
+		desc->buffer1 = cpu_to_le32(info->mapping);
+		desc->status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL );
+	}
+
+	/* Setup TX ring. TX buffers descriptors will be filled in as needed */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		desc = &priv->tx_ring[i];
+		info = &priv->tx_buffers[i];
+
+		info->skb = NULL;
+		info->mapping = 0;
+		desc->status = 0;
+	}
+	desc->length = cpu_to_le32(TDES1_CONTROL_TER);
+
+	priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0;
+	ADM8211_CSR_WRITE(RDB, priv->rx_ring_dma);
+	ADM8211_CSR_WRITE(TDBD, priv->tx_ring_dma);
+}
+
+static void adm8211_free_rings(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		if (!priv->rx_buffers[i].skb)
+			continue;
+
+		pci_unmap_single(
+			priv->pdev,
+			priv->rx_buffers[i].mapping,
+			RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+
+		dev_kfree_skb(priv->rx_buffers[i].skb);
+	}
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (!priv->tx_buffers[i].skb)
+			continue;
+
+		pci_unmap_single(
+			priv->pdev,
+			priv->tx_buffers[i].mapping,
+			priv->tx_buffers[i].skb->len, PCI_DMA_TODEVICE);
+
+		dev_kfree_skb(priv->tx_buffers[i].skb);
+	}
+}
+
+static int adm8211_open(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int retval;
+
+	/* Power up MAC and RF chips */
+	retval = adm8211_hw_reset(dev);
+	if (retval) {
+		printk(KERN_ERR "%s: hardware reset failed\n", dev->name);
+		goto fail;
+	}
+
+	adm8211_init_rings(dev);
+
+	/* Init hardware */
+	adm8211_hw_init(dev);
+	adm8211_rf_set_channel(dev, max(priv->ieee80211.pref_channel,
+					priv->ieee80211.chan_range.min));
+
+	/* set mac address */
+	ADM8211_CSR_WRITE(PAR0, *(u32 *)dev->dev_addr);
+	ADM8211_CSR_WRITE(PAR1, *(u16 *)(dev->dev_addr + 4));
+
+	adm8211_set_rx_mode(dev);
+
+	retval = request_irq(dev->irq, &adm8211_interrupt,
+			     SA_SHIRQ, dev->name, dev);
+	if (retval) {
+		printk(KERN_ERR "%s: failed to register IRQ handler\n",
+		       dev->name);
+		goto fail;
+	}
+
+	ADM8211_CSR_WRITE(IER, ADM8211_INTMASK);
+	ADM8211_CSR_READ(IER);
+
+	adm8211_update_mode(dev);
+
+	/* should be more than enough time for the card to settle down */
+	mdelay(200);
+
+	ieee80211_start(&priv->ieee80211);
+
+	netif_start_queue(dev);
+	return 0;
+
+fail:
+	return retval;
+}
+
+
+static int adm8211_stop(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	del_singleshot_timer_sync(&priv->timer);
+
+	ieee80211_stop(&priv->ieee80211);
+
+	netif_stop_queue(dev);
+
+	ADM8211_CSR_WRITE(IER, 0);
+	priv->nar &= ~(ADM8211_NAR_ST | ADM8211_NAR_PR | ADM8211_NAR_PB |
+		       ADM8211_NAR_SR | ADM8211_NAR_MM );
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+	ADM8211_CSR_READ(NAR);
+
+	free_irq(dev->irq, dev);
+
+	adm8211_free_rings(dev);
+
+	skb_queue_purge(&priv->rx_queue);
+
+	adm8211_hw_reset(dev);
+	return 0;
+}
+
+static void adm8211_timer(unsigned long ptr)
+{
+	struct net_device *dev = (struct net_device *) ptr;
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	u16 rra, rwa;
+
+	if (priv->revid != ADM8211_REV_BA ||
+	   !(dev->flags & IFF_UP))
+		return;
+
+	/* checks for stalls on adm8211b */
+	reg = ADM8211_CSR_READ(CSR_TEST1);
+	rra = (reg >> 12) & 0x1FF;
+	rwa = (reg >> 2 ) & 0x1FF;
+
+	if ( (rra != rwa) && !(reg & (1<<1)) ) {
+		printk(KERN_DEBUG "%s: stalled. resetting card\n", dev->name);
+		adm8211_stop(dev);
+		adm8211_open(dev);
+	}
+}
+
+static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len,
+				   int plcp_signal, int short_preamble)
+{
+	/* Alternative calculation from NetBSD: */
+
+/* IEEE 802.11b durations for DSSS PHY in microseconds */
+#define IEEE80211_DUR_DS_LONG_PREAMBLE	144
+#define IEEE80211_DUR_DS_SHORT_PREAMBLE	72
+#define IEEE80211_DUR_DS_FAST_PLCPHDR	24
+#define IEEE80211_DUR_DS_SLOW_PLCPHDR	48
+#define IEEE80211_DUR_DS_SLOW_ACK	112
+#define IEEE80211_DUR_DS_FAST_ACK	56
+#define IEEE80211_DUR_DS_SLOW_CTS	112
+#define IEEE80211_DUR_DS_FAST_CTS	56
+#define IEEE80211_DUR_DS_SLOT		20
+#define IEEE80211_DUR_DS_SIFS		10
+
+	int remainder;
+
+	*dur = (80 * (WLAN_ADDR3_HDR_LEN + payload_len) + plcp_signal - 1)
+		/ plcp_signal;
+
+	if (plcp_signal <= PLCP_SIGNAL_2M) {
+		/* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */
+		*dur += 3 * (IEEE80211_DUR_DS_SIFS +
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE +
+			     IEEE80211_DUR_DS_FAST_PLCPHDR) +
+			     IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK;
+	} else {
+		/* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */
+		*dur += 3 * (IEEE80211_DUR_DS_SIFS +
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE +
+			     IEEE80211_DUR_DS_FAST_PLCPHDR) +
+			     IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK;
+	}
+
+	/* lengthen duration if long preamble */
+	if (!short_preamble) {
+		*dur +=
+			3 * (IEEE80211_DUR_DS_LONG_PREAMBLE -
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE) +
+			3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR -
+			     IEEE80211_DUR_DS_FAST_PLCPHDR);
+	}
+
+
+	*plcp = (80 * len) / plcp_signal;
+	remainder = (80 * len) % plcp_signal;
+	if (plcp_signal == PLCP_SIGNAL_11M &&
+	    remainder <= 30 && remainder > 0) {
+		*plcp = (*plcp | 0x8000) + 1;
+	} else if (remainder)
+		(*plcp)++;
+}
+
+/* TODO: add addr4 to this */
+/* Transmit skb (802.11 header created by hardware) */
+static int adm8211_tx(struct net_device *dev, struct sk_buff *skb,
+		      __le16 fc, int short_preamble, u8 *dst)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct adm8211_tx_hdr *txhdr;
+	int entry;
+	dma_addr_t mapping;
+	u32 flag;
+	unsigned long flags;
+	u16 fc_;
+	size_t payload_len;
+	int plcp, dur, len;
+
+	int plcp_signal;
+
+	fc_ = le16_to_cpu(fc);
+	if (WLAN_FC_GET_TYPE(fc_) != WLAN_FC_TYPE_DATA) {
+		if (fc_ & WLAN_FC_ISWEP && wep_encrypt(skb, 0, data)) {
+			printk(KERN_DEBUG "%s: failed to WEP encrypt frame\n", dev->name);
+			dev_kfree_skb(skb);
+			return 0;
+		}
+		fc_ &= ~WLAN_FC_ISWEP;
+	}
+
+	payload_len = skb->len;
+
+	if (skb_headroom(skb) < sizeof(struct adm8211_tx_hdr)) {
+		if (pskb_expand_head(skb, sizeof(struct adm8211_tx_hdr), 0,
+				     GFP_ATOMIC)) {
+			printk(KERN_DEBUG "%s: failed to allocate room for TX "
+			       "header\n", dev->name);
+			dev_kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	plcp_signal = ieee80211_get_rate(&priv->ieee80211, dst) * 5;
+
+	txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr));
+	memset(txhdr, 0, sizeof(*txhdr));
+	memcpy(txhdr->da, dst, ETH_ALEN);
+	txhdr->signal = plcp_signal;
+	txhdr->frame_body_size = cpu_to_le16(payload_len);
+	txhdr->frame_control = fc;
+
+	len = WLAN_ADDR3_HDR_LEN + payload_len + WLAN_FCS_LEN;
+	if (fc_ & WLAN_FC_ISWEP)
+		len += 8;
+
+	if (len > priv->frag_thresh && is_valid_ether_addr(dst)) {
+	txhdr->frag_threshold = cpu_to_le16(priv->frag_thresh);
+
+	len = WLAN_ADDR3_HDR_LEN + priv->frag_thresh + WLAN_FCS_LEN;
+	if (fc_ & WLAN_FC_ISWEP)
+		len += 8;
+	adm8211_calc_durations(&dur, &plcp, priv->frag_thresh,
+				len, plcp_signal, short_preamble);
+	txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
+	txhdr->dur_frag_head = cpu_to_le16(dur);
+
+	if (payload_len % priv->frag_thresh) {
+		len = WLAN_ADDR3_HDR_LEN +
+		      (payload_len % priv->frag_thresh) + WLAN_FCS_LEN;
+		if (fc_ & WLAN_FC_ISWEP)
+			len += 8;
+
+		adm8211_calc_durations(&dur, &plcp, payload_len % priv->frag_thresh,
+				       len, plcp_signal, short_preamble);
+
+		txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
+		txhdr->dur_frag_tail = cpu_to_le16(dur);
+	} else {
+		txhdr->plcp_frag_tail_len = txhdr->plcp_frag_head_len;
+		txhdr->dur_frag_tail = txhdr->dur_frag_head;
+	}
+
+	} else {
+		txhdr->frag_threshold = cpu_to_le16(0x0FFF);
+		adm8211_calc_durations(&dur, &plcp, payload_len,
+				       len, plcp_signal, short_preamble);
+		txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
+		txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
+		txhdr->dur_frag_head = cpu_to_le16(dur);
+		txhdr->dur_frag_tail = cpu_to_le16(dur);
+	}
+
+	txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER);
+
+	if (short_preamble)
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE);
+
+	if ((payload_len + (fc_ & WLAN_FC_ISWEP ? 8 : 0)) > priv->rts_thresh
+	    && is_valid_ether_addr(dst))
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS);
+
+	if (fc_ & WLAN_FC_ISWEP)
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE);
+
+	txhdr->retry_limit = priv->retry_limit;
+	mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+				 PCI_DMA_TODEVICE);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	entry = priv->cur_tx % TX_RING_SIZE;
+
+	priv->tx_buffers[entry].skb = skb;
+	priv->tx_buffers[entry].mapping = mapping;
+	priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping);
+
+	if (priv->cur_tx - priv->dirty_tx < TX_RING_SIZE / 2)
+		flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else if (priv->cur_tx - priv->dirty_tx == TX_RING_SIZE / 2)
+		flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else if (priv->cur_tx - priv->dirty_tx < TX_RING_SIZE - 2)
+		flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else {
+		flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+		netif_stop_queue(dev);
+	}
+	if (entry == TX_RING_SIZE - 1)
+		flag |= TDES1_CONTROL_TER;
+	priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len);
+
+	/* Set TX rate (SIGNAL field in PLCP PPDU format) */
+	flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */;
+	priv->tx_ring[entry].status = cpu_to_le32(flag);
+
+	priv->cur_tx++;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Trigger transmit poll */
+	ADM8211_CSR_WRITE(TDR, 0);
+
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+
+static int adm8211_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	return ieee80211_data_tx(&priv->ieee80211, skb);
+}
+
+
+int adm8211_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+	memcpy(haddr, skb->mac.raw + 10 + sizeof(struct avs_caphdr), ETH_ALEN); /* addr2 */
+	return ETH_ALEN;
+}
+
+static int __devinit adm8211_probe(struct pci_dev *pdev,
+				   const struct pci_device_id *id)
+{
+	struct net_device *dev;
+	struct adm8211_priv *priv;
+	unsigned long mem_addr, mem_len;
+	unsigned int io_addr, io_len;
+	unsigned int ring_size;
+	int err;
+	u32 reg;
+
+#ifndef MODULE
+	static unsigned int cardidx;
+	if (!cardidx++) {
+		printk(version);
+		printk(KERN_INFO "adm8211: release " RELEASE_DATE "\n");
+	}
+#endif
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", pci_name(pdev));
+		return err;
+	}
+
+	io_addr = pci_resource_start(pdev, 0);
+	io_len = pci_resource_len(pdev, 0);
+	mem_addr = pci_resource_start(pdev, 1);
+	mem_len = pci_resource_len(pdev, 1);
+	if (io_len < 256 || mem_len < 1024) {
+		printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", pci_name(pdev));
+		goto err_disable_pdev;
+	}
+
+
+	/* check signature */
+	pci_read_config_dword(pdev, 0x80 /* CR32 */, &reg);
+	if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) {
+		printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", pci_name(pdev), reg);
+		goto err_disable_pdev;
+	}
+
+	err = pci_request_regions(pdev, "adm8211");
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", pci_name(pdev));
+		goto err_disable_pdev;
+	}
+
+	if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+		printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", pci_name(pdev));
+		goto err_free_reg;
+	}
+	pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+
+	pci_set_master(pdev);
+
+	dev = alloc_etherdev(sizeof(struct adm8211_priv));
+	if (!dev) {
+		printk(KERN_ERR "%s (adm8211): Etherdev alloc failed\n", pci_name(pdev));
+		err = -ENOMEM;
+		goto err_free_reg;
+	}
+	priv = netdev_priv(dev);
+	priv->pdev = pdev;
+
+	spin_lock_init(&priv->lock);
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	pci_set_drvdata(pdev, dev);
+
+	priv->map = pci_iomap(pdev, 1, mem_len);
+	if (!priv->map)
+		priv->map = pci_iomap(pdev, 0, io_len);
+
+	if (!priv->map) {
+		printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", pci_name(pdev));
+		goto err_free_dev;
+	}
+
+	dev->irq = pdev->irq;
+
+	/* Allocate TX/RX descriptors */
+	ring_size = sizeof(struct adm8211_desc) * RX_RING_SIZE +
+		sizeof(struct adm8211_desc) * TX_RING_SIZE;
+	priv->rx_ring = pci_alloc_consistent(pdev, ring_size,
+					     &priv->rx_ring_dma);
+	if (!priv->rx_ring) {
+		printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", pci_name(pdev));
+		goto err_iounmap;
+	}
+	priv->tx_ring = (struct adm8211_desc *) (priv->rx_ring + RX_RING_SIZE);
+	priv->tx_ring_dma = priv->rx_ring_dma +
+		sizeof(struct adm8211_desc) * RX_RING_SIZE;
+
+	skb_queue_head_init(&priv->rx_queue);
+	priv->rx_tasklet.func = adm8211_rx_tasklet;
+	priv->rx_tasklet.data = (unsigned long) dev;
+
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &priv->revid);
+
+	/* Power-on issue */
+	ADM8211_CSR_WRITE(FRCTL, 0);
+	ADM8211_CSR_READ(FRCTL);
+	ADM8211_CSR_WRITE(FRCTL, 1);
+	ADM8211_CSR_READ(FRCTL);
+	mdelay(100);
+
+	/* Clear the missed-packet counter. */
+	ADM8211_CSR_READ(LPC);
+
+	put_unaligned(ADM8211_CSR_READ(PAR0), (u32 *) dev->dev_addr);
+	put_unaligned(ADM8211_CSR_READ(PAR1) & (__force u32) __constant_cpu_to_le32(0xffff),
+		      (u16 *) &dev->dev_addr[4]);
+
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING "%s (adm8211): Invalid hwaddr! Using randomly generated hwaddr\n", pci_name(pdev));
+		random_ether_addr(dev->dev_addr);
+	}
+
+	dev->features |= NETIF_F_LLTX;
+	dev->open = adm8211_open;
+	dev->stop = adm8211_stop;
+	dev->get_stats = adm8211_get_stats;
+	dev->set_multicast_list = adm8211_set_rx_mode;
+	dev->set_mac_address = adm8211_set_mac_address;
+	dev->hard_start_xmit = adm8211_hard_start_xmit;
+
+	dev->get_wireless_stats = adm8211_get_wireless_stats;
+	dev->wireless_handlers =
+		(struct iw_handler_def *) &adm8211_iw_handler_def;
+
+	init_timer(&priv->timer);
+	priv->timer.data = (unsigned long) dev;
+	priv->timer.function = adm8211_timer;
+
+	priv->iw_mode = IW_MODE_INFRA;
+
+	priv->ieee80211.dev = dev;
+	priv->ieee80211.set_channel = adm8211_rf_set_channel;
+	priv->ieee80211.set_bssid = adm8211_set_bssid;
+	priv->ieee80211.set_ssid = adm8211_set_ssid;
+	priv->ieee80211.tx = adm8211_tx;
+	priv->ieee80211.set_interval = adm8211_set_interval;
+	priv->ieee80211.set_tbtt = adm8211_set_tbtt;
+	priv->ieee80211.get_tsft = adm8211_get_tsft;
+	priv->ieee80211.link_change = adm8211_link_change;
+	priv->ieee80211.set_rate = adm8211_set_rate;
+	priv->ieee80211.supp_rates = (u8 *)IEEE80211_B_RATES;
+	priv->ieee80211.capab = WLAN_CAPABILITY_SHORT_PREAMBLE;
+	if (ieee80211_init(&priv->ieee80211)) {
+		printk(KERN_ERR "%s (adm8211): Cannot load ARC4 cipher\n", pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	priv->rts_thresh = 2347;
+	priv->frag_thresh = 2346;
+	priv->plcp_signal = priv->ieee80211.rate * 5;
+	priv->retry_limit = 3;
+	priv->ant_power = 0x40;
+	priv->tx_power = 0x40;
+	priv->lpf_cutoff = 0xFF;
+	priv->lnags_threshold = 0xFF;
+
+	err = adm8211_read_eeprom(dev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot allocate eeprom buffer\n", pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	err = register_netdev(dev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot register netdevice\n", pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	printk("%s: hwaddr %02x:%02x:%02x:%02x:%02x:%02x, IRQ %d, Rev 0x%02x\n",
+	       dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5],
+	       dev->irq, priv->revid);
+
+	return 0;
+
+ err_free_desc:
+	pci_free_consistent(pdev, ring_size, priv->rx_ring, priv->rx_ring_dma);
+
+ err_iounmap:
+	pci_iounmap(pdev, priv->map);
+
+ err_free_dev:
+	pci_set_drvdata(pdev, NULL);
+	free_netdev(dev);
+
+ err_free_reg:
+	pci_release_regions(pdev);
+
+ err_disable_pdev:
+	pci_disable_device(pdev);
+	return err;
+}
+
+
+static void __devexit adm8211_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct adm8211_priv *priv;
+
+	if (!dev)
+		return;
+
+	unregister_netdev(dev);
+
+	priv = netdev_priv(dev);
+	ieee80211_deinit(&priv->ieee80211);
+
+	pci_free_consistent(pdev,
+			    sizeof(struct adm8211_desc) * RX_RING_SIZE +
+			    sizeof(struct adm8211_desc) * TX_RING_SIZE,
+			    priv->rx_ring, priv->rx_ring_dma);
+
+	kfree(priv->eeprom);
+	pci_iounmap(pdev, priv->map);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	free_netdev(dev);
+}
+
+
+#ifdef CONFIG_PM
+static int adm8211_suspend(struct pci_dev *pdev, u32 state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	netif_device_detach(dev);
+
+	if (dev->flags & IFF_UP)
+		dev->stop(dev);
+
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, 3);
+	return 0;
+}
+
+static int adm8211_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	pci_set_power_state(pdev, 0);
+	pci_restore_state(pdev);
+
+	if (dev->flags & IFF_UP)
+		dev->open(dev);
+
+	netif_device_attach(dev);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table);
+
+/* TODO: enable_wake */
+static struct pci_driver adm8211_driver = {
+	.name		= "adm8211",
+	.id_table	= adm8211_pci_id_table,
+	.probe		= adm8211_probe,
+	.remove		= __devexit_p(adm8211_remove),
+#ifdef CONFIG_PM
+	.suspend	= adm8211_suspend,
+	.resume		= adm8211_resume,
+#endif /* CONFIG_PM */
+};
+
+
+
+static int __init adm8211_init(void)
+{
+#ifdef MODULE
+	printk(version);
+	printk(KERN_INFO "adm8211: release " RELEASE_DATE "\n");
+#endif
+
+	return pci_register_driver(&adm8211_driver);
+}
+
+
+static void __exit adm8211_exit(void)
+{
+	pci_unregister_driver(&adm8211_driver);
+
+	printk(KERN_INFO "adm8211: Driver unloaded\n");
+}
+
+
+module_init(adm8211_init);
+module_exit(adm8211_exit);
diff -Naur linux-2.6.12-rc3-no1/adm8211/adm8211_ioctl.c no-sources-new/adm8211/adm8211_ioctl.c
--- linux-2.6.12-rc3-no1/adm8211/adm8211_ioctl.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/adm8211_ioctl.c	2005-03-23 11:36:35.000000000 +0000
@@ -0,0 +1,974 @@
+/*
+ * Linux driver for ADMtek adm8211 (IEEE 802.11b wireless LAN card)
+ *
+ * Copyright (c) 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004-2005, Michael Wu <flamingice@sourmilk.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT > 12
+#include <net/iw_handler.h>
+#endif /* WIRELESS_EXT > 12 */
+#include <linux/random.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+
+#include "adm8211.h"
+#include "adm8211_ioctl.h"
+
+
+static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+				  2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+#define FREQ_COUNT ARRAY_SIZE(freq_list)
+
+#if 0
+#define SET_IWFREQ(freq, chan) \
+        freq->m = freq_list[chan - 1] * 100000; \
+        freq->e = 1;
+#else
+#define SET_IWFREQ(freq, chan) \
+	freq->m = chan; \
+        freq->e = 0;
+#endif
+
+static int adm8211_ioctl_giwname(struct net_device *dev,
+				 struct iw_request_info *info,
+				 char *name, char *extra)
+{
+	strcpy(name, "IEEE 802.11b");
+	return 0;
+}
+
+static int adm8211_ioctl_siwfreq(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_freq *freq, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	/* freq => chan. */
+	if (freq->e == 1 &&
+	    freq->m / 100000 >= freq_list[0] &&
+	    freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) {
+		int ch;
+		int fr = freq->m / 100000;
+		for (ch = 0; ch < FREQ_COUNT; ch++) {
+			if (fr == freq_list[ch]) {
+				freq->e = 0;
+				freq->m = ch + 1;
+				break;
+			}
+		}
+	}
+
+	if (freq->e != 0 || freq->m < priv->ieee80211.chan_range.min || freq->m > priv->ieee80211.chan_range.max)
+		return -EINVAL;
+
+	if (dev->flags & IFF_UP) {
+		if (priv->iw_mode == IW_MODE_MONITOR)
+			adm8211_rf_set_channel(dev, freq->m);
+		else
+			ieee80211_start_scan(&priv->ieee80211);
+	}
+
+	priv->ieee80211.channel = priv->ieee80211.pref_channel = freq->m;
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwfreq(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_freq *freq, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int chan = priv->ieee80211.channel;
+
+	if (chan < 1 || chan > FREQ_COUNT)
+		return -EINVAL;
+
+	SET_IWFREQ(freq, chan);
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_siwmode(struct net_device *dev,
+				 struct iw_request_info *info,
+				 __u32 *mode, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+
+	if (*mode != IW_MODE_INFRA && *mode != IW_MODE_ADHOC &&
+	    *mode != IW_MODE_MONITOR)
+		return -EOPNOTSUPP;
+
+	if (*mode == priv->iw_mode)
+		return 0;
+
+	ieee80211_stop(data);
+	priv->iw_mode = *mode;
+	
+	switch (*mode) {
+	case IW_MODE_INFRA:
+		priv->ieee80211.mode = IEEE80211_MANAGED;
+		priv->ieee80211.capab &= ~WLAN_CAPABILITY_IBSS;
+		dev->type = ARPHRD_ETHER;
+		dev->hard_header_parse = priv->eth_header_parse;
+		break;
+	case IW_MODE_ADHOC:
+		priv->ieee80211.mode = IEEE80211_ADHOC;
+		priv->ieee80211.capab |= WLAN_CAPABILITY_IBSS;
+		dev->type = ARPHRD_ETHER;
+		dev->hard_header_parse = priv->eth_header_parse;
+		break;
+	case IW_MODE_MONITOR:
+		priv->ieee80211.mode = IEEE80211_MONITOR;
+		dev->type = ARPHRD_IEEE80211_PRISM;
+		dev->hard_header_parse = adm8211_80211_header_parse;
+		break;
+	}
+
+	if (dev->flags & IFF_UP) {
+		adm8211_update_mode(dev);
+		ieee80211_start(data);
+	}
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwmode(struct net_device *dev,
+				 struct iw_request_info *info,
+				 __u32 *mode, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	*mode = priv->iw_mode;
+	return 0;
+}
+
+static int adm8211_ioctl_giwrange(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *dwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct iw_range *range = (struct iw_range *) extra;
+	int i, j;
+
+        dwrq->length = sizeof(struct iw_range);
+        memset(range, 0, sizeof(range));
+
+	range->min_nwid = range->max_nwid = 0;
+	
+	range->max_qual.qual  = 255;
+	range->max_qual.level = 1;
+	range->max_qual.noise = 1;
+
+	range->num_bitrates = data->num_supp_rates;
+	i = 0;
+	while (i++ < min(data->num_supp_rates, IW_MAX_BITRATES))
+		range->bitrate[i-1] = (data->supp_rates[i-1]*1000000)/2;
+
+	range->min_rts = 0;
+	range->max_rts = 2346;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	/* are the values really in dBm? */
+	range->txpower_capa = IW_TXPOW_RANGE | IW_TXPOW_DBM;
+
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = WIRELESS_EXT;
+	range->num_channels = data->chan_range.max - data->chan_range.min + 1;
+	range->num_frequency = range->num_channels;
+
+	range->max_encoding_tokens = 4;
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+	range->num_encoding_sizes = 2;
+
+	j=0;
+	for (i = data->chan_range.min; i <= data->chan_range.max; i+=1) {
+		range->freq[j].m=freq_list[i-1] * 100000;
+		range->freq[j].e=1;
+		range->freq[j].i=i;
+		j+=1;
+	}
+	return 0;
+}
+
+/*
+static int adm8211_ioctl_giwsens(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct ieee80211_bss *bss = ieee80211_get_bss(data, data->bssid);
+	return 0;
+}
+*/
+static int adm8211_ioctl_siwscan(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_point *data, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (dev->flags & IFF_UP && priv->iw_mode != IW_MODE_MONITOR)
+		ieee80211_start_scan(&priv->ieee80211);
+	else
+		return -1;
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwscan(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_point *data, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct iw_event iwe;
+	char *current_ev = extra;
+	char *end_buf = extra + IW_SCAN_MAX_DATA;
+	char *current_val;
+	struct ieee80211_bss *bss;
+	int i;
+
+	if (priv->ieee80211.state == IEEE80211_SCAN)
+		return -EAGAIN;
+
+	spin_lock_bh(&priv->ieee80211.lock);
+	bss = priv->ieee80211.bss;
+	while (bss) {
+		/* First entry must be AP MAC address; other entries will be
+		 * shown in the order they are added. */
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+		iwe.len = IW_EV_ADDR_LEN;
+		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.length = bss->ssid_len;
+		if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
+			iwe.u.data.length = IW_ESSID_MAX_SIZE;
+		iwe.u.data.flags = 1;
+		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+						  bss->ssid);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWMODE;
+		if (bss->capability & (WLAN_CAPABILITY_ESS |
+				       WLAN_CAPABILITY_IBSS)) {
+			if (bss->capability & WLAN_CAPABILITY_ESS)
+				iwe.u.mode = IW_MODE_MASTER;
+			else
+				iwe.u.mode = IW_MODE_ADHOC;
+			iwe.len = IW_EV_UINT_LEN;
+			current_ev = iwe_stream_add_event(current_ev, end_buf,
+							  &iwe,
+							  IW_EV_UINT_LEN);
+		}
+
+		if (bss->channel >= 1 && bss->channel <= FREQ_COUNT) {
+			memset(&iwe, 0, sizeof(iwe));
+			iwe.cmd = SIOCGIWFREQ;
+			SET_IWFREQ((&iwe.u.freq), bss->channel);
+			iwe.len = IW_EV_FREQ_LEN;
+			current_ev = iwe_stream_add_event(current_ev, end_buf,
+							  &iwe,
+							  IW_EV_FREQ_LEN);
+		}
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.qual = bss->last_rssi;
+		iwe.u.qual.level = 0;
+		iwe.u.qual.noise = 0;
+		iwe.len = IW_EV_QUAL_LEN;
+		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+						  IW_EV_QUAL_LEN);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWENCODE;
+		if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		iwe.u.data.length = 0;
+		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+		/* bss->ssid is a dummy pointer for 0-byte memcpy */
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+						  bss->ssid);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWRATE;
+		current_val = current_ev + IW_EV_LCP_LEN;
+		for (i = 0; i < sizeof(bss->supp_rates); i++) {
+			if (bss->supp_rates[i] == 0)
+				break;
+			/* Bit rate given in 500 kb/s units (+ 0x80) */
+			iwe.u.bitrate.value =
+				((bss->supp_rates[i] & 0x7f) * 500000);
+			current_val = iwe_stream_add_value(
+				current_ev, current_val, end_buf, &iwe,
+				IW_EV_PARAM_LEN);
+		}
+		if ((current_val - current_ev) > IW_EV_LCP_LEN)
+			current_ev = current_val;
+
+		bss = bss->next;
+	}
+	spin_unlock_bh(&priv->ieee80211.lock);
+
+	data->length = current_ev - extra;
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwessid(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *essid)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	data->flags = 1; /* active */
+	data->length = priv->ieee80211.ssid_len;
+	if (data->length > IW_ESSID_MAX_SIZE)
+		data->length = IW_ESSID_MAX_SIZE;
+	memset(essid, 0, IW_ESSID_MAX_SIZE);
+	memcpy(essid, priv->ieee80211.ssid, data->length);
+	return 0;
+}
+
+
+static int adm8211_ioctl_siwessid(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *essid)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int len;
+
+	if (data->flags == 0 || data->length < 1)
+		len = 0;
+	else
+		len = data->length - 1;
+	if (len > IEEE80211_MAX_SSID_LEN)
+		len = IEEE80211_MAX_SSID_LEN;
+
+	memcpy(priv->ieee80211.ssid, essid, len);
+	priv->ieee80211.ssid_len = len;
+	if (len)
+		priv->ieee80211.flags &= ~ANY_SSID;
+	else
+		priv->ieee80211.flags |= ANY_SSID;
+
+	if (dev->flags & IFF_UP)
+		ieee80211_start(&priv->ieee80211);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwnickn(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *nickn)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+        memset(nickn, 0, IW_ESSID_MAX_SIZE);
+        strncpy(nickn, priv->ieee80211.nick, IW_ESSID_MAX_SIZE);
+        return 0;
+}
+
+static int adm8211_ioctl_siwnickn(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *nickn)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int len = data->length;
+
+	if (len > IW_ESSID_MAX_SIZE)
+		len = IW_ESSID_MAX_SIZE;
+
+	memset(priv->ieee80211.nick, 0, IW_ESSID_MAX_SIZE);
+	strncpy(priv->ieee80211.nick, nickn, len);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwap(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct sockaddr *ap_addr, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	ap_addr->sa_family = ARPHRD_ETHER;
+	if (dev->flags & IFF_UP || !(priv->ieee80211.flags & PREF_BSSID_SET))
+		memcpy(ap_addr->sa_data, priv->ieee80211.bssid, ETH_ALEN);
+	else
+		memcpy(ap_addr->sa_data, priv->ieee80211.pref_bssid, ETH_ALEN);
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_siwap(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct sockaddr *ap_addr, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u8 *addr = ap_addr->sa_data;
+	memcpy(priv->ieee80211.pref_bssid, addr, ETH_ALEN);
+	if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5])
+		priv->ieee80211.flags |= PREF_BSSID_SET;
+	else
+		priv->ieee80211.flags &= ~PREF_BSSID_SET;
+
+	if (dev->flags & IFF_UP)
+		ieee80211_start(&priv->ieee80211);
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwrate(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int i, j;
+
+	if (vwrq->value <= 0)
+		return -EINVAL;
+
+	data->flags &= ~AUTO_RATE;
+	if (!vwrq->fixed) {
+		data->flags |= AUTO_RATE;
+
+		/* assumes last rate is the highest rate */
+		data->rate = data->supp_rates[data->num_supp_rates-1];
+	} else if (vwrq->value <= data->num_supp_rates) {
+		data->rate = data->supp_rates[vwrq->value-1];
+	} else {
+		i = 0;
+		j = (vwrq->value*2)/1000000;
+
+		/* make sure the rate given matches a supported rate */
+		while (i < data->num_supp_rates && data->supp_rates[i] != j)
+			i+=1;
+
+		/* give up if it doesn't */
+		if (i >= data->num_supp_rates)
+			return -EINVAL;
+
+		data->rate = j;
+	}
+
+	if (dev->flags & IFF_UP)
+		data->set_rate(dev);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwrate(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+
+	vwrq->fixed = !(data->flags & AUTO_RATE);
+	vwrq->value = (data->rate*1000000)/2;
+	return 0;
+}
+
+static int adm8211_ioctl_siwrts(struct net_device *dev,
+				struct iw_request_info *info,
+				struct iw_param *vwrq,
+				char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (vwrq->disabled)
+		priv->rts_thresh = 2347;
+	else if ((vwrq->value >= 0) && (vwrq->value <= 2346))
+		priv->rts_thresh = vwrq->value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwrts(struct net_device *dev,
+				struct iw_request_info *info,
+				struct iw_param *vwrq,
+				char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->value = priv->rts_thresh;
+	vwrq->disabled = (vwrq->value > 2346);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwfrag(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (vwrq->disabled)
+		priv->frag_thresh = 2346;
+	else if ((vwrq->value >= 256) && (vwrq->value <= 2346))
+		priv->frag_thresh = vwrq->value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwfrag(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->value = priv->frag_thresh;
+	vwrq->disabled = (vwrq->value >= 2346);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwtxpow(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->flags = IW_TXPOW_DBM;
+	if (priv->tx_power > 0x3F) {
+		vwrq->fixed = 0;
+		vwrq->value = priv->eeprom->tx_power[priv->ieee80211.channel-1];
+	} else {
+		vwrq->fixed = 1;
+		vwrq->value = priv->tx_power;
+	}
+
+	if (!vwrq->value)
+		vwrq->disabled = 1;
+	else
+		vwrq->disabled = 0;
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwtxpow(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (vwrq->disabled || !vwrq->fixed) {
+		priv->tx_power = 0x40;
+	} else if (vwrq->value >= 0 && vwrq->value <= 0x3F) {
+		priv->tx_power = vwrq->value;
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwretry(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT))
+		priv->retry_limit = vwrq->value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwretry(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->disabled = 0;
+	vwrq->value = priv->retry_limit;
+	vwrq->flags = IW_RETRY_LIMIT;
+
+	return 0;
+}
+
+/* WEP ioctls adapted from atmel driver */
+static int adm8211_ioctl_siwencode(struct net_device *dev,
+				   struct iw_request_info *info,
+				   struct iw_point *dwrq,
+				   char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+
+	if (dwrq->length > 0) {
+		if (dwrq->length > 13)
+			return -EINVAL;
+
+		/* Check the index (none -> use current) */
+		if (index < 0 || index >= 4)
+			index = data->default_key;
+		else
+			data->default_key = index;
+
+		if (dwrq->length > 5)
+			data->wep_data[index].len = 13;
+		else
+			data->wep_data[index].len = 5;
+
+		/* Check if the key is not marked as invalid */
+		if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
+			/* Cleanup */
+			memset(data->wep_data[index].key, 0, 13);
+			/* Copy the key in the driver */
+			memcpy(data->wep_data[index].key, extra, dwrq->length);
+		} else {
+			memset(data->wep_data[index].key, 0, 13);
+			data->wep_data[index].len = 0;
+		}
+
+		if (dev->flags & IFF_UP)
+			adm8211_write_wepkey(dev, index);
+	} else {
+		/* Do we want to just set the transmit key index ? */
+		if ( index >= 0 && index < 4 )
+			data->default_key = index;
+		else if (!dwrq->flags & IW_ENCODE_MODE)
+			return -EINVAL;
+	}
+
+	if (dwrq->flags & IW_ENCODE_DISABLED) {
+		data->flags &= ~WEP_ENABLED;
+		data->auth_algorithm = 0;
+		priv->ieee80211.capab &= ~WLAN_CAPABILITY_PRIVACY;
+	} else {
+		data->flags |= WEP_ENABLED;
+		if (dwrq->flags & IW_ENCODE_RESTRICTED)
+			data->flags |= WEP_RESTRICTED;
+		else
+			data->flags &= ~WEP_RESTRICTED;
+		data->auth_algorithm = 1;
+		priv->ieee80211.capab |= WLAN_CAPABILITY_PRIVACY;
+	}
+
+	if (dev->flags & IFF_UP)
+		adm8211_update_wep(dev);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwencode(struct net_device *dev,
+				   struct iw_request_info *info,
+				   struct iw_point *dwrq,
+				   char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	if (data->flags & WEP_ENABLED) {
+		if (data->flags & WEP_RESTRICTED)
+			dwrq->flags = IW_ENCODE_RESTRICTED;
+		else
+			dwrq->flags = IW_ENCODE_OPEN;
+	} else
+		 dwrq->flags = IW_ENCODE_DISABLED;
+
+	if (index < 0 || index >= 4)
+		index = data->default_key;
+
+	dwrq->flags |= index + 1;
+	dwrq->length = data->wep_data[index].len;
+	memset(extra, 0, 16);
+	memcpy(extra, data->wep_data[index].key, dwrq->length);
+
+	return 0;
+}
+
+static const iw_handler adm8211_handler[] =
+{
+	(iw_handler) NULL,				/* SIOCSIWCOMMIT */
+	(iw_handler) adm8211_ioctl_giwname,		/* SIOCGIWNAME */
+	(iw_handler) NULL,				/* SIOCSIWNWID */
+	(iw_handler) NULL,				/* SIOCGIWNWID */
+	(iw_handler) adm8211_ioctl_siwfreq,		/* SIOCSIWFREQ */
+	(iw_handler) adm8211_ioctl_giwfreq,		/* SIOCGIWFREQ */
+	(iw_handler) adm8211_ioctl_siwmode,		/* SIOCSIWMODE */
+	(iw_handler) adm8211_ioctl_giwmode,		/* SIOCGIWMODE */
+	(iw_handler) NULL,				/* SIOCSIWSENS */
+	(iw_handler) NULL,				/* SIOCGIWSENS */
+	(iw_handler) NULL /* not used */,		/* SIOCSIWRANGE */
+	(iw_handler) adm8211_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) adm8211_ioctl_siwap,		/* SIOCSIWAP */
+	(iw_handler) adm8211_ioctl_giwap,		/* SIOCGIWAP */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* SIOCGIWAPLIST */
+	(iw_handler) adm8211_ioctl_siwscan,		/* SIOCSIWSCAN */
+	(iw_handler) adm8211_ioctl_giwscan,		/* SIOCGIWSCAN */
+	(iw_handler) adm8211_ioctl_siwessid,		/* SIOCSIWESSID */
+	(iw_handler) adm8211_ioctl_giwessid,		/* SIOCGIWESSID */
+	(iw_handler) adm8211_ioctl_siwnickn,		/* SIOCSIWNICKN */
+	(iw_handler) adm8211_ioctl_giwnickn,		/* SIOCGIWNICKN */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) adm8211_ioctl_siwrate,		/* SIOCSIWRATE */
+	(iw_handler) adm8211_ioctl_giwrate,		/* SIOCGIWRATE */
+	(iw_handler) adm8211_ioctl_siwrts,		/* SIOCSIWRTS */
+	(iw_handler) adm8211_ioctl_giwrts,		/* SIOCGIWRTS */
+	(iw_handler) adm8211_ioctl_siwfrag,		/* SIOCSIWFRAG */
+	(iw_handler) adm8211_ioctl_giwfrag,		/* SIOCGIWFRAG */
+	(iw_handler) adm8211_ioctl_siwtxpow,		/* SIOCSIWTXPOW */
+	(iw_handler) adm8211_ioctl_giwtxpow,		/* SIOCGIWTXPOW */
+	(iw_handler) adm8211_ioctl_siwretry,		/* SIOCSIWRETRY */
+	(iw_handler) adm8211_ioctl_giwretry,		/* SIOCGIWRETRY */
+	(iw_handler) adm8211_ioctl_siwencode,		/* SIOCSIWENCODE */
+	(iw_handler) adm8211_ioctl_giwencode,		/* SIOCGIWENCODE */
+	(iw_handler) NULL,				/* SIOCSIWPOWER */
+	(iw_handler) NULL,				/* SIOCGIWPOWER */
+};
+
+static int adm8211_ioctl_sreg (struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu,
+			       char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int *i = (int *) extra;
+	u32 addr = i[0];
+	u32 reg = i[1];
+
+	if (addr >= 0 && addr <= 0x10C) {
+		printk(KERN_NOTICE "%s: writing 0x%x to 0x%x\n",
+		       dev->name, reg, addr);
+		iowrite32(reg, (void __iomem *)priv->map + addr);
+	} else {
+		printk(KERN_NOTICE "%s: setreg: register value out of range\n", dev->name);
+	}
+	return 0;
+}
+
+static int adm8211_ioctl_greg (struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu,
+			       char *extra)
+{
+        struct adm8211_priv *priv = netdev_priv(dev);
+        u32 addr = wrqu->param.value;
+
+	if (addr >= 0 && addr <= 0x10C) {
+	        printk(KERN_NOTICE "%s: value of register 0x%x is 0x%x\n",
+		       dev->name, addr, ioread32((void __iomem *)priv->map + addr));
+	} else {
+		printk(KERN_NOTICE "%s: getreg: register value out of range\n", dev->name);
+	}
+        return 0;
+}
+
+static int adm8211_ioctl_print_eeprom (struct net_device *dev,
+				       struct iw_request_info *info,
+				       union iwreq_data *wrqu,
+				       char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i, bl;
+
+	if (priv->eeprom) {
+	printk(KERN_NOTICE "%s: eeprom dump:\n", dev->name);
+
+	for (bl = 0; bl < priv->eeprom_len; bl += 16) {
+		printk(KERN_NOTICE "%s:", dev->name);
+		for (i = 0; i<16; i+=1)
+			printk(" %02x", ((u8 *)priv->eeprom)[bl+i]);
+		printk("\n");
+	}
+
+	} else
+		printk(KERN_NOTICE "%s: no eeprom data\n", dev->name);
+
+	return 0;
+}
+
+static int adm8211_ioctl_set_country (struct net_device *dev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu,
+				      char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int code = wrqu->param.value;
+
+	if (code >= ARRAY_SIZE(cranges) || code < 0)
+		return -EINVAL;
+
+	/* potential, but unlikely race, I think. need to check */
+	priv->ieee80211.chan_range = cranges[code];
+	printk(KERN_DEBUG "%s: Channel range: %d-%d\n",
+	       dev->name, (int)priv->ieee80211.chan_range.min, (int)priv->ieee80211.chan_range.max);
+
+	return 0;
+}
+
+static int adm8211_ioctl_set_antpower(struct net_device *dev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu,
+				      char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (wrqu->param.value > 0x3F) {
+		priv->ant_power = 0x40;
+	} else if (wrqu->param.value >= 0 && wrqu->param.value <= 0x3F) {
+		priv->ant_power = wrqu->param.value;
+	} else
+		return -EINVAL;
+
+	adm8211_rf_set_channel(dev, priv->ieee80211.channel);
+	return 0;
+}
+
+static int adm8211_ioctl_set_lpfcutoff(struct net_device *dev,
+				       struct iw_request_info *info,
+				       union iwreq_data *wrqu,
+				       char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (wrqu->param.value >= 0 && wrqu->param.value <= 0xFF) {
+		priv->lpf_cutoff = wrqu->param.value;
+	} else
+		return -EINVAL;
+
+	adm8211_rf_set_channel(dev, priv->ieee80211.channel);
+	return 0;
+}
+
+static int adm8211_ioctl_set_lnagsthresh(struct net_device *dev,
+					 struct iw_request_info *info,
+					 union iwreq_data *wrqu,
+					 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (wrqu->param.value >= 0 && wrqu->param.value <= 0xFF) {
+		priv->lnags_threshold = wrqu->param.value;
+	} else
+		return -EINVAL;
+
+	adm8211_rf_set_channel(dev, priv->ieee80211.channel);
+	return 0;
+}
+
+static int adm8211_ioctl_print_radio (struct net_device *dev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu,
+				      char *extra)
+{
+        struct adm8211_priv *priv = netdev_priv(dev);
+	int channel = priv->ieee80211.channel - 1;
+
+	printk(KERN_DEBUG "%s: Antenna Power: %d (%d) \n",
+			dev->name, priv->ant_power,
+			priv->eeprom->antenna_power[channel]);
+	printk(KERN_DEBUG "%s: LPF Cutoff: %d (%d)\n",
+			dev->name, priv->lpf_cutoff,
+			priv->eeprom->lpf_cutoff[channel]);
+	printk(KERN_DEBUG "%s: LNAGS Threshold: %d (%d)\n",
+			dev->name, priv->lnags_threshold,
+			priv->eeprom->lnags_threshold[channel]);
+	
+	return 0;
+}
+
+static const iw_handler adm8211_private_handler[] =
+{							/* SIOCIWFIRSTPRIV + */
+	(iw_handler) adm8211_ioctl_sreg,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_greg,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_print_eeprom,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_print_radio,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_country,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_antpower,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_lpfcutoff,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_lnagsthresh,
+};
+
+static const struct iw_priv_args adm8211_priv[] = {
+	{SIOCIWFIRSTPRIV, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setreg" },
+	{0},
+	{SIOCIWFIRSTPRIV+2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "getreg" },
+	{0},
+	{SIOCIWFIRSTPRIV+4, IW_PRIV_TYPE_NONE, 0, "print_eeprom" },
+	{0},
+	{SIOCIWFIRSTPRIV+6, IW_PRIV_TYPE_NONE, 0, "print_radio" },
+	{0},
+	{SIOCIWFIRSTPRIV+8, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_country" },
+	{0},
+	{SIOCIWFIRSTPRIV+10, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_antpower" },
+	{0},
+	{SIOCIWFIRSTPRIV+12, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_lpfcutoff" },
+	{0},
+	{SIOCIWFIRSTPRIV+14, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_lnagsthresh" },
+};
+
+const struct iw_handler_def adm8211_iw_handler_def =
+{
+	.num_standard   = ARRAY_SIZE(adm8211_handler),
+	.num_private    = ARRAY_SIZE(adm8211_private_handler),
+	.num_private_args = ARRAY_SIZE(adm8211_priv),
+	.standard       = (iw_handler *) adm8211_handler,
+	.private        = (iw_handler *) adm8211_private_handler,
+	.private_args   = (struct iw_priv_args *) adm8211_priv,
+};
+
diff -Naur linux-2.6.12-rc3-no1/adm8211/adm8211_ioctl.h no-sources-new/adm8211/adm8211_ioctl.h
--- linux-2.6.12-rc3-no1/adm8211/adm8211_ioctl.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/adm8211_ioctl.h	2004-10-30 16:24:53.000000000 +0000
@@ -0,0 +1,6 @@
+#ifndef ADM8211_IOCTL_H
+#define ADM8211_IOCTL_H
+
+extern const struct iw_handler_def adm8211_iw_handler_def;
+
+#endif /* ADM8211_IOCTL_H */
diff -Naur linux-2.6.12-rc3-no1/adm8211/avs_caphdr.h no-sources-new/adm8211/avs_caphdr.h
--- linux-2.6.12-rc3-no1/adm8211/avs_caphdr.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/avs_caphdr.h	2004-11-16 22:22:42.000000000 +0000
@@ -0,0 +1,21 @@
+#ifndef AVS_CAPHDR_H
+#define AVS_CAPHDR_H
+
+struct avs_caphdr {
+	__be32 version;
+	__be32 length;
+	__be64 mactime;
+	__be64 hosttime;
+	__be32 phytype;
+	__be32 channel;
+	__be32 datarate;
+	__be32 antenna;
+	__be32 priority;
+	__be32 ssi_type;
+	__be32 ssi_signal;	/* signed */
+	__be32 ssi_noise;	/* signed */
+	__be32 preamble;
+	__be32 encoding;
+} __attribute__ ((packed));
+
+#endif /* AVS_CAPHDR_H */
diff -Naur linux-2.6.12-rc3-no1/adm8211/ieee80211.c no-sources-new/adm8211/ieee80211.c
--- linux-2.6.12-rc3-no1/adm8211/ieee80211.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/ieee80211.c	2005-02-26 23:42:24.000000000 +0000
@@ -0,0 +1,1663 @@
+/*
+ * IEEE 802.11 station / management functionality
+ *
+ * Copyright (c) 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004-2005, Michael Wu <flamingice@sourmilk.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/wireless.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/types.h>
+#include <asm/delay.h>
+#include <asm/scatterlist.h>
+#include <asm/div64.h>
+
+#include "ieee80211.h"
+
+#define BEACON_SKIP 2
+
+#define IEEE80211_SCAN_INTERVAL (5 * HZ)
+#define IEEE80211_SCAN_LISTEN (HZ / 10)
+#define IEEE80211_AUTH_TIMEOUT (1 * HZ)
+#define IEEE80211_AUTH_MAX_TRIES 3
+#define IEEE80211_ASSOC_TIMEOUT (1 * HZ)
+#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_MONITORING_INTERVAL (30 * HZ)
+#define IEEE80211_LINKCHECK_INTERVAL (3 * HZ)
+
+static void ieee80211_timer(unsigned long ptr);
+static void ieee80211_associate(struct ieee80211_data *data);
+
+ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
+				struct ieee802_11_elems *elems)
+{
+	size_t left = len;
+	u8 *pos = start;
+	int unknown = 0;
+
+	memset(elems, 0, sizeof(*elems));
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		left -= 2;
+
+		if (elen > left) {
+#if 0
+			if (net_ratelimit())
+				printk(KERN_DEBUG "IEEE 802.11 element parse "
+				       "failed (id=%d elen=%d left=%d)\n",
+				       id, elen, left);
+#endif
+			return ParseFailed;
+		}
+
+		switch (id) {
+		case WLAN_EID_SSID:
+			elems->ssid = pos;
+			elems->ssid_len = elen;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			elems->supp_rates = pos;
+			elems->supp_rates_len = elen;
+			break;
+		case WLAN_EID_FH_PARAMS:
+			elems->fh_params = pos;
+			elems->fh_params_len = elen;
+			break;
+		case WLAN_EID_DS_PARAMS:
+			elems->ds_params = pos;
+			elems->ds_params_len = elen;
+			break;
+		case WLAN_EID_CF_PARAMS:
+			elems->cf_params = pos;
+			elems->cf_params_len = elen;
+			break;
+		case WLAN_EID_TIM:
+			elems->tim = pos;
+			elems->tim_len = elen;
+			break;
+		case WLAN_EID_IBSS_PARAMS:
+			elems->ibss_params = pos;
+			elems->ibss_params_len = elen;
+			break;
+		case WLAN_EID_CHALLENGE:
+			elems->challenge = pos;
+			elems->challenge_len = elen;
+			break;
+		default:
+#if 0
+			printk(KERN_DEBUG "IEEE 802.11 element parse ignored "
+				      "unknown element (id=%d elen=%d)\n",
+				      id, elen);
+#endif
+			unknown++;
+			break;
+		}
+
+		left -= elen;
+		pos += elen;
+	}
+
+	if (left)
+		return ParseFailed;
+
+	return unknown ? ParseUnknown : ParseOK;
+}
+
+
+static void ieee80211_dump_ssid(u8 *ssid, size_t ssid_len)
+{
+	int i;
+
+	for (i = 0; i < ssid_len; i++)
+		printk((ssid[i] >= 32 && ssid[i] < 127) ?
+		       "%c" : "<%02x>", ssid[i]);
+}
+
+static char * ieee80211_status_code(int status)
+{
+	switch (status) {
+	case WLAN_STATUS_SUCCESS:
+		return "Success";
+	case WLAN_STATUS_UNSPECIFIED_FAILURE:
+		return "Unspecified failure";
+	case WLAN_STATUS_CAPS_UNSUPPORTED:
+		return "Capabilities unsupported";
+	case WLAN_STATUS_REASSOC_NO_ASSOC:
+		return "Reassociation failure; no association";
+	case WLAN_STATUS_ASSOC_DENIED_UNSPEC:
+		return "Association denied";
+	case WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG:
+		return "Authentication algorithm not supported";
+	case WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION:
+		return "Unknown authentication transaction";
+	case WLAN_STATUS_CHALLENGE_FAIL:
+		return "Challenge failed";
+	case WLAN_STATUS_AUTH_TIMEOUT:
+		return "Authentication timeout";
+	case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+		return "AP unable to handle new STA";
+	case WLAN_STATUS_ASSOC_DENIED_RATES:
+		return "Association denied: Rates not supported";
+
+	/* 802.11b */
+	case WLAN_STATUS_ASSOC_DENIED_NOSHORT:
+		return "Association denied: Short preamble not supported";
+	case WLAN_STATUS_ASSOC_DENIED_NOPBCC:
+		return "Association denied: PBCC not supported";
+	case WLAN_STATUS_ASSOC_DENIED_NOAGILITY:
+		return "Association denied: Agility not supported";
+	default:
+		return "Unknown status code";
+	}
+}
+
+
+static void ieee80211_set_associated(struct ieee80211_data *data, int assoc)
+{
+	if ((data->flags & ASSOCIATED) == (assoc ? ASSOCIATED : 0))
+		return;
+
+	if (assoc)
+		data->flags |= ASSOCIATED;
+	else
+		data->flags &= ~ASSOCIATED;
+
+	if (data->link_change)
+		data->link_change(data->dev, assoc);
+}
+
+
+struct ieee80211_bss * ieee80211_get_bss(struct ieee80211_data *data, u8 *addr)
+{
+	struct ieee80211_bss *bss;
+
+	bss = data->bss;
+	while (bss) {
+		if (memcmp(bss->bssid, addr, ETH_ALEN) == 0)
+			return bss;
+		bss = bss->next;
+	}
+	return NULL;
+}
+
+
+static int ieee80211_bss_score(struct ieee80211_data *data,
+			       struct ieee80211_bss *bss)
+{
+	int score, i;
+
+	score = bss->last_rssi;
+	score -= 50 * bss->num_auth_fail;
+	score -= 50 * bss->num_assoc_fail;
+
+	/* detect hidden SSIDs and avoid them */
+	bss->hidden_ssid = 1;
+	for (i = 0; i < bss->ssid_len; i += 1)
+		if (bss->ssid[i] != '\0') {
+			bss->hidden_ssid = 0;
+			break;
+		}
+	
+	if (bss->hidden_ssid)
+		score -= 50;
+
+	if (data->pref_channel == bss->channel)
+		score += 100;
+
+	return score;
+}
+
+/* removes all bss */
+static void ieee80211_free_bss(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss, *prev;
+	unsigned long flags;
+
+	/* need irqsave as opposed to bh so this can be run during suspend */
+	spin_lock_irqsave(&data->lock, flags);
+
+	bss = data->bss;
+	data->bss = NULL;
+	while (bss) {
+		prev = bss;
+		bss = bss->next;
+		kfree(prev);
+	}
+
+	spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static int ieee80211_select_bssid(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss, *prev = NULL, *selected = NULL;
+	int best_score = 0, score;
+	u64 oldest_timestamp = 0;
+
+	spin_lock_bh(&data->lock);
+	bss = data->bss;
+
+	/* Select first BSS that matches with current configuration */
+	while (bss) {
+		score = ieee80211_bss_score(data, bss);
+		if (score < -250)
+			goto skip;
+
+		if (data->mode == IEEE80211_MANAGED && 
+		    ((bss->capability & WLAN_CAPABILITY_IBSS) || 
+		    !(bss->capability & WLAN_CAPABILITY_ESS) ))
+			goto skip;
+		if (data->mode == IEEE80211_ADHOC &&
+		    ((bss->capability & WLAN_CAPABILITY_ESS) ||
+		    !(bss->capability & WLAN_CAPABILITY_IBSS) ))
+			goto skip;
+
+		printk(KERN_DEBUG "%s: CHAN=%d BSSID=" MACSTR " SSID=",
+		       data->dev->name, bss->channel, MAC2STR(bss->bssid));
+		ieee80211_dump_ssid(bss->ssid, bss->ssid_len);
+		printk(" RSSI=%d num=%lu/%lu score=%d\n", bss->last_rssi,
+		       bss->num_beacon, bss->num_proberesp, score);
+
+		if (data->flags & PREF_BSSID_SET &&
+		    memcmp(bss->bssid, data->pref_bssid, ETH_ALEN) == 0) {
+			selected = bss;
+			break;
+		} else if (data->flags & ANY_SSID || bss->ssid_len == 0 ||
+			  (bss->ssid_len == data->ssid_len && 
+			  (bss->hidden_ssid || !memcmp(bss->ssid, data->ssid, data->ssid_len)))) {
+			if (data->mode == IEEE80211_MANAGED &&
+			    (!selected || score > best_score) ) {
+				best_score = score;
+				selected = bss;
+			} else if (data->mode == IEEE80211_ADHOC &&
+				   bss->timestamp > oldest_timestamp) {
+				oldest_timestamp = bss->timestamp;
+				selected = bss;
+			}
+		}
+
+		skip:
+		prev = bss;
+		bss = bss->next;
+	}
+
+	if (selected) {
+		if (memcmp(data->bssid, selected->bssid, ETH_ALEN) != 0) {
+			printk(KERN_DEBUG "%s: new BSSID " MACSTR "\n",
+			       data->dev->name, MAC2STR(selected->bssid));
+			ieee80211_set_associated(data, 0);
+		}
+		memcpy(data->bssid, selected->bssid, ETH_ALEN);
+		if (selected->hidden_ssid)
+			data->flags |= HIDDEN_SSID;
+		else {
+			data->flags &= ~HIDDEN_SSID;
+			data->ssid_len = selected->ssid_len;
+			memcpy(data->ssid, selected->ssid, selected->ssid_len);
+		}
+		data->channel = selected->channel;
+		data->beacon_interval = (selected->interval ? selected->interval : 100);
+		data->timestamp = selected->timestamp;
+	}
+
+	spin_unlock_bh(&data->lock);
+
+	return selected ? 0 : -1;
+}
+
+static void ieee80211_sync_ibss(struct ieee80211_data *data)
+{
+	u64 tbttpre;
+	u32 beacon = data->beacon_interval << 10;
+
+	tbttpre  = data->timestamp;
+	tbttpre  = data->timestamp - do_div(tbttpre, beacon);
+	tbttpre += (beacon*BEACON_SKIP) << 10;			/* skip a few beacons in case we're slow */
+	tbttpre -= 20 << 10;				/* wake up early */
+
+	tbttpre >>= 10;					/* convert everything to TUs */
+
+	data->set_tbtt(data->dev, (u16)(tbttpre & 0xFFFF));
+}
+
+static void ieee80211_send_auth(struct ieee80211_data *data, struct ieee80211_bss *bss)
+{
+	struct sk_buff *skb;
+	union ieee80211_mgmt_payload *mgmt;
+	u8 *pos;
+	__le16 fc;
+
+	if (!data->tx)
+		return;
+
+	skb = dev_alloc_skb(6 + 2 + WLAN_AUTH_CHALLENGE_LEN + 8);
+	if (skb == NULL) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
+		       "frame\n", data->dev->name);
+		return;
+	}
+
+	mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 6);
+	fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+			  WLAN_FC_STYPE_AUTH);
+
+	bss->auth_state+=1;
+	if (bss->auth_alg == WLAN_AUTH_OPEN) {
+		mgmt->auth.auth_alg = cpu_to_le16(WLAN_AUTH_OPEN);
+		mgmt->auth.auth_transaction = cpu_to_le16(bss->auth_state);
+		mgmt->auth.status_code = 0;
+
+		if (bss->auth_state != 1) {
+			printk(KERN_DEBUG "%s: Unable to handle authentication transaction: %d\n",
+			       data->dev->name, bss->auth_state);
+			goto fail;
+		}
+	} else if (bss->auth_alg == WLAN_AUTH_SHARED_KEY) {
+		mgmt->auth.auth_alg = cpu_to_le16(WLAN_AUTH_SHARED_KEY);
+		mgmt->auth.auth_transaction = cpu_to_le16(bss->auth_state);
+		mgmt->auth.status_code = 0;
+
+		switch (bss->auth_state) {
+		case 1:
+			break;
+		case 3:
+			pos = skb_put(skb, 2 + WLAN_AUTH_CHALLENGE_LEN);
+			*pos++ = WLAN_EID_CHALLENGE;
+			*pos++ = WLAN_AUTH_CHALLENGE_LEN;
+			if (bss->challenge) {
+				memcpy(pos, bss->challenge, WLAN_AUTH_CHALLENGE_LEN);
+			} else {
+				printk(KERN_DEBUG "%s: No challenge to return\n", data->dev->name);
+				goto fail;
+			}
+
+			fc |= cpu_to_le16(WLAN_FC_ISWEP);
+			break;
+		default:
+			printk(KERN_DEBUG "%s: Unable to handle authentication transaction: %d\n",
+			       data->dev->name, bss->auth_state);
+			goto fail;
+		}
+	} else {
+		printk(KERN_DEBUG "%s: Unknown authentication algorithm: %d\n", data->dev->name, bss->auth_alg);
+		goto fail;
+	}
+
+	printk(KERN_DEBUG "%s: TX authentication (alg=%d transaction=%d)\n",
+	       data->dev->name, bss->auth_alg, bss->auth_state);
+
+	data->tx(data->dev, skb, fc, bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE, bss->bssid);
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static void ieee80211_authenticate(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss;
+
+	data->auth_tries++;
+	if (data->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: authentication with AP " MACSTR
+		       " CHAN=%d timed out\n",
+		       data->dev->name, MAC2STR(data->bssid), data->channel);
+
+		spin_lock_bh(&data->lock);
+		bss = ieee80211_get_bss(data, data->bssid);
+		if (bss)
+			bss->num_auth_fail++;
+		spin_unlock_bh(&data->lock);
+		ieee80211_start_scan(data);
+		return;
+	}
+
+	if (data->set_channel)
+		data->set_channel(data->dev, data->channel);
+	if (data->set_bssid)
+		data->set_bssid(data->dev, data->bssid);
+	if (data->set_rate)
+		data->set_rate(data->dev);
+	if (data->set_interval)
+		data->set_interval(data->dev);
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, data->bssid);
+	if (bss->auth_state != 5) {
+		data->state = IEEE80211_AUTHENTICATE;
+		printk(KERN_DEBUG "%s: authenticate with AP " MACSTR " CHAN=%d\n",
+		       data->dev->name, MAC2STR(data->bssid), data->channel);
+
+		ieee80211_set_associated(data, 0);
+		bss->auth_state = 0;
+		ieee80211_send_auth(data, bss);
+	}
+	spin_unlock_bh(&data->lock);
+
+	if (data->state != IEEE80211_AUTHENTICATE)
+		ieee80211_associate(data);
+
+	mod_timer(&data->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+}
+
+static void ieee80211_send_assoc_req(struct ieee80211_data *data)
+{
+	struct sk_buff *skb;
+	union ieee80211_mgmt_payload *mgmt;
+	struct ieee80211_bss *bss;
+	u8 *pos;
+	__le16 fc;
+	int i = 0;
+	int reassoc = 0;
+
+	if (!data->tx)
+		return;
+
+	skb = dev_alloc_skb(2 + 2 + 2 + data->ssid_len + 2 + data->num_supp_rates);
+	if (skb == NULL) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc req "
+		       "frame\n", data->dev->name);
+		return;
+	}
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, data->bssid);
+
+	/* TODO: check how prev_bssid_set is suppose to work */
+	if (data->flags & PREV_BSSID_SET) {
+		mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 10);
+		fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+				  WLAN_FC_STYPE_REASSOC_REQ);
+		mgmt->reassoc_req.capab_info = cpu_to_le16(bss->capability);
+		mgmt->reassoc_req.listen_interval =
+			cpu_to_le16(data->listen_interval);
+		memcpy(mgmt->reassoc_req.current_ap, data->prev_bssid,
+		       ETH_ALEN);
+		reassoc = 1;
+	} else {
+		mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 4);
+		fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+				  WLAN_FC_STYPE_ASSOC_REQ);
+		mgmt->assoc_req.capab_info = cpu_to_le16(bss->capability);
+		mgmt->assoc_req.listen_interval =
+			cpu_to_le16(data->listen_interval);
+	}
+
+	/* SSID */
+	pos = skb_put(skb, 2 + data->ssid_len);
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = data->ssid_len;
+	memcpy(pos, data->ssid, data->ssid_len);
+
+	pos = skb_put(skb, 2 + data->num_supp_rates);
+	/* All supported rates */
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = data->num_supp_rates;
+	while (i++ < data->num_supp_rates)
+		*pos++ = data->supp_rates[i-1];
+
+	printk(KERN_DEBUG "%s: TX %sssocReq (capab=0x%x)\n",
+	       data->dev->name, reassoc ? "Rea" : "A", bss->capability);
+
+	data->tx(data->dev, skb, fc, bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE, data->bssid);
+	spin_unlock_bh(&data->lock);
+}
+
+static void ieee80211_associated(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss, *tmp, *prev = NULL;
+
+	/* TODO: start monitoring current AP signal quality and number of
+	 * missed beacons. Scan other channels every now and then and search
+	 * for better APs. */
+
+	spin_lock_bh(&data->lock);
+
+	/* remove expired BSSes */
+	bss = data->bss;
+	while (bss) {
+		if (time_after(jiffies, bss->last_rx + IEEE80211_MONITORING_INTERVAL) &&
+		     memcmp(data->bssid, bss->bssid, ETH_ALEN)) {
+			if (prev)
+				prev->next = bss->next;
+			else
+				data->bss = bss->next;
+
+			tmp = bss;
+			bss = bss->next;
+			kfree(tmp);
+		} else {
+			prev = bss;
+			bss = bss->next;
+		}
+	}
+
+	spin_unlock_bh(&data->lock);
+
+	if (data->state != IEEE80211_ASSOCIATED) {
+		data->state = IEEE80211_ASSOCIATED;
+		netif_wake_queue(data->dev);
+		mod_timer(&data->timer, jiffies + IEEE80211_LINKCHECK_INTERVAL);
+	} else {
+		if (data->flags & LINK_ON || data->mode == IEEE80211_MANAGED)
+			mod_timer(&data->timer, jiffies + IEEE80211_MONITORING_INTERVAL);
+		else
+			ieee80211_start_scan(data);
+	}
+}
+
+static void ieee80211_associate(struct ieee80211_data *data)
+{
+	if (data->flags & ASSOCIATED) {
+		ieee80211_associated(data);
+		return;
+	}
+
+	data->assoc_tries++;
+	if (data->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
+		struct ieee80211_bss *bss;
+		printk(KERN_DEBUG "%s: association with AP " MACSTR
+		       " CHAN=%d timed out\n",
+		       data->dev->name, MAC2STR(data->bssid), data->channel);
+		spin_lock_bh(&data->lock);
+		bss = ieee80211_get_bss(data, data->bssid);
+		if (bss) {
+			bss->num_assoc_fail++;
+			if (bss->num_assoc_fail > 6) {
+				spin_unlock_bh(&data->lock);
+				ieee80211_free_bss(data);
+				/* yuck */
+				spin_lock_bh(&data->lock);
+			}
+		}
+		spin_unlock_bh(&data->lock);
+		ieee80211_start_scan(data);
+		return;
+	}
+
+	data->state = IEEE80211_ASSOCIATE;
+	printk(KERN_DEBUG "%s: associate with AP " MACSTR "\n",
+	       data->dev->name, MAC2STR(data->bssid));
+
+	ieee80211_send_assoc_req(data);
+
+	mod_timer(&data->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
+}
+
+static void ieee80211_send_probe_req(struct ieee80211_data *data)
+{
+	struct sk_buff *skb;
+	union ieee80211_mgmt_payload *mgmt;
+	u8 *pos;
+	__le16 fc;
+	u8 dst[ETH_ALEN];
+	int i = 0;
+
+	if (!data->tx)
+		return;
+
+	skb = dev_alloc_skb(2 + 2 + data->num_supp_rates);
+	if (skb == NULL) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+		       "request\n", data->dev->name);
+		return;
+	}
+
+	mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 2 + 2 + data->num_supp_rates);
+	memset(mgmt, 0, 2 + 2 + data->num_supp_rates);
+	fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+			  WLAN_FC_STYPE_PROBE_REQ);
+	memset(dst, ~0, ETH_ALEN);
+	pos = mgmt->probe_req.variable;
+
+	/* Broadcast SSID */
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = 0;
+
+	/* All supported rates */
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = data->num_supp_rates;
+	while (i++ < data->num_supp_rates)
+		*pos++ = data->supp_rates[i-1];
+
+	spin_lock_bh(&data->lock);
+	data->tx(data->dev, skb, fc, 0, dst);
+	spin_unlock_bh(&data->lock);
+}
+
+
+static void ieee80211_scan(struct ieee80211_data *data)
+{
+	data->state = IEEE80211_SCAN;
+	if (data->channel == 0) {
+		printk(KERN_DEBUG "%s: scanning..\n", data->dev->name);
+		data->channel = data->chan_range.min;
+	} else
+		data->channel++;
+	
+	if (data->channel > data->chan_range.max) {
+	if (data->mode == IEEE80211_MANAGED) {
+		if (ieee80211_select_bssid(data) == 0) {
+			ieee80211_authenticate(data);
+			return;
+		} else {
+			ieee80211_free_bss(data);
+
+			data->channel = 0;
+			mod_timer(&data->timer,
+				  jiffies + IEEE80211_SCAN_INTERVAL);
+			return;
+		}
+	} else if (data->mode == IEEE80211_ADHOC) {
+		if (ieee80211_select_bssid(data)) {
+			random_ether_addr(data->bssid);
+			data->beacon_interval = 100;
+			if (data->pref_channel)
+				data->channel = data->pref_channel;
+			else
+				data->channel = data->chan_range.min;
+
+			if (!data->ssid) {
+				printk(KERN_WARNING "%s: No SSID set. SSID set to \"default\"\n", data->dev->name);
+				memcpy(data->ssid, "default", 7);
+				data->ssid_len = 7;
+			}
+			printk(KERN_DEBUG "%s: No matching adhoc sta found. Creating IBSS " MACSTR " CHAN=%d\n",
+			       data->dev->name, MAC2STR(data->bssid), data->channel);
+			data->timestamp = data->get_tsft(data->dev);
+		} else {
+			printk(KERN_DEBUG "%s: joining IBSS " MACSTR " CHAN=%d\n",
+			       data->dev->name, MAC2STR(data->bssid), data->channel);
+		}
+
+		if (data->set_rate)
+			data->set_rate(data->dev);
+		if (data->set_interval)
+			data->set_interval(data->dev);
+		if (data->set_bssid)
+			data->set_bssid(data->dev, data->bssid);
+		if (data->set_ssid)
+			data->set_ssid(data->dev, data->ssid, data->ssid_len);
+		if (data->set_channel)
+			data->set_channel(data->dev, data->channel);
+		ieee80211_sync_ibss(data);
+
+		data->flags |= ASSOCIATED;
+		ieee80211_associated(data);
+		return;
+	}
+	}
+
+	if (data->set_channel)
+		data->set_channel(data->dev, data->channel);
+
+	ieee80211_send_probe_req(data);
+
+	mod_timer(&data->timer, jiffies + IEEE80211_SCAN_LISTEN);
+}
+
+
+void ieee80211_start_scan(struct ieee80211_data *data)
+{
+	static const u8 scan_bssid[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+	/* don't do anything if we're already scanning */
+	if (data->state == IEEE80211_SCAN)
+		return;
+
+	data->channel = 0;
+	data->auth_tries = data->assoc_tries = 0;
+
+	/* Set BSSID to ff:ff:ff:ff:ff:ff for active scanning */
+	if (data->set_bssid)
+		data->set_bssid(data->dev, (u8 *)scan_bssid);
+
+	data->beacon_interval = 0;
+	if (data->set_interval)
+		data->set_interval(data->dev);
+
+	if (in_interrupt()) {
+		data->state = IEEE80211_SCAN;
+		mod_timer(&data->timer, jiffies + IEEE80211_SCAN_LISTEN);
+	} else {
+		ieee80211_scan(data);
+	}
+}
+
+void ieee80211_start(struct ieee80211_data *data)
+{
+	if (data->mode == IEEE80211_MANAGED || data->mode == IEEE80211_ADHOC)
+		ieee80211_start_scan(data);
+}
+
+int ieee80211_init(struct ieee80211_data *data)
+{
+	init_timer(&data->timer);
+	data->timer.data = (unsigned long) data;
+	data->timer.function = ieee80211_timer;
+
+	spin_lock_init(&data->lock);
+
+	data->listen_interval = 10;
+	data->mode = IEEE80211_MANAGED;
+	data->state = IEEE80211_STOP;
+	data->flags = AUTO_RATE | ANY_SSID;
+
+	if (!data->supp_rates)
+		data->supp_rates = (u8 *)IEEE80211_B_RATES;
+
+	data->num_supp_rates=strlen(data->supp_rates);
+	data->rate = data->supp_rates[data->num_supp_rates-1];
+
+	data->tfm = crypto_alloc_tfm("arc4", 0);
+	if (!data->tfm)
+		return -1;
+
+	get_random_bytes(&data->wep_data[0].iv, 4);
+	get_random_bytes(&data->wep_data[1].iv, 4);
+	get_random_bytes(&data->wep_data[2].iv, 4);
+	get_random_bytes(&data->wep_data[3].iv, 4);
+
+	return 0;
+}
+
+void ieee80211_deinit(struct ieee80211_data *data)
+{
+	crypto_free_tfm(data->tfm);
+
+	return;
+}
+
+void ieee80211_stop(struct ieee80211_data *data)
+{
+	int i;
+
+	del_timer_sync(&data->timer);
+	data->state = IEEE80211_STOP;
+
+	ieee80211_free_bss(data);
+	for (i = 0; i < IEEE80211_FRAG_CACHE_SIZE; i+=1) {
+		if (data->frag_cache[i].skb) {
+			dev_kfree_skb(data->frag_cache[i].skb);
+			data->frag_cache[i].skb = NULL;
+		}
+	}
+
+	memset(data->bssid, 0, ETH_ALEN);
+	if (data->set_ssid)
+		data->set_ssid(data->dev, data->ssid, 0);
+	if (data->set_bssid)
+		data->set_bssid(data->dev, data->bssid);
+}
+
+/* TODO: merge with the only function that calls this, or reduce the number of arguments */
+static void ieee80211_add_bss(struct ieee80211_data *data,
+			      u8 *bssid, struct ieee802_11_elems *elems,
+			      int channel, struct ieee80211_mgmt *mgmt,
+			      int rssi, u8 rate, int beacon)
+{
+	struct ieee80211_bss *bss;
+	int i;
+
+	spin_lock_bh(&data->lock);
+
+	bss = data->bss;
+
+	while (bss != NULL &&
+	       (memcmp(bss->bssid, bssid, ETH_ALEN) != 0 /* ||
+		bss->ssid_len != elems->ssid_len ||
+		memcmp(bss->ssid, elems->ssid, elems->ssid_len) != 0 */ ))
+		bss = bss->next;
+
+	if (bss == NULL) {
+/*		printk(KERN_DEBUG "%s: new BSS - CHAN=%d BSSID=" MACSTR
+		       " SSID=",
+		       data->dev->name, channel, MAC2STR(bssid));
+		ieee80211_dump_ssid(ssid, ssid_len);
+		printk("\n");*/
+
+		bss = (struct ieee80211_bss *)
+			kmalloc(sizeof(*bss), GFP_ATOMIC);
+		if (bss == NULL) {
+			spin_unlock_bh(&data->lock);
+			printk(KERN_DEBUG "%s: failed to allocate BSS info "
+			       "structure\n", data->dev->name);
+			return;
+		}
+		memset(bss, 0, sizeof(*bss));
+		memcpy(bss->bssid, bssid, ETH_ALEN);
+		bss->last_seq = __constant_cpu_to_le16(~0);
+		bss->next = data->bss;
+		data->bss = bss;
+
+		if (mgmt->u.beacon.capab_info & __cpu_to_le16(WLAN_CAPABILITY_PRIVACY))
+			bss->auth_alg = WLAN_AUTH_SHARED_KEY;
+		else
+			bss->auth_alg = WLAN_AUTH_OPEN;
+
+		if (data->state == IEEE80211_ADHOC &&
+		    data->timestamp < bss->timestamp &&
+		    data->get_tsft(data->dev) < bss->timestamp) {
+			printk(KERN_DEBUG "%s: found older BSS - BSSID=" MACSTR "\n",
+					data->dev->name, MAC2STR(bssid));
+		}
+
+	}
+
+	if (beacon)
+		bss->num_beacon++;
+	else
+		bss->num_proberesp++;
+
+	memcpy(bss->ssid, elems->ssid, elems->ssid_len);
+	bss->ssid_len = elems->ssid_len;
+	if (elems->supp_rates) {
+		memset(bss->supp_rates, 0, IEEE80211_MAX_SUPP_RATES);
+		memcpy(bss->supp_rates, elems->supp_rates,
+		       elems->supp_rates_len > IEEE80211_MAX_SUPP_RATES ?
+		       IEEE80211_MAX_SUPP_RATES : elems->supp_rates_len);
+		for (i = data->num_supp_rates; i > 0; i-=1) {
+			if (strchr(bss->supp_rates, data->supp_rates[i-1])) {
+				bss->rate = data->supp_rates[i-1];
+				break;
+			}
+		}
+	}
+	if (!bss->rate)
+		bss->rate = data->supp_rates[data->num_supp_rates-1];
+	bss->channel = channel;
+	bss->interval = le16_to_cpu(mgmt->u.beacon.beacon_int);
+	bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+	bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
+	bss->last_rx = jiffies;
+	if (rssi)
+		bss->last_rssi = rssi;
+	bss->last_rate = rate;
+
+	spin_unlock_bh(&data->lock);
+}
+
+
+/* Process beacon and probe response frames in common function since they have
+ * almost identical contents. */
+static void ieee80211_rx_mgmt_bss_info(struct ieee80211_data *data,
+				       struct ieee80211_mgmt *mgmt,
+				       size_t len,
+				       struct ieee80211_rx_status *rx_status,
+				       int beacon)
+{
+	struct ieee802_11_elems elems;
+	size_t baselen;
+	int channel;
+
+#if 0
+	printk(KERN_DEBUG "%s: %s from " MACSTR " rate=%d rssi=%d\n",
+	       data->dev->name, beacon ? "Beacon" : "ProbeResp",
+	       MAC2STR(mgmt->sa), rx_status->rate, rx_status->rssi);
+#endif
+
+	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+	if (baselen > len) {
+		printk(KERN_DEBUG "%s: Beacon/ProbeResp underflow %d > %d\n",
+		       data->dev->name, baselen, len);
+		return;
+	}
+
+	ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
+/*	if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
+				   &elems) == ParseFailed) {
+		printk(KERN_DEBUG "%s: Beacon/ProbeResp invalid\n",
+		       data->dev->name);
+		return;
+	}*/
+
+/*	if (elems.ssid == NULL)
+		return;*/
+
+	if (elems.ds_params && elems.ds_params_len == 1)
+		channel = elems.ds_params[0];
+	else
+		channel = data->channel;
+
+	ieee80211_add_bss(data, mgmt->bssid, &elems, channel,
+			  mgmt, rx_status->rssi, rx_status->rate, beacon);
+}
+
+static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_data *data,
+					 struct ieee80211_mgmt *mgmt,
+					 size_t len,
+					 struct ieee80211_rx_status *rx_status)
+{
+	ieee80211_rx_mgmt_bss_info(data, mgmt, len, rx_status, 0);
+}
+
+
+static void ieee80211_rx_mgmt_beacon(struct ieee80211_data *data,
+				     struct ieee80211_mgmt *mgmt,
+				     size_t len,
+				     struct ieee80211_rx_status *rx_status)
+{
+	ieee80211_rx_mgmt_bss_info(data, mgmt, len, rx_status, 1);
+}
+
+static void ieee80211_rx_mgmt_auth(struct ieee80211_data *data,
+				   struct ieee80211_mgmt *mgmt,
+				   size_t len,
+				   struct ieee80211_rx_status *rx_status, struct sk_buff *skb)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	struct ieee802_11_elems elems;
+	struct ieee80211_bss *bss;
+	size_t baselen;
+
+	if (data->state != IEEE80211_AUTHENTICATE) {
+		printk(KERN_DEBUG "%s: authentication frame received from "
+		       MACSTR ", but not in authenticate state - ignored\n",
+		       data->dev->name, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (len < 24 + 6) {
+		printk(KERN_DEBUG "%s: too short (%d) authentication frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: authentication frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	baselen = (u8 *) mgmt->u.auth.variable - (u8 *) mgmt;
+	if (baselen > len) {
+		printk(KERN_DEBUG "%s: Auth underflow %d > %d\n",
+		       data->dev->name, baselen, len);
+		return;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable, len - baselen,
+				   &elems) == ParseFailed) {
+		printk(KERN_DEBUG "%s: Parse failed.\n", data->dev->name);
+		return;
+	}
+
+	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
+	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
+	status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
+	printk(KERN_DEBUG "%s: RX authentication (alg=%d "
+	       "transaction=%d status=%d %s)\n",
+	       data->dev->name, auth_alg,
+	       auth_transaction, status_code,
+	       ieee80211_status_code(status_code));
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (!bss) {
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+
+	if (auth_transaction != bss->auth_state + 1) {
+		printk(KERN_DEBUG "%s: unexpected authentication "
+		       "transaction number. expected %d, got %d\n",
+		       data->dev->name, bss->auth_state + 1, auth_transaction);
+		bss->auth_state = 0;
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+	bss->auth_alg = auth_alg;
+	bss->auth_state = auth_transaction;
+
+	if ((auth_transaction == 0x2 || auth_transaction == 0x4)
+	    && status_code != WLAN_STATUS_SUCCESS) {
+		bss->num_auth_fail++;
+		bss->auth_state = 0;
+		if (auth_transaction == 0x2) {
+			bss->auth_alg = WLAN_AUTH_OPEN;
+			printk(KERN_DEBUG "%s: Falling back on open authentication...\n", data->dev->name);
+		}
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+
+	/* TODO: necessary? */
+	if (data->mode == IEEE80211_MANAGED && auth_transaction == 1) {
+		if (status_code == WLAN_STATUS_SUCCESS)
+			printk(KERN_DEBUG "%s: Ignoring attempt to initiate authentication\n",
+			        data->dev->name);
+		return;
+	}
+
+	switch (auth_alg) {
+	case WLAN_AUTH_OPEN:
+		if (auth_transaction == 2)
+			bss->auth_state = 5;
+		else
+			printk(KERN_DEBUG "%s: invalid authentication "
+			       "transaction number: %d\n",
+			       data->dev->name, auth_transaction);
+		break;
+	case WLAN_AUTH_SHARED_KEY:
+		switch (auth_transaction) {
+		case 2:
+			if (elems.challenge_len != WLAN_AUTH_CHALLENGE_LEN) {
+				printk(KERN_DEBUG "%s: invalid challenge length: %d\n",
+				       data->dev->name, elems.challenge_len);
+				return;
+			}
+			bss->challenge = elems.challenge;
+			ieee80211_send_auth(data, bss);
+			break;
+		case 4:
+			bss->auth_state = 5;
+			break;
+		default:
+			printk(KERN_DEBUG "%s: invalid authentication"
+			       "transaction number: %d\n",
+			       data->dev->name, auth_transaction);
+			break;
+		}
+		break;
+	}
+
+	if (bss->auth_state == 5) {
+		data->auth_tries = 0;
+		printk(KERN_DEBUG "%s: authenticated\n", data->dev->name);
+
+		spin_unlock_bh(&data->lock);
+		ieee80211_associate(data);
+	} else
+		spin_unlock_bh(&data->lock);
+}
+
+static void ieee80211_rx_mgmt_deauth(struct ieee80211_data *data,
+				     struct ieee80211_mgmt *mgmt,
+				     size_t len,
+				     struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_bss *bss;
+	u16 reason_code;
+
+	if (len < 24 + 2) {
+		printk(KERN_DEBUG "%s: too short (%d) deauthentication frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: deauthentication frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+	printk(KERN_DEBUG "%s: RX deauthentication from " MACSTR
+	       " (reason=%d)\n",
+	       data->dev->name, MAC2STR(mgmt->sa), reason_code);
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (bss) {
+		bss->num_auth_fail++;
+		bss->auth_state = 0;
+		if (bss->auth_state == 5)
+			printk(KERN_DEBUG "%s: deauthenticated\n", data->dev->name);
+	}
+
+	spin_unlock_bh(&data->lock);
+	ieee80211_set_associated(data, 0);
+
+	ieee80211_authenticate(data);
+}
+
+
+static void ieee80211_rx_mgmt_disassoc(struct ieee80211_data *data,
+				       struct ieee80211_mgmt *mgmt,
+				       size_t len,
+				       struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_bss *bss;
+	u16 reason_code;
+
+	if (len < 24 + 2) {
+		printk(KERN_DEBUG "%s: too short (%d) disassociation frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: disassociation frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+
+	printk(KERN_DEBUG "%s: RX disassociation from " MACSTR
+	       " (reason=%d)\n",
+	       data->dev->name, MAC2STR(mgmt->sa), reason_code);
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (bss)
+		bss->num_assoc_fail++;
+	spin_unlock_bh(&data->lock);
+
+	if (data->flags & ASSOCIATED)
+		printk(KERN_DEBUG "%s: disassociated\n", data->dev->name);
+	ieee80211_set_associated(data, 0);
+
+	ieee80211_associate(data);
+}
+
+static void ieee80211_rx_mgmt_assoc_req(struct ieee80211_data *data,
+					struct ieee80211_mgmt *mgmt,
+					size_t len,
+					struct ieee80211_rx_status *rx_status,
+					int reassoc)
+{
+	if (reassoc && data->mode == IEEE80211_MANAGED)
+		ieee80211_associate(data);
+
+	return;
+}
+
+static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_data *data,
+					 struct ieee80211_mgmt *mgmt,
+					 size_t len,
+					 struct ieee80211_rx_status *rx_status,
+					 int reassoc)
+{
+	static const char hidden_ssid[IEEE80211_MAX_SSID_LEN] = {0};
+	struct ieee802_11_elems elems;
+	struct ieee80211_bss *bss;
+	size_t baselen;
+	u16 capab_info, status_code, aid;
+
+	/* AssocResp and ReassocResp have identical structure, so process both
+	 * of them in this function. */
+
+	if (data->state != IEEE80211_ASSOCIATE) {
+		printk(KERN_DEBUG "%s: association frame received from "
+		       MACSTR ", but not in associate state - ignored\n",
+		       data->dev->name, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (len < 24 + 6) {
+		printk(KERN_DEBUG "%s: too short (%d) association frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: association frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+	aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+
+	printk(KERN_DEBUG "%s: RX %sssocResp (capab=0x%x "
+	       "aid=%d status=%d %s)\n",
+	       data->dev->name, reassoc ? "Rea" : "A",
+	       capab_info, aid & 0x3FFF, status_code,
+	       ieee80211_status_code(status_code));
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (status_code != WLAN_STATUS_SUCCESS) {
+		printk(KERN_DEBUG "%s: AP denied association\n",
+		       data->dev->name);
+		if (bss)
+			bss->num_assoc_fail++;
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+
+	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
+		printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
+		       "set\n", data->dev->name, aid);
+	aid &= ~(BIT(15) | BIT(14));
+
+	if (bss)
+		bss->num_auth_fail = bss->num_assoc_fail = 0;
+
+	baselen = (u8 *) mgmt->u.assoc_resp.variable - (u8 *) mgmt;
+        if (ieee802_11_parse_elems(mgmt->u.assoc_resp.variable, len - baselen, &elems) == ParseOK
+	    && elems.supp_rates) {
+		memset(bss->supp_rates, 0, IEEE80211_MAX_SUPP_RATES);
+		memcpy(bss->supp_rates, elems.supp_rates,
+		       elems.supp_rates_len > IEEE80211_MAX_SUPP_RATES ?
+		       IEEE80211_MAX_SUPP_RATES : elems.supp_rates_len);
+	}
+
+	spin_unlock_bh(&data->lock);
+
+	printk(KERN_DEBUG "%s: associated\n", data->dev->name);
+	data->aid = aid;
+	data->assoc_tries = 0;
+	ieee80211_set_associated(data, 1);
+
+	if (data->set_ssid) {
+		if (data->flags & HIDDEN_SSID)
+			data->set_ssid(data->dev, (char *)hidden_ssid, data->ssid_len);
+		else
+			data->set_ssid(data->dev, data->ssid, data->ssid_len);
+	}
+	ieee80211_associated(data);
+}
+
+
+void ieee80211_rx_mgmt(struct ieee80211_data *data, struct sk_buff *skb,
+		       struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_mgmt *mgmt;
+	u16 fc;
+
+	if (skb->len < 24)
+		goto fail;
+
+	mgmt = (struct ieee80211_mgmt *) skb->data;
+	fc = le16_to_cpu(mgmt->frame_control);
+
+	switch (WLAN_FC_GET_STYPE(fc)) {
+	case WLAN_FC_STYPE_PROBE_REQ:
+		break;
+	case WLAN_FC_STYPE_PROBE_RESP:
+		ieee80211_rx_mgmt_probe_resp(data, mgmt, skb->len, rx_status);
+		break;
+	case WLAN_FC_STYPE_BEACON:
+		ieee80211_rx_mgmt_beacon(data, mgmt, skb->len, rx_status);
+		break;
+	case WLAN_FC_STYPE_AUTH:
+		ieee80211_rx_mgmt_auth(data, mgmt, skb->len, rx_status, skb);
+		break;
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		ieee80211_rx_mgmt_assoc_req(data, mgmt, skb->len, rx_status, 0);
+		break;
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		ieee80211_rx_mgmt_assoc_resp(data, mgmt, skb->len, rx_status, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		ieee80211_rx_mgmt_assoc_req(data, mgmt, skb->len, rx_status, 1);
+		break;
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		ieee80211_rx_mgmt_assoc_resp(data, mgmt, skb->len, rx_status, 1);
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		ieee80211_rx_mgmt_deauth(data, mgmt, skb->len, rx_status);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		ieee80211_rx_mgmt_disassoc(data, mgmt, skb->len, rx_status);
+		break;
+	default:
+		printk(KERN_DEBUG "%s: received unknown management frame - "
+		       "stype=%d\n", data->dev->name, WLAN_FC_GET_STYPE(fc));
+		break;
+	}
+
+ fail:
+	if (skb)
+		dev_kfree_skb(skb);
+}
+
+
+static void ieee80211_timer(unsigned long ptr)
+{
+	struct ieee80211_data *data = (struct ieee80211_data *) ptr;
+
+	switch (data->state) {
+	case IEEE80211_SCAN:
+		ieee80211_scan(data);
+		break;
+	case IEEE80211_AUTHENTICATE:
+		ieee80211_authenticate(data);
+		break;
+	case IEEE80211_ASSOCIATE:
+		ieee80211_associate(data);
+		break;
+	case IEEE80211_ASSOCIATED:
+		ieee80211_associated(data);
+		break;
+	case IEEE80211_STOP:
+		break;
+	default:
+		printk(KERN_DEBUG "ieee80211_timer: Unknown state %d\n",
+		       data->state);
+		break;
+	}
+}
+
+struct sk_buff * ieee80211_wep_decode(struct ieee80211_data *data,
+				      struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int hlen;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	if (!(fc & WLAN_FC_ISWEP))
+		return skb;
+
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+		  (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		hlen = 30;
+	else
+		hlen = 24;
+
+	if (wep_decrypt(skb, hlen, data)) {
+		dev_kfree_skb(skb);
+		return NULL;
+	} else
+		return skb;
+}
+
+int ieee80211_data_tx(struct ieee80211_data *data,
+		      struct sk_buff *skb)
+{
+	struct ieee80211_bss *bss;
+	u8 dst[ETH_ALEN];
+	u16 fc = (WLAN_FC_TYPE_DATA << 2) | (WLAN_FC_STYPE_DATA << 4);
+	int rc, short_preamble = 0;
+
+	/* hold the packets till we're authenticated/associated */
+	if (data->state != IEEE80211_ASSOCIATED) {
+		netif_stop_queue(data->dev);
+		return 1;
+	}
+
+	if (skb->len < ETH_HLEN) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	if (data->flags & WEP_ENABLED)
+		fc |= WLAN_FC_ISWEP;
+
+	switch (data->mode) {
+	case IEEE80211_MANAGED:
+		fc |= WLAN_FC_TODS;
+		break;
+	case IEEE80211_ADHOC:
+		break;
+	case IEEE80211_AP:
+		fc |= WLAN_FC_FROMDS;
+		break;
+	case IEEE80211_WDS:
+		fc |= WLAN_FC_FROMDS | WLAN_FC_TODS;
+		break;
+	case IEEE80211_MONITOR:
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	memcpy(dst, skb->data, ETH_ALEN);
+
+	skb = ieee80211_data_encaps(data, skb);
+
+        spin_lock_bh(&data->lock);
+        bss = ieee80211_get_bss(data, dst);
+
+	if (bss)
+		short_preamble = bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+	rc = data->tx(data->dev, skb, cpu_to_le16(fc), short_preamble, dst);
+        spin_unlock_bh(&data->lock);
+
+	return rc;
+}
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+
+struct sk_buff * ieee80211_data_encaps(struct ieee80211_data *data,
+				       struct sk_buff *skb)
+{
+	u16 ethertype;
+
+	ethertype = (skb->data[12] << 8) | skb->data[13];
+
+	skb_pull(skb, 12);
+
+	memcpy(skb_push(skb, 6),
+	       ethertype == ETH_P_AARP || ethertype == ETH_P_IPX ?
+	       bridge_tunnel_header : rfc1042_header, 6);
+
+	return skb;
+}
+
+
+struct sk_buff * ieee80211_data_decaps(struct ieee80211_data *data,
+				       struct sk_buff *skb)
+{
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	struct ieee80211_hdr *hdr;
+	int hlen;
+	u16 fc;
+	u8 *pos;
+
+	if (data->state != IEEE80211_ASSOCIATED ||
+	    skb->len < 24)
+		goto drop;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	hlen = 24;
+
+	switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+	case WLAN_FC_TODS | WLAN_FC_FROMDS:
+		/* RA, TA, DA, SA */
+		if (skb->len < 30)
+			goto drop;
+		memcpy(dst, hdr->addr3, ETH_ALEN);
+		memcpy(src, hdr->addr4, ETH_ALEN);
+		hlen += ETH_ALEN;
+		break;
+	case WLAN_FC_TODS:
+		/* BSSID, SA, DA */
+		memcpy(dst, hdr->addr3, ETH_ALEN);
+		memcpy(src, hdr->addr2, ETH_ALEN);
+		break;
+	case WLAN_FC_FROMDS:
+		/* DA, BSSID, SA */
+		memcpy(dst, hdr->addr1, ETH_ALEN);
+		memcpy(src, hdr->addr3, ETH_ALEN);
+		break;
+	case 0:
+		/* DA, SA, BSSID */
+		memcpy(dst, hdr->addr1, ETH_ALEN);
+		memcpy(src, hdr->addr2, ETH_ALEN);
+		break;
+	}
+
+	pos = skb_pull(skb, hlen);
+	if (hlen >= 6 &&
+	    (memcmp(pos, rfc1042_header, 6) == 0 ||
+	     memcmp(pos, bridge_tunnel_header, 6) == 0)) {
+		skb_pull(skb, 6);
+	}
+
+	memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+	memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+
+	return skb;
+
+drop:
+	dev_kfree_skb(skb);
+	return NULL;
+}
+
+struct sk_buff * ieee80211_reassemble_frag (struct ieee80211_data *data,
+			       struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr, *hdr2;
+	struct ieee80211_frag_entry *entry;
+	u16 seq_ctrl, fc;
+	unsigned int seq, frag, hlen;
+	int i, j;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+	seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
+	seq = WLAN_GET_SEQ_SEQ(seq_ctrl);
+	frag = WLAN_GET_SEQ_FRAG(seq_ctrl);
+
+	if (!frag && fc & WLAN_FC_MOREFRAG) {
+		i = 0;
+		/* look for an empty entry */
+		while (i < IEEE80211_FRAG_CACHE_SIZE
+		       && data->frag_cache[i].skb != NULL)
+			i+=1;
+
+		if (data->frag_cache[i].skb) { /* cache is full */
+			i = -1;
+			for (j = 0; j < IEEE80211_FRAG_CACHE_SIZE; j += 1) {
+			entry = &data->frag_cache[j];
+			if (entry->skb != NULL &&
+			    time_after(jiffies, entry->first_rx_time + 2 * HZ)) {
+				dev_kfree_skb(entry->skb);
+				entry->skb = NULL;
+				i = j;
+			}
+			}
+		}
+
+		if (i == -1)
+			goto drop;	/* could not free a stale entry */
+
+		entry = &data->frag_cache[i];
+		entry->skb = dev_alloc_skb(2346);
+		if (!entry->skb)
+			goto drop;
+
+		entry->skb->dev = data->dev;
+		entry->first_rx_time = jiffies;
+		memcpy(skb_put(entry->skb, skb->len), skb->data, skb->len);
+		goto drop;
+	} else if (frag) {
+		if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+			  (WLAN_FC_TODS | WLAN_FC_FROMDS))
+			hlen = 30;
+		else
+			hlen = 24;
+
+		for (i = 0; i < IEEE80211_FRAG_CACHE_SIZE; i += 1) {
+		entry = &data->frag_cache[i];
+		if (!entry->skb)
+			continue;
+		hdr2 = (struct ieee80211_hdr *) entry->skb->data;
+		if (WLAN_GET_SEQ_SEQ(le16_to_cpu(hdr2->seq_ctrl)) != seq 
+		    || memcmp(hdr2->addr1, hdr->addr1, ETH_ALEN)
+		    || memcmp(hdr2->addr2, hdr->addr2, ETH_ALEN))
+			continue;
+
+		skb_pull(skb, hlen);
+		if (entry->skb->tail + skb->len > entry->skb->end) {
+			dev_kfree_skb(entry->skb);
+			entry->skb = NULL;
+			goto drop;
+		}
+
+		memcpy(skb_put(entry->skb, skb->len), skb->data,
+                               skb->len);
+
+		if (fc & WLAN_FC_MOREFRAG)
+			goto drop;
+		else {
+			dev_kfree_skb(skb);
+			skb = entry->skb;
+			entry->skb = NULL;
+			return skb;
+		}
+
+		}
+
+		goto drop;
+	}
+
+	return skb;
+drop:
+	dev_kfree_skb(skb);
+	return NULL;
+}
+
+int ieee80211_filter_duplicates(struct ieee80211_data *data,
+				struct sk_buff *skb, u16 fc)
+{
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_bss *bss;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, hdr->addr2);
+
+	if (bss) {
+		if (fc & WLAN_FC_RETRY && bss->last_seq == hdr->seq_ctrl) {
+			spin_unlock_bh(&data->lock);
+			return 1;
+		}
+		bss->last_seq = hdr->seq_ctrl;
+	}
+	spin_unlock_bh(&data->lock);
+
+	return 0;
+}
+
+/* returns a rate appropriate for the destination, if it can be determined */
+u8 ieee80211_get_rate(struct ieee80211_data *data, u8 *addr)
+{
+	struct ieee80211_bss *bss;
+	u8 rate;
+
+	bss = ieee80211_get_bss(data, addr);
+
+	if (bss)
+		rate = bss->rate;
+	else
+		rate = data->rate;
+
+	return rate;
+}
diff -Naur linux-2.6.12-rc3-no1/adm8211/ieee80211.h no-sources-new/adm8211/ieee80211.h
--- linux-2.6.12-rc3-no1/adm8211/ieee80211.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/ieee80211.h	2005-02-05 18:36:42.000000000 +0000
@@ -0,0 +1,384 @@
+#ifndef IEEE80211_H
+#define IEEE80211_H
+
+#define BIT(x) (1 << (x))
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FCS_LEN 4
+#define WLAN_ADDR3_HDR_LEN 24
+
+#define WLAN_FC_PVER (BIT(1) | BIT(0))
+#define WLAN_FC_TODS BIT(8)
+#define WLAN_FC_FROMDS BIT(9)
+#define WLAN_FC_MOREFRAG BIT(10)
+#define WLAN_FC_RETRY BIT(11)
+#define WLAN_FC_PWRMGT BIT(12)
+#define WLAN_FC_MOREDATA BIT(13)
+#define WLAN_FC_ISWEP BIT(14)
+#define WLAN_FC_ORDER BIT(15)
+
+#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2)
+#define WLAN_FC_GET_STYPE(fc) \
+	(((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4)
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
+#define WLAN_GET_SEQ_SEQ(seq) \
+	(((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
+
+#define IEEE80211_FC(type, stype) cpu_to_le16((type << 2) | (stype << 4))
+
+#define WLAN_FC_TYPE_MGMT 0
+#define WLAN_FC_TYPE_CTRL 1
+#define WLAN_FC_TYPE_DATA 2
+
+/* management */
+#define WLAN_FC_STYPE_ASSOC_REQ 0
+#define WLAN_FC_STYPE_ASSOC_RESP 1
+#define WLAN_FC_STYPE_REASSOC_REQ 2
+#define WLAN_FC_STYPE_REASSOC_RESP 3
+#define WLAN_FC_STYPE_PROBE_REQ 4
+#define WLAN_FC_STYPE_PROBE_RESP 5
+#define WLAN_FC_STYPE_BEACON 8
+#define WLAN_FC_STYPE_ATIM 9
+#define WLAN_FC_STYPE_DISASSOC 10
+#define WLAN_FC_STYPE_AUTH 11
+#define WLAN_FC_STYPE_DEAUTH 12
+
+/* control */
+#define WLAN_FC_STYPE_PSPOLL 10
+#define WLAN_FC_STYPE_RTS 11
+#define WLAN_FC_STYPE_CTS 12
+#define WLAN_FC_STYPE_ACK 13
+#define WLAN_FC_STYPE_CFEND 14
+#define WLAN_FC_STYPE_CFENDACK 15
+
+/* data */
+#define WLAN_FC_STYPE_DATA 0
+#define WLAN_FC_STYPE_DATA_CFACK 1
+#define WLAN_FC_STYPE_DATA_CFPOLL 2
+#define WLAN_FC_STYPE_DATA_CFACKPOLL 3
+#define WLAN_FC_STYPE_NULLFUNC 4
+#define WLAN_FC_STYPE_CFACK 5
+#define WLAN_FC_STYPE_CFPOLL 6
+#define WLAN_FC_STYPE_CFACKPOLL 7
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS BIT(0)
+#define WLAN_CAPABILITY_IBSS BIT(1)
+#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
+#define WLAN_CAPABILITY_PRIVACY BIT(4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
+
+/* Status codes */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+
+/* Reason codes */
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_CHALLENGE 16
+
+#define IEEE80211_MAX_SSID_LEN 32
+
+#define WEP_KEY_MAX_LEN 13
+#define WEP_KEY_MAX_KEYS 4
+
+#define FCS_LEN 4
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+struct ieee80211_hdr {
+	__le16 frame_control;
+	__le16 duration_id;
+	u8 addr1[6];
+	u8 addr2[6];
+	u8 addr3[6];
+	__le16 seq_ctrl;
+	u8 addr4[6];
+} __attribute__ ((packed));
+
+union ieee80211_mgmt_payload {
+	struct {
+		__le16 auth_alg;
+		__le16 auth_transaction;
+		__le16 status_code;
+		/* possibly followed by Challenge text */
+		u8 variable[0];
+	} __attribute__ ((packed)) auth;
+	struct {
+		__le16 reason_code;
+	} __attribute__ ((packed)) deauth;
+	struct {
+		__le16 capab_info;
+		__le16 listen_interval;
+		/* followed by SSID and Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) assoc_req;
+	struct {
+		__le16 capab_info;
+		__le16 status_code;
+		__le16 aid;
+		/* followed by Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) assoc_resp, reassoc_resp;
+	struct {
+		__le16 capab_info;
+		__le16 listen_interval;
+		u8 current_ap[ETH_ALEN];
+		/* followed by SSID and Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) reassoc_req;
+	struct {
+		__le16 reason_code;
+	} __attribute__ ((packed)) disassoc;
+	struct {
+		__le64 timestamp;
+		__le16 beacon_int;
+		__le16 capab_info;
+		/* followed by some of SSID, Supported rates,
+		 * FH Params, DS Params, CF Params, IBSS Params, TIM */
+		u8 variable[0];
+	} __attribute__ ((packed)) beacon;
+	struct {
+		/* only variable items: SSID, Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) probe_req;
+	struct {
+		__le64 timestamp;
+		__le16 beacon_int;
+		__le16 capab_info;
+		/* followed by some of SSID, Supported rates,
+		 * FH Params, DS Params, CF Params, IBSS Params */
+		u8 variable[0];
+	} __attribute__ ((packed)) probe_resp;
+};
+
+struct ieee80211_mgmt {
+	__le16 frame_control;
+	__le16 duration;
+	u8 da[6];
+	u8 sa[6];
+	u8 bssid[6];
+	__le16 seq_ctrl;
+	union ieee80211_mgmt_payload u;
+} __attribute__ ((packed));
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+	u8 *ssid;
+	u8 ssid_len;
+	u8 *supp_rates;
+	u8 supp_rates_len;
+	u8 *fh_params;
+	u8 fh_params_len;
+	u8 *ds_params;
+	u8 ds_params_len;
+	u8 *cf_params;
+	u8 cf_params_len;
+	u8 *tim;
+	u8 tim_len;
+	u8 *ibss_params;
+	u8 ibss_params_len;
+	u8 *challenge;
+	u8 challenge_len;
+};
+
+typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+
+ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
+				struct ieee802_11_elems *elems);
+
+
+struct ieee80211_rx_status {
+	int rssi;
+	int rate; /* in 100 kbps */
+};
+
+
+struct ieee80211_bss {
+	u8 bssid[ETH_ALEN];
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	size_t ssid_len;
+	int hidden_ssid;
+
+	int channel;
+	u16 interval;
+	u64 timestamp;
+	u16 capability;
+#define IEEE80211_MAX_SUPP_RATES 8
+	u8 supp_rates[IEEE80211_MAX_SUPP_RATES + 1];
+	u8 rate;
+
+	unsigned long last_rx;
+	int last_rssi;
+	u8 last_rate;
+	unsigned long num_beacon, num_proberesp;
+	int num_auth_fail, num_assoc_fail;
+	int auth_state;
+	u8 *challenge;
+	u16 auth_alg;
+
+	__le16 last_seq; /* last received seq number field (in 802.11 byte
+		       * order) from the AP */
+
+	struct ieee80211_bss *next;
+};
+
+struct ieee80211_chan_range {
+	u8 min;
+	u8 max;
+};
+
+struct ieee80211_wep_data {
+	u32 iv;
+	unsigned char len;
+	u8 key[WEP_KEY_MAX_LEN];
+};
+
+#define IEEE80211_FRAG_CACHE_SIZE 4
+struct ieee80211_frag_entry {
+	unsigned long first_rx_time;
+	struct sk_buff *skb;
+};
+
+struct ieee80211_data {
+	struct net_device *dev;
+	int (*set_channel)(struct net_device *dev, int channel);
+	int (*set_bssid)(struct net_device *dev, u8 *bssid);
+	int (*set_ssid)(struct net_device *dev, u8 *ssid, size_t ssid_len);
+	int (*set_rate)(struct net_device *dev);
+	int (*tx)(struct net_device *dev, struct sk_buff *skb,
+		  __le16 fc, int short_preamble, u8 *dst);
+	void (*link_change)(struct net_device *dev, int link);
+	void (*set_interval)(struct net_device *dev);
+	void (*set_tbtt)(struct net_device *dev, u16 tbtt);
+	u64 (*get_tsft)(struct net_device *dev);
+
+	enum {
+		IEEE80211_SCAN, IEEE80211_AUTHENTICATE, IEEE80211_ASSOCIATE,
+		IEEE80211_ASSOCIATED, IEEE80211_STOP
+	} state;
+	struct timer_list timer;
+
+	enum {
+		IEEE80211_MANAGED, IEEE80211_ADHOC, IEEE80211_MONITOR,
+		IEEE80211_AP, IEEE80211_WDS
+	} mode;
+
+#define WEP_ENABLED (1<<0)
+#define WEP_RESTRICTED (1<<1)
+#define AUTO_RATE (1<<2)
+#define ANY_SSID (1<<3)
+#define HIDDEN_SSID (1<<4)
+#define PREF_BSSID_SET (1<<5)
+#define PREV_BSSID_SET (1<<6)
+#define ASSOCIATED (1<<8)
+#define LINK_ON (1<<9)
+
+	u32 flags;
+	
+	char nick[IW_ESSID_MAX_SIZE];
+
+	unsigned int auth_algorithm, default_key;
+	struct ieee80211_wep_data wep_data[WEP_KEY_MAX_KEYS];
+	struct crypto_tfm *tfm;
+
+	struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_SIZE];
+
+	u8 bssid[ETH_ALEN];
+	u8 rate;
+	u8 pref_channel;
+	u8 channel;
+	struct ieee80211_chan_range chan_range;
+	int num_supp_rates;
+	u8 *supp_rates;
+
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	size_t ssid_len;
+	u8 pref_bssid[ETH_ALEN];
+
+	int auth_tries;
+	int assoc_tries;
+
+	int aid;
+
+	u8 prev_bssid[ETH_ALEN];
+
+	u64 timestamp;
+	u16 capab, beacon_interval, listen_interval;
+
+	struct ieee80211_bss *bss;
+	spinlock_t lock;
+};
+
+static const u8 IEEE80211_B_RATES[] = { 0x02, 0x04, 0x0b, 0x16, 0x00 };
+
+struct ieee80211_bss * ieee80211_get_bss(struct ieee80211_data *data, u8 *addr);
+
+int ieee80211_init(struct ieee80211_data *data);
+void ieee80211_deinit(struct ieee80211_data *data);
+void ieee80211_rx_mgmt(struct ieee80211_data *data, struct sk_buff *skb,
+		       struct ieee80211_rx_status *rx_status);
+void ieee80211_start(struct ieee80211_data *data);
+void ieee80211_start_scan(struct ieee80211_data *data);
+void ieee80211_stop(struct ieee80211_data *data);
+
+int ieee80211_data_tx(struct ieee80211_data *data,
+		      struct sk_buff *skb);
+
+struct sk_buff * ieee80211_wep_decode(struct ieee80211_data *data,
+				      struct sk_buff *skb);
+
+struct sk_buff * ieee80211_data_encaps(struct ieee80211_data *data,
+				       struct sk_buff *skb);
+struct sk_buff * ieee80211_data_decaps(struct ieee80211_data *data,
+				       struct sk_buff *skb);
+
+struct sk_buff * ieee80211_reassemble_frag (struct ieee80211_data *data,
+					    struct sk_buff *skb);
+int ieee80211_filter_duplicates(struct ieee80211_data *data,
+				struct sk_buff *skb, u16 fc);
+u8 ieee80211_get_rate(struct ieee80211_data *data, u8 *addr);
+
+#include "wep.h"
+
+#endif /* IEEE80211_H */
diff -Naur linux-2.6.12-rc3-no1/adm8211/kernel.patch no-sources-new/adm8211/kernel.patch
--- linux-2.6.12-rc3-no1/adm8211/kernel.patch	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/kernel.patch	2004-10-30 16:24:53.000000000 +0000
@@ -0,0 +1,24 @@
+diff -Nru linux-2.6.8.1/drivers/net/wireless/Kconfig linux-2.6.8.1-adm8211/drivers/net/wireless/Kconfig
+--- linux-2.6.8.1/drivers/net/wireless/Kconfig	2004-08-14 06:54:52.000000000 -0400
++++ linux-2.6.8.1-adm8211/drivers/net/wireless/Kconfig	2004-08-19 20:07:50.000000000 -0400
+@@ -307,6 +307,8 @@
+ 	 It has basic support for Linux wireless extensions and initial
+ 	 micro support for ethtool.
+ 
++source "drivers/net/wireless/adm8211/Kconfig"
++
+ comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support"
+ 	depends on NET_RADIO && PCI
+ config PRISM54
+diff -Nru linux-2.6.8.1/drivers/net/wireless/Makefile linux-2.6.8.1-adm8211/drivers/net/wireless/Makefile
+--- linux-2.6.8.1/drivers/net/wireless/Makefile	2004-08-14 06:56:25.000000000 -0400
++++ linux-2.6.8.1-adm8211/drivers/net/wireless/Makefile	2004-08-19 20:04:20.000000000 -0400
+@@ -28,6 +28,8 @@
+ 
+ obj-$(CONFIG_PRISM54)		+= prism54/
+ 
++obj-$(CONFIG_ADM8211)		+= adm8211/
++
+ # 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.12-rc3-no1/adm8211/rdate.h no-sources-new/adm8211/rdate.h
--- linux-2.6.12-rc3-no1/adm8211/rdate.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/rdate.h	2005-03-23 11:45:18.000000000 +0000
@@ -0,0 +1 @@
+#define RELEASE_DATE "20050323"
diff -Naur linux-2.6.12-rc3-no1/adm8211/wep.c no-sources-new/adm8211/wep.c
--- linux-2.6.12-rc3-no1/adm8211/wep.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/wep.c	2004-11-22 17:18:28.000000000 +0000
@@ -0,0 +1,160 @@
+/*
+ * Host AP crypt: host-based WEP encryption implementation for Host AP driver
+ * (adapted for use within the adm8211 driver)
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/crc32.h>
+#include <asm/string.h>
+#include <asm/scatterlist.h>
+
+#ifndef CONFIG_CRYPTO
+#error CONFIG_CRYPTO required to build
+#endif
+ 
+#include <linux/crypto.h>
+#include "ieee80211.h"
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+int wep_encrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data)
+{
+	struct ieee80211_wep_data *wep = &data->wep_data[data->default_key];
+	struct scatterlist sg;
+	u32 crc, klen, len;
+	u8 key[WEP_KEY_MAX_LEN + 3];
+	u8 *pos, *icv;
+
+	if (skb->len < hdr_len)
+		return -1;
+
+	if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4) {
+		if (pskb_expand_head(skb, (skb_headroom(skb) < 4 ? 4 : 0),
+		    (skb_tailroom(skb) < 4 ? 4 : 0), GFP_ATOMIC))
+			return -1;
+	}
+
+	len = skb->len - hdr_len;
+	pos = skb_push(skb, 4);
+	memmove(pos, pos + 4, hdr_len);
+	pos += hdr_len;
+
+	klen = 3 + wep->len;
+
+	wep->iv++;
+
+	/* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+	 * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+	 * can be used to speedup attacks, so avoid using them. */
+	if ((wep->iv & 0xff00) == 0xff00) {
+		u8 B = (wep->iv >> 16) & 0xff;
+		if (B >= 3 && B < klen)
+			wep->iv += 0x0100;
+	}
+
+	/* Prepend 24-bit IV to RC4 key and TX frame */
+	*pos++ = key[0] = (wep->iv >> 16) & 0xff;
+	*pos++ = key[1] = (wep->iv >> 8) & 0xff;
+	*pos++ = key[2] = wep->iv & 0xff;
+	*pos++ = data->default_key << 6;
+
+	/* Copy rest of the WEP key (the secret part) */
+	memcpy(key + 3, wep->key, wep->len);
+
+	/* Append little-endian CRC32 and encrypt it to produce ICV */
+	crc = ~crc32_le(~0, pos, len);
+	icv = skb_put(skb, 4);
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+
+	crypto_cipher_setkey(data->tfm, key, klen);
+	sg.page = virt_to_page(pos);
+	sg.offset = offset_in_page(pos);
+	sg.length = len + 4;
+	crypto_cipher_encrypt(data->tfm, &sg, &sg, len + 4);
+	
+	return 0;
+}
+
+
+/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
+ * the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+int wep_decrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data)
+{
+	struct ieee80211_wep_data *wep;
+	struct scatterlist sg;
+	u32 crc, klen, plen;
+	u8 key[WEP_KEY_MAX_LEN + 3];
+	u8 keyidx, *pos, icv[4];
+
+	if (skb->len < hdr_len + 8)
+		return -1;
+
+	pos = skb->data + hdr_len;
+	key[0] = *pos++;
+	key[1] = *pos++;
+	key[2] = *pos++;
+	keyidx = *pos++ >> 6;
+	keyidx %= 4;
+
+	wep = &data->wep_data[keyidx];
+	klen = 3 + wep->len;
+
+	/* Copy rest of the WEP key (the secret part) */
+	memcpy(key + 3, wep->key, wep->len);
+
+	/* Apply RC4 to data and compute CRC32 over decrypted data */
+	plen = skb->len - hdr_len - 8;
+
+	crypto_cipher_setkey(data->tfm, key, klen);
+	sg.page = virt_to_page(pos);
+	sg.offset = offset_in_page(pos);
+	sg.length = plen + 4;
+	crypto_cipher_decrypt(data->tfm, &sg, &sg, plen + 4);
+
+	crc = ~crc32_le(~0, pos, plen);
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+	if (memcmp(icv, pos + plen, 4) != 0) {
+		/* ICV mismatch - drop frame */
+		return -2;
+	}
+
+	/* Remove IV and ICV */
+	memmove(skb->data + 4, skb->data, hdr_len);
+	skb_pull(skb, 4);
+	skb_trim(skb, skb->len - 4);
+
+	return 0;
+}
+
diff -Naur linux-2.6.12-rc3-no1/adm8211/wep.h no-sources-new/adm8211/wep.h
--- linux-2.6.12-rc3-no1/adm8211/wep.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211/wep.h	2004-11-22 16:55:48.000000000 +0000
@@ -0,0 +1,9 @@
+#ifndef WEP_H
+#define WEP_H
+
+int wep_encrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data);
+int wep_decrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data);
+
+#endif
diff -Naur linux-2.6.12-rc3-no1/adm8211-20050323.tar.bz2 no-sources-new/adm8211-20050323.tar.bz2
--- linux-2.6.12-rc3-no1/adm8211-20050323.tar.bz2	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/adm8211-20050323.tar.bz2	2005-05-05 21:40:17.729245904 +0000
@@ -0,0 +1,176 @@
+BZh91AY&SYehRÙ Ð,ÿ÷þ±ÿÿÿÿÿÿþÿÿÿÿ    @   H`×;Ãèô  1ŸÀ   ŸommR
+«ßmÕ 	óÜpîí.ÞööîÑšt1 òÙéÞCßgN&VByôo£Ö|É>ïfçž÷/pW ÓÈ ÑÞûÞª RN¶MbUóìõºôaëåïn<úö _Ñw}°;2ŒEöy{îk7Õyõ5ÞîùŸµç£ï{×tö·víõ÷>ù»ï|ð_}í÷Ÿu}ŸÙÛozõÞ+^ßnïÞáëè4}«ì wÛ|×!·ÙÁöLÖw.W×÷qÚ®û\û·ÚõÜ]œ:³f·{ºz»]ÝžŒãp¶·œî{U®û«åçvN.Þ÷%SËiPï¯}ÝixyBé¶}Ù¶­W¹F¥²³[n	k%]bÛ]vVêdtÝœïzÛœÏxŠp[j¶U[UÎ8w{^·f]¯k{ÁÏHõ{ìôw¯œ÷ŸŸä9Æ×sdÝÁí)­Û)ÖñÌSïn}c¥ÖvqÑŠ!õœêî;w:ÙÎÕÜíÝÐ*9Ø4g6ë×»
+4õZ®ë78Îí]ÀMVØž¹Ë±ªÙoAy<ÜÖ±²e¶ë;³­|ïkfj{°ñ®s6Ÿï)Ù§¶Ñ¥*YÜúÞxPÓH  h4È)à4LÔÄe0&Põ&C 4ÐA ú54©ê~€òýORSz)Š h$"	ŠdÐOeFzIèFF¡M@h RA@&	FèDÔöMSÆôi4ÓAê Ð "	ÐÚ6MOI=4G¥<ÑOÅ4Âe4FjM FSh©ú©¿TOQi dj<P &@hßì¢þjoóùÏOò·çŸ¶¿W¯Gð |ßn¹	9(SJHEBXÿÎ{lšv»ßïêËßYîÆ¬+Ù¢®5æwEŒfïÍ6Þè¡ñHëZwáá®üdðw\7nßGî(Œu±ã .ª0#hi%
+b
+Ôa4ùC¬CbQXŒXŠÂib*Y0ãÛ!aCJ¥€ 1mR A üªÿ*ÿç,ÐªÐÑ1!E-% DUÌJP@DHß·ûŸ?ÛŸ~¿×õjÙ'äªÒº÷R~(ÒÂ°+#h#Ò®$*šEÅÈ}j°§P£ã|ÿ7¯£þøÿgr!0Ôš 2
+LT3DÄPU1U5ÑETÁS+$A PB4D+$Ò1ÅUME¥¡UTD-RPQIRIQ+M5QRÄP4$QHLTIADE)1ÌÅ?ÍcHRU4£TTÒÄDBÒ€%(ÐÌ!UUQPLQARÐ5BRÐQ¬HQ Æ	ª"
+)
+€€(**i"e&(ª§çÁ¹õcÏ^ó¿Ïìç}¬ÏPWã×ÊBÂHýØ;#ßd¬JãQkÿOmc3çÏDË<3òcö­Ä@¥Cþë×ò°¯ð²|ÏØËcíPßº{ø?)_\hó*œí«ŠiMS@¿¯ Š{èXæôÍçœâr¹ßÍÆG©" D@Dþ#2û*ZbJH©JA2gð@aLÍfUfc~9tA*Æ¥LÛ0Ì0jh)%(š¡UJ
+%uâeøŠVfª·B×ÌJ"rÛ#[áÀ¬út¶z\ÒæÜiÒß@çA@@"Q311€CÌ@JS 1APÁ¢LU4ÑBRDAG÷C€143TÔ,ÍPÀ+@SÇim! ÓUE1Ô¡TÍ12@QóÆ$LTÔÐVØDQ!(Q%$ÒD°ÄT²CLHB²AFÓ!PSDEQDDLÒÄSQE1RDÔAMTÔÉQÍUPAHTUøó"&²ÊšJ
+¢¢¢f	ªIh¢)
+U%j"ÓXÔE,RQCËLÄ1PXMDQL)4E%SDTDÃ$@$BÔHAKTÔAQM4MTÓ2RDEQ10$ÍDÔQTÉTÀSTUSA`bžBÅTÁ!DšAEA¶ÌC3BB! ¢`F3HPÍI«( RJiA 
+	` ¡MCST$CI€P%BC@AÙ?çý}öÿ§£÷Í×ÌÏõ­®ÓÊÿsvðŽ"B_ä<'§»ð3	¿f÷¶6Ó1q¬ljž#ÚŸé^ØéÜÉÍÚËf@îôdÿVÅ$O/ /ßâDŠ ÂwÀÖŠ])¹ÁWò({Õhá=4áR)'+-mÓjªªªM£f¢wœq¹±±Ÿ1sðÈ1Úø«i^y[|Ë_k¥Ó8ÔYÕO¹ç«6:ŽRtõž®&ØäBçM ìÀgÌxÎÑRñiÛö|<Q<û5Ó$á|»ü>}t8b×«ØX-}íÏ#Mq­ŠJµQ¢5Û±Ñ­ôÃ¢6ÍUÃõ0ñe$ÄD9ÑÐªJ
+¶ïÔª AY¡óp¯L@Úô²4Hâ5YŒ5µ%ÍèãmÈ÷Û]!šçoÆÆkBcpÈa://0IepmaPÍ2MLM	³£ZÖdH0ÞpÆXçº¶ÂŽ÷0¬qŽÛ[°Z:BÇ5,È¥P(ëIªqB5Îe\ð¢á¡Æ³0,Ýy(±ù-Š`Ú|^ldvºIÌúCpÛ}EÂÙ$zr&êfjñÓEÈÝTâk~»ê7xá]vÀÆ`!¹@Ól»®#Ó+æçà¹Óºëk*ê·;á±±G\šŠÊA*5IÑú3ä<;²ý	Lí³ëÿù»-ö(QÇ4ÿŽÅšŽNÀ§ÿÎšýÆŽD÷ÁÞ°²Èøük(ü;ÿbú::ç
+X+€P6u|3qžÅ¢«*Ž©Y¬m«É!ÁR"=ŸßWC¯êðø34±EdÛ
+ZD¬h(Õ ¡0£QŠÓ¶#$6$ÆÈÔ9OVEN&²  )Š¥É8ùvãb7Ã(ŠµeF°É"#êß¥£j ­Â Y	mNNY.aïÌaì¹#mYAM[ÙNÑšÔQMEÖqÌÛi¶å$€0c#Þ ¡dm6DØáLÓ1afÖLO8omBiS*OºŽNTeFŽa¢£,ªžã×waMMRÆŸ§Sps>ËÆšãË"&&µM=cÛ2e³'¢Ì€ÔÕµV ÔYTU!LmÓÒ#jÀÈgWEoQc±Î©\0IšÌ2 Ì&2JðÓ-/MTo-v/
+ ñ·6%4V{X~2Mf¢~(ÄvÚ³L'%¬µj+ñÀdÓû#yÔÁU,ÎFR|Ë¶.h|e
+B ç  JiZ(@É,ÌR Èý?ÏÎÛ}02)GilÂ)Š	o^9¬L«Ïè=zæ@Þ{³0§m°u SPQPRQ@U]12Vß1
+ 5ö@4@DmU­kLQT¥SCŸÌÄª*ªšhbIJÞi0*3ãRBŽÛdaxÄÂœÂkù!})¯\:æ2stkFÿGÅûç v2Œ÷NfÄH -ÿKÒé£»ùG0ÊEÆÒ·)œfÎVHá¡QzLãßí hi¿ËXT'üîvøJ=<€=²ÙÉ Á©ÎG&@DRjÉ
+ª"b(
+j©«¥£ãÚ¢ª#rd)'ûÕÉÙÌû+óòŠEPi*FàS®Qç3Šô)M-¬ÕüýàERZçÿj 	ÀþºPS1ñë×Á's&ù2ÿDGoø_°U,£§býÝ,kP)J$ê âŒTD`.²cý V?)Î¬[òú8Ã7Š4ýgÇ0îwl¶}0ýæ.üÿIkYq`Ýõ/çŽ P7Ùí·&ÕâqhêáhÆKÙZù`Ø o¢¶.áFÆ-kÞà	ÀÕ_
+Ø©³®KÄà¶C_këm3YÑŠpo.xH÷	AÑŒWËñìBQ-Œ7Ì1 È<d2ò³òý¿îñç¯ØøðÍÞÎZÇÿî>,£D~­së¡[ñÙ¯á/GÃD9õ[_*Y=ŠÝ8ïÇç·9õ{?WNtü²O.< "ç1lWB3Kx}XšÓ7ßML«g;;®~ÝÖzu¬j»gü1âóS¯þºòQ=M®!µ¹×¡õúqÒnÒ²öÏÅ[/-\S6A,Q3(i,Yí¡j¿ttðïÝçÿéGFEF a@¥&(	DaDËÁO%©ÐmÒì­Å'ý¶Ó·³»ÙÙ
+e.g°Í
+å"Ç))ÔæWÞémJt=QJ$ P%HGÃ4X'>«ûqÐäÌR52±H(	Ô­sÁÍNš@®C€ùó+È)J
+_(2Ô>/Nk}ðªªªªµû©c@¿Ë"PèÈ)h(€æoÆ[d1}ÿŽÄ B§ÛòOàþý46z$ûDë>pdüógøfh
+ŠÔy­ADL¥ôÊ³ÛI Ñ9TC12AÁÒ HXÈð·þœþÿ/ãß©¥RÎ"Ðþ¢ÕÊ«ªÿ±Ò/k 01 œ="¡ïñ¬¬QžlNÕb`D74ýðzJBÎ hzm/ÄZIólâGÛÃ¢WŒwoï;Uúz#Ñ«»ÚrŒD'Ð*ïZC  ?ØÓ
+l¹fbäRiÛÄðfÎì<»ÃfÈÔWO©ü ÕùgŠÀíò÷a@z¿êM~ý>ú%­H*\FÇbà?²ì5Á;=$h5dRô5a Ù÷£ +7ø	"×)DGAŽ{GPìØ`dñ;ŒÿÓ_Ÿ}Y?ß~Íú4øviTKøþß³ ||qeÕÝxÂŠ8¢õÄxhÚšXq 4/dãxüÓ×µ÷&w Ò±=Ó`ç¹± úMß^1ŽÄûblVaiûŽ±×®Ní_žÜé¿Èe%@û
+Z
+å$þœsÿ€ÒÈAÞ*¯úÞŠLûv~öäbÖÖúñoÝl} bfšo^ sBÏìï¢@g]ÂúöŠú¢ÖïÎYÙðOÎ/èŸÆ	,ú÷o@ÐA AqBhGX ÞSf$7}ª*"D±š©U@yyß c`cü;@šâßÙœ%úoÆuëÏ°}gZ#Â}þ?ËÀyôô|yÑóEêB¡ßñPêAMz?ãAn®»hœqûê[ÇXÐ÷Kù¿7<OíÆ|^:ÌÐø,§{úT[B®úä	@d qnl3ØEušüŽ¯I÷¯Ú=àAB|GàñSogÕÔîì&Yj±»ëéÁÆC6ªF\
+0(p! 89@$9|ï<¢ÓLÊºíŸ/Å9 QŒ«úü1ßéš${(Ã®ÿw§Ð\ß€ìëËÔDw~ÊšžMÉRýPé+öcÌûàø¢œ]ôüóDbÏí3XÙ"\øFOë`äÛÝè/?_üø»êÕ¬wkþŸ=?û5
+ýö@úãê»¬·éŸÍiyÂ±¹ed«,ÒI$ËeïjèáÃ·<Ñ>.×NøàžëVÎ)£Ç}[ë×MÖFmÕÓDpkùéÓIté]¹ß·lc"=írË¹ÔéÎÙåÍSGßãÝ>¿;FÍÛ]ëô2ŽÏÏòVÝP3mÌaKÈÁPÀ¢Ë÷3Ð`lRCF5QGUsý]:7å»âÝB	KOWÀ>Í@\\äÍÏŸ Œ ü°2œ³ý	iÓÀýzg¶­3ÙÌ Fâ¡»~L>€!`"×N,Ã)C:ùÞþ¿@ËèùWh·ÏñúFž#&qÊ
+H? [F(Ò8=*²i(òÅ`éòr%b°#B¿MÃ+qÜ²j
+»š ,F>¿õápÊQÓnaààœdäÔ6vùî|<UÕùs·7ïÒÄõû$ÖÞ®`€Þ¹:?Ÿ3ôÌb£%$ïžnÙ·3BAþ@z\ÙøÿÚPÊDìÐ JâQÃ·ýÞ	ÑCšfcG ÉyÁ¥?¡)þýà;úT?	°þa  ðû?>4šH÷ýÛEëüQTð51ö'žZÉ oÏüßä6>rV$¢(¬	T}<$æãÌÄö! ¬ü>mDqkTq@ìþY|Sû£á
+x"2k
+áAXùaÖàéñå3z-õñpm·ÂÂa¢ÕæK|û÷INO¯Ãÿv&f	C1:d+OÚcè?vÜ?SP"ÔdŠÂc¿1ôáóaá·Í§.²å\æÕGoàÃd³X Q§.oÝëÏDÏá¹ãj¡.*i wcÕÃm£À^Ã9>[$¿fþikÀ=ZÞE"O_Í­ûÝáL/wD$-Y&œà#ùäLš%ý\íê÷*2,¥7f -!HÎU@` Õ`& íTÆºò[(Ž$\fsV¡P±,ÊéTøÍÝÙ4å­Ûû×9ÌÃïù8n¬V¢+ðiûG<@tOá;0k@îze:ŒþÌÇÉò 'S7€_398üïèö}~Ú§åéG éþS8RCó82À`,VyÅÌÙ(7sæÇThÌ1åV¡¬¢Ö°$C	H]ËÌÀ¶x`ñýùû-¯+CRV¢`#k(>
+-Ýê÷FÈLs
+(<^÷`8ó{áåå¥¢¥%Õ¥3([HÌ$&^œºóâí z»œÎú{íký£éõ3ÄåØ?ÖñÌ¡ñÔ°#Q CÈrß/ÒÿU©ÉìÎŽ!ùè¶ß-±#Öø¯+ãa7ÄõP³?ŸyÒ{zšsü¿²²?MR²os€( hëJkÎýõcRÌ5¯ÎxÌd
+à/Á­xrÏþªI÷ñš&ÿÜô5FªÎê@äFüIŽ~hõ}@µÓŠ0ÇOxÉ!u<FsÎàš®©Ï­>µøRÂ ²Csßo"îÃÝ·ßa¥;F'€[C­TYzKPòUu6Çò[-r APõl0ì×SŽìòºÈÁÙß&zVäb×ãîíèQš3}IÈáRDq@s>4À 4(ï&ZÄåtè.·lx7³øšPÆ)áA6A
+éêt¥@¿ÀpÎÚ`mmÆËSN}vö?mn' 3tÓ'*W6Ár­Ähw°ô"û§0o:W£;°Icñb$H	$0Cá4ÐDCº€@\wÅ7efI". ²Ä¡t	MÉOõŽd®¥}*¢±Æ¥ÆûB"sï-I"Áß¶©æ€¥ûñi("Úw=qóåµÑQ¯Ð2çi¯ù&rê\b|p8oñqG;FSK®°© Õq)Š€@*¢ ú6YÙ9ñö9ÈpâüC`=¬Cºì
+0;°ñ)¬žùºPuAÂ=¶Ïcb>f­Op;ãØ< þÈ8¹-øwšS5ÌOµÍO,ËÞ>#ÑM#ÊæV{ZÁÇæÖýM úÈË ÏÝjhÖÙÒ\Ê4oOÝÓ9ÔqfÛémð³7(K$Ùaw»ŠÿpdAUpUûnm|E£êê²N·utÎæIVbiÇ`È3#ÞZ¢Á@LS$ÒÂHRJU$~64`fYÖÀ_Õ9Ë0mþùýëóéEâ'ë>Ç\UÊ=;Ÿ>}`\CøâÞ@Ž°1+ÉuuMî£z,iÕôgBG«öããÉÉðàšç«mTiÖë×u¬4szå q\#;µ0ºpê¥¶ÒY$Dólf×µXÆ~ìÇ32Ûnae¶ÛJ[s32ÒÛó,·'lÌÇÝ³¿Ïâª¯Ï%¡ó\M¡ÕŠ*MÚî0 ûS#Dr3$	^	RBe"<¬ïh±³	D t³¶öó÷ä(z×,&¬n±ïA-Ç°ÆAA3:¿ãÃuŸØÉì! P©( -ÝþAøWõH&	ß"×%'^eÊH)óÛ·æéïÈ$nH¿ù)<yôÈÒlÀ~ŠÓötÒQ!ÊáÇ,€'1
+áÄÇ
+îÖÔw{üæ&íìªBé%žÑi%á¡AC`2Ÿ2ó·qŒ€¯aÏŠêKBÒðúPûgáfN	#Åº8ñ¶Š@<àKss²Ð	äyP}ùyŸ.Ê0@¢\ÖšãqbÓŽ¢Ãÿù§f\©0xAŒ¶U7wzsH&T)J^Fo2}^ãn4Z¿Çnã×XàëÌ¿|+mFÐï&ÎË¢)óÍöK5€¹laj¯;À	S÷ž®Rïo÷î|ÚH×Xë.O"5µPž¹;+,ù/ÏUUÿAjbM€ügÎþÏW¹ÝT	 GòJóÇÑ$ýo4®2Þ¬:úllŸGËÁeèÂ×ŠÎÚ¢Ç02é<íc# JC×®:ì}6ÕQUGÏ9G_TUKH§Pæåõ2Í0VjQq°3lÓP2²v&f	UDRä<6£©.Áb
+ñ³Û©H¯éÐî	øÇ 6zÜdG@y¿,Î Ø7öWœoµŽî§ôôã6Ãí»x%Öð%jØE*ª¡XcXmµÏ-¡J@?ßæ ÉfKëLvfmyèŸÂÑN=@¥x_:±$iêÑde;:eœ¹I¬0}R%.·Ç ª &' Úhm{?zÇ ×;Œšúò+û:;-ó+Æ¿-tÌáø1HÍMx×Ô<Á
+o]l`äî²öÄçsZåcI*þé}Smº
+Ï(å­ñú¬žjÊ7ÏÆÂ¥H1iaŽe»A(kÖZ+©§ãÄc3hÎÑê!çñúWAøVzÏÜgÓœ]t¹	óHwÛnªªªª¢ªªªªªªªªªªûžÚöZé°qó]¶Ûh ø^"2HãmÉq·\ÊÑäùCK<Ú9aê?·ž/=UæQsïþKñOór¶L®Ù+óeüîöáöµÜå(Û·dœA¶Fü+Ç¹œÙ[MïlÅißf0ãµ¿Çáúé]òÈDÀ38ãßUNá+¹¥~èq?,zÙÏÊÆ
+šTÙ drÌ]ÙBØþ3Î]$%v»'Cé-"×¹Žõª=C9yŒ\èÜ·­#äŸ°+XøºµýÏÆ:ÃÒÖêèÇžÃ¯õ³9à)àvEu`]Uý5Ó<$Ü@¿NÀÏå"ÅKU«SQ~:zwQÙOölõ4PéXZ6~À1Óv]wM"ZŠšOIj¬Ï=ïöœÐžzÑP²T±ùÆþ.ùlÂ-á©e¿ÓØGÌCàš B'5Ö5ËÂ¡Øms&ëfT@UEÍÛþ¶,|K²HÆ#øtÙ§p¢x¹­×9ã:vSmr9(,éétç¥ Ï€ÖÁ&ç÷ó%J)+ùšZŽA1l^Í¢kVëÇ¢¿aÌ€löQ!û}~;}¡õÕ6±lÙZ«-Ç!&?Î×óU§žË\Þñ.ø¬u£ê(+~/-vúÑ;(TåS\+{+xçD RB! `CÜ¡ ¢÷U²§FIY": 7Å}|ü¬ü6Ú§O(ðBRû¯)Ð@è«Xc¯Ñû6ÂÄßYÉhÆ
+v÷}œýÚ?þºý?gÇ<U[ ÃPàjå:fÏ4ï@Î­Vd;.7¹50 ¹×â¯Æª÷GÚÇ¶W-ÐNX(ÍÄKVÛLÂUs¯£ÌÖk SW3Þi¢æçI¬ÈÐ SBå.ìÄcå !M\Y°ùXõ7ìx~kŠán>ÌCyÊªz²¡¡|ÂÌB"O4ï`Ñ^Ÿ<JšöÇ`c>C=v·Vq®Ž%	$K»sš}+F1*Î:¶LøI\ï ÉVe·y¶ÔJX2")óÖ-ä®f©$¡JRrQÆÿ5¿ëñÁŽÝ\tßWáá+)]5.IŠÒ6Ç0E¡'Î!ÿ É/ŠEjÚ¹ubäÙÿ$õW"œÕ\<ÔÐù ²)±íœæ¿ÒŸ«ä|DæbTñë#øoXk±}g}£îíP+œï·guXxÀ
+Œ îöüÃŠ€rñ!5v\ó±sÙ±I£Øå/ðv9Ëè±k¥ü¬è8M glèTþqÝ"É\-kšjÔÑäðÐÔôÝå'ØEš0övíkšÍ7O[žoá-P)} ÁšX?¹œ¶í~  edÇ@ï¡·J`4Jï>{Ëô|>Ù]/W	œŽ8@º°H+q²Å"&Áõ0BÒÈHÁ»	e/Sp§Ú>×õº|ñô_³=Ë<<¯ÖÔWÂ¶®KÞ3ŽL²È*42$TÍUËúÉÑŸÊã:÷5A/<6IÒÏpùöY!xóHøðûõâ[iRKåvmã\äP BÀQ€ÉÄPt³F~@dlÃâ³žFý0µÝ²-œgwCx ª=ëÊMî«vfn.ÚvH"cê!­ÎWJÜÆE×âðÚÔH (
+6š/#BZ,¯QQ§7ú(Ùh3XÕÞÌF±rv ï`l=9µâ|³jÆÔ°ÏÌB xO}UoŒÎ¢9F-£$ÛD±³¿·°ÞižOž£ã®nòWvad/Øe|ÈÑ²PAÊÝuJi&NÏ4ÜiŠÅ!ÖVH§Ðëgt`*°ð)4º~{'TxÜVP,;Áç=ï°A9 e	õßÏ~c4Šfe#YÔgWÅg×4``ºéla¢o
+s8ùd°ØÃMktÁüvÂŒSÒ$)Ö5^ÛÝ- @xk}îš<=Ò1ÁvxBº²ñôÔfÓ'UýKðìjõ?QÝôÅoÖnÌ«oç#m|ÒKê
+ÜÆéçGÖâØµœC-`óJ¹Ú²±bô{çš-°ÔÌí°5á\Ó&ÂfÛoÜÝ^&°||\|ý·ó¹åéà_Éçfðîö]qqìAüÛ<x¡è>kšm°¥Ôqý ÈÁDV q©á\®@H¶»þøá"Q8Î%©O×ÁÙÜÎwžÀÌü8xÀWO¥¹ìÌJgbŽM"õÏdÑ×X³j£ë¹þ6}Ìùœõ¬O5jlc¶Ôë$`§Ã1 ¡IOOÅ~.S~FnA¡Ú6ÈÓ°Î<î§îª€ÃxNúchhë'­ªm7qV±bDßcüzØà|ÀB(#©wÆ yPÀw¯õû;Ø !J9ÿíj|»òY]ü,ãàiY¢»Ûaó¶Ûâù+Œg,»í
+~êÂQ3æïÒøD®ÿlö÷HøßåFÞFº?p+Åœ¯¥ÂL^2¹¡>f»õÏ,:GÏÕo¶[¯÷(÷cåŸ~*W6}U>©ñ&qÈþ¬³+_³êÇsžWEÕA],ÀžØ²×ÌŠù/èž¬ó¶o¬@|BßUvZJI>¶Ù¥Á,ÖŽžx	4>i¹öäïwãÝäËp=íÄã²¬÷;Z3Ûù~©QTy Aº+ÄÍJÅwkž6röÏÙûdNkläéyùÃUáä0JP9íÒf=ü~wÖÿgáËo~§×¿Õë³Àï§¿¶ÆYkÔÖž6D1/j æÓtZ&Å]ÚÇ>+Ãpòmd	'QåchššÀíÐÖCZ×WàôjJ=`Í¹+°psŸk)ß548mÖåC}jçmñ5[NøÛÁZ/ÁŠ5ßWzu¶RLàŸ)àTyS(Ê±>n;ë`'N\4X.úbÓDš}ÛÅqk]ÈG¿iÂ«¿«€ÿº^mc©u¹ÙÛ~Uõ; ÕZ5jBýÒ¥åâ¢šêŠ*²1ÐéttOåCéÐ»AúæsPÅEWÒËœúØkVõ ôÅãM:È£ñðã±#{*i¶ÇùºìtJ;(³pŠUÁ`>ñ/VÒRBµtyü<?EÖzñ¡oDnâRl"[ÖGñf/3ÃCv°Šþìü®£Ž-a>yŠ§Ã56*ÉÂå#£D#¯°Bú³Ã²|Rèp³¬oNMô	,ª|7Ë%(Ñ%	Ä 8± ?{µÎ£búñf€BöÍÑpZBž:©@÷¯TÂâ&Iè-Ñþ'niÄæuÚZŒêî¡ŽAEu'íayô#â¯.áÕœ°Ì)&KXÄ³k+é©ÍÔðÎWs~fIjþ	íçKNÎšÐ>à¹$Z)*cêÂÂ¥¢~ØÈf
+iXÖAûÎÖ¡*ª 
+
+
+€b! 2!¥uã4{n!€=Ñ®ûß)	ÃÂ9õæ§Ëìî>aïá Tj72û{{õ&Ø UÆûòŒ	Á _"s§1·Žå{pç"é±ßIÀéæAä3	£hG¢ÉÅîyŒMnxsV°É;Â¥H=PØ/·i¥õô|;~ëìý l^pô,Y$>H4_WÛóýÇóÑTe€H×ìÁôÀ?É7Ñü°jÅÃYæPxÞlº`GHq2DŸß?ÙO¿.®¯øŸn¬|Eè4Àãðû(ãñQ÷àRvÅN² `üþ«#·7Gëíö~üsõg^z§ÆZ[:(%Rÿ)On°Ôé¢H$€û5ôßåÿx;§P ~ØPLj€W[ÿYÝ:L¿ÎË#ïOÁÍp0Dü¥ATô²@ûHRp ¯ÔB!ÔßÈ€€(f¡Yb%*¡OdúbâýÙôÎ%eš?¿2a îþý¿Â?ÌüBóÃebà äìÏ¿=Ì{ÐwÀvMÀï\ªIr0ùŒ/mJÜb¥§åwKÿÃ£ÒÿÕG­Èmî¯.TýH¿ceÛûÂXO§[gŒ±ÀÁ §%È)J ,ÁrBÌÄ¥É2ÀÌÆ%jä:æ6¡ iy10¬¡8Bô|háà7ùþH,ëýQþj±ðï:èjÛlŠ0m¿ñ*5÷áÅë÷ó¥€h¶Êš	|bHSðÀhJAA(5iLw«z%_Á~ëàÐÊRH² d¥O ÌÉ{ÆÚm/Ò8NãÓCí¬PïÒCÑlàwj"LÈãý4JÈ(;`K5Ç©Å£  iHaà£òÍäÀtÛÑ<;üÏ®ì-Ç;\þ5!êÿ¶å/÷í}¬ ò{ÇÃ£ß€ÇÛR.6Ç÷NÙé÷ÀwE$B§ßíýÜxaÛ~zQ¬švX ¶éÙV5¹IÞ¯¹¶×&ÿçñölÓSlAZRÄ%#dF.
+ê©¯Vàsý§~îb÷AuwÜ`Z'á¥IÒ{ŠßÖÀß±OõX\Û#ÞKP*(iy:v÷Y}(¯©u±éé,oºÈý0z¶|AÃ:FñQØÂ©:UüU]]Vä/ëÖQ8UµË}>ë?1ÌtÃ¡ì}ÀÖÃ^BJFßkéß·ŠgàêÑJà÷n­ºÊpã^?¯Ç}ÓF:ýe	<!ÞÂK|ì:þ<òžz= òp=P}*šøš³kÉÄ¹Ýš>çŠ-Ö®YžCb _×
+=-Ìy,JHÝÇWÂr1av§è3Y$Ê' HôÄ^­Wš`5¬¶or,ÞLÑò&±Ð-y
+Ú¶q0l€Î%fkÖÀóºÉY#ßß`üßµ	­­ö/°P Y8ò(6ôh=È}Ík$€!ûÿñJ?-NÚ<»ÐË³íôSªxÍ2wgáüñÕíøE±+[Îá_€à41.)ŠHÿZ¿ŸNXH(5kn S
+5äü(ïÌŒŽŒO@ÄnÙÓW¢"RúmfŽØÎÈš×jKÎÝ\ Ðžá0?/3EoÅ²8è¯71ó×iÉÐ¹LÆDèzóß~y3Ò#ÖŒM2}î{¿¿š_Ë`Óy >f7/¡ðhÖPû|%ªïóu0Çú¶v«1YýîëèS])ÈöÝ$º8¥î}F!¯Okµ±V²²ÞU1ËÌZ"pÓhXÝ°ÓgŸ{X²ÞU¯WÅZ^WzÆ1ÕÝ±&-{ÉÅx_ÁÎb±ºUã#KâW°wU{Å­j©Ë³±ÀÇÝùuñÿ/ÊÕ§ãÕøã¹A#©s+¹8&V
+Ò
+Nz (\Saî²ÁÙ@àš¹Ð9]ž°»ŒÍQbóÄÄX¥¯ãåØŒ¥ñìÞ)îÿÓõ·ÔStpIê/véP5Fr£&V7h Ü9CúŒZ5O××>Ïž	ŠAý\ æîXxâgÛtl+ndª3ïVµ'ÏùV6Ä  ¹ç$áÀ ×`c15K+øµp	0Ï	A÷šD£öÿpß·×{õ·0ÿµN}Û= >ÑÁ?Ùe{°gf¯Ï?uÌßæ\ëü+ßÂù¿F«
+ØîO2÷iìŠGâ#nÿWŒzþŸ6|šlØ°€²Z£*<¡n>õÏnMã4­?°
+ÛYFvãêËŸf³¿$ÕL^9øØP€ý|ìÓö|?	ì>ï^Û0õå~¹ï9eÙ3táhårnÅ=¿<;ÆM¶ÈÌÁãw«Ì\ÓÏÕavöx7²Z³±5ßF?os$Ô®¹þ°6ÛX6ê@<ßyäÍV'ÛÙÍœ±òÃ]gTÃtŒÈng2ê·Cé¬Ðm
+ô9øžqÖP/°åßVÊrâmÊØ7RðöývTøP)2¬¯øúçA»+äHqÒMi'í@ÇŒ>Òyq_=ÛÙ®ìÃÒj`ÈÀÆkf6cïÏM$YÇJšæŠEØË °ñœºÎgÍðÁejÌÓ¥Ò ð:/Òû;cÓ0}R÷ÿ¿$3]ÌÏæ¿mák.÷KÏy×ÐÊ3Dói0,3FîB	Ä=)ÉCÑŽ,IDqãud×µ|ÃUEžò1^=Î^ÂµYÆHÎB4L³tFzÏ­2xj3Qëáu ÏCÅ 0-v4pQL£¥Ê#9U»
+#÷2ÌóPHuêŽÏì_Ua­zÇ·}ŸêŽïØ¹ûW_NÁ0žÏ³ä``œ¡U)æÏ7§YqÎlk>.ÔØ|%¬hÐŽdïÝ5®.¡QŽc€3;Š{fïYºíÃwëÄÈ³©3k@3þRëÂ:påØ+¿eœrcÝªýih+]P¢È3}L'ÝLŽ:
+<ÖÉFgo4Ñ$ïHžYÇbîEFÕË%ÖÍÕc(Û&`²Np¢÷;FºcÓt/Ëgk2qÎ94ë
+ŽŒeôÏœü Áð¯;2g w®Á4
+«èèÑåÍà6Ümä)ÈýUT¹ô¹gtññ2èo¹²çw5OUY-ŒßtÌ2úÜÇÍ
+Ðú#ŽÞÖw5bd6ßJÆZ4ÈÝs)+§d<«.0*×ÎÀçäÞ³ÑKøh²F<'µÔtÙ"(UŸöL<aÇ8wã¢E4Ôðú'TdèGmy1%u>jTI*Q§Ï²4[S( ðk3€àºÂ5,£v¯iRj2Øú cU1­ýšØ)Ô¢·µ!éQ°\vëöEÊsÓklßVEÞEÈ}«Ì9Ë4ÃBî	Æ=ô+'ÌeÝØÌÕUhÚ}{1|a6h>B;Vdn¿ ³ýoçH*FE¥¢& šF	bd¢aª`%jd¡R¢ fh"HBHRJOù'ZhªCý.!û¡üIþp*#QþQ×çñ+yMõÄ|Aîr) C ömÃ0Tš€9
+Ø	#çøZû'òÉàhnö9ªJøþL}{ñÿÁ×ê®štY âBI Óù7ün~ÄDáOÏø0êïQúuwü¯7œÚš-îï'NŽMºû5BQ_òNšáðFá¿3wL$PÁâb³ÉñýS·(ehYéIçú8råÀPÀŸ5wÝÒïLcDl!Ú®m° ® Ò-xYí­éK'È<©`Ò"ŽŒ³G
+}þí,åëÍ	l(x"ºB¡VZ1äÁH	&q_f<ÐßóL@jòä eã(uè!jlæéÙhsV~wðMh)°a¢©"afxx/ašéõkõ ¢üªÃK Ù,ç;Äz(>Üp±âÍ0Y¢RŽ­7§H°H^²9±ýÛ<æîI@íŠ7llLŠ£KgŸn4žöÑêº=/*IÛë!6:@P¿;ÃNYñË\ÞèK²å$#¢š'ÔñXÀjá8&°"G` XíÌÝÇ§«éàš¬.0DŠä@ôVa6QT-H¥'óÞÆõª²IZÝ0j3J€9ýoIèÅn8Q ÛÚ5+ÀÜu0nd5³¿yõ¹ÇÈïÝPnÝÜ&Ûörù8sq/66k^'"vPÀ(åà¡á²ð+ÔäBÆàà±%žKª*yÞ;6, £ýÂ-è_hoYÏç[()ÊÆÚ@Ü·ê1c&X1¯A¶*=	#'HÐ¬c}{«»9äp+`€  »1ÎÕ"õ@kªOñbð	÷VbóØÊlÈMqšf 9ÒFÐÔèº+"úãlé®Z­:tÆg$$ Qu$šš»(ÀDKm (¶v4h32mì9[e;Ì1  Fe°ÎÊ§àü°:	|TÙ`põcš5Ù^ðÑæ#®Gc€*BÙªÏŸAU 'A<.2œF¹[æñCËåcU6Ú6Þ-D×].P2êHFH&
+IEIA€Rix«5Hq@6Ë2ld³ËHx7RéÎvßc öcÕ0bÀÁX
+áB/N®³÷Î<:xcèÇgIÖ|ßA>ãúâ"$A	"@š*(	"`¡ H'ú²I¬*¡"HçLîØHvžl0ûò#ÇwŽúzÝÖ:Îv«¯p6Ðõxjú¥ÞEÎçÊì |>x5ò< ¢@À|ä>]·#µFL?l`¿bËE×
+·Î¢u~-RAOBfb6K©)æ7¥¥dÅ9C§G[lœ¬"Š²ÄúB4D4@\<wÚÌÊï0"Š(€ÃBpñ­õ;ä .¡¶(]!uKp0AÏF(&bãÆòÁ²26m9±8¢H(ÜbG<º
++gŒvzDD^X`EÙRâù1ªY
+¢{ÃÑRÅSo¡LYCÆ{Ô<;lpà±fröÉÚ€È:3 Ò!ìÂºëuÉ_¢YØ0ÖCWÑ)Äí€DóS§ó(:åúµéVŒ}7)Ïß6ØTËv]#Ò>þ|zíR.r÷¶°\}4+òéZørò=WßOå€×ñÄâú×æ?=ÞÂô§l1XŽß3|<Ï$SG¬R»ÂVÈÍE ÷ÔÞ6ã·ÓÚåÎúövbhºê Òg¡œó×_ÌáÒêÛ==oZ¥Óó&<Ü y/×Ýð¿pïÜ ¹Àwà{€4àª5ÚÂŽ(üwRìV<ÞŸÞtÔ}»l/£Ë=CÜ©¶Žl$m(óùâQÄ`lÌª3îÒÈ^²Ïi` AtžtÕVaõC Çx,F¡ºx`óAè¡ÞâÃIØç/!+°ê!ËsQtÚUWoÏ×ßßüò f
+tù©ÙçO?þ$ý©@Ê%PU5 5RžRÆX5FcRFX!ýÖCÄ¢€"*Ú §#û,ÕSDJHÞ$µ.Áÿ =¯+~O¯õÃõ)Õ÷OÌØT4â±îêEOœ3À3ªêxSÐŸ-êZcË`LÉrÁŽHq-õoÕÏCiøl¥06õJ\#x}p áwvª9âabBPúO@OóLÉpP?7éúÿyú°Ô$ü~{§Õî¯¶ëó±%\Y«B1¡=áÂ?GÄ3G¿N.àâëÔšàÊßKWãÏéø}þ÷._	Mó8y|Ë_Í\ÝbúèšuÔ_[AÓ4ÆÛ¥h¡ÿ_áUuYÄ
+£ÚbGxl]ŽGãÁ¥7µ§üó&i a¡Š/î{Žc9Óœ,YÖÜ¬êÏ7ò÷ãZþÁBfHC\-º%ð Òæwà¡Ë0-7ýûæåßG°óÁ®	X"ÅágùßJf €	d>g«?b 
+?Ã,>ïü `÷«[í;û°}G³FªÚ¬#(Ë?ôPýÁ,õ"ôµQÍBõ/×Q/[@D_V~­¢;á»îÞtdaÁß«8©¡Ç{TŠXÂ0'ã®Ê&jŽåVLÐžB¶5!bns©¥fÛÞvmËOW×Šó3v±!ÔB >J.rÒhd` (70@²È3ÌVëb,(Ã,,x®nA9£åó{üþÍðúDíéáu÷ÐïÇ$Ú:=°	Ì räXåü!y$µ{«;W<$CGùvpõ$¡ÅO»š8ÀOAyób>ì(1*m~7JDÊå Yú`Žÿ¡0p¶ø»Âi5	INF¥Â¡Õ!Žd	A©J2¡S,%(+l?,mŒ9)(   ^dÚI»ûãúò3L}æõU4ÓgkÌ¡{õ©#$}öhÏßtkÀ¯}>{ïzh"*þÞ9+^ª
+z{ž~ñ·3èÝRÂ»ióYZï×ô×#éÇ;Úò€PäñRt
+Å4¬áánØYþ$hkMØl×PAZWRXÆÎ­¯¬fÐ(w}ÇvS0$ÓËã£¯S©3cFHõb¥éLrfÜ°$§®Ò©÷\ºD®]î+U@:6¿:,×AŠE PÄ5Â	9ãí'Ÿh£È#š{gxeEŸxŸéÐ¢§¡þYžœaqöÖÑTæÙdU!{JITœ¡f*]lì=A@Ž6ÉYhT²Ö7AÍš|ÆÍ§ÊlÁ¶úñóiŒÈì°m bôë²Î€;}3~DsÙCÎ1üBŸ?_.`{(zç~W@(±ÊNk,l*º
+B2¢dÁ©iÔÇr p¥ù)?pvÃLIAUIK .ÀðÕNÏ×L=>EÙ!§ÏÜ<8
+gN}£öøJ·jÇMÏ@øT²Ô/žÉÚÕò0ìNßËSÛñûýößåÐ8	p{#RÂ/µÒíš2×$pmFGßð^7Æf×Ú©rO¯¿gÑßÛx/Ì_œµSEÊ"zdEæBE°¶¢nÓ=WàYS^ŠêÍ#KôÚ»óðiT1DêŒ¥€4({â
+p{*ÏÌ¶nžþ$ÚÉ!§$ÎÜj Àäñøà@|$7íôà Òå)$Õ;î_º=ç-{à|qŒ¥{7ææô/Äã}}øûtz[=ŸÓjÆÏ±N¿ Œ×Ý/=0$:`t°7|¶öZOÙÄâb/ÝÀáô©«PÏÒ¿^¹ïÐzs
+šÈ¶ÎNï@5ýlïÍ+3ßÏ yHAJR,2¡!JùÓI€²2
+²-4U"²0ÀR) HD©*L¢HÌBD)+#2HÌ€(H%M K¯û{ôwòt>?7níøºÏnð/õþñiãŠ%ÙºÛ{º·ècšës Íã^žòí×©Â÷X2€$-gÜýí;	}?ÜOTLÅçxôÈ.\7ÝŒ{÷öòÔ_€I*ELë®¹C3¥7·Ò^Dµ¹Íþmé ÀÆ¶MtBåãôÞ0¢!DBÐb9süÝ|cõ^_ãÊD¿eÜßRVxÛk3]œÜ>|vÐIP6Çx|áÜàt©?0 U©E%À]äò;0.I¢÷óœPIq|®ct©Êh:,[²M6Ða2çœ¶Îöú]íÎS8ïùÀ£çÝ(£MÙ5=°ÄIêõRPÂ$BNdÄË6c0E@H­ ž^NßŽÙ³Rl7ç»Êí<ºz!²ü3óê<+^» H)þUx ­t¢+¶ÍSÀ]VÞ<ziìâ	ùž C @ëö6/)Pfw^¹à
+?+C¶,ÂÅ^;Ñ ºv?£»êk·}¯Ê!ðš,&QýÈG÷ù7Í×,rõôÓTö€õBTkªÌXÂP+$#ÅáÖºA#ö5GIq¿É»1YnEßxTÁ ¢IJÂEBÊ`6ÇÊ.€(TkïãÙ¢ï¬ÿO§4øgBê»}X H)Öp±C?ý)!±ŠowÑ65­ÆvÔ©pj³@ñ(fÆædjØŽ3Ñ3ÅPD 3ÙàÈóî1¢¢	(:'+¬3·?iû¬í§?ßG  ÍÒì/œî€}Œü÷E9ÀdÍPL€íÉ|tržAªøtFþF0`iP`px{âT°éÂøsæºNEçœä÷ÐÙ€¡ÁºMTq0î@\NU}óY=K µOePi¥éM"% :#[§fY=Añ{ðùc=±ÇmwN=)UnaÔ!ráh;+KaN8`wÈÏ|ŽÒÍ È°Â l°Kc5	#Â[:Óx*ÎãYÆeU3uG®ÎÀ5qöµ/Æ~?¿WnFdùtGÊ¯ SãvPKÔ7ð£««ÒÜTÜvâ²q\Z\²Â»šókû!³±Ã¹(#TDvm}MI.L5÷å S0&TA ªiõyRT}VT4Û[u`X	 7€Qn$J£ÊÊÞw§ú
+ø<:×^Å#dÞf»@	{§ÀsÕdG%DÕW"­  &ð¢-VnŸ®®ßÞ>¯<è{r²,5söÅnŸR	O<=5SPÁY€©núÙ!]Ë=®n¯95¥üÎ)Ù1 8¢¬SÚÄ &ÓSºa6¡¶ûªNü0ŠÞT(2&A  ®/ŸÞîŸ·Æœ`Ã;Bû3H×³«rñÈÈ7€»nTÍ¥S­ãÒ_Ë6$6ÉánÞ;:®q`~JtÛ±C:ÂÕ¯N7m·i{œXl9/Í++#Ž(,Ý÷÷ÉÊÚ$¥OÆ@áþŸ&v0O«â 9³¬ÿkßðj§î1<ÕË+=¿ð_°®!g~4Çý,õ1œß«WŸºŸåùþ£¿ëvì 6>Ù?³òè'T»«ñnÛc?ÕzN Ií#XÞIÉ»[^|LG7µ Î@]±*Me3Xóçùx¶ÛêBü©ÐOû=ÆÓÀwµ£¶7îIô hö¿À+gß.¢Îâó?œÃÄ>rãÚQE-QŠvzêÏsvÇš1$äµÝµÏ¢æ2"¡çQ^!øöKl³/^³'],€iNâyÄŽË+ÜÐ=Àùåz!ëU£êîO0jg( <pÚÊ°œ3é=p€ #Õ¢îfÄ+L)í3ôEßŒâ­ó«in¯!mD6@¡€Ê×1€ åXÆv:\*þH?ÕÓ/V	ëPzÙùB")Æ5r<E6É·Ù-aU>µüš_2ÈÀîéo	Ù&,jk¿NV¢HIôqk 9Á¥@ÊuÙ~Ð=$
+Ž³Q OÓM²¿\E2à!ôìË L6ÔÌDY+œÊ$gjéð=LDÞ@â7Ål=Á
+">~ödŽê$QòMëFÆH¿Ð±æ+è¬:ËÁò~k8P íÇ<}Ëç<úÃ1a8œjQAªeÆP	Ä³ÐzÏ'8Q¡èœý;Gn¥ ÆYºXd	_¢ìAú±ÛØ­ÌDÂmý.SmófFìuØÉñjŠIyùqGPl¡åÊ|&e'®&è|d4;!~KP\6í{\;XPw"s5ýDæbâÐðér1bX10iÙ<t»3¥ÙFcS8R€î\T@X¡Žvë×(Éû¹+Œ¯QwmS4Ml0Ûõ<lc,á« ) uÜ©Þ@ÈP;C ö|ŽúÔfö,N¹wh×o<Kò(6Ð6¹DFá3ŠÌpt¯|_Vw»ú)lkÒ£YI!ÆWUTÊ£']}Nº÷Ø+q$dÐÆPäŸù¥uÄbUp\L($Ÿ¡¬6žEbÊêÅY®QRÜŸ#äÚhÖúc ÓÓ#Úæ®'MÇUÌÛNÔ3nrt©áK *€gÚÆÎoXžG3ÃO¶R+ ^c¢·iuûSñtëmvQÃPøßGßòïÃ}rÒ9y§Äül>é³àñ¯·€Ìê&"×Z1µ1µQÙ%æ®-~FõÑ|Â0úYÙ7sÀ°JyNEùägÒû°ofÕàE®
+Vq%­C3X!:î]%œ6qoP{US!	jš#%¥$;jgÏ;Æ³!Í
+& Ps
+* Þr ^g3ªyô(ÄÏÞœ/ezÑû%ÓÛ¢ë/|Äd YRLœ­A/zÓ$÷?À£CÑCj!ªšÝ9Øß¥³k¡Z@ŒY}1ïLzÕ<ðŸ€êÎœŸWµÒ@RËr€fšmsFeRðÂbVÅ!JYRlH¶Ò°9~`éñ/*me$ÎÑV%ÙÔž[°ó3Ù×Ldš¥0]ïPnŽš;€QiIè¡£q÷ão|Î	Tk¢8 (HÄG×og£«Õ×4Ù¯êFð5# Àô|Üð¢&Ð×Òï|3»¿œT£Îø	Îè°ïdÂEž~š-^×OOfi¿­Í5šYý+cì_°vs-·Æ¶5Á/~®SCÆÕÃHoìõÈ!B€¥ Xd)(PhJñ`?¿ ° §šNÕt²µ/ôÄ!4s5PL øÁþ?$?³þ,ÿ/÷ÌI8F·Ú_ÇÌ-5ÇÔ&&Î¥¶0f ­AÓ­àÜÔ%)+ªR¢±¡6påS ufÍiiÉ1pjï÷õ¶2ÁA¢2¹12ÈB[þ³
+áæøî^0êº0ÔóqYŽF T£iyŽRœèëb3ÓcDIA$p)JÆyQ°sÄÚÔV`ÖÃ(àÂtT%Ûç¡f(UæŠbÖÐ.òÝp8ãUŽÌ0!ºÈvRÐm.JèÈ (GÃà |=]Gõ÷3ÝÕÜ:er²"Vv ÔéÙï§¿Ý0êò9Q	ò¬ÙCIqIÄ^¹B ù§íëãÐòMÎO/ãyê"bf Õ_zÛÛÓ®­Ääp 4'×û²€ôÁ-5æÉÿÐ.#Ž|"üýßdôÁôÞ:Ç/O§WìhoËÖç¯âà	{h£ñQy_c{U÷x¥$J)\àwþtùkjêê9QÙ±ôzwfa¡¶}ÒúÝ?z|. ÄèüôTHA}Bê}ÓŠ¶66XêdýP6œ8TOoD4HúB;ÇÎ>KA:š€ïûpSý±«%ÉPÑLDY M1, êPÔ§ßØýPUQHmŸA³F: ÂM )
+WD'ì¢€$TžâÒý`ðÉ|ÀÇjøËõñ3v$nfX:²cZÒVÜ×ý-pÝ°Ûù)|	ê-P=8$CeâŠiž'|5UQ&jÐúw;ðmfzËŠ2^
+húé€ffQBIßSU»îèt°Ò'%¥
+f|œÔá1#èï€ìQSWŽ%UU×ZxçØ_¥WÔŸÃ3332²:x_$R{UññwÊÚS¢ïÈìP×Â87ÌÆ#"i$ÝŠÌlyµ9Ñ[`â ÚdjnáË T9ú(œei8Ý8@=0¥æÜÝ|-+×µð00üú÷àN$ÉNº=ŸMqSSQBar 3¥Øÿ¡íÍärF"í ÊÙGªüú8ÜR+tžôH%¹§gC Å§6Ç³C^-~d	³·ovÙwl×.ÊšÙoos€AT[Ä\8äZ9nk`Þ-Ý¹ÓÄqÉènÀíÈVðîë[¹õ³è­Îå*.EÉôF]¹ßÃ»wºÜUøh®&ýÉG&D4*PqQÓ¶Ê]}GZßÊEùBpO(^49A9Èý¡3nÞJRü0âBJû¶/«©© ÂKÔ/k_ÉÓ=öî?74â	·(T'^mYCKTwö[Ô2ô<ýyAF0cMdàf1¹§4Mš# Ðtì/`·@z'À=bH üfèÏÎìÇJTÄYå£æÁÌo@A!xF6+€`	ÐÔFjUl÷ÛÕ,Ï~öxëšò:zf>®rŽÇ§?¯Ç&×Íßß¶û4G¯*© ù÷¹}Rq@ÃàzC¿ÓØ$6ùþp§÷Ý.DRF
+B@/Ò¬ËEÑÂý;`pPSBèXC<ä[Ñâ<'áÎNã€n«5¬ñÆhŠÃ(J{õ­Åš¬ou×MµkÃ!(ÙáÌîÂšÜQÔÚRx¥=  k-Uì,A¯zn¶X«&.v	ÿÊd
+4©E!}0Àõ-ïHI³^=1®Æùo3Ì4k~Óød xÍ`±²¯€	ÑoÏ€]:3Ô`:*5øowŸ/g%£'*Ã«®«šÖ³£[J©Â5bñCUDø>@EíÃ%ÜcãgÎ+19cIÒB°¢TÍ·BÒ	ØšTÔÛ±8"Né\ 1À;|;)Ú`,PHh{œ~{ÐõJBd4iuMÓ$V©4;µRÍèp>ãâ6Ø:n=èœ!^°pÈ"èf"K.ËnL ÉäíPÉ HWÜ9Lwãç&š<œäŸîu¹8I'"Cpô`É	,ÒÙŠ#=uüðOHhšñw¹{t<ÐÊ:t
+Ù@èQCPO§È3ãÛšßäÃL¶O4,æwîœGd,4}11lŠUÐ€ðK
+ÜÚ; r@pAÂÕwn6x{<²í`¡Èu»æ$HÍà/ßéõó2+Ò-èH[Î\*Ž5«[Ìm³mß ö¡|ÐdGO»ùXcbAâw÷c:A7¬9ÂªI»¡ùªæñ	¡±×»÷+ôBvè·ñK94B!Ö¡ºù¬DÒvQT
+j3ø	òïŽç¬ ÚeŽXJlOlT÷NOäÀ	 µÔl/z­LÐ6bFgøFihÃÁ!}"À4£I~6GyÙÂÆ&ÍPÑ Æ¢ù<ZÙ2!Á1QOb¥¹kCéå=hãayÖ18ÙnšùŠò [èé5Ós×6J³{,6FŸônÖÎÎóê§/ç44Õ:ú "\(Û©ÊEû÷ò	;¡ÐMÞcÙ$ f®bröRÐ0E%[6²ÕwÓðÛÿ|ÙnÇ]D€ùÅï«$>
+WüH>mìl Ë!>ž ÐOyìàº@;C®ÖÝ:¹;6È`>1+ïðOs@4|" ÑHùÚ?@hþ.>²#ø`@œ$H¡iuá¿'Ì~PÈîrn€êÕßÌA¹L{QbšìŸ9ÄåWvlXŽIv®Áîæ*š×Êµ^1UTTU°§Ï×/Í 8AäP°=ðOñ0Oq1<;4è¹Ù/SdØÖêÓ>Î¡ñ­êh b\TpÔfs¹¡ `>!4ÙNýŒ{5·÷Âxýnù|NP?[ë/MkdÍmFYŠ¹º-¯¶HîzŠ7Š c'YÀ²kš_ZÎHÈF@`TÐÑŠÙæAÛ	Å:ØTG¬õ!¬Ÿ| fh²K!ÀlèGRS é¡¿(vŸö~ú[ÌG mÁ|kkRåcêqÆ¢dòÃÐÇy;C(Oo3åsÐè6/Bšd€"RE)'lpN`M.û»úÛý~J#Àôõ÷Í:¶òcÕö·haâ
+
+i]:5:1"÷E>0ÈÓzRÊ3Ód
+n ^Êü¡å÷²$|à:v]êl®¶nÜáÔ¶H<µNnFÛò1 bêwkÂÃ0×ì)9=èèr ìœ×E{þJÇRRöB:z>©íàgvµa ±ŠÍ1aÄZi õœçÎ,²BhF£1Ôóµ#ÇÊ|NãkÌYAØjÍwYX.Ò£fîÅšsvá¡«q@H6 &ÄÈ(ÀHnfŽ»#Äá 8<²vc0Ï{`,X¹¬t&$wªà_`pAw`¡¹XÐÅ¢GDÂ%££@£
+K$)t00«F)ª~zÄ< }Oy®ßP²Š(žTÆì©zÚI	(àÀI}=5ÜÃ%©BBÄSŒ÷&Ã²ëµPß.dë.š©NNl0Èä*Ü5_,£/Ócj@r&8Å2M4\³åÔÉ³Ð7$ºîw.,ê_CëP É~u5e­¹ÈTÀª¥]9
+Z#sðW6ØÑ·ÑUlM³3LMféÕã~<J£Î'*äø6Û:VúÅçm zVïÇê=ì³yÐŽk|Ã/lücrë!à*&)b!GÉÛEççõX6txßr]ÎÎÕ6=©¶ÿîÃfŸQL40{n@éÏß¬w'R»È8>ÁKç ëÜ>:ŠB8`Æ#}€ùLpÓCc!Úm<RkîŸÞ `hjs=fðØopô=§ZA¡äåÀ¥{þÂÀØã¿×Õ(Kép,>íåÂ=}ën)Âeê(³Ó^ŽÒâê ÖÞÕÓVh¢ÞØË¯?"#DÑdÄžÌI¥(MnHL%` B6Ã80 H°%{TÉ(ÊRp¿,)u³D]Z"!§Q 8vG¡Îó0ÈO!ê`x¡kEÉd$,ðAëÏ*H2|_}«ÛÒ<­ŠéÏuDÀ!Ö\Z Ý»FésO.nAÏ€ÅB, ÜA3hjõõìIDVÃ ¶Å¢2"Y`6â=»ž9®0+GhEQÄÈb¡pau×NÕ_3k×É!nCçÉS%ã¥Šr\èUÆÂø.ÓGµÊMZLd}'át}ØŒC©!Çy©8TF
+|@&ÞêpJØÁÑC ªÇGäHÅ£A¢v6ŠD«3*« ìDœÇ÷h)ŠÉ4ÛËåñÉ9|Åç&Lô¿x×l:ÓkËhýì~öDÚßwcæÑÐícoÅðKTòÈvÐi##~SËú»Ißô'P_¡mFÊv77œ-£8#ðOq{»úIoO¡ñ¬|xr°ÑëA¿äÔÁ,é$Ê@zÃ€Œq·ÇE»p_ònãs¬ ©HXu€è"çå­ÛÑPŒsÚÅì$Žy¥&!Ðòì¶ãD*¡!PéièH6Ò°šX CÀ8
+žÔa\Ë³³qdn$©8ÑmÑD¥²Ôµœžë¶l.0®DDŸ°îaJ³­&o÷ì7Ä2'^§e0=Þþ®£±JÅ@±íµãñå_zÃÖ/èÒÕÁO²RD	Þï/=œýø«íÄùÛ7=AV?É6ëpqN@`æØ t	±Ðã4f@×cÇs69<ÜœÈÒ¬ÉÃë,_*£n-mÃiiÉÊ©×¶@Êâ)uayÒZIgnëc:D	D	8ŒÔ8pBNH E{åÂµM83Á9=ÊèÍHAÑº­E9ŒY 2«ÙT $$ñæp¬žîÛUMCÙb(®€jhõ»€ËëŒ€ß­á!6€:A×G¥/€BÚBöhÒ¢C¢¡÷ûÛŽîÑÝ©9¢F=1¬ãjŽ#sb©ÕÃÅëI£€ÜÓvElVŠ VþeÛ >y<w8BDD°Â=âîhõGúCAž8±øÇÔqw§Ä&¿<RÆHhüî|Š~:æw¥êÔ
+	æµöVŒ¡6}ìöv§µÅRþGà=œø!ô*swí/ª«ÝÌ/Àè$@Ñ,Üæp Ð¿]¹Ê\ %EÂt:þ§aG+bxjýAlllëñ\# ¶ÉOËQ£$.o &uôzÍ«`JJ»¶:²Q2"ÌEÉIihê÷÷	ëßt}M£eD4`áÂB!ió'§ì=Ü#îû	÷Qv±flêc¯·ÞœÆÆÌ8§BæN,CQjÊ¥dªfª1Çþ¯çÄcÉå¯å·£yÂ;¹¹ôÔ Šat{@ÀžÙLÆéF¯³Ep@@:.úQé,éÒ(D8©Ø\á(xpmyŽÐ2*¿³dµÑ¯eÄx²ÏPœ#ï¯ÖbJØÄ«ÚÕØÚÀuÛ§q
+üK4øÆ8óò€V&ìî9M¡!&5»0F1ÖCäY;ó®J£þ0{»Nøä	ú7	xdqûXµá¡Ùóüc
+/MGóªVÐómbŸáùa±;¶U×°íyxaE%ª€@`È@,è=Ê²ZMÖÕ||Þ€Å§üvL1ÿ,í¹ãvöÞeŸõÖ÷ðH6¶Òè¹ÿ~ÊÃbÜîCËâÜräí©­¶(Ž"T"ä.¶³œË\@[ÅMÇÑõ~ìDB4ýõÀh~±¢ÖÖÿ°x!e³L_£eÔþ7JíÐêLM\,1Aý(çü X¹õm>ÖâPÕ!÷Ö!ž+0I÷ÇE)ZIGßAaÛoÖp2#ŠÑ€ž:G>žt;Òïîá#W¡OÜqÐLÁŒ€bÔîKÜh¢òß'_ò_]wët<`èf`ä%uÃ¬åCl!GJu@ÈëÄdLù/áÆäv|Çdy'j§0ŠrBÃ%ePf:.ð28öõ±äyh¶=<Œgg€êéÕ7r1ßwuWWg_§jwø<Œ÷Ž°/ÏÜ:EÜuø0`€{û¯$«é:ë
+ížÙ¡Â8çËdUÃ"²ÞEÖzìék3ËÅ€Ã@5Ëš&ùÙ¬s`Ó%3ÜÊ{rØ;À^=2f;B·j²Á&A°2 èQaáÀ³ž«ÐåÀÏgBŒÿ$ ±ŸÙÜ	\ UcÙš >@r8Àîs#Öþ¢9æN3 Ç.µ·­5[VžìqÂg·§±HCŠ!âôíLRðL<Ðõ<ø5Jl8uï¿h`Àà«ñâmîJ2ÌØx:wJoÍLËC{Í6õ.6iØú€4¿l$ª
+w9ÎYçÜ&ËøV£A0nêš$3HB_À}r9#Ð¶#JåËœ®êèŒP;ºúV<¶%#I a¬A;\ Òœ:t8tSÁ&`Ò¥qpd1CoM×5mÉÀ4nN
+ndà§cäõLsfShôæjp:h7»]î¢» JC6ñÀŒ0@AL2ŒÌ,ý*ýÂÀ~æ% þê¥UšHÃsüóá_B{n?y6_š=ìèDÓÓò6í6/ËAâÇÔ®ÆqÝ7vR»Þê$&GûÊÐÛdrÀª€²+ËÒ­03>šJCPåö©N#ßÞBPEt<sžä|øá¬	ÉÀËNî^ºÑKhÔG'CxlÖMêR s,'4Âh]³€`Ýàñ¡:'Cž@u-âdÁ²Ígz`7á­š£	²ÍÝ<.ND€1ëÑ®Œjå<zmRÈCÿKÎÎ®ŸP$°ãBeÐ2a~uwq(9B;Î!öªìÒ°³×wISë=~§`éüWr:YÔìQ©Ý0ã-ÕÉÌÒ(éÓÙòx^[zö(^çT0±D;¡ÆìØ97r#AS,| êß¥~r/ jMÍä5&Ç£q°Ž±àËØØd6Â1è2Ç5o+õÄîåÒàØvbµô+×`|Á"9òQPÏ"(hàQdë1Ó6ölp4Smï²È,Ç3œ£Õ	ïPôã""ò*Á}aÐ=
+¡CÖ`õ8,öŠÂaò}]Û\|4Y3®ÃšdV1cÚ÷äÿvM÷(Øù6.ªÓh ,ÙQêÉÜ+Ï&ûg®ØYAÀ€€#&©=Aéõ idl SS¢\@!ÈÐwì>9#\"hy4x]p*Ê$$^Rà4pp(­ïÚª€ä ìŠç+à8(ŽSqÃVõ2Q"^æêOaÞ=áêãËb(ÖMÃÓ`þÅ>èÞÙ¹®ÎµÄBt28|úIGxK¹$*5IóòŠý1jA$Á.ø{j9M±ñ|=7<Þ$.N&WH·R¿»áË\]ñ8ËåA·fo,ÇÕñ.ºÉÂá#Æhá3Í©ícæ@òŽ%	ßIà%1òÛÕ€;­òqr
+RžG§Ðø1s CR€ Œ>êCKj¶Dª³0ñ¿z^Ýà41,¡=¯X=¢lM.ª$²ÁŒDBziš31:¥4ëcCÈ,íÒ²ª­CŒGž~7.p1²0 Ôä$$6yéûÏðÑõ§èXbá3Dþ:ðh{šòÜÃrua£X:³úC	)N5UÁÉ£yØ*
+(ñÿ1Üx"ÝHWPZÐY°C÷!y.Cþ³ÿïwýñÿ÷þßöþûx{}ß¿ØüÚy~ïêôŸ,_ùšvÕy£%¢aÉ(£Yùlpb m;BŒPbA#CKJßë©2B%Š SÈL
+H¬~Ó_<tª#I²DAM
+=¿?Ø~N!+ýé»×)þØHtöüfÚ¡Y}ÐHBãu€ÇñûoÃ«Ö8ü=Ý¢)®N»KEZþÝiü»ŠÕ1ÇæŒ?Û©¶ÛH61Ià~j]]!d€ÐsQUDLnû{ôbì£ô[SfZ2)óE>g\ŸŠÚ@7Þpf©}HoÎñ2XCÇ ú,Ë)aÜ|ãèðPþyQéº÷wÞ4Õ.mi8b³k4Hï#Zwæ)~Ê¢Dd;ÀÍQ3»o¶i ªdZ@¢FPvŠõÐí×W_ŒÈ>Z8÷àÆDåq€cžÓÔÑz õ9Žr°øÓ
+Å7Òd£ëAþÈ%æŸþýüçŠ¥îœ1+S þét2EQDÔÅÛúsè@öwwÂqÉÕ=0Þ%&@æCÓ'Ç³l*í%åá<8ï"*uÐTiBx\NBJaCqr*¬WP}i«kàÐl÷±LâuVöª =^[_Cf oòÚüC#ÕxH$FÄ«¯~Š.·þ"Ðª	»É¬ÈÑîøàDH=d)§Ç]îkjoD¯ØjÒiŠ¡à	]É	Ð3[eÓŠû)¥ñ#ŒE.æy;puYÐ)ÍàÖ\Ô86Ûzo²ÅAç|îêd#­ôâÈRžù ^ähfHxPE5
+|Ðõ»ibjcWüÑhl.d¡ª){>óñÂuð× ü~ÿ©ømÛç=€Îxy?9æ';4 %/ËfH±AÑúàè:GhÞurò£Y D`ï93ÂÅÉ?yûÆ	å0ß@6=2cÙa+2E€ÄÞ13ÆûË¢ÉÖ.B¥þ±u*9 WÏþpãºÜÄïçëßÝId9`Å`Xbž¥òÉ)»Ò bQ"]4ö/ý4YÇGzé ÙÑã©ÍÕáè§rþì:3®4DbãYÊNafc5	äÖË·Óc2`,íÍ	ìÂºEÔtá`«
+Û4ãÝ¶mºN¬óûP7ÿQŸ±ÃÝÕDhÉô¶'wu¥eœñŸä€z"~e0B±šyOò£ËÜµè¶DúÆ1%éty¹ô(EºÂŠpîèsÎK°n`g2sÑ}ŸÒG¹2àA$Ânk×šÜ ãÓ:ÃEé
+êao	DvX³êØQa)X){ðêüÈÀ)š"îùh*S>mÂ{ûÃödr,ÑN_HBHZä©6ùa\¡LBÆ«öê=o®	C¡þ]­l Qñ-ñ7$ÌLšÄŽª)BÄÛª0\ÑÛbJ ;ûì¢4)r'µsUcô8LòwZß¶Ré÷»åèÔTêii©¹­a¥FA)$Jb;#8fbrÜYÖÔG±ÿ_M«mu mÞ%ÔÝ~apÄ0Bv 'ÅÌAòG°>ü³[ñ¢@šRgÓ\hTO-ïHŒÖ3ÃÑ@85óY =µSO9BòŠ#ht¢÷õa0ÑS8LC/?fòs3nŠa( é ¡ªë @P^ø%Èôó×àCôÂ°.êÖKôFjÃÕ#ÔÈrlRÔ7âQc:zéßÅ7|ÈªÎÕ-ÏuÔ6á> £HDT@(R uPÊpêædPÊe1Òì*:š%ºnÛà}:<bzCÎ<ý ×~VËÄ×nAø°€êË_÷È ôÉò*u5_°&5Ë#Yb)ÍŒôHÄÛm²5Ñ±7WÔÆ#3iÍZcf¥cÐæïYU`'hÆ¬Ò»vÄÕÈ-6E-|5ŠÝ¥*é9æg.X¢h\äÂF	6µL²&ùÑ³|ó0JÅMÝæé·[8$[qá5·käFÕ4ËíºCv-0á2³Mkp:©G[{qžÆðeÖi `hªefoö2QpA[PL0þcónõžã¯×6ÍêÐ !DªFþÎuhb3x³Ã¡/° ­ý]yr¬ÆÎISjhÔwAÝ®Æž5ºåÁ)øzupªë`Ùåßišš¢"&cc6`ä·>MS¿ L@íÈÂ[ ÈüJ€6
+ ÁDYX(Ë`0b^s2!žHçERhTtŽ¹e~à~(?1?« Àx%Žq}«X²I	"ÒŽ17GVÀÌ¬	&üù€6·*(\M\Á²ÚS77.&	ý3,qØ"âsªD¡3ò>¢Ô»ãCA	!ÈB|?b(P»*_ÛÃÎŠãP"zíêÔNR÷V$5¿²ššÃþmÏb€Èÿx;ŒÍ°?y^o$p=[×trýh ¶®îÃ!ÎçÎž2ßDõ­hÛÈ=V;þ>?¢d×jòdb\©Ræ &04Ëa01«È! Ó`@A1DHA!Ð&ÐdÅûE_~û9q¶ÆØ*·°JšÌ@iIñdtm÷âßXdÈ«ÄŽ·øÅk%Pî¬ÑçAÜ1ÔuØHI {­}K ûöãÞœþÖŽL%9åºGÆST±Ž(=løÚDáÌ.uÎ8u8F$b¢bö<së§óÑ; ?€8	 ;àlw& L°2ÈÌÐS#T	 2B@RA¡Q0S&€cyª$}
+{|^GzNÞ}ç}hÀÉõyûó.6ÏQd3×ÑÙ!N8¶jÐlZÃš+¥Â÷³ ôÙ±µx;}ýÏoÅëûkâ»ïø°¡\gö\·Ç
+øWm:·k}Äd±SÎÊJ®ØÓKÜx0Übê-C xRô4Íøç!VŽËáwÆHÊ°Mme8_¿à 2 5ÄLfo	'2ÎöåŠú9.% 0²ø³Ç
+ÒæCÃ	
+hÄ
+º«Èkÿ-#	ñ3 oO·#Æ¶žæ×fr €hÑ£pÍtð òªX%ªš€rïÏ0ÑµßÁŸÞçÇÇryTÅ pW  H$EÅ$=éG	ÜH ñÛLâ$ t4bA4\{Fç;Ò¥ªªªª)J¢©J¢šª"ª jªªªªšK>*_zÛ+Q¶fvÃÛlbøøÕ6±.ë-ROTâÑ(6/­³ªYcFÊÜ4é"ó`¶Agk³Èœ:9Ÿ.âUK^.+ÔR²÷Ž­Z-0*&F[(,+np.Eí°áŠ®m¶eJA.3(®ºKËZÂ±"a[0ZÀËfµFº9}ží7Ë3c'S	Ó7GvføBfÅNþîIÎÈçví\ MìïÜ¡V:\Œ"2û$œ£t,Ë.Ù»Àôv­4žf0à¶Q¡5Æïg5ž;èÒTá
+Swà«Eî,úâP -"õÃ@ÄÕ+6 9p_t²À¹+a ç±¡tÓYÁS:B6,c}î(Q³fîMÕÜÄ&Ç8JRfû"4áûÍÃ@A	$]Hm"iá)TF"Kv`sÈV¶öID· ((]BA±`ßF Rü5šŠ4â¢fí(:ªÚ;YšhìšàÁ0¬!·^<û^fPñhpÂ`)3XòAãnÇXíá·ãsdÙÛS¿VGÀgù
+€@tL68âè€Öf]¥U¯ÓAÇä§c\Vwp¹@MyŒŒW®ìm aF	£wÇžh6iž²0:êsZPØÕtî£Ýà)®[·8²Sî6¡üà€"#Óû<vÉ%$XÛ
+uë6TãÞR«Ð2"K%¹·~Q2ÊÐ2§Aw `1 ÒÍT®©p5¥BÐØÍ(Ÿ~øÚëÖ]³IçCÓÛ;+M€$E A®¡ŠæÌ·Ž#±ôwN
+4,î¢ÀCp«4j±®mHJá±:@Z[:%e&i»BÅ	dA£²Š[|%²£2Æ8ŸOjSBÚî P(ßu`»"#1×,0SEw±@¡×@rs@5Ì:3ÄÎ'à·îqjç ·ß E5qT(t q·ª=ÁÕÇã>#œh0)ñkí]l|= BH7U1GP+t ÑN  Àí ±ÌÛÍÄN{d¡øA"/Q±z»Ü}ûsÜŠå¬ÏÏZš£Prí¶:¢áèôÔIGÒzÍz^àNÒPDÑ3	ù3QIaÕõæO#t³Ý{a ÁX.×I¯@Â.=(4~)I3ÈkV£"=  pŸºØ÷Ù»OFI1U ÐÆÒ^ °F@jvÀñšX¢}9ŒiÓ¿£Ï}û; 2!§I'üŽ:QØœ[þ Ï Œ¯Ÿ)ÜËvRW_ÅCkX
+ÒOHt^Ø~âVÝ¿t_Ô(eq¯Ù@ìM¯hâ`ÄñHò£§eò4f ¯œ÷k÷»žÑ°TL!€Ç¯ÖyµÝ}t3ÜaæÅLYüÌzÙ8w\Í3äz&ï£KÅN.<h7xFà­uR/UÐ;®¥á4"UòD A(Ñk0ô$®¡4bòhÒZmóïŸð¹Àu9×ßeGZSÎxL0häà-9Ð²¢-
+>FæÍp8tLÃ(»ö÷:ã»Ð_vFAAç*ì9wcÑ=CÄCÀÒžîSNRß(uZ-(ÁÁa,ŸNîÃÝÎ³æ·` @)yPCh¡úlq-5X²íšFÓ]üUA%ó[ÛkmVsÂt tT`EB
+M×<8#ÂÛf"ñh¯3wÁ08,Æaâ69$ª¹q¹ÏÈWÆûæR9¢aÔ*ùR¿»ìs€±CÅìxšhdÄÄžpo?L'3ŒfeS]žØ©ÔLÉø>¬:\ÂŒEvµhÐ&aÙÒœ+AdJiÌLCÅ:CéÄE?Ó&rìöõùFŠ§Ýwh}z9v7oxû°ŽÉs¡ ;Èf¯bzN4"`ËKB9"@ 	Š@Ð42ûH»ò:ŠW0Ð¢sPÜbõ¢	§25xy)ù!Þ«ðDï l"¢ HÙÐ|ðÓje {ì")@yàõ5@4eÄÔA#!#-þf}v§Ú]ãF8ÆäG€òDÄ7²Q?Dõ?0Àq#æ1âð÷ {Ë "[sBx)¿x0õÄsÀOÝN_¢DÂìò&WáÀ ìÍ€,7%mñåPâ-a@ìàâhÍÁEíÓý8¯óœ¯Ÿ×@î¡cHÌòð vÀ[úCS=ª§,ôÐ5Ï¬ãá`åóž÷­IIhÎ èH`÷'º³HXi¢©30"pOÔ<?!ö) àv: rÒ;"m]Êh_BZ	ÛgzöBÆð.¥
+R¡|IR,CÒÜbqoI:>âšÄ^Î®§Z <Ñ<ýòó#ºh3ž|³Jv£9Éç©^§Ê8j¯§&Ót`ÊcRÀ²"ÕF€HË!8P!É6Ä6¡ÔtwhãâÙí¬énµå©HŽÁŒŠ-ŠžjS³á&<$á£¡³D$@cI­v.v É(,ÊÀÉ+©uÃøq074NC4hO²<(íµCÃóÅ»GïÃw\ÓT\QÈ>U@4A"$ÂÝýK\Ÿ	"íÞ·çåÐxÙq	A]Ž 1CQB	ÎÏÓìhõÏ®Â!rFÇÔBDþK=žúÕŠûª4EUÖnBÑš4ÖèŽ+ñ	äíòŸÞ ÿEBó1ëÖ³£UDLCÿŒb® 6çSyú"ŸŒj?Ã0µ_ï~©ÁTtœTÐ©@è( Æ]Ê°É®]1Ü£Ò" dŠð¹"Tvh$ú Ö ØôE1¬9 ÇíÒy³<ÖD40A¬Ú^°}»aéÞ·_wp¡úúwBwàäHDÈ \Áê¬Ç2/Œ&~L²ÊóéÙ*`§Q,²=¯<Ž:D;9Qê=±5øa²Çs¶}ãqã ü$²&=fyáßó_šßóH]€6Ä8+1(ß(ÊËÁàï1$	ÛÈlÌOõÍ)×ë í {-z@l~¢×2{Ïé¿N@ÝF!ä¬*hH=wøÎ0®Þï@åHCúÃúúÇ7Tt6ÙÝßÊãŽó>Ça9Ë©Otu%ajóDu&ºU MÚ5ÖNÏÓ2Hžhö¿µßP!ÑV&U:.¡Ž 	ãÍâá° Æk°Òmò{i6> UÏÈ ¯u$ÁL ŽDyÉöhÁ]B9EUU#QTòàaúË§]ÝaØôz= %øYA¡NaJi*bŸ2Cøá\*j)¡hP§ä¢/êBÌ]I ²~Ø¿æãèF×¬WýØ6Ñ¢!ÈÄa$H G¡}pú>óšWî]Ã²×Ie$ÛAõAÍ€3ÝLÈ6ÚxåŒðµc%í_`Uº¥Ö©Ÿ&¯©)F<OW`Î<	ªBÄ{ÈŽw|jª#È Ô't8tüQ€äÅ@»¢·ð êüÙy®ªãžõs¢¥Ê/Aéj7Ê?R)cDYïâqWkSö;ÝÚHÊ±Ñ~×/ùÞÎ$®­X@SS_+«Q,U0P À\&Pfþ d*ÝŒŒí9Ã€		×È¡ñ3ârüºDš¡ŸãÇ¥§8Æ€÷£$ÀsÒ4æ°¿niª#ìÌ&)Œn6Î8DM'áÌ#06ÇpèuØ#Þ=üñÃàD}².MV' tàîU0?¯«ÎùÕãBNØ?åjB!SçBrÀ'$¡Hš(¥b¡ª ©k@@@±ÉšüÊÉ­.ÖJ÷jÚäQºáóÀBú'6<å4G_D9ŽbÁ³E4€y?Í£?­Ì /súÂèõÇ Cwy»qxPQšðA¿m-JqaÍ!±©5ùÉªMô<jÌ©·úò4«láFn%$n«e1CË°R¹Ê
+¡µwŠ@ÊÃ@ðÒ|#HwZïØ86ìZðÌàB¡B¡2j2þ PuÓóÃ*oNq¢¥HCwwï"ŸŒšP g¶LWöéF ÞÃg@qÅUUEQ`UEô?JPÊL8yú{fz¿Pb}cõWiäÏ©žûiÄëóÏ
+;íRëìRÍ(4ä¹óÚæÁÆW+n1Öö¶"šñ!þØG÷Q{×4DZU¶:8.ç;
++ã(ŽÇªi¥hhÜžWlSgŒÒÏ±2¡Œù'3îŸég¹üá£ÕT ¹DðŠÈ!ÃpCÃbfÔ5lPãnbÈmá°J*4p»öéžíÖÅI>ú:þãÓ%iš¬âFcÕ%èáàÈ1Œc`È-LàXµ	 ]HhÐë)e#A¬Pný[ÔïTdá5DFî='Y5FIkZ#uho£#ziíqãYZ[l @uüŽËF á6¥²ÆÞø¶ Ý7W7*M`@QOÑ|QàyUSo'Ÿ@$g®'Ù€Ü¢NÇÈ-'l¥%"òŽ¢£p_KEyó¿/H|Í;HAÖ Î0öçlS¬q¶Ú(n"ëóÀÖ(€Åº$ñÔ1ÂO¯gú
+d£ÈïýxSÙRO
+oMqM.š$÷|çO@ÖÎŒ×q-` FÏ)XIö8`¿ªŒnÎÈÏC4qÐ>YÌ}Ü2ôZ º@ ù]¬Ægh;Û²³§OÄqðgwiŽúrÈùft¬Œ;oš»ÑÀ ÂÊÀ^.ò·R·°BA7¶n0Ô·¥FdêÃoÊ1öqgêÙ¢5-úUÜÜêÔÑVYúñÖËå Oñ8Ÿì^FÑ	"gšâ`ù¢þã`LìCŒ»I«öuZYIÜ[â\eÙ=n^w§Ið|9Ïà¬!ûEïÞ;Î¥!bÖ®¹Ä`r­q­ÖIÜÕ r
+^oUÄbìtÖ& ËŽJ6X ÆÀàÚr0.xWÓAúÀÛß-§Ü)ïã~Q=rR'&d(¢f1çÈÎâÂ~³áëéÞv¢ÇÃ©äûk&ÅIÛ2ž{»tFË·úÁ Á =ÿl\#:ïqÝ'Å³€<97fµaøn?ÅžiÃ(ZQ%CyêáTÉÛÐ§Stï¬Aþ/î19ê6ºué#pŒ÷®òÜ!ìf\mïµ1×®AÜg£È9â§ Q;iZïß¿>º>+±¥«3$H9Øæù»NñôI-ãHÏ8AÒAÄ&ÓŽ°Ï<€¯'	\ ìýŠ¡åaÂÍ/RPhµ[.ë¢÷µþílØÐë°= ßÃÕm4Ï;	f#$Ä±;Tl$$IF*ÕM4Á3>£gOìï×íìY¡7ÙDÕO¿§ÑÜ®8é1F`I:Ýþ=/	×{`)C§)±O ìVe#M%,&vÖÎÕD=FGÍÀr9M¶e]«æSŒÝ+ÀÕü1ŠTTueß5Ynmë< Ðw3ÌG'âÈò€U1"ðf\2ªF×sBgæ®$²	þ2ïDH0b=ñâ^x³é×š±ÇöÜŠ»fç:h#Ú(X£äTÃZPW?×nºx@ÚKjCŒìh°Ì$³1]"D \%Ýz1ThDh©Ô4@dkX`FloTidÒ{Ôš£O»·ÎÊÚp»JóqÍ²êcG,ÖwMiVž ic1ãoP&²K06ÃÃÉ)(k&t¥Ž\cCm©JÔyCÌ^YýHL~Ðñ§.n5³FbÏ¬Ã+EºÓÄ¹-A»FHû=ãx$h°	l+ ÂHÓÞÖF÷&$ï²LÀ
+gBÐ9Xãk1&š- ÚumÒ5EàÙN ®"&h0Se06Ø	}húÌ	ÿ3eAí ä.°4"¶òQäëPáB³ÍJñïéëž·uÃnHu:¥ ·cd	!"áª°PØbñ~þ.£VÍ`$Ä`aöä<DhFŸ]ÐA¡PwNÿë/xÔAâm'dvgy"ÌI0È"nuÙ	ßáÏž:ùÈ=jKpAëíJ Kóç¯\cëù¯ðuø]>óÔ
+1ŒdÁ$@Õ4ŽL	0®@šY,UCIB%KŽ
+.^o_êG®GÜ{ª¿0ÉF.ô5Ë^	§Iwê`QÝVY¶²·sÚ|aòk~GÇnI€$$PðÒm»p;¡ÿ|]žÞlÒRÂé)Q9@*ÓÙ&XÈÁÎGéç£°Å@ìaa
+ÁÔA@LLë1X²l1¡ÖO¯ÃHu1äñÖ=EÑ¶±ò,nl&UW·JÃ}ž`O£ÚÒzÈÌÁO\gÊ0ÌÍÚfF-·RqËkÊxúçfXb@û¯ðêeû#{h§Ro	šµË~ØYžF"²?\Œ&
+GºçŽ|`4Ó|sk»Ã8ïçÆ-±"ñzìÐ=Al¢l*}@ùàíõwŸ«§ºÞã|biKP% UJECcA¡J7íãÇÁA$ó¿)©²)š&`šZêÇ ¢bXJŠ`""%;HÈsÓÛà}bÑ(Ø@èœ ô3ªÜ4m£ô gRâ)ÏzêSDAîC#?Áú:üÀrè¡ú;BiÁ|ÑD?)áìèÐ^[Î:ôJSÇ8Ž4"IÅh¶
+±#ÙWEñ%uUÌG#åš4¢'àGwÌK8@dõ&0I!gßáC_'æBLQ(<àÊ^øÈîÒ§³ÛÜXÂ|HS 	:gåÆ¡£p69:6(îµ vÜÁ^/ç­ò¢iJÄšÿ ÈÌMcEÒ_PP£4É  ôÓ,*£x=Ð{±Óø=x°æœ=aA»"«Ø?¬b8>éKgT%£Æ|{XoxúŸ£(ø žpß Èªu¡€OäãÑ»%	æ"<ŽÀhgÕþép!î 9»töó@aKçu§€»¬2ËþëÜ(XÄä/Ñç_ó²Höò²ÈÑ 5j £fÏCïzcíÊÖœ¯\ºWàÃ O~HF£.Àå×áz~|'yEmØmðÎÞè[äÔÙlÆmæ€|ÚgÏÑÇÉ*èÃFµfþë²óZ¥Y.ØQëÁ¡h}µ=Þçž÷$&#Št¬ŸÉpÐyôtCeCœB­
+]tw£H&ëô+7r!' ãû>×©ë©CëYØU[¥\³0Z.ò¯Ódÿyç®MÜ¿%FË~¬þPÓI<Ïë`Ûô|)/OÎOE Ì(¢ÏgúûO=Þz/(CÓÉnÐ§ÉOuõzìc> _$sI£¡:.ÝÞ×ÎTôºœ*:øyè«Mdo£ ÄûÆcS¹®ðgÕ>\ÍçóîSXN¡Æ%XØâN#,¢Að±;ÆÁP¶Wboû:Ä!pö5ÎeGM©	ÊüIO3£åyË¿oâ·€6pbA@P§ì#]'Pêm6YRU¬q¡üòôjÓ~×*ÿ	~FPfU¹#^ðXÀ{®Ûîck«¶ÜHTQž5R[!óÃJ!18)Á$!%cOÒ3GáÝê7¢a¡€ÝiŠ0*Ù!5fÊÈËšÕ;ÎàòìI°úaCÑS'V_æ}+òü>ÇÚmk-7}df6³"í³Iñ zºŠŠÛÚ÷¿`Ô:,ô*ñ4²ÃŠ%;ûòÌ,RJ0€"ÀŸÀâÒt7>¯fµÂ²t&ôXú=¡«>·Ï2]÷ŠjÈÜÍZ*MAêuŽHTh]qÆó8ûaFlyå*bdö2LL,Ê^£X­RŠTVò%%"°Ij5rFVaåŸxÐLÄÐQQ&aL£Há%£ùRP$§ð,
+¯€äM~3€íÖMVém§.ØDb&QAšÍa=!Wèà4L^¹ÀéŠÉp,ñÛ³ÝnaQ«±ªJè@FÇž
+á*KÆ*D4µÃ
+Ä(>~Sn@¯ÏŠjdÀœüÝCŒÛGÓÎªº1ŽHs \ê&yÍòËl(ÇÍ°Ð§^îé7w[qò6L±Ì¢ â3ºÞçŽ¹!p8F®ŽÅgÊ­svþ6^i°]^õ%­nõŽFu¿¥ðæcÜÉ«FQàõtèdÁÅTXÌ€kOB¯).WÌd¡8³Èœä=6è[Ñî;R+°CgQÌÄuayx1~Êš#Àê!8C§­Ó{	ÑyŒäŽžÔ1SN<­;ü)VäBBä€d$ Å	øD4ÒÏÐ€¢#g2Û{Ê*íGÒw"ñ6Çìºžb|`h@ú©Ù~{¯71ù[ÂêHóï÷÷ü»§1Ëp"`c'9%õ|èÒæºþÉ=ËÁÔ>N@	úEAòŠ7Â€|¹$d Þ&;N`dÅ"BPÉ(ñÌº¯aK5ìù
+wg¡ðË$>	çSyF÷s÷ÉŠ±¢¢2#ÛM0 5NÛfÞ""qX€-UîíÈX\à.*h¶<Üz)"²rEøsÇ3¿<!K]>á_X»Q5!W	»ÕÈª@áDÞ®Æ,7çž9#$bQíçXtRL0dd¢u ?'¿]­kîîÔjËÀ{)Ú¥í&U3BÈhV Û4ø!œÀ!úO§Þ nÕõ/AÁ"49\èX@(#R}r9°&ÏOàŽd67á²>ð"íÝŠŸ|ÅÚBôvš~	pPûk»äî³òû13ìÞmJ6Oä¿²ìöÂÌV6®vópf] ( ÍÔ ÿðüBË8K8ÐJÃ	µú-_ìË¶_>Ró /ìpYº]d peR)MfûçgùuÍëÉ2NKázLAœFÖâ<ò§nðÌ©q
+?©OU^rxÄÍoÄü9Ó³<3RkÑÁVÛ6NH¥5Ûê*3äº3
+e®i ã/K¬žaÀtpŒ
+(:Eq#:(M`aà"C#ŽØ9;n1œÉÈ€weTÉKÀ	%š«aPi­R!H[MdÃYÞúÎ?OGÝhKî¡ÅåG¬Y|º³ž\0~LóªEã& .ÌähÐaãLÌØÈÙÐv(÷Òë¿jTÚc[Ê8!åÀkMÃ]€iôlÒÝuÊÒÂjfB7¢ÉËk$Ûçg¢Ðš¡€îQ¬X"ªI:ïÎ³Œ;ZøuðéÉÃãŸ×fHçiŠSD4Hå©Hmjj^
+ûö	Öºº=@©!ÎÞ88hÖ8£s¢ÍñÂ63^1Îùákÿ]cÄV;£úTÜÓÑ6yhk©ª-áé!žlô"z>:	.#ËŠâ91L ùC×Ð#÷×aÁ!âlÒÃòÊè;jù¢>Ý±*C¥ýC  >&LžLœ§ß·|N+\å¹Èfr 9\ÜçtmrÍ2VAfŽ<Úû·õúžÉ"ìwj4hŒè4³\ã1ŒSœA€Qõú4Ù$é×ž$÷0õ4áøñ
+ÛWm,nÀäÖkSLàÆZmŽ#VÚßc¬Oz/ï#ni
+1À84!c-ªêxÕÒ©Þí«NyBÙäSw¬ï œäGË$Få6 tGÞ}ŠÑiÊÐ×cŽ*ãÄ¢<}VrÕ\šý²
+ãéo
+àóÑëJ£ÖBiÈ×e2heP$6@`ah1Ž«J2sÄ(f4FÃÿ¯Úá;#ù$Î"m~åÐ;¹*cµ¬²çÆ­jL}ZB}cç­Ñ·Ñ%WŽgÉ»0þhóGÖ~^Ð<vÃ©À5¢â;NŒ¿(Ù¢µÃ§?=€zõ£sù(*)AšR¡
+P@¥ÙbGØ8ý >_bºŒôYH6¶#¶¶Tq·(ÌpÆÌÊ{w8q$:MMì^gV(ªXQ[]W~ x«$@ãìùKœSÀãi\q*°­(¶;\1
+oÅN.ŠLD9iMÜp'žýó|êD4­	sà¡zR¥Ð7WáÝ?aÆxöÛDC¥ºÖ²(2ý§2Eò@``|Í-¡BÎ;Ôi·FÄ®Í7Í0š,,Hm!¶«tÂbe.=Àü`nTå OxFð ¶ì	papãÕ=¿g¶NÝiªJšB®$) ¡Í-òäc(HcMà¢$ü&(õ©L	cOÐZ®dq	±	«ý/&Ê¹¹¡ !ãºðä#|Êå5»%Pþ§3·d4=H$jŽ¹(=þç­Ž3y'ŽÆ¬Ì¬~X§Š6¬PøÌR[ïîÃ¯£ÀYR`U)h£Q"'}~z°ÃàK P/ =[ÛÅŸ³õÒ8	¬ {^îU ÌñhAvLj°H\	¢ë,Ws¥šS€HwáÒŠ:tÈëÂ-S;è°ºi¶Â!ó·;rkL;ãh'EÓmjèFaÂI *Šðf»Ígqëf"Â17vßuÎ§VûëØuÛâ$"°2)zðÜä3UÃKë[l`×mÜoWJ0QÑ"0 J+µÄÙkHR  ­ÙÄ ÞæÔ`` Àvä¹ÓÖ1Î§m\`6yA{A`Ldáb)à¹l	ÀQqŽT+*+]n V,º išj@Â L©#Œ¹5ý6¢ç$5¢çHRrÖã·BÍtYÇ!pÈkM>KtðèÍö\6`XZò¬ä)d dG jÁÔÈŠ^«£o²iŸ9Ï^wáÒs9pÂdö
+@	ºÀk`ªj1;ï¹T]®4ô­¬D¶U W
+È$X81ÔÌVo
+QÙ4` ÅAHe@M@.2d¶Ó094)MáŠŽíbž"d£<ž^`£¬A;k}=`­}ýtÄíAÛÌÕCR~£!o>cðZà*sI
+@ËÇÓqUrjmvMßHªÐ~Ñ!å/9rBÀÛ^·`ÉDÐŠÌÆ 
+0KÝÈ×Ï¿lq=Fo\/¥Csi¶DcHâöÔHÚß<#ÝAê» vqŠ#b^ÈÂÏ]°#¶^÷K4ß°DMB/ç9àÃ·.àòÝxÖÿI-Dö-RÄ#á«vWÉý|û?ÓEaºZÒÉŽR{U÷J§Â!MíÀNõÅÎc?¹ÀÇÉü@h6^ža}ß§<ÄôÄ$T·£ÃÃ§FÈ«ë0C}ùå:ûŸ9|¬èóZŸ_á 0£dzYÓN+í¶(ŸOBú=/ÑjxÂ6ß7µ§pòâ¢h9i21ÆÉ¢€ö!%3¬×mŸ;+àYó@DŸdi<ä2Ï.zñÄ$ßR>°»ÊÊc€ÜûorB4 ÌKô ^tXaM7.`RÊ9lÏš
+Ø(§ÐÎî^ÅÅ,!T6Å
+@ Þ¯¯až<yNE` š ÛLq0=§4záÎø!œRk[iï{w[3C4Èk2€³B»°Ôšuj©Í3 KAr-Î`b*úzÐÓc£kâî­Ž"®Q (`%/lvw)óÁúÐ7`ášvlñM+iï!Zh°÷žêéî þLþ7ìÃé,þlïìÊ[HÐÖ²&_tnÙ5í$ÿ€Gè 'ÚQµA°°Š?p'yØ:ÃÞ°ÂB€ÐÁ³$A"ì¥ü6@kÂIBØ@-(_gx+µúºvcpd¹ÌÂ.}ù ñ°÷€I¿3<1ÔÆØLöÉ /öÊ'ÔŸP¯j]S?Yö uAÇrCìÝøëÝß}Ë Œ èvŠ`vyEø$€ôÁ¥»ëBx<~ê0rL ïQÌ P­^{óÀT4òÃbô?áÄ1ö»ŽaVD,j'š6ê£,ž=H$èRá·lZ@¥gO<àªèmêÚ¿,ÖIlqõ>s$GQÞîŽ!DHý,×\iBPÅDTvÉŸí?ê¡\g<®ô¿`Kïÿ.¿`wâcY_Ð0ü;ª@òþiDj¥=ŒTþù6é×¯³²ùßrB¡P,*tÇh@)h"BJA	EÈ ÁXQ%	HdTBe!hD"@(XñsªXHf
+D(/Ù:ù»?¯k:jÜ%y"öîî!»|þa§g6ïCóýtMjHÌ&vÀM3Iâ)ßåDU8NE&b`Æ4%M4ð³öMuµÆâchŽAÇ«eóïàñÎŽc%A¥;å_8ÍônIààv%>â"$g\oSöäbQTLcCÓô\4PÔslrÊ¯$cùmÍx	oEÓ7Î>ßhAMSè|²ŒÊÅbX'4êiÆ×ŠPõ,PŽDÇ&/eL¡Ü vBÑ¿Ž²MÐ.Í\}B"í&ÝÁšåç§Š9ñ#z©GÒ:ë³u9CJt¹FbTóOI¢öÓjg
+ç?WÏnFöàve=_©óój#¡ÙmV«&m~€:F»uÏ:ºÕÕ,ÿIÙC¿ÕÅØÿ.Ê$7
+j°LÄŸYî¢Z±!*.¥n¡Ï<UMA4TÔ1I9JãQTÄÕY¯)¿%+[\øŽ¶»èÖê}ïôÐ-IKÂÏÄ0cOZpÃÏkŠ62ñºOtŽ'ð{Œ6q>ÏSFqÝH5ID"  ÐÄ!2ibj2ÁšTbªJ¢2À60üŸéCªZŠózCÞL/ŠT>HmUþî!ôãöÀvÆu;á±/ |s#Ç4õÀ¢åT-4P+Q5EM,FPa±!/ûæ IH)Ae ¢e"G¡;lÒ*À€Ä0Uþÿ²OüùV'øQç¯ô²8DÀ¢(SžØ_æ$Gÿñw$S	V-
\ No newline at end of file
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/Kconfig no-sources-new/drivers/net/wireless/Kconfig
--- linux-2.6.12-rc3-no1/drivers/net/wireless/Kconfig	2005-05-04 19:18:07.000000000 +0000
+++ no-sources-new/drivers/net/wireless/Kconfig	2005-05-05 22:50:28.201156880 +0000
@@ -221,6 +221,15 @@
 	  common.  Some of the built-in wireless adaptors in laptops are of
 	  this variety.
 
+#config PCI_ADM
+#	tristate "ADM8211 802.11b Wireless PCI Card"
+#	depends on PCI && NET_RADIO && EXPERIMENTAL
+#	---help--
+#	Enable this to support ADM8211 Wireless PCI Cards. It is better 
+#	enabled as a module.
+#
+
+
 config ATMEL
       tristate "Atmel at76c50x chipset  802.11b support"
       depends on NET_RADIO && EXPERIMENTAL
@@ -307,6 +316,8 @@
 	 It has basic support for Linux wireless extensions and initial
 	 micro support for ethtool.
 
+source "drivers/net/wireless/adm8211/Kconfig"
+
 comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support"
 	depends on NET_RADIO && PCI
 config PRISM54
@@ -355,6 +366,17 @@
 	  say M here and read <file:Documentation/modules.txt>.  The module
 	  will be called prism54.ko.
 
+#config ADM8211_PCI
+#	tristate 'ADM8211 Wireless PCI Card'
+#	depends on PCI && NET_RADIO && EXPERIMENTAL
+#	select
+#	
+#	---help--
+#	Enable this PCI support for the following:
+#	
+#	ADM8211 802.11b Wireless PCI Card
+#
+
 source "drivers/net/wireless/hostap/Kconfig"
 
 source "drivers/net/wireless/ath/Kconfig"
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/Makefile no-sources-new/drivers/net/wireless/Makefile
--- linux-2.6.12-rc3-no1/drivers/net/wireless/Makefile	2005-05-04 19:18:07.000000000 +0000
+++ no-sources-new/drivers/net/wireless/Makefile	2005-05-05 22:04:31.405253560 +0000
@@ -30,6 +30,8 @@
 
 obj-$(CONFIG_HOSTAP)		+= hostap/
 
+obj-$(CONFIG_ADM8211)		+= adm8211/
+
 # 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.12-rc3-no1/drivers/net/wireless/adm8211/Changelog no-sources-new/drivers/net/wireless/adm8211/Changelog
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/Changelog	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/Changelog	2005-05-05 21:46:25.807289536 +0000
@@ -0,0 +1,255 @@
+20050323
+- Added private ioctl to tune antenna power, lpf cutoff, and lnags threshold
+- Made txpow ioctl actually work
+- Added documentation for these ioctls
+20050307
+- pci_module_init -> pci_register_driver
+20050226
+- Change docs to reflect adhoc is working well now
+- Don't get confused if LinkOn and LinkOff occur at the same time
+20050208
+- Change linkup timeout to 3 seconds
+20050205
+- Use flags in ieee80211_data structure
+- Rescan if the link doesn't go up in 2 seconds
+20050128
+- Update copyright
+- Optimize adhoc settings on adm8211b with input from Emard
+20050124
+- Remove bogus test for monitor mode
+- Fix monitor mode opps
+20050123
+- Make sure we're not scanning before reporting scan results
+- Use time_after in ieee80211_associated
+- Remove NAPI from todo list. Shouldn't be necessary, I don't think
+- Enable LLTX
+20041227
+- Fix the 2.6.10 fix
+20041225
+- Updated suspend/resume stuff for 2.6.10
+- Adjust whitespace
+20041122
+- Crypto API and ARC4 now used for WEP
+- Documentation updated to reflect ARC4 requirement
+20041121
+- EEPROM better documented
+- Adjust whitespace
+- Add more CSRs
+- Remove procfs debugging code
+- Remove unused ADM8211_CR macros
+- Fix ordering in adm8211 tx header
+- Added some casts to make sparse happy
+20041116
+- 2.6 only release. Now working towards using only 2.6 API and inclusion in the main tree
+- Use __iomem stuff
+- Use a big struct instead of macros to access CSRs
+- Use bitwise stuff like __le32 so sparse can find endianess problems
+20041111
+- Change capab to just short_preamble
+- Fix reauthentication after deauthentication. Thanks to Olaf for finding the source of the bug
+- Detect hidden SSID properly. Thanks to Olaf for input on supporting this
+20041106
+- Reset authentication/association counters after success
+- Tolerate bad beacons/probe resp from stupid APs
+20041021
+- Improve eeprom handling code
+- Fix deauthentication handling in ieee80211 code
+20041011
+- Properly expire old BSSes
+- Use proper errnos
+- Fix locking (hopefully)
+- Avoid unnecessary association
+- Misc cleanups
+20041005
+- Adhoc on ADM8211B and up fixed
+- Whitespace changes
+- MWI/cacheline change
+20041002
+- Restructure code in ieee80211.c to avoid unnecessary authentication and association
+- Remove delay in scanning
+- Prevent stall timer from restarting card when card is shutting down
+- Redo beacon/TSF/link stuff
+20040929
+- Remove unused authentication code in ieee80211.c
+- Show AP MAC addr setting if the interface is down and the user set one
+- Replace TX timeout stall detection with proper dma stall detection
+- Reduce number of arguments to ieee80211_add_bss
+20040927
+- Put write to PAR in the beginning of open
+- Move adm8211_write_bbp function
+- Make switch indenting consistant
+- Remove unnecessary function prototypes in adm8211_hw.c
+- Other cleanups
+20040926
+- Fix bug in hidden SSID support
+- Disable write to PAR for now
+20040923
+- Update notes file to reflect the SMC2635W I have for testing now. Thanks nurey!
+- Use proper max RX duration
+- Fixed bug causing bbp postwrite errors
+- Adjustment to hidden SSID code
+- Add locks for future compatibility with LLTX
+- Better use of register defines for PAR
+- Whitespace changes in adm8211_tx function
+- Other misc. cleanups and changes
+20040916
+- Use RF3000 BBP register defines from adm8211.h
+- Another attempt at hidden SSID support
+20040912
+- Whitespace changes
+- Dead code removed from link change code
+- MAC addr setting code simplified
+- Cleanup tx rate setting IOCTL
+- Remove unneeded code in wep setting IOCTL
+- Use dev->name in private IOCTLs in printk
+20040911
+- Cleanup channel setting code
+- Try to support cloaked ssids
+- Init BBP for adm8211b twice
+20040901
+- Updated Fedora Core 2 HOWTO from author
+- Some modifications of FC2 HOWTO with suggestions from Per Bjornsson
+- Report current TX power
+- Cleanup error bit ignoring for ADM8211B/C + Ad-hoc
+20040831
+- Use DMA_32BIT_MASK macro. yay.
+- Use pci_dma_sync_single_for_device and pci_dma_sync_single_for_cpu
+- Add more messages for ad-hoc mode
+- Uncomment some beacon configuration code
+20040825
+- Replace "select BSSID" message with better message
+- Fix compile on 2.4 by adding compat.h to adm8211_ioctl
+20040821
+- Fix kernel.patch
+- Improve/simplify authentication/association TX/RX printks
+- Suspend/resume tested & fixed
+20040820
+- Changed dev->priv to netdev_priv(dev)
+- Use skb_queue_purge
+- Remove module options for enabling/disabling hardware WEP - no one has reported usage of this.
+- Updated kernel.patch to source Kconfig from this dir
+- TX power IOCTLs added, not yet used
+20040817
+- Change Makefile so it works inside the kernel source tree
+- Added kernel.patch, against 2.6.8.1
+- Updated INSTALL file with info on putting driver into kernel tree
+- Disable BBP warning on adm8211b again
+20040816
+- Remove alternate SYN writing code for rfmd2958
+- Return 1 in ieee80211_data_tx when we're waiting for the card to associate
+- Check MAC for validity, generate a valid one if necessary
+- Add eeprom struct for better code self-documentation
+20040815
+- Fix compile error
+20040814
+- Adjust adm8211_set_rx_mode
+- Separate ieee80211 and adm8211_hw code better
+- Add alternate SYN writing code for rfmd2958
+- Changed BSSID generation in adhoc mode slightly
+- Initial suspend/resume support
+20040812
+- Start RX/TX early
+- Fix memleak and other things in fragment reassembly
+- Make adm8211b and up use error bits if not in adhoc mode
+- Made direct syn writing function for rfmd2948.. not used yet
+20040811
+- Adhoc fixed
+- Remove unnecessary check in ieee80211_encaps
+- Retry limit ioctl finished
+20040810
+- Makefile-2.4 now checks KDIR
+- 2.4 build fixes
+- Move things around in adm8211_probe
+- Added NOTES.FC2, contributed by Kyrre Ness Sjobak <kyrre@solution-forge.net>.
+20040809
+- Move compatibility stuff to compat.h
+- Added watchdog timer for ADM8211B to restart the card if it gets stuck
+- Remove some unnecessary code in ring setup
+- Fix value written to WCSR
+- Disable hardware link off detection for now
+20040804
+- Adjust MWI enabling
+- Really fix monitor mode
+- Cleanup adm8211_interrupt
+- Fix value written to BPLI
+20040731
+- Cleanup in adm8211_rx_skb
+- Bunch of fixes to minimize usage count problem
+20040730
+- Added module parameters to disable hardware wep for RX (soft_rxwep) and TX (soft_txwep). Set to non-zero value to use.
+- Fixed ieee80211_stop deadlock
+20040726
+- Fixed crash after authentication attempt times out 5 times for a single AP
+- register_netdev only when everything is ready
+- Display pci_name when error occurs during probe
+- Disable postwrite warning on adm8211b
+20040724
+- Monitor mode fixed
+- Move the setting of dev->last_rx
+20040719
+- MWI enabled
+- Fix promiscuous mode for WEP. Some WEP disabling code for monitor mode removed, not needed.
+- Cleaned up TX rate selection code
+- Removed some unnecessary code
+- Removed Philips SA2400A - not used anymore
+20040715
+- Hardware WEP fixed
+- RFMD2958 and MAX2420 transceiver support for ADM8211C added. No hardware to test though..
+20040714
+- Clean up ring on device removal
+- Do not receive data packets before association
+- Tiny syn writing cleanup
+- Ensure packet is unicast before doing frag or rts
+- 2.4 compile fix (no SET_NETDEV_DEV)
+20040711
+- Big cleanups to adm8211_tx and all the code that calls it
+20040707
+- More misc cleanups
+- Mode change bug fix
+20040705
+- Fix select_bssid function
+- Bunch of fixes for adhoc so things don't crash
+20040704
+- Improve authentication failure
+- Don't start queue until associated (makes dhcpcd faster..)
+- Started retry count IOCTLs
+20040702
+- Save/use NAR register state in adm8211_priv
+- Possible workaround for SMC 2635W bug
+- Use AVS Capture Frame Format v2 for monitor mode
+20040630
+- Reassemble 802.11 fragments properly
+- Fix WEP cleanup
+- Removed some magic numbers
+- Finished hardware beacon support for adhoc (or at least the hard part)
+- Other misc stuff
+20040626
+- Association request changed to match admtek's code.. need to check if that is correct
+- Do not authenticate with adhoc station when in managed mode, and vice versa
+- Clean up some wep code
+- Eep. Fix bad rfmd2958 writing code (used in rev 0x20). Strange, the AL2210 isn't affected by the same bug..
+- Another silly mistake. MMIRD1 wasn't actually written to for rev 0x20...
+20040624
+- RTS threshold used now. Needs testing
+- Added a simple 2.4 makefile from SuD
+- Improved writing to SRAM
+- PCI ID for 3Com 3CRSHPW796 added. Needs testing
+- Some work on adhoc
+20040623
+- Software WEP fixed
+20040622
+- Added RTS ioctls. Not actually used by the driver yet.
+- Removed some unnecessary/dead code
+- Improved error reporting on association failure
+- Interface doesn't have to be down to switch wireless modes anymore
+20040617
+- Even more (little) changes to try to get adm8211b working
+20040616
+- More adm8211b stuff
+- Makefile changed to support both 2.6 and 2.4 (inspired by a makefile created by SCR <king_903@yahoo.com>)
+- Make link light turn on after association, not anytime before
+20040615
+- Changelog created
+- Locking bug triggered by more than 6 failed association attempts fixed.
+- Other changes that I can't remember
+
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/INSTALL no-sources-new/drivers/net/wireless/adm8211/INSTALL
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/INSTALL	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/INSTALL	2005-05-05 21:46:25.807289536 +0000
@@ -0,0 +1,21 @@
+Installing this driver should be fairly easy if you meet the prerequisites.
+
+For 2.6:
+1. Latest module-init-tools. Ok, you probably don't need the very latest, but I don't want to guess what the minimum version should be, so just make sure you have the latest.
+2. Latest gcc 3.x or 2.95.x. Chances are, many many other versions also work - I'm just not supporting them.
+3. 2.6.9 kernel or newer. You must have your kernel tree installed and configured for kbuild to build your module. Crypto API (CONFIG_CRYPTO) and the ARC4 cipher must be compiled in or as modules or the driver will not work.
+4. Wireless tools. Latest is prefered, but having slightly older versions are very unlikely to cause problems, at least compared to potential problems with the driver itself.
+5. Latest hotplug scripts. This is somewhat optional, but it makes things easier.
+
+To install, just run make install. You can also do make, then make modules_install, which uses the kernel build system to install the modules. However, if you do it the second way, you'll need to run depmod -a manually.
+
+To put the driver inside the kernel source tree (2.6 only, not recommended unless you run a kernel w/o module loading):
+1. Apply kernel.patch
+2. Copy the entire adm8211 directory into $(KERNEL_SOURCE)/drivers/net/wireless
+
+Then you can configure/compile/install the driver like any other driver included with the kernel. Note that the driver is installed to a different place (as a module) than usual when compiled & installed inside the kernel tree, so be careful when upgrading.
+
+To load the driver if it isn't automatically loaded, just run modprobe adm8211.
+
+For 2.4:
+2.4 is no longer supported. 20041111 was the last release that supports both 2.4 and 2.6.
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/Kconfig no-sources-new/drivers/net/wireless/adm8211/Kconfig
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/Kconfig	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/Kconfig	2005-05-05 21:46:25.805289840 +0000
@@ -0,0 +1,27 @@
+config ADM8211
+	tristate "ADMtek ADM8211 support"
+	depends on NET_RADIO && PCI && CRYPTO_ARC4 && EXPERIMENTAL
+	---help---
+	  This driver is for ADM8211A, ADM8211B, and ADM8211C based cards.
+	  These are PCI/mini-PCI/Cardbus 802.11b chips found in cards like:
+
+	  Xterasys Cardbus XN-2411b
+	  Blitz NetWave Point PC
+	  TrendNet 221pc
+	  Belkin F5D6001
+	  SMC 2635W
+	  Linksys WPC11 v1
+	  Fiberline FL-WL-200X
+	  3com Office Connect (3CRSHPW796)
+	  Corega WLPCIB-11
+	  SMC 2602W V2 EU
+	  D-Link DWL-520 Revision C
+
+	  However, some of these cards have been replaced with other chips
+	  like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or
+	  the Ralink RT2400 (SMC2635W) without a model number change.
+
+	  Thanks to Infineon-ADMtek for their support of this driver.
+
+	  The latest version of this driver can be found at:
+	  <http://aluminum.sourmilk.net/adm8211>
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/Makefile no-sources-new/drivers/net/wireless/adm8211/Makefile
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/Makefile	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/Makefile	2005-05-05 21:46:25.802290296 +0000
@@ -0,0 +1,37 @@
+# Makefile for Linux 2.6.x and 2.4.x kbuild
+
+ifneq ($(KERNELRELEASE),)
+
+ifeq ($(CONFIG_ADM8211),)
+	CONFIG_ADM8211=m
+endif
+
+adm8211-objs		:= adm8211_hw.o adm8211_ioctl.o ieee80211.o wep.o
+obj-$(CONFIG_ADM8211)	+= adm8211.o
+
+else
+KDIR	:= /lib/modules/$(shell uname -r)/build
+PWD	:= $(shell pwd)
+
+MODULE_NAME = adm8211.ko
+KBUILD_PARAMS = -C $(KDIR) M=$(PWD)
+
+all: modules
+
+modules:
+	$(MAKE) $(KBUILD_PARAMS) modules
+
+modules_install:
+	$(MAKE) $(KBUILD_PARAMS) modules_install
+
+checkstack:
+	$(MAKE) $(KBUILD_PARAMS) checkstack
+
+install: all
+	cp -f $(MODULE_NAME) /lib/modules/$(shell uname -r)/kernel/drivers/net/wireless/
+	depmod -a
+
+clean:
+	$(MAKE) $(KBUILD_PARAMS) clean
+
+endif
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/NOTES no-sources-new/drivers/net/wireless/adm8211/NOTES
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/NOTES	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/NOTES	2005-05-05 21:46:25.802290296 +0000
@@ -0,0 +1,48 @@
+This driver requires crypto api (CONFIG_CRYPTO) & the ARC4 cipher.
+
+This card has been tested and known to work on:
+	Xterasys Cardbus XN-2411b
+	Blitz - NetWave Point PC
+	TrendNet 221pc
+	Belkin F5D6001
+	SMC 2635W
+	Linksys WPC11 v1
+	Fiberline FL-WL-200X
+	3com office connect card (3CRSHPW796)
+	Corega WLPCIB-11
+	SMC 2602W V2 EU
+	D-Link DWL-520 Revision C
+	Blitz BWP612
+	D-Link DWL-650 rev. L1
+
+Cards made by Runtop (mac addr starts with 00:03:6D) do not seem to work correctly with this driver.
+
+Note that some cards that use ADM8211 are being replaced with RTL8180L without a change to the model number/name. For example, the Xterasys XN-2411b and Belkin F5D6001. Also, a SMC 2635W has been found with a RaLink RT2400 chip. If the driver is installed correctly yet doesn't work, make sure you really have an ADM8211 based card.
+
+PCI Revisions:
+	0x11 (ADM8211A): Should work.
+	0x15 (ADM8211A?): Unknown. Does this exist? If anyone has one of these, let me know. I assume it should work..
+	0x20 (ADM8211B): Commonly found on SMC 2635W cards. nurey sent me a SMC 2635W. AP mode possible. (but not yet implemented)
+	0x30 (ADM8211C): Support is completed. ADMtek sent me two samples of this card with an airoha transceiver. AP mode possible.
+
+Please email me with your card name/info, the kernel messages (dmesg) from the driver about the card (revision, bbp, etc.), and whether or not it worked if it isn't on the list. Note that if the card can't get a dynamic address via dhcp, it's unlikely to work with a staticly assigned address either so there is no need to staticly assign an IP address to your interface unless that's what you already do.
+
+The txpow setting accepts values from 0x0 to 0x3F. For the private wireless ioctls set_lpfcutoff and set_lnagsthresh, values from 0x0 to 0xFF are accepted, but 0xFF disables manual override of eeprom settings. Antenna power (set_antpower) is only used on the ADM8211B, and accepts values from 0x0 to 0x3F. Any value above 0x3F disables manual override of antenna power.
+
+Managed mode is supported and well tested.
+
+Monitor mode is supported and tested.
+
+Adhoc mode is supported and tested.
+
+Suspending/resuming works.
+
+This driver is aiming for integration into the netdev/mm series. Check the TODO file for the current progress.
+
+This driver is tested on the latest stable 2.6 kernel (with various patches like mm/ck/suspend2 sometimes) with gcc 3.4.2.
+
+There are two mailing lists for this driver:
+http://sourmilk.net/mailman/listinfo/adm8211-announce_sourmilk.net
+http://sourmilk.net/mailman/listinfo/adm8211-user_sourmilk.net
+
+Michael Wu <flamingice@sourmilk.net> (current maintainer of this driver. please do not bug Jouni, since he does not work on this driver right now)
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/NOTES-FC2 no-sources-new/drivers/net/wireless/adm8211/NOTES-FC2
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/NOTES-FC2	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/NOTES-FC2	2005-05-05 21:46:25.806289688 +0000
@@ -0,0 +1,86 @@
+(the following is a howto contributed by Kyree Ness Sjøbæk)
+
+The SMC 2635 W HOWTO (ADM8211 chipset) for Fedora Core 2
+
+This howto describes how to install the SMC 2635 W PCMCIA wireless
+adapter on Fedora Core 2. It could probably be adapted to other
+compatible adapters and distributions as well, but this is the equipment
+i have used.
+
+What you need:
+- A wlan pcmcia card with the adm 8211 chipset, such as the SMC 2635
+- A 2.6.6 kernel or better (i used the Redhat binary/source rpm version
+"2.6.7-1.494.2.2", which you can download manually, or with a tool such
+as yum or apt. You need the kernel-source, the kernel (can be generated
+from the source, but that is outside the scope of this document). While
+your at it, grab the documentation as well.
+- GCC and friends. Can be installed from the CD, or using
+"system-config-packages" and installing "development tools" from there.
+- kudzu - the redhat hardware detection and installation tool.
+(installed by default)
+- neat alias system-config-network - the redhat network set-up gui
+(installed by default?)
+- The drivers, which you can download from:
+
+http://aluminum.sourmilk.net/adm8211/index.php?sort=date
+
+There is also a forum (or at least a thread) dedicated to the drivers,
+just take a look at:
+
+http://www.linuxquestions.org/questions/showthread.php?s=&threadid=132161&perpage=15&pagenumber=1
+
+And two (low traffic) mailing-lists - which are found at:
+http://sourmilk.net/mailman/listinfo/adm8211-announce_sourmilk.net -
+announcments of new driver versions etc
+http://sourmilk.net/mailman/listinfo/adm8211-user_sourmilk.net - General
+discussion, support etc.
+
+Okay, so this is what we are doing:
+
+1. Install the new kernel if needed. Install the development-tools.
+Reboot into the new kernel, and make sure everything else works as
+intended. When it does, remove the extra entry for the old kernel in
+/etc/grub.conf so you dont boot it - it will make your card inpossible
+to connect.
+
+2. Get the latest drivers from this site:
+
+http://aluminum.sourmilk.net/adm8211/index.php?sort=date
+
+Make sure you read the "INSTALL" file included - the procedure may have
+changed.
+
+Unzip the driver, and fire up your terminal. Then do:
+
+- Login as root, including its paths:
+        $ su -
+        <type root-password followed by enter>
+- cd into the path where you unzipped the drivers
+        # cd /path/to/drivers/
+- Compile and install the drivers (this may take a minute)
+        # make install
+- If everything went well - load the newly installed driver
+        # modprobe adm8211
+- Then run kudzu in order to set up and configure the new network
+device. Make shure the card is connected and the "PWR/ACT" light is on.
+        # kudzu
+- Log out of the terminal. Press control-D until you are logged out.
+
+The card has now been installed, and you may configure it through the
+"system-config-network" alias "neat" GUI. To open it, run "neat" from
+the run-dialogue, or simply click RedHat-menu -> system-settings ->
+network
+
+You will see your new device there. Select it and push "edit" in order
+to bring up the configuration dialogue.
+
+Tips: If you are using gnome, you can activate the wifi applet on the
+taskbar. In order to do it, right-click the taskbar, click Add to panel
+-> Internet -> Wireless Link Monitor.
+
+Known bugs in the driver (as for 20040831):
+- None AFIC
+
+This howto was written by Kyrre Ness Sjøbæk. For suggestions etc,
+contact me on:
+kyrre [a-with-a-circle] solution-forge [period] net
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/TODO no-sources-new/drivers/net/wireless/adm8211/TODO
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/TODO	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/TODO	2005-05-05 21:46:25.801290448 +0000
@@ -0,0 +1,25 @@
+
+Things that need to be tested
+ - Multicast
+ - RTS
+ - Fragment reassembly
+ - TX fragment.. tested and found to be broken.
+
+Things to be completed before being sent to the mm series
+ - clean up duplicate frame handling
+ - fix ping problem (improving..)
+ - private IOCTLs
+	- clean up
+	- add ioctls to adjust bbp values
+		- antenna
+
+Things scheduled for work after above is completed
+ - WPA support (software only first)
+
+Things scheduled for work in the wireless-2.6 branch (after above is finished)
+ - AP mode
+ - 802.11i support
+ - PCF support
+ - Power Management (beyond software suspend/resume)
+ - WOL
+ - Ethtool support
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211.h no-sources-new/drivers/net/wireless/adm8211/adm8211.h
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/adm8211.h	2005-05-05 21:46:25.806289688 +0000
@@ -0,0 +1,449 @@
+#ifndef ADM8211_H
+#define ADM8211_H
+
+#include "ieee80211.h"
+
+/* ADM8211 Registers */
+
+/* CR32 (SIG) signature */
+#define ADM8211_SIG1		0x82011317 /* ADM8211A */
+#define ADM8211_SIG2		0x82111317 /* ADM8211B/ADM8211C */
+
+#define ADM8211_CSR_READ(r) ioread32(&priv->map->r)
+#define ADM8211_CSR_WRITE(r, val) iowrite32((__force u32)val, &priv->map->r)
+
+/* CSR (Host Control and Status Registers) */
+struct adm8211_csr {
+	__le32 PAR;		/* 0x00 CSR0 */
+	__le32 FRCTL;		/* 0x04 CSR0A */
+	__le32 TDR;		/* 0x08 CSR1 */
+	__le32 WTDP;		/* 0x0C CSR1A */
+	__le32 RDR;		/* 0x10 CSR2 */
+	__le32 WRDP;		/* 0x14 CSR2A */
+	__le32 RDB;		/* 0x18 CSR3 */
+	__le32 TDBH;		/* 0x1C CSR3A */
+	__le32 TDBD;		/* 0x20 CSR4 */
+	__le32 TDBP;		/* 0x24 CSR4A */
+	__le32 STSR;		/* 0x28 CSR5 */
+	__le32 TDBB;		/* 0x2C CSR5A */
+	__le32 NAR;		/* 0x30 CSR6 */
+	__le32 CSR6A;		/* reserved */
+	__le32 IER;		/* 0x38 CSR7 */
+	__le32 TKIPSCEP;	/* 0x3C CSR7A */	
+	__le32 LPC;		/* 0x40 CSR8 */
+	__le32 CSR_TEST1;	/* 0x44 CSR8A */
+	__le32 SPR;		/* 0x48 CSR9 */
+	__le32 CSR_TEST0;	/* 0x4C CSR9A */
+	__le32 WCSR;		/* 0x50 CSR10 */
+	__le32 WPDR;		/* 0x54 CSR10A */
+	__le32 GPTMR;		/* 0x58 CSR11 */
+	__le32 GPIO;		/* 0x5C CSR11A */
+	__le32 BBPCTL;		/* 0x60 CSR12 */
+	__le32 SYNCTL;		/* 0x64 CSR12A */
+	__le32 PLCPHD;		/* 0x68 CSR13 */
+	__le32 MMIWA;		/* 0x6C CSR13A */
+	__le32 MMIRD0;		/* 0x70 CSR14 */
+	__le32 MMIRD1;		/* 0x74 CSR14A */
+	__le32 TXBR;		/* 0x78 CSR15 */
+	__le32 SYNDATA;		/* 0x7C CSR15A */
+	__le32 ALCS;		/* 0x80 CSR16 */
+	__le32 TOFS2;		/* 0x84 CSR17 */
+	__le32 CMDR;		/* 0x88 CSR18 */
+	__le32 PCIC;		/* 0x8C CSR19 */
+	__le32 PMCSR;		/* 0x90 CSR20 */
+	u32 PAR0;		/* 0x94 CSR21 */
+	u32 PAR1;		/* 0x98 CSR22 */
+	u32 MAR0;		/* 0x9C CSR23 */
+	u32 MAR1;		/* 0xA0 CSR24 */
+	u32 ATIMDA0;		/* 0xA4 CSR25 */
+	u32 ABDA1;		/* 0xA8 CSR26 */
+	u32 BSSID0;		/* 0xAC CSR27 */
+	__le32 TXLMT;		/* 0xB0 CSR28 */
+	__le32 MIBCNT;		/* 0xB4 CSR29 */
+	__le32 BCNT;		/* 0xB8 CSR30 */
+	__le32 TSFTH;		/* 0xBC CSR31 */
+	__le32 TSC;		/* 0xC0 CSR32 */
+	__le32 SYNRF;		/* 0xC4 CSR33 */
+	__le32 BPLI;		/* 0xC8 CSR34 */
+	__le32 CAP0;		/* 0xCC CSR35 */
+	__le32 CAP1;		/* 0xD0 CSR36 */
+	__le32 RMD;		/* 0xD4 CSR37 */
+	__le32 CFPP;		/* 0xD8 CSR38 */
+	__le32 TOFS0;		/* 0xDC CSR39 */
+	__le32 TOFS1;		/* 0xE0 CSR40 */
+	__le32 IFST;		/* 0xE4 CSR41 */
+	__le32 RSPT;		/* 0xE8 CSR42 */
+	__le32 TSFTL;		/* 0xEC CSR43 */
+	__le32 WEPCTL;		/* 0xF0 CSR44 */
+	__le32 WESK;		/* 0xF4 CSR45 */
+	__le32 WEPCNT;		/* 0xF8 CSR46 */
+	__le32 MACTEST;		/* 0xFC CSR47 */
+	__le32 FER;		/* 0x100 */
+	__le32 FEMR;		/* 0x104 */
+	__le32 FPSR;		/* 0x108 */
+	__le32 FFER;		/* 0x10C */
+} __attribute__ ((packed));
+
+/* CSR0 - PAR (PCI Address Register) */
+#define ADM8211_PAR_MWIE	(1 << 24)
+#define ADM8211_PAR_MRLE	(1 << 23)
+#define ADM8211_PAR_MRME	(1 << 21)
+#define ADM8211_PAR_RAP		((1 << 18) | (1 << 17))
+#define ADM8211_PAR_CAL		((1 << 15) | (1 << 14))
+#define ADM8211_PAR_PBL		0x00003f00
+#define ADM8211_PAR_BLE		(1 << 7)
+#define ADM8211_PAR_DSL		0x0000007c
+#define ADM8211_PAR_BAR		(1 << 1)
+#define ADM8211_PAR_SWR		(1 << 0)
+
+/* CSR1 - FRCTL (Frame Control Register) */
+#define ADM8211_FRCTL_PWRMGT	(1 << 31)
+#define ADM8211_FRCTL_MAXPSP	(1 << 27)
+#define ADM8211_FRCTL_AID	0x0000ffff
+#define ADM8211_FRCTL_AID_ON	0x0000c000
+
+/* CSR5 - STSR (Status Register) */
+#define ADM8211_STSR_PCF	(1 << 31)
+#define ADM8211_STSR_BCNTC	(1 << 30)
+#define ADM8211_STSR_GPINT	(1 << 29)
+#define ADM8211_STSR_LinkOff	(1 << 28)
+#define ADM8211_STSR_ATIMTC	(1 << 27)
+#define ADM8211_STSR_TSFTF	(1 << 26)
+#define ADM8211_STSR_TSCZ	(1 << 25)
+#define ADM8211_STSR_LinkOn	(1 << 24)
+#define ADM8211_STSR_SQL	(1 << 23)
+#define ADM8211_STSR_WEPTD	(1 << 22)
+#define ADM8211_STSR_ATIME	(1 << 21)
+#define ADM8211_STSR_TBTT	(1 << 20)
+#define ADM8211_STSR_NISS	(1 << 16)
+#define ADM8211_STSR_AISS	(1 << 15)
+#define ADM8211_STSR_TEIS	(1 << 14)
+#define ADM8211_STSR_FBE	(1 << 13)
+#define ADM8211_STSR_REIS	(1 << 12)
+#define ADM8211_STSR_GPTT	(1 << 11)
+#define ADM8211_STSR_RPS	(1 << 8)
+#define ADM8211_STSR_RDU	(1 << 7)
+#define ADM8211_STSR_RCI	(1 << 6)
+#define ADM8211_STSR_TUF	(1 << 5)
+#define ADM8211_STSR_TRT	(1 << 4)
+#define ADM8211_STSR_TLT	(1 << 3)
+#define ADM8211_STSR_TDU	(1 << 2)
+#define ADM8211_STSR_TPS	(1 << 1)
+#define ADM8211_STSR_TCI	(1 << 0)
+
+/* CSR6 - NAR (Network Access Register) */
+#define ADM8211_NAR_TXCF	(1 << 31)
+#define ADM8211_NAR_HF		(1 << 30)
+#define ADM8211_NAR_UTR		(1 << 29)
+#define ADM8211_NAR_SQ		(1 << 28)
+#define ADM8211_NAR_CFP		(1 << 27)
+#define ADM8211_NAR_SF		(1 << 21)
+#define ADM8211_NAR_TR		((1 << 15) | (1 << 14))
+#define ADM8211_NAR_ST		(1 << 13)
+#define ADM8211_NAR_OM		((1 << 11) | (1 << 10))
+#define ADM8211_NAR_MM		(1 << 7)
+#define ADM8211_NAR_PR		(1 << 6)
+#define ADM8211_NAR_EA		(1 << 5)
+#define ADM8211_NAR_PB		(1 << 3)
+#define ADM8211_NAR_STPDMA	(1 << 2)
+#define ADM8211_NAR_SR		(1 << 1)
+#define ADM8211_NAR_CTX		(1 << 0)
+
+/* CSR7 - IER (Interrupt Enable Register) */
+#define ADM8211_IER_PCFIE	(1 << 31)
+#define ADM8211_IER_BCNTCIE	(1 << 30)
+#define ADM8211_IER_GPIE	(1 << 29)
+#define ADM8211_IER_LinkOffIE	(1 << 28)
+#define ADM8211_IER_ATIMTCIE	(1 << 27)
+#define ADM8211_IER_TSFTFIE	(1 << 26)
+#define ADM8211_IER_TSCZE	(1 << 25)
+#define ADM8211_IER_LinkOnIE	(1 << 24)
+#define ADM8211_IER_SQLIE	(1 << 23)
+#define ADM8211_IER_WEPIE	(1 << 22)
+#define ADM8211_IER_ATIMEIE	(1 << 21)
+#define ADM8211_IER_TBTTIE	(1 << 20)
+#define ADM8211_IER_NIE		(1 << 16)
+#define ADM8211_IER_AIE		(1 << 15)
+#define ADM8211_IER_TEIE	(1 << 14)
+#define ADM8211_IER_FBEIE	(1 << 13)
+#define ADM8211_IER_REIE	(1 << 12)
+#define ADM8211_IER_GPTIE	(1 << 11)
+#define ADM8211_IER_RSIE	(1 << 8)
+#define ADM8211_IER_RUIE	(1 << 7)
+#define ADM8211_IER_RCIE	(1 << 6)
+#define ADM8211_IER_TUIE	(1 << 5)
+#define ADM8211_IER_TRTIE	(1 << 4)
+#define ADM8211_IER_TLTTIE	(1 << 3)
+#define ADM8211_IER_TDUIE	(1 << 2)
+#define ADM8211_IER_TPSIE	(1 << 1)
+#define ADM8211_IER_TCIE	(1 << 0)
+
+/* CSR9 - SPR (Serial Port Register) */
+#define ADM8211_SPR_SRS		(1 << 11)
+#define ADM8211_SPR_SDO		(1 << 3)
+#define ADM8211_SPR_SDI		(1 << 2)
+#define ADM8211_SPR_SCLK	(1 << 1)
+#define ADM8211_SPR_SCS		(1 << 0)
+
+/* CSR9A - CSR_TEST0 */
+#define ADM8211_CSR_TEST0_EPNE	(1 << 18)
+#define ADM8211_CSR_TEST0_EPSNM	(1 << 17)
+#define ADM8211_CSR_TEST0_EPTYP	(1 << 16)
+#define ADM8211_CSR_TEST0_EPRLD	(1 << 15)
+
+/* CSR11A - GPIO */
+#define ADM8211_CSR_GPIO_EN5	(1 << 17)
+#define ADM8211_CSR_GPIO_EN4	(1 << 16)
+#define ADM8211_CSR_GPIO_EN3	(1 << 15)
+#define ADM8211_CSR_GPIO_EN2	(1 << 14)
+#define ADM8211_CSR_GPIO_EN1	(1 << 13)
+#define ADM8211_CSR_GPIO_EN0	(1 << 12)
+#define ADM8211_CSR_GPIO_O5	(1 << 11)
+#define ADM8211_CSR_GPIO_O4	(1 << 10)
+#define ADM8211_CSR_GPIO_O3	(1 << 9)
+#define ADM8211_CSR_GPIO_O2	(1 << 8)
+#define ADM8211_CSR_GPIO_O1	(1 << 7)
+#define ADM8211_CSR_GPIO_O0	(1 << 6)
+#define ADM8211_CSR_GPIO_IN	0x0000003f
+
+/* CSR12 - BBPCTL (BBP Control port) */
+#define ADM8211_BBPCTL_MMISEL	(1 << 31)
+#define ADM8211_BBPCTL_SPICADD  (0x7F << 24)
+#define ADM8211_BBPCTL_RF3000	(0x20 << 24)
+#define ADM8211_BBPCTL_TXCE	(1 << 23)
+#define ADM8211_BBPCTL_RXCE	(1 << 22)
+#define ADM8211_BBPCTL_CCAP	(1 << 21)
+#define ADM8211_BBPCTL_TYPE	0x001c0000
+#define ADM8211_BBPCTL_WR	(1 << 17)
+#define ADM8211_BBPCTL_RD	(1 << 16)
+#define ADM8211_BBPCTL_ADDR	0x0000ff00
+#define ADM8211_BBPCTL_DATA	0x000000ff
+
+/* CSR12A - SYNCTL (Synthesizer Control port) */
+#define ADM8211_SYNCTL_WR	(1 << 31)
+#define ADM8211_SYNCTL_RD	(1 << 30)
+#define ADM8211_SYNCTL_CS0	(1 << 29)
+#define ADM8211_SYNCTL_CS1	(1 << 28)
+#define ADM8211_SYNCTL_CAL	(1 << 27)
+#define ADM8211_SYNCTL_SELCAL	(1 << 26)
+#define ADM8211_SYNCTL_RFtype	((1 << 24) || (1 << 23) || (1 << 22))
+#define ADM8211_SYNCTL_RFMD	(1 << 22)
+#define ADM8211_SYNCTL_GENERAL	(0x7 << 22)
+/* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */
+
+/* CSR18 - CMDR (Command Register) */
+#define ADM8211_CMDR_PM		(1 << 19)
+#define ADM8211_CMDR_APM	(1 << 18)
+#define ADM8211_CMDR_RTE	(1 << 4)
+#define ADM8211_CMDR_DRT	((1 << 3) | (1 << 2))
+#define ADM8211_CMDR_DRT_8DW	(0x0 << 2)
+#define ADM8211_CMDR_DRT_16DW	(0x1 << 2)
+#define ADM8211_CMDR_DRT_SF	(0x2 << 2)
+
+/* CSR33 - SYNRF (SYNRF direct control) */
+#define ADM8211_SYNRF_SELSYN	(1 << 31)
+#define ADM8211_SYNRF_SELRF	(1 << 30)
+#define ADM8211_SYNRF_LERF	(1 << 29)
+#define ADM8211_SYNRF_LEIF	(1 << 28)
+#define ADM8211_SYNRF_SYNCLK	(1 << 27)
+#define ADM8211_SYNRF_SYNDATA	(1 << 26)
+#define ADM8211_SYNRF_PE1	(1 << 25)
+#define ADM8211_SYNRF_PE2	(1 << 24)
+#define ADM8211_SYNRF_PA_PE	(1 << 23)
+#define ADM8211_SYNRF_TR_SW	(1 << 22)
+#define ADM8211_SYNRF_TR_SWN	(1 << 21)
+#define ADM8211_SYNRF_RADIO	(1 << 20)
+#define ADM8211_SYNRF_CAL_EN	(1 << 19)
+#define ADM8211_SYNRF_PHYRST	(1 << 18)
+
+#define ADM8211_SYNRF_IF_SELECT_0 	(1 << 31)
+#define ADM8211_SYNRF_IF_SELECT_1 	((1 << 31) | (1 << 28))
+#define ADM8211_SYNRF_WRITE_SYNDATA_0	(1 << 31)
+#define ADM8211_SYNRF_WRITE_SYNDATA_1	((1 << 31) | (1 << 26))
+#define ADM8211_SYNRF_WRITE_CLOCK_0	(1 << 31)
+#define ADM8211_SYNRF_WRITE_CLOCK_1	((1 << 31) | (1 << 27))
+
+/* CSR44 - WEPCTL (WEP Control) */
+#define ADM8211_WEPCTL_WEPENABLE   (1 << 31)
+#define ADM8211_WEPCTL_WPAENABLE   (1 << 30)
+#define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29)
+#define ADM8211_WEPCTL_TABLE_WR	(1 << 28)
+#define ADM8211_WEPCTL_TABLE_RD	(1 << 27)
+#define ADM8211_WEPCTL_WEPRXBYP	(1 << 25)
+#define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23)
+#define ADM8211_WEPCTL_ADDR	(0x000001ff)
+
+/* CSR45 - WESK (Data Entry for Share/Individual Key) */
+#define ADM8211_WESK_DATA	(0x0000ffff)
+
+/* FER (Function Event Register) */
+#define ADM8211_FER_INTR_EV_ENT	(1 << 15)
+
+
+/* Si4126 RF Synthesizer - Control Registers */
+#define SI4126_MAIN_CONF	0
+#define SI4126_PHASE_DET_GAIN	1
+#define SI4126_POWERDOWN	2
+#define SI4126_RF1_N_DIV	3 /* only Si4136 */
+#define SI4126_RF2_N_DIV	4
+#define SI4126_IF_N_DIV		5
+#define SI4126_RF1_R_DIV	6 /* only Si4136 */
+#define SI4126_RF2_R_DIV	7
+#define SI4126_IF_R_DIV		8
+
+/* Main Configuration */
+#define SI4126_MAIN_XINDIV2	(1 << 6)
+#define SI4126_MAIN_IFDIV	((1 << 11) | (1 << 10))
+/* Powerdown */
+#define SI4126_POWERDOWN_PDIB	(1 << 1)
+#define SI4126_POWERDOWN_PDRB	(1 << 0)
+
+
+/* RF3000 BBP - Control Port Registers */
+/* 0x00 - reserved */
+#define RF3000_MODEM_CTRL__RX_STATUS 0x01
+#define RF3000_CCA_CTRL 0x02
+#define RF3000_DIVERSITY__RSSI 0x03
+#define RF3000_RX_SIGNAL_FIELD 0x04
+#define RF3000_RX_LEN_MSB 0x05
+#define RF3000_RX_LEN_LSB 0x06
+#define RF3000_RX_SERVICE_FIELD 0x07
+#define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11
+#define RF3000_TX_LEN_MSB 0x12
+#define RF3000_TX_LEN_LSB 0x13
+#define RF3000_LOW_GAIN_CALIB 0x14
+#define RF3000_HIGH_GAIN_CALIB 0x15
+
+/* ADM8211 revisions */
+#define ADM8211_REV_AB 0x11
+#define ADM8211_REV_AF 0x15
+#define ADM8211_REV_BA 0x20
+#define ADM8211_REV_CA 0x30
+
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 16
+
+struct adm8211_ring_info {
+	struct sk_buff *skb;
+	dma_addr_t mapping;
+};
+
+struct adm8211_eeprom {
+	__le16	signature;		/* 0x00 */
+	u8	major_version;		/* 0x02 */
+	u8	minor_version;		/* 0x03 */
+	u8	reserved_1[4];		/* 0x04 */
+	u8	hwaddr[6];		/* 0x08 */
+	u8	reserved_2[8];		/* 0x1E */
+	__le16	cr49;			/* 0x16 */
+	u8	cr03;			/* 0x18 */
+	u8	cr28;			/* 0x19 */
+	u8	cr29;			/* 0x1A */
+	u8	country_code;		/* 0x1B */
+
+/* specific bbp types */
+#define ADM8211_BBP_RFMD3000	0x00
+#define ADM8211_BBP_RFMD3002	0x01
+#define ADM8211_BBP_ADM8011	0x04
+	u8	specific_bbptype;	/* 0x1C */
+	u8	specific_rftype;	/* 0x1D */
+	u8	reserved_3[2];		/* 0x1E */
+	__le16	device_id;		/* 0x20 */
+	__le16	vendor_id;		/* 0x22 */
+	__le16	subsystem_id;		/* 0x24 */
+	__le16	subsystem_vendor_id;	/* 0x26 */
+	u8	maxlat;			/* 0x28 */
+	u8	mingnt;			/* 0x29 */
+	__le16	cis_pointer_low;	/* 0x2A */
+	__le16	cis_pointer_high;	/* 0x2C */
+	__le16	csr18;			/* 0x2E */
+	u8	reserved_4[16];		/* 0x30 */
+	u8	d1_pwrdara;		/* 0x40 */
+	u8	d0_pwrdara;		/* 0x41 */
+	u8	d3_pwrdara;		/* 0x42 */
+	u8	d2_pwrdara;		/* 0x43 */
+	u8	antenna_power[14];	/* 0x44 */
+	__le16	cis_wordcnt;		/* 0x52 */
+	u8	tx_power[14];		/* 0x54 */
+	u8	lpf_cutoff[14];		/* 0x62 */
+	u8	lnags_threshold[14];	/* 0x70 */
+	__le16	checksum;		/* 0x7E */
+	u8	cis_data[0];		/* 0x80, 384 bytes */
+} __attribute__ ((packed));
+
+struct adm8211_priv {
+	struct pci_dev *pdev;
+	spinlock_t lock;
+	struct adm8211_csr __iomem *map;
+	struct adm8211_desc *rx_ring;
+	struct adm8211_desc *tx_ring;
+	dma_addr_t rx_ring_dma;
+	dma_addr_t tx_ring_dma;
+	struct adm8211_ring_info rx_buffers[RX_RING_SIZE];
+	struct adm8211_ring_info tx_buffers[TX_RING_SIZE];
+	unsigned cur_tx, dirty_tx, cur_rx;
+	struct sk_buff_head rx_queue;
+	struct tasklet_struct rx_tasklet;
+#ifdef ADM8211_PROC
+	struct proc_dir_entry *proc;
+#endif
+	int iw_mode; /* operating mode (IW_MODE_*) */
+
+	struct net_device_stats stats;
+	struct iw_statistics wstats;
+	struct ieee80211_data ieee80211;
+
+	int (*eth_header_parse)(struct sk_buff *skb, unsigned char *haddr);
+	struct timer_list timer;
+
+	u8 soft_rx_crc;
+	u8 plcp_signal;
+	u8 retry_limit;
+	u16 rts_thresh;
+	u16 frag_thresh;
+
+	u8 ant_power;
+	u8 tx_power;
+	u8 lpf_cutoff;
+	u8 lnags_threshold;
+	struct adm8211_eeprom *eeprom;
+	size_t eeprom_len;
+
+	u8 revid;
+
+	u32 nar;
+
+#define ADM8211_TYPE_INTERSIL	0x00
+#define ADM8211_TYPE_RFMD	0x01
+#define ADM8211_TYPE_MARVEL	0x02
+#define ADM8211_TYPE_ADMTEK     0x05
+	unsigned int rf_type:3;
+	unsigned int bbp_type:3;
+
+	enum {
+		ADM8211_RFMD2948 = 0x0,
+		ADM8211_RFMD2958 = 0x1,
+		ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2,
+		ADM8211_MAX2820 = 0x8,
+		ADM8211_AL2210L = 0xC,	/* Airoha */
+	} transceiver_type;
+};
+
+static const struct ieee80211_chan_range cranges[] = {
+	{1,  11},	/* FCC */
+	{1,  11},	/* IC */
+	{1,  13},	/* ETSI */
+	{10, 11},	/* SPAIN */
+	{10, 13},	/* FRANCE */
+	{14, 14},	/* MMK */
+	{1,  14},	/* MMK2 */
+};
+
+int adm8211_rf_set_channel(struct net_device *dev, int channel);
+int adm8211_80211_header_parse(struct sk_buff *skb, unsigned char *haddr);
+void adm8211_write_wepkey(struct net_device *dev, int index);
+void adm8211_update_wep(struct net_device *dev);
+void adm8211_update_mode(struct net_device *dev);
+void adm8211_set_beacon(struct net_device *dev);
+
+#endif /* ADM8211_H */
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211_hw.c no-sources-new/drivers/net/wireless/adm8211/adm8211_hw.c
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211_hw.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/adm8211_hw.c	2005-05-05 21:46:25.805289840 +0000
@@ -0,0 +1,2636 @@
+
+/*
+ * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP)
+ *
+ * Copyright (c) 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004-2005, Michael Wu <flamingice@sourmilk.net>
+ * Some parts copyright (c) 2003 by David Young <dyoung@pobox.com>
+ * and used with permission.
+ *
+ * Much thanks to Infineon-ADMtek for their support of this driver.
+ *
+ * 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.
+ */
+
+/*
+ * Note!
+ * - this is a pre-release of the driver; it is not yet finished and needs
+ *   more testing.
+ *
+ * FIX:
+ * - properly idle chip in parts that need it (goes w/ above)
+ * - improve signal quality + RSSI stuff
+ * - figure out why ping -s 5913 and up lose packets..
+ *   (can be reduced with a mtu of 920..)
+ * - check FCS for rev 0x20 and up?
+ */
+
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <asm/delay.h>
+#include <asm/unaligned.h>
+#include <asm/types.h>
+
+#include "adm8211.h"
+#include "adm8211_ioctl.h"
+#include "ieee80211.h"
+#include "avs_caphdr.h"
+
+#include "rdate.h"
+
+MODULE_AUTHOR("Jouni Malinen <jkmaline@cc.hut.fi>, Michael Wu <flamingice@sourmilk.net>");
+MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless LAN cards based on ADMtek"
+		   " ADM8211");
+MODULE_SUPPORTED_DEVICE("ADM8211");
+MODULE_LICENSE("GPL");
+
+
+static const char *version = KERN_INFO "adm8211: Copyright 2003, "
+"Jouni Malinen <jkmaline@cc.hut.fi>; "
+"Copyright 2004-2005, Michael Wu <flamingice@sourmilk.net>\n";
+
+
+static struct pci_device_id adm8211_pci_id_table[] __devinitdata = {
+	/* ADMtek ADM8211 */
+	{ 0x10B7, 0x6000, PCI_ANY_ID, PCI_ANY_ID }, /* 3Com 3CRSHPW796 */
+	{ 0x1200, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ? */
+	{ 0x1317, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211A */
+	{ 0x1317, 0x8211, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211B/C */
+	{ 0 }
+};
+
+
+#define ADM8211_INTMASK \
+(ADM8211_IER_NIE | ADM8211_IER_AIE | ADM8211_IER_RCIE | ADM8211_IER_TCIE | \
+ADM8211_IER_TDUIE | ADM8211_IER_GPTIE)
+
+
+struct adm8211_desc {
+	__le32 status;
+	__le32 length;
+	__le32 buffer1;
+	__le32 buffer2;
+};
+
+#define RDES0_STATUS_OWN	(1 << 31)
+#define RDES0_STATUS_ES		(1 << 30)
+#define RDES0_STATUS_SQL	(1 << 29)
+#define RDES0_STATUS_DE		(1 << 28)
+#define RDES0_STATUS_FS		(1 << 27)
+#define RDES0_STATUS_LS		(1 << 26)
+#define RDES0_STATUS_PCF	(1 << 25)
+#define RDES0_STATUS_SFDE	(1 << 24)
+#define RDES0_STATUS_SIGE	(1 << 23)
+#define RDES0_STATUS_CRC16E	(1 << 22)
+#define RDES0_STATUS_RXTOE	(1 << 21)
+#define RDES0_STATUS_CRC32E	(1 << 20)
+#define RDES0_STATUS_ICVE	(1 << 19)
+#define RDES0_STATUS_DA1	(1 << 17)
+#define RDES0_STATUS_DA0	(1 << 16)
+#define RDES0_STATUS_RXDR	((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12))
+#define RDES0_STATUS_FL		(0x00000fff)
+
+#define RDES1_CONTROL_RER	(1 << 25)
+#define RDES1_CONTROL_RCH	(1 << 24)
+#define RDES1_CONTROL_RBS2	(0x00fff000)
+#define RDES1_CONTROL_RBS1	(0x00000fff)
+
+#define RDES1_STATUS_RSSI	(0x0000007f)
+
+
+#define TDES0_CONTROL_OWN	(1 << 31)
+#define TDES0_CONTROL_DONE	(1 << 30)
+#define TDES0_CONTROL_TXDR	(0x0ff00000)
+
+#define TDES0_STATUS_OWN	(1 << 31)
+#define TDES0_STATUS_DONE	(1 << 30)
+#define TDES0_STATUS_ES		(1 << 29)
+#define TDES0_STATUS_TLT	(1 << 28)
+#define TDES0_STATUS_TRT	(1 << 27)
+#define TDES0_STATUS_TUF	(1 << 26)
+#define TDES0_STATUS_TRO	(1 << 25)
+#define TDES0_STATUS_SOFBR	(1 << 24)
+#define TDES0_STATUS_ACR	(0x00000fff)
+
+#define TDES1_CONTROL_IC	(1 << 31)
+#define TDES1_CONTROL_LS	(1 << 30)
+#define TDES1_CONTROL_FS	(1 << 29)
+#define TDES1_CONTROL_TER	(1 << 25)
+#define TDES1_CONTROL_TCH	(1 << 24)
+#define TDES1_CONTROL_RBS2	(0x00fff000)
+#define TDES1_CONTROL_RBS1	(0x00000fff)
+
+
+#define PLCP_SIGNAL_1M		0x0a
+#define PLCP_SIGNAL_2M		0x14
+#define PLCP_SIGNAL_5M5		0x37
+#define PLCP_SIGNAL_11M		0x6e
+
+
+/* RX status - stored in skb->cb so this structure must be 48 bytes or less */
+struct adm8211_rx_status {
+	u8 rssi;
+	u8 rate;
+};
+
+
+struct adm8211_tx_hdr {
+	u8 da[6];
+	u8 signal; /* PLCP signal / TX rate in 100 Kbps */
+	u8 service;
+	__le16 frame_body_size;
+	__le16 frame_control;
+	__le16 plcp_frag_tail_len;
+	__le16 plcp_frag_head_len;
+	__le16 dur_frag_tail;
+	__le16 dur_frag_head;
+	u8 address4[6];
+
+#define ADM8211_TXHDRCTL_SHORT_PREAMBLE		(1 <<  0)
+#define ADM8211_TXHDRCTL_MORE_FRAG		(1 <<  1)
+#define ADM8211_TXHDRCTL_MORE_DATA		(1 <<  2)
+#define ADM8211_TXHDRCTL_FRAG_NO		(1 <<  3) /* ? */
+#define ADM8211_TXHDRCTL_ENABLE_RTS		(1 <<  4)
+#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE	(1 <<  5)
+#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER	(1 << 15) /* ? */
+	__le16 header_control;
+#ifdef BIG_ENDIAN
+	u32 retry_limit:8;
+	u32 reserved_0:8;
+	u32 frag_number:4;
+	u32 frag_threshold:12;
+#else
+	u32 frag_threshold:12;
+	u32 frag_number:4;
+	u32 reserved_0:8;
+	u32 retry_limit:8;
+#endif
+
+	u32 wep2key0;
+	u32 wep2key1;
+	u32 wep2key2;
+	u32 wep2key3;
+
+	u8 keyid;
+	u8 reserved_1[3];
+	u32 reserved_2;
+} __attribute__ ((packed));
+
+
+
+#define RX_COPY_BREAK 128
+
+#define RX_PKT_SIZE 2400
+
+/* SRAM offsets */
+#define ADM8211_SRAM(x) (priv->revid < ADM8211_REV_BA ? \
+        ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x)
+
+#define ADM8211_SRAM_INDIV_KEY   0x0000
+#define ADM8211_SRAM_A_SHARE_KEY 0x0160
+#define ADM8211_SRAM_B_SHARE_KEY 0x00c0
+
+#define ADM8211_SRAM_A_SSID      0x0180
+#define ADM8211_SRAM_B_SSID      0x00d4
+#define ADM8211_SRAM_SSID ADM8211_SRAM(SSID)
+
+#define ADM8211_SRAM_A_SUPP_RATE 0x0191
+#define ADM8211_SRAM_B_SUPP_RATE 0x00dd
+#define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE)
+
+#define ADM8211_SRAM_A_SIZE      0x0200
+#define ADM8211_SRAM_B_SIZE      0x01c0
+#define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE)
+
+/* Serial EEPROM reading for 93C66/93C46 */
+#define EE_ENB		(0x4000 | ADM8211_SPR_SRS | ADM8211_SPR_SCS)
+#define EE_READ_CMD	(6)
+#define eeprom_delay()	ADM8211_CSR_READ(SPR);
+
+
+static u16 adm8211_eeprom_read_word(struct net_device *dev, int addr,
+				    int addr_len)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u16 retval = 0;
+	int read_cmd = addr | (EE_READ_CMD << addr_len);
+
+	ADM8211_CSR_WRITE(SPR, EE_ENB & ~ADM8211_SPR_SCS);
+	eeprom_delay();
+	ADM8211_CSR_WRITE(SPR, EE_ENB);
+	eeprom_delay();
+
+	/* Shift the read command bits out. */
+	for (i = 4 + addr_len; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? ADM8211_SPR_SDI : 0;
+		ADM8211_CSR_WRITE(SPR, EE_ENB | dataval);
+		eeprom_delay();
+		ADM8211_CSR_WRITE(SPR, EE_ENB | dataval | ADM8211_SPR_SCLK);
+		eeprom_delay();
+	}
+
+	ADM8211_CSR_WRITE(SPR, EE_ENB);
+	eeprom_delay();
+
+	for (i = 16; i > 0; i--) {
+		ADM8211_CSR_WRITE(SPR, EE_ENB | ADM8211_SPR_SCLK);
+		eeprom_delay();
+		retval <<= 1;
+		retval |= ((ADM8211_CSR_READ(SPR) & ADM8211_SPR_SDO) ? 1 : 0);
+		ADM8211_CSR_WRITE(SPR, EE_ENB);
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	ADM8211_CSR_WRITE(SPR, EE_ENB & ~ADM8211_SPR_SCS);
+
+	return retval;
+}
+
+
+static int adm8211_read_eeprom(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	int addr_len, words, i;
+
+	reg = ADM8211_CSR_READ(CSR_TEST0);
+
+	if (reg & ADM8211_CSR_TEST0_EPTYP) {
+		printk(KERN_DEBUG "%s (adm8211): EEPROM type: 93C66\n", pci_name(priv->pdev));
+		/* 256 * 16-bit = 512 bytes */
+		addr_len = 8;
+		words = 256;
+	} else {
+		printk(KERN_DEBUG "%s (adm8211): EEPROM type 93C46\n", pci_name(priv->pdev));
+		/* 64 * 16-bit = 128 bytes */
+		addr_len = 6;
+		words = 64;
+	}
+
+	priv->eeprom_len = words * 2;
+	priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL);
+	if (priv->eeprom == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < words; i++)
+		*((u16 *) &((u8 *)priv->eeprom)[i * 2]) =
+			adm8211_eeprom_read_word(dev, i, addr_len);
+
+	priv->rf_type = (le16_to_cpu(priv->eeprom->cr49) & (0x7 << 3)) >> 3;
+	priv->bbp_type = le16_to_cpu(priv->eeprom->cr49) &  0x7;
+
+	/* read country code from eeprom and make sure it's valid */
+	if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) {
+		printk(KERN_WARNING "%s (adm8211): Invalid country code (%d) in EEPROM, assuming ETSI\n",
+		       pci_name(priv->pdev), priv->eeprom->country_code);
+
+		priv->ieee80211.chan_range = cranges[2];
+	} else
+		priv->ieee80211.chan_range = cranges[priv->eeprom->country_code];
+
+	printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n",
+	       pci_name(priv->pdev), (int)priv->ieee80211.chan_range.min, (int)priv->ieee80211.chan_range.max);
+
+	switch (priv->eeprom->specific_rftype) {
+	case ADM8211_RFMD2948:
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+	case ADM8211_MAX2820:
+	case ADM8211_AL2210L:
+		priv->transceiver_type = priv->eeprom->specific_rftype;
+		break;
+
+	default:
+		if (priv->revid == ADM8211_REV_BA)
+			priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER;
+		else if (priv->revid == ADM8211_REV_CA)
+			priv->transceiver_type = ADM8211_AL2210L;
+		else if (priv->revid == ADM8211_REV_AB)
+			priv->transceiver_type = ADM8211_RFMD2948;
+
+		printk(KERN_WARNING "%s (adm8211): Invalid or unsupported transceiver: %d, assuming %d\n",
+		       pci_name(priv->pdev), priv->eeprom->specific_rftype, priv->transceiver_type);
+
+		break;
+	}
+
+	printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d Transceiver=%d\n",
+	       pci_name(priv->pdev), priv->rf_type, priv->bbp_type,
+	       priv->eeprom->specific_bbptype, priv->eeprom->specific_rftype);
+
+	return 0;
+}
+
+static inline void adm8211_write_sram(struct net_device *dev, u32 addr, __le32 data)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(addr | ADM8211_WEPCTL_TABLE_WR |
+			  (priv->revid < ADM8211_REV_BA ?
+			   0 : ADM8211_WEPCTL_SEL_WEPTABLE )) );
+	ADM8211_CSR_READ(WEPCTL);
+	mdelay(1);
+
+	ADM8211_CSR_WRITE(WESK, data);
+	ADM8211_CSR_READ(WESK);
+	mdelay(1);
+}
+
+static void adm8211_write_sram_bytes(struct net_device *dev, int addr, u8 *buf, int len)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg = ADM8211_CSR_READ(WEPCTL);
+	int i;
+
+	if (priv->revid < ADM8211_REV_BA) {
+		for (i = 0; i < len; i += 2) {
+			u16 val = buf[i] | buf[i + 1] << 8;
+			adm8211_write_sram(dev, addr + i / 2, cpu_to_le32(val));
+		}
+	} else {
+		for (i = 0; i < len; i += 4) {
+			u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) |
+				  (buf[i + 2] << 16) | (buf[i + 3] << 24);
+			adm8211_write_sram(dev, addr + i / 4, cpu_to_le32(val));
+		}
+	}
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static void adm8211_clear_sram(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg = ADM8211_CSR_READ(WEPCTL);
+	int addr;
+
+	for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++)
+			adm8211_write_sram(dev, addr, 0);
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static void adm8211_link_change(struct net_device *dev, int link)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+
+	/* Set aid for beacon TIM element decoding */
+	reg = ADM8211_CSR_READ(FRCTL);
+	reg &= ~ADM8211_FRCTL_AID;
+	if (link) {
+		reg |= ADM8211_FRCTL_AID_ON;
+		reg |= priv->ieee80211.aid;
+	}
+	ADM8211_CSR_WRITE(FRCTL, reg);
+}
+
+static struct net_device_stats *adm8211_get_stats(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	return &priv->stats;
+}
+
+static struct iw_statistics *adm8211_get_wireless_stats(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	return &priv->wstats;
+}
+
+static void adm8211_set_rx_mode(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i, bit_nr;
+	__le32 mc_filter[2];
+	struct dev_mc_list *mclist;
+	unsigned long flags;
+
+	if (dev->flags & IFF_PROMISC) {
+		priv->nar |= ADM8211_NAR_PR;
+		priv->nar &= ~ADM8211_NAR_MM;
+		mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
+	} else if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 32)) {
+		priv->nar &= ~ADM8211_NAR_PR;
+		priv->nar |= ADM8211_NAR_MM;
+		mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
+	} else {
+		priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR);
+		mc_filter[1] = mc_filter[0] = 0;
+		mclist = dev->mc_list;
+		for (i = 0; i < dev->mc_count; i+=1) {
+			if (!mclist)
+				break;
+			bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+			bit_nr &= 0x3F;
+                        mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31));
+			mclist = mclist->next;
+		}
+	}
+
+	/* TODO: is this locking necessary? I don't think so.. */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* make sure receive is stopped */
+	if (priv->nar & ADM8211_NAR_SR) {
+	ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR);
+	ADM8211_CSR_READ(NAR);
+	mdelay(20);
+	}
+
+	ADM8211_CSR_WRITE(MAR0, mc_filter[0]);
+	ADM8211_CSR_WRITE(MAR1, mc_filter[1]);
+	ADM8211_CSR_READ(NAR);
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int adm8211_set_mac_address(struct net_device *dev, void *p)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+        struct sockaddr *addr = p;
+	u32 reg;
+
+        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	if (!(dev->flags & IFF_UP))
+		return 0;
+
+	/* sure, we can change the MAC addr while running */
+	reg = ADM8211_CSR_READ(NAR);
+
+	/* make sure receive is stopped */
+	if (priv->nar & ADM8211_NAR_SR) {
+	ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR);
+	mdelay(20);
+	}
+
+	ADM8211_CSR_WRITE(PAR0, *(u32 *)dev->dev_addr);
+	ADM8211_CSR_WRITE(PAR1, *(u16 *)(dev->dev_addr + 4));
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	return 0;
+}
+
+static void adm8211_rx_skb(struct net_device *dev, struct sk_buff *skb)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct ieee80211_hdr *hdr;
+	struct adm8211_rx_status *stat;
+	struct iw_statistics *wstats = &priv->wstats;
+	struct ieee80211_rx_status rx_status;
+	static const u8 rate[] = {10, 20, 55, 110, 220};
+	u16 fc;
+
+	if (skb->len < 14)
+		goto drop;
+
+	/* TODO: Add crc checking here for cards/modes that need it */
+
+	stat = (struct adm8211_rx_status *) skb->cb;
+	memset(&rx_status, 0, sizeof(rx_status));
+	if (priv->revid < ADM8211_REV_CA)
+		rx_status.rssi = stat->rssi;
+	else
+		rx_status.rssi = 100 - stat->rssi;
+
+	if (stat->rate <= 4)
+		rx_status.rate = rate[stat->rate];
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	if (priv->iw_mode == IW_MODE_MONITOR) {
+		struct avs_caphdr *chdr;
+		if (skb_headroom(skb) < sizeof(struct avs_caphdr)) {
+			if (pskb_expand_head(skb, sizeof(struct avs_caphdr), 0,
+					     GFP_ATOMIC)) {
+				printk(KERN_DEBUG "%s: failed to allocate room for prism2 "
+				       "header\n", dev->name);
+				goto drop;
+			}
+		}
+		memset(skb->cb, 0, sizeof(skb->cb));
+
+		chdr = (struct avs_caphdr *) skb_push(skb, sizeof(struct avs_caphdr));
+		chdr->version = cpu_to_be32(0x80211001);
+		chdr->length = cpu_to_be32(sizeof(struct avs_caphdr));
+		chdr->mactime = 0;
+		chdr->hosttime = cpu_to_be64(jiffies);
+		chdr->phytype = cpu_to_be32(4); /* phytype_dsss_dot11_b */
+		chdr->channel = cpu_to_be32(data->channel);
+		chdr->datarate = cpu_to_be32(rx_status.rate);
+		chdr->antenna = 0; /* TODO: once antenna setting is possible.. */
+		chdr->priority = 0; /* hmm, dunno if this is possible.. */
+		chdr->ssi_type = cpu_to_be32(3); /* Raw RSSI */
+		chdr->ssi_signal = cpu_to_be32(rx_status.rssi);
+		chdr->ssi_noise = cpu_to_be32(0xFFFFFFFF);
+		if (skb->len >= 14 + sizeof(struct avs_caphdr))
+			chdr->preamble = cpu_to_be32(
+					 le16_to_cpu(hdr->frame_control) & WLAN_CAPABILITY_SHORT_PREAMBLE
+					 ? 2 : 1);
+		else
+			chdr->preamble = 0;
+		chdr->encoding = cpu_to_be32(1); /* CCK */
+
+		priv->stats.rx_bytes += skb->len;
+		priv->stats.rx_packets += 1;
+
+		skb->pkt_type = PACKET_OTHERHOST;
+		skb->mac.raw = skb->data;
+
+		netif_rx(skb);
+		dev->last_rx = jiffies;
+		return;
+	}
+
+	if (ieee80211_filter_duplicates(&priv->ieee80211, skb, fc))
+		goto drop;
+
+	/* remove FCS */
+	if (dev->flags & IFF_PROMISC)
+		skb_trim(skb, skb->len - FCS_LEN);
+
+	/* Promiscuous mode disables hardware WEP RX decryption */
+	if (priv->revid < ADM8211_REV_BA || dev->flags & IFF_PROMISC) {
+		skb = ieee80211_wep_decode(&priv->ieee80211, skb);
+		if (!skb) {
+			priv->wstats.discard.code += 1;
+			priv->stats.rx_errors += 1;
+			return;
+		}
+	}
+
+	skb = ieee80211_reassemble_frag(&priv->ieee80211, skb);
+	if (!skb)
+		return;
+
+	/* FIX: this is a hack */
+	wstats->qual.qual = stat->rssi;
+	wstats->qual.updated = 1;
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		ieee80211_rx_mgmt(&priv->ieee80211, skb, &rx_status);
+		break;
+	case WLAN_FC_TYPE_DATA:
+		skb = ieee80211_data_decaps(&priv->ieee80211, skb);
+		if (skb) {
+			skb->protocol = eth_type_trans(skb, dev);
+			memset(skb->cb, 0, sizeof(skb->cb));
+			priv->stats.rx_bytes+=skb->len;
+			priv->stats.rx_packets+=1;
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+		}
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		goto drop;
+	}
+
+	return;
+
+ drop:
+	dev_kfree_skb(skb);
+}
+
+
+static void adm8211_rx_tasklet(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *) data;
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&priv->rx_queue)) != NULL)
+		adm8211_rx_skb(dev, skb);
+}
+
+
+static void adm8211_interrupt_tci(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	unsigned dirty_tx;
+
+#if 0
+	printk(KERN_DEBUG "TCI (dirty_tx=%d cur_tx=%d)\n",
+	       priv->dirty_tx, priv->cur_tx);
+#endif
+
+	spin_lock(&priv->lock);
+
+	for (dirty_tx = priv->dirty_tx;
+	     priv->cur_tx - dirty_tx > 0; dirty_tx++) {
+		unsigned entry = dirty_tx % TX_RING_SIZE;
+		u32 status = le32_to_cpu(priv->tx_ring[entry].status);
+		if (status & TDES0_CONTROL_OWN ||
+		    !(status & TDES0_CONTROL_DONE))
+			break;
+
+		if (status & TDES0_STATUS_ES) {
+			priv->stats.tx_errors += 1;
+
+			if (status & (TDES0_STATUS_TUF | TDES0_STATUS_TRO))
+				priv->stats.tx_fifo_errors += 1;
+			if (status & (TDES0_STATUS_TLT | TDES0_STATUS_SOFBR))
+				priv->wstats.discard.misc += 1;
+			if (status & TDES0_STATUS_TRT)
+				priv->wstats.discard.retries += 1;
+		} else {
+			priv->stats.tx_bytes += priv->tx_buffers[entry].skb->len;
+			priv->stats.tx_packets += 1;
+		}
+
+		pci_unmap_single(priv->pdev, priv->tx_buffers[entry].mapping,
+				 priv->tx_buffers[entry].skb->len,
+				 PCI_DMA_TODEVICE);
+		dev_kfree_skb_irq(priv->tx_buffers[entry].skb);
+		priv->tx_buffers[entry].skb = NULL;
+	}
+
+	if (priv->cur_tx - dirty_tx < TX_RING_SIZE - 2)
+		netif_wake_queue(dev);
+
+	priv->dirty_tx = dirty_tx;
+	spin_unlock(&priv->lock);
+}
+
+
+static void adm8211_interrupt_rci(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int entry = priv->cur_rx % RX_RING_SIZE;
+	u32 status;
+	unsigned pktlen;
+	struct sk_buff *skb, *newskb;
+	int limit = RX_RING_SIZE;
+	u8 rssi, rate;
+
+#if 0
+	printk(KERN_DEBUG "RCI\n");
+#endif
+
+	while (!(priv->rx_ring[entry].status &
+		 __constant_cpu_to_le32(RDES0_STATUS_OWN))) {
+		if (--limit < 0)
+			break;
+
+		status = le32_to_cpu(priv->rx_ring[entry].status);
+		rate = (status & RDES0_STATUS_RXDR) >> 12;
+		rssi = le32_to_cpu(priv->rx_ring[entry].length) &
+			RDES1_STATUS_RSSI;
+
+#if 0
+		printk(KERN_DEBUG "%s: RX %02x RXDR=%d FL=%d RSSI=%d "
+		       "%s%s%s%s\n",
+		       dev->name, status, rate,
+		       status & RDES0_STATUS_FL,
+		       rssi,
+		       status & RDES0_STATUS_ES ? "[ES]" : "",
+		       status & RDES0_STATUS_FS ? "[FS]" : "",
+		       status & RDES0_STATUS_LS ? "[LS]" : "",
+		       status & RDES0_STATUS_CRC16E ? "[CRC16E]" : "");
+#endif
+
+		pktlen = status & RDES0_STATUS_FL;
+		if (pktlen > RX_PKT_SIZE) {
+			printk(KERN_DEBUG "%s: too long frame (pktlen=%d)\n",
+			       dev->name, pktlen);
+			pktlen = RX_PKT_SIZE;
+		}
+
+		if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) {
+#if 0
+			printk(KERN_DEBUG "%s: dropped RX frame with error "
+			       "(status=0x%x)\n", dev->name, status);
+#endif
+			skb = NULL; /* old buffer will be reused */
+			priv->stats.rx_errors += 1;
+			if (status & (RDES0_STATUS_SFDE |
+				      RDES0_STATUS_SIGE | RDES0_STATUS_RXTOE))
+				priv->wstats.discard.misc += 1;
+			if (status & RDES0_STATUS_ICVE)
+				priv->wstats.discard.code += 1;
+			if (status & (RDES0_STATUS_CRC16E | RDES0_STATUS_CRC32E))
+				priv->stats.rx_crc_errors += 1;
+
+		} else if (pktlen < RX_COPY_BREAK) {
+			skb = dev_alloc_skb(pktlen);
+			if (skb) {
+				skb->dev = dev;
+				pci_dma_sync_single_for_cpu(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					pktlen, PCI_DMA_FROMDEVICE);
+				memcpy(skb_put(skb, pktlen),
+				       priv->rx_buffers[entry].skb->tail,
+				       pktlen);
+				pci_dma_sync_single_for_device(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+			}
+		} else {
+			newskb = dev_alloc_skb(RX_PKT_SIZE);
+			if (newskb) {
+				newskb->dev = dev;
+				skb = priv->rx_buffers[entry].skb;
+				skb_put(skb, pktlen);
+				pci_unmap_single(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+				priv->rx_buffers[entry].skb = newskb;
+				priv->rx_buffers[entry].mapping =
+					pci_map_single(priv->pdev,
+						       newskb->tail,
+						       RX_PKT_SIZE,
+						       PCI_DMA_FROMDEVICE);
+			} else {
+				skb = NULL;
+				priv->stats.rx_dropped += 1;
+			}
+
+			priv->rx_ring[entry].buffer1 =
+				cpu_to_le32(priv->rx_buffers[entry].mapping);
+		}
+
+		priv->rx_ring[entry].status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL );
+		priv->rx_ring[entry].length =
+			cpu_to_le32(RX_PKT_SIZE |
+				    (entry == RX_RING_SIZE - 1 ?
+				     RDES1_CONTROL_RER : 0));
+
+		if (skb) {
+			struct adm8211_rx_status *stat =
+				(struct adm8211_rx_status *) skb->cb;
+#if 0
+			{
+				int i;
+				printk(KERN_DEBUG "RX[%d/%d]",
+				       pktlen, skb->len);
+				for (i = 0; i < skb->len; i++)
+					printk(" %02x", skb->data[i]);
+				printk("\n");
+			}
+#endif
+			stat->rssi = rssi;
+			stat->rate = rate;
+			skb->mac.raw = skb->data;
+			skb->protocol = (__force unsigned short) __constant_htons(ETH_P_802_2);
+			skb_queue_tail(&priv->rx_queue, skb);
+			tasklet_schedule(&priv->rx_tasklet);
+		}
+
+		entry = (++priv->cur_rx) % RX_RING_SIZE;
+	}
+
+	priv->stats.rx_missed_errors += ADM8211_CSR_READ(LPC) & 0xFFFF;
+}
+
+
+static irqreturn_t adm8211_interrupt(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+#define ADM8211_INT(x) if (stsr & ADM8211_STSR_ ## x) printk(KERN_DEBUG "%s: " #x "\n", dev->name)
+
+	struct net_device *dev = dev_id;
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 stsr;
+	int count = 0;
+
+	do {
+		stsr = ADM8211_CSR_READ(STSR);
+		ADM8211_CSR_WRITE(STSR, stsr);
+		if (stsr == 0xffffffff)
+			return IRQ_HANDLED;
+
+		if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS)))
+			break;
+
+		/*if (stsr & ADM8211_STSR_TBTT) {
+			priv->ieee80211.beacon_sync(dev);
+		}*/
+
+		if (stsr & ADM8211_STSR_RCI)
+			adm8211_interrupt_rci(dev);
+		if (stsr & ADM8211_STSR_TCI)
+			adm8211_interrupt_tci(dev);
+		if (stsr & (ADM8211_STSR_RCI | ADM8211_STSR_TCI))
+			mod_timer(&priv->timer, jiffies + HZ);
+
+		if ((stsr & (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff))
+			 != (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff)) {
+			if (stsr & ADM8211_STSR_LinkOn) {
+				printk(KERN_DEBUG "%s: LinkOn\n", dev->name);
+				priv->ieee80211.flags |= LINK_ON;
+			}
+		
+			if (stsr & ADM8211_STSR_LinkOff) {
+				printk(KERN_DEBUG "%s: LinkOff\n", dev->name);
+				priv->ieee80211.flags &= ~LINK_ON;
+
+				if (dev->flags & IFF_UP)
+					ieee80211_start(&priv->ieee80211);
+			}
+		}
+		
+		ADM8211_INT(PCF);
+		/*ADM8211_INT(BCNTC);*/
+		ADM8211_INT(GPINT);
+		ADM8211_INT(ATIMTC);
+		/*ADM8211_INT(TSFTF);*/
+		ADM8211_INT(TSCZ);
+		ADM8211_INT(SQL);
+		ADM8211_INT(WEPTD);
+		ADM8211_INT(ATIME);
+		/*ADM8211_INT(TBTT);*/
+		ADM8211_INT(TEIS);
+		ADM8211_INT(FBE);
+		ADM8211_INT(REIS);
+		ADM8211_INT(GPTT);
+		ADM8211_INT(RPS);
+		ADM8211_INT(RDU);
+		ADM8211_INT(TUF);
+		/*ADM8211_INT(TRT);*/
+		/*ADM8211_INT(TLT);*/
+		/*ADM8211_INT(TDU);*/
+		ADM8211_INT(TPS);
+
+	} while (count++ < 20);
+
+	return IRQ_RETVAL(count);
+
+#undef ADM8211_INT
+}
+
+static int adm8211_rf_write_syn_max2820 (struct net_device *dev, u8 addr, u16 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg;
+
+	value &= 0xFFF;
+	addr  &= 0xF;
+	value = value | (addr << 12);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 15; i+=1) {
+		if ( value & (1 << (15 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_rf_write_syn_al2210l (struct net_device *dev, u8 addr, u32 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg;
+
+	value &= 0xFFFFF;
+	addr  &= 0xF;
+	value = (value << 4) | addr;
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 23; i+=1) {
+		if ( value & (1 << (23 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_rf_write_syn_rfmd2958 (struct net_device *dev, u16 addr, u32 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg;
+
+	value &= 0x3FFFF;
+	addr  &= 0x1F;
+	value = value | (addr << 18);
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 23; i+=1) {
+		if ( value & (1 << (23 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_rf_write_syn_rfmd2948 (struct net_device *dev, u8 addr, u16 value)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	u32 reg, bits;
+
+	value &= 0xFFFF;
+	addr  &= 0xF;
+	bits = (value << 4) | addr;
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0);
+	ADM8211_CSR_READ(SYNRF);
+
+	for (i = 0; i <= 21; i+=1) {
+		if ( bits & (1 << (21 - i)) )
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;
+		else
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;
+
+		ADM8211_CSR_WRITE(SYNRF, reg);
+		ADM8211_CSR_READ(SYNRF);
+
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1);
+		ADM8211_CSR_READ(SYNRF);
+		ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0);
+		ADM8211_CSR_READ(SYNRF);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_1);
+	ADM8211_CSR_READ(SYNRF);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+
+	return 0;
+}
+
+static int adm8211_write_bbp(struct net_device *dev, u8 addr, u8 data)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int timeout;
+	u32 reg;
+
+	timeout = 10;
+	while (timeout > 0) {
+		reg = ADM8211_CSR_READ(BBPCTL);
+		if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD)))
+			break;
+		timeout--;
+		mdelay(2);
+	}
+
+	if (timeout == 0) {
+		printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed prewrite "
+		       "(reg=0x%08x)\n",
+		       dev->name, addr, data, reg);
+		return -ETIMEDOUT;
+	}
+
+	switch (priv->bbp_type) {
+	case ADM8211_TYPE_INTERSIL:
+		reg = ADM8211_BBPCTL_MMISEL;	/* three wire interface */
+		break;
+	case ADM8211_TYPE_RFMD:
+		reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+		      (0x01<<18);
+		break;
+	case ADM8211_TYPE_ADMTEK:
+		reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+		      (0x05<<18);
+		break;
+	}
+	reg |= ADM8211_BBPCTL_WR | (addr << 8) | data;
+
+	ADM8211_CSR_WRITE(BBPCTL, reg);
+
+	timeout=10;
+	while (timeout > 0) {
+		reg = ADM8211_CSR_READ(BBPCTL);
+		if (!(reg & ADM8211_BBPCTL_WR))
+			break;
+		timeout--;
+		mdelay(2);
+	}
+
+	if (timeout == 0) {
+		ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) &
+				  (~ADM8211_BBPCTL_WR));
+		printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed postwrite "
+		       "(reg=0x%08x)\n",
+		       dev->name, addr, data, reg);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int adm8211_rf_set_channel(struct net_device *dev, int channel)
+{
+	static const u32 adm8211_rfmd2958_reg5[] =
+		{0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340,
+		 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7};
+	static const u32 adm8211_rfmd2958_reg6[] =
+		{0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000,
+		 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745};
+
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u8 ant_power = priv->ant_power > 0x3F ?
+		priv->eeprom->antenna_power[channel-1] : priv->ant_power;
+	u8 tx_power = priv->tx_power > 0x3F ?
+		priv->eeprom->tx_power[channel-1] : priv->tx_power;
+	u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ?
+		priv->eeprom->lpf_cutoff[channel-1] : priv->lpf_cutoff;
+	u8 lnags_thresh = priv->lnags_threshold == 0xFF ?
+		priv->eeprom->lnags_threshold[channel-1] : priv->lnags_threshold;
+	u32 reg;
+
+	if (channel < 1 || channel > 14)
+		return -EINVAL;
+
+	/* Stop transmit */
+	ADM8211_CSR_WRITE(NAR, priv->nar & ~(ADM8211_NAR_ST | ADM8211_NAR_SR));
+	ADM8211_CSR_READ(NAR);
+	mdelay(20);
+
+	/* Program synthesizer to new channel */
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033);
+
+		adm8211_rf_write_syn_rfmd2958(dev, 0x05,
+					adm8211_rfmd2958_reg5[channel-1]);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x06,
+					adm8211_rfmd2958_reg6[channel-1]);
+		break;
+
+	case ADM8211_RFMD2948:
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, SI4126_MAIN_XINDIV2);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN,
+				     SI4126_POWERDOWN_PDIB | SI4126_POWERDOWN_PDRB);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV,
+				     (channel == 14 ? 2110 : 2033 + (channel * 5)));
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_rf_write_syn_max2820(dev, 0x3,
+			(channel == 14 ? 0x054 : 0x7 + (channel * 5)));
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_rf_write_syn_al2210l(dev, 0x0,
+			(channel == 14 ? 0x229B4 : 0x22967 + (channel * 5)));
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
+		       dev->name, priv->transceiver_type);
+		break;
+	}
+
+	/* write BBP regs */
+	if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+
+	/* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */
+	/* TODO: remove if SMC 2635W doesn't need this */
+	if (priv->transceiver_type == ADM8211_RFMD2948) {
+		reg = ADM8211_CSR_READ(GPIO);
+		reg &= 0xfffc0000;
+		reg |= ADM8211_CSR_GPIO_EN0;
+		if (channel != 14)
+			reg |= ADM8211_CSR_GPIO_O0;
+		ADM8211_CSR_WRITE(GPIO, reg);
+	}
+
+	if (priv->transceiver_type == ADM8211_RFMD2958) {
+		/* set PCNT2 */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100);
+		/* set PCNT1 P_DESIRED/MID_BIAS */
+		reg = le16_to_cpu(priv->eeprom->cr49);
+		reg >>= 13;
+		reg <<= 15;
+		reg |= ant_power<<9;
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg);
+		/* set TXRX TX_GAIN */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 |
+			(priv->revid < ADM8211_REV_CA ? tx_power : 0));
+	} else {
+		reg = ADM8211_CSR_READ(PLCPHD);
+		reg &= 0xff00ffff;
+		reg |= tx_power<<18;
+		ADM8211_CSR_WRITE(PLCPHD, reg);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | ADM8211_SYNRF_PE1 |
+			  ADM8211_SYNRF_PHYRST);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(30);
+
+	/* RF3000 BBP */
+	if (priv->transceiver_type != ADM8211_RFMD2958)
+		adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT,
+				  tx_power<<2);
+	adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff);
+	adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh);
+	adm8211_write_bbp(dev, 0x1c, priv->revid == ADM8211_REV_BA
+			  ? priv->eeprom->cr28 : 0);
+	adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+
+	} else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) {	/* Nothing to do for ADMtek BBP */
+		printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
+		       dev->name, priv->bbp_type);
+	}
+
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	/* update current channel for adhoc (and maybe AP mode) */
+	reg = ADM8211_CSR_READ(CAP0);
+	reg &= ~0xF;
+	reg |= channel;
+	ADM8211_CSR_WRITE(CAP0, reg);
+
+	return 0;
+}
+
+void adm8211_write_wepkey(struct net_device *dev, int index)
+{
+#define ADM8211_WEP_ENABLE	(1 << 7)
+#define ADM8211_WEP_A_104	(1 << 6)
+#define ADM8211_WEP_B_104	(1 << 4)
+
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int addr;
+	u8 buf[32];
+	memset(buf, 0, sizeof(buf));
+
+	if (priv->revid < ADM8211_REV_BA) {
+		addr = (index*7) + ADM8211_SRAM_A_SHARE_KEY;
+
+		/* control entry */
+		if (data->wep_data[index].len > 5)
+			buf[1] = ADM8211_WEP_ENABLE | ADM8211_WEP_A_104;
+		else if (data->wep_data[index].len > 0)
+			buf[1] = ADM8211_WEP_ENABLE;
+
+		buf[0]=data->wep_data[index].key[0];
+
+		if (data->wep_data[index].len > 0)
+			memcpy(buf+2, &(data->wep_data[index].key[1]), data->wep_data[index].len-1);
+
+		adm8211_write_sram_bytes(dev, addr, buf, 14);
+	} else {
+		addr = (index*5) + ADM8211_SRAM_B_SHARE_KEY;
+
+		/* control entry */
+		if (data->wep_data[index].len > 5)
+			*(__le32 *)buf = cpu_to_le32(ADM8211_WEP_ENABLE | ADM8211_WEP_B_104);
+		else if (data->wep_data[index].len > 0)
+			*(__le32 *)buf = cpu_to_le32(ADM8211_WEP_ENABLE);
+
+		if (data->wep_data[index].len > 0)
+			memcpy(buf+4, data->wep_data[index].key, data->wep_data[index].len);
+
+		adm8211_write_sram_bytes(dev, addr, buf, 17);
+	}
+}
+
+void adm8211_update_wep(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	u32 reg;
+
+	reg = ADM8211_CSR_READ(MACTEST);
+	if (data->flags & WEP_ENABLED) {
+		reg &= ~(3<<20);
+		reg |= (1<<22);
+		reg |= (data->default_key << 20);
+	} else
+		reg &= ~(7<<20);
+	ADM8211_CSR_WRITE(MACTEST, reg);
+
+	reg = ADM8211_CSR_READ(WEPCTL);
+	if (data->flags & WEP_ENABLED)
+		reg |= ADM8211_WEPCTL_WEPENABLE;
+	else
+		reg &= ~ADM8211_WEPCTL_WEPENABLE;
+
+	/* no hardware WEP RX decryption on the ADM8211A */
+	if (priv->revid < ADM8211_REV_BA)
+		reg |= ADM8211_WEPCTL_WEPRXBYP;
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+void adm8211_update_mode(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+
+	/* make sure we're stopped */
+	if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) {
+		ADM8211_CSR_WRITE(NAR, priv->nar & ~(ADM8211_NAR_SR | ADM8211_NAR_ST));
+		ADM8211_CSR_READ(NAR);
+		mdelay(20);
+	}
+
+	priv->soft_rx_crc = 0;
+	switch (priv->iw_mode) {
+	case IW_MODE_INFRA:
+		priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA);
+		priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR;
+		ADM8211_CSR_WRITE(CAP1, data->capab<<16);
+		break;
+	case IW_MODE_ADHOC:
+		priv->nar &= ~ADM8211_NAR_PR;
+		priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR;
+
+		ADM8211_CSR_WRITE(CAP1, data->capab<<16);
+
+		/* don't trust the error bits on rev 0x20 and up in adhoc */
+		if (priv->revid >= ADM8211_REV_BA)
+			priv->soft_rx_crc = 1;
+		break;
+	case IW_MODE_MONITOR:
+		priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST);
+		priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR;
+		ADM8211_CSR_WRITE(CAP1, 0);
+		break;
+	}
+
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+}
+
+static void adm8211_hw_init_syn(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x09,
+			(priv->transceiver_type == ADM8211_RFMD2958
+			? 0x10050 : 0x00050) );
+		adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E);
+		adm8211_rf_write_syn_max2820(dev, 0x2, 0x001);
+		adm8211_rf_write_syn_max2820(dev, 0x3, 0x054);
+		adm8211_rf_write_syn_max2820(dev, 0x4, 0x310);
+		adm8211_rf_write_syn_max2820(dev, 0x5, 0x000);
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C);
+		adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB);
+		adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F);
+		adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9);
+		adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280);
+		adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641);
+		adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130);
+		adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000);
+		adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F);
+		adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C);
+		adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000);
+		adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000);
+		break;
+
+	case ADM8211_RFMD2948:
+	default:
+		break;
+	}
+}
+
+static int adm8211_hw_init_bbp(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+
+	/* write addresses */
+	if (priv->bbp_type == ADM8211_TYPE_INTERSIL) {
+		ADM8211_CSR_WRITE(MMIWA,  0x100E0C0A);
+		ADM8211_CSR_WRITE(MMIRD0, 0x00007c7e);
+		ADM8211_CSR_WRITE(MMIRD1, 0x00100000);
+	} else if (priv->bbp_type == ADM8211_TYPE_RFMD ||
+		   priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+
+	/* check specific BBP type */
+	switch (priv->eeprom->specific_bbptype) {
+	case ADM8211_BBP_RFMD3000:
+	case ADM8211_BBP_RFMD3002:
+		ADM8211_CSR_WRITE(MMIWA,  0x00009101);
+		ADM8211_CSR_WRITE(MMIRD0, 0x00000301);
+		break;
+
+	case ADM8211_BBP_ADM8011:
+		ADM8211_CSR_WRITE(MMIWA,  0x00008903);
+		ADM8211_CSR_WRITE(MMIRD0, 0x00001716);
+
+		reg = ADM8211_CSR_READ(BBPCTL);
+		reg &= ~ADM8211_BBPCTL_TYPE;
+		reg |= 0x5 << 18;
+		ADM8211_CSR_WRITE(BBPCTL, reg);
+		break;
+	}
+
+	switch (priv->revid) {
+	case ADM8211_REV_CA:
+		if (priv->transceiver_type == ADM8211_RFMD2958 ||
+		    priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER ||
+		    priv->transceiver_type == ADM8211_RFMD2948)
+			ADM8211_CSR_WRITE(SYNCTL, (0x1 << 22));
+		else if (priv->transceiver_type == ADM8211_MAX2820 ||
+			 priv->transceiver_type == ADM8211_AL2210L)
+			ADM8211_CSR_WRITE(SYNCTL, (0x3 << 22));
+		break;
+
+	case ADM8211_REV_BA:
+		reg  = ADM8211_CSR_READ(MMIRD1);
+		reg &= 0x0000FFFF;
+		reg |= 0x7e100000;
+		ADM8211_CSR_WRITE(MMIRD1, reg);
+		break;
+
+	case ADM8211_REV_AB:
+	case ADM8211_REV_AF:
+	default:
+		ADM8211_CSR_WRITE(MMIRD1, 0x7e100000);
+		break;
+	}
+
+	/* For RFMD */
+	ADM8211_CSR_WRITE(MACTEST, 0x800);
+	}
+
+	adm8211_hw_init_syn(dev);
+
+	/* Set RF Power control IF pin to PE1+PHYRST# */
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | ADM8211_SYNRF_PE1 |
+			  ADM8211_SYNRF_PHYRST);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(20);
+
+	/* write BBP regs */
+	if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+		/* RF3000 BBP */
+		/* another set:
+		 * 11: c8
+		 * 14: 14
+		 * 15: 50 (chan 1..13; chan 14: d0)
+		 * 1c: 00
+		 * 1d: 84
+		 */
+		adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80);
+		adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); /* antenna selection: diversity */
+		adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74);
+		adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38);
+		adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40);
+
+		if (priv->eeprom->major_version < 2) {
+			adm8211_write_bbp(dev, 0x1c, 0x00);
+			adm8211_write_bbp(dev, 0x1d, 0x80);
+		} else {
+			if (priv->revid == ADM8211_REV_BA)
+				adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28);
+			else
+				adm8211_write_bbp(dev, 0x1c, 0x00);
+
+			adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+		}
+	} else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+	adm8211_write_bbp(dev, 0x00, 0xFF);	/* reset baseband */
+	adm8211_write_bbp(dev, 0x07, 0x0A);	/* antenna selection: diversity */
+
+	/* TODO: find documentation for this */
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x09, 0x00);
+		adm8211_write_bbp(dev, 0x0a, 0x00);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x00);
+		adm8211_write_bbp(dev, 0x0f, 0xAA);
+		adm8211_write_bbp(dev, 0x10, 0x8c);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x40);
+		adm8211_write_bbp(dev, 0x20, 0x23);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x28);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x28, 0x35);
+		adm8211_write_bbp(dev, 0x2a, 0x8c);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x2d, 0x0A);
+		adm8211_write_bbp(dev, 0x29, 0x40);
+		adm8211_write_bbp(dev, 0x60, 0x08);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x09, 0x05);
+		adm8211_write_bbp(dev, 0x0a, 0x02);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x0f);
+		adm8211_write_bbp(dev, 0x0f, 0x55);
+		adm8211_write_bbp(dev, 0x10, 0x8d);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x4a);
+		adm8211_write_bbp(dev, 0x20, 0x20);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x23);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x2a, 0x8c);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x29, 0x4a);
+		adm8211_write_bbp(dev, 0x60, 0x2b);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x07, 0x05);
+		adm8211_write_bbp(dev, 0x08, 0x03);
+		adm8211_write_bbp(dev, 0x09, 0x00);
+		adm8211_write_bbp(dev, 0x0a, 0x00);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x10);
+		adm8211_write_bbp(dev, 0x0f, 0x55);
+		adm8211_write_bbp(dev, 0x10, 0x8d);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x4a);
+		adm8211_write_bbp(dev, 0x20, 0x20);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x23);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x2a, 0xaa);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x29, 0xfa);
+		adm8211_write_bbp(dev, 0x60, 0x2d);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_RFMD2948:
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
+		       dev->name, priv->transceiver_type);
+		break;
+	}
+	} else {
+		printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
+		       dev->name, priv->bbp_type);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+
+	/* Set RF CAL control source to MAC control */
+	reg = ADM8211_CSR_READ(SYNCTL);
+	reg |= ADM8211_SYNCTL_SELCAL;
+	ADM8211_CSR_WRITE(SYNCTL, reg);
+
+	return 0;
+}
+
+static int adm8211_set_rate(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	u32 reg;
+	int i = 0;
+	u8 rate_buf[12] = {0};
+
+	/* write supported rates */
+	if (priv->revid != ADM8211_REV_BA) {
+		rate_buf[0] = data->num_supp_rates;
+		for (i = 0; i < data->num_supp_rates; i+=1)
+			rate_buf[i+1] = data->supp_rates[i] |
+					(data->supp_rates[i] <= data->rate ? 0x80 : 0);
+	} else {
+		/* workaround for rev BA specific bug */
+		rate_buf[0]=4;
+		rate_buf[1]=0x82;
+		rate_buf[2]=0x04;
+		rate_buf[3]=0x0b;
+		rate_buf[4]=0x16;
+	}
+	
+	adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, data->num_supp_rates+1);
+
+	priv->plcp_signal = data->rate * 5;
+
+	/* keep bits 0-23 */
+	reg = ADM8211_CSR_READ(PLCPHD) & 0x00FFFFFF;
+
+	/* short preamble */
+	reg |= (1 << 15);
+
+	reg |= priv->plcp_signal << 24;
+	ADM8211_CSR_WRITE(PLCPHD, reg);
+
+	/* MTMLT   = 512 TU (max TX MSDU lifetime)
+	 * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate)
+	 * SRTYLIM = 224 (short retry limit, value in TX header used by default) */
+	ADM8211_CSR_WRITE(TXLMT, (512<<16) | (priv->plcp_signal<<8) | (224<<0));
+
+	if (priv->revid >= ADM8211_REV_BA)
+		adm8211_set_beacon(dev);
+
+	return 0;
+}
+
+static void adm8211_hw_init(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	u8 cacheline;
+
+	reg = ADM8211_CSR_READ(PAR);
+	reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME;
+	reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL);
+
+	if (!pci_set_mwi(priv->pdev)) {
+		reg |= (0x1<<24);
+		pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cacheline);
+
+		switch (cacheline) {
+		case  0x8: reg |= (0x1<<14);
+			   break;
+		case 0x16: reg |= (0x2<<14);
+			   break;
+		case 0x32: reg |= (0x3<<14);
+			   break;
+		  default: reg |= (0x0<<14);
+			   break;
+		}
+	}
+
+	ADM8211_CSR_WRITE(PAR, reg);
+
+	reg = ADM8211_CSR_READ(CSR_TEST1);
+	reg &= ~(0xF<<28);
+	reg |= ((1 << 28) | (1 << 31));
+	ADM8211_CSR_WRITE(CSR_TEST1, reg);
+
+	reg = (0x07 << 21) | (1 << 20) | (1 << 8);
+	ADM8211_CSR_WRITE(WCSR, reg);
+
+	/* Disable APM, enable receive FIFO threshold, and set drain receive
+	 * threshold to store-and-forward */
+	reg = ADM8211_CSR_READ(CMDR);
+	reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT);
+	reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF;
+	ADM8211_CSR_WRITE(CMDR, reg);
+
+	adm8211_set_rate(dev);
+
+	/* 4-bit values:
+	 * PWR1UP   = 8 * 2 ms
+	 * PWR0PAPE = 8 us or 5 us
+	 * PWR1PAPE = 1 us or 3 us
+	 * PWR0TRSW = 5 us
+	 * PWR1TRSW = 12 us
+	 * PWR0PE2  = 13 us
+	 * PWR1PE2  = 1 us
+	 * PWR0TXPE = 8 or 6 */
+	if (priv->revid < ADM8211_REV_CA)
+		ADM8211_CSR_WRITE(TOFS2, 0x8815cd18);
+	else
+		ADM8211_CSR_WRITE(TOFS2, 0x8535cd16);
+
+	/* Enable store and forward for transmit */
+	priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB;
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+
+	/* Reset RF */
+	ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_RADIO);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(10);
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(5);
+
+	/* Set CFP Max Duration to 0x10 TU */
+	reg = ADM8211_CSR_READ(CFPP);
+	reg &= ~(0xffff<<8);
+	reg |= 0x0010<<8;
+	ADM8211_CSR_WRITE(CFPP, reg);
+
+	/* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us
+	 * TUCNT = 0x3ff - Tu counter 1024 us  */
+	ADM8211_CSR_WRITE(TOFS0, (0x16 << 24) | 0x3ff);
+
+	/* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us),
+	 * DIFS=50 us, EIFS=100 us */
+	if (priv->revid < ADM8211_REV_CA)
+		ADM8211_CSR_WRITE(IFST, (20 << 23) | (110 << 15) |
+					(50 << 9)  | 100);
+	else
+		ADM8211_CSR_WRITE(IFST, (20 << 23) | (24 << 15) |
+					(50 << 9)  | 100);
+
+	/* PCNT = 1 (MAC idle time awake/sleep, unit S)
+	 * RMRD = 2346 * 8 + 1 us (max RX duration)  */
+	ADM8211_CSR_WRITE(RMD, (1 << 16) | 18769);
+
+	/* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */
+	ADM8211_CSR_WRITE(RSPT, 0xffffff00);
+
+	/* Initialize BBP (and SYN) */
+	adm8211_hw_init_bbp(dev);
+
+	/* make sure interrupts are off */
+	ADM8211_CSR_WRITE(IER, 0);
+
+	/* ACK interrupts */
+	ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR));
+
+	/* Setup WEP */
+	adm8211_write_wepkey(dev, 0);
+	adm8211_write_wepkey(dev, 1);
+	adm8211_write_wepkey(dev, 2);
+	adm8211_write_wepkey(dev, 3);
+	adm8211_update_wep(dev);
+}
+
+static int adm8211_hw_reset(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	int timeout = 50;
+
+	/* Power-on issue */
+	ADM8211_CSR_WRITE(FRCTL, 0);
+
+	/* Reset the chip */
+	reg = ADM8211_CSR_READ(PAR);
+	ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR);
+
+	while ((ADM8211_CSR_READ(PAR) & ADM8211_PAR_SWR) && timeout) {
+		mdelay(100);
+		timeout-=1;
+	}
+	if (timeout <= 0)
+		return -ETIMEDOUT;
+
+	ADM8211_CSR_WRITE(PAR, reg);
+
+	if (priv->revid == ADM8211_REV_BA &&
+	    ( priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER
+	   || priv->transceiver_type == ADM8211_RFMD2958)) {
+		reg = ADM8211_CSR_READ(CSR_TEST1);
+		reg |= (1 << 4) | (1 << 5);
+		ADM8211_CSR_WRITE(CSR_TEST1, reg);
+	} else if (priv->revid == ADM8211_REV_CA) {
+		reg = ADM8211_CSR_READ(CSR_TEST1);
+		reg &= ~((1 << 4) | (1 << 5));
+		ADM8211_CSR_WRITE(CSR_TEST1, reg);
+	}
+
+	ADM8211_CSR_WRITE(FRCTL, 0);
+
+	reg = ADM8211_CSR_READ(CSR_TEST0);
+	reg |= ADM8211_CSR_TEST0_EPRLD;
+	ADM8211_CSR_WRITE(CSR_TEST0, reg);
+
+	adm8211_clear_sram(dev);
+
+	return 0;
+}
+
+static void adm8211_set_tbtt (struct net_device *dev, u16 tbtt)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	/* TSFTOFSR (RX TSFT Offset) = 1 us
+	 * TBTTPRE (prediction time) = tbtt TU
+	 * TBTTOFS (Wake up time offset before TBTT) = 20 TU */
+	ADM8211_CSR_WRITE(TOFS1, (1 << 24) | (tbtt << 8) | 20);
+	ADM8211_CSR_READ(TOFS1);
+}
+
+static u64 adm8211_get_tsft (struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 tsftl;
+	u64 tsft;
+
+	tsftl = ADM8211_CSR_READ(TSFTL);
+	tsft = ADM8211_CSR_READ(TSFTH);
+	tsft <<= 32;
+	tsft |= tsftl;
+
+	return tsft;
+}
+
+static void adm8211_set_interval(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	u32 reg;
+
+	/* BP (beacon interval) = data->beacon_interval
+	 * LI (listen interval) = data->listen_interval (in beacon intervals) */
+	reg = (data->beacon_interval << 16) | data->listen_interval;
+	ADM8211_CSR_WRITE(BPLI, reg);
+
+	/* lose link after 7 lost beacons */
+	reg = ADM8211_CSR_READ(WCSR) & ~(0xFF<<21);
+	ADM8211_CSR_WRITE(WCSR, reg | (0x07<<21));
+	ADM8211_CSR_READ(WCSR);
+}
+
+static int adm8211_set_bssid(struct net_device *dev, u8 *bssid)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+
+	reg = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24);
+	ADM8211_CSR_WRITE(BSSID0, reg);
+	reg = ADM8211_CSR_READ(ABDA1);
+	reg &= 0x0000ffff;
+	reg |= (bssid[4] << 16) | (bssid[5] << 24);
+	ADM8211_CSR_WRITE(ABDA1, reg);
+
+	return 0;
+}
+
+static int adm8211_set_ssid(struct net_device *dev, u8 *ssid, size_t ssid_len)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u8 buf[36];
+
+	if (ssid_len > 32)
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = ssid_len;
+	memcpy(buf + 1, ssid, ssid_len);
+	adm8211_write_sram_bytes(dev, ADM8211_SRAM_SSID, buf, 33);
+	adm8211_set_beacon(dev);
+
+	return 0;
+}
+
+void adm8211_set_beacon (struct net_device *dev) {
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	unsigned int blen, len, rem;
+	u32 reg;
+
+	if (priv->iw_mode == IW_MODE_ADHOC)
+		blen = 24 +
+			8 + 2 + 2 + 2 + data->ssid_len + 2 + data->num_supp_rates +
+			3 + 4 + WLAN_FCS_LEN;
+	else
+		blen = 0;
+
+	if (priv->revid < ADM8211_REV_BA) {
+		/* 11M PLCP length */
+		blen *= 8;
+		rem   = blen % 11;
+		len   = blen / 11;
+		if (rem) {
+			len += 1;
+			if (rem <= 3)
+				len |= 1<<7;
+		}
+		reg = len << 16;
+
+		/* 5.5M PLCP length */
+		rem   = (blen*2) % 11;
+		len   = (blen*2) / 11;
+		if (rem)
+			len += 1;
+		reg |= len << 8;
+
+		reg |= blen;
+
+		ADM8211_CSR_WRITE(BCNT, reg);
+	} else {
+		len = blen;
+		rem = (blen*80) % priv->plcp_signal;
+		len = (blen*80) / priv->plcp_signal;
+		if (rem) {
+			len += 1;
+			if (data->rate == 0x16 && ((blen*8) % 11) <= 3)
+				len |= 1<<15;
+		}
+
+		len &= 0xFFFF;
+
+		ADM8211_CSR_WRITE(BCNT, len);
+
+		reg = ADM8211_CSR_READ(MMIRD1);
+		reg &= 0xFFFF0000;
+		reg |= len;
+		ADM8211_CSR_WRITE(MMIRD1, reg);
+	}
+}
+
+static void adm8211_init_rings(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+	struct adm8211_desc *desc;
+	struct adm8211_ring_info *info;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		desc = &priv->rx_ring[i];
+		desc->status = 0;
+		desc->length = cpu_to_le32(RX_PKT_SIZE);
+		priv->rx_buffers[i].skb = NULL;
+	}
+	/* Mark the end of RX ring; hw returns to base address after this
+	 * descriptor */
+	desc->length |= cpu_to_le32(RDES1_CONTROL_RER);
+
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		desc = &priv->rx_ring[i];
+		info = &priv->rx_buffers[i];
+
+		info->skb = dev_alloc_skb(RX_PKT_SIZE);
+		if (info->skb == NULL)
+			break;
+		info->mapping = pci_map_single(priv->pdev, info->skb->tail,
+					       RX_PKT_SIZE,
+					       PCI_DMA_FROMDEVICE);
+		info->skb->dev = dev;
+		desc->buffer1 = cpu_to_le32(info->mapping);
+		desc->status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL );
+	}
+
+	/* Setup TX ring. TX buffers descriptors will be filled in as needed */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		desc = &priv->tx_ring[i];
+		info = &priv->tx_buffers[i];
+
+		info->skb = NULL;
+		info->mapping = 0;
+		desc->status = 0;
+	}
+	desc->length = cpu_to_le32(TDES1_CONTROL_TER);
+
+	priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0;
+	ADM8211_CSR_WRITE(RDB, priv->rx_ring_dma);
+	ADM8211_CSR_WRITE(TDBD, priv->tx_ring_dma);
+}
+
+static void adm8211_free_rings(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		if (!priv->rx_buffers[i].skb)
+			continue;
+
+		pci_unmap_single(
+			priv->pdev,
+			priv->rx_buffers[i].mapping,
+			RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+
+		dev_kfree_skb(priv->rx_buffers[i].skb);
+	}
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (!priv->tx_buffers[i].skb)
+			continue;
+
+		pci_unmap_single(
+			priv->pdev,
+			priv->tx_buffers[i].mapping,
+			priv->tx_buffers[i].skb->len, PCI_DMA_TODEVICE);
+
+		dev_kfree_skb(priv->tx_buffers[i].skb);
+	}
+}
+
+static int adm8211_open(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int retval;
+
+	/* Power up MAC and RF chips */
+	retval = adm8211_hw_reset(dev);
+	if (retval) {
+		printk(KERN_ERR "%s: hardware reset failed\n", dev->name);
+		goto fail;
+	}
+
+	adm8211_init_rings(dev);
+
+	/* Init hardware */
+	adm8211_hw_init(dev);
+	adm8211_rf_set_channel(dev, max(priv->ieee80211.pref_channel,
+					priv->ieee80211.chan_range.min));
+
+	/* set mac address */
+	ADM8211_CSR_WRITE(PAR0, *(u32 *)dev->dev_addr);
+	ADM8211_CSR_WRITE(PAR1, *(u16 *)(dev->dev_addr + 4));
+
+	adm8211_set_rx_mode(dev);
+
+	retval = request_irq(dev->irq, &adm8211_interrupt,
+			     SA_SHIRQ, dev->name, dev);
+	if (retval) {
+		printk(KERN_ERR "%s: failed to register IRQ handler\n",
+		       dev->name);
+		goto fail;
+	}
+
+	ADM8211_CSR_WRITE(IER, ADM8211_INTMASK);
+	ADM8211_CSR_READ(IER);
+
+	adm8211_update_mode(dev);
+
+	/* should be more than enough time for the card to settle down */
+	mdelay(200);
+
+	ieee80211_start(&priv->ieee80211);
+
+	netif_start_queue(dev);
+	return 0;
+
+fail:
+	return retval;
+}
+
+
+static int adm8211_stop(struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	del_singleshot_timer_sync(&priv->timer);
+
+	ieee80211_stop(&priv->ieee80211);
+
+	netif_stop_queue(dev);
+
+	ADM8211_CSR_WRITE(IER, 0);
+	priv->nar &= ~(ADM8211_NAR_ST | ADM8211_NAR_PR | ADM8211_NAR_PB |
+		       ADM8211_NAR_SR | ADM8211_NAR_MM );
+	ADM8211_CSR_WRITE(NAR, priv->nar);
+	ADM8211_CSR_READ(NAR);
+
+	free_irq(dev->irq, dev);
+
+	adm8211_free_rings(dev);
+
+	skb_queue_purge(&priv->rx_queue);
+
+	adm8211_hw_reset(dev);
+	return 0;
+}
+
+static void adm8211_timer(unsigned long ptr)
+{
+	struct net_device *dev = (struct net_device *) ptr;
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u32 reg;
+	u16 rra, rwa;
+
+	if (priv->revid != ADM8211_REV_BA ||
+	   !(dev->flags & IFF_UP))
+		return;
+
+	/* checks for stalls on adm8211b */
+	reg = ADM8211_CSR_READ(CSR_TEST1);
+	rra = (reg >> 12) & 0x1FF;
+	rwa = (reg >> 2 ) & 0x1FF;
+
+	if ( (rra != rwa) && !(reg & (1<<1)) ) {
+		printk(KERN_DEBUG "%s: stalled. resetting card\n", dev->name);
+		adm8211_stop(dev);
+		adm8211_open(dev);
+	}
+}
+
+static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len,
+				   int plcp_signal, int short_preamble)
+{
+	/* Alternative calculation from NetBSD: */
+
+/* IEEE 802.11b durations for DSSS PHY in microseconds */
+#define IEEE80211_DUR_DS_LONG_PREAMBLE	144
+#define IEEE80211_DUR_DS_SHORT_PREAMBLE	72
+#define IEEE80211_DUR_DS_FAST_PLCPHDR	24
+#define IEEE80211_DUR_DS_SLOW_PLCPHDR	48
+#define IEEE80211_DUR_DS_SLOW_ACK	112
+#define IEEE80211_DUR_DS_FAST_ACK	56
+#define IEEE80211_DUR_DS_SLOW_CTS	112
+#define IEEE80211_DUR_DS_FAST_CTS	56
+#define IEEE80211_DUR_DS_SLOT		20
+#define IEEE80211_DUR_DS_SIFS		10
+
+	int remainder;
+
+	*dur = (80 * (WLAN_ADDR3_HDR_LEN + payload_len) + plcp_signal - 1)
+		/ plcp_signal;
+
+	if (plcp_signal <= PLCP_SIGNAL_2M) {
+		/* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */
+		*dur += 3 * (IEEE80211_DUR_DS_SIFS +
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE +
+			     IEEE80211_DUR_DS_FAST_PLCPHDR) +
+			     IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK;
+	} else {
+		/* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */
+		*dur += 3 * (IEEE80211_DUR_DS_SIFS +
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE +
+			     IEEE80211_DUR_DS_FAST_PLCPHDR) +
+			     IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK;
+	}
+
+	/* lengthen duration if long preamble */
+	if (!short_preamble) {
+		*dur +=
+			3 * (IEEE80211_DUR_DS_LONG_PREAMBLE -
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE) +
+			3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR -
+			     IEEE80211_DUR_DS_FAST_PLCPHDR);
+	}
+
+
+	*plcp = (80 * len) / plcp_signal;
+	remainder = (80 * len) % plcp_signal;
+	if (plcp_signal == PLCP_SIGNAL_11M &&
+	    remainder <= 30 && remainder > 0) {
+		*plcp = (*plcp | 0x8000) + 1;
+	} else if (remainder)
+		(*plcp)++;
+}
+
+/* TODO: add addr4 to this */
+/* Transmit skb (802.11 header created by hardware) */
+static int adm8211_tx(struct net_device *dev, struct sk_buff *skb,
+		      __le16 fc, int short_preamble, u8 *dst)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct adm8211_tx_hdr *txhdr;
+	int entry;
+	dma_addr_t mapping;
+	u32 flag;
+	unsigned long flags;
+	u16 fc_;
+	size_t payload_len;
+	int plcp, dur, len;
+
+	int plcp_signal;
+
+	fc_ = le16_to_cpu(fc);
+	if (WLAN_FC_GET_TYPE(fc_) != WLAN_FC_TYPE_DATA) {
+		if (fc_ & WLAN_FC_ISWEP && wep_encrypt(skb, 0, data)) {
+			printk(KERN_DEBUG "%s: failed to WEP encrypt frame\n", dev->name);
+			dev_kfree_skb(skb);
+			return 0;
+		}
+		fc_ &= ~WLAN_FC_ISWEP;
+	}
+
+	payload_len = skb->len;
+
+	if (skb_headroom(skb) < sizeof(struct adm8211_tx_hdr)) {
+		if (pskb_expand_head(skb, sizeof(struct adm8211_tx_hdr), 0,
+				     GFP_ATOMIC)) {
+			printk(KERN_DEBUG "%s: failed to allocate room for TX "
+			       "header\n", dev->name);
+			dev_kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	plcp_signal = ieee80211_get_rate(&priv->ieee80211, dst) * 5;
+
+	txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr));
+	memset(txhdr, 0, sizeof(*txhdr));
+	memcpy(txhdr->da, dst, ETH_ALEN);
+	txhdr->signal = plcp_signal;
+	txhdr->frame_body_size = cpu_to_le16(payload_len);
+	txhdr->frame_control = fc;
+
+	len = WLAN_ADDR3_HDR_LEN + payload_len + WLAN_FCS_LEN;
+	if (fc_ & WLAN_FC_ISWEP)
+		len += 8;
+
+	if (len > priv->frag_thresh && is_valid_ether_addr(dst)) {
+	txhdr->frag_threshold = cpu_to_le16(priv->frag_thresh);
+
+	len = WLAN_ADDR3_HDR_LEN + priv->frag_thresh + WLAN_FCS_LEN;
+	if (fc_ & WLAN_FC_ISWEP)
+		len += 8;
+	adm8211_calc_durations(&dur, &plcp, priv->frag_thresh,
+				len, plcp_signal, short_preamble);
+	txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
+	txhdr->dur_frag_head = cpu_to_le16(dur);
+
+	if (payload_len % priv->frag_thresh) {
+		len = WLAN_ADDR3_HDR_LEN +
+		      (payload_len % priv->frag_thresh) + WLAN_FCS_LEN;
+		if (fc_ & WLAN_FC_ISWEP)
+			len += 8;
+
+		adm8211_calc_durations(&dur, &plcp, payload_len % priv->frag_thresh,
+				       len, plcp_signal, short_preamble);
+
+		txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
+		txhdr->dur_frag_tail = cpu_to_le16(dur);
+	} else {
+		txhdr->plcp_frag_tail_len = txhdr->plcp_frag_head_len;
+		txhdr->dur_frag_tail = txhdr->dur_frag_head;
+	}
+
+	} else {
+		txhdr->frag_threshold = cpu_to_le16(0x0FFF);
+		adm8211_calc_durations(&dur, &plcp, payload_len,
+				       len, plcp_signal, short_preamble);
+		txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
+		txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
+		txhdr->dur_frag_head = cpu_to_le16(dur);
+		txhdr->dur_frag_tail = cpu_to_le16(dur);
+	}
+
+	txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER);
+
+	if (short_preamble)
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE);
+
+	if ((payload_len + (fc_ & WLAN_FC_ISWEP ? 8 : 0)) > priv->rts_thresh
+	    && is_valid_ether_addr(dst))
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS);
+
+	if (fc_ & WLAN_FC_ISWEP)
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE);
+
+	txhdr->retry_limit = priv->retry_limit;
+	mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+				 PCI_DMA_TODEVICE);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	entry = priv->cur_tx % TX_RING_SIZE;
+
+	priv->tx_buffers[entry].skb = skb;
+	priv->tx_buffers[entry].mapping = mapping;
+	priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping);
+
+	if (priv->cur_tx - priv->dirty_tx < TX_RING_SIZE / 2)
+		flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else if (priv->cur_tx - priv->dirty_tx == TX_RING_SIZE / 2)
+		flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else if (priv->cur_tx - priv->dirty_tx < TX_RING_SIZE - 2)
+		flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else {
+		flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+		netif_stop_queue(dev);
+	}
+	if (entry == TX_RING_SIZE - 1)
+		flag |= TDES1_CONTROL_TER;
+	priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len);
+
+	/* Set TX rate (SIGNAL field in PLCP PPDU format) */
+	flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */;
+	priv->tx_ring[entry].status = cpu_to_le32(flag);
+
+	priv->cur_tx++;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Trigger transmit poll */
+	ADM8211_CSR_WRITE(TDR, 0);
+
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+
+static int adm8211_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	return ieee80211_data_tx(&priv->ieee80211, skb);
+}
+
+
+int adm8211_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+	memcpy(haddr, skb->mac.raw + 10 + sizeof(struct avs_caphdr), ETH_ALEN); /* addr2 */
+	return ETH_ALEN;
+}
+
+static int __devinit adm8211_probe(struct pci_dev *pdev,
+				   const struct pci_device_id *id)
+{
+	struct net_device *dev;
+	struct adm8211_priv *priv;
+	unsigned long mem_addr, mem_len;
+	unsigned int io_addr, io_len;
+	unsigned int ring_size;
+	int err;
+	u32 reg;
+
+#ifndef MODULE
+	static unsigned int cardidx;
+	if (!cardidx++) {
+		printk(version);
+		printk(KERN_INFO "adm8211: release " RELEASE_DATE "\n");
+	}
+#endif
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", pci_name(pdev));
+		return err;
+	}
+
+	io_addr = pci_resource_start(pdev, 0);
+	io_len = pci_resource_len(pdev, 0);
+	mem_addr = pci_resource_start(pdev, 1);
+	mem_len = pci_resource_len(pdev, 1);
+	if (io_len < 256 || mem_len < 1024) {
+		printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", pci_name(pdev));
+		goto err_disable_pdev;
+	}
+
+
+	/* check signature */
+	pci_read_config_dword(pdev, 0x80 /* CR32 */, &reg);
+	if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) {
+		printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", pci_name(pdev), reg);
+		goto err_disable_pdev;
+	}
+
+	err = pci_request_regions(pdev, "adm8211");
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", pci_name(pdev));
+		goto err_disable_pdev;
+	}
+
+	if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+		printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", pci_name(pdev));
+		goto err_free_reg;
+	}
+	pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+
+	pci_set_master(pdev);
+
+	dev = alloc_etherdev(sizeof(struct adm8211_priv));
+	if (!dev) {
+		printk(KERN_ERR "%s (adm8211): Etherdev alloc failed\n", pci_name(pdev));
+		err = -ENOMEM;
+		goto err_free_reg;
+	}
+	priv = netdev_priv(dev);
+	priv->pdev = pdev;
+
+	spin_lock_init(&priv->lock);
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	pci_set_drvdata(pdev, dev);
+
+	priv->map = pci_iomap(pdev, 1, mem_len);
+	if (!priv->map)
+		priv->map = pci_iomap(pdev, 0, io_len);
+
+	if (!priv->map) {
+		printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", pci_name(pdev));
+		goto err_free_dev;
+	}
+
+	dev->irq = pdev->irq;
+
+	/* Allocate TX/RX descriptors */
+	ring_size = sizeof(struct adm8211_desc) * RX_RING_SIZE +
+		sizeof(struct adm8211_desc) * TX_RING_SIZE;
+	priv->rx_ring = pci_alloc_consistent(pdev, ring_size,
+					     &priv->rx_ring_dma);
+	if (!priv->rx_ring) {
+		printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", pci_name(pdev));
+		goto err_iounmap;
+	}
+	priv->tx_ring = (struct adm8211_desc *) (priv->rx_ring + RX_RING_SIZE);
+	priv->tx_ring_dma = priv->rx_ring_dma +
+		sizeof(struct adm8211_desc) * RX_RING_SIZE;
+
+	skb_queue_head_init(&priv->rx_queue);
+	priv->rx_tasklet.func = adm8211_rx_tasklet;
+	priv->rx_tasklet.data = (unsigned long) dev;
+
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &priv->revid);
+
+	/* Power-on issue */
+	ADM8211_CSR_WRITE(FRCTL, 0);
+	ADM8211_CSR_READ(FRCTL);
+	ADM8211_CSR_WRITE(FRCTL, 1);
+	ADM8211_CSR_READ(FRCTL);
+	mdelay(100);
+
+	/* Clear the missed-packet counter. */
+	ADM8211_CSR_READ(LPC);
+
+	put_unaligned(ADM8211_CSR_READ(PAR0), (u32 *) dev->dev_addr);
+	put_unaligned(ADM8211_CSR_READ(PAR1) & (__force u32) __constant_cpu_to_le32(0xffff),
+		      (u16 *) &dev->dev_addr[4]);
+
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING "%s (adm8211): Invalid hwaddr! Using randomly generated hwaddr\n", pci_name(pdev));
+		random_ether_addr(dev->dev_addr);
+	}
+
+	dev->features |= NETIF_F_LLTX;
+	dev->open = adm8211_open;
+	dev->stop = adm8211_stop;
+	dev->get_stats = adm8211_get_stats;
+	dev->set_multicast_list = adm8211_set_rx_mode;
+	dev->set_mac_address = adm8211_set_mac_address;
+	dev->hard_start_xmit = adm8211_hard_start_xmit;
+
+	dev->get_wireless_stats = adm8211_get_wireless_stats;
+	dev->wireless_handlers =
+		(struct iw_handler_def *) &adm8211_iw_handler_def;
+
+	init_timer(&priv->timer);
+	priv->timer.data = (unsigned long) dev;
+	priv->timer.function = adm8211_timer;
+
+	priv->iw_mode = IW_MODE_INFRA;
+
+	priv->ieee80211.dev = dev;
+	priv->ieee80211.set_channel = adm8211_rf_set_channel;
+	priv->ieee80211.set_bssid = adm8211_set_bssid;
+	priv->ieee80211.set_ssid = adm8211_set_ssid;
+	priv->ieee80211.tx = adm8211_tx;
+	priv->ieee80211.set_interval = adm8211_set_interval;
+	priv->ieee80211.set_tbtt = adm8211_set_tbtt;
+	priv->ieee80211.get_tsft = adm8211_get_tsft;
+	priv->ieee80211.link_change = adm8211_link_change;
+	priv->ieee80211.set_rate = adm8211_set_rate;
+	priv->ieee80211.supp_rates = (u8 *)IEEE80211_B_RATES;
+	priv->ieee80211.capab = WLAN_CAPABILITY_SHORT_PREAMBLE;
+	if (ieee80211_init(&priv->ieee80211)) {
+		printk(KERN_ERR "%s (adm8211): Cannot load ARC4 cipher\n", pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	priv->rts_thresh = 2347;
+	priv->frag_thresh = 2346;
+	priv->plcp_signal = priv->ieee80211.rate * 5;
+	priv->retry_limit = 3;
+	priv->ant_power = 0x40;
+	priv->tx_power = 0x40;
+	priv->lpf_cutoff = 0xFF;
+	priv->lnags_threshold = 0xFF;
+
+	err = adm8211_read_eeprom(dev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot allocate eeprom buffer\n", pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	err = register_netdev(dev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot register netdevice\n", pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	printk("%s: hwaddr %02x:%02x:%02x:%02x:%02x:%02x, IRQ %d, Rev 0x%02x\n",
+	       dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5],
+	       dev->irq, priv->revid);
+
+	return 0;
+
+ err_free_desc:
+	pci_free_consistent(pdev, ring_size, priv->rx_ring, priv->rx_ring_dma);
+
+ err_iounmap:
+	pci_iounmap(pdev, priv->map);
+
+ err_free_dev:
+	pci_set_drvdata(pdev, NULL);
+	free_netdev(dev);
+
+ err_free_reg:
+	pci_release_regions(pdev);
+
+ err_disable_pdev:
+	pci_disable_device(pdev);
+	return err;
+}
+
+
+static void __devexit adm8211_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct adm8211_priv *priv;
+
+	if (!dev)
+		return;
+
+	unregister_netdev(dev);
+
+	priv = netdev_priv(dev);
+	ieee80211_deinit(&priv->ieee80211);
+
+	pci_free_consistent(pdev,
+			    sizeof(struct adm8211_desc) * RX_RING_SIZE +
+			    sizeof(struct adm8211_desc) * TX_RING_SIZE,
+			    priv->rx_ring, priv->rx_ring_dma);
+
+	kfree(priv->eeprom);
+	pci_iounmap(pdev, priv->map);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	free_netdev(dev);
+}
+
+
+#ifdef CONFIG_PM
+static int adm8211_suspend(struct pci_dev *pdev, u32 state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	netif_device_detach(dev);
+
+	if (dev->flags & IFF_UP)
+		dev->stop(dev);
+
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, 3);
+	return 0;
+}
+
+static int adm8211_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	pci_set_power_state(pdev, 0);
+	pci_restore_state(pdev);
+
+	if (dev->flags & IFF_UP)
+		dev->open(dev);
+
+	netif_device_attach(dev);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table);
+
+/* TODO: enable_wake */
+static struct pci_driver adm8211_driver = {
+	.name		= "adm8211",
+	.id_table	= adm8211_pci_id_table,
+	.probe		= adm8211_probe,
+	.remove		= __devexit_p(adm8211_remove),
+#ifdef CONFIG_PM
+	.suspend	= adm8211_suspend,
+	.resume		= adm8211_resume,
+#endif /* CONFIG_PM */
+};
+
+
+
+static int __init adm8211_init(void)
+{
+#ifdef MODULE
+	printk(version);
+	printk(KERN_INFO "adm8211: release " RELEASE_DATE "\n");
+#endif
+
+	return pci_register_driver(&adm8211_driver);
+}
+
+
+static void __exit adm8211_exit(void)
+{
+	pci_unregister_driver(&adm8211_driver);
+
+	printk(KERN_INFO "adm8211: Driver unloaded\n");
+}
+
+
+module_init(adm8211_init);
+module_exit(adm8211_exit);
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211_ioctl.c no-sources-new/drivers/net/wireless/adm8211/adm8211_ioctl.c
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211_ioctl.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/adm8211_ioctl.c	2005-05-05 21:46:25.803290144 +0000
@@ -0,0 +1,974 @@
+/*
+ * Linux driver for ADMtek adm8211 (IEEE 802.11b wireless LAN card)
+ *
+ * Copyright (c) 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004-2005, Michael Wu <flamingice@sourmilk.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT > 12
+#include <net/iw_handler.h>
+#endif /* WIRELESS_EXT > 12 */
+#include <linux/random.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+
+#include "adm8211.h"
+#include "adm8211_ioctl.h"
+
+
+static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+				  2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+#define FREQ_COUNT ARRAY_SIZE(freq_list)
+
+#if 0
+#define SET_IWFREQ(freq, chan) \
+        freq->m = freq_list[chan - 1] * 100000; \
+        freq->e = 1;
+#else
+#define SET_IWFREQ(freq, chan) \
+	freq->m = chan; \
+        freq->e = 0;
+#endif
+
+static int adm8211_ioctl_giwname(struct net_device *dev,
+				 struct iw_request_info *info,
+				 char *name, char *extra)
+{
+	strcpy(name, "IEEE 802.11b");
+	return 0;
+}
+
+static int adm8211_ioctl_siwfreq(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_freq *freq, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	/* freq => chan. */
+	if (freq->e == 1 &&
+	    freq->m / 100000 >= freq_list[0] &&
+	    freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) {
+		int ch;
+		int fr = freq->m / 100000;
+		for (ch = 0; ch < FREQ_COUNT; ch++) {
+			if (fr == freq_list[ch]) {
+				freq->e = 0;
+				freq->m = ch + 1;
+				break;
+			}
+		}
+	}
+
+	if (freq->e != 0 || freq->m < priv->ieee80211.chan_range.min || freq->m > priv->ieee80211.chan_range.max)
+		return -EINVAL;
+
+	if (dev->flags & IFF_UP) {
+		if (priv->iw_mode == IW_MODE_MONITOR)
+			adm8211_rf_set_channel(dev, freq->m);
+		else
+			ieee80211_start_scan(&priv->ieee80211);
+	}
+
+	priv->ieee80211.channel = priv->ieee80211.pref_channel = freq->m;
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwfreq(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_freq *freq, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int chan = priv->ieee80211.channel;
+
+	if (chan < 1 || chan > FREQ_COUNT)
+		return -EINVAL;
+
+	SET_IWFREQ(freq, chan);
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_siwmode(struct net_device *dev,
+				 struct iw_request_info *info,
+				 __u32 *mode, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+
+	if (*mode != IW_MODE_INFRA && *mode != IW_MODE_ADHOC &&
+	    *mode != IW_MODE_MONITOR)
+		return -EOPNOTSUPP;
+
+	if (*mode == priv->iw_mode)
+		return 0;
+
+	ieee80211_stop(data);
+	priv->iw_mode = *mode;
+	
+	switch (*mode) {
+	case IW_MODE_INFRA:
+		priv->ieee80211.mode = IEEE80211_MANAGED;
+		priv->ieee80211.capab &= ~WLAN_CAPABILITY_IBSS;
+		dev->type = ARPHRD_ETHER;
+		dev->hard_header_parse = priv->eth_header_parse;
+		break;
+	case IW_MODE_ADHOC:
+		priv->ieee80211.mode = IEEE80211_ADHOC;
+		priv->ieee80211.capab |= WLAN_CAPABILITY_IBSS;
+		dev->type = ARPHRD_ETHER;
+		dev->hard_header_parse = priv->eth_header_parse;
+		break;
+	case IW_MODE_MONITOR:
+		priv->ieee80211.mode = IEEE80211_MONITOR;
+		dev->type = ARPHRD_IEEE80211_PRISM;
+		dev->hard_header_parse = adm8211_80211_header_parse;
+		break;
+	}
+
+	if (dev->flags & IFF_UP) {
+		adm8211_update_mode(dev);
+		ieee80211_start(data);
+	}
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwmode(struct net_device *dev,
+				 struct iw_request_info *info,
+				 __u32 *mode, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	*mode = priv->iw_mode;
+	return 0;
+}
+
+static int adm8211_ioctl_giwrange(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *dwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct iw_range *range = (struct iw_range *) extra;
+	int i, j;
+
+        dwrq->length = sizeof(struct iw_range);
+        memset(range, 0, sizeof(range));
+
+	range->min_nwid = range->max_nwid = 0;
+	
+	range->max_qual.qual  = 255;
+	range->max_qual.level = 1;
+	range->max_qual.noise = 1;
+
+	range->num_bitrates = data->num_supp_rates;
+	i = 0;
+	while (i++ < min(data->num_supp_rates, IW_MAX_BITRATES))
+		range->bitrate[i-1] = (data->supp_rates[i-1]*1000000)/2;
+
+	range->min_rts = 0;
+	range->max_rts = 2346;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	/* are the values really in dBm? */
+	range->txpower_capa = IW_TXPOW_RANGE | IW_TXPOW_DBM;
+
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = WIRELESS_EXT;
+	range->num_channels = data->chan_range.max - data->chan_range.min + 1;
+	range->num_frequency = range->num_channels;
+
+	range->max_encoding_tokens = 4;
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+	range->num_encoding_sizes = 2;
+
+	j=0;
+	for (i = data->chan_range.min; i <= data->chan_range.max; i+=1) {
+		range->freq[j].m=freq_list[i-1] * 100000;
+		range->freq[j].e=1;
+		range->freq[j].i=i;
+		j+=1;
+	}
+	return 0;
+}
+
+/*
+static int adm8211_ioctl_giwsens(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	struct ieee80211_bss *bss = ieee80211_get_bss(data, data->bssid);
+	return 0;
+}
+*/
+static int adm8211_ioctl_siwscan(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_point *data, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (dev->flags & IFF_UP && priv->iw_mode != IW_MODE_MONITOR)
+		ieee80211_start_scan(&priv->ieee80211);
+	else
+		return -1;
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwscan(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_point *data, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct iw_event iwe;
+	char *current_ev = extra;
+	char *end_buf = extra + IW_SCAN_MAX_DATA;
+	char *current_val;
+	struct ieee80211_bss *bss;
+	int i;
+
+	if (priv->ieee80211.state == IEEE80211_SCAN)
+		return -EAGAIN;
+
+	spin_lock_bh(&priv->ieee80211.lock);
+	bss = priv->ieee80211.bss;
+	while (bss) {
+		/* First entry must be AP MAC address; other entries will be
+		 * shown in the order they are added. */
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+		iwe.len = IW_EV_ADDR_LEN;
+		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.length = bss->ssid_len;
+		if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
+			iwe.u.data.length = IW_ESSID_MAX_SIZE;
+		iwe.u.data.flags = 1;
+		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+						  bss->ssid);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWMODE;
+		if (bss->capability & (WLAN_CAPABILITY_ESS |
+				       WLAN_CAPABILITY_IBSS)) {
+			if (bss->capability & WLAN_CAPABILITY_ESS)
+				iwe.u.mode = IW_MODE_MASTER;
+			else
+				iwe.u.mode = IW_MODE_ADHOC;
+			iwe.len = IW_EV_UINT_LEN;
+			current_ev = iwe_stream_add_event(current_ev, end_buf,
+							  &iwe,
+							  IW_EV_UINT_LEN);
+		}
+
+		if (bss->channel >= 1 && bss->channel <= FREQ_COUNT) {
+			memset(&iwe, 0, sizeof(iwe));
+			iwe.cmd = SIOCGIWFREQ;
+			SET_IWFREQ((&iwe.u.freq), bss->channel);
+			iwe.len = IW_EV_FREQ_LEN;
+			current_ev = iwe_stream_add_event(current_ev, end_buf,
+							  &iwe,
+							  IW_EV_FREQ_LEN);
+		}
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.qual = bss->last_rssi;
+		iwe.u.qual.level = 0;
+		iwe.u.qual.noise = 0;
+		iwe.len = IW_EV_QUAL_LEN;
+		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+						  IW_EV_QUAL_LEN);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWENCODE;
+		if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		iwe.u.data.length = 0;
+		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+		/* bss->ssid is a dummy pointer for 0-byte memcpy */
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+						  bss->ssid);
+
+		memset(&iwe, 0, sizeof(iwe));
+		iwe.cmd = SIOCGIWRATE;
+		current_val = current_ev + IW_EV_LCP_LEN;
+		for (i = 0; i < sizeof(bss->supp_rates); i++) {
+			if (bss->supp_rates[i] == 0)
+				break;
+			/* Bit rate given in 500 kb/s units (+ 0x80) */
+			iwe.u.bitrate.value =
+				((bss->supp_rates[i] & 0x7f) * 500000);
+			current_val = iwe_stream_add_value(
+				current_ev, current_val, end_buf, &iwe,
+				IW_EV_PARAM_LEN);
+		}
+		if ((current_val - current_ev) > IW_EV_LCP_LEN)
+			current_ev = current_val;
+
+		bss = bss->next;
+	}
+	spin_unlock_bh(&priv->ieee80211.lock);
+
+	data->length = current_ev - extra;
+	return 0;
+}
+
+
+static int adm8211_ioctl_giwessid(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *essid)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	data->flags = 1; /* active */
+	data->length = priv->ieee80211.ssid_len;
+	if (data->length > IW_ESSID_MAX_SIZE)
+		data->length = IW_ESSID_MAX_SIZE;
+	memset(essid, 0, IW_ESSID_MAX_SIZE);
+	memcpy(essid, priv->ieee80211.ssid, data->length);
+	return 0;
+}
+
+
+static int adm8211_ioctl_siwessid(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *essid)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int len;
+
+	if (data->flags == 0 || data->length < 1)
+		len = 0;
+	else
+		len = data->length - 1;
+	if (len > IEEE80211_MAX_SSID_LEN)
+		len = IEEE80211_MAX_SSID_LEN;
+
+	memcpy(priv->ieee80211.ssid, essid, len);
+	priv->ieee80211.ssid_len = len;
+	if (len)
+		priv->ieee80211.flags &= ~ANY_SSID;
+	else
+		priv->ieee80211.flags |= ANY_SSID;
+
+	if (dev->flags & IFF_UP)
+		ieee80211_start(&priv->ieee80211);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwnickn(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *nickn)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+        memset(nickn, 0, IW_ESSID_MAX_SIZE);
+        strncpy(nickn, priv->ieee80211.nick, IW_ESSID_MAX_SIZE);
+        return 0;
+}
+
+static int adm8211_ioctl_siwnickn(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_point *data, char *nickn)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int len = data->length;
+
+	if (len > IW_ESSID_MAX_SIZE)
+		len = IW_ESSID_MAX_SIZE;
+
+	memset(priv->ieee80211.nick, 0, IW_ESSID_MAX_SIZE);
+	strncpy(priv->ieee80211.nick, nickn, len);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwap(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct sockaddr *ap_addr, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	ap_addr->sa_family = ARPHRD_ETHER;
+	if (dev->flags & IFF_UP || !(priv->ieee80211.flags & PREF_BSSID_SET))
+		memcpy(ap_addr->sa_data, priv->ieee80211.bssid, ETH_ALEN);
+	else
+		memcpy(ap_addr->sa_data, priv->ieee80211.pref_bssid, ETH_ALEN);
+
+	return 0;
+}
+
+
+static int adm8211_ioctl_siwap(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct sockaddr *ap_addr, char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	u8 *addr = ap_addr->sa_data;
+	memcpy(priv->ieee80211.pref_bssid, addr, ETH_ALEN);
+	if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5])
+		priv->ieee80211.flags |= PREF_BSSID_SET;
+	else
+		priv->ieee80211.flags &= ~PREF_BSSID_SET;
+
+	if (dev->flags & IFF_UP)
+		ieee80211_start(&priv->ieee80211);
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwrate(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int i, j;
+
+	if (vwrq->value <= 0)
+		return -EINVAL;
+
+	data->flags &= ~AUTO_RATE;
+	if (!vwrq->fixed) {
+		data->flags |= AUTO_RATE;
+
+		/* assumes last rate is the highest rate */
+		data->rate = data->supp_rates[data->num_supp_rates-1];
+	} else if (vwrq->value <= data->num_supp_rates) {
+		data->rate = data->supp_rates[vwrq->value-1];
+	} else {
+		i = 0;
+		j = (vwrq->value*2)/1000000;
+
+		/* make sure the rate given matches a supported rate */
+		while (i < data->num_supp_rates && data->supp_rates[i] != j)
+			i+=1;
+
+		/* give up if it doesn't */
+		if (i >= data->num_supp_rates)
+			return -EINVAL;
+
+		data->rate = j;
+	}
+
+	if (dev->flags & IFF_UP)
+		data->set_rate(dev);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwrate(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+
+	vwrq->fixed = !(data->flags & AUTO_RATE);
+	vwrq->value = (data->rate*1000000)/2;
+	return 0;
+}
+
+static int adm8211_ioctl_siwrts(struct net_device *dev,
+				struct iw_request_info *info,
+				struct iw_param *vwrq,
+				char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (vwrq->disabled)
+		priv->rts_thresh = 2347;
+	else if ((vwrq->value >= 0) && (vwrq->value <= 2346))
+		priv->rts_thresh = vwrq->value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwrts(struct net_device *dev,
+				struct iw_request_info *info,
+				struct iw_param *vwrq,
+				char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->value = priv->rts_thresh;
+	vwrq->disabled = (vwrq->value > 2346);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwfrag(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (vwrq->disabled)
+		priv->frag_thresh = 2346;
+	else if ((vwrq->value >= 256) && (vwrq->value <= 2346))
+		priv->frag_thresh = vwrq->value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwfrag(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *vwrq,
+				 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->value = priv->frag_thresh;
+	vwrq->disabled = (vwrq->value >= 2346);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwtxpow(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->flags = IW_TXPOW_DBM;
+	if (priv->tx_power > 0x3F) {
+		vwrq->fixed = 0;
+		vwrq->value = priv->eeprom->tx_power[priv->ieee80211.channel-1];
+	} else {
+		vwrq->fixed = 1;
+		vwrq->value = priv->tx_power;
+	}
+
+	if (!vwrq->value)
+		vwrq->disabled = 1;
+	else
+		vwrq->disabled = 0;
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwtxpow(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (vwrq->disabled || !vwrq->fixed) {
+		priv->tx_power = 0x40;
+	} else if (vwrq->value >= 0 && vwrq->value <= 0x3F) {
+		priv->tx_power = vwrq->value;
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_siwretry(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT))
+		priv->retry_limit = vwrq->value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwretry(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *vwrq,
+				  char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	vwrq->disabled = 0;
+	vwrq->value = priv->retry_limit;
+	vwrq->flags = IW_RETRY_LIMIT;
+
+	return 0;
+}
+
+/* WEP ioctls adapted from atmel driver */
+static int adm8211_ioctl_siwencode(struct net_device *dev,
+				   struct iw_request_info *info,
+				   struct iw_point *dwrq,
+				   char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+
+	if (dwrq->length > 0) {
+		if (dwrq->length > 13)
+			return -EINVAL;
+
+		/* Check the index (none -> use current) */
+		if (index < 0 || index >= 4)
+			index = data->default_key;
+		else
+			data->default_key = index;
+
+		if (dwrq->length > 5)
+			data->wep_data[index].len = 13;
+		else
+			data->wep_data[index].len = 5;
+
+		/* Check if the key is not marked as invalid */
+		if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
+			/* Cleanup */
+			memset(data->wep_data[index].key, 0, 13);
+			/* Copy the key in the driver */
+			memcpy(data->wep_data[index].key, extra, dwrq->length);
+		} else {
+			memset(data->wep_data[index].key, 0, 13);
+			data->wep_data[index].len = 0;
+		}
+
+		if (dev->flags & IFF_UP)
+			adm8211_write_wepkey(dev, index);
+	} else {
+		/* Do we want to just set the transmit key index ? */
+		if ( index >= 0 && index < 4 )
+			data->default_key = index;
+		else if (!dwrq->flags & IW_ENCODE_MODE)
+			return -EINVAL;
+	}
+
+	if (dwrq->flags & IW_ENCODE_DISABLED) {
+		data->flags &= ~WEP_ENABLED;
+		data->auth_algorithm = 0;
+		priv->ieee80211.capab &= ~WLAN_CAPABILITY_PRIVACY;
+	} else {
+		data->flags |= WEP_ENABLED;
+		if (dwrq->flags & IW_ENCODE_RESTRICTED)
+			data->flags |= WEP_RESTRICTED;
+		else
+			data->flags &= ~WEP_RESTRICTED;
+		data->auth_algorithm = 1;
+		priv->ieee80211.capab |= WLAN_CAPABILITY_PRIVACY;
+	}
+
+	if (dev->flags & IFF_UP)
+		adm8211_update_wep(dev);
+
+	return 0;
+}
+
+static int adm8211_ioctl_giwencode(struct net_device *dev,
+				   struct iw_request_info *info,
+				   struct iw_point *dwrq,
+				   char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	struct ieee80211_data *data = &priv->ieee80211;
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	if (data->flags & WEP_ENABLED) {
+		if (data->flags & WEP_RESTRICTED)
+			dwrq->flags = IW_ENCODE_RESTRICTED;
+		else
+			dwrq->flags = IW_ENCODE_OPEN;
+	} else
+		 dwrq->flags = IW_ENCODE_DISABLED;
+
+	if (index < 0 || index >= 4)
+		index = data->default_key;
+
+	dwrq->flags |= index + 1;
+	dwrq->length = data->wep_data[index].len;
+	memset(extra, 0, 16);
+	memcpy(extra, data->wep_data[index].key, dwrq->length);
+
+	return 0;
+}
+
+static const iw_handler adm8211_handler[] =
+{
+	(iw_handler) NULL,				/* SIOCSIWCOMMIT */
+	(iw_handler) adm8211_ioctl_giwname,		/* SIOCGIWNAME */
+	(iw_handler) NULL,				/* SIOCSIWNWID */
+	(iw_handler) NULL,				/* SIOCGIWNWID */
+	(iw_handler) adm8211_ioctl_siwfreq,		/* SIOCSIWFREQ */
+	(iw_handler) adm8211_ioctl_giwfreq,		/* SIOCGIWFREQ */
+	(iw_handler) adm8211_ioctl_siwmode,		/* SIOCSIWMODE */
+	(iw_handler) adm8211_ioctl_giwmode,		/* SIOCGIWMODE */
+	(iw_handler) NULL,				/* SIOCSIWSENS */
+	(iw_handler) NULL,				/* SIOCGIWSENS */
+	(iw_handler) NULL /* not used */,		/* SIOCSIWRANGE */
+	(iw_handler) adm8211_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) adm8211_ioctl_siwap,		/* SIOCSIWAP */
+	(iw_handler) adm8211_ioctl_giwap,		/* SIOCGIWAP */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* SIOCGIWAPLIST */
+	(iw_handler) adm8211_ioctl_siwscan,		/* SIOCSIWSCAN */
+	(iw_handler) adm8211_ioctl_giwscan,		/* SIOCGIWSCAN */
+	(iw_handler) adm8211_ioctl_siwessid,		/* SIOCSIWESSID */
+	(iw_handler) adm8211_ioctl_giwessid,		/* SIOCGIWESSID */
+	(iw_handler) adm8211_ioctl_siwnickn,		/* SIOCSIWNICKN */
+	(iw_handler) adm8211_ioctl_giwnickn,		/* SIOCGIWNICKN */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) adm8211_ioctl_siwrate,		/* SIOCSIWRATE */
+	(iw_handler) adm8211_ioctl_giwrate,		/* SIOCGIWRATE */
+	(iw_handler) adm8211_ioctl_siwrts,		/* SIOCSIWRTS */
+	(iw_handler) adm8211_ioctl_giwrts,		/* SIOCGIWRTS */
+	(iw_handler) adm8211_ioctl_siwfrag,		/* SIOCSIWFRAG */
+	(iw_handler) adm8211_ioctl_giwfrag,		/* SIOCGIWFRAG */
+	(iw_handler) adm8211_ioctl_siwtxpow,		/* SIOCSIWTXPOW */
+	(iw_handler) adm8211_ioctl_giwtxpow,		/* SIOCGIWTXPOW */
+	(iw_handler) adm8211_ioctl_siwretry,		/* SIOCSIWRETRY */
+	(iw_handler) adm8211_ioctl_giwretry,		/* SIOCGIWRETRY */
+	(iw_handler) adm8211_ioctl_siwencode,		/* SIOCSIWENCODE */
+	(iw_handler) adm8211_ioctl_giwencode,		/* SIOCGIWENCODE */
+	(iw_handler) NULL,				/* SIOCSIWPOWER */
+	(iw_handler) NULL,				/* SIOCGIWPOWER */
+};
+
+static int adm8211_ioctl_sreg (struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu,
+			       char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int *i = (int *) extra;
+	u32 addr = i[0];
+	u32 reg = i[1];
+
+	if (addr >= 0 && addr <= 0x10C) {
+		printk(KERN_NOTICE "%s: writing 0x%x to 0x%x\n",
+		       dev->name, reg, addr);
+		iowrite32(reg, (void __iomem *)priv->map + addr);
+	} else {
+		printk(KERN_NOTICE "%s: setreg: register value out of range\n", dev->name);
+	}
+	return 0;
+}
+
+static int adm8211_ioctl_greg (struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu,
+			       char *extra)
+{
+        struct adm8211_priv *priv = netdev_priv(dev);
+        u32 addr = wrqu->param.value;
+
+	if (addr >= 0 && addr <= 0x10C) {
+	        printk(KERN_NOTICE "%s: value of register 0x%x is 0x%x\n",
+		       dev->name, addr, ioread32((void __iomem *)priv->map + addr));
+	} else {
+		printk(KERN_NOTICE "%s: getreg: register value out of range\n", dev->name);
+	}
+        return 0;
+}
+
+static int adm8211_ioctl_print_eeprom (struct net_device *dev,
+				       struct iw_request_info *info,
+				       union iwreq_data *wrqu,
+				       char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int i, bl;
+
+	if (priv->eeprom) {
+	printk(KERN_NOTICE "%s: eeprom dump:\n", dev->name);
+
+	for (bl = 0; bl < priv->eeprom_len; bl += 16) {
+		printk(KERN_NOTICE "%s:", dev->name);
+		for (i = 0; i<16; i+=1)
+			printk(" %02x", ((u8 *)priv->eeprom)[bl+i]);
+		printk("\n");
+	}
+
+	} else
+		printk(KERN_NOTICE "%s: no eeprom data\n", dev->name);
+
+	return 0;
+}
+
+static int adm8211_ioctl_set_country (struct net_device *dev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu,
+				      char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+	int code = wrqu->param.value;
+
+	if (code >= ARRAY_SIZE(cranges) || code < 0)
+		return -EINVAL;
+
+	/* potential, but unlikely race, I think. need to check */
+	priv->ieee80211.chan_range = cranges[code];
+	printk(KERN_DEBUG "%s: Channel range: %d-%d\n",
+	       dev->name, (int)priv->ieee80211.chan_range.min, (int)priv->ieee80211.chan_range.max);
+
+	return 0;
+}
+
+static int adm8211_ioctl_set_antpower(struct net_device *dev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu,
+				      char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (wrqu->param.value > 0x3F) {
+		priv->ant_power = 0x40;
+	} else if (wrqu->param.value >= 0 && wrqu->param.value <= 0x3F) {
+		priv->ant_power = wrqu->param.value;
+	} else
+		return -EINVAL;
+
+	adm8211_rf_set_channel(dev, priv->ieee80211.channel);
+	return 0;
+}
+
+static int adm8211_ioctl_set_lpfcutoff(struct net_device *dev,
+				       struct iw_request_info *info,
+				       union iwreq_data *wrqu,
+				       char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (wrqu->param.value >= 0 && wrqu->param.value <= 0xFF) {
+		priv->lpf_cutoff = wrqu->param.value;
+	} else
+		return -EINVAL;
+
+	adm8211_rf_set_channel(dev, priv->ieee80211.channel);
+	return 0;
+}
+
+static int adm8211_ioctl_set_lnagsthresh(struct net_device *dev,
+					 struct iw_request_info *info,
+					 union iwreq_data *wrqu,
+					 char *extra)
+{
+	struct adm8211_priv *priv = netdev_priv(dev);
+
+	if (wrqu->param.value >= 0 && wrqu->param.value <= 0xFF) {
+		priv->lnags_threshold = wrqu->param.value;
+	} else
+		return -EINVAL;
+
+	adm8211_rf_set_channel(dev, priv->ieee80211.channel);
+	return 0;
+}
+
+static int adm8211_ioctl_print_radio (struct net_device *dev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu,
+				      char *extra)
+{
+        struct adm8211_priv *priv = netdev_priv(dev);
+	int channel = priv->ieee80211.channel - 1;
+
+	printk(KERN_DEBUG "%s: Antenna Power: %d (%d) \n",
+			dev->name, priv->ant_power,
+			priv->eeprom->antenna_power[channel]);
+	printk(KERN_DEBUG "%s: LPF Cutoff: %d (%d)\n",
+			dev->name, priv->lpf_cutoff,
+			priv->eeprom->lpf_cutoff[channel]);
+	printk(KERN_DEBUG "%s: LNAGS Threshold: %d (%d)\n",
+			dev->name, priv->lnags_threshold,
+			priv->eeprom->lnags_threshold[channel]);
+	
+	return 0;
+}
+
+static const iw_handler adm8211_private_handler[] =
+{							/* SIOCIWFIRSTPRIV + */
+	(iw_handler) adm8211_ioctl_sreg,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_greg,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_print_eeprom,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_print_radio,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_country,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_antpower,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_lpfcutoff,
+	(iw_handler) NULL,
+	(iw_handler) adm8211_ioctl_set_lnagsthresh,
+};
+
+static const struct iw_priv_args adm8211_priv[] = {
+	{SIOCIWFIRSTPRIV, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setreg" },
+	{0},
+	{SIOCIWFIRSTPRIV+2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "getreg" },
+	{0},
+	{SIOCIWFIRSTPRIV+4, IW_PRIV_TYPE_NONE, 0, "print_eeprom" },
+	{0},
+	{SIOCIWFIRSTPRIV+6, IW_PRIV_TYPE_NONE, 0, "print_radio" },
+	{0},
+	{SIOCIWFIRSTPRIV+8, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_country" },
+	{0},
+	{SIOCIWFIRSTPRIV+10, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_antpower" },
+	{0},
+	{SIOCIWFIRSTPRIV+12, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_lpfcutoff" },
+	{0},
+	{SIOCIWFIRSTPRIV+14, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_lnagsthresh" },
+};
+
+const struct iw_handler_def adm8211_iw_handler_def =
+{
+	.num_standard   = ARRAY_SIZE(adm8211_handler),
+	.num_private    = ARRAY_SIZE(adm8211_private_handler),
+	.num_private_args = ARRAY_SIZE(adm8211_priv),
+	.standard       = (iw_handler *) adm8211_handler,
+	.private        = (iw_handler *) adm8211_private_handler,
+	.private_args   = (struct iw_priv_args *) adm8211_priv,
+};
+
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211_ioctl.h no-sources-new/drivers/net/wireless/adm8211/adm8211_ioctl.h
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/adm8211_ioctl.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/adm8211_ioctl.h	2005-05-05 21:46:25.803290144 +0000
@@ -0,0 +1,6 @@
+#ifndef ADM8211_IOCTL_H
+#define ADM8211_IOCTL_H
+
+extern const struct iw_handler_def adm8211_iw_handler_def;
+
+#endif /* ADM8211_IOCTL_H */
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/avs_caphdr.h no-sources-new/drivers/net/wireless/adm8211/avs_caphdr.h
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/avs_caphdr.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/avs_caphdr.h	2005-05-05 21:46:25.807289536 +0000
@@ -0,0 +1,21 @@
+#ifndef AVS_CAPHDR_H
+#define AVS_CAPHDR_H
+
+struct avs_caphdr {
+	__be32 version;
+	__be32 length;
+	__be64 mactime;
+	__be64 hosttime;
+	__be32 phytype;
+	__be32 channel;
+	__be32 datarate;
+	__be32 antenna;
+	__be32 priority;
+	__be32 ssi_type;
+	__be32 ssi_signal;	/* signed */
+	__be32 ssi_noise;	/* signed */
+	__be32 preamble;
+	__be32 encoding;
+} __attribute__ ((packed));
+
+#endif /* AVS_CAPHDR_H */
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/ieee80211.c no-sources-new/drivers/net/wireless/adm8211/ieee80211.c
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/ieee80211.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/ieee80211.c	2005-05-05 21:46:25.808289384 +0000
@@ -0,0 +1,1663 @@
+/*
+ * IEEE 802.11 station / management functionality
+ *
+ * Copyright (c) 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004-2005, Michael Wu <flamingice@sourmilk.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/wireless.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/types.h>
+#include <asm/delay.h>
+#include <asm/scatterlist.h>
+#include <asm/div64.h>
+
+#include "ieee80211.h"
+
+#define BEACON_SKIP 2
+
+#define IEEE80211_SCAN_INTERVAL (5 * HZ)
+#define IEEE80211_SCAN_LISTEN (HZ / 10)
+#define IEEE80211_AUTH_TIMEOUT (1 * HZ)
+#define IEEE80211_AUTH_MAX_TRIES 3
+#define IEEE80211_ASSOC_TIMEOUT (1 * HZ)
+#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_MONITORING_INTERVAL (30 * HZ)
+#define IEEE80211_LINKCHECK_INTERVAL (3 * HZ)
+
+static void ieee80211_timer(unsigned long ptr);
+static void ieee80211_associate(struct ieee80211_data *data);
+
+ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
+				struct ieee802_11_elems *elems)
+{
+	size_t left = len;
+	u8 *pos = start;
+	int unknown = 0;
+
+	memset(elems, 0, sizeof(*elems));
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		left -= 2;
+
+		if (elen > left) {
+#if 0
+			if (net_ratelimit())
+				printk(KERN_DEBUG "IEEE 802.11 element parse "
+				       "failed (id=%d elen=%d left=%d)\n",
+				       id, elen, left);
+#endif
+			return ParseFailed;
+		}
+
+		switch (id) {
+		case WLAN_EID_SSID:
+			elems->ssid = pos;
+			elems->ssid_len = elen;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			elems->supp_rates = pos;
+			elems->supp_rates_len = elen;
+			break;
+		case WLAN_EID_FH_PARAMS:
+			elems->fh_params = pos;
+			elems->fh_params_len = elen;
+			break;
+		case WLAN_EID_DS_PARAMS:
+			elems->ds_params = pos;
+			elems->ds_params_len = elen;
+			break;
+		case WLAN_EID_CF_PARAMS:
+			elems->cf_params = pos;
+			elems->cf_params_len = elen;
+			break;
+		case WLAN_EID_TIM:
+			elems->tim = pos;
+			elems->tim_len = elen;
+			break;
+		case WLAN_EID_IBSS_PARAMS:
+			elems->ibss_params = pos;
+			elems->ibss_params_len = elen;
+			break;
+		case WLAN_EID_CHALLENGE:
+			elems->challenge = pos;
+			elems->challenge_len = elen;
+			break;
+		default:
+#if 0
+			printk(KERN_DEBUG "IEEE 802.11 element parse ignored "
+				      "unknown element (id=%d elen=%d)\n",
+				      id, elen);
+#endif
+			unknown++;
+			break;
+		}
+
+		left -= elen;
+		pos += elen;
+	}
+
+	if (left)
+		return ParseFailed;
+
+	return unknown ? ParseUnknown : ParseOK;
+}
+
+
+static void ieee80211_dump_ssid(u8 *ssid, size_t ssid_len)
+{
+	int i;
+
+	for (i = 0; i < ssid_len; i++)
+		printk((ssid[i] >= 32 && ssid[i] < 127) ?
+		       "%c" : "<%02x>", ssid[i]);
+}
+
+static char * ieee80211_status_code(int status)
+{
+	switch (status) {
+	case WLAN_STATUS_SUCCESS:
+		return "Success";
+	case WLAN_STATUS_UNSPECIFIED_FAILURE:
+		return "Unspecified failure";
+	case WLAN_STATUS_CAPS_UNSUPPORTED:
+		return "Capabilities unsupported";
+	case WLAN_STATUS_REASSOC_NO_ASSOC:
+		return "Reassociation failure; no association";
+	case WLAN_STATUS_ASSOC_DENIED_UNSPEC:
+		return "Association denied";
+	case WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG:
+		return "Authentication algorithm not supported";
+	case WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION:
+		return "Unknown authentication transaction";
+	case WLAN_STATUS_CHALLENGE_FAIL:
+		return "Challenge failed";
+	case WLAN_STATUS_AUTH_TIMEOUT:
+		return "Authentication timeout";
+	case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+		return "AP unable to handle new STA";
+	case WLAN_STATUS_ASSOC_DENIED_RATES:
+		return "Association denied: Rates not supported";
+
+	/* 802.11b */
+	case WLAN_STATUS_ASSOC_DENIED_NOSHORT:
+		return "Association denied: Short preamble not supported";
+	case WLAN_STATUS_ASSOC_DENIED_NOPBCC:
+		return "Association denied: PBCC not supported";
+	case WLAN_STATUS_ASSOC_DENIED_NOAGILITY:
+		return "Association denied: Agility not supported";
+	default:
+		return "Unknown status code";
+	}
+}
+
+
+static void ieee80211_set_associated(struct ieee80211_data *data, int assoc)
+{
+	if ((data->flags & ASSOCIATED) == (assoc ? ASSOCIATED : 0))
+		return;
+
+	if (assoc)
+		data->flags |= ASSOCIATED;
+	else
+		data->flags &= ~ASSOCIATED;
+
+	if (data->link_change)
+		data->link_change(data->dev, assoc);
+}
+
+
+struct ieee80211_bss * ieee80211_get_bss(struct ieee80211_data *data, u8 *addr)
+{
+	struct ieee80211_bss *bss;
+
+	bss = data->bss;
+	while (bss) {
+		if (memcmp(bss->bssid, addr, ETH_ALEN) == 0)
+			return bss;
+		bss = bss->next;
+	}
+	return NULL;
+}
+
+
+static int ieee80211_bss_score(struct ieee80211_data *data,
+			       struct ieee80211_bss *bss)
+{
+	int score, i;
+
+	score = bss->last_rssi;
+	score -= 50 * bss->num_auth_fail;
+	score -= 50 * bss->num_assoc_fail;
+
+	/* detect hidden SSIDs and avoid them */
+	bss->hidden_ssid = 1;
+	for (i = 0; i < bss->ssid_len; i += 1)
+		if (bss->ssid[i] != '\0') {
+			bss->hidden_ssid = 0;
+			break;
+		}
+	
+	if (bss->hidden_ssid)
+		score -= 50;
+
+	if (data->pref_channel == bss->channel)
+		score += 100;
+
+	return score;
+}
+
+/* removes all bss */
+static void ieee80211_free_bss(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss, *prev;
+	unsigned long flags;
+
+	/* need irqsave as opposed to bh so this can be run during suspend */
+	spin_lock_irqsave(&data->lock, flags);
+
+	bss = data->bss;
+	data->bss = NULL;
+	while (bss) {
+		prev = bss;
+		bss = bss->next;
+		kfree(prev);
+	}
+
+	spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static int ieee80211_select_bssid(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss, *prev = NULL, *selected = NULL;
+	int best_score = 0, score;
+	u64 oldest_timestamp = 0;
+
+	spin_lock_bh(&data->lock);
+	bss = data->bss;
+
+	/* Select first BSS that matches with current configuration */
+	while (bss) {
+		score = ieee80211_bss_score(data, bss);
+		if (score < -250)
+			goto skip;
+
+		if (data->mode == IEEE80211_MANAGED && 
+		    ((bss->capability & WLAN_CAPABILITY_IBSS) || 
+		    !(bss->capability & WLAN_CAPABILITY_ESS) ))
+			goto skip;
+		if (data->mode == IEEE80211_ADHOC &&
+		    ((bss->capability & WLAN_CAPABILITY_ESS) ||
+		    !(bss->capability & WLAN_CAPABILITY_IBSS) ))
+			goto skip;
+
+		printk(KERN_DEBUG "%s: CHAN=%d BSSID=" MACSTR " SSID=",
+		       data->dev->name, bss->channel, MAC2STR(bss->bssid));
+		ieee80211_dump_ssid(bss->ssid, bss->ssid_len);
+		printk(" RSSI=%d num=%lu/%lu score=%d\n", bss->last_rssi,
+		       bss->num_beacon, bss->num_proberesp, score);
+
+		if (data->flags & PREF_BSSID_SET &&
+		    memcmp(bss->bssid, data->pref_bssid, ETH_ALEN) == 0) {
+			selected = bss;
+			break;
+		} else if (data->flags & ANY_SSID || bss->ssid_len == 0 ||
+			  (bss->ssid_len == data->ssid_len && 
+			  (bss->hidden_ssid || !memcmp(bss->ssid, data->ssid, data->ssid_len)))) {
+			if (data->mode == IEEE80211_MANAGED &&
+			    (!selected || score > best_score) ) {
+				best_score = score;
+				selected = bss;
+			} else if (data->mode == IEEE80211_ADHOC &&
+				   bss->timestamp > oldest_timestamp) {
+				oldest_timestamp = bss->timestamp;
+				selected = bss;
+			}
+		}
+
+		skip:
+		prev = bss;
+		bss = bss->next;
+	}
+
+	if (selected) {
+		if (memcmp(data->bssid, selected->bssid, ETH_ALEN) != 0) {
+			printk(KERN_DEBUG "%s: new BSSID " MACSTR "\n",
+			       data->dev->name, MAC2STR(selected->bssid));
+			ieee80211_set_associated(data, 0);
+		}
+		memcpy(data->bssid, selected->bssid, ETH_ALEN);
+		if (selected->hidden_ssid)
+			data->flags |= HIDDEN_SSID;
+		else {
+			data->flags &= ~HIDDEN_SSID;
+			data->ssid_len = selected->ssid_len;
+			memcpy(data->ssid, selected->ssid, selected->ssid_len);
+		}
+		data->channel = selected->channel;
+		data->beacon_interval = (selected->interval ? selected->interval : 100);
+		data->timestamp = selected->timestamp;
+	}
+
+	spin_unlock_bh(&data->lock);
+
+	return selected ? 0 : -1;
+}
+
+static void ieee80211_sync_ibss(struct ieee80211_data *data)
+{
+	u64 tbttpre;
+	u32 beacon = data->beacon_interval << 10;
+
+	tbttpre  = data->timestamp;
+	tbttpre  = data->timestamp - do_div(tbttpre, beacon);
+	tbttpre += (beacon*BEACON_SKIP) << 10;			/* skip a few beacons in case we're slow */
+	tbttpre -= 20 << 10;				/* wake up early */
+
+	tbttpre >>= 10;					/* convert everything to TUs */
+
+	data->set_tbtt(data->dev, (u16)(tbttpre & 0xFFFF));
+}
+
+static void ieee80211_send_auth(struct ieee80211_data *data, struct ieee80211_bss *bss)
+{
+	struct sk_buff *skb;
+	union ieee80211_mgmt_payload *mgmt;
+	u8 *pos;
+	__le16 fc;
+
+	if (!data->tx)
+		return;
+
+	skb = dev_alloc_skb(6 + 2 + WLAN_AUTH_CHALLENGE_LEN + 8);
+	if (skb == NULL) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
+		       "frame\n", data->dev->name);
+		return;
+	}
+
+	mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 6);
+	fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+			  WLAN_FC_STYPE_AUTH);
+
+	bss->auth_state+=1;
+	if (bss->auth_alg == WLAN_AUTH_OPEN) {
+		mgmt->auth.auth_alg = cpu_to_le16(WLAN_AUTH_OPEN);
+		mgmt->auth.auth_transaction = cpu_to_le16(bss->auth_state);
+		mgmt->auth.status_code = 0;
+
+		if (bss->auth_state != 1) {
+			printk(KERN_DEBUG "%s: Unable to handle authentication transaction: %d\n",
+			       data->dev->name, bss->auth_state);
+			goto fail;
+		}
+	} else if (bss->auth_alg == WLAN_AUTH_SHARED_KEY) {
+		mgmt->auth.auth_alg = cpu_to_le16(WLAN_AUTH_SHARED_KEY);
+		mgmt->auth.auth_transaction = cpu_to_le16(bss->auth_state);
+		mgmt->auth.status_code = 0;
+
+		switch (bss->auth_state) {
+		case 1:
+			break;
+		case 3:
+			pos = skb_put(skb, 2 + WLAN_AUTH_CHALLENGE_LEN);
+			*pos++ = WLAN_EID_CHALLENGE;
+			*pos++ = WLAN_AUTH_CHALLENGE_LEN;
+			if (bss->challenge) {
+				memcpy(pos, bss->challenge, WLAN_AUTH_CHALLENGE_LEN);
+			} else {
+				printk(KERN_DEBUG "%s: No challenge to return\n", data->dev->name);
+				goto fail;
+			}
+
+			fc |= cpu_to_le16(WLAN_FC_ISWEP);
+			break;
+		default:
+			printk(KERN_DEBUG "%s: Unable to handle authentication transaction: %d\n",
+			       data->dev->name, bss->auth_state);
+			goto fail;
+		}
+	} else {
+		printk(KERN_DEBUG "%s: Unknown authentication algorithm: %d\n", data->dev->name, bss->auth_alg);
+		goto fail;
+	}
+
+	printk(KERN_DEBUG "%s: TX authentication (alg=%d transaction=%d)\n",
+	       data->dev->name, bss->auth_alg, bss->auth_state);
+
+	data->tx(data->dev, skb, fc, bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE, bss->bssid);
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static void ieee80211_authenticate(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss;
+
+	data->auth_tries++;
+	if (data->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: authentication with AP " MACSTR
+		       " CHAN=%d timed out\n",
+		       data->dev->name, MAC2STR(data->bssid), data->channel);
+
+		spin_lock_bh(&data->lock);
+		bss = ieee80211_get_bss(data, data->bssid);
+		if (bss)
+			bss->num_auth_fail++;
+		spin_unlock_bh(&data->lock);
+		ieee80211_start_scan(data);
+		return;
+	}
+
+	if (data->set_channel)
+		data->set_channel(data->dev, data->channel);
+	if (data->set_bssid)
+		data->set_bssid(data->dev, data->bssid);
+	if (data->set_rate)
+		data->set_rate(data->dev);
+	if (data->set_interval)
+		data->set_interval(data->dev);
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, data->bssid);
+	if (bss->auth_state != 5) {
+		data->state = IEEE80211_AUTHENTICATE;
+		printk(KERN_DEBUG "%s: authenticate with AP " MACSTR " CHAN=%d\n",
+		       data->dev->name, MAC2STR(data->bssid), data->channel);
+
+		ieee80211_set_associated(data, 0);
+		bss->auth_state = 0;
+		ieee80211_send_auth(data, bss);
+	}
+	spin_unlock_bh(&data->lock);
+
+	if (data->state != IEEE80211_AUTHENTICATE)
+		ieee80211_associate(data);
+
+	mod_timer(&data->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+}
+
+static void ieee80211_send_assoc_req(struct ieee80211_data *data)
+{
+	struct sk_buff *skb;
+	union ieee80211_mgmt_payload *mgmt;
+	struct ieee80211_bss *bss;
+	u8 *pos;
+	__le16 fc;
+	int i = 0;
+	int reassoc = 0;
+
+	if (!data->tx)
+		return;
+
+	skb = dev_alloc_skb(2 + 2 + 2 + data->ssid_len + 2 + data->num_supp_rates);
+	if (skb == NULL) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc req "
+		       "frame\n", data->dev->name);
+		return;
+	}
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, data->bssid);
+
+	/* TODO: check how prev_bssid_set is suppose to work */
+	if (data->flags & PREV_BSSID_SET) {
+		mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 10);
+		fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+				  WLAN_FC_STYPE_REASSOC_REQ);
+		mgmt->reassoc_req.capab_info = cpu_to_le16(bss->capability);
+		mgmt->reassoc_req.listen_interval =
+			cpu_to_le16(data->listen_interval);
+		memcpy(mgmt->reassoc_req.current_ap, data->prev_bssid,
+		       ETH_ALEN);
+		reassoc = 1;
+	} else {
+		mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 4);
+		fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+				  WLAN_FC_STYPE_ASSOC_REQ);
+		mgmt->assoc_req.capab_info = cpu_to_le16(bss->capability);
+		mgmt->assoc_req.listen_interval =
+			cpu_to_le16(data->listen_interval);
+	}
+
+	/* SSID */
+	pos = skb_put(skb, 2 + data->ssid_len);
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = data->ssid_len;
+	memcpy(pos, data->ssid, data->ssid_len);
+
+	pos = skb_put(skb, 2 + data->num_supp_rates);
+	/* All supported rates */
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = data->num_supp_rates;
+	while (i++ < data->num_supp_rates)
+		*pos++ = data->supp_rates[i-1];
+
+	printk(KERN_DEBUG "%s: TX %sssocReq (capab=0x%x)\n",
+	       data->dev->name, reassoc ? "Rea" : "A", bss->capability);
+
+	data->tx(data->dev, skb, fc, bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE, data->bssid);
+	spin_unlock_bh(&data->lock);
+}
+
+static void ieee80211_associated(struct ieee80211_data *data)
+{
+	struct ieee80211_bss *bss, *tmp, *prev = NULL;
+
+	/* TODO: start monitoring current AP signal quality and number of
+	 * missed beacons. Scan other channels every now and then and search
+	 * for better APs. */
+
+	spin_lock_bh(&data->lock);
+
+	/* remove expired BSSes */
+	bss = data->bss;
+	while (bss) {
+		if (time_after(jiffies, bss->last_rx + IEEE80211_MONITORING_INTERVAL) &&
+		     memcmp(data->bssid, bss->bssid, ETH_ALEN)) {
+			if (prev)
+				prev->next = bss->next;
+			else
+				data->bss = bss->next;
+
+			tmp = bss;
+			bss = bss->next;
+			kfree(tmp);
+		} else {
+			prev = bss;
+			bss = bss->next;
+		}
+	}
+
+	spin_unlock_bh(&data->lock);
+
+	if (data->state != IEEE80211_ASSOCIATED) {
+		data->state = IEEE80211_ASSOCIATED;
+		netif_wake_queue(data->dev);
+		mod_timer(&data->timer, jiffies + IEEE80211_LINKCHECK_INTERVAL);
+	} else {
+		if (data->flags & LINK_ON || data->mode == IEEE80211_MANAGED)
+			mod_timer(&data->timer, jiffies + IEEE80211_MONITORING_INTERVAL);
+		else
+			ieee80211_start_scan(data);
+	}
+}
+
+static void ieee80211_associate(struct ieee80211_data *data)
+{
+	if (data->flags & ASSOCIATED) {
+		ieee80211_associated(data);
+		return;
+	}
+
+	data->assoc_tries++;
+	if (data->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
+		struct ieee80211_bss *bss;
+		printk(KERN_DEBUG "%s: association with AP " MACSTR
+		       " CHAN=%d timed out\n",
+		       data->dev->name, MAC2STR(data->bssid), data->channel);
+		spin_lock_bh(&data->lock);
+		bss = ieee80211_get_bss(data, data->bssid);
+		if (bss) {
+			bss->num_assoc_fail++;
+			if (bss->num_assoc_fail > 6) {
+				spin_unlock_bh(&data->lock);
+				ieee80211_free_bss(data);
+				/* yuck */
+				spin_lock_bh(&data->lock);
+			}
+		}
+		spin_unlock_bh(&data->lock);
+		ieee80211_start_scan(data);
+		return;
+	}
+
+	data->state = IEEE80211_ASSOCIATE;
+	printk(KERN_DEBUG "%s: associate with AP " MACSTR "\n",
+	       data->dev->name, MAC2STR(data->bssid));
+
+	ieee80211_send_assoc_req(data);
+
+	mod_timer(&data->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
+}
+
+static void ieee80211_send_probe_req(struct ieee80211_data *data)
+{
+	struct sk_buff *skb;
+	union ieee80211_mgmt_payload *mgmt;
+	u8 *pos;
+	__le16 fc;
+	u8 dst[ETH_ALEN];
+	int i = 0;
+
+	if (!data->tx)
+		return;
+
+	skb = dev_alloc_skb(2 + 2 + data->num_supp_rates);
+	if (skb == NULL) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+		       "request\n", data->dev->name);
+		return;
+	}
+
+	mgmt = (union ieee80211_mgmt_payload *) skb_put(skb, 2 + 2 + data->num_supp_rates);
+	memset(mgmt, 0, 2 + 2 + data->num_supp_rates);
+	fc = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+			  WLAN_FC_STYPE_PROBE_REQ);
+	memset(dst, ~0, ETH_ALEN);
+	pos = mgmt->probe_req.variable;
+
+	/* Broadcast SSID */
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = 0;
+
+	/* All supported rates */
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = data->num_supp_rates;
+	while (i++ < data->num_supp_rates)
+		*pos++ = data->supp_rates[i-1];
+
+	spin_lock_bh(&data->lock);
+	data->tx(data->dev, skb, fc, 0, dst);
+	spin_unlock_bh(&data->lock);
+}
+
+
+static void ieee80211_scan(struct ieee80211_data *data)
+{
+	data->state = IEEE80211_SCAN;
+	if (data->channel == 0) {
+		printk(KERN_DEBUG "%s: scanning..\n", data->dev->name);
+		data->channel = data->chan_range.min;
+	} else
+		data->channel++;
+	
+	if (data->channel > data->chan_range.max) {
+	if (data->mode == IEEE80211_MANAGED) {
+		if (ieee80211_select_bssid(data) == 0) {
+			ieee80211_authenticate(data);
+			return;
+		} else {
+			ieee80211_free_bss(data);
+
+			data->channel = 0;
+			mod_timer(&data->timer,
+				  jiffies + IEEE80211_SCAN_INTERVAL);
+			return;
+		}
+	} else if (data->mode == IEEE80211_ADHOC) {
+		if (ieee80211_select_bssid(data)) {
+			random_ether_addr(data->bssid);
+			data->beacon_interval = 100;
+			if (data->pref_channel)
+				data->channel = data->pref_channel;
+			else
+				data->channel = data->chan_range.min;
+
+			if (!data->ssid) {
+				printk(KERN_WARNING "%s: No SSID set. SSID set to \"default\"\n", data->dev->name);
+				memcpy(data->ssid, "default", 7);
+				data->ssid_len = 7;
+			}
+			printk(KERN_DEBUG "%s: No matching adhoc sta found. Creating IBSS " MACSTR " CHAN=%d\n",
+			       data->dev->name, MAC2STR(data->bssid), data->channel);
+			data->timestamp = data->get_tsft(data->dev);
+		} else {
+			printk(KERN_DEBUG "%s: joining IBSS " MACSTR " CHAN=%d\n",
+			       data->dev->name, MAC2STR(data->bssid), data->channel);
+		}
+
+		if (data->set_rate)
+			data->set_rate(data->dev);
+		if (data->set_interval)
+			data->set_interval(data->dev);
+		if (data->set_bssid)
+			data->set_bssid(data->dev, data->bssid);
+		if (data->set_ssid)
+			data->set_ssid(data->dev, data->ssid, data->ssid_len);
+		if (data->set_channel)
+			data->set_channel(data->dev, data->channel);
+		ieee80211_sync_ibss(data);
+
+		data->flags |= ASSOCIATED;
+		ieee80211_associated(data);
+		return;
+	}
+	}
+
+	if (data->set_channel)
+		data->set_channel(data->dev, data->channel);
+
+	ieee80211_send_probe_req(data);
+
+	mod_timer(&data->timer, jiffies + IEEE80211_SCAN_LISTEN);
+}
+
+
+void ieee80211_start_scan(struct ieee80211_data *data)
+{
+	static const u8 scan_bssid[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+	/* don't do anything if we're already scanning */
+	if (data->state == IEEE80211_SCAN)
+		return;
+
+	data->channel = 0;
+	data->auth_tries = data->assoc_tries = 0;
+
+	/* Set BSSID to ff:ff:ff:ff:ff:ff for active scanning */
+	if (data->set_bssid)
+		data->set_bssid(data->dev, (u8 *)scan_bssid);
+
+	data->beacon_interval = 0;
+	if (data->set_interval)
+		data->set_interval(data->dev);
+
+	if (in_interrupt()) {
+		data->state = IEEE80211_SCAN;
+		mod_timer(&data->timer, jiffies + IEEE80211_SCAN_LISTEN);
+	} else {
+		ieee80211_scan(data);
+	}
+}
+
+void ieee80211_start(struct ieee80211_data *data)
+{
+	if (data->mode == IEEE80211_MANAGED || data->mode == IEEE80211_ADHOC)
+		ieee80211_start_scan(data);
+}
+
+int ieee80211_init(struct ieee80211_data *data)
+{
+	init_timer(&data->timer);
+	data->timer.data = (unsigned long) data;
+	data->timer.function = ieee80211_timer;
+
+	spin_lock_init(&data->lock);
+
+	data->listen_interval = 10;
+	data->mode = IEEE80211_MANAGED;
+	data->state = IEEE80211_STOP;
+	data->flags = AUTO_RATE | ANY_SSID;
+
+	if (!data->supp_rates)
+		data->supp_rates = (u8 *)IEEE80211_B_RATES;
+
+	data->num_supp_rates=strlen(data->supp_rates);
+	data->rate = data->supp_rates[data->num_supp_rates-1];
+
+	data->tfm = crypto_alloc_tfm("arc4", 0);
+	if (!data->tfm)
+		return -1;
+
+	get_random_bytes(&data->wep_data[0].iv, 4);
+	get_random_bytes(&data->wep_data[1].iv, 4);
+	get_random_bytes(&data->wep_data[2].iv, 4);
+	get_random_bytes(&data->wep_data[3].iv, 4);
+
+	return 0;
+}
+
+void ieee80211_deinit(struct ieee80211_data *data)
+{
+	crypto_free_tfm(data->tfm);
+
+	return;
+}
+
+void ieee80211_stop(struct ieee80211_data *data)
+{
+	int i;
+
+	del_timer_sync(&data->timer);
+	data->state = IEEE80211_STOP;
+
+	ieee80211_free_bss(data);
+	for (i = 0; i < IEEE80211_FRAG_CACHE_SIZE; i+=1) {
+		if (data->frag_cache[i].skb) {
+			dev_kfree_skb(data->frag_cache[i].skb);
+			data->frag_cache[i].skb = NULL;
+		}
+	}
+
+	memset(data->bssid, 0, ETH_ALEN);
+	if (data->set_ssid)
+		data->set_ssid(data->dev, data->ssid, 0);
+	if (data->set_bssid)
+		data->set_bssid(data->dev, data->bssid);
+}
+
+/* TODO: merge with the only function that calls this, or reduce the number of arguments */
+static void ieee80211_add_bss(struct ieee80211_data *data,
+			      u8 *bssid, struct ieee802_11_elems *elems,
+			      int channel, struct ieee80211_mgmt *mgmt,
+			      int rssi, u8 rate, int beacon)
+{
+	struct ieee80211_bss *bss;
+	int i;
+
+	spin_lock_bh(&data->lock);
+
+	bss = data->bss;
+
+	while (bss != NULL &&
+	       (memcmp(bss->bssid, bssid, ETH_ALEN) != 0 /* ||
+		bss->ssid_len != elems->ssid_len ||
+		memcmp(bss->ssid, elems->ssid, elems->ssid_len) != 0 */ ))
+		bss = bss->next;
+
+	if (bss == NULL) {
+/*		printk(KERN_DEBUG "%s: new BSS - CHAN=%d BSSID=" MACSTR
+		       " SSID=",
+		       data->dev->name, channel, MAC2STR(bssid));
+		ieee80211_dump_ssid(ssid, ssid_len);
+		printk("\n");*/
+
+		bss = (struct ieee80211_bss *)
+			kmalloc(sizeof(*bss), GFP_ATOMIC);
+		if (bss == NULL) {
+			spin_unlock_bh(&data->lock);
+			printk(KERN_DEBUG "%s: failed to allocate BSS info "
+			       "structure\n", data->dev->name);
+			return;
+		}
+		memset(bss, 0, sizeof(*bss));
+		memcpy(bss->bssid, bssid, ETH_ALEN);
+		bss->last_seq = __constant_cpu_to_le16(~0);
+		bss->next = data->bss;
+		data->bss = bss;
+
+		if (mgmt->u.beacon.capab_info & __cpu_to_le16(WLAN_CAPABILITY_PRIVACY))
+			bss->auth_alg = WLAN_AUTH_SHARED_KEY;
+		else
+			bss->auth_alg = WLAN_AUTH_OPEN;
+
+		if (data->state == IEEE80211_ADHOC &&
+		    data->timestamp < bss->timestamp &&
+		    data->get_tsft(data->dev) < bss->timestamp) {
+			printk(KERN_DEBUG "%s: found older BSS - BSSID=" MACSTR "\n",
+					data->dev->name, MAC2STR(bssid));
+		}
+
+	}
+
+	if (beacon)
+		bss->num_beacon++;
+	else
+		bss->num_proberesp++;
+
+	memcpy(bss->ssid, elems->ssid, elems->ssid_len);
+	bss->ssid_len = elems->ssid_len;
+	if (elems->supp_rates) {
+		memset(bss->supp_rates, 0, IEEE80211_MAX_SUPP_RATES);
+		memcpy(bss->supp_rates, elems->supp_rates,
+		       elems->supp_rates_len > IEEE80211_MAX_SUPP_RATES ?
+		       IEEE80211_MAX_SUPP_RATES : elems->supp_rates_len);
+		for (i = data->num_supp_rates; i > 0; i-=1) {
+			if (strchr(bss->supp_rates, data->supp_rates[i-1])) {
+				bss->rate = data->supp_rates[i-1];
+				break;
+			}
+		}
+	}
+	if (!bss->rate)
+		bss->rate = data->supp_rates[data->num_supp_rates-1];
+	bss->channel = channel;
+	bss->interval = le16_to_cpu(mgmt->u.beacon.beacon_int);
+	bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+	bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
+	bss->last_rx = jiffies;
+	if (rssi)
+		bss->last_rssi = rssi;
+	bss->last_rate = rate;
+
+	spin_unlock_bh(&data->lock);
+}
+
+
+/* Process beacon and probe response frames in common function since they have
+ * almost identical contents. */
+static void ieee80211_rx_mgmt_bss_info(struct ieee80211_data *data,
+				       struct ieee80211_mgmt *mgmt,
+				       size_t len,
+				       struct ieee80211_rx_status *rx_status,
+				       int beacon)
+{
+	struct ieee802_11_elems elems;
+	size_t baselen;
+	int channel;
+
+#if 0
+	printk(KERN_DEBUG "%s: %s from " MACSTR " rate=%d rssi=%d\n",
+	       data->dev->name, beacon ? "Beacon" : "ProbeResp",
+	       MAC2STR(mgmt->sa), rx_status->rate, rx_status->rssi);
+#endif
+
+	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+	if (baselen > len) {
+		printk(KERN_DEBUG "%s: Beacon/ProbeResp underflow %d > %d\n",
+		       data->dev->name, baselen, len);
+		return;
+	}
+
+	ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
+/*	if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
+				   &elems) == ParseFailed) {
+		printk(KERN_DEBUG "%s: Beacon/ProbeResp invalid\n",
+		       data->dev->name);
+		return;
+	}*/
+
+/*	if (elems.ssid == NULL)
+		return;*/
+
+	if (elems.ds_params && elems.ds_params_len == 1)
+		channel = elems.ds_params[0];
+	else
+		channel = data->channel;
+
+	ieee80211_add_bss(data, mgmt->bssid, &elems, channel,
+			  mgmt, rx_status->rssi, rx_status->rate, beacon);
+}
+
+static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_data *data,
+					 struct ieee80211_mgmt *mgmt,
+					 size_t len,
+					 struct ieee80211_rx_status *rx_status)
+{
+	ieee80211_rx_mgmt_bss_info(data, mgmt, len, rx_status, 0);
+}
+
+
+static void ieee80211_rx_mgmt_beacon(struct ieee80211_data *data,
+				     struct ieee80211_mgmt *mgmt,
+				     size_t len,
+				     struct ieee80211_rx_status *rx_status)
+{
+	ieee80211_rx_mgmt_bss_info(data, mgmt, len, rx_status, 1);
+}
+
+static void ieee80211_rx_mgmt_auth(struct ieee80211_data *data,
+				   struct ieee80211_mgmt *mgmt,
+				   size_t len,
+				   struct ieee80211_rx_status *rx_status, struct sk_buff *skb)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	struct ieee802_11_elems elems;
+	struct ieee80211_bss *bss;
+	size_t baselen;
+
+	if (data->state != IEEE80211_AUTHENTICATE) {
+		printk(KERN_DEBUG "%s: authentication frame received from "
+		       MACSTR ", but not in authenticate state - ignored\n",
+		       data->dev->name, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (len < 24 + 6) {
+		printk(KERN_DEBUG "%s: too short (%d) authentication frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: authentication frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	baselen = (u8 *) mgmt->u.auth.variable - (u8 *) mgmt;
+	if (baselen > len) {
+		printk(KERN_DEBUG "%s: Auth underflow %d > %d\n",
+		       data->dev->name, baselen, len);
+		return;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable, len - baselen,
+				   &elems) == ParseFailed) {
+		printk(KERN_DEBUG "%s: Parse failed.\n", data->dev->name);
+		return;
+	}
+
+	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
+	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
+	status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
+	printk(KERN_DEBUG "%s: RX authentication (alg=%d "
+	       "transaction=%d status=%d %s)\n",
+	       data->dev->name, auth_alg,
+	       auth_transaction, status_code,
+	       ieee80211_status_code(status_code));
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (!bss) {
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+
+	if (auth_transaction != bss->auth_state + 1) {
+		printk(KERN_DEBUG "%s: unexpected authentication "
+		       "transaction number. expected %d, got %d\n",
+		       data->dev->name, bss->auth_state + 1, auth_transaction);
+		bss->auth_state = 0;
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+	bss->auth_alg = auth_alg;
+	bss->auth_state = auth_transaction;
+
+	if ((auth_transaction == 0x2 || auth_transaction == 0x4)
+	    && status_code != WLAN_STATUS_SUCCESS) {
+		bss->num_auth_fail++;
+		bss->auth_state = 0;
+		if (auth_transaction == 0x2) {
+			bss->auth_alg = WLAN_AUTH_OPEN;
+			printk(KERN_DEBUG "%s: Falling back on open authentication...\n", data->dev->name);
+		}
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+
+	/* TODO: necessary? */
+	if (data->mode == IEEE80211_MANAGED && auth_transaction == 1) {
+		if (status_code == WLAN_STATUS_SUCCESS)
+			printk(KERN_DEBUG "%s: Ignoring attempt to initiate authentication\n",
+			        data->dev->name);
+		return;
+	}
+
+	switch (auth_alg) {
+	case WLAN_AUTH_OPEN:
+		if (auth_transaction == 2)
+			bss->auth_state = 5;
+		else
+			printk(KERN_DEBUG "%s: invalid authentication "
+			       "transaction number: %d\n",
+			       data->dev->name, auth_transaction);
+		break;
+	case WLAN_AUTH_SHARED_KEY:
+		switch (auth_transaction) {
+		case 2:
+			if (elems.challenge_len != WLAN_AUTH_CHALLENGE_LEN) {
+				printk(KERN_DEBUG "%s: invalid challenge length: %d\n",
+				       data->dev->name, elems.challenge_len);
+				return;
+			}
+			bss->challenge = elems.challenge;
+			ieee80211_send_auth(data, bss);
+			break;
+		case 4:
+			bss->auth_state = 5;
+			break;
+		default:
+			printk(KERN_DEBUG "%s: invalid authentication"
+			       "transaction number: %d\n",
+			       data->dev->name, auth_transaction);
+			break;
+		}
+		break;
+	}
+
+	if (bss->auth_state == 5) {
+		data->auth_tries = 0;
+		printk(KERN_DEBUG "%s: authenticated\n", data->dev->name);
+
+		spin_unlock_bh(&data->lock);
+		ieee80211_associate(data);
+	} else
+		spin_unlock_bh(&data->lock);
+}
+
+static void ieee80211_rx_mgmt_deauth(struct ieee80211_data *data,
+				     struct ieee80211_mgmt *mgmt,
+				     size_t len,
+				     struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_bss *bss;
+	u16 reason_code;
+
+	if (len < 24 + 2) {
+		printk(KERN_DEBUG "%s: too short (%d) deauthentication frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: deauthentication frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+	printk(KERN_DEBUG "%s: RX deauthentication from " MACSTR
+	       " (reason=%d)\n",
+	       data->dev->name, MAC2STR(mgmt->sa), reason_code);
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (bss) {
+		bss->num_auth_fail++;
+		bss->auth_state = 0;
+		if (bss->auth_state == 5)
+			printk(KERN_DEBUG "%s: deauthenticated\n", data->dev->name);
+	}
+
+	spin_unlock_bh(&data->lock);
+	ieee80211_set_associated(data, 0);
+
+	ieee80211_authenticate(data);
+}
+
+
+static void ieee80211_rx_mgmt_disassoc(struct ieee80211_data *data,
+				       struct ieee80211_mgmt *mgmt,
+				       size_t len,
+				       struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_bss *bss;
+	u16 reason_code;
+
+	if (len < 24 + 2) {
+		printk(KERN_DEBUG "%s: too short (%d) disassociation frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: disassociation frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+
+	printk(KERN_DEBUG "%s: RX disassociation from " MACSTR
+	       " (reason=%d)\n",
+	       data->dev->name, MAC2STR(mgmt->sa), reason_code);
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (bss)
+		bss->num_assoc_fail++;
+	spin_unlock_bh(&data->lock);
+
+	if (data->flags & ASSOCIATED)
+		printk(KERN_DEBUG "%s: disassociated\n", data->dev->name);
+	ieee80211_set_associated(data, 0);
+
+	ieee80211_associate(data);
+}
+
+static void ieee80211_rx_mgmt_assoc_req(struct ieee80211_data *data,
+					struct ieee80211_mgmt *mgmt,
+					size_t len,
+					struct ieee80211_rx_status *rx_status,
+					int reassoc)
+{
+	if (reassoc && data->mode == IEEE80211_MANAGED)
+		ieee80211_associate(data);
+
+	return;
+}
+
+static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_data *data,
+					 struct ieee80211_mgmt *mgmt,
+					 size_t len,
+					 struct ieee80211_rx_status *rx_status,
+					 int reassoc)
+{
+	static const char hidden_ssid[IEEE80211_MAX_SSID_LEN] = {0};
+	struct ieee802_11_elems elems;
+	struct ieee80211_bss *bss;
+	size_t baselen;
+	u16 capab_info, status_code, aid;
+
+	/* AssocResp and ReassocResp have identical structure, so process both
+	 * of them in this function. */
+
+	if (data->state != IEEE80211_ASSOCIATE) {
+		printk(KERN_DEBUG "%s: association frame received from "
+		       MACSTR ", but not in associate state - ignored\n",
+		       data->dev->name, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (len < 24 + 6) {
+		printk(KERN_DEBUG "%s: too short (%d) association frame "
+		       "received from " MACSTR " - ignored\n",
+		       data->dev->name, len, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (memcmp(data->bssid, mgmt->sa, ETH_ALEN) != 0) {
+		printk(KERN_DEBUG "%s: association frame received from "
+		       "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+		       "ignored\n", data->dev->name, MAC2STR(mgmt->sa),
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+	aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+
+	printk(KERN_DEBUG "%s: RX %sssocResp (capab=0x%x "
+	       "aid=%d status=%d %s)\n",
+	       data->dev->name, reassoc ? "Rea" : "A",
+	       capab_info, aid & 0x3FFF, status_code,
+	       ieee80211_status_code(status_code));
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, mgmt->sa);
+	if (status_code != WLAN_STATUS_SUCCESS) {
+		printk(KERN_DEBUG "%s: AP denied association\n",
+		       data->dev->name);
+		if (bss)
+			bss->num_assoc_fail++;
+		spin_unlock_bh(&data->lock);
+		return;
+	}
+
+	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
+		printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
+		       "set\n", data->dev->name, aid);
+	aid &= ~(BIT(15) | BIT(14));
+
+	if (bss)
+		bss->num_auth_fail = bss->num_assoc_fail = 0;
+
+	baselen = (u8 *) mgmt->u.assoc_resp.variable - (u8 *) mgmt;
+        if (ieee802_11_parse_elems(mgmt->u.assoc_resp.variable, len - baselen, &elems) == ParseOK
+	    && elems.supp_rates) {
+		memset(bss->supp_rates, 0, IEEE80211_MAX_SUPP_RATES);
+		memcpy(bss->supp_rates, elems.supp_rates,
+		       elems.supp_rates_len > IEEE80211_MAX_SUPP_RATES ?
+		       IEEE80211_MAX_SUPP_RATES : elems.supp_rates_len);
+	}
+
+	spin_unlock_bh(&data->lock);
+
+	printk(KERN_DEBUG "%s: associated\n", data->dev->name);
+	data->aid = aid;
+	data->assoc_tries = 0;
+	ieee80211_set_associated(data, 1);
+
+	if (data->set_ssid) {
+		if (data->flags & HIDDEN_SSID)
+			data->set_ssid(data->dev, (char *)hidden_ssid, data->ssid_len);
+		else
+			data->set_ssid(data->dev, data->ssid, data->ssid_len);
+	}
+	ieee80211_associated(data);
+}
+
+
+void ieee80211_rx_mgmt(struct ieee80211_data *data, struct sk_buff *skb,
+		       struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_mgmt *mgmt;
+	u16 fc;
+
+	if (skb->len < 24)
+		goto fail;
+
+	mgmt = (struct ieee80211_mgmt *) skb->data;
+	fc = le16_to_cpu(mgmt->frame_control);
+
+	switch (WLAN_FC_GET_STYPE(fc)) {
+	case WLAN_FC_STYPE_PROBE_REQ:
+		break;
+	case WLAN_FC_STYPE_PROBE_RESP:
+		ieee80211_rx_mgmt_probe_resp(data, mgmt, skb->len, rx_status);
+		break;
+	case WLAN_FC_STYPE_BEACON:
+		ieee80211_rx_mgmt_beacon(data, mgmt, skb->len, rx_status);
+		break;
+	case WLAN_FC_STYPE_AUTH:
+		ieee80211_rx_mgmt_auth(data, mgmt, skb->len, rx_status, skb);
+		break;
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		ieee80211_rx_mgmt_assoc_req(data, mgmt, skb->len, rx_status, 0);
+		break;
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		ieee80211_rx_mgmt_assoc_resp(data, mgmt, skb->len, rx_status, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		ieee80211_rx_mgmt_assoc_req(data, mgmt, skb->len, rx_status, 1);
+		break;
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		ieee80211_rx_mgmt_assoc_resp(data, mgmt, skb->len, rx_status, 1);
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		ieee80211_rx_mgmt_deauth(data, mgmt, skb->len, rx_status);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		ieee80211_rx_mgmt_disassoc(data, mgmt, skb->len, rx_status);
+		break;
+	default:
+		printk(KERN_DEBUG "%s: received unknown management frame - "
+		       "stype=%d\n", data->dev->name, WLAN_FC_GET_STYPE(fc));
+		break;
+	}
+
+ fail:
+	if (skb)
+		dev_kfree_skb(skb);
+}
+
+
+static void ieee80211_timer(unsigned long ptr)
+{
+	struct ieee80211_data *data = (struct ieee80211_data *) ptr;
+
+	switch (data->state) {
+	case IEEE80211_SCAN:
+		ieee80211_scan(data);
+		break;
+	case IEEE80211_AUTHENTICATE:
+		ieee80211_authenticate(data);
+		break;
+	case IEEE80211_ASSOCIATE:
+		ieee80211_associate(data);
+		break;
+	case IEEE80211_ASSOCIATED:
+		ieee80211_associated(data);
+		break;
+	case IEEE80211_STOP:
+		break;
+	default:
+		printk(KERN_DEBUG "ieee80211_timer: Unknown state %d\n",
+		       data->state);
+		break;
+	}
+}
+
+struct sk_buff * ieee80211_wep_decode(struct ieee80211_data *data,
+				      struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int hlen;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	if (!(fc & WLAN_FC_ISWEP))
+		return skb;
+
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+		  (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		hlen = 30;
+	else
+		hlen = 24;
+
+	if (wep_decrypt(skb, hlen, data)) {
+		dev_kfree_skb(skb);
+		return NULL;
+	} else
+		return skb;
+}
+
+int ieee80211_data_tx(struct ieee80211_data *data,
+		      struct sk_buff *skb)
+{
+	struct ieee80211_bss *bss;
+	u8 dst[ETH_ALEN];
+	u16 fc = (WLAN_FC_TYPE_DATA << 2) | (WLAN_FC_STYPE_DATA << 4);
+	int rc, short_preamble = 0;
+
+	/* hold the packets till we're authenticated/associated */
+	if (data->state != IEEE80211_ASSOCIATED) {
+		netif_stop_queue(data->dev);
+		return 1;
+	}
+
+	if (skb->len < ETH_HLEN) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	if (data->flags & WEP_ENABLED)
+		fc |= WLAN_FC_ISWEP;
+
+	switch (data->mode) {
+	case IEEE80211_MANAGED:
+		fc |= WLAN_FC_TODS;
+		break;
+	case IEEE80211_ADHOC:
+		break;
+	case IEEE80211_AP:
+		fc |= WLAN_FC_FROMDS;
+		break;
+	case IEEE80211_WDS:
+		fc |= WLAN_FC_FROMDS | WLAN_FC_TODS;
+		break;
+	case IEEE80211_MONITOR:
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	memcpy(dst, skb->data, ETH_ALEN);
+
+	skb = ieee80211_data_encaps(data, skb);
+
+        spin_lock_bh(&data->lock);
+        bss = ieee80211_get_bss(data, dst);
+
+	if (bss)
+		short_preamble = bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+	rc = data->tx(data->dev, skb, cpu_to_le16(fc), short_preamble, dst);
+        spin_unlock_bh(&data->lock);
+
+	return rc;
+}
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+
+struct sk_buff * ieee80211_data_encaps(struct ieee80211_data *data,
+				       struct sk_buff *skb)
+{
+	u16 ethertype;
+
+	ethertype = (skb->data[12] << 8) | skb->data[13];
+
+	skb_pull(skb, 12);
+
+	memcpy(skb_push(skb, 6),
+	       ethertype == ETH_P_AARP || ethertype == ETH_P_IPX ?
+	       bridge_tunnel_header : rfc1042_header, 6);
+
+	return skb;
+}
+
+
+struct sk_buff * ieee80211_data_decaps(struct ieee80211_data *data,
+				       struct sk_buff *skb)
+{
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	struct ieee80211_hdr *hdr;
+	int hlen;
+	u16 fc;
+	u8 *pos;
+
+	if (data->state != IEEE80211_ASSOCIATED ||
+	    skb->len < 24)
+		goto drop;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	hlen = 24;
+
+	switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+	case WLAN_FC_TODS | WLAN_FC_FROMDS:
+		/* RA, TA, DA, SA */
+		if (skb->len < 30)
+			goto drop;
+		memcpy(dst, hdr->addr3, ETH_ALEN);
+		memcpy(src, hdr->addr4, ETH_ALEN);
+		hlen += ETH_ALEN;
+		break;
+	case WLAN_FC_TODS:
+		/* BSSID, SA, DA */
+		memcpy(dst, hdr->addr3, ETH_ALEN);
+		memcpy(src, hdr->addr2, ETH_ALEN);
+		break;
+	case WLAN_FC_FROMDS:
+		/* DA, BSSID, SA */
+		memcpy(dst, hdr->addr1, ETH_ALEN);
+		memcpy(src, hdr->addr3, ETH_ALEN);
+		break;
+	case 0:
+		/* DA, SA, BSSID */
+		memcpy(dst, hdr->addr1, ETH_ALEN);
+		memcpy(src, hdr->addr2, ETH_ALEN);
+		break;
+	}
+
+	pos = skb_pull(skb, hlen);
+	if (hlen >= 6 &&
+	    (memcmp(pos, rfc1042_header, 6) == 0 ||
+	     memcmp(pos, bridge_tunnel_header, 6) == 0)) {
+		skb_pull(skb, 6);
+	}
+
+	memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+	memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+
+	return skb;
+
+drop:
+	dev_kfree_skb(skb);
+	return NULL;
+}
+
+struct sk_buff * ieee80211_reassemble_frag (struct ieee80211_data *data,
+			       struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr, *hdr2;
+	struct ieee80211_frag_entry *entry;
+	u16 seq_ctrl, fc;
+	unsigned int seq, frag, hlen;
+	int i, j;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+	seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
+	seq = WLAN_GET_SEQ_SEQ(seq_ctrl);
+	frag = WLAN_GET_SEQ_FRAG(seq_ctrl);
+
+	if (!frag && fc & WLAN_FC_MOREFRAG) {
+		i = 0;
+		/* look for an empty entry */
+		while (i < IEEE80211_FRAG_CACHE_SIZE
+		       && data->frag_cache[i].skb != NULL)
+			i+=1;
+
+		if (data->frag_cache[i].skb) { /* cache is full */
+			i = -1;
+			for (j = 0; j < IEEE80211_FRAG_CACHE_SIZE; j += 1) {
+			entry = &data->frag_cache[j];
+			if (entry->skb != NULL &&
+			    time_after(jiffies, entry->first_rx_time + 2 * HZ)) {
+				dev_kfree_skb(entry->skb);
+				entry->skb = NULL;
+				i = j;
+			}
+			}
+		}
+
+		if (i == -1)
+			goto drop;	/* could not free a stale entry */
+
+		entry = &data->frag_cache[i];
+		entry->skb = dev_alloc_skb(2346);
+		if (!entry->skb)
+			goto drop;
+
+		entry->skb->dev = data->dev;
+		entry->first_rx_time = jiffies;
+		memcpy(skb_put(entry->skb, skb->len), skb->data, skb->len);
+		goto drop;
+	} else if (frag) {
+		if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+			  (WLAN_FC_TODS | WLAN_FC_FROMDS))
+			hlen = 30;
+		else
+			hlen = 24;
+
+		for (i = 0; i < IEEE80211_FRAG_CACHE_SIZE; i += 1) {
+		entry = &data->frag_cache[i];
+		if (!entry->skb)
+			continue;
+		hdr2 = (struct ieee80211_hdr *) entry->skb->data;
+		if (WLAN_GET_SEQ_SEQ(le16_to_cpu(hdr2->seq_ctrl)) != seq 
+		    || memcmp(hdr2->addr1, hdr->addr1, ETH_ALEN)
+		    || memcmp(hdr2->addr2, hdr->addr2, ETH_ALEN))
+			continue;
+
+		skb_pull(skb, hlen);
+		if (entry->skb->tail + skb->len > entry->skb->end) {
+			dev_kfree_skb(entry->skb);
+			entry->skb = NULL;
+			goto drop;
+		}
+
+		memcpy(skb_put(entry->skb, skb->len), skb->data,
+                               skb->len);
+
+		if (fc & WLAN_FC_MOREFRAG)
+			goto drop;
+		else {
+			dev_kfree_skb(skb);
+			skb = entry->skb;
+			entry->skb = NULL;
+			return skb;
+		}
+
+		}
+
+		goto drop;
+	}
+
+	return skb;
+drop:
+	dev_kfree_skb(skb);
+	return NULL;
+}
+
+int ieee80211_filter_duplicates(struct ieee80211_data *data,
+				struct sk_buff *skb, u16 fc)
+{
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_bss *bss;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+
+	spin_lock_bh(&data->lock);
+	bss = ieee80211_get_bss(data, hdr->addr2);
+
+	if (bss) {
+		if (fc & WLAN_FC_RETRY && bss->last_seq == hdr->seq_ctrl) {
+			spin_unlock_bh(&data->lock);
+			return 1;
+		}
+		bss->last_seq = hdr->seq_ctrl;
+	}
+	spin_unlock_bh(&data->lock);
+
+	return 0;
+}
+
+/* returns a rate appropriate for the destination, if it can be determined */
+u8 ieee80211_get_rate(struct ieee80211_data *data, u8 *addr)
+{
+	struct ieee80211_bss *bss;
+	u8 rate;
+
+	bss = ieee80211_get_bss(data, addr);
+
+	if (bss)
+		rate = bss->rate;
+	else
+		rate = data->rate;
+
+	return rate;
+}
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/ieee80211.h no-sources-new/drivers/net/wireless/adm8211/ieee80211.h
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/ieee80211.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/ieee80211.h	2005-05-05 21:46:25.808289384 +0000
@@ -0,0 +1,384 @@
+#ifndef IEEE80211_H
+#define IEEE80211_H
+
+#define BIT(x) (1 << (x))
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FCS_LEN 4
+#define WLAN_ADDR3_HDR_LEN 24
+
+#define WLAN_FC_PVER (BIT(1) | BIT(0))
+#define WLAN_FC_TODS BIT(8)
+#define WLAN_FC_FROMDS BIT(9)
+#define WLAN_FC_MOREFRAG BIT(10)
+#define WLAN_FC_RETRY BIT(11)
+#define WLAN_FC_PWRMGT BIT(12)
+#define WLAN_FC_MOREDATA BIT(13)
+#define WLAN_FC_ISWEP BIT(14)
+#define WLAN_FC_ORDER BIT(15)
+
+#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2)
+#define WLAN_FC_GET_STYPE(fc) \
+	(((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4)
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
+#define WLAN_GET_SEQ_SEQ(seq) \
+	(((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
+
+#define IEEE80211_FC(type, stype) cpu_to_le16((type << 2) | (stype << 4))
+
+#define WLAN_FC_TYPE_MGMT 0
+#define WLAN_FC_TYPE_CTRL 1
+#define WLAN_FC_TYPE_DATA 2
+
+/* management */
+#define WLAN_FC_STYPE_ASSOC_REQ 0
+#define WLAN_FC_STYPE_ASSOC_RESP 1
+#define WLAN_FC_STYPE_REASSOC_REQ 2
+#define WLAN_FC_STYPE_REASSOC_RESP 3
+#define WLAN_FC_STYPE_PROBE_REQ 4
+#define WLAN_FC_STYPE_PROBE_RESP 5
+#define WLAN_FC_STYPE_BEACON 8
+#define WLAN_FC_STYPE_ATIM 9
+#define WLAN_FC_STYPE_DISASSOC 10
+#define WLAN_FC_STYPE_AUTH 11
+#define WLAN_FC_STYPE_DEAUTH 12
+
+/* control */
+#define WLAN_FC_STYPE_PSPOLL 10
+#define WLAN_FC_STYPE_RTS 11
+#define WLAN_FC_STYPE_CTS 12
+#define WLAN_FC_STYPE_ACK 13
+#define WLAN_FC_STYPE_CFEND 14
+#define WLAN_FC_STYPE_CFENDACK 15
+
+/* data */
+#define WLAN_FC_STYPE_DATA 0
+#define WLAN_FC_STYPE_DATA_CFACK 1
+#define WLAN_FC_STYPE_DATA_CFPOLL 2
+#define WLAN_FC_STYPE_DATA_CFACKPOLL 3
+#define WLAN_FC_STYPE_NULLFUNC 4
+#define WLAN_FC_STYPE_CFACK 5
+#define WLAN_FC_STYPE_CFPOLL 6
+#define WLAN_FC_STYPE_CFACKPOLL 7
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS BIT(0)
+#define WLAN_CAPABILITY_IBSS BIT(1)
+#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
+#define WLAN_CAPABILITY_PRIVACY BIT(4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
+
+/* Status codes */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+
+/* Reason codes */
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_CHALLENGE 16
+
+#define IEEE80211_MAX_SSID_LEN 32
+
+#define WEP_KEY_MAX_LEN 13
+#define WEP_KEY_MAX_KEYS 4
+
+#define FCS_LEN 4
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+struct ieee80211_hdr {
+	__le16 frame_control;
+	__le16 duration_id;
+	u8 addr1[6];
+	u8 addr2[6];
+	u8 addr3[6];
+	__le16 seq_ctrl;
+	u8 addr4[6];
+} __attribute__ ((packed));
+
+union ieee80211_mgmt_payload {
+	struct {
+		__le16 auth_alg;
+		__le16 auth_transaction;
+		__le16 status_code;
+		/* possibly followed by Challenge text */
+		u8 variable[0];
+	} __attribute__ ((packed)) auth;
+	struct {
+		__le16 reason_code;
+	} __attribute__ ((packed)) deauth;
+	struct {
+		__le16 capab_info;
+		__le16 listen_interval;
+		/* followed by SSID and Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) assoc_req;
+	struct {
+		__le16 capab_info;
+		__le16 status_code;
+		__le16 aid;
+		/* followed by Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) assoc_resp, reassoc_resp;
+	struct {
+		__le16 capab_info;
+		__le16 listen_interval;
+		u8 current_ap[ETH_ALEN];
+		/* followed by SSID and Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) reassoc_req;
+	struct {
+		__le16 reason_code;
+	} __attribute__ ((packed)) disassoc;
+	struct {
+		__le64 timestamp;
+		__le16 beacon_int;
+		__le16 capab_info;
+		/* followed by some of SSID, Supported rates,
+		 * FH Params, DS Params, CF Params, IBSS Params, TIM */
+		u8 variable[0];
+	} __attribute__ ((packed)) beacon;
+	struct {
+		/* only variable items: SSID, Supported rates */
+		u8 variable[0];
+	} __attribute__ ((packed)) probe_req;
+	struct {
+		__le64 timestamp;
+		__le16 beacon_int;
+		__le16 capab_info;
+		/* followed by some of SSID, Supported rates,
+		 * FH Params, DS Params, CF Params, IBSS Params */
+		u8 variable[0];
+	} __attribute__ ((packed)) probe_resp;
+};
+
+struct ieee80211_mgmt {
+	__le16 frame_control;
+	__le16 duration;
+	u8 da[6];
+	u8 sa[6];
+	u8 bssid[6];
+	__le16 seq_ctrl;
+	union ieee80211_mgmt_payload u;
+} __attribute__ ((packed));
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+	u8 *ssid;
+	u8 ssid_len;
+	u8 *supp_rates;
+	u8 supp_rates_len;
+	u8 *fh_params;
+	u8 fh_params_len;
+	u8 *ds_params;
+	u8 ds_params_len;
+	u8 *cf_params;
+	u8 cf_params_len;
+	u8 *tim;
+	u8 tim_len;
+	u8 *ibss_params;
+	u8 ibss_params_len;
+	u8 *challenge;
+	u8 challenge_len;
+};
+
+typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+
+ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
+				struct ieee802_11_elems *elems);
+
+
+struct ieee80211_rx_status {
+	int rssi;
+	int rate; /* in 100 kbps */
+};
+
+
+struct ieee80211_bss {
+	u8 bssid[ETH_ALEN];
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	size_t ssid_len;
+	int hidden_ssid;
+
+	int channel;
+	u16 interval;
+	u64 timestamp;
+	u16 capability;
+#define IEEE80211_MAX_SUPP_RATES 8
+	u8 supp_rates[IEEE80211_MAX_SUPP_RATES + 1];
+	u8 rate;
+
+	unsigned long last_rx;
+	int last_rssi;
+	u8 last_rate;
+	unsigned long num_beacon, num_proberesp;
+	int num_auth_fail, num_assoc_fail;
+	int auth_state;
+	u8 *challenge;
+	u16 auth_alg;
+
+	__le16 last_seq; /* last received seq number field (in 802.11 byte
+		       * order) from the AP */
+
+	struct ieee80211_bss *next;
+};
+
+struct ieee80211_chan_range {
+	u8 min;
+	u8 max;
+};
+
+struct ieee80211_wep_data {
+	u32 iv;
+	unsigned char len;
+	u8 key[WEP_KEY_MAX_LEN];
+};
+
+#define IEEE80211_FRAG_CACHE_SIZE 4
+struct ieee80211_frag_entry {
+	unsigned long first_rx_time;
+	struct sk_buff *skb;
+};
+
+struct ieee80211_data {
+	struct net_device *dev;
+	int (*set_channel)(struct net_device *dev, int channel);
+	int (*set_bssid)(struct net_device *dev, u8 *bssid);
+	int (*set_ssid)(struct net_device *dev, u8 *ssid, size_t ssid_len);
+	int (*set_rate)(struct net_device *dev);
+	int (*tx)(struct net_device *dev, struct sk_buff *skb,
+		  __le16 fc, int short_preamble, u8 *dst);
+	void (*link_change)(struct net_device *dev, int link);
+	void (*set_interval)(struct net_device *dev);
+	void (*set_tbtt)(struct net_device *dev, u16 tbtt);
+	u64 (*get_tsft)(struct net_device *dev);
+
+	enum {
+		IEEE80211_SCAN, IEEE80211_AUTHENTICATE, IEEE80211_ASSOCIATE,
+		IEEE80211_ASSOCIATED, IEEE80211_STOP
+	} state;
+	struct timer_list timer;
+
+	enum {
+		IEEE80211_MANAGED, IEEE80211_ADHOC, IEEE80211_MONITOR,
+		IEEE80211_AP, IEEE80211_WDS
+	} mode;
+
+#define WEP_ENABLED (1<<0)
+#define WEP_RESTRICTED (1<<1)
+#define AUTO_RATE (1<<2)
+#define ANY_SSID (1<<3)
+#define HIDDEN_SSID (1<<4)
+#define PREF_BSSID_SET (1<<5)
+#define PREV_BSSID_SET (1<<6)
+#define ASSOCIATED (1<<8)
+#define LINK_ON (1<<9)
+
+	u32 flags;
+	
+	char nick[IW_ESSID_MAX_SIZE];
+
+	unsigned int auth_algorithm, default_key;
+	struct ieee80211_wep_data wep_data[WEP_KEY_MAX_KEYS];
+	struct crypto_tfm *tfm;
+
+	struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_SIZE];
+
+	u8 bssid[ETH_ALEN];
+	u8 rate;
+	u8 pref_channel;
+	u8 channel;
+	struct ieee80211_chan_range chan_range;
+	int num_supp_rates;
+	u8 *supp_rates;
+
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	size_t ssid_len;
+	u8 pref_bssid[ETH_ALEN];
+
+	int auth_tries;
+	int assoc_tries;
+
+	int aid;
+
+	u8 prev_bssid[ETH_ALEN];
+
+	u64 timestamp;
+	u16 capab, beacon_interval, listen_interval;
+
+	struct ieee80211_bss *bss;
+	spinlock_t lock;
+};
+
+static const u8 IEEE80211_B_RATES[] = { 0x02, 0x04, 0x0b, 0x16, 0x00 };
+
+struct ieee80211_bss * ieee80211_get_bss(struct ieee80211_data *data, u8 *addr);
+
+int ieee80211_init(struct ieee80211_data *data);
+void ieee80211_deinit(struct ieee80211_data *data);
+void ieee80211_rx_mgmt(struct ieee80211_data *data, struct sk_buff *skb,
+		       struct ieee80211_rx_status *rx_status);
+void ieee80211_start(struct ieee80211_data *data);
+void ieee80211_start_scan(struct ieee80211_data *data);
+void ieee80211_stop(struct ieee80211_data *data);
+
+int ieee80211_data_tx(struct ieee80211_data *data,
+		      struct sk_buff *skb);
+
+struct sk_buff * ieee80211_wep_decode(struct ieee80211_data *data,
+				      struct sk_buff *skb);
+
+struct sk_buff * ieee80211_data_encaps(struct ieee80211_data *data,
+				       struct sk_buff *skb);
+struct sk_buff * ieee80211_data_decaps(struct ieee80211_data *data,
+				       struct sk_buff *skb);
+
+struct sk_buff * ieee80211_reassemble_frag (struct ieee80211_data *data,
+					    struct sk_buff *skb);
+int ieee80211_filter_duplicates(struct ieee80211_data *data,
+				struct sk_buff *skb, u16 fc);
+u8 ieee80211_get_rate(struct ieee80211_data *data, u8 *addr);
+
+#include "wep.h"
+
+#endif /* IEEE80211_H */
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/kernel.patch no-sources-new/drivers/net/wireless/adm8211/kernel.patch
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/kernel.patch	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/kernel.patch	2005-05-05 21:46:25.807289536 +0000
@@ -0,0 +1,24 @@
+diff -Nru linux-2.6.8.1/drivers/net/wireless/Kconfig linux-2.6.8.1-adm8211/drivers/net/wireless/Kconfig
+--- linux-2.6.8.1/drivers/net/wireless/Kconfig	2004-08-14 06:54:52.000000000 -0400
++++ linux-2.6.8.1-adm8211/drivers/net/wireless/Kconfig	2004-08-19 20:07:50.000000000 -0400
+@@ -307,6 +307,8 @@
+ 	 It has basic support for Linux wireless extensions and initial
+ 	 micro support for ethtool.
+ 
++source "drivers/net/wireless/adm8211/Kconfig"
++
+ comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support"
+ 	depends on NET_RADIO && PCI
+ config PRISM54
+diff -Nru linux-2.6.8.1/drivers/net/wireless/Makefile linux-2.6.8.1-adm8211/drivers/net/wireless/Makefile
+--- linux-2.6.8.1/drivers/net/wireless/Makefile	2004-08-14 06:56:25.000000000 -0400
++++ linux-2.6.8.1-adm8211/drivers/net/wireless/Makefile	2004-08-19 20:04:20.000000000 -0400
+@@ -28,6 +28,8 @@
+ 
+ obj-$(CONFIG_PRISM54)		+= prism54/
+ 
++obj-$(CONFIG_ADM8211)		+= adm8211/
++
+ # 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.12-rc3-no1/drivers/net/wireless/adm8211/rdate.h no-sources-new/drivers/net/wireless/adm8211/rdate.h
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/rdate.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/rdate.h	2005-05-05 21:46:25.802290296 +0000
@@ -0,0 +1 @@
+#define RELEASE_DATE "20050323"
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/wep.c no-sources-new/drivers/net/wireless/adm8211/wep.c
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/wep.c	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/wep.c	2005-05-05 21:46:25.803290144 +0000
@@ -0,0 +1,160 @@
+/*
+ * Host AP crypt: host-based WEP encryption implementation for Host AP driver
+ * (adapted for use within the adm8211 driver)
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/crc32.h>
+#include <asm/string.h>
+#include <asm/scatterlist.h>
+
+#ifndef CONFIG_CRYPTO
+#error CONFIG_CRYPTO required to build
+#endif
+ 
+#include <linux/crypto.h>
+#include "ieee80211.h"
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+int wep_encrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data)
+{
+	struct ieee80211_wep_data *wep = &data->wep_data[data->default_key];
+	struct scatterlist sg;
+	u32 crc, klen, len;
+	u8 key[WEP_KEY_MAX_LEN + 3];
+	u8 *pos, *icv;
+
+	if (skb->len < hdr_len)
+		return -1;
+
+	if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4) {
+		if (pskb_expand_head(skb, (skb_headroom(skb) < 4 ? 4 : 0),
+		    (skb_tailroom(skb) < 4 ? 4 : 0), GFP_ATOMIC))
+			return -1;
+	}
+
+	len = skb->len - hdr_len;
+	pos = skb_push(skb, 4);
+	memmove(pos, pos + 4, hdr_len);
+	pos += hdr_len;
+
+	klen = 3 + wep->len;
+
+	wep->iv++;
+
+	/* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+	 * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+	 * can be used to speedup attacks, so avoid using them. */
+	if ((wep->iv & 0xff00) == 0xff00) {
+		u8 B = (wep->iv >> 16) & 0xff;
+		if (B >= 3 && B < klen)
+			wep->iv += 0x0100;
+	}
+
+	/* Prepend 24-bit IV to RC4 key and TX frame */
+	*pos++ = key[0] = (wep->iv >> 16) & 0xff;
+	*pos++ = key[1] = (wep->iv >> 8) & 0xff;
+	*pos++ = key[2] = wep->iv & 0xff;
+	*pos++ = data->default_key << 6;
+
+	/* Copy rest of the WEP key (the secret part) */
+	memcpy(key + 3, wep->key, wep->len);
+
+	/* Append little-endian CRC32 and encrypt it to produce ICV */
+	crc = ~crc32_le(~0, pos, len);
+	icv = skb_put(skb, 4);
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+
+	crypto_cipher_setkey(data->tfm, key, klen);
+	sg.page = virt_to_page(pos);
+	sg.offset = offset_in_page(pos);
+	sg.length = len + 4;
+	crypto_cipher_encrypt(data->tfm, &sg, &sg, len + 4);
+	
+	return 0;
+}
+
+
+/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
+ * the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+int wep_decrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data)
+{
+	struct ieee80211_wep_data *wep;
+	struct scatterlist sg;
+	u32 crc, klen, plen;
+	u8 key[WEP_KEY_MAX_LEN + 3];
+	u8 keyidx, *pos, icv[4];
+
+	if (skb->len < hdr_len + 8)
+		return -1;
+
+	pos = skb->data + hdr_len;
+	key[0] = *pos++;
+	key[1] = *pos++;
+	key[2] = *pos++;
+	keyidx = *pos++ >> 6;
+	keyidx %= 4;
+
+	wep = &data->wep_data[keyidx];
+	klen = 3 + wep->len;
+
+	/* Copy rest of the WEP key (the secret part) */
+	memcpy(key + 3, wep->key, wep->len);
+
+	/* Apply RC4 to data and compute CRC32 over decrypted data */
+	plen = skb->len - hdr_len - 8;
+
+	crypto_cipher_setkey(data->tfm, key, klen);
+	sg.page = virt_to_page(pos);
+	sg.offset = offset_in_page(pos);
+	sg.length = plen + 4;
+	crypto_cipher_decrypt(data->tfm, &sg, &sg, plen + 4);
+
+	crc = ~crc32_le(~0, pos, plen);
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+	if (memcmp(icv, pos + plen, 4) != 0) {
+		/* ICV mismatch - drop frame */
+		return -2;
+	}
+
+	/* Remove IV and ICV */
+	memmove(skb->data + 4, skb->data, hdr_len);
+	skb_pull(skb, 4);
+	skb_trim(skb, skb->len - 4);
+
+	return 0;
+}
+
diff -Naur linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/wep.h no-sources-new/drivers/net/wireless/adm8211/wep.h
--- linux-2.6.12-rc3-no1/drivers/net/wireless/adm8211/wep.h	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/drivers/net/wireless/adm8211/wep.h	2005-05-05 21:46:25.803290144 +0000
@@ -0,0 +1,9 @@
+#ifndef WEP_H
+#define WEP_H
+
+int wep_encrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data);
+int wep_decrypt(struct sk_buff *skb, int hdr_len,
+		struct ieee80211_data *data);
+
+#endif
diff -Naur linux-2.6.12-rc3-no1/kernel.patch no-sources-new/kernel.patch
--- linux-2.6.12-rc3-no1/kernel.patch	1970-01-01 00:00:00.000000000 +0000
+++ no-sources-new/kernel.patch	2005-05-05 22:03:47.063994456 +0000
@@ -0,0 +1,24 @@
+diff -Nru linux-2.6.8.1/drivers/net/wireless/Kconfig linux-2.6.8.1-adm8211/drivers/net/wireless/Kconfig
+--- linux-2.6.8.1/drivers/net/wireless/Kconfig	2004-08-14 06:54:52.000000000 -0400
++++ linux-2.6.8.1-adm8211/drivers/net/wireless/Kconfig	2004-08-19 20:07:50.000000000 -0400
+@@ -307,6 +307,8 @@
+ 	 It has basic support for Linux wireless extensions and initial
+ 	 micro support for ethtool.
+ 
++source "drivers/net/wireless/adm8211/Kconfig"
++
+ comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support"
+ 	depends on NET_RADIO && PCI
+ config PRISM54
+diff -Nru linux-2.6.8.1/drivers/net/wireless/Makefile linux-2.6.8.1-adm8211/drivers/net/wireless/Makefile
+--- linux-2.6.8.1/drivers/net/wireless/Makefile	2004-08-14 06:56:25.000000000 -0400
++++ linux-2.6.8.1-adm8211/drivers/net/wireless/Makefile	2004-08-19 20:04:20.000000000 -0400
+@@ -28,6 +28,8 @@
+ 
+ obj-$(CONFIG_PRISM54)		+= prism54/
+ 
++obj-$(CONFIG_ADM8211)		+= adm8211/
++
+ # 16-bit wireless PCMCIA client drivers
+ obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
+ obj-$(CONFIG_PCMCIA_WL3501)	+= wl3501_cs.o
