GIT 875761f279482222ca385443102444fbe9fb0733 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git#mm-master

commit 934d49b0ffd1b3f9255ec08b169bebbe584369b9
Author: Daniel Drake <dsd@gentoo.org>
Date:   Sun Oct 7 16:23:33 2007 +0100

    [PATCH] zd1211rw-mac80211: Add ID for TalkTalk SNU5630NS/05
    
    Tested by Su-Jong You
    zd1211b chip 0471:1237 v4810 high 00-12-bf AL2230_RF pa0 g--N
    
    Signed-off-by: Daniel Drake <dsd@gentoo.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 3b1ed734b2ab3085caa47a7be67c934a9f1be293
Author: Daniel Drake <dsd@gentoo.org>
Date:   Thu Oct 4 12:35:09 2007 +0100

    [PATCH] zd1211rw-mac80211: More complete configure_filter implementation
    
    This patch extends the configure_filter implementation and fixes the
    structures around it.
    
    The mac80211 configure_filter work also clarified handling of our own mac
    address. We now program it in add_interface and remove it in
    remove_interface. We also no longer need to keep it in the zd_mac structure.
    
    With the recent mac80211 changes, has_monitor_interfaces() always returns
    FALSE because the driver is not directly told about monitor interfaces.
    I've removed this function and corrected the logic elsewhere.
    
    The sniffer is no longer enabled in monitor mode. This is somewhat
    guesswork, but I think by default we are given frames that have a bad FCS,
    and enabling the sniffer also passes us frames that have a bad PLCP
    checksum. However, when the sniffer is enabled, the device stops telling us
    which frames failed FCS checks, and also does not indicate which frames
    failed PLCP checks. So, it's best that we disable this functionality for now
    and only pass good and known-failed-FCS frames to the stack.
    
    Signed-off-by: Daniel Drake <dsd@gentoo.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 7ed6c5d8c89b03c2f50f6f49dd53ce92eb4aa290
Author: John W. Linville <linville@tuxdriver.com>
Date:   Tue Oct 2 19:56:29 2007 -0400

    [PATCH] ath5k: remove reference to ALG_NONE
    
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit ec9c21c66924ecc8bd15468ff987a404cc5073f2
Author: Ulrich Kunitz <kune@deine-taler.de>
Date:   Tue Oct 2 18:18:15 2007 +0100

    [PATCH] zd1211rw-mac80211: return -EOPNOTSUPP from add_interface
    
    Match behaviour of other drivers by returning -EOPNOTSUPP rather than -1.
    
    Signed-off-by: Ulrich Kunitz <kune@deine-taler.de>
    Signed-off-by: Daniel Drake <dsd@gentoo.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 9bb9c0a5d7b010532ce3263ca05bebbf3c33ef26
Author: Ulrich Kunitz <kune@deine-taler.de>
Date:   Tue Oct 2 18:18:04 2007 +0100

    [PATCH] zd1211rw-mac80211: Add lock around struct zd_mac access
    
    Calls to zd_op_config_interface() are not synchronized, added
    therefore a lock around the access to the configuration variable.
    
    Signed-off-by: Ulrich Kunitz <kune@deine-taler.de>
    Signed-off-by: Daniel Drake <dsd@gentoo.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 00c5068484c59d5e10d8f13c4224f919f48166fb
Author: Ulrich Kunitz <kune@deine-taler.de>
Date:   Tue Oct 2 18:17:38 2007 +0100

    [PATCH] zd1211rw-mac80211: Removed zd_util.c and zd_util.h
    
    The kernel now provides a generic hexdump implementation should we need
    it again, so we can remove it from zd1211rw. After removing that, only
    one single-user function is left in zd_util. Move that to zd_mac and
    remove zd_util.
    
    Signed-off-by: Ulrich Kunitz <kune@deine-taler.de>
    Signed-off-by: Daniel Drake <dsd@gentoo.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 707ad8b19ef0bbea49898d537041423b0b5c7f26
Author: Michael Buesch <mb@bu3sch.de>
Date:   Sat Sep 29 12:23:20 2007 +0200

    [PATCH] zd1211rw-mac80211: Make the LEDs warning debug-only
    
    This warning triggers now and then with an -ETIMEDOUT error.
    Though the LEDs work fine.
    Make this warning debug-only, as nobody is really interested
    in a LED failure that happens only now and then.
    If the LEDs don't work, one can easily enable debugging to see
    what's going on.
    This reduces dmesg spamming.
    
    Signed-off-by: Michael Buesch <mb@bu3sch.de>
    Acked-by: Daniel Drake <dsd@gentoo.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit ec9e5496191cf021c3b756f4aa924534c59fd55b
Author: Johannes Berg <johannes@sipsolutions.net>
Date:   Fri Sep 28 14:01:25 2007 +0200

    [PATCH] zd1211rw-mac80211: add "invalid" interface type
    
    Since I cannot convince the lazy driver authors (hello Michael)
    to stop (ab)using the MGMT interface type internally in their
    drivers, this patch introduces a new _INVALID type especially
    for their use and changes all affected drivers to use it.
    
    Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 75724a86bf11a5168a563c3e36a139befde7ac80
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:41:25 2007 -0400

    [PATCH] at76_usb: Add ID for at76c503a based CNETUSB611
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit f166694e49d01173e149ddd1ddbe63c596784229
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:41:18 2007 -0400

    [PATCH] at76_usb: Bump version to 0.17
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit a9451cf56b8d4ec7e173e065a7e8ca400b1b33a9
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:41:12 2007 -0400

    [PATCH] at76_usb: Use ETH_P_802_2 in monitor mode
    
    It's used by most other wireless drivers, including mac80211.
    ETH_P_80211_RAW is obsolete.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 50c5e2f51d6790d2e79b67c563b630bb06e9e924
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:41:06 2007 -0400

    [PATCH] at76_usb: Be more verbose on startup
    
    Promote some debug messages from at76_dbg() to unconditional KERN_DEBUG.
    Use dev_printk() before the device is registered, as "wlan%d" doesn't
    identify the device uniquely.
    
    Print firmware name and version when it's loaded from file.  Print
    firmware version reported by the card.  Print USB address and network
    device name on the same line to help identifying the device.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 615b27ceebf4b24de46dc4de349be89d637b48c1
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:59 2007 -0400

    [PATCH] at76_usb: Use dev_printk() where possible
    
    dev_printk() prints the USB device name in addition to the driver name,
    which makes it easier to understand the diagnostics in case of multiple
    devices.
    
    Use &interface->dev, as it's more specific and includes the driver name,
    but use &udev->dev if interface is not available.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 6b48e08e3108de3f67efffc901a4448da7927e85
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:53 2007 -0400

    [PATCH] at76_usb: Remove incorrect firmware version check
    
    The major version of the firmware reported by the device can mismatch
    the version of the originally loaded firmware, as it is the case for
    Belkin F5D6050.
    
    Remove this check and assume the firmware to be working as long that the
    external firmware download didn't fail and the firmware version could be
    read at all.
    
    Reported by Corey Pappas <pappascd@gmail.com>
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit f027d04661e6458720444f5d9021d6ff8889a6f1
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:47 2007 -0400

    [PATCH] at76_usb: Revert to network device names starting with "wlan"
    
    The change to the default "eth" in 0.16 was unnecessary and broke some
    setups.  Reported by Mark Sansome <msansome@troodos.demon.co.uk>
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit e6ffb45326746baba9ceb80d9b8d208660d72612
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:40 2007 -0400

    [PATCH] at76_usb: Add ID for Corega Wireless LAN USB-11 mini and mini2
    
    Original patch by Weihua Yao <weihuayao@gmail.com>
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit b39a9a54ade91802b27691293d1d3725e4361e52
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:34 2007 -0400

    [PATCH] at76_usb: Avoid dealing with milliseconds when possible, use jiffies
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 7ef745efc7ea2a20d23a86dc8d13f213f749d576
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:27 2007 -0400

    [PATCH] at76_usb: Move some parts from at76_usb.h to at76_usb.c
    
    Don't include anything from at76_usb.h.  Keep DRIVER_NAME and
    DRIVER_VERSION together.  Define at76_dbg() after DRIVER_NAME.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit d5e2f2fe37001bfd82abc8e93e34e61e21b68b92
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:21 2007 -0400

    [PATCH] at76_usb: Remove unneeded memset() calls
    
    Use kzalloc for bss table entries.  priv is already zeroized, no need to
    do it again.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit a3a77550f4438cf0cab43609d7de28305065c4af
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:15 2007 -0400

    [PATCH] at76_usb: Clean reserved tx area for mgmt frames like it's done for data
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit cd01ffb2e2de0a94f699b48b4e5a56f155674f1d
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:08 2007 -0400

    [PATCH] at76_usb: Make it clear that management frames are sent at 1Mbps
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 0e26a4292c1dc7adc45e080f30b126a9a07dbc19
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:40:02 2007 -0400

    [PATCH] at76_usb: Eliminate variables used only in at76_dbg()
    
    Make it possible to disable at76_dbg() at the compile time without
    introducing any warnings.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 15a18c861b655d67711cd4974e2a551b2c2e5aea
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:55 2007 -0400

    [PATCH] at76_usb: Use string precision to avoid line termination
    
    In particular, don't extend ESSID to 33 bytes.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 06e260461bb3c2c860939809e371c561d36fd06a
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:49 2007 -0400

    [PATCH] at76_usb: Stop worrying about line termination in ethtool info
    
    The driver's responsibility is not to overwrite the buffer and not to
    leak kernel data.  Line termination is the userspace responsibility.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit fe1768d2fe934a7287a8e668fcec37edf3c8e895
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:42 2007 -0400

    [PATCH] at76_usb: Rename tlv to ie for readability
    
    "ie" is the standard abbreviation for "information element".
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 2e57df9c2857d24efb57638235a629c01e025e51
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:36 2007 -0400

    [PATCH] at76_usb: Remove commented out code for multicast support
    
    If anyone cares to implement it, it should be started anew.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 90aa6ea99eedeb725700e58e7947db362185ae81
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:30 2007 -0400

    [PATCH] at76_usb: Eliminate err(), it's USB specific
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 4a4f2a074c2529222b63f61558dc541e0d853bfa
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:23 2007 -0400

    [PATCH] at76_usb: Integrate at76_set_monitor_mode() into at76_dwork_restart()
    
    Reorder some statements in at76_dwork_restart()
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 62cd4759798e770a96f9bdb05298bf0e5546da1e
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:17 2007 -0400

    [PATCH] at76_usb: Fix turning radio off
    
    The Atmel's driver uses command 7 to turn radio off.  Also,
    CMD_GETOPMODE is bogus, remove it.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit e8b30a14ccbf32f08b3089e3cf3851864467b6c5
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:11 2007 -0400

    [PATCH] at76_usb: No need to reserve 72 bytes in set_mib_buffer - we only need 6
    
    Use union for data in set_mib_buffer to guarantee minimally needed
    buffer length.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit b4a183b0cf1dcfd9e9f0ace0144af7f03ee8ef24
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:39:04 2007 -0400

    [PATCH] at76_usb: Don't clean priv->mib_buf, just clean data explicitly if needed
    
    priv->mib_buf.reserved is 0 when priv is allocated, and stays 0.  When
    setting ibss_change, set data[0] to 0.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 755ff28640b81385200da13661980a99b3100e8f
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:38:58 2007 -0400

    [PATCH] at76_usb: Avoid dubious use of '\0'
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 8470e259e044849fb208ad9deab0a7ecf8093776
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:38:51 2007 -0400

    [PATCH] at76_usb: Fix some device names in the device table
    
    Add vendor where it's missing.  Use spelling preferred by the vendors.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit eb4a662a848a1d91b709fc14a1af6a4069bc8a5a
Author: Pavel Roskin <proski@gnu.org>
Date:   Sun Sep 30 14:38:45 2007 -0400

    [PATCH] at76_usb: Use USB_DEVICE_DATA to improve readability of the device table
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit a57ed07266a415101e043848689bf91b76a76b55
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Fri Sep 28 13:01:09 2007 -0400

    [PATCH] zd1211rw-mac80211: add configure_filter() support
    
    Resending.. it seems my mail setup isn't quite right yet..
    
    This patch:
    
    * Moves driver to new start() / stop()
    * Removes the old zd_op_set_multicast_list()
    * Adds the new configure_filter() support
    * Fixes compile errors as it depends on the removed DECLARE_MAC_BUF,
      and print_mac()
    
    This has been tested, and applies to the 'everything' branch of wireless-2.6.
    
    Cc: Daniel Drake <dsd@gentoo.org>
    Cc: Ulrich Kunitz <kune@deine-taler.de>
    Cc: Johannes Berg <johannes@sipsolutions.net>
    Cc: Michael Wu <flamingice@sourmilk.net>
    
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit ed03777f2d6de4e9e10befe71d25efaa0bfb8804
Author: David S. Miller <davem@sunset.davemloft.net>
Date:   Wed Sep 19 14:33:58 2007 -0700

    [PATCH] zd1211rw-mac80211: Fix build with debugging enabled.
    
    Some missed MAC_FMT conversions.
    
    Signed-off-by: David S. Miller <davem@davemloft.net>

commit a76109dfd9ac3b617581af9cf01161f3408ac0c6
Author: Michael Buesch <mb@bu3sch.de>
Date:   Wed Sep 19 13:10:35 2007 -0700

    [PATCH] zd1211rw-mac80211: Fix TX status reports.
    
    Automatic rate scaling does not work on the zd-mac80211 driver.  The
    driver does not properly report succeed and failed frames to mac80211.
    
    We need to indicate failed frames with the excessive_retries field
    (Should we also fake report some high retry count here?)  Otherwise
    the rc algo is not able to scale down.
    
    Remove the conditional in REQ_TX_STATUS, as mac80211 handles that
    internally.
    
    Signed-off-by: Michael Buesch <mb@bu3sch.de>
    Signed-off-by: David S. Miller <davem@davemloft.net>

commit 29d6a10e1a0cfa7c80dcfd89eb179c78725281f5
Author: Daniel Drake <dsd@gentoo.org>
Date:   Wed Sep 19 10:32:24 2007 -0400

    [PATCH] zd1211rw-mac80211: add mac80211-based driver for ZD1211 hardware
    
    Port to mac80211 infrastructure done by Michael Wu.
    
    Signed-off-by: Michael Wu <flamingice@sourmilk.net>
    Signed-off-by: Ulrich Kunitz <kune@deine-taler.de>
    Signed-off-by: Daniel Drake <dsd@gentoo.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit c6e51e580e58cfbb4d97cc4f4776cf879bd6d187
Author: Michael Buesch <mb@bu3sch.de>
Date:   Tue Sep 18 15:17:13 2007 -0400

    [PATCH] usb: ssb-hosted OHCI driver
    
    Signed-off-by: Michael Buesch <mb@bu3sch.de>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 9deb02cc73be77080b48d8d0521c198706c524a3
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Fri Sep 28 11:26:16 2007 -0400

    [PATCH] ath5k: Fix a few typos in copyright
    
    This patch fixes a few typos overseen by the introduction of
    two new files, intvals.c and phy.c. It also adds the missing
    warranty on base.c and removes the the last CVS tag from base.h
    
    Changes to base.[ch]
    Changes-licensed-under: 3-clause-BSD
    
    Changes to phy.c, initvals.c
    Changes-licensed-under: ISC
    
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 5d0c48aeb45018dd2de2112c666dad1019070f36
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Thu Sep 27 21:04:19 2007 -0400

    [PATCH] ath5k: add new configure_filter, compile fix on wireless-2.6
    
    This patch:
    
    * removes the old ath_calcrxfilter() and ath_set_multicast_list()
    * adds new required required configure_filter() ops, this one
      just stores the filter value in cache for later use.
    * introduces a cached sc->filter_flags for hw filter flags
    * moves the driver to mac80211's new required start()/stop()
    * initializes at add_interface() sc->bintval to a common value,
      this will later be updated as per mac80211's preference.
    * Fix compile bug on ath_set_key() (adds enum for set_key_cmd)
    
    We'll later port some driver-specific filter stuff onto mac80211.
    
    This has been tested. This patch applies to the wireless-2.6 everything branch,
    after the new ath5k directory move and file renames.
    
    Changes-licensed-under: 3-clause-BSD
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 08877c0cdbbec4431cbd1d517b224ab010c43c9b
Author: Bradley M. Kuhn <bkuhn@softwarefreedom.org>
Date:   Thu Sep 27 18:58:24 2007 -0400

    [PATCH] ath5k: final correction of copyright notices
    
    The patch below is for application to the wireless-2.6 ath5k branch, and
    takes into account the latest driver directory move and file renames.  I
    believe this patch handles all copyright notice issues that SFLC is
    currently aware of.  Based on our extensive research (which is summarized
    in http://www.softwarefreedom.org/resources/2007/ath5k-code-analysis.html)
    we found that the copyright holders below also have a copyright on some
    files; the patch below adds their copyright notices.
    
    We encourage future patchers to add their copyright notice (and any
    appropriate additional licensing notices as needed) into the files when
    they make patches.  We also refer developers to our paper at
    http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
    that recommends ways to incorporate permissive-licensed code like this in
    a GPL-licensed project like Linux.
    
    We at SFLC were all very happy to do this work for the Linux Wireless team
    and are glad that a favorable outcome for all was reached.
    
    I believe I've used all the patch-submission tags correctly based on
    previous discussions and email exchanges with everyone involved, but I
    obviously don't patch the kernel often (er, at all :), so if I've made a
    mistake, please let me know.
    
    Changes-licensed-under: ISC
    Signed-off-by: Richard Fontana <fontana@softwarefreedom.org>
    Signed-off-by: Bradley M. Kuhn <bkuhn@softwarefreedom.org>
    Signed-off-by: Matt Norwood <norwood@softwarefreedom.org>
    Signed-off-by: Karen Sandler <karen@softwarefreedom.org>
    Acked-by: Matthew W. S. Bell  <mentor@madwifi.org>
    Acked-by: Pavel Roskin <proski@gnu.org>
    Acked-by: Nick Kossifidis <mickflemm@gmail.com>
    Acked-by: Jiri Slaby <jirislaby@gmail.com>
    Acked-by: Luis Rodriguez <mcgrof@winlab.rutgers.edu>
    Acked-by: Michael Taylor <mike.taylor@apprion.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit de50b363e8b429a0cdd11e29dc564391103afbb0
Author: Andrew Morton <akpm@linux-foundation.org>
Date:   Thu Sep 20 14:02:31 2007 -0700

    [PATCH] ath5k: Remove pointless empty CVS ID string.
    
    Cc: Jeff Garzik <jeff@garzik.org>
    Cc: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 4b4d8d579a6f317985bb9fab947c9cb3be75b761
Author: Stephen Hemminger <shemminger@linux-foundation.org>
Date:   Tue Sep 18 10:06:47 2007 -0700

    [PATCH] ath5k: msi support
    
    The atheros on my Fujitsu b6210 supports MSI.
    
    Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit a49855f50f2d9108971796b0e8607e5c75530d3c
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Thu Sep 20 17:13:26 2007 -0400

    [PATCH] ath5k - Fix filters for AR5212, correct return values for WEP
    
    I was not get getting DHCP replies back with ath5k on a AR5212. Turns
    out out of all filter flags if I enable AR5K_RX_FILTER_PROM I get my
    replies back. I've tested enabling AR5K_RX_FILTER_BCAST and
    AR5K_RX_FILTER_MCAST but AR5K_RX_FILTER_PROM only does the trick. This
    may fix this for other cards if you are not getting DHCP replies
    please let us know. For now we can enable AR5K_RX_FILTER_PROM on STA
    and Ad-hoc only for AR5212 until we sort out the filter flags
    properly. This patch also takes into account new changes to mac80211
    for ieee80211_ops's set_key().
    
    Filter API changes to come soon.
    
    In summary this patch has these changes:
    
    * AR5212 now receives broadcasts (DHCP works now)
    * ath5k_hw_set_key() was checking against key table size against
    key->keyid -- this can only be 0, 1, 2 or 3. Check against key->keylen
    and divide the table size by 8.
    * return proper values for WEP setting as per mac80211 documenation
    
    This patch applies to the ath5k branch of wireless-dev.
    
    Changes to ath5k_base.c
    Changes-licensed-under: 3-clause-BSD
    
    Changes to ath5k_hw.c, ath5k_reg.h
    Changes-licensed-under: ISC
    
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 7c6db582d195fe52648cfc070358fffa273cdb09
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Wed Sep 26 14:04:10 2007 -0400

    [PATCH] Net: ath5k, remove unused/dead code
    
    ath5k, remove unused/dead code
    
    Remove ifdeffed code, which is currently (and might not be in the future too)
    unused. If it was ever really needed, it could be easily ported back from the
    madwifi-old-openhal repository.
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit a736fa769ff7de817b4a76838a4285d83425f626
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Wed Sep 26 14:03:27 2007 -0400

    [PATCH] Net: ath5k, use int as retval
    
    ath5k, use int as retval
    
    Changes-licensed-under: ISC
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 55c7293d7aae2d2c6a8c66f2e4a1e01b4eedc576
Author: Nick Kossifidis <mickflemm@gmail.com>
Date:   Wed Sep 26 14:02:50 2007 -0400

    [PATCH] Net: ath5k, split hw into hw, phy and initvals
    
    ath5k, split hw into hw, phy and initvals
    
    Separate the hw code into logical pieces hw, phy and initvals for better
    readability and maintainability.
    
    This includes suggested file renames (_hw removal).
    
    Changes-licensed-under: ISC
    Signed-off-by: Nick Kossifidis <mickflemm@gmail.com>
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 3341123aa77a5d1889c9f2045906596efb4fe815
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Wed Sep 26 14:02:11 2007 -0400

    [PATCH] Net: ath5k, make its own directory
    
    ath5k, make its own directory
    
    also remove ath5_ prefix from the filenames
    
    Changes-licensed-under: ISC
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 366266df4c66edb01d608390f6f7d46bcb41b086
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Mon Sep 17 17:31:51 2007 -0400

    [PATCH] ath5k: Internalize Atheros Turbo modes
    
    This patch:
    
    * Internalizes Atheros Turbo modes (MODE_ATHEROS_TURBO,
    MODE_ATHEROS_TURBOG)
    
    * Internatizes Turbo modulation (MODULATION_TURBO)
    
    * Adds documention for Atheros Turbo modes (MODE_ATHEROS_TURBO,
    MODE_ATHEROS_TURBOG), Turbo modulation (MODULATION_TURBO) and XR
    modulation (MODULATION_XR)
    
    Changes-licensed-under: BSD
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 74f50de692a6596690e91384770b60b5568ef449
Author: John W. Linville <linville@tuxdriver.com>
Date:   Mon Sep 17 17:11:29 2007 -0400

    [PATCH] ath5k: remove HW_KEY_IDX_INVALID
    
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 8e617aad8033a2349938465a05b56079119d9762
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Mon Sep 17 00:48:27 2007 -0400

    [PATCH] Correct ath5k Kconfig depends
    
    This patch correct's ath5k's Kconfig depends line by making it depend
    on WLAN_80211 and also for now marks it EXPERIMENTAL. This patch
    applies to the ath5k branch of wireless-dev.
    
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 178861b790c8f26a72c2f5ba7f024e353fd087d4
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Mon Sep 17 00:01:09 2007 -0400

    [PATCH] Add Luis to MAINTAINERS for ath5k
    
    This patch adds me to the MAINTAINERS entry for ath5k. It applies to
    the ath5k branch on wireless-dev.
    
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 43395800ab8a7fba9d75c25f746a7c07b0151407
Author: Krzysztof Halasa <khc@pm.waw.pl>
Date:   Mon Sep 3 20:35:52 2007 +0200

    [PATCH] Ath5k panic fix
    
    Ath5k panics on ath_open() because sc->pdev is never set, fixed.
    
    Signed-off-by: Krzysztof Halasa <khc@pm.waw.pl>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit df42c535877e95bb8057006caa402e38a9f52e81
Author: John W. Linville <linville@tuxdriver.com>
Date:   Sat Sep 15 11:05:32 2007 -0400

    [PATCH] ath5k: remove use of IEEE80211_HW_WEP_INCLUDE_IV
    
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 512570b4e5eb78c424304b36b31b4ad4057be7ef
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Mon Sep 10 16:00:38 2007 -0400

    [PATCH] ath5k: more license corrections (ath5k_reg.h)
    
    This patch corrects the copyright and license on ath5k_reg.h.
    
    Specifically, this patch:
    
    * Removes the GPL license and restores the ISC license from
    ath5k_reg.h as Nick indicated.
    
    * Adds Reyk's Copyright to ath5k_reg.h
    
    Changes-licensed-under: ISC
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 74b0fd15c71eece4fafa8ab876a80229a273df2a
Author: Luis R. Rodriguez <mcgrof@gmail.com>
Date:   Thu Sep 6 10:18:22 2007 -0400

    [PATCH] ath5k: correct some license damage
    
    The attached patch fixes copyright notice for 3 files which need to be
    corrected regardless of the outcome of the license of the other files.
    
    This patch:
    
    * Adds warranty notices back to ath5k_regdom.[ch]
    
    * Adds Devicescape Copyright to ath5k_base.c
    
    Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Changes-licensed-under: ISC
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit c14692dbd6aa796703af95eff4c61b0cdda53720
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Tue Aug 28 11:59:54 2007 -0400

    [PATCH] Net: ath5k, switch to ioread/iowrite
    
    ath5k, switch to ioread/iowrite
    
    Do not use readl/writel, since iomap retval is platform dependent and
    needn't be virtual address awaited by readl/writel.
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit f55ecef744fe76b7145ed2ec457dbf3db237c164
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Sat Aug 25 03:59:28 2007 -0400

    [PATCH] Net: ath5k, remove some ieee80211 re-defines
    
    ath5k, remove some ieee80211 re-defines
    
    use mac80211 defines directly instead. this means MODULATION_* to
    IEEE80211_RATE_* switch.
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: <linville@tuxdriver.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 1c9adbeda0abb21469185a5b2b8645f1677a6de6
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Sat Aug 25 03:58:47 2007 -0400

    [PATCH] Net: ath5k, use short preamble for some rates
    
    ath5k, use short preamble for some rates
    
    2, 5.5 and 11 in b/g are now in short preamble mode
    
    Jiri Slaby wrote:
    > Johannes Berg wrote:
    > > umm, mac80211 needs to be able to choose depending on the network.
    
    Hmm, misleading log comment. It should be 'can now be in SP mode'.
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: <linville@tuxdriver.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit e09c0cb9abbfff5cbfd3606c2ce29c1decb75f56
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Sat Aug 25 03:51:51 2007 -0400

    [PATCH] Net: ath5k, initial write cleanup
    
    ath5k, initial write cleanup
    
    The final step of initial writing cleanup. ar5211_rf is going away.
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: <linville@tuxdriver.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 8d266a21d1d904882a62b033bb1cf259389231e0
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Sat Aug 25 03:57:26 2007 -0400

    [PATCH] Net: ath5k, comment some EEPROM registers
    
    ath5k, comment some EEPROM registers
    
    make some registers meaning clear
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Cc: <linville@tuxdriver.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 4d2fef63597ede5952119d942b65ef880e9960a2
Author: John W. Linville <linville@tuxdriver.com>
Date:   Thu Aug 23 15:23:30 2007 -0400

    [PATCH] ath5k: fix build breakage from recent mac80211 changes
    
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 7a0e04e86efee9abd76fe80ebf8f709c86c8e5cd
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Wed Aug 22 22:54:07 2007 +0200

    [PATCH] Net: ath5k, remove sysctls
    
    ath5k, remove sysctls
    
    Syscalls were buggy and defunct in later kernels (due to sysctl check).
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 0658473f185844150d65e5ed73d9443d50c1443b
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Wed Aug 22 22:53:25 2007 +0200

    [PATCH] Net: ath5k, no printk after STA->IBSS switch
    
    ath5k, no printk after STA->IBSS switch
    
    If STA->IBSS switch was done, but beacon_update weas not called so far, do
    not emit a warning about non-existent skbuf.
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit b8e8efa1988fafb88ad38feb9888975bf6130fbb
Author: akpm@linux-foundation.org <akpm@linux-foundation.org>
Date:   Tue Aug 21 16:53:53 2007 -0700

    [PATCH] ath5k: needs PCI
    
    drivers/net/wireless/ath5k_base.c: In function `ath_pci_probe':
    drivers/net/wireless/ath5k_base.c:2279: error: implicit declaration of function `pci_request_region'
    drivers/net/wireless/ath5k_base.c:2359: error: implicit declaration of function `pci_release_region'
    
    Cc: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 26d476d29bc182e0b0cda20f31ea4658cfc1e8c0
Author: akpm@linux-foundation.org <akpm@linux-foundation.org>
Date:   Tue Aug 21 00:51:20 2007 -0700

    [PATCH] ath5k: kconfig fix
    
    It needs stuff from net/mac80211/ieee80211.c
    
    Cc: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit e0cc717df74c822a74fb1ebb543e23c76faf5228
Author: akpm@linux-foundation.org <akpm@linux-foundation.org>
Date:   Tue Aug 21 00:42:32 2007 -0700

    [PATCH] ath5k: printk fix
    
    drivers/net/wireless/ath5k_base.c: In function 'ath_txq_setup':
    drivers/net/wireless/ath5k_base.c:2033: warning: format '%u' expects type 'unsigned int', but argument 3 has type 'long unsigned int'
    
    Cc: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit e2da439bda7d27027f03f2e29964fb207ddc3c07
Author: Jiri Slaby <jirislaby@gmail.com>
Date:   Sun Aug 12 17:33:16 2007 +0200

    [PATCH] Net: add ath5k wireless driver
    
    add ath5k wireless driver
    
    Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit e1d4f02abfde9dd92043b8183fa0c059005c1c43
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:37:34 2007 -0400

    [PATCH] at76_usb: Replace scan_runs with scan_need_any
    
    It's more meaningful than a number.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit f1ed02885b9a1302f02a7bb3139ea805707be0b9
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:37:29 2007 -0400

    [PATCH] at76_usb: Don't do additional MIB dumps if DEBUG is defined
    
    They are already printed from at76_startup_device(), which should be
    enough.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 6a34c483850de1271db8f0abe53d6825aa1092ce
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:37:23 2007 -0400

    [PATCH] at76_usb: Remove international roaming support
    
    It's broken beyond repair and legally dubious.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 62a072a880fdd1032e24f287584b259b3507531a
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:37:17 2007 -0400

    [PATCH] at76_usb: Massive cleanup of dump functions
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 138e229ca562fe21b3149815d06534b27f67faad
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:37:11 2007 -0400

    [PATCH] at76_usb: Simplify logic in at76_is_hidden_ssid()
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit ce3422c6760063f90f3ecd8faf33a0ee814d4abf
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:37:06 2007 -0400

    [PATCH] at76_usb: Eliminate pr_debug() in favor of at76_dbg()
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 4b60fe90eb7b952315ff79223ca514657a1ecde5
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:37:00 2007 -0400

    [PATCH] at76_usb: Eliminate at76_dbg_dumpbuf() in favor of hex2str()
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit cecc5c4bf796c1773632f1d35111898580b2c31f
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:54 2007 -0400

    [PATCH] at76_usb: Convert dbg() to at76_dbg() or remove it
    
    Using two debug macros is inconsistent.  Besides, dbg() only prints
    something if DEBUG is defined.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 64d72722d5f1880c31fa5aa755ac24e48f9bbf9e
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:49 2007 -0400

    [PATCH] at76_usb: Remove unneeded braces, found by checkpatch.pl
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 9616d8698d241c629b85f66dddd4ac90521fdd73
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:43 2007 -0400

    [PATCH] at76_usb: Improve dump of MAC_ADDR
    
    Don't require DEBUG to be defined.  Dump each group address separately,
    next to the status.  Rename MIB_MAC_ADD to MIB_MAC_ADDR.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit dd61c51db8d13c3523c147070523c235128657a8
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:37 2007 -0400

    [PATCH] at76_usb: Don't dump mib_mdomain while scanning, it's done on device startup
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 29c02a1ad890dfff58086d4de78a099b142ec39d
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:31 2007 -0400

    [PATCH] at76_usb: Do implicit scanning only with current ESSID
    
    This should speed up association.  We don't need non-matching APs if
    ESSID is set.
    
    For user-requested scanning, scan for any essid after that.  For
    international scanning, start with scan_runs 1, as it was meant to be
    done.  Actually use ir_step 0 for international scanning.  Adjust debug
    messages correspondingly.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 615391d6ddc5b8695d6ec11bd82bdd34d4dc78ba
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:26 2007 -0400

    [PATCH] at76_usb: Simplify at76_get_mib_mdomain()
    
    The "error" label is not needed.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 6b397f82ae645352d1d4918e3f158af990effb85
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:20 2007 -0400

    [PATCH] at76_usb: Only retry resubmitting rx_urb once
    
    Other drivers don't even bother resubmitting it with GFP_KERNEL.  If it
    fails with GFP_KERNEL, we have a problem, and restarting the task is not
    likely to be helpful.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit f98e14433f2ee35e30f55f92b68c5c9d182b97aa
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:14 2007 -0400

    [PATCH] at76_usb: Eliminate vendor IDs
    
    They are useless next to comments with full device names.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 154ecd11473066dc01eb4c911aaa7a471afab374
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:09 2007 -0400

    [PATCH] at76_usb: Protect at76_iw_handler_set_scan() with mutex
    
    This prevents a race condition when a response from an AP comes before
    we set state to MAC_SCANNING, but after at76_quiesce().
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit f2835b25679327863c54bc8c5390c12bef1c199b
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:36:03 2007 -0400

    [PATCH] at76_usb: Improve output of the regdomain id
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 5e7d14329b25b97a45ef9f7dc6bd6fe038a1e257
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:57 2007 -0400

    [PATCH] at76_usb: Only cancel correct timeouts for Auth and Assoc replies
    
    Don't cancel all delayed works indiscriminately.  In case of
    at76_rx_mgmt_auth(), cancel the timeout regardless of the status of the
    reply, as long as the reply is for us.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 6c7f43826ba0798dadefb76cdcab217511899054
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:52 2007 -0400

    [PATCH] at76_usb: Start beacon timeout task when connected
    
    Limit beacon timeout task to the managed mode.  Don't warn of the wrong
    mode or state, just exit.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 82074aefed229f4a73fecfd75abdddb44ead0e31
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:46 2007 -0400

    [PATCH] at76_usb: Don't disable and enable tasklets, it doesn't work as expected
    
    Bringing the interface down causes high CPU utilization.  Correct fix
    should probably disable rx URB, not only the rx tasklet.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 1d315c786a0198ef929885ae36591571e85972b9
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:40 2007 -0400

    [PATCH] at76_usb: Introduce at76_quiesce(), use it to stop network activity
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 2b8b6b19981336929b424bb92f5102b39399bc4a
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:34 2007 -0400

    [PATCH] at76_usb: Fix incorrect queue management in at76_tx_mgmt()
    
    The old pending buffer should be freed, not the new one.  No need to set
    txbuf to NULL.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 36c5a6e51b5049d2359de381ca4344132696c95f
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:29 2007 -0400

    [PATCH] at76_usb: Rename some long functions and fields
    
    Mostly try to avoid "bulk", which is a minor technical detail.  Also
    avoid read/write in favor of more common rx/tx.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 140aaba60a3276e853f71b7eb66a7f688b511af6
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:23 2007 -0400

    [PATCH] at76_usb: Fix hex2str() and mac2str() to avoid buffer overlap
    
    Declarations for two-dimensional arrays were written backwards.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 883f4f00dba021c8c391b2b20603492a861df86d
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:17 2007 -0400

    [PATCH] at76_usb: Simplify logic in at76_get_reg_domain()
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 61d1ac018a4813e22fd71e26e15f23c9751aeb10
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:11 2007 -0400

    [PATCH] at76_usb: Add minimal support to 505AMX
    
    Use inline functions for board classification.  Drop "_2958" from
    BOARD_505A_2958, it's implied.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 9b130002739d567bcecc882a0684b8a21eaf14b5
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:06 2007 -0400

    [PATCH] at76_usb: Simplify at76_usbdfu_download()
    
    Make it look similar to at76_load_external_fw()
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 88a0886b493220b19f34dfc54e6a4f95136e5fea
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:35:00 2007 -0400

    [PATCH] at76_usb: Merge at76_download_external_fw() into at76_load_external_fw()
    
    Simplify logic to ensure that the last block is empty.  Unify
    DFU_PACKETSIZE and EXT_FW_BLOCK_SIZE into FW_BLOCK_SIZE.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit ec501300e04f8113e41767ff8cb793a37ecc745e
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:34:54 2007 -0400

    [PATCH] at76_usb: Don't use shift on numeric constants in usb_control_msg() arguments
    
    We don't know the meaning of 0x0a02 and 0x0902 in at76_get_hw_cfg() and
    at76_get_hw_cfg_intersil() respectively, so don't pretend that we do.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 882d12d6bfecfc8b1f48df6c4f74cd047b96095c
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:34:49 2007 -0400

    [PATCH] at76_usb: Move (de)initialization functions closer to the end of file
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 76545302e02f90a871b87d1f1453b1b1f0bbca02
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:34:43 2007 -0400

    [PATCH] at76_usb: Add myself to the author list
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 86cb0cca95916994c97402b37a2d2b06facadf34
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:34:37 2007 -0400

    [PATCH] at76_usb: Avoid overuse of NULL
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 48ceb86bbcd74b620c2f4ddbe0cd00d641431afa
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:34:31 2007 -0400

    [PATCH] at76_usb: Rewrite at76_alloc_urbs() in a more linear fashion
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit a3e233571a1b6b53676dffa73311a0783786e8a1
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:34:26 2007 -0400

    [PATCH] at76_usb: Use existing macros to find bulk in and bulk out endpoints
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit b50b9624f610cecf3f03a02e7e45499400ff235a
Author: Pavel Roskin <proski@gnu.org>
Date:   Sat Sep 1 00:34:20 2007 -0400

    [PATCH] at76_usb: Protect at76_get_op_mode() and at76_get_mib() against short reads
    
    Don't rely on usb_control_msg() returning -EPIPE in absence of working
    firmware.  In some cases, no error is returned, but no data is read.
    
    Interpret short reads as an error, return -EIO.
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

commit 302151c9c6deadaab7f71fcbae655f489c0b4fa4
Author: Pavel Roskin <proski@gnu.org>
Date:   Thu Aug 23 14:40:17 2007 -0400

    [PATCH] Add at76_usb driver
    
    This driver supports USB devices using Atmel at76c503, at76c505 and
    at76c505a chipsets.  Known devices with this chip are Belkin F5D6050,
    Dynalink/Askey WLL013, Linksys WUSB11 v2.6, Netgear MA101B and many
    more.
    
    All supported devices require firmware, which can be downloaded from
    http://developer.berlios.de/projects/at76c503a/
    
    Signed-off-by: Pavel Roskin <proski@gnu.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---

 MAINTAINERS                                            |   27 
 drivers/net/wireless/Kconfig                           |    8 
 drivers/net/wireless/at76_usb.c                        | 5556 ++++++++++
 drivers/net/wireless/at76_usb.h                        |  619 +
 drivers/net/wireless/ath5k/Makefile                    |    2 
 drivers/net/wireless/ath5k/ath5k.h                     | 1086 +
 drivers/net/wireless/ath5k/base.c                      | 2579 ++++
 drivers/net/wireless/ath5k/base.h                      |  207 
 drivers/net/wireless/ath5k/hw.c                        | 4322 +++++++
 drivers/net/wireless/ath5k/hw.h                        |  588 +
 drivers/net/wireless/ath5k/initvals.c                  | 1102 +
 drivers/net/wireless/ath5k/phy.c                       | 1686 +++
 drivers/net/wireless/ath5k/reg.h                       | 1978 +++
 drivers/net/wireless/ath5k/regdom.c                    |  121 
 drivers/net/wireless/ath5k/regdom.h                    |  500 
 drivers/net/wireless/zd1211rw-mac80211/Kconfig         |   18 
 drivers/net/wireless/zd1211rw-mac80211/Makefile        |   10 
 drivers/net/wireless/zd1211rw-mac80211/zd_chip.c       | 1622 ++
 drivers/net/wireless/zd1211rw-mac80211/zd_chip.h       |  943 +
 drivers/net/wireless/zd1211rw-mac80211/zd_def.h        |   57 
 drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.c  |  100 
 drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.h  |   75 
 drivers/net/wireless/zd1211rw-mac80211/zd_mac.c        |  978 +
 drivers/net/wireless/zd1211rw-mac80211/zd_mac.h        |  238 
 drivers/net/wireless/zd1211rw-mac80211/zd_rf.c         |  178 
 drivers/net/wireless/zd1211rw-mac80211/zd_rf.h         |  108 
 drivers/net/wireless/zd1211rw-mac80211/zd_rf_al2230.c  |  439 
 drivers/net/wireless/zd1211rw-mac80211/zd_rf_al7230b.c |  492 
 drivers/net/wireless/zd1211rw-mac80211/zd_rf_rf2959.c  |  279 
 drivers/net/wireless/zd1211rw-mac80211/zd_rf_uw2453.c  |  534 
 drivers/net/wireless/zd1211rw-mac80211/zd_usb.c        | 1528 ++
 drivers/net/wireless/zd1211rw-mac80211/zd_usb.h        |  263 
 drivers/usb/host/Kconfig                               |   13 
 drivers/usb/host/ohci-hcd.c                            |   21 
 drivers/usb/host/ohci-ssb.c                            |  254 
 35 files changed, 28530 insertions(+), 1 deletion(-)

diff -puN MAINTAINERS~git-wireless MAINTAINERS
--- a/MAINTAINERS~git-wireless
+++ a/MAINTAINERS
@@ -648,6 +648,14 @@ M:	ecashin@coraid.com
 W:	http://www.coraid.com/support/linux
 S:	Supported
 
+ATHEROS ATH5K WIRELESS DRIVER
+P:	Jiri Slaby
+M:	jirislaby@gmail.com
+P:	Luis R. Rodriguez
+M:	mcgrof@gmail.com
+L:	linux-wireless@vger.kernel.org
+S:	Maintained
+
 ATL1 ETHERNET DRIVER
 P:	Jay Cliburn
 M:	jcliburn@gmail.com
@@ -691,6 +699,15 @@ W:	http://www.thekelleys.org.uk/atmel
 W:	http://atmelwlandriver.sourceforge.net/
 S:	Maintained
 
+ATMEL USB WIRELESS DRIVER
+P:	Pavel Roskin
+M:	proski@gnu.org
+L:	linux-wireless@vger.kernel.org
+L:	at76c503a-user@lists.berlios.de
+L:	at76c503a-develop@lists.berlios.de
+W:	http://at76c503a.berlios.de/
+S:	Maintained
+
 AUDIT SUBSYSTEM
 P:	David Woodhouse
 M:	dwmw2@infradead.org
@@ -4331,6 +4348,16 @@ W:	http://www.qsl.net/dl1bke/
 L:	linux-hams@vger.kernel.org
 S:	Maintained
 
+ZD1211-MAC80211 WIRELESS DRIVER
+P:	Daniel Drake
+M:	dsd@gentoo.org
+P:	Ulrich Kunitz
+M:	kune@deine-taler.de
+W:	http://zd1211.ath.cx/wiki/DriverRewrite
+L:	linux-wireless@vger.kernel.org
+L:	zd1211-devs@lists.sourceforge.net (subscribers-only)
+S:	Maintained
+
 ZD1211RW WIRELESS DRIVER
 P:	Daniel Drake
 M:	dsd@gentoo.org
diff -puN drivers/net/wireless/Kconfig~git-wireless drivers/net/wireless/Kconfig
--- a/drivers/net/wireless/Kconfig~git-wireless
+++ a/drivers/net/wireless/Kconfig
@@ -381,6 +381,14 @@ config PCI_HERMES
 	  common.  Some of the built-in wireless adaptors in laptops are of
 	  this variety.
 
+config USB_ATMEL
+	tristate "Atmel at76c503/at76c505/at76c505a USB cards"
+	depends on WLAN_80211 && USB
+	select FW_LOADER
+	---help---
+	  Enable support for USB Wireless devices using Atmel at76c503,
+	  at76c505 or at76c505a chips.
+
 config PCMCIA_HERMES
 	tristate "Hermes PCMCIA card support"
 	depends on PCMCIA && HERMES
diff -puN drivers/net/wireless/Makefile~git-wireless drivers/net/wireless/Makefile
diff -puN /dev/null drivers/net/wireless/at76_usb.c
--- /dev/null
+++ a/drivers/net/wireless/at76_usb.c
@@ -0,0 +1,5556 @@
+/*
+ * at76c503/at76c505 USB driver
+ *
+ * Copyright (c) 2002 - 2003 Oliver Kurth
+ * Copyright (c) 2004 Joerg Albert <joerg.albert@gmx.de>
+ * Copyright (c) 2004 Nick Jones
+ * Copyright (c) 2004 Balint Seeber <n0_5p4m_p13453@hotmail.com>
+ * Copyright (c) 2007 Guido Guenther <agx@sigxcpu.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This file is part of the Berlios driver for WLAN USB devices based on the
+ * Atmel AT76C503A/505/505A.
+ *
+ * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/firmware.h>
+#include <linux/leds.h>
+#include <net/ieee80211.h>
+
+#include "at76_usb.h"
+
+/* Version information */
+#define DRIVER_NAME "at76_usb"
+#define DRIVER_VERSION	"0.17"
+#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver"
+
+/* at76_debug bits */
+#define DBG_PROGRESS		0x00000001	/* authentication/accociation */
+#define DBG_BSS_TABLE		0x00000002	/* show BSS table after scans */
+#define DBG_IOCTL		0x00000004	/* ioctl calls / settings */
+#define DBG_MAC_STATE		0x00000008	/* MAC state transitions */
+#define DBG_TX_DATA		0x00000010	/* tx header */
+#define DBG_TX_DATA_CONTENT	0x00000020	/* tx content */
+#define DBG_TX_MGMT		0x00000040	/* tx management */
+#define DBG_RX_DATA		0x00000080	/* rx data header */
+#define DBG_RX_DATA_CONTENT	0x00000100	/* rx data content */
+#define DBG_RX_MGMT		0x00000200	/* rx mgmt frame headers */
+#define DBG_RX_BEACON		0x00000400	/* rx beacon */
+#define DBG_RX_CTRL		0x00000800	/* rx control */
+#define DBG_RX_MGMT_CONTENT	0x00001000	/* rx mgmt content */
+#define DBG_RX_FRAGS		0x00002000	/* rx data fragment handling */
+#define DBG_DEVSTART		0x00004000	/* fw download, device start */
+#define DBG_URB			0x00008000	/* rx urb status, ... */
+#define DBG_RX_ATMEL_HDR	0x00010000	/* Atmel-specific Rx headers */
+#define DBG_PROC_ENTRY		0x00020000	/* procedure entries/exits */
+#define DBG_PM			0x00040000	/* power management settings */
+#define DBG_BSS_MATCH		0x00080000	/* BSS match failures */
+#define DBG_PARAMS		0x00100000	/* show configured parameters */
+#define DBG_WAIT_COMPLETE	0x00200000	/* command completion */
+#define DBG_RX_FRAGS_SKB	0x00400000	/* skb header of Rx fragments */
+#define DBG_BSS_TABLE_RM	0x00800000	/* purging bss table entries */
+#define DBG_MONITOR_MODE	0x01000000	/* monitor mode */
+#define DBG_MIB			0x02000000	/* dump all MIBs on startup */
+#define DBG_MGMT_TIMER		0x04000000	/* dump mgmt_timer ops */
+#define DBG_WE_EVENTS		0x08000000	/* dump wireless events */
+#define DBG_FW			0x10000000	/* firmware download */
+#define DBG_DFU			0x20000000	/* device firmware upgrade */
+
+#define DBG_DEFAULTS		0
+
+/* Use our own dbg macro */
+#define at76_dbg(bits, format, arg...) \
+	do { \
+		if (at76_debug & (bits)) \
+		printk(KERN_DEBUG DRIVER_NAME ": " format "\n" , ## arg); \
+	} while (0)
+
+static int at76_debug = DBG_DEFAULTS;
+
+/* Protect against concurrent firmware loading and parsing */
+static struct mutex fw_mutex;
+
+static struct fwentry firmwares[] = {
+	[0] = {""},
+	[BOARD_503_ISL3861] = {"atmel_at76c503-i3861.bin"},
+	[BOARD_503_ISL3863] = {"atmel_at76c503-i3863.bin"},
+	[BOARD_503] = {"atmel_at76c503-rfmd.bin"},
+	[BOARD_503_ACC] = {"atmel_at76c503-rfmd-acc.bin"},
+	[BOARD_505] = {"atmel_at76c505-rfmd.bin"},
+	[BOARD_505_2958] = {"atmel_at76c505-rfmd2958.bin"},
+	[BOARD_505A] = {"atmel_at76c505a-rfmd2958.bin"},
+	[BOARD_505AMX] = {"atmel_at76c505amx-rfmd.bin"},
+};
+
+#define USB_DEVICE_DATA(__ops)	.driver_info = (kernel_ulong_t)(__ops)
+
+static struct usb_device_id dev_table[] = {
+	/*
+	 * at76c503-i3861
+	 */
+	/* Generic AT76C503/3861 device */
+	{USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Linksys WUSB11 v2.1/v2.6 */
+	{USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Netgear MA101 rev. A */
+	{USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Tekram U300C / Allnet ALL0193 */
+	{USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* HP HN210W J7801A */
+	{USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Sitecom/Z-Com/Zyxel M4Y-750 */
+	{USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Dynalink/Askey WLL013 (intersil) */
+	{USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */
+	{USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* BenQ AWL300 */
+	{USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Addtron AWU-120, Compex WLU11 */
+	{USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Intel AP310 AnyPoint II USB */
+	{USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Dynalink L11U */
+	{USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* Arescom WL-210, FCC id 07J-GL2411USB */
+	{USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* I-O DATA WN-B11/USB */
+	{USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/* BT Voyager 1010 */
+	{USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861)},
+	/*
+	 * at76c503-i3863
+	 */
+	/* Generic AT76C503/3863 device */
+	{USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863)},
+	/* Samsung SWL-2100U */
+	{USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863)},
+	/*
+	 * at76c503-rfmd
+	 */
+	/* Generic AT76C503/RFMD device */
+	{USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503)},
+	/* Dynalink/Askey WLL013 (rfmd) */
+	{USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503)},
+	/* Linksys WUSB11 v2.6 */
+	{USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503)},
+	/* Network Everywhere NWU11B */
+	{USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503)},
+	/* Netgear MA101 rev. B */
+	{USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503)},
+	/* D-Link DWL-120 rev. E */
+	{USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503)},
+	/* Actiontec 802UAT1, HWU01150-01UK */
+	{USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503)},
+	/* AirVast W-Buddie WN210 */
+	{USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503)},
+	/* Dick Smith Electronics XH1153 802.11b USB adapter */
+	{USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503)},
+	/* CNet CNUSB611 */
+	{USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503)},
+	/* FiberLine FL-WL200U */
+	{USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503)},
+	/* BenQ AWL400 USB stick */
+	{USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503)},
+	/* 3Com 3CRSHEW696 */
+	{USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503)},
+	/* Siemens Santis ADSL WLAN USB adapter WLL 013 */
+	{USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503)},
+	/* Belkin F5D6050, version 2 */
+	{USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503)},
+	/* iBlitzz, BWU613 (not *B or *SB) */
+	{USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503)},
+	/* Gigabyte GN-WLBM101 */
+	{USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503)},
+	/* Planex GW-US11S */
+	{USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503)},
+	/* Internal WLAN adapter in h5[4,5]xx series iPAQs */
+	{USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503)},
+	/* Corega Wireless LAN USB-11 mini */
+	{USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503)},
+	/* Corega Wireless LAN USB-11 mini2 */
+	{USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503)},
+	/*
+	 * at76c503-rfmd-acc
+	 */
+	/* SMC2664W */
+	{USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC)},
+	/* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */
+	{USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC)},
+	/*
+	 * at76c505-rfmd
+	 */
+	/* Generic AT76C505/RFMD */
+	{USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505)},
+	/*
+	 * at76c505-rfmd2958
+	 */
+	/* Generic AT76C505/RFMD, OvisLink WL-1130USB */
+	{USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958)},
+	/* Fiberline FL-WL240U */
+	{USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958)},
+	/* CNet CNUSB-611G */
+	{USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958)},
+	/* Linksys WUSB11 v2.8 */
+	{USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958)},
+	/* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */
+	{USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958)},
+	/* Corega WLAN USB Stick 11 */
+	{USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958)},
+	/* Microstar MSI Box MS6978 */
+	{USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958)},
+	/*
+	 * at76c505a-rfmd2958
+	 */
+	/* Generic AT76C505A device */
+	{USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A)},
+	/* Generic AT76C505AS device */
+	{USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A)},
+	/* Siemens Gigaset USB WLAN Adapter 11 */
+	{USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A)},
+	/*
+	 * at76c505amx-rfmd
+	 */
+	/* Generic AT76C505AMX device */
+	{USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, dev_table);
+
+/* Supported rates of this hardware, bit 7 marks basic rates */
+static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 };
+
+/* Frequency of each channel in MHz */
+static const long channel_frequency[] = {
+	2412, 2417, 2422, 2427, 2432, 2437, 2442,
+	2447, 2452, 2457, 2462, 2467, 2472, 2484
+};
+
+#define NUM_CHANNELS ARRAY_SIZE(channel_frequency)
+
+static const char *const preambles[] = { "long", "short", "auto" };
+
+static const char *const mac_states[] = {
+	[MAC_INIT] = "INIT",
+	[MAC_SCANNING] = "SCANNING",
+	[MAC_AUTH] = "AUTH",
+	[MAC_ASSOC] = "ASSOC",
+	[MAC_JOINING] = "JOINING",
+	[MAC_CONNECTED] = "CONNECTED",
+	[MAC_OWN_IBSS] = "OWN_IBSS"
+};
+
+/* Firmware download */
+/* DFU states */
+#define STATE_IDLE			0x00
+#define STATE_DETACH			0x01
+#define STATE_DFU_IDLE			0x02
+#define STATE_DFU_DOWNLOAD_SYNC		0x03
+#define STATE_DFU_DOWNLOAD_BUSY		0x04
+#define STATE_DFU_DOWNLOAD_IDLE		0x05
+#define STATE_DFU_MANIFEST_SYNC		0x06
+#define STATE_DFU_MANIFEST		0x07
+#define STATE_DFU_MANIFEST_WAIT_RESET	0x08
+#define STATE_DFU_UPLOAD_IDLE		0x09
+#define STATE_DFU_ERROR			0x0a
+
+/* DFU commands */
+#define DFU_DETACH			0
+#define DFU_DNLOAD			1
+#define DFU_UPLOAD			2
+#define DFU_GETSTATUS			3
+#define DFU_CLRSTATUS			4
+#define DFU_GETSTATE			5
+#define DFU_ABORT			6
+
+#define FW_BLOCK_SIZE 1024
+
+struct dfu_status {
+	unsigned char status;
+	unsigned char poll_timeout[3];
+	unsigned char state;
+	unsigned char string;
+} __attribute__((packed));
+
+static inline int at76_is_intersil(enum board_type board)
+{
+	return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863);
+}
+
+static inline int at76_is_503rfmd(enum board_type board)
+{
+	return (board == BOARD_503 || board == BOARD_503_ACC);
+}
+
+static inline int at76_is_505a(enum board_type board)
+{
+	return (board == BOARD_505A || board == BOARD_505AMX);
+}
+
+/* Load a block of the first (internal) part of the firmware */
+static int at76_load_int_fw_block(struct usb_device *udev, int blockno,
+				  void *block, int size)
+{
+	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD,
+			       USB_TYPE_CLASS | USB_DIR_OUT |
+			       USB_RECIP_INTERFACE, blockno, 0, block, size,
+			       USB_CTRL_GET_TIMEOUT);
+}
+
+static int at76_dfu_get_status(struct usb_device *udev,
+			       struct dfu_status *status)
+{
+	int ret;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS,
+			      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+			      0, 0, status, sizeof(struct dfu_status),
+			      USB_CTRL_GET_TIMEOUT);
+	return ret;
+}
+
+static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state)
+{
+	int ret;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE,
+			      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+			      0, 0, state, 1, USB_CTRL_GET_TIMEOUT);
+	return ret;
+}
+
+/* Convert timeout from the DFU status to jiffies */
+static inline unsigned long at76_get_timeout(struct dfu_status *s)
+{
+	return msecs_to_jiffies((s->poll_timeout[2] << 16)
+				| (s->poll_timeout[1] << 8)
+				| (s->poll_timeout[0]));
+}
+
+/* Load internal firmware from the buffer.  If manifest_sync_timeout > 0, use
+ * its value in jiffies in the MANIFEST_SYNC state.  */
+static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
+				int manifest_sync_timeout)
+{
+	u8 *block;
+	struct dfu_status dfu_stat_buf;
+	int ret = 0;
+	int need_dfu_state = 1;
+	int is_done = 0;
+	u8 dfu_state = 0;
+	u32 dfu_timeout = 0;
+	int bsize = 0;
+	int blockno = 0;
+
+	at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size,
+		 manifest_sync_timeout);
+
+	if (!size) {
+		dev_printk(KERN_ERR, &udev->dev, "FW buffer length invalid!\n");
+		return -EINVAL;
+	}
+
+	block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
+	if (!block)
+		return -ENOMEM;
+
+	do {
+		if (need_dfu_state) {
+			ret = at76_dfu_get_state(udev, &dfu_state);
+			if (ret < 0) {
+				dev_printk(KERN_ERR, &udev->dev,
+					   "cannot get DFU state: %d\n", ret);
+				goto exit;
+			}
+			need_dfu_state = 0;
+		}
+
+		switch (dfu_state) {
+		case STATE_DFU_DOWNLOAD_SYNC:
+			at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC");
+			ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+			if (ret >= 0) {
+				dfu_state = dfu_stat_buf.state;
+				dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+				need_dfu_state = 0;
+			} else
+				dev_printk(KERN_ERR, &udev->dev,
+					   "at76_dfu_get_status returned %d\n",
+					   ret);
+			break;
+
+		case STATE_DFU_DOWNLOAD_BUSY:
+			at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY");
+			need_dfu_state = 1;
+
+			at76_dbg(DBG_DFU, "DFU: Resetting device");
+			schedule_timeout_interruptible(dfu_timeout);
+			break;
+
+		case STATE_DFU_DOWNLOAD_IDLE:
+			at76_dbg(DBG_DFU, "DOWNLOAD...");
+			/* fall through */
+		case STATE_DFU_IDLE:
+			at76_dbg(DBG_DFU, "DFU IDLE");
+
+			bsize = min_t(int, size, FW_BLOCK_SIZE);
+			memcpy(block, buf, bsize);
+			at76_dbg(DBG_DFU, "int fw, size left = %5d, "
+				 "bsize = %4d, blockno = %2d", size, bsize,
+				 blockno);
+			ret =
+			    at76_load_int_fw_block(udev, blockno, block, bsize);
+			buf += bsize;
+			size -= bsize;
+			blockno++;
+
+			if (ret != bsize)
+				dev_printk(KERN_ERR, &udev->dev,
+					   "at76_load_int_fw_block "
+					   "returned %d\n", ret);
+			need_dfu_state = 1;
+			break;
+
+		case STATE_DFU_MANIFEST_SYNC:
+			at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC");
+
+			ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+			if (ret < 0)
+				break;
+
+			dfu_state = dfu_stat_buf.state;
+			dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+			need_dfu_state = 0;
+
+			/* override the timeout from the status response,
+			   needed for AT76C505A */
+			if (manifest_sync_timeout > 0)
+				dfu_timeout = manifest_sync_timeout;
+
+			at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase");
+			schedule_timeout_interruptible(dfu_timeout);
+			break;
+
+		case STATE_DFU_MANIFEST:
+			at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST");
+			is_done = 1;
+			break;
+
+		case STATE_DFU_MANIFEST_WAIT_RESET:
+			at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET");
+			is_done = 1;
+			break;
+
+		case STATE_DFU_UPLOAD_IDLE:
+			at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE");
+			break;
+
+		case STATE_DFU_ERROR:
+			at76_dbg(DBG_DFU, "STATE_DFU_ERROR");
+			ret = -EPIPE;
+			break;
+
+		default:
+			at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state);
+			ret = -EINVAL;
+			break;
+		}
+	} while (!is_done && (ret >= 0));
+
+exit:
+	kfree(block);
+	if (ret >= 0)
+		ret = 0;
+
+	return ret;
+}
+
+/* Report that the scan results are ready */
+static inline void at76_iwevent_scan_complete(struct net_device *netdev)
+{
+	union iwreq_data wrqu;
+	wrqu.data.length = 0;
+	wrqu.data.flags = 0;
+	wireless_send_event(netdev, SIOCGIWSCAN, &wrqu, NULL);
+	at76_dbg(DBG_WE_EVENTS, "%s: SIOCGIWSCAN sent", netdev->name);
+}
+
+static inline void at76_iwevent_bss_connect(struct net_device *netdev,
+					    u8 *bssid)
+{
+	union iwreq_data wrqu;
+	wrqu.data.length = 0;
+	wrqu.data.flags = 0;
+	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL);
+	at76_dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", netdev->name,
+		 __func__);
+}
+
+static inline void at76_iwevent_bss_disconnect(struct net_device *netdev)
+{
+	union iwreq_data wrqu;
+	wrqu.data.length = 0;
+	wrqu.data.flags = 0;
+	memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL);
+	at76_dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", netdev->name,
+		 __func__);
+}
+
+#define HEX2STR_BUFFERS 4
+#define HEX2STR_MAX_LEN 64
+#define BIN2HEX(x) ((x) < 10 ? '0' + (x) : (x) + 'A' - 10)
+
+/* Convert binary data into hex string */
+static char *hex2str(void *buf, int len)
+{
+	static atomic_t a = ATOMIC_INIT(0);
+	static char bufs[HEX2STR_BUFFERS][3 * HEX2STR_MAX_LEN + 1];
+	char *ret = bufs[atomic_inc_return(&a) & (HEX2STR_BUFFERS - 1)];
+	char *obuf = ret;
+	u8 *ibuf = buf;
+
+	if (len > HEX2STR_MAX_LEN)
+		len = HEX2STR_MAX_LEN;
+
+	if (len <= 0) {
+		ret[0] = '\0';
+		return ret;
+	}
+
+	while (len--) {
+		*obuf++ = BIN2HEX(*ibuf >> 4);
+		*obuf++ = BIN2HEX(*ibuf & 0xf);
+		*obuf++ = '-';
+		ibuf++;
+	}
+	*(--obuf) = '\0';
+
+	return ret;
+}
+
+#define MAC2STR_BUFFERS 4
+
+static inline char *mac2str(u8 *mac)
+{
+	static atomic_t a = ATOMIC_INIT(0);
+	static char bufs[MAC2STR_BUFFERS][6 * 3];
+	char *str;
+
+	str = bufs[atomic_inc_return(&a) & (MAC2STR_BUFFERS - 1)];
+	sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	return str;
+}
+
+/* LED trigger */
+static int tx_activity;
+static void at76_ledtrig_tx_timerfunc(unsigned long data);
+static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0);
+DEFINE_LED_TRIGGER(ledtrig_tx);
+
+static void at76_ledtrig_tx_timerfunc(unsigned long data)
+{
+	static int tx_lastactivity;
+
+	if (tx_lastactivity != tx_activity) {
+		tx_lastactivity = tx_activity;
+		led_trigger_event(ledtrig_tx, LED_FULL);
+		mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4);
+	} else
+		led_trigger_event(ledtrig_tx, LED_OFF);
+}
+
+static void at76_ledtrig_tx_activity(void)
+{
+	tx_activity++;
+	if (!timer_pending(&ledtrig_tx_timer))
+		mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4);
+}
+
+/* Check if the given ssid is hidden */
+static inline int at76_is_hidden_ssid(u8 *ssid, int length)
+{
+	static const u8 zeros[32];
+
+	if (length == 0)
+		return 1;
+
+	if (length == 1 && ssid[0] == ' ')
+		return 1;
+
+	return (memcmp(ssid, zeros, length) == 0);
+}
+
+static inline void at76_free_bss_list(struct at76_priv *priv)
+{
+	struct list_head *next, *ptr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
+
+	priv->curr_bss = NULL;
+
+	list_for_each_safe(ptr, next, &priv->bss_list) {
+		list_del(ptr);
+		kfree(list_entry(ptr, struct bss_info, list));
+	}
+
+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
+}
+
+static int at76_remap(struct usb_device *udev)
+{
+	int ret;
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a,
+			      USB_TYPE_VENDOR | USB_DIR_OUT |
+			      USB_RECIP_INTERFACE, 0, 0, NULL, 0,
+			      USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int at76_get_op_mode(struct usb_device *udev)
+{
+	int ret;
+	u8 op_mode;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+			      USB_TYPE_VENDOR | USB_DIR_IN |
+			      USB_RECIP_INTERFACE, 0x01, 0, &op_mode, 1,
+			      USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		return ret;
+	else if (ret < 1)
+		return -EIO;
+	else
+		return op_mode;
+}
+
+/* Load a block of the second ("external") part of the firmware */
+static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno,
+					 void *block, int size)
+{
+	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e,
+			       USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+			       0x0802, blockno, block, size,
+			       USB_CTRL_GET_TIMEOUT);
+}
+
+static inline int at76_get_hw_cfg(struct usb_device *udev,
+				  union at76_hwcfg *buf, int buf_size)
+{
+	return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+			       USB_TYPE_VENDOR | USB_DIR_IN |
+			       USB_RECIP_INTERFACE, 0x0a02, 0,
+			       buf, buf_size, USB_CTRL_GET_TIMEOUT);
+}
+
+/* Intersil boards use a different "value" for GetHWConfig requests */
+static inline int at76_get_hw_cfg_intersil(struct usb_device *udev,
+					   union at76_hwcfg *buf, int buf_size)
+{
+	return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+			       USB_TYPE_VENDOR | USB_DIR_IN |
+			       USB_RECIP_INTERFACE, 0x0902, 0,
+			       buf, buf_size, USB_CTRL_GET_TIMEOUT);
+}
+
+/* Get the hardware configuration for the adapter and put it to the appropriate
+ * fields of 'priv' (the GetHWConfig request and interpretation of the result
+ * depends on the board type) */
+static int at76_get_hw_config(struct at76_priv *priv)
+{
+	int ret;
+	union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL);
+
+	if (!hwcfg)
+		return -ENOMEM;
+
+	if (at76_is_intersil(priv->board_type)) {
+		ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg,
+					       sizeof(hwcfg->i));
+		if (ret < 0)
+			goto exit;
+		memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN);
+		priv->regulatory_domain = hwcfg->i.regulatory_domain;
+	} else if (at76_is_503rfmd(priv->board_type)) {
+		ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3));
+		if (ret < 0)
+			goto exit;
+		memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN);
+		priv->regulatory_domain = hwcfg->r3.regulatory_domain;
+	} else {
+		ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5));
+		if (ret < 0)
+			goto exit;
+		memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN);
+		priv->regulatory_domain = hwcfg->r5.regulatory_domain;
+	}
+
+exit:
+	kfree(hwcfg);
+	if (ret < 0)
+		printk(KERN_ERR "%s: cannot get HW Config (error %d)\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+static struct reg_domain const *at76_get_reg_domain(u16 code)
+{
+	int i;
+	static struct reg_domain const fd_tab[] = {
+		{0x10, "FCC (USA)", 0x7ff},	/* ch 1-11 */
+		{0x20, "IC (Canada)", 0x7ff},	/* ch 1-11 */
+		{0x30, "ETSI (most of Europe)", 0x1fff},	/* ch 1-13 */
+		{0x31, "Spain", 0x600},	/* ch 10-11 */
+		{0x32, "France", 0x1e00},	/* ch 10-13 */
+		{0x40, "MKK (Japan)", 0x2000},	/* ch 14 */
+		{0x41, "MKK1 (Japan)", 0x3fff},	/* ch 1-14 */
+		{0x50, "Israel", 0x3fc},	/* ch 3-9 */
+		{0x00, "<unknown>", 0xffffffff}	/* ch 1-32 */
+	};
+
+	/* Last entry is fallback for unknown domain code */
+	for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++)
+		if (code == fd_tab[i].code)
+			break;
+
+	return &fd_tab[i];
+}
+
+static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf,
+			       int buf_size)
+{
+	int ret;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33,
+			      USB_TYPE_VENDOR | USB_DIR_IN |
+			      USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size,
+			      USB_CTRL_GET_TIMEOUT);
+	if (ret >= 0 && ret != buf_size)
+		return -EIO;
+	return ret;
+}
+
+/* Return positive number for status, negative for an error */
+static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd)
+{
+	u8 stat_buf[40];
+	int ret;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22,
+			      USB_TYPE_VENDOR | USB_DIR_IN |
+			      USB_RECIP_INTERFACE, cmd, 0, stat_buf,
+			      sizeof(stat_buf), USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		return ret;
+
+	return stat_buf[5];
+}
+
+static int at76_set_card_command(struct usb_device *udev, int cmd, void *buf,
+				 int buf_size)
+{
+	int ret;
+	struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) +
+					       buf_size, GFP_KERNEL);
+
+	if (!cmd_buf)
+		return -ENOMEM;
+
+	cmd_buf->cmd = cmd;
+	cmd_buf->reserved = 0;
+	cmd_buf->size = cpu_to_le16(buf_size);
+	memcpy(cmd_buf->data, buf, buf_size);
+
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e,
+			      USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+			      0, 0, cmd_buf,
+			      sizeof(struct at76_command) + buf_size,
+			      USB_CTRL_GET_TIMEOUT);
+	kfree(cmd_buf);
+	return ret;
+}
+
+#define MAKE_CMD_STATUS_CASE(c)	case (c): return #c
+static const char *at76_get_cmd_status_string(u8 cmd_status)
+{
+	switch (cmd_status) {
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE);
+		MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED);
+	}
+
+	return "UNKNOWN";
+}
+
+/* Wait until the command is completed */
+static int at76_wait_completion(struct at76_priv *priv, int cmd)
+{
+	int status = 0;
+	unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT;
+
+	do {
+		status = at76_get_cmd_status(priv->udev, cmd);
+		if (status < 0) {
+			printk(KERN_ERR "%s: at76_get_cmd_status failed: %d\n",
+			       priv->netdev->name, status);
+			break;
+		}
+
+		at76_dbg(DBG_WAIT_COMPLETE,
+			 "%s: Waiting on cmd %d, status = %d (%s)",
+			 priv->netdev->name, cmd, status,
+			 at76_get_cmd_status_string(status));
+
+		if (status != CMD_STATUS_IN_PROGRESS
+		    && status != CMD_STATUS_IDLE)
+			break;
+
+		schedule_timeout_interruptible(HZ / 10);	/* 100 ms */
+		if (time_after(jiffies, timeout)) {
+			printk(KERN_ERR
+			       "%s: completion timeout for command %d\n",
+			       priv->netdev->name, cmd);
+			status = -ETIMEDOUT;
+			break;
+		}
+	} while (1);
+
+	return status;
+}
+
+static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf)
+{
+	int ret;
+
+	ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf,
+				    offsetof(struct set_mib_buffer,
+					     data) + buf->size);
+	if (ret < 0)
+		return ret;
+
+	ret = at76_wait_completion(priv, CMD_SET_MIB);
+	if (ret != CMD_STATUS_COMPLETE) {
+		printk(KERN_INFO
+		       "%s: set_mib: at76_wait_completion failed "
+		       "with %d\n", priv->netdev->name, ret);
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */
+static int at76_set_radio(struct at76_priv *priv, int enable)
+{
+	int ret;
+	int cmd;
+
+	if (priv->radio_on == enable)
+		return 0;
+
+	cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF;
+
+	ret = at76_set_card_command(priv->udev, cmd, NULL, 0);
+	if (ret < 0)
+		printk(KERN_ERR "%s: at76_set_card_command(%d) failed: %d\n",
+		       priv->netdev->name, cmd, ret);
+	else
+		ret = 1;
+
+	priv->radio_on = enable;
+	return ret;
+}
+
+/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */
+static int at76_set_pm_mode(struct at76_priv *priv)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_MAC_MGMT;
+	priv->mib_buf.size = 1;
+	priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode);
+	priv->mib_buf.data.byte = priv->pm_mode;
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (pm_mode) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+/* Set the association id for power save mode */
+static int at76_set_associd(struct at76_priv *priv, u16 id)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_MAC_MGMT;
+	priv->mib_buf.size = 2;
+	priv->mib_buf.index = offsetof(struct mib_mac_mgmt, station_id);
+	priv->mib_buf.data.word = cpu_to_le16(id);
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (associd) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+/* Set the listen interval for power save mode */
+static int at76_set_listen_interval(struct at76_priv *priv, u16 interval)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_MAC;
+	priv->mib_buf.size = 2;
+	priv->mib_buf.index = offsetof(struct mib_mac, listen_interval);
+	priv->mib_buf.data.word = cpu_to_le16(interval);
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR
+		       "%s: set_mib (listen_interval) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+static int at76_set_preamble(struct at76_priv *priv, u8 type)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_LOCAL;
+	priv->mib_buf.size = 1;
+	priv->mib_buf.index = offsetof(struct mib_local, preamble_type);
+	priv->mib_buf.data.byte = type;
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (preamble) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+static int at76_set_frag(struct at76_priv *priv, u16 size)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_MAC;
+	priv->mib_buf.size = 2;
+	priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold);
+	priv->mib_buf.data.word = cpu_to_le16(size);
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (frag threshold) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+static int at76_set_rts(struct at76_priv *priv, u16 size)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_MAC;
+	priv->mib_buf.size = 2;
+	priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold);
+	priv->mib_buf.data.word = cpu_to_le16(size);
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (rts) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_LOCAL;
+	priv->mib_buf.size = 1;
+	priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback);
+	priv->mib_buf.data.byte = onoff;
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (autorate fallback) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+static int at76_add_mac_address(struct at76_priv *priv, void *addr)
+{
+	int ret = 0;
+
+	priv->mib_buf.type = MIB_MAC_ADDR;
+	priv->mib_buf.size = ETH_ALEN;
+	priv->mib_buf.index = offsetof(struct mib_mac_addr, mac_addr);
+	memcpy(priv->mib_buf.data.addr, addr, ETH_ALEN);
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (MAC_ADDR, mac_addr) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	return ret;
+}
+
+static void at76_dump_mib_mac_addr(struct at76_priv *priv)
+{
+	int i;
+	int ret;
+	struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr),
+					 GFP_KERNEL);
+
+	if (!m)
+		return;
+
+	ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m,
+			   sizeof(struct mib_mac_addr));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib (MAC_ADDR) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %s res 0x%x 0x%x",
+		 priv->netdev->name,
+		 mac2str(m->mac_addr), m->res[0], m->res[1]);
+	for (i = 0; i < ARRAY_SIZE(m->group_addr); i++)
+		at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %s, "
+			 "status %d", priv->netdev->name, i,
+			 mac2str(m->group_addr[i]), m->group_addr_status[i]);
+exit:
+	kfree(m);
+}
+
+static void at76_dump_mib_mac_wep(struct at76_priv *priv)
+{
+	int i;
+	int ret;
+	int key_len;
+	struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL);
+
+	if (!m)
+		return;
+
+	ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m,
+			   sizeof(struct mib_mac_wep));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib (MAC_WEP) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u "
+		 "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u "
+		 "encr_level %u key %d", priv->netdev->name,
+		 m->privacy_invoked, m->wep_default_key_id,
+		 m->wep_key_mapping_len, m->exclude_unencrypted,
+		 le32_to_cpu(m->wep_icv_error_count),
+		 le32_to_cpu(m->wep_excluded_count), m->encryption_level,
+		 m->wep_default_key_id);
+
+	key_len = (m->encryption_level == 1) ?
+	    WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN;
+
+	for (i = 0; i < WEP_KEYS; i++)
+		at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %s",
+			 priv->netdev->name, i,
+			 hex2str(m->wep_default_keyvalue[i], key_len));
+exit:
+	kfree(m);
+}
+
+static void at76_dump_mib_mac_mgmt(struct at76_priv *priv)
+{
+	int ret;
+	struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt),
+					 GFP_KERNEL);
+
+	if (!m)
+		return;
+
+	ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m,
+			   sizeof(struct mib_mac_mgmt));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib (MAC_MGMT) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration "
+		 "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d "
+		 "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d "
+		 "current_bssid %s current_essid %s current_bss_type %d "
+		 "pm_mode %d ibss_change %d res %d "
+		 "multi_domain_capability_implemented %d "
+		 "international_roaming %d country_string %.3s",
+		 priv->netdev->name, le16_to_cpu(m->beacon_period),
+		 le16_to_cpu(m->CFP_max_duration),
+		 le16_to_cpu(m->medium_occupancy_limit),
+		 le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window),
+		 m->CFP_mode, m->privacy_option_implemented, m->DTIM_period,
+		 m->CFP_period, mac2str(m->current_bssid),
+		 hex2str(m->current_essid, IW_ESSID_MAX_SIZE),
+		 m->current_bss_type, m->power_mgmt_mode, m->ibss_change,
+		 m->res, m->multi_domain_capability_implemented,
+		 m->multi_domain_capability_enabled, m->country_string);
+exit:
+	kfree(m);
+}
+
+static void at76_dump_mib_mac(struct at76_priv *priv)
+{
+	int ret;
+	struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL);
+
+	if (!m)
+		return;
+
+	ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib (MAC) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d "
+		 "max_rx_lifetime %d frag_threshold %d rts_threshold %d "
+		 "cwmin %d cwmax %d short_retry_time %d long_retry_time %d "
+		 "scan_type %d scan_channel %d probe_delay %u "
+		 "min_channel_time %d max_channel_time %d listen_int %d "
+		 "desired_ssid %s desired_bssid %s desired_bsstype %d",
+		 priv->netdev->name, le32_to_cpu(m->max_tx_msdu_lifetime),
+		 le32_to_cpu(m->max_rx_lifetime),
+		 le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold),
+		 le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax),
+		 m->short_retry_time, m->long_retry_time, m->scan_type,
+		 m->scan_channel, le16_to_cpu(m->probe_delay),
+		 le16_to_cpu(m->min_channel_time),
+		 le16_to_cpu(m->max_channel_time),
+		 le16_to_cpu(m->listen_interval),
+		 hex2str(m->desired_ssid, IW_ESSID_MAX_SIZE),
+		 mac2str(m->desired_bssid), m->desired_bsstype);
+exit:
+	kfree(m);
+}
+
+static void at76_dump_mib_phy(struct at76_priv *priv)
+{
+	int ret;
+	struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL);
+
+	if (!m)
+		return;
+
+	ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib (PHY) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d "
+		 "sifs_time %d preamble_length %d plcp_header_length %d "
+		 "mpdu_max_length %d cca_mode_supported %d operation_rate_set "
+		 "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d "
+		 "phy_type %d current_reg_domain %d",
+		 priv->netdev->name, le32_to_cpu(m->ed_threshold),
+		 le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time),
+		 le16_to_cpu(m->preamble_length),
+		 le16_to_cpu(m->plcp_header_length),
+		 le16_to_cpu(m->mpdu_max_length),
+		 le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0],
+		 m->operation_rate_set[1], m->operation_rate_set[2],
+		 m->operation_rate_set[3], m->channel_id, m->current_cca_mode,
+		 m->phy_type, m->current_reg_domain);
+exit:
+	kfree(m);
+}
+
+static void at76_dump_mib_local(struct at76_priv *priv)
+{
+	int ret;
+	struct mib_local *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL);
+
+	if (!m)
+		return;
+
+	ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(struct mib_local));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib (LOCAL) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d "
+		 "txautorate_fallback %d ssid_size %d promiscuous_mode %d "
+		 "preamble_type %d", priv->netdev->name, m->beacon_enable,
+		 m->txautorate_fallback, m->ssid_size, m->promiscuous_mode,
+		 m->preamble_type);
+exit:
+	kfree(m);
+}
+
+static void at76_dump_mib_mdomain(struct at76_priv *priv)
+{
+	int ret;
+	struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL);
+
+	if (!m)
+		return;
+
+	ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m,
+			   sizeof(struct mib_mdomain));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib (MDOMAIN) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %s",
+		 priv->netdev->name,
+		 hex2str(m->channel_list, sizeof(m->channel_list)));
+
+	at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %s",
+		 priv->netdev->name,
+		 hex2str(m->tx_powerlevel, sizeof(m->tx_powerlevel)));
+exit:
+	kfree(m);
+}
+
+static int at76_get_current_bssid(struct at76_priv *priv)
+{
+	int ret = 0;
+	struct mib_mac_mgmt *mac_mgmt =
+	    kmalloc(sizeof(struct mib_mac_mgmt), GFP_KERNEL);
+
+	if (!mac_mgmt) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, mac_mgmt,
+			   sizeof(struct mib_mac_mgmt));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib failed: %d\n",
+		       priv->netdev->name, ret);
+		goto error;
+	}
+	memcpy(priv->bssid, mac_mgmt->current_bssid, ETH_ALEN);
+	printk(KERN_INFO "%s: using BSSID %s\n", priv->netdev->name,
+	       mac2str(priv->bssid));
+error:
+	kfree(mac_mgmt);
+exit:
+	return ret;
+}
+
+static int at76_get_current_channel(struct at76_priv *priv)
+{
+	int ret = 0;
+	struct mib_phy *phy = kmalloc(sizeof(struct mib_phy), GFP_KERNEL);
+
+	if (!phy) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+	ret = at76_get_mib(priv->udev, MIB_PHY, phy, sizeof(struct mib_phy));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib(MIB_PHY) failed: %d\n",
+		       priv->netdev->name, ret);
+		goto error;
+	}
+	priv->channel = phy->channel_id;
+error:
+	kfree(phy);
+exit:
+	return ret;
+}
+
+/**
+ * at76_start_scan - start a scan
+ *
+ * @use_essid - use the configured ESSID in non passive mode
+ */
+static int at76_start_scan(struct at76_priv *priv, int use_essid)
+{
+	struct at76_req_scan scan;
+
+	memset(&scan, 0, sizeof(struct at76_req_scan));
+	memset(scan.bssid, 0xff, ETH_ALEN);
+
+	if (use_essid) {
+		memcpy(scan.essid, priv->essid, IW_ESSID_MAX_SIZE);
+		scan.essid_size = priv->essid_size;
+	} else
+		scan.essid_size = 0;
+
+	/* jal: why should we start at a certain channel? we do scan the whole
+	   range allowed by reg domain. */
+	scan.channel = priv->channel;
+
+	/* atmelwlandriver differs between scan type 0 and 1 (active/passive)
+	   For ad-hoc mode, it uses type 0 only. */
+	scan.scan_type = priv->scan_mode;
+
+	/* INFO: For probe_delay, not multiplying by 1024 as this will be
+	   slightly less than min_channel_time
+	   (per spec: probe delay < min. channel time) */
+	scan.min_channel_time = cpu_to_le16(priv->scan_min_time);
+	scan.max_channel_time = cpu_to_le16(priv->scan_max_time);
+	scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000);
+	scan.international_scan = 0;
+
+	/* other values are set to 0 for type 0 */
+
+	at76_dbg(DBG_PROGRESS, "%s: start_scan (use_essid = %d, intl = %d, "
+		 "channel = %d, probe_delay = %d, scan_min_time = %d, "
+		 "scan_max_time = %d)",
+		 priv->netdev->name, use_essid,
+		 scan.international_scan, scan.channel,
+		 le16_to_cpu(scan.probe_delay),
+		 le16_to_cpu(scan.min_channel_time),
+		 le16_to_cpu(scan.max_channel_time));
+
+	return at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan));
+}
+
+/* Enable monitor mode */
+static int at76_start_monitor(struct at76_priv *priv)
+{
+	struct at76_req_scan scan;
+	int ret;
+
+	memset(&scan, 0, sizeof(struct at76_req_scan));
+	memset(scan.bssid, 0xff, ETH_ALEN);
+
+	scan.channel = priv->channel;
+	scan.scan_type = SCAN_TYPE_PASSIVE;
+	scan.international_scan = 0;
+
+	ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan));
+	if (ret >= 0)
+		ret = at76_get_cmd_status(priv->udev, CMD_SCAN);
+
+	return ret;
+}
+
+static int at76_start_ibss(struct at76_priv *priv)
+{
+	struct at76_req_ibss bss;
+	int ret;
+
+	WARN_ON(priv->mac_state != MAC_OWN_IBSS);
+	if (priv->mac_state != MAC_OWN_IBSS)
+		return -EBUSY;
+
+	memset(&bss, 0, sizeof(struct at76_req_ibss));
+	memset(bss.bssid, 0xff, ETH_ALEN);
+	memcpy(bss.essid, priv->essid, IW_ESSID_MAX_SIZE);
+	bss.essid_size = priv->essid_size;
+	bss.bss_type = ADHOC_MODE;
+	bss.channel = priv->channel;
+
+	ret = at76_set_card_command(priv->udev, CMD_START_IBSS, &bss,
+				    sizeof(struct at76_req_ibss));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: start_ibss failed: %d\n",
+		       priv->netdev->name, ret);
+		return ret;
+	}
+
+	ret = at76_wait_completion(priv, CMD_START_IBSS);
+	if (ret != CMD_STATUS_COMPLETE) {
+		printk(KERN_ERR "%s: start_ibss failed to complete, %d\n",
+		       priv->netdev->name, ret);
+		return ret;
+	}
+
+	ret = at76_get_current_bssid(priv);
+	if (ret < 0)
+		return ret;
+
+	ret = at76_get_current_channel(priv);
+	if (ret < 0)
+		return ret;
+
+	/* not sure what this is good for ??? */
+	priv->mib_buf.type = MIB_MAC_MGMT;
+	priv->mib_buf.size = 1;
+	priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change);
+	priv->mib_buf.data.byte = 0;
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n",
+		       priv->netdev->name, ret);
+		return ret;
+	}
+
+	netif_carrier_on(priv->netdev);
+	netif_start_queue(priv->netdev);
+	return 0;
+}
+
+/* Request card to join BSS in managed or ad-hoc mode */
+static int at76_join_bss(struct at76_priv *priv, struct bss_info *ptr)
+{
+	struct at76_req_join join;
+
+	BUG_ON(!ptr);
+
+	memset(&join, 0, sizeof(struct at76_req_join));
+	memcpy(join.bssid, ptr->bssid, ETH_ALEN);
+	memcpy(join.essid, ptr->ssid, ptr->ssid_len);
+	join.essid_size = ptr->ssid_len;
+	join.bss_type = (priv->iw_mode == IW_MODE_ADHOC ? 1 : 2);
+	join.channel = ptr->channel;
+	join.timeout = cpu_to_le16(2000);
+
+	at76_dbg(DBG_PROGRESS,
+		 "%s join addr %s ssid %s type %d ch %d timeout %d",
+		 priv->netdev->name, mac2str(join.bssid), join.essid,
+		 join.bss_type, join.channel, le16_to_cpu(join.timeout));
+	return at76_set_card_command(priv->udev, CMD_JOIN, &join,
+				     sizeof(struct at76_req_join));
+}
+
+/* Calculate padding from txbuf->wlength (which excludes the USB TX header),
+   likely to compensate a flaw in the AT76C503A USB part ... */
+static inline int at76_calc_padding(int wlen)
+{
+	/* add the USB TX header */
+	wlen += AT76_TX_HDRLEN;
+
+	wlen = wlen % 64;
+
+	if (wlen < 50)
+		return 50 - wlen;
+
+	if (wlen >= 61)
+		return 64 + 50 - wlen;
+
+	return 0;
+}
+
+/* We are doing a lot of things here in an interrupt. Need
+   a bh handler (Watching TV with a TV card is probably
+   a good test: if you see flickers, we are doing too much.
+   Currently I do see flickers... even with our tasklet :-( )
+   Maybe because the bttv driver and usb-uhci use the same interrupt
+*/
+/* Or maybe because our BH handler is preempting bttv's BH handler.. BHs don't
+ * solve everything.. (alex) */
+static void at76_rx_callback(struct urb *urb)
+{
+	struct at76_priv *priv = urb->context;
+
+	priv->rx_tasklet.data = (unsigned long)urb;
+	tasklet_schedule(&priv->rx_tasklet);
+	return;
+}
+
+static void at76_tx_callback(struct urb *urb)
+{
+	struct at76_priv *priv = urb->context;
+	struct net_device_stats *stats = &priv->stats;
+	unsigned long flags;
+	struct at76_tx_buffer *mgmt_buf;
+	int ret;
+
+	switch (urb->status) {
+	case 0:
+		stats->tx_packets++;
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+		/* urb has been unlinked */
+		return;
+	default:
+		at76_dbg(DBG_URB, "%s - nonzero tx status received: %d",
+			 __func__, urb->status);
+		stats->tx_errors++;
+		break;
+	}
+
+	spin_lock_irqsave(&priv->mgmt_spinlock, flags);
+	mgmt_buf = priv->next_mgmt_bulk;
+	priv->next_mgmt_bulk = NULL;
+	spin_unlock_irqrestore(&priv->mgmt_spinlock, flags);
+
+	if (!mgmt_buf) {
+		netif_wake_queue(priv->netdev);
+		return;
+	}
+
+	/* we don't copy the padding bytes, but add them
+	   to the length */
+	memcpy(priv->bulk_out_buffer, mgmt_buf,
+	       le16_to_cpu(mgmt_buf->wlength) + AT76_TX_HDRLEN);
+	usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe,
+			  priv->bulk_out_buffer,
+			  le16_to_cpu(mgmt_buf->wlength) + mgmt_buf->padding +
+			  AT76_TX_HDRLEN, at76_tx_callback, priv);
+	ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC);
+	if (ret)
+		printk(KERN_ERR "%s: error in tx submit urb: %d\n",
+		       priv->netdev->name, ret);
+
+	kfree(mgmt_buf);
+}
+
+/* Send a management frame on bulk-out.  txbuf->wlength must be set */
+static int at76_tx_mgmt(struct at76_priv *priv, struct at76_tx_buffer *txbuf)
+{
+	unsigned long flags;
+	int ret;
+	int urb_status;
+	void *oldbuf = NULL;
+
+	netif_carrier_off(priv->netdev);	/* stop netdev watchdog */
+	netif_stop_queue(priv->netdev);	/* stop tx data packets */
+
+	spin_lock_irqsave(&priv->mgmt_spinlock, flags);
+
+	urb_status = priv->tx_urb->status;
+	if (urb_status == -EINPROGRESS) {
+		/* cannot transmit now, put in the queue */
+		oldbuf = priv->next_mgmt_bulk;
+		priv->next_mgmt_bulk = txbuf;
+	}
+	spin_unlock_irqrestore(&priv->mgmt_spinlock, flags);
+
+	if (oldbuf) {
+		/* a data/mgmt tx is already pending in the URB -
+		   if this is no error in some situations we must
+		   implement a queue or silently modify the old msg */
+		printk(KERN_ERR "%s: removed pending mgmt buffer %s\n",
+		       priv->netdev->name, hex2str(oldbuf, 64));
+		kfree(oldbuf);
+		return 0;
+	}
+
+	txbuf->tx_rate = TX_RATE_1MBIT;
+	txbuf->padding = at76_calc_padding(le16_to_cpu(txbuf->wlength));
+	memset(txbuf->reserved, 0, sizeof(txbuf->reserved));
+
+	if (priv->next_mgmt_bulk)
+		printk(KERN_ERR "%s: URB status %d, but mgmt is pending\n",
+		       priv->netdev->name, urb_status);
+
+	at76_dbg(DBG_TX_MGMT,
+		 "%s: tx mgmt: wlen %d tx_rate %d pad %d %s",
+		 priv->netdev->name, le16_to_cpu(txbuf->wlength),
+		 txbuf->tx_rate, txbuf->padding,
+		 hex2str(txbuf->packet, le16_to_cpu(txbuf->wlength)));
+
+	/* txbuf was not consumed above -> send mgmt msg immediately */
+	memcpy(priv->bulk_out_buffer, txbuf,
+	       le16_to_cpu(txbuf->wlength) + AT76_TX_HDRLEN);
+	usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe,
+			  priv->bulk_out_buffer,
+			  le16_to_cpu(txbuf->wlength) + txbuf->padding +
+			  AT76_TX_HDRLEN, at76_tx_callback, priv);
+	ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC);
+	if (ret)
+		printk(KERN_ERR "%s: error in tx submit urb: %d\n",
+		       priv->netdev->name, ret);
+
+	kfree(txbuf);
+
+	return ret;
+}
+
+/* Go to the next information element */
+static inline void next_ie(struct ieee80211_info_element **ie)
+{
+	*ie = (struct ieee80211_info_element *)(&(*ie)->data[(*ie)->len]);
+}
+
+/* Challenge is the challenge string (in TLV format)
+   we got with seq_nr 2 for shared secret authentication only and
+   send in seq_nr 3 WEP encrypted to prove we have the correct WEP key;
+   otherwise it is NULL */
+static int at76_auth_req(struct at76_priv *priv, struct bss_info *bss,
+			 int seq_nr, struct ieee80211_info_element *challenge)
+{
+	struct at76_tx_buffer *tx_buffer;
+	struct ieee80211_hdr_3addr *mgmt;
+	struct ieee80211_auth *req;
+	int buf_len = (seq_nr != 3 ? AUTH_FRAME_SIZE :
+		       AUTH_FRAME_SIZE + 1 + 1 + challenge->len);
+
+	BUG_ON(!bss);
+	BUG_ON(seq_nr == 3 && !challenge);
+	tx_buffer = kmalloc(buf_len + MAX_PADDING_SIZE, GFP_ATOMIC);
+	if (!tx_buffer)
+		return -ENOMEM;
+
+	req = (struct ieee80211_auth *)tx_buffer->packet;
+	mgmt = &req->header;
+
+	/* make wireless header */
+	/* first auth msg is not encrypted, only the second (seq_nr == 3) */
+	mgmt->frame_ctl =
+	    cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH |
+			(seq_nr == 3 ? IEEE80211_FCTL_PROTECTED : 0));
+
+	mgmt->duration_id = cpu_to_le16(0x8000);
+	memcpy(mgmt->addr1, bss->bssid, ETH_ALEN);
+	memcpy(mgmt->addr2, priv->netdev->dev_addr, ETH_ALEN);
+	memcpy(mgmt->addr3, bss->bssid, ETH_ALEN);
+	mgmt->seq_ctl = cpu_to_le16(0);
+
+	req->algorithm = cpu_to_le16(priv->auth_mode);
+	req->transaction = cpu_to_le16(seq_nr);
+	req->status = cpu_to_le16(0);
+
+	if (seq_nr == 3)
+		memcpy(req->info_element, challenge, 1 + 1 + challenge->len);
+
+	/* init. at76_priv tx header */
+	tx_buffer->wlength = cpu_to_le16(buf_len - AT76_TX_HDRLEN);
+	at76_dbg(DBG_TX_MGMT, "%s: AuthReq bssid %s alg %d seq_nr %d",
+		 priv->netdev->name, mac2str(mgmt->addr3),
+		 le16_to_cpu(req->algorithm), le16_to_cpu(req->transaction));
+	if (seq_nr == 3)
+		at76_dbg(DBG_TX_MGMT, "%s: AuthReq challenge: %s ...",
+			 priv->netdev->name, hex2str(req->info_element, 18));
+
+	/* either send immediately (if no data tx is pending
+	   or put it in pending list */
+	return at76_tx_mgmt(priv, tx_buffer);
+}
+
+static int at76_assoc_req(struct at76_priv *priv, struct bss_info *bss)
+{
+	struct at76_tx_buffer *tx_buffer;
+	struct ieee80211_hdr_3addr *mgmt;
+	struct ieee80211_assoc_request *req;
+	struct ieee80211_info_element *ie;
+	char *essid;
+	int essid_len;
+	u16 capa;
+
+	BUG_ON(!bss);
+
+	tx_buffer = kmalloc(ASSOCREQ_MAX_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC);
+	if (!tx_buffer)
+		return -ENOMEM;
+
+	req = (struct ieee80211_assoc_request *)tx_buffer->packet;
+	mgmt = &req->header;
+	ie = req->info_element;
+
+	/* make wireless header */
+	mgmt->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+				      IEEE80211_STYPE_ASSOC_REQ);
+
+	mgmt->duration_id = cpu_to_le16(0x8000);
+	memcpy(mgmt->addr1, bss->bssid, ETH_ALEN);
+	memcpy(mgmt->addr2, priv->netdev->dev_addr, ETH_ALEN);
+	memcpy(mgmt->addr3, bss->bssid, ETH_ALEN);
+	mgmt->seq_ctl = cpu_to_le16(0);
+
+	/* we must set the Privacy bit in the capabilities to assure an
+	   Agere-based AP with optional WEP transmits encrypted frames
+	   to us.  AP only set the Privacy bit in their capabilities
+	   if WEP is mandatory in the BSS! */
+	capa = bss->capa;
+	if (priv->wep_enabled)
+		capa |= WLAN_CAPABILITY_PRIVACY;
+	if (priv->preamble_type != PREAMBLE_TYPE_LONG)
+		capa |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+	req->capability = cpu_to_le16(capa);
+
+	req->listen_interval = cpu_to_le16(2 * bss->beacon_interval);
+
+	/* write TLV data elements */
+
+	ie->id = MFIE_TYPE_SSID;
+	ie->len = bss->ssid_len;
+	memcpy(ie->data, bss->ssid, bss->ssid_len);
+	next_ie(&ie);
+
+	ie->id = MFIE_TYPE_RATES;
+	ie->len = sizeof(hw_rates);
+	memcpy(ie->data, hw_rates, sizeof(hw_rates));
+	next_ie(&ie);		/* ie points behind the supp_rates field */
+
+	/* init. at76_priv tx header */
+	tx_buffer->wlength = cpu_to_le16((u8 *)ie - (u8 *)mgmt);
+
+	ie = req->info_element;
+	essid = ie->data;
+	essid_len = min_t(int, IW_ESSID_MAX_SIZE, ie->len);
+
+	next_ie(&ie);		/* points to IE of rates now */
+	at76_dbg(DBG_TX_MGMT,
+		 "%s: AssocReq bssid %s capa 0x%04x ssid %.*s rates %s",
+		 priv->netdev->name, mac2str(mgmt->addr3),
+		 le16_to_cpu(req->capability), essid_len, essid,
+		 hex2str(ie->data, ie->len));
+
+	/* either send immediately (if no data tx is pending
+	   or put it in pending list */
+	return at76_tx_mgmt(priv, tx_buffer);
+}
+
+/* We got to check the bss_list for old entries */
+static void at76_bss_list_timeout(unsigned long par)
+{
+	struct at76_priv *priv = (struct at76_priv *)par;
+	unsigned long flags;
+	struct list_head *lptr, *nptr;
+	struct bss_info *ptr;
+
+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
+
+	list_for_each_safe(lptr, nptr, &priv->bss_list) {
+
+		ptr = list_entry(lptr, struct bss_info, list);
+
+		if (ptr != priv->curr_bss
+		    && time_after(jiffies, ptr->last_rx + BSS_LIST_TIMEOUT)) {
+			at76_dbg(DBG_BSS_TABLE_RM,
+				 "%s: bss_list: removing old BSS %s ch %d",
+				 priv->netdev->name, mac2str(ptr->bssid),
+				 ptr->channel);
+			list_del(&ptr->list);
+			kfree(ptr);
+		}
+	}
+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
+	/* restart the timer */
+	mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT);
+}
+
+static inline void at76_set_mac_state(struct at76_priv *priv,
+				      enum mac_state mac_state)
+{
+	at76_dbg(DBG_MAC_STATE, "%s state: %s", priv->netdev->name,
+		 mac_states[mac_state]);
+	priv->mac_state = mac_state;
+}
+
+static void at76_dump_bss_table(struct at76_priv *priv)
+{
+	struct bss_info *ptr;
+	unsigned long flags;
+	struct list_head *lptr;
+
+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
+
+	at76_dbg(DBG_BSS_TABLE, "%s BSS table (curr=%p):", priv->netdev->name,
+		 priv->curr_bss);
+
+	list_for_each(lptr, &priv->bss_list) {
+		ptr = list_entry(lptr, struct bss_info, list);
+		at76_dbg(DBG_BSS_TABLE, "0x%p: bssid %s channel %d ssid %.*s "
+			 "(%s) capa 0x%04x rates %s rssi %d link %d noise %d",
+			 ptr, mac2str(ptr->bssid), ptr->channel, ptr->ssid_len,
+			 ptr->ssid, hex2str(ptr->ssid, ptr->ssid_len),
+			 ptr->capa, hex2str(ptr->rates, ptr->rates_len),
+			 ptr->rssi, ptr->link_qual, ptr->noise_level);
+	}
+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
+}
+
+/* Called upon successful association to mark interface as connected */
+static void at76_work_assoc_done(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      work_assoc_done);
+
+	mutex_lock(&priv->mtx);
+
+	WARN_ON(priv->mac_state != MAC_ASSOC);
+	WARN_ON(!priv->curr_bss);
+	if (priv->mac_state != MAC_ASSOC || !priv->curr_bss)
+		goto exit;
+
+	if (priv->iw_mode == IW_MODE_INFRA) {
+		if (priv->pm_mode != AT76_PM_OFF) {
+			/* calculate the listen interval in units of
+			   beacon intervals of the curr_bss */
+			u32 pm_period_beacon = (priv->pm_period >> 10) /
+			    priv->curr_bss->beacon_interval;
+
+			pm_period_beacon = max(pm_period_beacon, 2u);
+			pm_period_beacon = min(pm_period_beacon, 0xffffu);
+
+			at76_dbg(DBG_PM,
+				 "%s: pm_mode %d assoc id 0x%x listen int %d",
+				 priv->netdev->name, priv->pm_mode,
+				 priv->assoc_id, pm_period_beacon);
+
+			at76_set_associd(priv, priv->assoc_id);
+			at76_set_listen_interval(priv, (u16)pm_period_beacon);
+		}
+		schedule_delayed_work(&priv->dwork_beacon, BEACON_TIMEOUT);
+	}
+	at76_set_pm_mode(priv);
+
+	netif_carrier_on(priv->netdev);
+	netif_wake_queue(priv->netdev);
+	at76_set_mac_state(priv, MAC_CONNECTED);
+	at76_iwevent_bss_connect(priv->netdev, priv->curr_bss->bssid);
+	at76_dbg(DBG_PROGRESS, "%s: connected to BSSID %s",
+		 priv->netdev->name, mac2str(priv->curr_bss->bssid));
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+/* We only store the new mac address in netdev struct,
+   it gets set when the netdev is opened. */
+static int at76_set_mac_address(struct net_device *netdev, void *addr)
+{
+	struct sockaddr *mac = addr;
+	memcpy(netdev->dev_addr, mac->sa_data, ETH_ALEN);
+	return 1;
+}
+
+static struct net_device_stats *at76_get_stats(struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	return &priv->stats;
+}
+
+static struct iw_statistics *at76_get_wireless_stats(struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_IOCTL, "RETURN qual %d level %d noise %d updated %d",
+		 priv->wstats.qual.qual, priv->wstats.qual.level,
+		 priv->wstats.qual.noise, priv->wstats.qual.updated);
+
+	return &priv->wstats;
+}
+
+static void at76_set_multicast(struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int promisc;
+
+	promisc = ((netdev->flags & IFF_PROMISC) != 0);
+	if (promisc != priv->promisc) {
+		/* This gets called in interrupt, must reschedule */
+		priv->promisc = promisc;
+		schedule_work(&priv->work_set_promisc);
+	}
+}
+
+/* Stop all network activity, flush all pending tasks */
+static void at76_quiesce(struct at76_priv *priv)
+{
+	unsigned long flags;
+
+	netif_stop_queue(priv->netdev);
+	netif_carrier_off(priv->netdev);
+
+	at76_set_mac_state(priv, MAC_INIT);
+
+	cancel_delayed_work(&priv->dwork_get_scan);
+	cancel_delayed_work(&priv->dwork_beacon);
+	cancel_delayed_work(&priv->dwork_auth);
+	cancel_delayed_work(&priv->dwork_assoc);
+	cancel_delayed_work(&priv->dwork_restart);
+
+	spin_lock_irqsave(&priv->mgmt_spinlock, flags);
+	kfree(priv->next_mgmt_bulk);
+	priv->next_mgmt_bulk = NULL;
+	spin_unlock_irqrestore(&priv->mgmt_spinlock, flags);
+}
+
+/*******************************************************************************
+ * at76_priv implementations of iw_handler functions:
+ */
+static int at76_iw_handler_commit(struct net_device *netdev,
+				  struct iw_request_info *info,
+				  void *null, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_IOCTL, "%s %s: restarting the device", netdev->name,
+		 __func__);
+
+	if (priv->mac_state != MAC_INIT)
+		at76_quiesce(priv);
+
+	/* Wait half second before the restart to process subsequent
+	 * requests from the same iwconfig in a single restart */
+	schedule_delayed_work(&priv->dwork_restart, HZ / 2);
+
+	return 0;
+}
+
+static int at76_iw_handler_get_name(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    char *name, char *extra)
+{
+	strcpy(name, "IEEE 802.11b");
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWNAME - name %s", netdev->name, name);
+	return 0;
+}
+
+static int at76_iw_handler_set_freq(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    struct iw_freq *freq, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int chan = -1;
+	int ret = -EIWCOMMIT;
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - freq.m %d freq.e %d",
+		 netdev->name, freq->m, freq->e);
+
+	if ((freq->e == 0) && (freq->m <= 1000))
+		/* Setting by channel number */
+		chan = freq->m;
+	else {
+		/* Setting by frequency - search the table */
+		int mult = 1;
+		int i;
+
+		for (i = 0; i < (6 - freq->e); i++)
+			mult *= 10;
+
+		for (i = 0; i < NUM_CHANNELS; i++) {
+			if (freq->m == (channel_frequency[i] * mult))
+				chan = i + 1;
+		}
+	}
+
+	if (chan < 1 || !priv->domain)
+		/* non-positive channels are invalid
+		 * we need a domain info to set the channel
+		 * either that or an invalid frequency was
+		 * provided by the user */
+		ret = -EINVAL;
+	else if (!(priv->domain->channel_map & (1 << (chan - 1)))) {
+		printk(KERN_INFO "%s: channel %d not allowed for domain %s\n",
+		       priv->netdev->name, chan, priv->domain->name);
+		ret = -EINVAL;
+	}
+
+	if (ret == -EIWCOMMIT) {
+		priv->channel = chan;
+		at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - ch %d", netdev->name,
+			 chan);
+	}
+
+	return ret;
+}
+
+static int at76_iw_handler_get_freq(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    struct iw_freq *freq, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	freq->m = priv->channel;
+	freq->e = 0;
+
+	if (priv->channel)
+		at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - freq %ld x 10e%d",
+			 netdev->name, channel_frequency[priv->channel - 1], 6);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - ch %d", netdev->name,
+		 priv->channel);
+
+	return 0;
+}
+
+static int at76_iw_handler_set_mode(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    __u32 *mode, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWMODE - %d", netdev->name, *mode);
+
+	if ((*mode != IW_MODE_ADHOC) && (*mode != IW_MODE_INFRA) &&
+	    (*mode != IW_MODE_MONITOR))
+		return -EINVAL;
+
+	priv->iw_mode = *mode;
+	if (priv->iw_mode != IW_MODE_INFRA)
+		priv->pm_mode = AT76_PM_OFF;
+
+	return -EIWCOMMIT;
+}
+
+static int at76_iw_handler_get_mode(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    __u32 *mode, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	*mode = priv->iw_mode;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWMODE - %d", netdev->name, *mode);
+
+	return 0;
+}
+
+static int at76_iw_handler_get_range(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_point *data, char *extra)
+{
+	/* inspired by atmel.c */
+	struct at76_priv *priv = netdev_priv(netdev);
+	struct iw_range *range = (struct iw_range *)extra;
+	int i;
+
+	data->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(struct iw_range));
+
+	/* TODO: range->throughput = xxxxxx; */
+
+	range->min_nwid = 0x0000;
+	range->max_nwid = 0x0000;
+
+	/* this driver doesn't maintain sensitivity information */
+	range->sensitivity = 0;
+
+	range->max_qual.qual = 100;
+	range->max_qual.level = 100;
+	range->max_qual.noise = 0;
+	range->max_qual.updated = IW_QUAL_NOISE_INVALID;
+
+	range->avg_qual.qual = 50;
+	range->avg_qual.level = 50;
+	range->avg_qual.noise = 0;
+	range->avg_qual.updated = IW_QUAL_NOISE_INVALID;
+
+	range->bitrate[0] = 1000000;
+	range->bitrate[1] = 2000000;
+	range->bitrate[2] = 5500000;
+	range->bitrate[3] = 11000000;
+	range->num_bitrates = 4;
+
+	range->min_rts = 0;
+	range->max_rts = MAX_RTS_THRESHOLD;
+
+	range->min_frag = MIN_FRAG_THRESHOLD;
+	range->max_frag = MAX_FRAG_THRESHOLD;
+
+	range->pmp_flags = IW_POWER_PERIOD;
+	range->pmt_flags = IW_POWER_ON;
+	range->pm_capa = IW_POWER_PERIOD | IW_POWER_ALL_R;
+
+	range->encoding_size[0] = WEP_SMALL_KEY_LEN;
+	range->encoding_size[1] = WEP_LARGE_KEY_LEN;
+	range->num_encoding_sizes = 2;
+	range->max_encoding_tokens = WEP_KEYS;
+
+	/* both WL-240U and Linksys WUSB11 v2.6 specify 15 dBm as output power
+	   - take this for all (ignore antenna gains) */
+	range->txpower[0] = 15;
+	range->num_txpower = 1;
+	range->txpower_capa = IW_TXPOW_DBM;
+
+	range->we_version_source = WIRELESS_EXT;
+	range->we_version_compiled = WIRELESS_EXT;
+
+	/* same as the values used in atmel.c */
+	range->retry_capa = IW_RETRY_LIMIT;
+	range->retry_flags = IW_RETRY_LIMIT;
+	range->r_time_flags = 0;
+	range->min_retry = 1;
+	range->max_retry = 255;
+
+	range->num_channels = NUM_CHANNELS;
+	range->num_frequency = 0;
+
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		/* test if channel map bit is raised */
+		if (priv->domain->channel_map & (0x1 << i)) {
+			range->num_frequency += 1;
+
+			range->freq[i].i = i + 1;
+			range->freq[i].m = channel_frequency[i] * 100000;
+			range->freq[i].e = 1;	/* freq * 10^1 */
+		}
+	}
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWRANGE", netdev->name);
+
+	return 0;
+}
+
+static int at76_iw_handler_set_spy(struct net_device *netdev,
+				   struct iw_request_info *info,
+				   struct iw_point *data, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = 0;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWSPY - number of addresses %d",
+		 netdev->name, data->length);
+
+	spin_lock_bh(&priv->spy_spinlock);
+	ret = iw_handler_set_spy(priv->netdev, info, (union iwreq_data *)data,
+				 extra);
+	spin_unlock_bh(&priv->spy_spinlock);
+
+	return ret;
+}
+
+static int at76_iw_handler_get_spy(struct net_device *netdev,
+				   struct iw_request_info *info,
+				   struct iw_point *data, char *extra)
+{
+
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = 0;
+
+	spin_lock_bh(&priv->spy_spinlock);
+	ret = iw_handler_get_spy(priv->netdev, info,
+				 (union iwreq_data *)data, extra);
+	spin_unlock_bh(&priv->spy_spinlock);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWSPY - number of addresses %d",
+		 netdev->name, data->length);
+
+	return ret;
+}
+
+static int at76_iw_handler_set_thrspy(struct net_device *netdev,
+				      struct iw_request_info *info,
+				      struct iw_point *data, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWTHRSPY - number of addresses %d)",
+		 netdev->name, data->length);
+
+	spin_lock_bh(&priv->spy_spinlock);
+	ret = iw_handler_set_thrspy(netdev, info, (union iwreq_data *)data,
+				    extra);
+	spin_unlock_bh(&priv->spy_spinlock);
+
+	return ret;
+}
+
+static int at76_iw_handler_get_thrspy(struct net_device *netdev,
+				      struct iw_request_info *info,
+				      struct iw_point *data, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret;
+
+	spin_lock_bh(&priv->spy_spinlock);
+	ret = iw_handler_get_thrspy(netdev, info, (union iwreq_data *)data,
+				    extra);
+	spin_unlock_bh(&priv->spy_spinlock);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWTHRSPY - number of addresses %d)",
+		 netdev->name, data->length);
+
+	return ret;
+}
+
+static int at76_iw_handler_set_wap(struct net_device *netdev,
+				   struct iw_request_info *info,
+				   struct sockaddr *ap_addr, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWAP - wap/bssid %s", netdev->name,
+		 mac2str(ap_addr->sa_data));
+
+	/* if the incoming address == ff:ff:ff:ff:ff:ff, the user has
+	   chosen any or auto AP preference */
+	if (is_broadcast_ether_addr(ap_addr->sa_data)
+	    || is_zero_ether_addr(ap_addr->sa_data))
+		priv->wanted_bssid_valid = 0;
+	else {
+		/* user wants to set a preferred AP address */
+		priv->wanted_bssid_valid = 1;
+		memcpy(priv->wanted_bssid, ap_addr->sa_data, ETH_ALEN);
+	}
+
+	return -EIWCOMMIT;
+}
+
+static int at76_iw_handler_get_wap(struct net_device *netdev,
+				   struct iw_request_info *info,
+				   struct sockaddr *ap_addr, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	ap_addr->sa_family = ARPHRD_ETHER;
+	memcpy(ap_addr->sa_data, priv->bssid, ETH_ALEN);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWAP - wap/bssid %s", netdev->name,
+		 mac2str(ap_addr->sa_data));
+
+	return 0;
+}
+
+static int at76_iw_handler_set_scan(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    union iwreq_data *wrqu, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = 0;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWSCAN", netdev->name);
+
+	if (mutex_lock_interruptible(&priv->mtx))
+		return -EINTR;
+
+	if (!netif_running(netdev)) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	/* jal: we don't allow "iwlist ethX scan" while we are
+	   in monitor mode */
+	if (priv->iw_mode == IW_MODE_MONITOR) {
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	/* Discard old scan results */
+	if ((jiffies - priv->last_scan) > (20 * HZ))
+		priv->scan_state = SCAN_IDLE;
+	priv->last_scan = jiffies;
+
+	/* Initiate a scan command */
+	if (priv->scan_state == SCAN_IN_PROGRESS) {
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	priv->scan_state = SCAN_IN_PROGRESS;
+
+	at76_quiesce(priv);
+
+	/* Try to do passive or active scan if WE asks as. */
+	if (wrqu->data.length
+	    && wrqu->data.length == sizeof(struct iw_scan_req)) {
+		struct iw_scan_req *req = (struct iw_scan_req *)extra;
+
+		if (req->scan_type == IW_SCAN_TYPE_PASSIVE)
+			priv->scan_mode = SCAN_TYPE_PASSIVE;
+		else if (req->scan_type == IW_SCAN_TYPE_ACTIVE)
+			priv->scan_mode = SCAN_TYPE_ACTIVE;
+
+		/* Sanity check values? */
+		if (req->min_channel_time > 0)
+			priv->scan_min_time = req->min_channel_time;
+
+		if (req->max_channel_time > 0)
+			priv->scan_max_time = req->max_channel_time;
+	}
+
+	/* change to scanning state */
+	at76_set_mac_state(priv, MAC_SCANNING);
+	schedule_work(&priv->work_start_scan);
+
+exit:
+	mutex_unlock(&priv->mtx);
+	return ret;
+}
+
+static int at76_iw_handler_get_scan(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    struct iw_point *data, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	unsigned long flags;
+	struct list_head *lptr, *nptr;
+	struct bss_info *curr_bss;
+	struct iw_event *iwe = kmalloc(sizeof(struct iw_event), GFP_KERNEL);
+	char *curr_val, *curr_pos = extra;
+	int i;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWSCAN", netdev->name);
+
+	if (!iwe)
+		return -ENOMEM;
+
+	if (priv->scan_state != SCAN_COMPLETED)
+		/* scan not yet finished */
+		return -EAGAIN;
+
+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
+
+	list_for_each_safe(lptr, nptr, &priv->bss_list) {
+		curr_bss = list_entry(lptr, struct bss_info, list);
+
+		iwe->cmd = SIOCGIWAP;
+		iwe->u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe->u.ap_addr.sa_data, curr_bss->bssid, 6);
+		curr_pos = iwe_stream_add_event(curr_pos,
+						extra + IW_SCAN_MAX_DATA, iwe,
+						IW_EV_ADDR_LEN);
+
+		iwe->u.data.length = curr_bss->ssid_len;
+		iwe->cmd = SIOCGIWESSID;
+		iwe->u.data.flags = 1;
+
+		curr_pos = iwe_stream_add_point(curr_pos,
+						extra + IW_SCAN_MAX_DATA, iwe,
+						curr_bss->ssid);
+
+		iwe->cmd = SIOCGIWMODE;
+		iwe->u.mode = (curr_bss->capa & WLAN_CAPABILITY_IBSS) ?
+		    IW_MODE_ADHOC :
+		    (curr_bss->capa & WLAN_CAPABILITY_ESS) ?
+		    IW_MODE_MASTER : IW_MODE_AUTO;
+		/* IW_MODE_AUTO = 0 which I thought is
+		 * the most logical value to return in this case */
+		curr_pos = iwe_stream_add_event(curr_pos,
+						extra + IW_SCAN_MAX_DATA, iwe,
+						IW_EV_UINT_LEN);
+
+		iwe->cmd = SIOCGIWFREQ;
+		iwe->u.freq.m = curr_bss->channel;
+		iwe->u.freq.e = 0;
+		curr_pos = iwe_stream_add_event(curr_pos,
+						extra + IW_SCAN_MAX_DATA, iwe,
+						IW_EV_FREQ_LEN);
+
+		iwe->cmd = SIOCGIWENCODE;
+		if (curr_bss->capa & 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;
+		curr_pos = iwe_stream_add_point(curr_pos,
+						extra + IW_SCAN_MAX_DATA, iwe,
+						NULL);
+
+		/* Add quality statistics */
+		iwe->cmd = IWEVQUAL;
+		iwe->u.qual.noise = 0;
+		iwe->u.qual.updated =
+		    IW_QUAL_NOISE_INVALID | IW_QUAL_LEVEL_UPDATED;
+		iwe->u.qual.level = (curr_bss->rssi * 100 / 42);
+		if (iwe->u.qual.level > 100)
+			iwe->u.qual.level = 100;
+		if (at76_is_intersil(priv->board_type))
+			iwe->u.qual.qual = curr_bss->link_qual;
+		else {
+			iwe->u.qual.qual = 0;
+			iwe->u.qual.updated |= IW_QUAL_QUAL_INVALID;
+		}
+		/* Add new value to event */
+		curr_pos = iwe_stream_add_event(curr_pos,
+						extra + IW_SCAN_MAX_DATA, iwe,
+						IW_EV_QUAL_LEN);
+
+		/* Rate: stuffing multiple values in a single event requires
+		 * a bit more of magic - Jean II */
+		curr_val = curr_pos + IW_EV_LCP_LEN;
+
+		iwe->cmd = SIOCGIWRATE;
+		/* Those two flags are ignored... */
+		iwe->u.bitrate.fixed = 0;
+		iwe->u.bitrate.disabled = 0;
+		/* Max 8 values */
+		for (i = 0; i < curr_bss->rates_len; i++) {
+			/* Bit rate given in 500 kb/s units (+ 0x80) */
+			iwe->u.bitrate.value =
+			    ((curr_bss->rates[i] & 0x7f) * 500000);
+			/* Add new value to event */
+			curr_val = iwe_stream_add_value(curr_pos, curr_val,
+							extra +
+							IW_SCAN_MAX_DATA, iwe,
+							IW_EV_PARAM_LEN);
+		}
+
+		/* Check if we added any event */
+		if ((curr_val - curr_pos) > IW_EV_LCP_LEN)
+			curr_pos = curr_val;
+
+		/* more information may be sent back using IWECUSTOM */
+
+	}
+
+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
+
+	data->length = (curr_pos - extra);
+	data->flags = 0;
+
+	kfree(iwe);
+	return 0;
+}
+
+static int at76_iw_handler_set_essid(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_point *data, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWESSID - %s", netdev->name, extra);
+
+	if (data->flags) {
+		memcpy(priv->essid, extra, data->length);
+		priv->essid_size = data->length;
+	} else
+		priv->essid_size = 0;	/* Use any SSID */
+
+	return -EIWCOMMIT;
+}
+
+static int at76_iw_handler_get_essid(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_point *data, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	if (priv->essid_size) {
+		/* not the ANY ssid in priv->essid */
+		data->flags = 1;
+		data->length = priv->essid_size;
+		memcpy(extra, priv->essid, data->length);
+	} else {
+		/* the ANY ssid was specified */
+		if (priv->mac_state == MAC_CONNECTED && priv->curr_bss) {
+			/* report the SSID we have found */
+			data->flags = 1;
+			data->length = priv->curr_bss->ssid_len;
+			memcpy(extra, priv->curr_bss->ssid, data->length);
+		} else {
+			/* report ANY back */
+			data->flags = 0;
+			data->length = 0;
+		}
+	}
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWESSID - %.*s", netdev->name,
+		 data->length, extra);
+
+	return 0;
+}
+
+static int at76_iw_handler_set_rate(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    struct iw_param *bitrate, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = -EIWCOMMIT;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWRATE - %d", netdev->name,
+		 bitrate->value);
+
+	switch (bitrate->value) {
+	case -1:
+		priv->txrate = TX_RATE_AUTO;
+		break;		/* auto rate */
+	case 1000000:
+		priv->txrate = TX_RATE_1MBIT;
+		break;
+	case 2000000:
+		priv->txrate = TX_RATE_2MBIT;
+		break;
+	case 5500000:
+		priv->txrate = TX_RATE_5_5MBIT;
+		break;
+	case 11000000:
+		priv->txrate = TX_RATE_11MBIT;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int at76_iw_handler_get_rate(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    struct iw_param *bitrate, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = 0;
+
+	switch (priv->txrate) {
+		/* return max rate if RATE_AUTO */
+	case TX_RATE_AUTO:
+		bitrate->value = 11000000;
+		break;
+	case TX_RATE_1MBIT:
+		bitrate->value = 1000000;
+		break;
+	case TX_RATE_2MBIT:
+		bitrate->value = 2000000;
+		break;
+	case TX_RATE_5_5MBIT:
+		bitrate->value = 5500000;
+		break;
+	case TX_RATE_11MBIT:
+		bitrate->value = 11000000;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	bitrate->fixed = (priv->txrate != TX_RATE_AUTO);
+	bitrate->disabled = 0;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWRATE - %d", netdev->name,
+		 bitrate->value);
+
+	return ret;
+}
+
+static int at76_iw_handler_set_rts(struct net_device *netdev,
+				   struct iw_request_info *info,
+				   struct iw_param *rts, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = -EIWCOMMIT;
+	int rthr = rts->value;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWRTS - value %d disabled %s",
+		 netdev->name, rts->value, (rts->disabled) ? "true" : "false");
+
+	if (rts->disabled)
+		rthr = MAX_RTS_THRESHOLD;
+
+	if ((rthr < 0) || (rthr > MAX_RTS_THRESHOLD))
+		ret = -EINVAL;
+	else
+		priv->rts_threshold = rthr;
+
+	return ret;
+}
+
+static int at76_iw_handler_get_rts(struct net_device *netdev,
+				   struct iw_request_info *info,
+				   struct iw_param *rts, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	rts->value = priv->rts_threshold;
+	rts->disabled = (rts->value >= MAX_RTS_THRESHOLD);
+	rts->fixed = 1;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWRTS - value %d disabled %s",
+		 netdev->name, rts->value, (rts->disabled) ? "true" : "false");
+
+	return 0;
+}
+
+static int at76_iw_handler_set_frag(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    struct iw_param *frag, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = -EIWCOMMIT;
+	int fthr = frag->value;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWFRAG - value %d, disabled %s",
+		 netdev->name, frag->value,
+		 (frag->disabled) ? "true" : "false");
+
+	if (frag->disabled)
+		fthr = MAX_FRAG_THRESHOLD;
+
+	if ((fthr < MIN_FRAG_THRESHOLD) || (fthr > MAX_FRAG_THRESHOLD))
+		ret = -EINVAL;
+	else
+		priv->frag_threshold = fthr & ~0x1;	/* get an even value */
+
+	return ret;
+}
+
+static int at76_iw_handler_get_frag(struct net_device *netdev,
+				    struct iw_request_info *info,
+				    struct iw_param *frag, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	frag->value = priv->frag_threshold;
+	frag->disabled = (frag->value >= MAX_FRAG_THRESHOLD);
+	frag->fixed = 1;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWFRAG - value %d, disabled %s",
+		 netdev->name, frag->value,
+		 (frag->disabled) ? "true" : "false");
+
+	return 0;
+}
+
+static int at76_iw_handler_get_txpow(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_param *power, char *extra)
+{
+	power->value = 15;
+	power->fixed = 1;	/* No power control */
+	power->disabled = 0;
+	power->flags = IW_TXPOW_DBM;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWTXPOW - txpow %d dBm", netdev->name,
+		 power->value);
+
+	return 0;
+}
+
+/* jal: short retry is handled by the firmware (at least 0.90.x),
+   while long retry is not (?) */
+static int at76_iw_handler_set_retry(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_param *retry, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = -EIWCOMMIT;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWRETRY disabled %d flags 0x%x val %d",
+		 netdev->name, retry->disabled, retry->flags, retry->value);
+
+	if (!retry->disabled && (retry->flags & IW_RETRY_LIMIT)) {
+		if ((retry->flags & IW_RETRY_MIN) ||
+		    !(retry->flags & IW_RETRY_MAX))
+			priv->short_retry_limit = retry->value;
+		else
+			ret = -EINVAL;
+	} else
+		ret = -EINVAL;
+
+	return ret;
+}
+
+/* Adapted (ripped) from atmel.c */
+static int at76_iw_handler_get_retry(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_param *retry, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWRETRY", netdev->name);
+
+	retry->disabled = 0;	/* Can't be disabled */
+	retry->flags = IW_RETRY_LIMIT;
+	retry->value = priv->short_retry_limit;
+
+	return 0;
+}
+
+static int at76_iw_handler_set_encode(struct net_device *netdev,
+				      struct iw_request_info *info,
+				      struct iw_point *encoding, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int index = (encoding->flags & IW_ENCODE_INDEX) - 1;
+	int len = encoding->length;
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - enc.flags %08x "
+		 "pointer %p len %d", netdev->name, encoding->flags,
+		 encoding->pointer, encoding->length);
+	at76_dbg(DBG_IOCTL,
+		 "%s: SIOCSIWENCODE - old wepstate: enabled %s key_id %d "
+		 "auth_mode %s", netdev->name,
+		 (priv->wep_enabled) ? "true" : "false", priv->wep_key_id,
+		 (priv->auth_mode ==
+		  WLAN_AUTH_SHARED_KEY) ? "restricted" : "open");
+
+	/* take the old default key if index is invalid */
+	if ((index < 0) || (index >= WEP_KEYS))
+		index = priv->wep_key_id;
+
+	if (len > 0) {
+		if (len > WEP_LARGE_KEY_LEN)
+			len = WEP_LARGE_KEY_LEN;
+
+		memset(priv->wep_keys[index], 0, WEP_KEY_LEN);
+		memcpy(priv->wep_keys[index], extra, len);
+		priv->wep_keys_len[index] = (len <= WEP_SMALL_KEY_LEN) ?
+		    WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN;
+		priv->wep_enabled = 1;
+	}
+
+	priv->wep_key_id = index;
+	priv->wep_enabled = ((encoding->flags & IW_ENCODE_DISABLED) == 0);
+
+	if (encoding->flags & IW_ENCODE_RESTRICTED)
+		priv->auth_mode = WLAN_AUTH_SHARED_KEY;
+	if (encoding->flags & IW_ENCODE_OPEN)
+		priv->auth_mode = WLAN_AUTH_OPEN;
+
+	at76_dbg(DBG_IOCTL,
+		 "%s: SIOCSIWENCODE - new wepstate: enabled %s key_id %d "
+		 "key_len %d auth_mode %s", netdev->name,
+		 (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1,
+		 priv->wep_keys_len[priv->wep_key_id],
+		 (priv->auth_mode ==
+		  WLAN_AUTH_SHARED_KEY) ? "restricted" : "open");
+
+	return -EIWCOMMIT;
+}
+
+static int at76_iw_handler_get_encode(struct net_device *netdev,
+				      struct iw_request_info *info,
+				      struct iw_point *encoding, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int index = (encoding->flags & IW_ENCODE_INDEX) - 1;
+
+	if ((index < 0) || (index >= WEP_KEYS))
+		index = priv->wep_key_id;
+
+	encoding->flags =
+	    (priv->auth_mode == WLAN_AUTH_SHARED_KEY) ?
+	    IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN;
+
+	if (!priv->wep_enabled)
+		encoding->flags |= IW_ENCODE_DISABLED;
+
+	if (encoding->pointer) {
+		encoding->length = priv->wep_keys_len[index];
+
+		memcpy(extra, priv->wep_keys[index], priv->wep_keys_len[index]);
+
+		encoding->flags |= (index + 1);
+	}
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x "
+		 "pointer %p len %d", netdev->name, encoding->flags,
+		 encoding->pointer, encoding->length);
+	at76_dbg(DBG_IOCTL,
+		 "%s: SIOCGIWENCODE - wepstate: enabled %s key_id %d "
+		 "key_len %d auth_mode %s", netdev->name,
+		 (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1,
+		 priv->wep_keys_len[priv->wep_key_id],
+		 (priv->auth_mode ==
+		  WLAN_AUTH_SHARED_KEY) ? "restricted" : "open");
+
+	return 0;
+}
+
+static int at76_iw_handler_set_power(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_param *prq, char *extra)
+{
+	int err = -EIWCOMMIT;
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_IOCTL,
+		 "%s: SIOCSIWPOWER - disabled %s flags 0x%x value 0x%x",
+		 netdev->name, (prq->disabled) ? "true" : "false", prq->flags,
+		 prq->value);
+
+	if (prq->disabled)
+		priv->pm_mode = AT76_PM_OFF;
+	else {
+		switch (prq->flags & IW_POWER_MODE) {
+		case IW_POWER_ALL_R:
+		case IW_POWER_ON:
+			break;
+		default:
+			err = -EINVAL;
+			goto exit;
+		}
+		if (prq->flags & IW_POWER_PERIOD)
+			priv->pm_period = prq->value;
+
+		if (prq->flags & IW_POWER_TIMEOUT) {
+			err = -EINVAL;
+			goto exit;
+		}
+		priv->pm_mode = AT76_PM_ON;
+	}
+exit:
+	return err;
+}
+
+static int at76_iw_handler_get_power(struct net_device *netdev,
+				     struct iw_request_info *info,
+				     struct iw_param *power, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	power->disabled = (priv->pm_mode == AT76_PM_OFF);
+	if (!power->disabled) {
+		power->flags = IW_POWER_PERIOD | IW_POWER_ALL_R;
+		power->value = priv->pm_period;
+	}
+
+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - %s flags 0x%x value 0x%x",
+		 netdev->name, power->disabled ? "disabled" : "enabled",
+		 power->flags, power->value);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * Private IOCTLS
+ */
+static int at76_iw_set_short_preamble(struct net_device *netdev,
+				      struct iw_request_info *info, char *name,
+				      char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int val = *((int *)name);
+	int ret = -EIWCOMMIT;
+
+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SHORT_PREAMBLE, %d",
+		 netdev->name, val);
+
+	if (val < PREAMBLE_TYPE_LONG || val > PREAMBLE_TYPE_AUTO)
+		ret = -EINVAL;
+	else
+		priv->preamble_type = val;
+
+	return ret;
+}
+
+static int at76_iw_get_short_preamble(struct net_device *netdev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	snprintf(wrqu->name, sizeof(wrqu->name), "%s (%d)",
+		 preambles[priv->preamble_type], priv->preamble_type);
+	return 0;
+}
+
+static int at76_iw_set_debug(struct net_device *netdev,
+			     struct iw_request_info *info,
+			     struct iw_point *data, char *extra)
+{
+	char *ptr;
+	u32 val;
+
+	if (data->length > 0) {
+		val = simple_strtol(extra, &ptr, 0);
+
+		if (ptr == extra)
+			val = DBG_DEFAULTS;
+
+		at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG input %d: %s -> 0x%x",
+			 netdev->name, data->length, extra, val);
+	} else
+		val = DBG_DEFAULTS;
+
+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG, old 0x%x, new 0x%x",
+		 netdev->name, at76_debug, val);
+
+	/* jal: some more output to pin down lockups */
+	at76_dbg(DBG_IOCTL, "%s: netif running %d queue_stopped %d "
+		 "carrier_ok %d", netdev->name, netif_running(netdev),
+		 netif_queue_stopped(netdev), netif_carrier_ok(netdev));
+
+	at76_debug = val;
+
+	return 0;
+}
+
+static int at76_iw_get_debug(struct net_device *netdev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	snprintf(wrqu->name, sizeof(wrqu->name), "0x%08x", at76_debug);
+	return 0;
+}
+
+static int at76_iw_set_powersave_mode(struct net_device *netdev,
+				      struct iw_request_info *info, char *name,
+				      char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int val = *((int *)name);
+	int ret = -EIWCOMMIT;
+
+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_POWERSAVE_MODE, %d (%s)",
+		 netdev->name, val,
+		 val == AT76_PM_OFF ? "active" : val == AT76_PM_ON ? "save" :
+		 val == AT76_PM_SMART ? "smart save" : "<invalid>");
+	if (val < AT76_PM_OFF || val > AT76_PM_SMART)
+		ret = -EINVAL;
+	else
+		priv->pm_mode = val;
+
+	return ret;
+}
+
+static int at76_iw_get_powersave_mode(struct net_device *netdev,
+				      struct iw_request_info *info,
+				      union iwreq_data *wrqu, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int *param = (int *)extra;
+
+	param[0] = priv->pm_mode;
+	return 0;
+}
+
+static int at76_iw_set_scan_times(struct net_device *netdev,
+				  struct iw_request_info *info, char *name,
+				  char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int mint = *((int *)name);
+	int maxt = *((int *)name + 1);
+	int ret = -EIWCOMMIT;
+
+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_TIMES - min %d max %d",
+		 netdev->name, mint, maxt);
+	if (mint <= 0 || maxt <= 0 || mint > maxt)
+		ret = -EINVAL;
+	else {
+		priv->scan_min_time = mint;
+		priv->scan_max_time = maxt;
+	}
+
+	return ret;
+}
+
+static int at76_iw_get_scan_times(struct net_device *netdev,
+				  struct iw_request_info *info,
+				  union iwreq_data *wrqu, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int *param = (int *)extra;
+
+	param[0] = priv->scan_min_time;
+	param[1] = priv->scan_max_time;
+	return 0;
+}
+
+static int at76_iw_set_scan_mode(struct net_device *netdev,
+				 struct iw_request_info *info, char *name,
+				 char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int val = *((int *)name);
+	int ret = -EIWCOMMIT;
+
+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_MODE - mode %s",
+		 netdev->name, (val = SCAN_TYPE_ACTIVE) ? "active" :
+		 (val = SCAN_TYPE_PASSIVE) ? "passive" : "<invalid>");
+
+	if (val != SCAN_TYPE_ACTIVE && val != SCAN_TYPE_PASSIVE)
+		ret = -EINVAL;
+	else
+		priv->scan_mode = val;
+
+	return ret;
+}
+
+static int at76_iw_get_scan_mode(struct net_device *netdev,
+				 struct iw_request_info *info,
+				 union iwreq_data *wrqu, char *extra)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int *param = (int *)extra;
+
+	param[0] = priv->scan_mode;
+	return 0;
+}
+
+#define AT76_SET_HANDLER(h, f) [h - SIOCIWFIRST] = (iw_handler) f
+
+/* Standard wireless handlers */
+static const iw_handler at76_handlers[] = {
+	AT76_SET_HANDLER(SIOCSIWCOMMIT, at76_iw_handler_commit),
+	AT76_SET_HANDLER(SIOCGIWNAME, at76_iw_handler_get_name),
+	AT76_SET_HANDLER(SIOCSIWFREQ, at76_iw_handler_set_freq),
+	AT76_SET_HANDLER(SIOCGIWFREQ, at76_iw_handler_get_freq),
+	AT76_SET_HANDLER(SIOCSIWMODE, at76_iw_handler_set_mode),
+	AT76_SET_HANDLER(SIOCGIWMODE, at76_iw_handler_get_mode),
+	AT76_SET_HANDLER(SIOCGIWRANGE, at76_iw_handler_get_range),
+	AT76_SET_HANDLER(SIOCSIWSPY, at76_iw_handler_set_spy),
+	AT76_SET_HANDLER(SIOCGIWSPY, at76_iw_handler_get_spy),
+	AT76_SET_HANDLER(SIOCSIWTHRSPY, at76_iw_handler_set_thrspy),
+	AT76_SET_HANDLER(SIOCGIWTHRSPY, at76_iw_handler_get_thrspy),
+	AT76_SET_HANDLER(SIOCSIWAP, at76_iw_handler_set_wap),
+	AT76_SET_HANDLER(SIOCGIWAP, at76_iw_handler_get_wap),
+	AT76_SET_HANDLER(SIOCSIWSCAN, at76_iw_handler_set_scan),
+	AT76_SET_HANDLER(SIOCGIWSCAN, at76_iw_handler_get_scan),
+	AT76_SET_HANDLER(SIOCSIWESSID, at76_iw_handler_set_essid),
+	AT76_SET_HANDLER(SIOCGIWESSID, at76_iw_handler_get_essid),
+	AT76_SET_HANDLER(SIOCSIWRATE, at76_iw_handler_set_rate),
+	AT76_SET_HANDLER(SIOCGIWRATE, at76_iw_handler_get_rate),
+	AT76_SET_HANDLER(SIOCSIWRTS, at76_iw_handler_set_rts),
+	AT76_SET_HANDLER(SIOCGIWRTS, at76_iw_handler_get_rts),
+	AT76_SET_HANDLER(SIOCSIWFRAG, at76_iw_handler_set_frag),
+	AT76_SET_HANDLER(SIOCGIWFRAG, at76_iw_handler_get_frag),
+	AT76_SET_HANDLER(SIOCGIWTXPOW, at76_iw_handler_get_txpow),
+	AT76_SET_HANDLER(SIOCSIWRETRY, at76_iw_handler_set_retry),
+	AT76_SET_HANDLER(SIOCGIWRETRY, at76_iw_handler_get_retry),
+	AT76_SET_HANDLER(SIOCSIWENCODE, at76_iw_handler_set_encode),
+	AT76_SET_HANDLER(SIOCGIWENCODE, at76_iw_handler_get_encode),
+	AT76_SET_HANDLER(SIOCSIWPOWER, at76_iw_handler_set_power),
+	AT76_SET_HANDLER(SIOCGIWPOWER, at76_iw_handler_get_power)
+};
+
+#define AT76_SET_PRIV(h, f) [h - SIOCIWFIRSTPRIV] = (iw_handler) f
+
+/* Private wireless handlers */
+static const iw_handler at76_priv_handlers[] = {
+	AT76_SET_PRIV(AT76_SET_SHORT_PREAMBLE, at76_iw_set_short_preamble),
+	AT76_SET_PRIV(AT76_GET_SHORT_PREAMBLE, at76_iw_get_short_preamble),
+	AT76_SET_PRIV(AT76_SET_DEBUG, at76_iw_set_debug),
+	AT76_SET_PRIV(AT76_GET_DEBUG, at76_iw_get_debug),
+	AT76_SET_PRIV(AT76_SET_POWERSAVE_MODE, at76_iw_set_powersave_mode),
+	AT76_SET_PRIV(AT76_GET_POWERSAVE_MODE, at76_iw_get_powersave_mode),
+	AT76_SET_PRIV(AT76_SET_SCAN_TIMES, at76_iw_set_scan_times),
+	AT76_SET_PRIV(AT76_GET_SCAN_TIMES, at76_iw_get_scan_times),
+	AT76_SET_PRIV(AT76_SET_SCAN_MODE, at76_iw_set_scan_mode),
+	AT76_SET_PRIV(AT76_GET_SCAN_MODE, at76_iw_get_scan_mode),
+};
+
+/* Names and arguments of private wireless handlers */
+static const struct iw_priv_args at76_priv_args[] = {
+	/* 0 - long, 1 - short */
+	{AT76_SET_SHORT_PREAMBLE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
+
+	{AT76_GET_SHORT_PREAMBLE,
+	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_preamble"},
+
+	/* we must pass the new debug mask as a string, because iwpriv cannot
+	 * parse hex numbers starting with 0x :-(  */
+	{AT76_SET_DEBUG,
+	 IW_PRIV_TYPE_CHAR | 10, 0, "set_debug"},
+
+	{AT76_GET_DEBUG,
+	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_debug"},
+
+	/* 1 - active, 2 - power save, 3 - smart power save */
+	{AT76_SET_POWERSAVE_MODE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_powersave"},
+
+	{AT76_GET_POWERSAVE_MODE,
+	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_powersave"},
+
+	/* min_channel_time, max_channel_time */
+	{AT76_SET_SCAN_TIMES,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_scan_times"},
+
+	{AT76_GET_SCAN_TIMES,
+	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, "get_scan_times"},
+
+	/* 0 - active, 1 - passive scan */
+	{AT76_SET_SCAN_MODE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_scan_mode"},
+
+	{AT76_GET_SCAN_MODE,
+	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_mode"},
+};
+
+static const struct iw_handler_def at76_handler_def = {
+	.num_standard = ARRAY_SIZE(at76_handlers),
+	.num_private = ARRAY_SIZE(at76_priv_handlers),
+	.num_private_args = ARRAY_SIZE(at76_priv_args),
+	.standard = at76_handlers,
+	.private = at76_priv_handlers,
+	.private_args = at76_priv_args,
+	.get_wireless_stats = at76_get_wireless_stats,
+};
+
+static const u8 snapsig[] = { 0xaa, 0xaa, 0x03 };
+
+/* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with
+ * a SNAP OID of 0 (0x00, 0x00, 0x00) */
+static const u8 rfc1042sig[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+static int at76_tx(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	struct net_device_stats *stats = &priv->stats;
+	int ret = 0;
+	int wlen;
+	int submit_len;
+	struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer;
+	struct ieee80211_hdr_3addr *i802_11_hdr =
+	    (struct ieee80211_hdr_3addr *)tx_buffer->packet;
+	u8 *payload = i802_11_hdr->payload;
+	struct ethhdr *eh = (struct ethhdr *)skb->data;
+
+	if (netif_queue_stopped(netdev)) {
+		printk(KERN_ERR "%s: %s called while netdev is stopped\n",
+		       netdev->name, __func__);
+		/* skip this packet */
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	if (priv->tx_urb->status == -EINPROGRESS) {
+		printk(KERN_ERR "%s: %s called while tx urb is pending\n",
+		       netdev->name, __func__);
+		/* skip this packet */
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	if (skb->len < ETH_HLEN) {
+		printk(KERN_ERR "%s: %s: skb too short (%d)\n",
+		       netdev->name, __func__, skb->len);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	at76_ledtrig_tx_activity();	/* tell ledtrigger we send a packet */
+
+	/* we can get rid of memcpy if we set netdev->hard_header_len to
+	   reserve enough space, but we would need to keep the skb around */
+
+	if (ntohs(eh->h_proto) <= ETH_DATA_LEN) {
+		/* this is a 802.3 packet */
+		if (skb->len >= ETH_HLEN + sizeof(rfc1042sig)
+		    && skb->data[ETH_HLEN] == rfc1042sig[0]
+		    && skb->data[ETH_HLEN + 1] == rfc1042sig[1]) {
+			/* higher layer delivered SNAP header - keep it */
+			memcpy(payload, skb->data + ETH_HLEN,
+			       skb->len - ETH_HLEN);
+			wlen = IEEE80211_3ADDR_LEN + skb->len - ETH_HLEN;
+		} else {
+			printk(KERN_ERR "%s: dropping non-SNAP 802.2 packet "
+			       "(DSAP 0x%02x SSAP 0x%02x cntrl 0x%02x)\n",
+			       priv->netdev->name, skb->data[ETH_HLEN],
+			       skb->data[ETH_HLEN + 1],
+			       skb->data[ETH_HLEN + 2]);
+			dev_kfree_skb(skb);
+			return 0;
+		}
+	} else {
+		/* add RFC 1042 header in front */
+		memcpy(payload, rfc1042sig, sizeof(rfc1042sig));
+		memcpy(payload + sizeof(rfc1042sig), &eh->h_proto,
+		       skb->len - offsetof(struct ethhdr, h_proto));
+		wlen = IEEE80211_3ADDR_LEN + sizeof(rfc1042sig) + skb->len -
+		    offsetof(struct ethhdr, h_proto);
+	}
+
+	/* make wireless header */
+	i802_11_hdr->frame_ctl =
+	    cpu_to_le16(IEEE80211_FTYPE_DATA |
+			(priv->wep_enabled ? IEEE80211_FCTL_PROTECTED : 0) |
+			(priv->iw_mode ==
+			 IW_MODE_INFRA ? IEEE80211_FCTL_TODS : 0));
+
+	if (priv->iw_mode == IW_MODE_ADHOC) {
+		memcpy(i802_11_hdr->addr1, eh->h_dest, ETH_ALEN);
+		memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN);
+		memcpy(i802_11_hdr->addr3, priv->bssid, ETH_ALEN);
+	} else if (priv->iw_mode == IW_MODE_INFRA) {
+		memcpy(i802_11_hdr->addr1, priv->bssid, ETH_ALEN);
+		memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN);
+		memcpy(i802_11_hdr->addr3, eh->h_dest, ETH_ALEN);
+	}
+
+	i802_11_hdr->duration_id = cpu_to_le16(0);
+	i802_11_hdr->seq_ctl = cpu_to_le16(0);
+
+	/* setup 'Atmel' header */
+	tx_buffer->wlength = cpu_to_le16(wlen);
+	tx_buffer->tx_rate = priv->txrate;
+	/* for broadcast destination addresses, the firmware 0.100.x
+	   seems to choose the highest rate set with CMD_STARTUP in
+	   basic_rate_set replacing this value */
+
+	memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved));
+
+	tx_buffer->padding = at76_calc_padding(wlen);
+	submit_len = wlen + AT76_TX_HDRLEN + tx_buffer->padding;
+
+	at76_dbg(DBG_TX_DATA_CONTENT, "%s skb->data %s", priv->netdev->name,
+		 hex2str(skb->data, 32));
+	at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr %s",
+		 priv->netdev->name,
+		 le16_to_cpu(tx_buffer->wlength),
+		 tx_buffer->padding, tx_buffer->tx_rate,
+		 hex2str(i802_11_hdr, sizeof(*i802_11_hdr)));
+	at76_dbg(DBG_TX_DATA_CONTENT, "%s payload %s", priv->netdev->name,
+		 hex2str(payload, 48));
+
+	/* send stuff */
+	netif_stop_queue(netdev);
+	netdev->trans_start = jiffies;
+
+	usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer,
+			  submit_len, at76_tx_callback, priv);
+	ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC);
+	if (ret) {
+		stats->tx_errors++;
+		printk(KERN_ERR "%s: error in tx submit urb: %d\n",
+		       netdev->name, ret);
+		if (ret == -EINVAL)
+			printk(KERN_ERR
+			       "%s: -EINVAL: tx urb %p hcpriv %p complete %p\n",
+			       priv->netdev->name, priv->tx_urb,
+			       priv->tx_urb->hcpriv, priv->tx_urb->complete);
+	} else {
+		stats->tx_bytes += skb->len;
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
+}
+
+static void at76_tx_timeout(struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	if (!priv)
+		return;
+	warn("%s: tx timeout.", netdev->name);
+
+	usb_unlink_urb(priv->tx_urb);
+	priv->stats.tx_errors++;
+}
+
+static int at76_submit_rx_urb(struct at76_priv *priv)
+{
+	int ret;
+	int size;
+	struct sk_buff *skb = priv->rx_skb;
+
+	if (!priv->rx_urb) {
+		printk(KERN_ERR "%s: %s: priv->rx_urb is NULL\n",
+		       priv->netdev->name, __func__);
+		return -EFAULT;
+	}
+
+	if (!skb) {
+		skb = dev_alloc_skb(sizeof(struct at76_rx_buffer));
+		if (!skb) {
+			printk(KERN_ERR "%s: cannot allocate rx skbuff\n",
+			       priv->netdev->name);
+			ret = -ENOMEM;
+			goto exit;
+		}
+		priv->rx_skb = skb;
+	} else {
+		skb_push(skb, skb_headroom(skb));
+		skb_trim(skb, 0);
+	}
+
+	size = skb_tailroom(skb);
+	usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe,
+			  skb_put(skb, size), size, at76_rx_callback, priv);
+	ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC);
+	if (ret < 0) {
+		if (ret == -ENODEV)
+			at76_dbg(DBG_DEVSTART,
+				 "usb_submit_urb returned -ENODEV");
+		else
+			printk(KERN_ERR "%s: rx, usb_submit_urb failed: %d\n",
+			       priv->netdev->name, ret);
+	}
+
+exit:
+	if (ret < 0 && ret != -ENODEV)
+		printk(KERN_ERR "%s: cannot submit rx urb - please unload the "
+		       "driver and/or power cycle the device\n",
+		       priv->netdev->name);
+
+	return ret;
+}
+
+static int at76_open(struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	int ret = 0;
+
+	at76_dbg(DBG_PROC_ENTRY, "%s(): entry", __func__);
+
+	if (mutex_lock_interruptible(&priv->mtx))
+		return -EINTR;
+
+	/* if netdev->dev_addr != priv->mac_addr we must
+	   set the mac address in the device ! */
+	if (compare_ether_addr(netdev->dev_addr, priv->mac_addr)) {
+		if (at76_add_mac_address(priv, netdev->dev_addr) >= 0)
+			at76_dbg(DBG_PROGRESS, "%s: set new MAC addr %s",
+				 netdev->name, mac2str(netdev->dev_addr));
+	}
+
+	priv->scan_state = SCAN_IDLE;
+	priv->last_scan = jiffies;
+
+	ret = at76_submit_rx_urb(priv);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: open: submit_rx_urb failed: %d\n",
+		       netdev->name, ret);
+		goto error;
+	}
+
+	schedule_delayed_work(&priv->dwork_restart, 0);
+
+	at76_dbg(DBG_PROC_ENTRY, "%s(): end", __func__);
+error:
+	mutex_unlock(&priv->mtx);
+	return ret < 0 ? ret : 0;
+}
+
+static int at76_stop(struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	at76_dbg(DBG_DEVSTART, "%s: ENTER", __func__);
+
+	if (mutex_lock_interruptible(&priv->mtx))
+		return -EINTR;
+
+	at76_quiesce(priv);
+
+	if (!priv->device_unplugged) {
+		/* We are called by "ifconfig ethX down", not because the
+		 * device is not available anymore. */
+		at76_set_radio(priv, 0);
+
+		/* We unlink rx_urb because at76_open() re-submits it.
+		 * If unplugged, at76_delete_device() takes care of it. */
+		usb_kill_urb(priv->rx_urb);
+	}
+
+	/* free the bss_list */
+	at76_free_bss_list(priv);
+
+	mutex_unlock(&priv->mtx);
+	at76_dbg(DBG_DEVSTART, "%s: EXIT", __func__);
+
+	return 0;
+}
+
+static void at76_ethtool_get_drvinfo(struct net_device *netdev,
+				     struct ethtool_drvinfo *info)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+
+	strncpy(info->driver, DRIVER_NAME, sizeof(info->driver));
+	strncpy(info->version, DRIVER_VERSION, sizeof(info->version));
+
+	usb_make_path(priv->udev, info->bus_info, sizeof(info->bus_info));
+
+	snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d-%d",
+		 priv->fw_version.major, priv->fw_version.minor,
+		 priv->fw_version.patch, priv->fw_version.build);
+}
+
+static u32 at76_ethtool_get_link(struct net_device *netdev)
+{
+	struct at76_priv *priv = netdev_priv(netdev);
+	return priv->mac_state == MAC_CONNECTED;
+}
+
+static struct ethtool_ops at76_ethtool_ops = {
+	.get_drvinfo = at76_ethtool_get_drvinfo,
+	.get_link = at76_ethtool_get_link,
+};
+
+/* Download external firmware */
+static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)
+{
+	int ret;
+	int op_mode;
+	int blockno = 0;
+	int bsize;
+	u8 *block;
+	u8 *buf = fwe->extfw;
+	int size = fwe->extfw_size;
+
+	if (!buf || !size)
+		return -ENOENT;
+
+	op_mode = at76_get_op_mode(udev);
+	at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
+
+	if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
+		dev_printk(KERN_ERR, &udev->dev, "unexpected opmode %d\n",
+			   op_mode);
+		return -EINVAL;
+	}
+
+	block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
+	if (!block)
+		return -ENOMEM;
+
+	at76_dbg(DBG_DEVSTART, "downloading external firmware");
+
+	/* for fw >= 0.100, the device needs an extra empty block */
+	do {
+		bsize = min_t(int, size, FW_BLOCK_SIZE);
+		memcpy(block, buf, bsize);
+		at76_dbg(DBG_DEVSTART,
+			 "ext fw, size left = %5d, bsize = %4d, blockno = %2d",
+			 size, bsize, blockno);
+		ret = at76_load_ext_fw_block(udev, blockno, block, bsize);
+		if (ret != bsize) {
+			dev_printk(KERN_ERR, &udev->dev,
+				   "loading %dth firmware block failed: %d\n",
+				   blockno, ret);
+			goto exit;
+		}
+		buf += bsize;
+		size -= bsize;
+		blockno++;
+	} while (bsize > 0);
+
+	if (at76_is_505a(fwe->board_type)) {
+		at76_dbg(DBG_DEVSTART, "200 ms delay for 505a");
+		schedule_timeout_interruptible(HZ / 5 + 1);
+	}
+
+exit:
+	kfree(block);
+	if (ret < 0)
+		dev_printk(KERN_ERR, &udev->dev,
+			   "downloading external firmware failed: %d\n", ret);
+	return ret;
+}
+
+/* Download internal firmware */
+static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe)
+{
+	int ret;
+	int need_remap = !at76_is_505a(fwe->board_type);
+
+	ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size,
+				   need_remap ? 0 : 2 * HZ);
+
+	if (ret < 0) {
+		dev_printk(KERN_ERR, &udev->dev,
+			   "downloading internal fw failed with %d\n", ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_DEVSTART, "sending REMAP");
+
+	/* no REMAP for 505A (see SF driver) */
+	if (need_remap) {
+		ret = at76_remap(udev);
+		if (ret < 0) {
+			dev_printk(KERN_ERR, &udev->dev,
+				   "sending REMAP failed with %d\n", ret);
+			goto exit;
+		}
+	}
+
+	at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds");
+	schedule_timeout_interruptible(2 * HZ + 1);
+	usb_reset_device(udev);
+
+exit:
+	return ret;
+}
+
+static int at76_match_essid(struct at76_priv *priv, struct bss_info *ptr)
+{
+	/* common criteria for both modi */
+
+	int ret = (priv->essid_size == 0 /* ANY ssid */  ||
+		   (priv->essid_size == ptr->ssid_len &&
+		    !memcmp(priv->essid, ptr->ssid, ptr->ssid_len)));
+	if (!ret)
+		at76_dbg(DBG_BSS_MATCH,
+			 "%s bss table entry %p: essid didn't match",
+			 priv->netdev->name, ptr);
+	return ret;
+}
+
+static inline int at76_match_mode(struct at76_priv *priv, struct bss_info *ptr)
+{
+	int ret;
+
+	if (priv->iw_mode == IW_MODE_ADHOC)
+		ret = ptr->capa & WLAN_CAPABILITY_IBSS;
+	else
+		ret = ptr->capa & WLAN_CAPABILITY_ESS;
+	if (!ret)
+		at76_dbg(DBG_BSS_MATCH,
+			 "%s bss table entry %p: mode didn't match",
+			 priv->netdev->name, ptr);
+	return ret;
+}
+
+static int at76_match_rates(struct at76_priv *priv, struct bss_info *ptr)
+{
+	int i;
+
+	for (i = 0; i < ptr->rates_len; i++) {
+		u8 rate = ptr->rates[i];
+
+		if (!(rate & 0x80))
+			continue;
+
+		/* this is a basic rate we have to support
+		   (see IEEE802.11, ch. 7.3.2.2) */
+		if (rate != (0x80 | hw_rates[0])
+		    && rate != (0x80 | hw_rates[1])
+		    && rate != (0x80 | hw_rates[2])
+		    && rate != (0x80 | hw_rates[3])) {
+			at76_dbg(DBG_BSS_MATCH,
+				 "%s: bss table entry %p: basic rate %02x not "
+				 "supported", priv->netdev->name, ptr, rate);
+			return 0;
+		}
+	}
+
+	/* if we use short preamble, the bss must support it */
+	if (priv->preamble_type == PREAMBLE_TYPE_SHORT &&
+	    !(ptr->capa & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
+		at76_dbg(DBG_BSS_MATCH,
+			 "%s: %p does not support short preamble",
+			 priv->netdev->name, ptr);
+		return 0;
+	} else
+		return 1;
+}
+
+static inline int at76_match_wep(struct at76_priv *priv, struct bss_info *ptr)
+{
+	if (!priv->wep_enabled && ptr->capa & WLAN_CAPABILITY_PRIVACY) {
+		/* we have disabled WEP, but the BSS signals privacy */
+		at76_dbg(DBG_BSS_MATCH,
+			 "%s: bss table entry %p: requires encryption",
+			 priv->netdev->name, ptr);
+		return 0;
+	}
+	/* otherwise if the BSS does not signal privacy it may well
+	   accept encrypted packets from us ... */
+	return 1;
+}
+
+static inline int at76_match_bssid(struct at76_priv *priv, struct bss_info *ptr)
+{
+	if (!priv->wanted_bssid_valid ||
+	    !compare_ether_addr(ptr->bssid, priv->wanted_bssid))
+		return 1;
+
+	at76_dbg(DBG_BSS_MATCH,
+		 "%s: requested bssid - %s does not match",
+		 priv->netdev->name, mac2str(priv->wanted_bssid));
+	at76_dbg(DBG_BSS_MATCH,
+		 "      AP bssid - %s of bss table entry %p",
+		 mac2str(ptr->bssid), ptr);
+	return 0;
+}
+
+/**
+ * at76_match_bss - try to find a matching bss in priv->bss
+ *
+ * last - last bss tried
+ *
+ * last == NULL signals a new round starting with priv->bss_list.next
+ * this function must be called inside an acquired priv->bss_list_spinlock
+ * otherwise the timeout on bss may remove the newly chosen entry
+ */
+static struct bss_info *at76_match_bss(struct at76_priv *priv,
+				       struct bss_info *last)
+{
+	struct bss_info *ptr = NULL;
+	struct list_head *curr;
+
+	curr = last ? last->list.next : priv->bss_list.next;
+	while (curr != &priv->bss_list) {
+		ptr = list_entry(curr, struct bss_info, list);
+		if (at76_match_essid(priv, ptr) && at76_match_mode(priv, ptr)
+		    && at76_match_wep(priv, ptr) && at76_match_rates(priv, ptr)
+		    && at76_match_bssid(priv, ptr))
+			break;
+		curr = curr->next;
+	}
+
+	if (curr == &priv->bss_list)
+		ptr = NULL;
+	/* otherwise ptr points to the struct bss_info we have chosen */
+
+	at76_dbg(DBG_BSS_TABLE, "%s %s: returned %p", priv->netdev->name,
+		 __func__, ptr);
+	return ptr;
+}
+
+/* Start joining a matching BSS, or create own IBSS */
+static void at76_work_join(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      work_join);
+	int ret;
+	unsigned long flags;
+
+	mutex_lock(&priv->mtx);
+
+	WARN_ON(priv->mac_state != MAC_JOINING);
+	if (priv->mac_state != MAC_JOINING)
+		goto exit;
+
+	/* secure the access to priv->curr_bss ! */
+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
+	priv->curr_bss = at76_match_bss(priv, priv->curr_bss);
+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
+
+	if (!priv->curr_bss) {
+		/* here we haven't found a matching (i)bss ... */
+		if (priv->iw_mode == IW_MODE_ADHOC) {
+			at76_set_mac_state(priv, MAC_OWN_IBSS);
+			at76_start_ibss(priv);
+			goto exit;
+		}
+		/* haven't found a matching BSS in infra mode - try again */
+		at76_set_mac_state(priv, MAC_SCANNING);
+		schedule_work(&priv->work_start_scan);
+		goto exit;
+	}
+
+	ret = at76_join_bss(priv, priv->curr_bss);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: join_bss failed with %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	ret = at76_wait_completion(priv, CMD_JOIN);
+	if (ret != CMD_STATUS_COMPLETE) {
+		if (ret != CMD_STATUS_TIME_OUT)
+			printk(KERN_ERR "%s: join_bss completed with %d\n",
+			       priv->netdev->name, ret);
+		else
+			printk(KERN_INFO "%s: join_bss ssid %s timed out\n",
+			       priv->netdev->name,
+			       mac2str(priv->curr_bss->bssid));
+
+		/* retry next BSS immediately */
+		schedule_work(&priv->work_join);
+		goto exit;
+	}
+
+	/* here we have joined the (I)BSS */
+	if (priv->iw_mode == IW_MODE_ADHOC) {
+		struct bss_info *bptr = priv->curr_bss;
+		at76_set_mac_state(priv, MAC_CONNECTED);
+		/* get ESSID, BSSID and channel for priv->curr_bss */
+		priv->essid_size = bptr->ssid_len;
+		memcpy(priv->essid, bptr->ssid, bptr->ssid_len);
+		memcpy(priv->bssid, bptr->bssid, ETH_ALEN);
+		priv->channel = bptr->channel;
+		at76_iwevent_bss_connect(priv->netdev, bptr->bssid);
+		netif_carrier_on(priv->netdev);
+		netif_start_queue(priv->netdev);
+		/* just to be sure */
+		cancel_delayed_work(&priv->dwork_get_scan);
+		cancel_delayed_work(&priv->dwork_auth);
+		cancel_delayed_work(&priv->dwork_assoc);
+	} else {
+		/* send auth req */
+		priv->retries = AUTH_RETRIES;
+		at76_set_mac_state(priv, MAC_AUTH);
+		at76_auth_req(priv, priv->curr_bss, 1, NULL);
+		at76_dbg(DBG_MGMT_TIMER,
+			 "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__);
+		schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
+	}
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+/* Reap scan results */
+static void at76_dwork_get_scan(struct work_struct *work)
+{
+	int status;
+	int ret;
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      dwork_get_scan.work);
+
+	mutex_lock(&priv->mtx);
+	WARN_ON(priv->mac_state != MAC_SCANNING);
+	if (priv->mac_state != MAC_SCANNING)
+		goto exit;
+
+	status = at76_get_cmd_status(priv->udev, CMD_SCAN);
+	if (status < 0) {
+		printk(KERN_ERR "%s: %s: at76_get_cmd_status failed with %d\n",
+		       priv->netdev->name, __func__, status);
+		status = CMD_STATUS_IN_PROGRESS;
+		/* INFO: Hope it was a one off error - if not, scanning
+		   further down the line and stop this cycle */
+	}
+	at76_dbg(DBG_PROGRESS,
+		 "%s %s: got cmd_status %d (state %s, need_any %d)",
+		 priv->netdev->name, __func__, status,
+		 mac_states[priv->mac_state], priv->scan_need_any);
+
+	if (status != CMD_STATUS_COMPLETE) {
+		if ((status != CMD_STATUS_IN_PROGRESS) &&
+		    (status != CMD_STATUS_IDLE))
+			printk(KERN_ERR "%s: %s: Bad scan status: %s\n",
+			       priv->netdev->name, __func__,
+			       at76_get_cmd_status_string(status));
+
+		/* the first cmd status after scan start is always a IDLE ->
+		   start the timer to poll again until COMPLETED */
+		at76_dbg(DBG_MGMT_TIMER,
+			 "%s:%d: starting mgmt_timer for %d ticks",
+			 __func__, __LINE__, SCAN_POLL_INTERVAL);
+		schedule_delayed_work(&priv->dwork_get_scan,
+				      SCAN_POLL_INTERVAL);
+		goto exit;
+	}
+
+	if (at76_debug & DBG_BSS_TABLE)
+		at76_dump_bss_table(priv);
+
+	if (priv->scan_need_any) {
+		ret = at76_start_scan(priv, 0);
+		if (ret < 0)
+			printk(KERN_ERR
+			       "%s: %s: start_scan (ANY) failed with %d\n",
+			       priv->netdev->name, __func__, ret);
+		at76_dbg(DBG_MGMT_TIMER,
+			 "%s:%d: starting mgmt_timer for %d ticks", __func__,
+			 __LINE__, SCAN_POLL_INTERVAL);
+		schedule_delayed_work(&priv->dwork_get_scan,
+				      SCAN_POLL_INTERVAL);
+		priv->scan_need_any = 0;
+	} else {
+		priv->scan_state = SCAN_COMPLETED;
+		/* report the end of scan to user space */
+		at76_iwevent_scan_complete(priv->netdev);
+		at76_set_mac_state(priv, MAC_JOINING);
+		schedule_work(&priv->work_join);
+	}
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+/* Handle loss of beacons from the AP */
+static void at76_dwork_beacon(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      dwork_beacon.work);
+
+	mutex_lock(&priv->mtx);
+	if (priv->mac_state != MAC_CONNECTED || priv->iw_mode != IW_MODE_INFRA)
+		goto exit;
+
+	/* We haven't received any beacons from out AP for BEACON_TIMEOUT */
+	printk(KERN_INFO "%s: lost beacon bssid %s\n",
+	       priv->netdev->name, mac2str(priv->curr_bss->bssid));
+
+	netif_carrier_off(priv->netdev);
+	netif_stop_queue(priv->netdev);
+	at76_iwevent_bss_disconnect(priv->netdev);
+	at76_set_mac_state(priv, MAC_SCANNING);
+	schedule_work(&priv->work_start_scan);
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+/* Handle authentication response timeout */
+static void at76_dwork_auth(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      dwork_auth.work);
+
+	mutex_lock(&priv->mtx);
+	WARN_ON(priv->mac_state != MAC_AUTH);
+	if (priv->mac_state != MAC_AUTH)
+		goto exit;
+
+	at76_dbg(DBG_PROGRESS, "%s: authentication response timeout",
+		 priv->netdev->name);
+
+	if (priv->retries-- >= 0) {
+		at76_auth_req(priv, priv->curr_bss, 1, NULL);
+		at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ",
+			 __func__, __LINE__);
+		schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
+	} else {
+		/* try to get next matching BSS */
+		at76_set_mac_state(priv, MAC_JOINING);
+		schedule_work(&priv->work_join);
+	}
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+/* Handle association response timeout */
+static void at76_dwork_assoc(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      dwork_assoc.work);
+
+	mutex_lock(&priv->mtx);
+	WARN_ON(priv->mac_state != MAC_ASSOC);
+	if (priv->mac_state != MAC_ASSOC)
+		goto exit;
+
+	at76_dbg(DBG_PROGRESS, "%s: association response timeout",
+		 priv->netdev->name);
+
+	if (priv->retries-- >= 0) {
+		at76_assoc_req(priv, priv->curr_bss);
+		at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ",
+			 __func__, __LINE__);
+		schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT);
+	} else {
+		/* try to get next matching BSS */
+		at76_set_mac_state(priv, MAC_JOINING);
+		schedule_work(&priv->work_join);
+	}
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+/* Read new bssid in ad-hoc mode */
+static void at76_work_new_bss(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      work_new_bss);
+	int ret;
+	struct mib_mac_mgmt mac_mgmt;
+
+	mutex_lock(&priv->mtx);
+
+	ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, &mac_mgmt,
+			   sizeof(struct mib_mac_mgmt));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_get_mib failed: %d\n",
+		       priv->netdev->name, ret);
+		goto exit;
+	}
+
+	at76_dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt.ibss_change);
+	memcpy(priv->bssid, mac_mgmt.current_bssid, ETH_ALEN);
+	at76_dbg(DBG_PROGRESS, "using BSSID %s", mac2str(priv->bssid));
+
+	at76_iwevent_bss_connect(priv->netdev, priv->bssid);
+
+	priv->mib_buf.type = MIB_MAC_MGMT;
+	priv->mib_buf.size = 1;
+	priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change);
+	priv->mib_buf.data.byte = 0;
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n",
+		       priv->netdev->name, ret);
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+static int at76_startup_device(struct at76_priv *priv)
+{
+	struct at76_card_config *ccfg = &priv->card_config;
+	int ret;
+
+	at76_dbg(DBG_PARAMS,
+		 "%s param: ssid %.*s (%s) mode %s ch %d wep %s key %d "
+		 "keylen %d", priv->netdev->name, priv->essid_size, priv->essid,
+		 hex2str(priv->essid, IW_ESSID_MAX_SIZE),
+		 priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra",
+		 priv->channel, priv->wep_enabled ? "enabled" : "disabled",
+		 priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]);
+	at76_dbg(DBG_PARAMS,
+		 "%s param: preamble %s rts %d retry %d frag %d "
+		 "txrate %s auth_mode %d", priv->netdev->name,
+		 preambles[priv->preamble_type], priv->rts_threshold,
+		 priv->short_retry_limit, priv->frag_threshold,
+		 priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate ==
+		 TX_RATE_2MBIT ? "2MBit" : priv->txrate ==
+		 TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate ==
+		 TX_RATE_11MBIT ? "11MBit" : priv->txrate ==
+		 TX_RATE_AUTO ? "auto" : "<invalid>", priv->auth_mode);
+	at76_dbg(DBG_PARAMS,
+		 "%s param: pm_mode %d pm_period %d auth_mode %s "
+		 "scan_times %d %d scan_mode %s",
+		 priv->netdev->name, priv->pm_mode, priv->pm_period,
+		 priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret",
+		 priv->scan_min_time, priv->scan_max_time,
+		 priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive");
+
+	memset(ccfg, 0, sizeof(struct at76_card_config));
+	ccfg->promiscuous_mode = 0;
+	ccfg->short_retry_limit = priv->short_retry_limit;
+
+	if (priv->wep_enabled) {
+		if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN)
+			ccfg->encryption_type = 2;
+		else
+			ccfg->encryption_type = 1;
+
+		/* jal: always exclude unencrypted if WEP is active */
+		ccfg->exclude_unencrypted = 1;
+	} else {
+		ccfg->exclude_unencrypted = 0;
+		ccfg->encryption_type = 0;
+	}
+
+	ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold);
+	ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold);
+
+	memcpy(ccfg->basic_rate_set, hw_rates, 4);
+	/* jal: really needed, we do a set_mib for autorate later ??? */
+	ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0);
+	ccfg->channel = priv->channel;
+	ccfg->privacy_invoked = priv->wep_enabled;
+	memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE);
+	ccfg->ssid_len = priv->essid_size;
+
+	ccfg->wep_default_key_id = priv->wep_key_id;
+	memcpy(ccfg->wep_default_key_value, priv->wep_keys, 4 * WEP_KEY_LEN);
+
+	ccfg->short_preamble = priv->preamble_type;
+	ccfg->beacon_period = cpu_to_le16(priv->beacon_period);
+
+	ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config,
+				    sizeof(struct at76_card_config));
+	if (ret < 0) {
+		printk(KERN_ERR "%s: at76_set_card_command failed: %d\n",
+		       priv->netdev->name, ret);
+		return ret;
+	}
+
+	at76_wait_completion(priv, CMD_STARTUP);
+
+	/* remove BSSID from previous run */
+	memset(priv->bssid, 0, ETH_ALEN);
+
+	if (at76_set_radio(priv, 1) == 1)
+		at76_wait_completion(priv, CMD_RADIO_ON);
+
+	ret = at76_set_preamble(priv, priv->preamble_type);
+	if (ret < 0)
+		return ret;
+
+	ret = at76_set_frag(priv, priv->frag_threshold);
+	if (ret < 0)
+		return ret;
+
+	ret = at76_set_rts(priv, priv->rts_threshold);
+	if (ret < 0)
+		return ret;
+
+	ret = at76_set_autorate_fallback(priv,
+					 priv->txrate == TX_RATE_AUTO ? 1 : 0);
+	if (ret < 0)
+		return ret;
+
+	ret = at76_set_pm_mode(priv);
+	if (ret < 0)
+		return ret;
+
+	if (at76_debug & DBG_MIB) {
+		at76_dump_mib_mac(priv);
+		at76_dump_mib_mac_addr(priv);
+		at76_dump_mib_mac_mgmt(priv);
+		at76_dump_mib_mac_wep(priv);
+		at76_dump_mib_mdomain(priv);
+		at76_dump_mib_phy(priv);
+		at76_dump_mib_local(priv);
+	}
+
+	return 0;
+}
+
+/* Restart the interface */
+static void at76_dwork_restart(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      dwork_restart.work);
+
+	mutex_lock(&priv->mtx);
+
+	netif_carrier_off(priv->netdev);	/* stop netdev watchdog */
+	netif_stop_queue(priv->netdev);	/* stop tx data packets */
+
+	at76_startup_device(priv);
+
+	if (priv->iw_mode != IW_MODE_MONITOR) {
+		priv->netdev->type = ARPHRD_ETHER;
+		at76_set_mac_state(priv, MAC_SCANNING);
+		schedule_work(&priv->work_start_scan);
+	} else {
+		priv->netdev->type = ARPHRD_IEEE80211_RADIOTAP;
+		at76_start_monitor(priv);
+	}
+
+	mutex_unlock(&priv->mtx);
+}
+
+/* Initiate scanning */
+static void at76_work_start_scan(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      work_start_scan);
+	int ret;
+
+	mutex_lock(&priv->mtx);
+
+	WARN_ON(priv->mac_state != MAC_SCANNING);
+	if (priv->mac_state != MAC_SCANNING)
+		goto exit;
+
+	/* only clear the bss list when a scan is actively initiated,
+	 * otherwise simply rely on at76_bss_list_timeout */
+	if (priv->scan_state == SCAN_IN_PROGRESS) {
+		at76_free_bss_list(priv);
+		priv->scan_need_any = 1;
+	} else
+		priv->scan_need_any = 0;
+
+	ret = at76_start_scan(priv, 1);
+
+	if (ret < 0)
+		printk(KERN_ERR "%s: %s: start_scan failed with %d\n",
+		       priv->netdev->name, __func__, ret);
+	else {
+		at76_dbg(DBG_MGMT_TIMER,
+			 "%s:%d: starting mgmt_timer for %d ticks",
+			 __func__, __LINE__, SCAN_POLL_INTERVAL);
+		schedule_delayed_work(&priv->dwork_get_scan,
+				      SCAN_POLL_INTERVAL);
+	}
+
+exit:
+	mutex_unlock(&priv->mtx);
+}
+
+/* Enable or disable promiscuous mode */
+static void at76_work_set_promisc(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      work_set_promisc);
+	int ret = 0;
+
+	mutex_lock(&priv->mtx);
+
+	priv->mib_buf.type = MIB_LOCAL;
+	priv->mib_buf.size = 1;
+	priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode);
+	priv->mib_buf.data.byte = priv->promisc ? 1 : 0;
+
+	ret = at76_set_mib(priv, &priv->mib_buf);
+	if (ret < 0)
+		printk(KERN_ERR "%s: set_mib (promiscuous_mode) failed: %d\n",
+		       priv->netdev->name, ret);
+
+	mutex_unlock(&priv->mtx);
+}
+
+/* Submit Rx urb back to the device */
+static void at76_work_submit_rx(struct work_struct *work)
+{
+	struct at76_priv *priv = container_of(work, struct at76_priv,
+					      work_submit_rx);
+
+	mutex_lock(&priv->mtx);
+	at76_submit_rx_urb(priv);
+	mutex_unlock(&priv->mtx);
+}
+
+/* We got an association response */
+static void at76_rx_mgmt_assoc(struct at76_priv *priv,
+			       struct at76_rx_buffer *buf)
+{
+	struct ieee80211_assoc_response *resp =
+	    (struct ieee80211_assoc_response *)buf->packet;
+	u16 assoc_id = le16_to_cpu(resp->aid);
+	u16 status = le16_to_cpu(resp->status);
+
+	at76_dbg(DBG_RX_MGMT, "%s: rx AssocResp bssid %s capa 0x%04x status "
+		 "0x%04x assoc_id 0x%04x rates %s", priv->netdev->name,
+		 mac2str(resp->header.addr3), le16_to_cpu(resp->capability),
+		 status, assoc_id, hex2str(resp->info_element->data,
+					   resp->info_element->len));
+
+	if (priv->mac_state != MAC_ASSOC) {
+		printk(KERN_INFO "%s: AssocResp in state %s ignored\n",
+		       priv->netdev->name, mac_states[priv->mac_state]);
+		return;
+	}
+
+	BUG_ON(!priv->curr_bss);
+
+	cancel_delayed_work(&priv->dwork_assoc);
+	if (status == WLAN_STATUS_SUCCESS) {
+		struct bss_info *ptr = priv->curr_bss;
+		priv->assoc_id = assoc_id & 0x3fff;
+		/* update iwconfig params */
+		memcpy(priv->bssid, ptr->bssid, ETH_ALEN);
+		memcpy(priv->essid, ptr->ssid, ptr->ssid_len);
+		priv->essid_size = ptr->ssid_len;
+		priv->channel = ptr->channel;
+		schedule_work(&priv->work_assoc_done);
+	} else {
+		at76_set_mac_state(priv, MAC_JOINING);
+		schedule_work(&priv->work_join);
+	}
+}
+
+/* Process disassociation request from the AP */
+static void at76_rx_mgmt_disassoc(struct at76_priv *priv,
+				  struct at76_rx_buffer *buf)
+{
+	struct ieee80211_disassoc *resp =
+	    (struct ieee80211_disassoc *)buf->packet;
+	struct ieee80211_hdr_3addr *mgmt = &resp->header;
+
+	at76_dbg(DBG_RX_MGMT,
+		 "%s: rx DisAssoc bssid %s reason 0x%04x destination %s",
+		 priv->netdev->name, mac2str(mgmt->addr3),
+		 le16_to_cpu(resp->reason), mac2str(mgmt->addr1));
+
+	/* We are not connected, ignore */
+	if (priv->mac_state == MAC_SCANNING || priv->mac_state == MAC_INIT
+	    || !priv->curr_bss)
+		return;
+
+	/* Not our BSSID, ignore */
+	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid))
+		return;
+
+	/* Not for our STA and not broadcast, ignore */
+	if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)
+	    && !is_broadcast_ether_addr(mgmt->addr1))
+		return;
+
+	if (priv->mac_state != MAC_ASSOC && priv->mac_state != MAC_CONNECTED
+	    && priv->mac_state != MAC_JOINING) {
+		printk(KERN_INFO "%s: DisAssoc in state %s ignored\n",
+		       priv->netdev->name, mac_states[priv->mac_state]);
+		return;
+	}
+
+	if (priv->mac_state == MAC_CONNECTED) {
+		netif_carrier_off(priv->netdev);
+		netif_stop_queue(priv->netdev);
+		at76_iwevent_bss_disconnect(priv->netdev);
+	}
+	cancel_delayed_work(&priv->dwork_get_scan);
+	cancel_delayed_work(&priv->dwork_beacon);
+	cancel_delayed_work(&priv->dwork_auth);
+	cancel_delayed_work(&priv->dwork_assoc);
+	at76_set_mac_state(priv, MAC_JOINING);
+	schedule_work(&priv->work_join);
+}
+
+static void at76_rx_mgmt_auth(struct at76_priv *priv,
+			      struct at76_rx_buffer *buf)
+{
+	struct ieee80211_auth *resp = (struct ieee80211_auth *)buf->packet;
+	struct ieee80211_hdr_3addr *mgmt = &resp->header;
+	int seq_nr = le16_to_cpu(resp->transaction);
+	int alg = le16_to_cpu(resp->algorithm);
+	int status = le16_to_cpu(resp->status);
+
+	at76_dbg(DBG_RX_MGMT,
+		 "%s: rx AuthFrame bssid %s alg %d seq_nr %d status %d "
+		 "destination %s", priv->netdev->name, mac2str(mgmt->addr3),
+		 alg, seq_nr, status, mac2str(mgmt->addr1));
+
+	if (alg == WLAN_AUTH_SHARED_KEY && seq_nr == 2)
+		at76_dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...",
+			 priv->netdev->name, hex2str(resp->info_element, 18));
+
+	if (priv->mac_state != MAC_AUTH) {
+		printk(KERN_INFO "%s: ignored AuthFrame in state %s\n",
+		       priv->netdev->name, mac_states[priv->mac_state]);
+		return;
+	}
+	if (priv->auth_mode != alg) {
+		printk(KERN_INFO "%s: ignored AuthFrame for alg %d\n",
+		       priv->netdev->name, alg);
+		return;
+	}
+
+	BUG_ON(!priv->curr_bss);
+
+	/* Not our BSSID or not for our STA, ignore */
+	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid)
+	    || compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1))
+		return;
+
+	cancel_delayed_work(&priv->dwork_auth);
+	if (status != WLAN_STATUS_SUCCESS) {
+		/* try to join next bss */
+		at76_set_mac_state(priv, MAC_JOINING);
+		schedule_work(&priv->work_join);
+		return;
+	}
+
+	if (priv->auth_mode == WLAN_AUTH_OPEN || seq_nr == 4) {
+		priv->retries = ASSOC_RETRIES;
+		at76_set_mac_state(priv, MAC_ASSOC);
+		at76_assoc_req(priv, priv->curr_bss);
+		at76_dbg(DBG_MGMT_TIMER,
+			 "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__);
+		schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT);
+		return;
+	}
+
+	WARN_ON(seq_nr != 2);
+	at76_auth_req(priv, priv->curr_bss, seq_nr + 1, resp->info_element);
+	at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __func__,
+		 __LINE__);
+	schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
+}
+
+static void at76_rx_mgmt_deauth(struct at76_priv *priv,
+				struct at76_rx_buffer *buf)
+{
+	struct ieee80211_disassoc *resp =
+	    (struct ieee80211_disassoc *)buf->packet;
+	struct ieee80211_hdr_3addr *mgmt = &resp->header;
+
+	at76_dbg(DBG_RX_MGMT | DBG_PROGRESS,
+		 "%s: rx DeAuth bssid %s reason 0x%04x destination %s",
+		 priv->netdev->name, mac2str(mgmt->addr3),
+		 le16_to_cpu(resp->reason), mac2str(mgmt->addr1));
+
+	if (priv->mac_state != MAC_AUTH && priv->mac_state != MAC_ASSOC
+	    && priv->mac_state != MAC_CONNECTED) {
+		printk(KERN_INFO "%s: DeAuth in state %s ignored\n",
+		       priv->netdev->name, mac_states[priv->mac_state]);
+		return;
+	}
+
+	BUG_ON(!priv->curr_bss);
+
+	/* Not our BSSID, ignore */
+	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid))
+		return;
+
+	/* Not for our STA and not broadcast, ignore */
+	if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)
+	    && !is_broadcast_ether_addr(mgmt->addr1))
+		return;
+
+	if (priv->mac_state == MAC_CONNECTED)
+		at76_iwevent_bss_disconnect(priv->netdev);
+
+	at76_set_mac_state(priv, MAC_JOINING);
+	schedule_work(&priv->work_join);
+	cancel_delayed_work(&priv->dwork_get_scan);
+	cancel_delayed_work(&priv->dwork_beacon);
+	cancel_delayed_work(&priv->dwork_auth);
+	cancel_delayed_work(&priv->dwork_assoc);
+}
+
+static void at76_rx_mgmt_beacon(struct at76_priv *priv,
+				struct at76_rx_buffer *buf)
+{
+	int varpar_len;
+	/* beacon content */
+	struct ieee80211_beacon *bdata = (struct ieee80211_beacon *)buf->packet;
+	struct ieee80211_hdr_3addr *mgmt = &bdata->header;
+
+	struct list_head *lptr;
+	struct bss_info *match;	/* entry matching addr3 with its bssid */
+	int new_entry = 0;
+	int len;
+	struct ieee80211_info_element *ie;
+	int have_ssid = 0;
+	int have_rates = 0;
+	int have_channel = 0;
+	int keep_going = 1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
+	if (priv->mac_state == MAC_CONNECTED) {
+		/* in state MAC_CONNECTED we use the mgmt_timer to control
+		   the beacon of the BSS */
+		BUG_ON(!priv->curr_bss);
+
+		if (!compare_ether_addr(priv->curr_bss->bssid, mgmt->addr3)) {
+			/* We got our AP's beacon, defer the timeout handler.
+			   Kill pending work first, as schedule_delayed_work()
+			   won't do it. */
+			cancel_delayed_work(&priv->dwork_beacon);
+			schedule_delayed_work(&priv->dwork_beacon,
+					      BEACON_TIMEOUT);
+			priv->curr_bss->rssi = buf->rssi;
+			priv->beacons_received++;
+			goto exit;
+		}
+	}
+
+	/* look if we have this BSS already in the list */
+	match = NULL;
+
+	if (!list_empty(&priv->bss_list)) {
+		list_for_each(lptr, &priv->bss_list) {
+			struct bss_info *bss_ptr =
+			    list_entry(lptr, struct bss_info, list);
+			if (!compare_ether_addr(bss_ptr->bssid, mgmt->addr3)) {
+				match = bss_ptr;
+				break;
+			}
+		}
+	}
+
+	if (!match) {
+		/* BSS not in the list - append it */
+		match = kzalloc(sizeof(struct bss_info), GFP_ATOMIC);
+		if (!match) {
+			at76_dbg(DBG_BSS_TABLE,
+				 "%s: cannot kmalloc new bss info (%zd byte)",
+				 priv->netdev->name, sizeof(struct bss_info));
+			goto exit;
+		}
+		new_entry = 1;
+		list_add_tail(&match->list, &priv->bss_list);
+	}
+
+	match->capa = le16_to_cpu(bdata->capability);
+	match->beacon_interval = le16_to_cpu(bdata->beacon_interval);
+	match->rssi = buf->rssi;
+	match->link_qual = buf->link_quality;
+	match->noise_level = buf->noise_level;
+	memcpy(match->bssid, mgmt->addr3, ETH_ALEN);
+	at76_dbg(DBG_RX_BEACON, "%s: bssid %s", priv->netdev->name,
+		 mac2str(match->bssid));
+
+	ie = bdata->info_element;
+
+	/* length of var length beacon parameters */
+	varpar_len = min_t(int, le16_to_cpu(buf->wlength) -
+			   sizeof(struct ieee80211_beacon),
+			   BEACON_MAX_DATA_LENGTH);
+
+	/* This routine steps through the bdata->data array to get
+	 * some useful information about the access point.
+	 * Currently, this implementation supports receipt of: SSID,
+	 * supported transfer rates and channel, in any order, with some
+	 * tolerance for intermittent unknown codes (although this
+	 * functionality may not be necessary as the useful information will
+	 * usually arrive in consecutively, but there have been some
+	 * reports of some of the useful information fields arriving in a
+	 * different order).
+	 * It does not support any more IE types although MFIE_TYPE_TIM may
+	 * be supported (on my AP at least).
+	 * The bdata->data array is about 1500 bytes long but only ~36 of those
+	 * bytes are useful, hence the have_ssid etc optimizations. */
+
+	while (keep_going &&
+	       ((&ie->data[ie->len] - (u8 *)bdata->info_element) <=
+		varpar_len)) {
+
+		switch (ie->id) {
+
+		case MFIE_TYPE_SSID:
+			if (have_ssid)
+				break;
+
+			len = min_t(int, IW_ESSID_MAX_SIZE, ie->len);
+
+			/* we copy only if this is a new entry,
+			   or the incoming SSID is not a hidden SSID. This
+			   will protect us from overwriting a real SSID read
+			   in a ProbeResponse with a hidden one from a
+			   following beacon. */
+			if (!new_entry && at76_is_hidden_ssid(ie->data, len)) {
+				have_ssid = 1;
+				break;
+			}
+
+			match->ssid_len = len;
+			memcpy(match->ssid, ie->data, len);
+			at76_dbg(DBG_RX_BEACON, "%s: SSID - %.*s",
+				 priv->netdev->name, len, match->ssid);
+			have_ssid = 1;
+			break;
+
+		case MFIE_TYPE_RATES:
+			if (have_rates)
+				break;
+
+			match->rates_len =
+			    min_t(int, sizeof(match->rates), ie->len);
+			memcpy(match->rates, ie->data, match->rates_len);
+			have_rates = 1;
+			at76_dbg(DBG_RX_BEACON, "%s: SUPPORTED RATES %s",
+				 priv->netdev->name,
+				 hex2str(ie->data, ie->len));
+			break;
+
+		case MFIE_TYPE_DS_SET:
+			if (have_channel)
+				break;
+
+			match->channel = ie->data[0];
+			have_channel = 1;
+			at76_dbg(DBG_RX_BEACON, "%s: CHANNEL - %d",
+				 priv->netdev->name, match->channel);
+			break;
+
+		case MFIE_TYPE_CF_SET:
+		case MFIE_TYPE_TIM:
+		case MFIE_TYPE_IBSS_SET:
+		default:
+			at76_dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s",
+				 priv->netdev->name, ie->id, ie->len,
+				 hex2str(ie->data, ie->len));
+			break;
+		}
+
+		/* advance to the next informational element */
+		next_ie(&ie);
+
+		/* Optimization: after all, the bdata->data array is
+		 * varpar_len bytes long, whereas we get all of the useful
+		 * information after only ~36 bytes, this saves us a lot of
+		 * time (and trouble as the remaining portion of the array
+		 * could be full of junk)
+		 * Comment this out if you want to see what other information
+		 * comes from the AP - although little of it may be useful */
+	}
+
+	at76_dbg(DBG_RX_BEACON, "%s: Finished processing beacon data",
+		 priv->netdev->name);
+
+	match->last_rx = jiffies;	/* record last rx of beacon */
+
+exit:
+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
+}
+
+/* Calculate the link level from a given rx_buffer */
+static void at76_calc_level(struct at76_priv *priv, struct at76_rx_buffer *buf,
+			    struct iw_quality *qual)
+{
+	/* just a guess for now, might be different for other chips */
+	int max_rssi = 42;
+
+	qual->level = (buf->rssi * 100 / max_rssi);
+	if (qual->level > 100)
+		qual->level = 100;
+	qual->updated |= IW_QUAL_LEVEL_UPDATED;
+}
+
+/* Calculate the link quality from a given rx_buffer */
+static void at76_calc_qual(struct at76_priv *priv, struct at76_rx_buffer *buf,
+			   struct iw_quality *qual)
+{
+	if (at76_is_intersil(priv->board_type))
+		qual->qual = buf->link_quality;
+	else {
+		unsigned long elapsed;
+
+		/* Update qual at most once a second */
+		elapsed = jiffies - priv->beacons_last_qual;
+		if (elapsed < 1 * HZ)
+			return;
+
+		qual->qual = qual->level * priv->beacons_received *
+		    msecs_to_jiffies(priv->beacon_period) / elapsed;
+
+		priv->beacons_last_qual = jiffies;
+		priv->beacons_received = 0;
+	}
+	qual->qual = (qual->qual > 100) ? 100 : qual->qual;
+	qual->updated |= IW_QUAL_QUAL_UPDATED;
+}
+
+/* Calculate the noise quality from a given rx_buffer */
+static void at76_calc_noise(struct at76_priv *priv, struct at76_rx_buffer *buf,
+			    struct iw_quality *qual)
+{
+	qual->noise = 0;
+	qual->updated |= IW_QUAL_NOISE_INVALID;
+}
+
+static void at76_update_wstats(struct at76_priv *priv,
+			       struct at76_rx_buffer *buf)
+{
+	struct iw_quality *qual = &priv->wstats.qual;
+
+	if (buf->rssi && priv->mac_state == MAC_CONNECTED) {
+		qual->updated = 0;
+		at76_calc_level(priv, buf, qual);
+		at76_calc_qual(priv, buf, qual);
+		at76_calc_noise(priv, buf, qual);
+	} else {
+		qual->qual = 0;
+		qual->level = 0;
+		qual->noise = 0;
+		qual->updated = IW_QUAL_ALL_INVALID;
+	}
+}
+
+static void at76_rx_mgmt(struct at76_priv *priv, struct at76_rx_buffer *buf)
+{
+	struct ieee80211_hdr_3addr *mgmt =
+	    (struct ieee80211_hdr_3addr *)buf->packet;
+	u16 framectl = le16_to_cpu(mgmt->frame_ctl);
+
+	/* update wstats */
+	if (priv->mac_state != MAC_INIT && priv->mac_state != MAC_SCANNING) {
+		/* jal: this is a dirty hack needed by Tim in ad-hoc mode */
+		/* Data packets always seem to have a 0 link level, so we
+		   only read link quality info from management packets.
+		   Atmel driver actually averages the present, and previous
+		   values, we just present the raw value at the moment - TJS */
+		if (priv->iw_mode == IW_MODE_ADHOC
+		    || (priv->curr_bss
+			&& !compare_ether_addr(mgmt->addr3,
+					       priv->curr_bss->bssid)))
+			at76_update_wstats(priv, buf);
+	}
+
+	at76_dbg(DBG_RX_MGMT_CONTENT, "%s rx mgmt framectl 0x%x %s",
+		 priv->netdev->name, framectl,
+		 hex2str(mgmt, le16_to_cpu(buf->wlength)));
+
+	switch (framectl & IEEE80211_FCTL_STYPE) {
+	case IEEE80211_STYPE_BEACON:
+	case IEEE80211_STYPE_PROBE_RESP:
+		at76_rx_mgmt_beacon(priv, buf);
+		break;
+
+	case IEEE80211_STYPE_ASSOC_RESP:
+		at76_rx_mgmt_assoc(priv, buf);
+		break;
+
+	case IEEE80211_STYPE_DISASSOC:
+		at76_rx_mgmt_disassoc(priv, buf);
+		break;
+
+	case IEEE80211_STYPE_AUTH:
+		at76_rx_mgmt_auth(priv, buf);
+		break;
+
+	case IEEE80211_STYPE_DEAUTH:
+		at76_rx_mgmt_deauth(priv, buf);
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n",
+		       priv->netdev->name, framectl);
+	}
+
+	return;
+}
+
+/* Convert the 802.11 header into an ethernet-style header, make skb
+ * ready for consumption by netif_rx() */
+static void at76_ieee80211_to_eth(struct sk_buff *skb, int iw_mode)
+{
+	struct ieee80211_hdr_3addr *i802_11_hdr;
+	struct ethhdr *eth_hdr_p;
+	u8 *src_addr;
+	u8 *dest_addr;
+
+	i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data;
+
+	/* That would be the ethernet header if the hardware converted
+	 * the frame for us.  Make sure the source and the destination
+	 * match the 802.11 header.  Which hardware does it? */
+	eth_hdr_p = (struct ethhdr *)skb_pull(skb, IEEE80211_3ADDR_LEN);
+
+	dest_addr = i802_11_hdr->addr1;
+	if (iw_mode == IW_MODE_ADHOC)
+		src_addr = i802_11_hdr->addr2;
+	else
+		src_addr = i802_11_hdr->addr3;
+
+	if (!compare_ether_addr(eth_hdr_p->h_source, src_addr) &&
+	    !compare_ether_addr(eth_hdr_p->h_dest, dest_addr))
+		/* Yes, we already have an ethernet header */
+		skb_reset_mac_header(skb);
+	else {
+		u16 len;
+
+		/* Need to build an ethernet header */
+		if (!memcmp(skb->data, snapsig, sizeof(snapsig))) {
+			/* SNAP frame - decapsulate, keep proto */
+			skb_push(skb, offsetof(struct ethhdr, h_proto) -
+				 sizeof(rfc1042sig));
+			len = 0;
+		} else {
+			/* 802.3 frame, proto is length */
+			len = skb->len;
+			skb_push(skb, ETH_HLEN);
+		}
+
+		skb_reset_mac_header(skb);
+		eth_hdr_p = eth_hdr(skb);
+		/* This needs to be done in this order (eth_hdr_p->h_dest may
+		 * overlap src_addr) */
+		memcpy(eth_hdr_p->h_source, src_addr, ETH_ALEN);
+		memcpy(eth_hdr_p->h_dest, dest_addr, ETH_ALEN);
+		if (len)
+			eth_hdr_p->h_proto = htons(len);
+	}
+
+	skb->protocol = eth_type_trans(skb, skb->dev);
+}
+
+/* Check for fragmented data in priv->rx_skb. If the packet was no fragment
+   or it was the last of a fragment set a skb containing the whole packet
+   is returned for further processing. Otherwise we get NULL and are
+   done and the packet is either stored inside the fragment buffer
+   or thrown away.  Every returned skb starts with the ieee802_11 header
+   and contains _no_ FCS at the end */
+static struct sk_buff *at76_check_for_rx_frags(struct at76_priv *priv)
+{
+	struct sk_buff *skb = priv->rx_skb;
+	struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data;
+	struct ieee80211_hdr_3addr *i802_11_hdr =
+	    (struct ieee80211_hdr_3addr *)buf->packet;
+	/* seq_ctrl, fragment_number, sequence number of new packet */
+	u16 sctl = le16_to_cpu(i802_11_hdr->seq_ctl);
+	u16 fragnr = sctl & 0xf;
+	u16 seqnr = sctl >> 4;
+	u16 frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl);
+
+	/* Length including the IEEE802.11 header, but without the trailing
+	 * FCS and without the Atmel Rx header */
+	int length = le16_to_cpu(buf->wlength) - IEEE80211_FCS_LEN;
+
+	/* where does the data payload start in skb->data ? */
+	u8 *data = i802_11_hdr->payload;
+
+	/* length of payload, excl. the trailing FCS */
+	int data_len = length - IEEE80211_3ADDR_LEN;
+
+	int i;
+	struct rx_data_buf *bptr, *optr;
+	unsigned long oldest = ~0UL;
+
+	at76_dbg(DBG_RX_FRAGS,
+		 "%s: rx data frame_ctl %04x addr2 %s seq/frag %d/%d "
+		 "length %d data %d: %s ...", priv->netdev->name, frame_ctl,
+		 mac2str(i802_11_hdr->addr2), seqnr, fragnr, length, data_len,
+		 hex2str(data, 32));
+
+	at76_dbg(DBG_RX_FRAGS_SKB, "%s: incoming skb: head %p data %p "
+		 "tail %p end %p len %d", priv->netdev->name, skb->head,
+		 skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
+		 skb->len);
+
+	if (data_len < 0) {
+		/* make sure data starts in the buffer */
+		printk(KERN_INFO "%s: data frame too short\n",
+		       priv->netdev->name);
+		return NULL;
+	}
+
+	WARN_ON(length <= AT76_RX_HDRLEN);
+	if (length <= AT76_RX_HDRLEN)
+		return NULL;
+
+	/* remove the at76_rx_buffer header - we don't need it anymore */
+	/* we need the IEEE802.11 header (for the addresses) if this packet
+	   is the first of a chain */
+	skb_pull(skb, AT76_RX_HDRLEN);
+
+	/* remove FCS at end */
+	skb_trim(skb, length);
+
+	at76_dbg(DBG_RX_FRAGS_SKB, "%s: trimmed skb: head %p data %p tail %p "
+		 "end %p len %d data %p data_len %d", priv->netdev->name,
+		 skb->head, skb->data, skb_tail_pointer(skb),
+		 skb_end_pointer(skb), skb->len, data, data_len);
+
+	if (fragnr == 0 && !(frame_ctl & IEEE80211_FCTL_MOREFRAGS)) {
+		/* unfragmented packet received */
+		/* Use a new skb for the next receive */
+		priv->rx_skb = NULL;
+		at76_dbg(DBG_RX_FRAGS, "%s: unfragmented", priv->netdev->name);
+		return skb;
+	}
+
+	/* look if we've got a chain for the sender address.
+	   afterwards optr points to first free or the oldest entry,
+	   or, if i < NR_RX_DATA_BUF, bptr points to the entry for the
+	   sender address */
+	/* determining the oldest entry doesn't cope with jiffies wrapping
+	   but I don't care to delete a young entry at these rare moments ... */
+
+	bptr = priv->rx_data;
+	optr = NULL;
+	for (i = 0; i < NR_RX_DATA_BUF; i++, bptr++) {
+		if (!bptr->skb) {
+			optr = bptr;
+			oldest = 0UL;
+			continue;
+		}
+
+		if (!compare_ether_addr(i802_11_hdr->addr2, bptr->sender))
+			break;
+
+		if (!optr) {
+			optr = bptr;
+			oldest = bptr->last_rx;
+		} else if (bptr->last_rx < oldest)
+			optr = bptr;
+	}
+
+	if (i < NR_RX_DATA_BUF) {
+
+		at76_dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag = %d/%d) "
+			 "matched sender addr",
+			 priv->netdev->name, i, bptr->seqnr, bptr->fragnr);
+
+		/* bptr points to an entry for the sender address */
+		if (bptr->seqnr == seqnr) {
+			int left;
+			/* the fragment has the current sequence number */
+			if (((bptr->fragnr + 1) & 0xf) != fragnr) {
+				/* wrong fragment number -> ignore it */
+				/* is & 0xf necessary above ??? */
+				at76_dbg(DBG_RX_FRAGS,
+					 "%s: frag nr mismatch: %d + 1 != %d",
+					 priv->netdev->name, bptr->fragnr,
+					 fragnr);
+				return NULL;
+			}
+			bptr->last_rx = jiffies;
+			/* the next following fragment number ->
+			   add the data at the end */
+
+			/* for test only ??? */
+			left = skb_tailroom(bptr->skb);
+			if (left < data_len)
+				printk(KERN_INFO
+				       "%s: only %d byte free (need %d)\n",
+				       priv->netdev->name, left, data_len);
+			else
+				memcpy(skb_put(bptr->skb, data_len), data,
+				       data_len);
+
+			bptr->fragnr = fragnr;
+			if (frame_ctl & IEEE80211_FCTL_MOREFRAGS)
+				return NULL;
+
+			/* this was the last fragment - send it */
+			skb = bptr->skb;
+			bptr->skb = NULL;	/* free the entry */
+			at76_dbg(DBG_RX_FRAGS, "%s: last frag of seq %d",
+				 priv->netdev->name, seqnr);
+			return skb;
+		}
+
+		/* got another sequence number */
+		if (fragnr == 0) {
+			/* it's the start of a new chain - replace the
+			   old one by this */
+			/* bptr->sender has the correct value already */
+			at76_dbg(DBG_RX_FRAGS,
+				 "%s: start of new seq %d, removing old seq %d",
+				 priv->netdev->name, seqnr, bptr->seqnr);
+			bptr->seqnr = seqnr;
+			bptr->fragnr = 0;
+			bptr->last_rx = jiffies;
+			/* swap bptr->skb and priv->rx_skb */
+			skb = bptr->skb;
+			bptr->skb = priv->rx_skb;
+			priv->rx_skb = skb;
+		} else {
+			/* it from the middle of a new chain ->
+			   delete the old entry and skip the new one */
+			at76_dbg(DBG_RX_FRAGS,
+				 "%s: middle of new seq %d (%d) "
+				 "removing old seq %d",
+				 priv->netdev->name, seqnr, fragnr,
+				 bptr->seqnr);
+			dev_kfree_skb(bptr->skb);
+			bptr->skb = NULL;
+		}
+		return NULL;
+	}
+
+	/* if we didn't find a chain for the sender address, optr
+	   points either to the first free or the oldest entry */
+
+	if (fragnr != 0) {
+		/* this is not the begin of a fragment chain ... */
+		at76_dbg(DBG_RX_FRAGS,
+			 "%s: no chain for non-first fragment (%d)",
+			 priv->netdev->name, fragnr);
+		return NULL;
+	}
+
+	BUG_ON(!optr);
+	if (optr->skb) {
+		/* swap the skb's */
+		skb = optr->skb;
+		optr->skb = priv->rx_skb;
+		priv->rx_skb = skb;
+
+		at76_dbg(DBG_RX_FRAGS,
+			 "%s: free old contents: sender %s seq/frag %d/%d",
+			 priv->netdev->name, mac2str(optr->sender),
+			 optr->seqnr, optr->fragnr);
+
+	} else {
+		/* take the skb from priv->rx_skb */
+		optr->skb = priv->rx_skb;
+		/* let at76_submit_rx_urb() allocate a new skb */
+		priv->rx_skb = NULL;
+
+		at76_dbg(DBG_RX_FRAGS, "%s: use a free entry",
+			 priv->netdev->name);
+	}
+	memcpy(optr->sender, i802_11_hdr->addr2, ETH_ALEN);
+	optr->seqnr = seqnr;
+	optr->fragnr = 0;
+	optr->last_rx = jiffies;
+
+	return NULL;
+}
+
+/* Rx interrupt: we expect the complete data buffer in priv->rx_skb */
+static void at76_rx_data(struct at76_priv *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	struct net_device_stats *stats = &priv->stats;
+	struct sk_buff *skb = priv->rx_skb;
+	struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data;
+	struct ieee80211_hdr_3addr *i802_11_hdr;
+	int length = le16_to_cpu(buf->wlength);
+
+	at76_dbg(DBG_RX_DATA, "%s received data packet: %s", netdev->name,
+		 hex2str(skb->data, AT76_RX_HDRLEN));
+
+	at76_dbg(DBG_RX_DATA_CONTENT, "rx packet: %s",
+		 hex2str(skb->data + AT76_RX_HDRLEN, length));
+
+	skb = at76_check_for_rx_frags(priv);
+	if (!skb)
+		return;
+
+	/* Atmel header and the FCS are already removed */
+	i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data;
+
+	skb->dev = netdev;
+	skb->ip_summed = CHECKSUM_NONE;	/* TODO: should check CRC */
+
+	if (is_broadcast_ether_addr(i802_11_hdr->addr1)) {
+		if (!compare_ether_addr(i802_11_hdr->addr1, netdev->broadcast))
+			skb->pkt_type = PACKET_BROADCAST;
+		else
+			skb->pkt_type = PACKET_MULTICAST;
+	} else if (compare_ether_addr(i802_11_hdr->addr1, netdev->dev_addr))
+		skb->pkt_type = PACKET_OTHERHOST;
+
+	at76_ieee80211_to_eth(skb, priv->iw_mode);
+
+	netdev->last_rx = jiffies;
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += length;
+
+	return;
+}
+
+static void at76_rx_monitor_mode(struct at76_priv *priv)
+{
+	struct at76_rx_radiotap *rt;
+	u8 *payload;
+	int skblen;
+	struct net_device *netdev = priv->netdev;
+	struct at76_rx_buffer *buf =
+	    (struct at76_rx_buffer *)priv->rx_skb->data;
+	/* length including the IEEE802.11 header and the trailing FCS,
+	   but not at76_rx_buffer */
+	int length = le16_to_cpu(buf->wlength);
+	struct sk_buff *skb = priv->rx_skb;
+	struct net_device_stats *stats = &priv->stats;
+
+	if (length < IEEE80211_FCS_LEN) {
+		/* buffer contains no data */
+		at76_dbg(DBG_MONITOR_MODE,
+			 "%s: MONITOR MODE: rx skb without data",
+			 priv->netdev->name);
+		return;
+	}
+
+	skblen = sizeof(struct at76_rx_radiotap) + length;
+
+	skb = dev_alloc_skb(skblen);
+	if (!skb) {
+		printk(KERN_ERR "%s: MONITOR MODE: dev_alloc_skb for radiotap "
+		       "header returned NULL\n", priv->netdev->name);
+		return;
+	}
+
+	skb_put(skb, skblen);
+
+	rt = (struct at76_rx_radiotap *)skb->data;
+	payload = skb->data + sizeof(struct at76_rx_radiotap);
+
+	rt->rt_hdr.it_version = 0;
+	rt->rt_hdr.it_pad = 0;
+	rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct at76_rx_radiotap));
+	rt->rt_hdr.it_present = cpu_to_le32(AT76_RX_RADIOTAP_PRESENT);
+
+	rt->rt_tsft = cpu_to_le64(le32_to_cpu(buf->rx_time));
+	rt->rt_rate = hw_rates[buf->rx_rate] & (~0x80);
+	rt->rt_signal = buf->rssi;
+	rt->rt_noise = buf->noise_level;
+	rt->rt_flags = IEEE80211_RADIOTAP_F_FCS;
+	if (buf->fragmentation)
+		rt->rt_flags |= IEEE80211_RADIOTAP_F_FRAG;
+
+	memcpy(payload, buf->packet, length);
+	skb->dev = netdev;
+	skb->ip_summed = CHECKSUM_NONE;
+	skb_reset_mac_header(skb);
+	skb->pkt_type = PACKET_OTHERHOST;
+	skb->protocol = htons(ETH_P_802_2);
+
+	netdev->last_rx = jiffies;
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += length;
+}
+
+/* Check if we spy on the sender address in buf and update stats */
+static void at76_iwspy_update(struct at76_priv *priv,
+			      struct at76_rx_buffer *buf)
+{
+	struct ieee80211_hdr_3addr *hdr =
+	    (struct ieee80211_hdr_3addr *)buf->packet;
+	struct iw_quality qual;
+
+	/* We can only set the level here */
+	qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
+	qual.level = 0;
+	qual.noise = 0;
+	at76_calc_level(priv, buf, &qual);
+
+	spin_lock_bh(&priv->spy_spinlock);
+
+	if (priv->spy_data.spy_number > 0)
+		wireless_spy_update(priv->netdev, hdr->addr2, &qual);
+
+	spin_unlock_bh(&priv->spy_spinlock);
+}
+
+static void at76_rx_tasklet(unsigned long param)
+{
+	struct urb *urb = (struct urb *)param;
+	struct at76_priv *priv = urb->context;
+	struct net_device *netdev = priv->netdev;
+	struct at76_rx_buffer *buf;
+	struct ieee80211_hdr_3addr *i802_11_hdr;
+	u16 frame_ctl;
+
+	if (priv->device_unplugged) {
+		at76_dbg(DBG_DEVSTART, "device unplugged");
+		if (urb)
+			at76_dbg(DBG_DEVSTART, "urb status %d", urb->status);
+		return;
+	}
+
+	if (!priv->rx_skb || !netdev || !priv->rx_skb->data)
+		return;
+
+	buf = (struct at76_rx_buffer *)priv->rx_skb->data;
+
+	i802_11_hdr = (struct ieee80211_hdr_3addr *)buf->packet;
+
+	frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl);
+
+	if (urb->status != 0) {
+		if (urb->status != -ENOENT && urb->status != -ECONNRESET)
+			at76_dbg(DBG_URB,
+				 "%s %s: - nonzero Rx bulk status received: %d",
+				 __func__, netdev->name, urb->status);
+		return;
+	}
+
+	at76_dbg(DBG_RX_ATMEL_HDR,
+		 "%s: rx frame: rate %d rssi %d noise %d link %d %s",
+		 priv->netdev->name, buf->rx_rate, buf->rssi, buf->noise_level,
+		 buf->link_quality, hex2str(i802_11_hdr, 48));
+	if (priv->iw_mode == IW_MODE_MONITOR) {
+		at76_rx_monitor_mode(priv);
+		goto exit;
+	}
+
+	/* there is a new bssid around, accept it: */
+	if (buf->newbss && priv->iw_mode == IW_MODE_ADHOC) {
+		at76_dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name);
+		schedule_work(&priv->work_new_bss);
+	}
+
+	switch (frame_ctl & IEEE80211_FCTL_FTYPE) {
+	case IEEE80211_FTYPE_DATA:
+		at76_rx_data(priv);
+		break;
+
+	case IEEE80211_FTYPE_MGMT:
+		/* jal: TODO: find out if we can update iwspy also on
+		   other frames than management (might depend on the
+		   radio chip / firmware version !) */
+
+		at76_iwspy_update(priv, buf);
+
+		at76_rx_mgmt(priv, buf);
+		break;
+
+	case IEEE80211_FTYPE_CTL:
+		at76_dbg(DBG_RX_CTRL, "%s: ignored ctrl frame: %04x",
+			 priv->netdev->name, frame_ctl);
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n",
+		       priv->netdev->name, frame_ctl);
+	}
+exit:
+	at76_submit_rx_urb(priv);
+}
+
+/* Load firmware into kernel memory and parse it */
+static struct fwentry *at76_load_firmware(struct usb_device *udev,
+					  enum board_type board_type)
+{
+	int ret;
+	char *str;
+	struct at76_fw_header *fwh;
+	struct fwentry *fwe = &firmwares[board_type];
+
+	mutex_lock(&fw_mutex);
+
+	if (fwe->loaded) {
+		at76_dbg(DBG_FW, "re-using previously loaded fw");
+		goto exit;
+	}
+
+	at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname);
+	ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev);
+	if (ret < 0) {
+		dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n",
+			   fwe->fwname);
+		dev_printk(KERN_ERR, &udev->dev,
+			   "you may need to download the firmware from "
+			   "http://developer.berlios.de/projects/at76c503a/");
+		goto exit;
+	}
+
+	at76_dbg(DBG_FW, "got it.");
+	fwh = (struct at76_fw_header *)(fwe->fw->data);
+
+	if (fwe->fw->size <= sizeof(*fwh)) {
+		dev_printk(KERN_ERR, &udev->dev,
+			   "firmware is too short (0x%zx)\n", fwe->fw->size);
+		goto exit;
+	}
+
+	/* CRC currently not checked */
+	fwe->board_type = le32_to_cpu(fwh->board_type);
+	if (fwe->board_type != board_type) {
+		dev_printk(KERN_ERR, &udev->dev,
+			   "board type mismatch, requested %u, got %u\n",
+			   board_type, fwe->board_type);
+		goto exit;
+	}
+
+	fwe->fw_version.major = fwh->major;
+	fwe->fw_version.minor = fwh->minor;
+	fwe->fw_version.patch = fwh->patch;
+	fwe->fw_version.build = fwh->build;
+
+	str = (char *)fwh + le32_to_cpu(fwh->str_offset);
+	fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset);
+	fwe->intfw_size = le32_to_cpu(fwh->int_fw_len);
+	fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset);
+	fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len);
+
+	fwe->loaded = 1;
+
+	dev_printk(KERN_DEBUG, &udev->dev,
+		   "using firmware %s (version %d.%d.%d-%d)\n",
+		   fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build);
+
+	at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type,
+		 le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len),
+		 le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len));
+	at76_dbg(DBG_DEVSTART, "firmware id %s", str);
+
+exit:
+	mutex_unlock(&fw_mutex);
+
+	if (fwe->loaded)
+		return fwe;
+	else
+		return NULL;
+}
+
+/* Allocate network device and initialize private data */
+static struct at76_priv *at76_alloc_new_device(struct usb_device *udev)
+{
+	struct net_device *netdev;
+	struct at76_priv *priv;
+	int i;
+
+	/* allocate memory for our device state and initialize it */
+	netdev = alloc_etherdev(sizeof(struct at76_priv));
+	if (!netdev) {
+		dev_printk(KERN_ERR, &udev->dev, "out of memory\n");
+		return NULL;
+	}
+
+	priv = netdev_priv(netdev);
+
+	priv->udev = udev;
+	priv->netdev = netdev;
+
+	mutex_init(&priv->mtx);
+	INIT_WORK(&priv->work_assoc_done, at76_work_assoc_done);
+	INIT_WORK(&priv->work_join, at76_work_join);
+	INIT_WORK(&priv->work_new_bss, at76_work_new_bss);
+	INIT_WORK(&priv->work_start_scan, at76_work_start_scan);
+	INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc);
+	INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx);
+	INIT_DELAYED_WORK(&priv->dwork_restart, at76_dwork_restart);
+	INIT_DELAYED_WORK(&priv->dwork_get_scan, at76_dwork_get_scan);
+	INIT_DELAYED_WORK(&priv->dwork_beacon, at76_dwork_beacon);
+	INIT_DELAYED_WORK(&priv->dwork_auth, at76_dwork_auth);
+	INIT_DELAYED_WORK(&priv->dwork_assoc, at76_dwork_assoc);
+
+	spin_lock_init(&priv->mgmt_spinlock);
+	priv->next_mgmt_bulk = NULL;
+	priv->mac_state = MAC_INIT;
+
+	/* initialize empty BSS list */
+	priv->curr_bss = NULL;
+	INIT_LIST_HEAD(&priv->bss_list);
+	spin_lock_init(&priv->bss_list_spinlock);
+
+	init_timer(&priv->bss_list_timer);
+	priv->bss_list_timer.data = (unsigned long)priv;
+	priv->bss_list_timer.function = at76_bss_list_timeout;
+
+	spin_lock_init(&priv->spy_spinlock);
+
+	/* mark all rx data entries as unused */
+	for (i = 0; i < NR_RX_DATA_BUF; i++)
+		priv->rx_data[i].skb = NULL;
+
+	priv->rx_tasklet.func = at76_rx_tasklet;
+	priv->rx_tasklet.data = 0;
+
+	priv->pm_mode = AT76_PM_OFF;
+	priv->pm_period = 0;
+
+	return priv;
+}
+
+static int at76_alloc_urbs(struct at76_priv *priv,
+			   struct usb_interface *interface)
+{
+	struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out;
+	int i;
+	int buffer_size;
+	struct usb_host_interface *iface_desc;
+
+	at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
+
+	at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__,
+		 interface->altsetting[0].desc.bNumEndpoints);
+
+	ep_in = NULL;
+	ep_out = NULL;
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x",
+			 __func__, i, endpoint->bEndpointAddress,
+			 endpoint->bmAttributes);
+
+		if (!ep_in && usb_endpoint_is_bulk_in(endpoint))
+			ep_in = endpoint;
+
+		if (!ep_out && usb_endpoint_is_bulk_out(endpoint))
+			ep_out = endpoint;
+	}
+
+	if (!ep_in || !ep_out) {
+		dev_printk(KERN_ERR, &interface->dev,
+			   "bulk endpoints missing\n");
+		return -ENXIO;
+	}
+
+	priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress);
+	priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress);
+
+	priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!priv->rx_urb || !priv->tx_urb) {
+		dev_printk(KERN_ERR, &interface->dev, "cannot allocate URB\n");
+		return -ENOMEM;
+	}
+
+	buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE;
+	priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!priv->bulk_out_buffer) {
+		dev_printk(KERN_ERR, &interface->dev,
+			   "cannot allocate output buffer\n");
+		return -ENOMEM;
+	}
+
+	at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
+
+	return 0;
+}
+
+/* Register network device and initialize the hardware */
+static int at76_init_new_device(struct at76_priv *priv,
+				struct usb_interface *interface)
+{
+	struct net_device *netdev = priv->netdev;
+	int ret;
+
+	/* set up the endpoint information */
+	/* check out the endpoints */
+
+	at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints",
+		 interface->cur_altsetting->desc.bNumEndpoints);
+
+	ret = at76_alloc_urbs(priv, interface);
+	if (ret < 0)
+		goto exit;
+
+	/* MAC address */
+	ret = at76_get_hw_config(priv);
+	if (ret < 0) {
+		dev_printk(KERN_ERR, &interface->dev,
+			   "cannot get MAC address\n");
+		goto exit;
+	}
+
+	priv->domain = at76_get_reg_domain(priv->regulatory_domain);
+	/* init. netdev->dev_addr */
+	memcpy(netdev->dev_addr, priv->mac_addr, ETH_ALEN);
+
+	priv->channel = DEF_CHANNEL;
+	priv->iw_mode = IW_MODE_INFRA;
+	priv->rts_threshold = DEF_RTS_THRESHOLD;
+	priv->frag_threshold = DEF_FRAG_THRESHOLD;
+	priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT;
+	priv->txrate = TX_RATE_AUTO;
+	priv->preamble_type = PREAMBLE_TYPE_LONG;
+	priv->beacon_period = 100;
+	priv->beacons_last_qual = jiffies;
+	priv->auth_mode = WLAN_AUTH_OPEN;
+	priv->scan_min_time = DEF_SCAN_MIN_TIME;
+	priv->scan_max_time = DEF_SCAN_MAX_TIME;
+	priv->scan_mode = SCAN_TYPE_ACTIVE;
+
+	netdev->flags &= ~IFF_MULTICAST;	/* not yet or never */
+	netdev->open = at76_open;
+	netdev->stop = at76_stop;
+	netdev->get_stats = at76_get_stats;
+	netdev->ethtool_ops = &at76_ethtool_ops;
+
+	/* Add pointers to enable iwspy support. */
+	priv->wireless_data.spy_data = &priv->spy_data;
+	netdev->wireless_data = &priv->wireless_data;
+
+	netdev->hard_start_xmit = at76_tx;
+	netdev->tx_timeout = at76_tx_timeout;
+	netdev->watchdog_timeo = 2 * HZ;
+	netdev->wireless_handlers = &at76_handler_def;
+	netdev->set_multicast_list = at76_set_multicast;
+	netdev->set_mac_address = at76_set_mac_address;
+	dev_alloc_name(netdev, "wlan%d");
+
+	ret = register_netdev(priv->netdev);
+	if (ret) {
+		dev_printk(KERN_ERR, &interface->dev,
+			   "cannot register netdevice (status %d)!\n", ret);
+		goto exit;
+	}
+	priv->netdev_registered = 1;
+
+	printk(KERN_INFO "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n",
+	       netdev->name, interface->dev.bus_id, mac2str(priv->mac_addr),
+	       priv->fw_version.major, priv->fw_version.minor,
+	       priv->fw_version.patch, priv->fw_version.build);
+	printk(KERN_INFO "%s: regulatory domain 0x%02x: %s\n", netdev->name,
+	       priv->regulatory_domain, priv->domain->name);
+
+	/* we let this timer run the whole time this driver instance lives */
+	mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT);
+
+exit:
+	return ret;
+}
+
+static void at76_delete_device(struct at76_priv *priv)
+{
+	int i;
+
+	at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
+
+	/* The device is gone, don't bother turning it off */
+	priv->device_unplugged = 1;
+
+	if (priv->netdev_registered)
+		unregister_netdev(priv->netdev);
+
+	/* assuming we used keventd, it must quiesce too */
+	flush_scheduled_work();
+
+	kfree(priv->bulk_out_buffer);
+
+	if (priv->tx_urb) {
+		usb_kill_urb(priv->tx_urb);
+		usb_free_urb(priv->tx_urb);
+	}
+	if (priv->rx_urb) {
+		usb_kill_urb(priv->rx_urb);
+		usb_free_urb(priv->rx_urb);
+	}
+
+	at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__);
+
+	if (priv->rx_skb)
+		kfree_skb(priv->rx_skb);
+
+	at76_free_bss_list(priv);
+	del_timer_sync(&priv->bss_list_timer);
+	cancel_delayed_work(&priv->dwork_get_scan);
+	cancel_delayed_work(&priv->dwork_beacon);
+	cancel_delayed_work(&priv->dwork_auth);
+	cancel_delayed_work(&priv->dwork_assoc);
+
+	if (priv->mac_state == MAC_CONNECTED)
+		at76_iwevent_bss_disconnect(priv->netdev);
+
+	for (i = 0; i < NR_RX_DATA_BUF; i++)
+		if (priv->rx_data[i].skb) {
+			dev_kfree_skb(priv->rx_data[i].skb);
+			priv->rx_data[i].skb = NULL;
+		}
+	usb_put_dev(priv->udev);
+
+	at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/netdev", __func__);
+	free_netdev(priv->netdev);	/* priv is in netdev */
+
+	at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
+}
+
+static int at76_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id)
+{
+	int ret;
+	struct at76_priv *priv;
+	struct fwentry *fwe;
+	struct usb_device *udev;
+	int op_mode;
+	int need_ext_fw = 0;
+	struct mib_fw_version fwv;
+	int board_type = (int)id->driver_info;
+
+	udev = usb_get_dev(interface_to_usbdev(interface));
+
+	/* Load firmware into kernel memory */
+	fwe = at76_load_firmware(udev, board_type);
+	if (!fwe) {
+		ret = -ENOENT;
+		goto error;
+	}
+
+	op_mode = at76_get_op_mode(udev);
+
+	at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
+
+	/* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ???
+	   we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */
+
+	if (op_mode == OPMODE_HW_CONFIG_MODE) {
+		dev_printk(KERN_ERR, &interface->dev,
+			   "cannot handle a device in HW_CONFIG_MODE\n");
+		ret = -EBUSY;
+		goto error;
+	}
+
+	if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH
+	    && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
+		/* download internal firmware part */
+		dev_printk(KERN_DEBUG, &interface->dev,
+			   "downloading internal firmware\n");
+		ret = at76_load_internal_fw(udev, fwe);
+		if (ret < 0) {
+			dev_printk(KERN_ERR, &interface->dev,
+				   "error %d downloading internal firmware\n",
+				   ret);
+			goto error;
+		}
+		usb_put_dev(udev);
+		return ret;
+	}
+
+	/* Internal firmware already inside the device.  Get firmware
+	 * version to test if external firmware is loaded.
+	 * This works only for newer firmware, e.g. the Intersil 0.90.x
+	 * says "control timeout on ep0in" and subsequent
+	 * at76_get_op_mode() fail too :-( */
+
+	/* if version >= 0.100.x.y or device with built-in flash we can
+	 * query the device for the fw version */
+	if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)
+	    || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) {
+		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
+		if (ret < 0 || (fwv.major | fwv.minor) == 0)
+			need_ext_fw = 1;
+	} else
+		/* No way to check firmware version, reload to be sure */
+		need_ext_fw = 1;
+
+	if (need_ext_fw) {
+		dev_printk(KERN_DEBUG, &interface->dev,
+			   "downloading external firmware\n");
+
+		ret = at76_load_external_fw(udev, fwe);
+		if (ret)
+			goto error;
+
+		/* Re-check firmware version */
+		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
+		if (ret < 0) {
+			dev_printk(KERN_ERR, &interface->dev,
+				   "error %d getting firmware version\n", ret);
+			goto error;
+		}
+	}
+
+	priv = at76_alloc_new_device(udev);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	SET_NETDEV_DEV(priv->netdev, &interface->dev);
+	usb_set_intfdata(interface, priv);
+
+	memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version));
+	priv->board_type = board_type;
+
+	ret = at76_init_new_device(priv, interface);
+	if (ret < 0)
+		at76_delete_device(priv);
+
+	return ret;
+
+error:
+	usb_put_dev(udev);
+	return ret;
+}
+
+static void at76_disconnect(struct usb_interface *interface)
+{
+	struct at76_priv *priv;
+
+	priv = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	/* Disconnect after loading internal firmware */
+	if (!priv)
+		return;
+
+	printk(KERN_INFO "%s: disconnecting\n", priv->netdev->name);
+	at76_delete_device(priv);
+	dev_printk(KERN_INFO, &interface->dev, "disconnected\n");
+}
+
+/* Structure for registering this driver with the USB subsystem */
+static struct usb_driver at76_driver = {
+	.name = DRIVER_NAME,
+	.probe = at76_probe,
+	.disconnect = at76_disconnect,
+	.id_table = dev_table,
+};
+
+static int __init at76_mod_init(void)
+{
+	int result;
+
+	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n");
+
+	mutex_init(&fw_mutex);
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&at76_driver);
+	if (result < 0)
+		printk(KERN_ERR DRIVER_NAME
+		       ": usb_register failed (status %d)\n", result);
+
+	led_trigger_register_simple("at76_usb-tx", &ledtrig_tx);
+	return result;
+}
+
+static void __exit at76_mod_exit(void)
+{
+	int i;
+
+	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n");
+	usb_deregister(&at76_driver);
+	for (i = 0; i < ARRAY_SIZE(firmwares); i++) {
+		if (firmwares[i].fw)
+			release_firmware(firmwares[i].fw);
+	}
+	led_trigger_unregister_simple(ledtrig_tx);
+}
+
+module_param_named(debug, at76_debug, int, 0600);
+MODULE_PARM_DESC(debug, "Debugging level");
+
+module_init(at76_mod_init);
+module_exit(at76_mod_exit);
+
+MODULE_AUTHOR("Oliver Kurth <oku@masqmail.cx>");
+MODULE_AUTHOR("Joerg Albert <joerg.albert@gmx.de>");
+MODULE_AUTHOR("Alex <alex@foogod.com>");
+MODULE_AUTHOR("Nick Jones");
+MODULE_AUTHOR("Balint Seeber <n0_5p4m_p13453@hotmail.com>");
+MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff -puN /dev/null drivers/net/wireless/at76_usb.h
--- /dev/null
+++ a/drivers/net/wireless/at76_usb.h
@@ -0,0 +1,619 @@
+/*
+ * Copyright (c) 2002,2003 Oliver Kurth
+ *	     (c) 2003,2004 Joerg Albert <joerg.albert@gmx.de>
+ *	     (c) 2007 Guido Guenther <agx@sigxcpu.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This driver was based on information from the Sourceforge driver
+ * released and maintained by Atmel:
+ *
+ *  http://sourceforge.net/projects/atmelwlandriver/
+ *
+ * Although the code was completely re-written,
+ * it would have been impossible without Atmel's decision to
+ * release an Open Source driver (unfortunately the firmware was
+ * kept binary only). Thanks for that decision to Atmel!
+ */
+
+#ifndef _AT76_USB_H
+#define _AT76_USB_H
+
+/* Board types */
+enum board_type {
+	BOARD_503_ISL3861 = 1,
+	BOARD_503_ISL3863 = 2,
+	BOARD_503 = 3,
+	BOARD_503_ACC = 4,
+	BOARD_505 = 5,
+	BOARD_505_2958 = 6,
+	BOARD_505A = 7,
+	BOARD_505AMX = 8
+};
+
+/* our private ioctl's */
+/* preamble length (0 - long, 1 - short, 2 - auto) */
+#define AT76_SET_SHORT_PREAMBLE		(SIOCIWFIRSTPRIV + 0)
+#define AT76_GET_SHORT_PREAMBLE		(SIOCIWFIRSTPRIV + 1)
+/* which debug channels are enabled */
+#define AT76_SET_DEBUG			(SIOCIWFIRSTPRIV + 2)
+#define AT76_GET_DEBUG			(SIOCIWFIRSTPRIV + 3)
+/* power save mode (incl. the Atmel proprietary smart save mode) */
+#define AT76_SET_POWERSAVE_MODE		(SIOCIWFIRSTPRIV + 4)
+#define AT76_GET_POWERSAVE_MODE		(SIOCIWFIRSTPRIV + 5)
+/* min and max channel times for scan */
+#define AT76_SET_SCAN_TIMES		(SIOCIWFIRSTPRIV + 6)
+#define AT76_GET_SCAN_TIMES		(SIOCIWFIRSTPRIV + 7)
+/* scan mode (0 - active, 1 - passive) */
+#define AT76_SET_SCAN_MODE		(SIOCIWFIRSTPRIV + 8)
+#define AT76_GET_SCAN_MODE		(SIOCIWFIRSTPRIV + 9)
+
+#define CMD_STATUS_IDLE				0x00
+#define CMD_STATUS_COMPLETE			0x01
+#define CMD_STATUS_UNKNOWN			0x02
+#define CMD_STATUS_INVALID_PARAMETER		0x03
+#define CMD_STATUS_FUNCTION_NOT_SUPPORTED	0x04
+#define CMD_STATUS_TIME_OUT			0x07
+#define CMD_STATUS_IN_PROGRESS			0x08
+#define CMD_STATUS_HOST_FAILURE			0xff
+#define CMD_STATUS_SCAN_FAILED			0xf0
+
+/* answers to get op mode */
+#define OPMODE_NONE				0x00
+#define OPMODE_NORMAL_NIC_WITH_FLASH		0x01
+#define OPMODE_HW_CONFIG_MODE			0x02
+#define OPMODE_DFU_MODE_WITH_FLASH		0x03
+#define OPMODE_NORMAL_NIC_WITHOUT_FLASH		0x04
+
+#define CMD_SET_MIB		0x01
+#define CMD_GET_MIB		0x02
+#define CMD_SCAN		0x03
+#define CMD_JOIN		0x04
+#define CMD_START_IBSS		0x05
+#define CMD_RADIO_ON		0x06
+#define CMD_RADIO_OFF		0x07
+#define CMD_STARTUP		0x0B
+
+#define MIB_LOCAL		0x01
+#define MIB_MAC_ADDR		0x02
+#define MIB_MAC			0x03
+#define MIB_MAC_MGMT		0x05
+#define MIB_MAC_WEP		0x06
+#define MIB_PHY			0x07
+#define MIB_FW_VERSION		0x08
+#define MIB_MDOMAIN		0x09
+
+#define ADHOC_MODE		1
+#define INFRASTRUCTURE_MODE	2
+
+/* values for struct mib_local, field preamble_type */
+#define PREAMBLE_TYPE_LONG	0
+#define PREAMBLE_TYPE_SHORT	1
+#define PREAMBLE_TYPE_AUTO	2
+
+/* values for tx_rate */
+#define TX_RATE_1MBIT		0
+#define TX_RATE_2MBIT		1
+#define TX_RATE_5_5MBIT 	2
+#define TX_RATE_11MBIT		3
+#define TX_RATE_AUTO		4
+
+/* power management modes */
+#define AT76_PM_OFF		1
+#define AT76_PM_ON		2
+#define AT76_PM_SMART		3
+
+struct hwcfg_r505 {
+	u8 cr39_values[14];
+	u8 reserved1[14];
+	u8 bb_cr[14];
+	u8 pidvid[4];
+	u8 mac_addr[ETH_ALEN];
+	u8 regulatory_domain;
+	u8 reserved2[14];
+	u8 cr15_values[14];
+	u8 reserved3[3];
+} __attribute__((packed));
+
+struct hwcfg_rfmd {
+	u8 cr20_values[14];
+	u8 cr21_values[14];
+	u8 bb_cr[14];
+	u8 pidvid[4];
+	u8 mac_addr[ETH_ALEN];
+	u8 regulatory_domain;
+	u8 low_power_values[14];
+	u8 normal_power_values[14];
+	u8 reserved1[3];
+} __attribute__((packed));
+
+struct hwcfg_intersil {
+	u8 mac_addr[ETH_ALEN];
+	u8 cr31_values[14];
+	u8 cr58_values[14];
+	u8 pidvid[4];
+	u8 regulatory_domain;
+	u8 reserved[1];
+} __attribute__((packed));
+
+union at76_hwcfg {
+	struct hwcfg_intersil i;
+	struct hwcfg_rfmd r3;
+	struct hwcfg_r505 r5;
+};
+
+#define WEP_SMALL_KEY_LEN	(40 / 8)
+#define WEP_LARGE_KEY_LEN	(104 / 8)
+
+struct at76_card_config {
+	u8 exclude_unencrypted;
+	u8 promiscuous_mode;
+	u8 short_retry_limit;
+	u8 encryption_type;
+	__le16 rts_threshold;
+	__le16 fragmentation_threshold;	/* 256..2346 */
+	u8 basic_rate_set[4];
+	u8 auto_rate_fallback;	/* 0,1 */
+	u8 channel;
+	u8 privacy_invoked;
+	u8 wep_default_key_id;	/* 0..3 */
+	u8 current_ssid[32];
+	u8 wep_default_key_value[4][WEP_KEY_LEN];
+	u8 ssid_len;
+	u8 short_preamble;
+	__le16 beacon_period;
+} __attribute__((packed));
+
+struct at76_command {
+	u8 cmd;
+	u8 reserved;
+	__le16 size;
+	u8 data[0];
+} __attribute__((packed));
+
+/* Length of Atmel-specific Rx header before 802.11 frame */
+#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet)
+
+struct at76_rx_buffer {
+	__le16 wlength;
+	u8 rx_rate;
+	u8 newbss;
+	u8 fragmentation;
+	u8 rssi;
+	u8 link_quality;
+	u8 noise_level;
+	__le32 rx_time;
+	u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN];
+} __attribute__((packed));
+
+/* Length of Atmel-specific Tx header before 802.11 frame */
+#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet)
+
+struct at76_tx_buffer {
+	__le16 wlength;
+	u8 tx_rate;
+	u8 padding;
+	u8 reserved[4];
+	u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN];
+} __attribute__((packed));
+
+/* defines for scan_type below */
+#define SCAN_TYPE_ACTIVE	0
+#define SCAN_TYPE_PASSIVE	1
+
+struct at76_req_scan {
+	u8 bssid[ETH_ALEN];
+	u8 essid[32];
+	u8 scan_type;
+	u8 channel;
+	__le16 probe_delay;
+	__le16 min_channel_time;
+	__le16 max_channel_time;
+	u8 essid_size;
+	u8 international_scan;
+} __attribute__((packed));
+
+struct at76_req_ibss {
+	u8 bssid[ETH_ALEN];
+	u8 essid[32];
+	u8 bss_type;
+	u8 channel;
+	u8 essid_size;
+	u8 reserved[3];
+} __attribute__((packed));
+
+struct at76_req_join {
+	u8 bssid[ETH_ALEN];
+	u8 essid[32];
+	u8 bss_type;
+	u8 channel;
+	__le16 timeout;
+	u8 essid_size;
+	u8 reserved;
+} __attribute__((packed));
+
+struct set_mib_buffer {
+	u8 type;
+	u8 size;
+	u8 index;
+	u8 reserved;
+	union {
+		u8 byte;
+		__le16 word;
+		u8 addr[ETH_ALEN];
+	} data;
+} __attribute__((packed));
+
+struct mib_local {
+	u16 reserved0;
+	u8 beacon_enable;
+	u8 txautorate_fallback;
+	u8 reserved1;
+	u8 ssid_size;
+	u8 promiscuous_mode;
+	u16 reserved2;
+	u8 preamble_type;
+	u16 reserved3;
+} __attribute__((packed));
+
+struct mib_mac_addr {
+	u8 mac_addr[ETH_ALEN];
+	u8 res[2];		/* ??? */
+	u8 group_addr[4][ETH_ALEN];
+	u8 group_addr_status[4];
+} __attribute__((packed));
+
+struct mib_mac {
+	__le32 max_tx_msdu_lifetime;
+	__le32 max_rx_lifetime;
+	__le16 frag_threshold;
+	__le16 rts_threshold;
+	__le16 cwmin;
+	__le16 cwmax;
+	u8 short_retry_time;
+	u8 long_retry_time;
+	u8 scan_type;		/* active or passive */
+	u8 scan_channel;
+	__le16 probe_delay;	/* delay before ProbeReq in active scan, RO */
+	__le16 min_channel_time;
+	__le16 max_channel_time;
+	__le16 listen_interval;
+	u8 desired_ssid[32];
+	u8 desired_bssid[ETH_ALEN];
+	u8 desired_bsstype;	/* ad-hoc or infrastructure */
+	u8 reserved2;
+} __attribute__((packed));
+
+struct mib_mac_mgmt {
+	__le16 beacon_period;
+	__le16 CFP_max_duration;
+	__le16 medium_occupancy_limit;
+	__le16 station_id;	/* assoc id */
+	__le16 ATIM_window;
+	u8 CFP_mode;
+	u8 privacy_option_implemented;
+	u8 DTIM_period;
+	u8 CFP_period;
+	u8 current_bssid[ETH_ALEN];
+	u8 current_essid[32];
+	u8 current_bss_type;
+	u8 power_mgmt_mode;
+	/* rfmd and 505 */
+	u8 ibss_change;
+	u8 res;
+	u8 multi_domain_capability_implemented;
+	u8 multi_domain_capability_enabled;
+	u8 country_string[3];
+	u8 reserved[3];
+} __attribute__((packed));
+
+struct mib_mac_wep {
+	u8 privacy_invoked;	/* 0 disable encr., 1 enable encr */
+	u8 wep_default_key_id;
+	u8 wep_key_mapping_len;
+	u8 exclude_unencrypted;
+	__le32 wep_icv_error_count;
+	__le32 wep_excluded_count;
+	u8 wep_default_keyvalue[WEP_KEYS][WEP_KEY_LEN];
+	u8 encryption_level;	/* 1 for 40bit, 2 for 104bit encryption */
+} __attribute__((packed));
+
+struct mib_phy {
+	__le32 ed_threshold;
+
+	__le16 slot_time;
+	__le16 sifs_time;
+	__le16 preamble_length;
+	__le16 plcp_header_length;
+	__le16 mpdu_max_length;
+	__le16 cca_mode_supported;
+
+	u8 operation_rate_set[4];
+	u8 channel_id;
+	u8 current_cca_mode;
+	u8 phy_type;
+	u8 current_reg_domain;
+} __attribute__((packed));
+
+struct mib_fw_version {
+	u8 major;
+	u8 minor;
+	u8 patch;
+	u8 build;
+} __attribute__((packed));
+
+struct mib_mdomain {
+	u8 tx_powerlevel[14];
+	u8 channel_list[14];	/* 0 for invalid channels */
+} __attribute__((packed));
+
+struct at76_fw_header {
+	__le32 crc;		/* CRC32 of the whole image */
+	__le32 board_type;	/* firmware compatibility code */
+	u8 build;		/* firmware build number */
+	u8 patch;		/* firmware patch level */
+	u8 minor;		/* firmware minor version */
+	u8 major;		/* firmware major version */
+	__le32 str_offset;	/* offset of the copyright string */
+	__le32 int_fw_offset;	/* internal firmware image offset */
+	__le32 int_fw_len;	/* internal firmware image length */
+	__le32 ext_fw_offset;	/* external firmware image offset */
+	__le32 ext_fw_len;	/* external firmware image length */
+} __attribute__((packed));
+
+enum mac_state {
+	MAC_INIT,
+	MAC_SCANNING,
+	MAC_AUTH,
+	MAC_ASSOC,
+	MAC_JOINING,
+	MAC_CONNECTED,
+	MAC_OWN_IBSS
+};
+
+/* a description of a regulatory domain and the allowed channels */
+struct reg_domain {
+	u16 code;
+	char const *name;
+	u32 channel_map;	/* if bit N is set, channel (N+1) is allowed */
+};
+
+/* how long do we keep a (I)BSS in the bss_list in jiffies
+   this should be long enough for the user to retrieve the table
+   (by iwlist ?) after the device started, because all entries from
+   other channels than the one the device locks on get removed, too */
+#define BSS_LIST_TIMEOUT	(120 * HZ)
+/* struct to store BSS info found during scan */
+#define BSS_LIST_MAX_RATE_LEN	32	/* 32 rates should be enough ... */
+
+struct bss_info {
+	struct list_head list;
+
+	u8 bssid[ETH_ALEN];	/* bssid */
+	u8 ssid[IW_ESSID_MAX_SIZE];	/* essid */
+	u8 ssid_len;		/* length of ssid above */
+	u8 channel;
+	u16 capa;		/* BSS capabilities */
+	u16 beacon_interval;	/* beacon interval, Kus (1024 microseconds) */
+	u8 rates[BSS_LIST_MAX_RATE_LEN];	/* supported rates in units of
+						   500 kbps, ORed with 0x80 for
+						   basic rates */
+	u8 rates_len;
+
+	/* quality of received beacon */
+	u8 rssi;
+	u8 link_qual;
+	u8 noise_level;
+
+	unsigned long last_rx;	/* time (jiffies) of last beacon received */
+};
+
+/* a rx data buffer to collect rx fragments */
+struct rx_data_buf {
+	u8 sender[ETH_ALEN];	/* sender address */
+	u16 seqnr;		/* sequence number */
+	u16 fragnr;		/* last fragment received */
+	unsigned long last_rx;	/* jiffies of last rx */
+	struct sk_buff *skb;	/* == NULL if entry is free */
+};
+
+#define NR_RX_DATA_BUF		8
+
+/* Data for one loaded firmware file */
+struct fwentry {
+	const char *const fwname;
+	const struct firmware *fw;
+	int extfw_size;
+	int intfw_size;
+	/* pointer to loaded firmware, no need to free */
+	u8 *extfw;		/* external firmware, extfw_size bytes long */
+	u8 *intfw;		/* internal firmware, intfw_size bytes long */
+	enum board_type board_type;	/* board type */
+	struct mib_fw_version fw_version;
+	int loaded;		/* Loaded and parsed successfully */
+};
+
+struct at76_priv {
+	struct usb_device *udev;	/* USB device pointer */
+	struct net_device *netdev;	/* net device pointer */
+	struct net_device_stats stats;	/* net device stats */
+	struct iw_statistics wstats;	/* wireless stats */
+
+	struct sk_buff *rx_skb;	/* skbuff for receiving data */
+	void *bulk_out_buffer;	/* buffer for sending data */
+
+	struct urb *tx_urb;	/* URB for sending data */
+	struct urb *rx_urb;	/* URB for receiving data */
+
+	unsigned int tx_pipe;	/* bulk out pipe */
+	unsigned int rx_pipe;	/* bulk in pipe */
+
+	struct mutex mtx;	/* locks this structure */
+
+	/* work queues */
+	struct work_struct work_assoc_done;
+	struct work_struct work_join;
+	struct work_struct work_new_bss;
+	struct work_struct work_start_scan;
+	struct work_struct work_set_promisc;
+	struct work_struct work_submit_rx;
+	struct delayed_work dwork_restart;
+	struct delayed_work dwork_get_scan;
+	struct delayed_work dwork_beacon;
+	struct delayed_work dwork_auth;
+	struct delayed_work dwork_assoc;
+
+	struct tasklet_struct rx_tasklet;
+
+	/* the WEP stuff */
+	int wep_enabled;	/* 1 if WEP is enabled */
+	int wep_key_id;		/* key id to be used */
+	u8 wep_keys[WEP_KEYS][WEP_KEY_LEN];	/* the four WEP keys,
+						   5 or 13 bytes are used */
+	u8 wep_keys_len[WEP_KEYS];	/* the length of the above keys */
+
+	int channel;
+	int iw_mode;
+	u8 bssid[ETH_ALEN];
+	u8 essid[IW_ESSID_MAX_SIZE];
+	int essid_size;
+	int radio_on;
+	int promisc;
+
+	int preamble_type;	/* 0 - long, 1 - short, 2 - auto */
+	int auth_mode;		/* authentication type: 0 open, 1 shared key */
+	int txrate;		/* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */
+	int frag_threshold;	/* threshold for fragmentation of tx packets */
+	int rts_threshold;	/* threshold for RTS mechanism */
+	int short_retry_limit;
+
+	int scan_min_time;	/* scan min channel time */
+	int scan_max_time;	/* scan max channel time */
+	int scan_mode;		/* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */
+	int scan_need_any;	/* if set, need to scan for any ESSID */
+
+	/* the list we got from scanning */
+	spinlock_t bss_list_spinlock;	/* protects bss_list operations */
+	struct list_head bss_list;	/* list of BSS we got beacons from */
+	struct timer_list bss_list_timer;	/* timer to purge old entries
+						   from bss_list */
+	struct bss_info *curr_bss;	/* current BSS */
+	u16 assoc_id;		/* current association ID, if associated */
+
+	u8 wanted_bssid[ETH_ALEN];
+	int wanted_bssid_valid;	/* != 0 if wanted_bssid is to be used */
+
+	/* some data for infrastructure mode only */
+	spinlock_t mgmt_spinlock;	/* this spinlock protects access to
+					   next_mgmt_bulk */
+
+	struct at76_tx_buffer *next_mgmt_bulk;	/* pending management msg to
+						   send via bulk out */
+	enum mac_state mac_state;
+	enum {
+		SCAN_IDLE,
+		SCAN_IN_PROGRESS,
+		SCAN_COMPLETED
+	} scan_state;
+	time_t last_scan;
+
+	int retries;		/* remaining retries in case of timeout when
+				 * sending AuthReq or AssocReq */
+	u8 pm_mode;		/* power management mode */
+	u32 pm_period;		/* power management period in microseconds */
+
+	struct reg_domain const *domain;	/* reg domain description */
+
+	/* iwspy support */
+	spinlock_t spy_spinlock;
+	struct iw_spy_data spy_data;
+
+	struct iw_public_data wireless_data;
+
+	/* These fields contain HW config provided by the device (not all of
+	 * these fields are used by all board types) */
+	u8 mac_addr[ETH_ALEN];
+	u8 regulatory_domain;
+
+	struct at76_card_config card_config;
+
+	/* store rx fragments until complete */
+	struct rx_data_buf rx_data[NR_RX_DATA_BUF];
+
+	enum board_type board_type;
+	struct mib_fw_version fw_version;
+
+	unsigned int device_unplugged:1;
+	unsigned int netdev_registered:1;
+	struct set_mib_buffer mib_buf;	/* global buffer for set_mib calls */
+
+	/* beacon counting */
+	int beacon_period;	/* period of mgmt beacons, Kus */
+	int beacons_received;
+	unsigned long beacons_last_qual;	/* time we restarted counting
+						   beacons */
+};
+
+struct at76_rx_radiotap {
+	struct ieee80211_radiotap_header rt_hdr;
+	__le64 rt_tsft;
+	u8 rt_flags;
+	u8 rt_rate;
+	s8 rt_signal;
+	s8 rt_noise;
+};
+
+#define AT76_RX_RADIOTAP_PRESENT		  \
+	((1 << IEEE80211_RADIOTAP_TSFT)		| \
+	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
+	(1 << IEEE80211_RADIOTAP_RATE)		| \
+	(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)	| \
+	(1 << IEEE80211_RADIOTAP_DB_ANTNOISE))
+
+#define BEACON_MAX_DATA_LENGTH	1500
+
+/* the maximum size of an AssocReq packet */
+#define ASSOCREQ_MAX_SIZE \
+  (AT76_TX_HDRLEN + sizeof(struct ieee80211_assoc_request) + \
+   1 + 1 + IW_ESSID_MAX_SIZE + 1 + 1 + 4)
+
+/* for shared secret auth, add the challenge text size */
+#define AUTH_FRAME_SIZE (AT76_TX_HDRLEN + sizeof(struct ieee80211_auth))
+
+/* Maximal number of AuthReq retries */
+#define AUTH_RETRIES		3
+
+/* Maximal number of AssocReq retries */
+#define ASSOC_RETRIES		3
+
+/* Beacon timeout in managed mode when we are connected */
+#define BEACON_TIMEOUT		(10 * HZ)
+
+/* Timeout for authentication response */
+#define AUTH_TIMEOUT		(1 * HZ)
+
+/* Timeout for association response */
+#define ASSOC_TIMEOUT		(1 * HZ)
+
+/* Polling interval when scan is running */
+#define SCAN_POLL_INTERVAL	(HZ / 4)
+
+/* Command completion timeout */
+#define CMD_COMPLETION_TIMEOUT	(5 * HZ)
+
+#define DEF_RTS_THRESHOLD	1536
+#define DEF_FRAG_THRESHOLD	1536
+#define DEF_SHORT_RETRY_LIMIT	8
+#define DEF_CHANNEL		10
+#define DEF_SCAN_MIN_TIME	10
+#define DEF_SCAN_MAX_TIME	120
+
+#define MAX_RTS_THRESHOLD	(MAX_FRAG_THRESHOLD + 1)
+
+/* the max padding size for tx in bytes (see calc_padding) */
+#define MAX_PADDING_SIZE	53
+
+#endif				/* _AT76_USB_H */
diff -puN /dev/null drivers/net/wireless/ath5k/Makefile
--- /dev/null
+++ a/drivers/net/wireless/ath5k/Makefile
@@ -0,0 +1,2 @@
+ath5k-objs		= base.o hw.o regdom.o initvals.o phy.o
+obj-$(CONFIG_ATH5K)	+= ath5k.o
diff -puN /dev/null drivers/net/wireless/ath5k/ath5k.h
--- /dev/null
+++ a/drivers/net/wireless/ath5k/ath5k.h
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ATH5K_H
+#define _ATH5K_H
+
+/* Set this to 1 to disable regulatory domain restrictions for channel tests.
+ * WARNING: This is for debuging only and has side effects (eg. scan takes too
+ * long and results timeouts). It's also illegal to tune to some of the
+ * supported frequencies in some countries, so use this at your own risk,
+ * you've been warned. */
+#define CHAN_DEBUG	0
+
+/* Uncomment this for debuging (warning that it results in TOO much output) */
+/*#define AR5K_DEBUG	1 */
+
+#include <linux/io.h>
+#include <linux/types.h>
+#include <net/mac80211.h>
+
+#include "hw.h"
+#include "regdom.h"
+
+/* PCI IDs */
+#define PCI_DEVICE_ID_ATHEROS_AR5210 		0x0007 /* AR5210 */
+#define PCI_DEVICE_ID_ATHEROS_AR5311 		0x0011 /* AR5311 */
+#define PCI_DEVICE_ID_ATHEROS_AR5211 		0x0012 /* AR5211 */
+#define PCI_DEVICE_ID_ATHEROS_AR5212 		0x0013 /* AR5212 */
+#define PCI_DEVICE_ID_3COM_3CRDAG675 		0x0013 /* 3CRDAG675 (Atheros AR5212) */
+#define PCI_DEVICE_ID_3COM_2_3CRPAG175 		0x0013 /* 3CRPAG175 (Atheros AR5212) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 	0x0207 /* AR5210 (Early) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_IBM	0x1014 /* AR5212 (IBM MiniPCI) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 	0x1107 /* AR5210 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 	0x1113 /* AR5212 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 	0x1112 /* AR5211 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 	0xf013 /* AR5212 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 	0xff12 /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 	0xf11b /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_REV2 	0x0052 /* AR5312 WMAC (AP31) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_REV7 	0x0057 /* AR5312 WMAC (AP30-040) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_REV8 	0x0058 /* AR5312 WMAC (AP43-030) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 	0x0014 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 	0x0015 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 	0x0016 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 	0x0017 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 	0x0018 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 	0x0019 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR2413 		0x001a /* AR2413 (Griffin-lite) */
+#define PCI_DEVICE_ID_ATHEROS_AR5413 		0x001b /* AR5413 (Eagle) */
+#define PCI_DEVICE_ID_ATHEROS_AR5424 		0x001c /* AR5424 (Condor PCI-E) */
+
+/****************************\
+  GENERIC DRIVER DEFINITIONS
+\****************************/
+
+#define AR5K_PRINTF(fmt, ...)   printk("%s: " fmt, __func__, ##__VA_ARGS__)
+#define AR5K_PRINT(fmt)         printk("%s: " fmt, __func__)
+#ifdef AR5K_DEBUG
+#define AR5K_TRACE              printk(KERN_DEBUG "%s:%d\n", __func__, __LINE__)
+#else
+#define AR5K_TRACE
+#endif
+
+/*
+ * Some tuneable values (these should be changeable by the user)
+ */
+#define AR5K_TUNE_DMA_BEACON_RESP		2
+#define AR5K_TUNE_SW_BEACON_RESP		10
+#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF	0
+#define AR5K_TUNE_RADAR_ALERT			false
+#define AR5K_TUNE_MIN_TX_FIFO_THRES		1
+#define AR5K_TUNE_MAX_TX_FIFO_THRES		((IEEE80211_MAX_LEN / 64) + 1)
+#define AR5K_TUNE_RSSI_THRES			1792
+#define AR5K_TUNE_REGISTER_TIMEOUT		20000
+#define AR5K_TUNE_REGISTER_DWELL_TIME		20000
+#define AR5K_TUNE_BEACON_INTERVAL		100
+#define AR5K_TUNE_AIFS				2
+#define AR5K_TUNE_AIFS_11B			2
+#define AR5K_TUNE_AIFS_XR			0
+#define AR5K_TUNE_CWMIN				15
+#define AR5K_TUNE_CWMIN_11B			31
+#define AR5K_TUNE_CWMIN_XR			3
+#define AR5K_TUNE_CWMAX				1023
+#define AR5K_TUNE_CWMAX_11B			1023
+#define AR5K_TUNE_CWMAX_XR			7
+#define AR5K_TUNE_NOISE_FLOOR			-72
+#define AR5K_TUNE_MAX_TXPOWER			60
+#define AR5K_TUNE_DEFAULT_TXPOWER		30
+#define AR5K_TUNE_TPC_TXPOWER			true
+#define AR5K_TUNE_ANT_DIVERSITY			true
+#define AR5K_TUNE_HWTXTRIES			4
+
+/* token to use for aifs, cwmin, cwmax in MadWiFi */
+#define	AR5K_TXQ_USEDEFAULT	((u32) -1)
+
+/* GENERIC CHIPSET DEFINITIONS */
+
+/* MAC Chips */
+enum ath5k_version {
+	AR5K_AR5210	= 0,
+	AR5K_AR5211	= 1,
+	AR5K_AR5212	= 2,
+};
+
+/* PHY Chips */
+enum ath5k_radio {
+	AR5K_RF5110	= 0,
+	AR5K_RF5111	= 1,
+	AR5K_RF5112	= 2,
+};
+
+/*
+ * Common silicon revision/version values
+ */
+#define AR5K_SREV_UNKNOWN	0xffff
+
+#define AR5K_SREV_VER_AR5210	0x00
+#define AR5K_SREV_VER_AR5311	0x10
+#define AR5K_SREV_VER_AR5311A	0x20
+#define AR5K_SREV_VER_AR5311B	0x30
+#define AR5K_SREV_VER_AR5211	0x40
+#define AR5K_SREV_VER_AR5212	0x50
+#define AR5K_SREV_VER_AR5213	0x55
+#define AR5K_SREV_VER_UNSUPP	0x60
+
+#define AR5K_SREV_RAD_5110	0x00
+#define AR5K_SREV_RAD_5111	0x10
+#define AR5K_SREV_RAD_5111A	0x15
+#define AR5K_SREV_RAD_2111	0x20
+#define AR5K_SREV_RAD_5112	0x30
+#define AR5K_SREV_RAD_5112A	0x35
+#define AR5K_SREV_RAD_2112	0x40
+#define AR5K_SREV_RAD_2112A	0x45
+#define AR5K_SREV_RAD_UNSUPP	0x50
+
+/* IEEE defs */
+
+#define IEEE80211_MAX_LEN       2500
+
+/* TODO add support to mac80211 for vendor-specific rates and modes */
+
+/*
+ * Some of this information is based on Documentation from:
+ *
+ * http://madwifi.org/wiki/ChipsetFeatures/SuperAG
+ *
+ * Modulation for Atheros' eXtended Range - range enhancing extension that is
+ * supposed to double the distance an Atheros client device can keep a
+ * connection with an Atheros access point. This is achieved by increasing
+ * the receiver sensitivity up to, -105dBm, which is about 20dB above what
+ * the 802.11 specifications demand. In addition, new (proprietary) data rates
+ * are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s.
+ *
+ * Please note that can you either use XR or TURBO but you cannot use both,
+ * they are exclusive.
+ *
+ */
+#define MODULATION_XR 		0x00000200
+/*
+ * Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a
+ * throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s
+ * signaling rate achieved through the bonding of two 54Mbit/s 802.11g
+ * channels. To use this feature your Access Point must also suport it.
+ * There is also a distinction between "static" and "dynamic" turbo modes:
+ *
+ * - Static: is the dumb version: devices set to this mode stick to it until
+ *     the mode is turned off.
+ * - Dynamic: is the intelligent version, the network decides itself if it
+ *     is ok to use turbo. As soon as traffic is detected on adjacent channels
+ *     (which would get used in turbo mode), or when a non-turbo station joins
+ *     the network, turbo mode won't be used until the situation changes again.
+ *     Dynamic mode is achieved by Atheros' Adaptive Radio (AR) feature which
+ *     monitors the used radio band in order to decide whether turbo mode may
+ *     be used or not.
+ *
+ * This article claims Super G sticks to bonding of channels 5 and 6 for
+ * USA:
+ *
+ * http://www.pcworld.com/article/id,113428-page,1/article.html
+ *
+ * The channel bonding seems to be driver specific though. In addition to
+ * deciding what channels will be used, these "Turbo" modes are accomplished
+ * by also enabling the following features:
+ *
+ * - Bursting: allows multiple frames to be sent at once, rather than pausing
+ *     after each frame. Bursting is a standards-compliant feature that can be
+ *     used with any Access Point.
+ * - Fast frames: increases the amount of information that can be sent per
+ *     frame, also resulting in a reduction of transmission overhead. It is a
+ *     proprietary feature that needs to be supported by the Access Point.
+ * - Compression: data frames are compressed in real time using a Lempel Ziv
+ *     algorithm. This is done transparently. Once this feature is enabled,
+ *     compression and decompression takes place inside the chipset, without
+ *     putting additional load on the host CPU.
+ */
+#define MODULATION_TURBO	0x00000080
+
+enum ath5k_vendor_mode {
+	MODE_ATHEROS_TURBO = NUM_IEEE80211_MODES+1,
+	MODE_ATHEROS_TURBOG
+};
+
+/* adding this flag to rate_code enables short preamble, see ar5212_reg.h */
+#define AR5K_SET_SHORT_PREAMBLE 0x04
+
+#define HAS_SHPREAMBLE(_ix) (rt->rates[_ix].modulation == IEEE80211_RATE_CCK_2)
+#define SHPREAMBLE_FLAG(_ix) (HAS_SHPREAMBLE(_ix) ? AR5K_SET_SHORT_PREAMBLE : 0)
+
+/****************\
+  TX DEFINITIONS
+\****************/
+
+/*
+ * Tx Descriptor
+ */
+struct ath_tx_status {
+	u16	ts_seqnum;
+	u16	ts_tstamp;
+	u8	ts_status;
+	u8	ts_rate;
+	s8	ts_rssi;
+	u8	ts_shortretry;
+	u8	ts_longretry;
+	u8	ts_virtcol;
+	u8	ts_antenna;
+};
+
+#define AR5K_TXSTAT_ALTRATE	0x80
+#define AR5K_TXERR_XRETRY	0x01
+#define AR5K_TXERR_FILT		0x02
+#define AR5K_TXERR_FIFO		0x04
+
+/*
+ * Queue types used to classify tx queues.
+ */
+enum ath5k_tx_queue {
+	AR5K_TX_QUEUE_INACTIVE = 0, /* q is unused -- see ath5k_hw_release_tx_queue */
+	AR5K_TX_QUEUE_DATA,	  /*A normal data queue*/
+	AR5K_TX_QUEUE_XR_DATA,	  /*An XR-data queue*/
+	AR5K_TX_QUEUE_BEACON,	  /*The beacon queue*/
+	AR5K_TX_QUEUE_CAB,	  /*The ater-beacon queue*/
+	AR5K_TX_QUEUE_UAPSD,	  /*Unscheduled Automatic Power Save Delivery queue*/
+};
+
+#define	AR5K_NUM_TX_QUEUES		10
+#define	AR5K_NUM_TX_QUEUES_NOQCU	2
+
+/*
+ * Queue syb-types to classify normal data queues.
+ * These are the 4 Access Categories as defined in
+ * WME spec. 0 is the lowest priority and 4 is the
+ * highest. Normal data that hasn't been classified
+ * goes to the Best Effort AC.
+ */
+enum ath5k_tx_queue_subtype {
+	AR5K_WME_AC_BK = 0,	/*Background traffic*/
+	AR5K_WME_AC_BE, 	/*Best-effort (normal) traffic)*/
+	AR5K_WME_AC_VI, 	/*Video traffic*/
+	AR5K_WME_AC_VO, 	/*Voice traffic*/
+};
+
+/*
+ * Queue ID numbers as returned by the HAL, each number
+ * represents a hw queue. If hw does not support hw queues
+ * (eg 5210) all data goes in one queue. These match
+ * d80211 definitions (net80211/MadWiFi don't use them).
+ */
+enum ath5k_tx_queue_id {
+	AR5K_TX_QUEUE_ID_NOQCU_DATA	= 0,
+	AR5K_TX_QUEUE_ID_NOQCU_BEACON	= 1,
+	AR5K_TX_QUEUE_ID_DATA_MIN	= 0, /*IEEE80211_TX_QUEUE_DATA0*/
+	AR5K_TX_QUEUE_ID_DATA_MAX	= 4, /*IEEE80211_TX_QUEUE_DATA4*/
+	AR5K_TX_QUEUE_ID_DATA_SVP	= 5, /*IEEE80211_TX_QUEUE_SVP - Spectralink Voice Protocol*/
+	AR5K_TX_QUEUE_ID_CAB		= 6, /*IEEE80211_TX_QUEUE_AFTER_BEACON*/
+	AR5K_TX_QUEUE_ID_BEACON		= 7, /*IEEE80211_TX_QUEUE_BEACON*/
+	AR5K_TX_QUEUE_ID_UAPSD		= 8,
+	AR5K_TX_QUEUE_ID_XR_DATA	= 9,
+};
+
+
+/*
+ * Flags to set hw queue's parameters...
+ */
+#define AR5K_TXQ_FLAG_TXINT_ENABLE		0x0001	/* Enable TXOK and TXERR interrupts -not used- */
+#define AR5K_TXQ_FLAG_TXDESCINT_ENABLE		0x0002	/* Enable TXDESC interrupt -not implemented- */
+#define AR5K_TXQ_FLAG_BACKOFF_DISABLE		0x0004	/* Disable random post-backoff */
+#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE	0x0008	/* Enable hw compression -not implemented-*/
+#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE	0x0010	/* Enable ready time expiry policy (?)*/
+#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE	0x0020	/* Enable backoff while bursting */
+#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS		0x0040	/* Disable backoff while bursting */
+#define AR5K_TXQ_FLAG_TXEOLINT_ENABLE		0x0080	/* Enable TXEOL interrupt -not implemented-*/
+
+/*
+ * A struct to hold tx queue's parameters
+ */
+struct ath5k_txq_info {
+	enum ath5k_tx_queue tqi_type;
+	enum ath5k_tx_queue_subtype tqi_subtype;
+	u16	tqi_flags;	/* Tx queue flags (see above) */
+	u32	tqi_aifs;	/* Arbitrated Interframe Space */
+	s32	tqi_cw_min;	/* Minimum Contention Window */
+	s32	tqi_cw_max;	/* Maximum Contention Window */
+	u32	tqi_cbr_period; /* Constant bit rate period */
+	u32	tqi_cbr_overflow_limit;
+	u32	tqi_burst_time;
+	u32	tqi_ready_time; /* Not used */
+	u32	tqi_comp_buffer;/* Compression Buffer's phys addr */
+};
+
+/*
+ * Transmit packet types.
+ * These are not fully used inside OpenHAL yet
+ */
+enum ath5k_pkt_type {
+	AR5K_PKT_TYPE_NORMAL		= 0,
+	AR5K_PKT_TYPE_ATIM		= 1,
+	AR5K_PKT_TYPE_PSPOLL		= 2,
+	AR5K_PKT_TYPE_BEACON		= 3,
+	AR5K_PKT_TYPE_PROBE_RESP	= 4,
+	AR5K_PKT_TYPE_PIFS		= 5,
+};
+
+/*
+ * TX power and TPC settings
+ */
+#define AR5K_TXPOWER_OFDM(_r, _v)	(			\
+	((0 & 1) << ((_v) + 6)) |				\
+	(((hal->ah_txpower.txp_rates[(_r)]) & 0x3f) << (_v))	\
+)
+
+#define AR5K_TXPOWER_CCK(_r, _v)	(			\
+	(hal->ah_txpower.txp_rates[(_r)] & 0x3f) << (_v)	\
+)
+
+/*
+ * Used to compute TX times
+ */
+#define AR5K_CCK_SIFS_TIME		10
+#define AR5K_CCK_PREAMBLE_BITS		144
+#define AR5K_CCK_PLCP_BITS		48
+
+#define AR5K_OFDM_SIFS_TIME		16
+#define AR5K_OFDM_PREAMBLE_TIME		20
+#define AR5K_OFDM_PLCP_BITS		22
+#define AR5K_OFDM_SYMBOL_TIME		4
+
+#define AR5K_TURBO_SIFS_TIME		8
+#define AR5K_TURBO_PREAMBLE_TIME	14
+#define AR5K_TURBO_PLCP_BITS		22
+#define AR5K_TURBO_SYMBOL_TIME		4
+
+#define AR5K_XR_SIFS_TIME		16
+#define AR5K_XR_PLCP_BITS		22
+#define AR5K_XR_SYMBOL_TIME		4
+
+/* CCK */
+#define AR5K_CCK_NUM_BITS(_frmlen) (_frmlen << 3)
+
+#define AR5K_CCK_PHY_TIME(_sp) (_sp ?					\
+	((AR5K_CCK_PREAMBLE_BITS + AR5K_CCK_PLCP_BITS) >> 1) :		\
+	(AR5K_CCK_PREAMBLE_BITS + AR5K_CCK_PLCP_BITS))
+
+#define AR5K_CCK_TX_TIME(_kbps, _frmlen, _sp)				\
+	(AR5K_CCK_PHY_TIME(_sp) +					\
+	((AR5K_CCK_NUM_BITS(_frmlen) * 1000) / _kbps) +			\
+	AR5K_CCK_SIFS_TIME)
+
+/* OFDM */
+#define AR5K_OFDM_NUM_BITS(_frmlen) (AR5K_OFDM_PLCP_BITS + (_frmlen << 3))
+
+#define AR5K_OFDM_NUM_BITS_PER_SYM(_kbps) ((_kbps *			\
+	AR5K_OFDM_SYMBOL_TIME) / 1000)
+
+#define AR5K_OFDM_NUM_BITS(_frmlen) (AR5K_OFDM_PLCP_BITS + (_frmlen << 3))
+
+#define AR5K_OFDM_NUM_SYMBOLS(_kbps, _frmlen)				\
+	DIV_ROUND_UP(AR5K_OFDM_NUM_BITS(_frmlen),			\
+			AR5K_OFDM_NUM_BITS_PER_SYM(_kbps))
+
+#define AR5K_OFDM_TX_TIME(_kbps, _frmlen)				\
+	(AR5K_OFDM_PREAMBLE_TIME + AR5K_OFDM_SIFS_TIME +		\
+	(AR5K_OFDM_NUM_SYMBOLS(_kbps, _frmlen) * AR5K_OFDM_SYMBOL_TIME))
+
+/* TURBO */
+#define AR5K_TURBO_NUM_BITS(_frmlen) (AR5K_TURBO_PLCP_BITS + (_frmlen << 3))
+
+#define AR5K_TURBO_NUM_BITS_PER_SYM(_kbps) (((_kbps << 1) *		\
+	AR5K_TURBO_SYMBOL_TIME) / 1000)
+
+#define AR5K_TURBO_NUM_BITS(_frmlen) (AR5K_TURBO_PLCP_BITS + (_frmlen << 3))
+
+#define AR5K_TURBO_NUM_SYMBOLS(_kbps, _frmlen)				\
+	DIV_ROUND_UP(AR5K_TURBO_NUM_BITS(_frmlen),			\
+			AR5K_TURBO_NUM_BITS_PER_SYM(_kbps))
+
+#define AR5K_TURBO_TX_TIME(_kbps, _frmlen)				\
+	(AR5K_TURBO_PREAMBLE_TIME + AR5K_TURBO_SIFS_TIME +		\
+	(AR5K_TURBO_NUM_SYMBOLS(_kbps, _frmlen) * AR5K_TURBO_SYMBOL_TIME))
+
+/* eXtendent Range (?)*/
+#define AR5K_XR_PREAMBLE_TIME(_kbps) (((_kbps) < 1000) ? 173 : 76)
+
+#define AR5K_XR_NUM_BITS_PER_SYM(_kbps) ((_kbps *			\
+	AR5K_XR_SYMBOL_TIME) / 1000)
+
+#define AR5K_XR_NUM_BITS(_frmlen) (AR5K_XR_PLCP_BITS + (_frmlen << 3))
+
+#define AR5K_XR_NUM_SYMBOLS(_kbps, _frmlen)				\
+	DIV_ROUND_UP(AR5K_XR_NUM_BITS(_frmlen), AR5K_XR_NUM_BITS_PER_SYM(_kbps))
+
+#define AR5K_XR_TX_TIME(_kbps, _frmlen)					\
+	(AR5K_XR_PREAMBLE_TIME(_kbps) + AR5K_XR_SIFS_TIME +		\
+	(AR5K_XR_NUM_SYMBOLS(_kbps, _frmlen) * AR5K_XR_SYMBOL_TIME))
+
+/*
+ * DMA size definitions (2^n+2)
+ */
+enum ath5k_dmasize {
+	AR5K_DMASIZE_4B	= 0,
+	AR5K_DMASIZE_8B,
+	AR5K_DMASIZE_16B,
+	AR5K_DMASIZE_32B,
+	AR5K_DMASIZE_64B,
+	AR5K_DMASIZE_128B,
+	AR5K_DMASIZE_256B,
+	AR5K_DMASIZE_512B
+};
+
+
+/****************\
+  RX DEFINITIONS
+\****************/
+
+/*
+ * Rx Descriptor
+ */
+struct ath_rx_status {
+	u16	rs_datalen;
+	u16	rs_tstamp;
+	u8	rs_status;
+	u8	rs_phyerr;
+	s8	rs_rssi;
+	u8	rs_keyix;
+	u8	rs_rate;
+	u8	rs_antenna;
+	u8	rs_more;
+};
+
+#define AR5K_RXERR_CRC		0x01
+#define AR5K_RXERR_PHY		0x02
+#define AR5K_RXERR_FIFO		0x04
+#define AR5K_RXERR_DECRYPT	0x08
+#define AR5K_RXERR_MIC		0x10
+#define AR5K_RXKEYIX_INVALID	((u8) - 1)
+#define AR5K_TXKEYIX_INVALID	((u32) - 1)
+
+struct ath5k_mib_stats {
+	u32	ackrcv_bad;
+	u32	rts_bad;
+	u32	rts_good;
+	u32	fcs_bad;
+	u32	beacons;
+};
+
+
+
+
+/**************************\
+ BEACON TIMERS DEFINITIONS
+\**************************/
+
+#define AR5K_BEACON_PERIOD	0x0000ffff
+#define AR5K_BEACON_ENA		0x00800000 /*enable beacon xmit*/
+#define AR5K_BEACON_RESET_TSF	0x01000000 /*force a TSF reset*/
+
+/*
+ * Per-station beacon timer state.
+ */
+struct ath5k_beacon_state {
+	u32	bs_next_beacon;
+	u32	bs_next_dtim;
+	u32	bs_interval;		/*in TU's -see net80211/ieee80211_var.h-
+						can also include the above flags*/
+	u8	bs_dtim_period;
+	u8	bs_cfp_period;
+	u16	bs_cfp_max_duration;	/*if non-zero hw is setup to coexist with
+						a Point Coordination Function capable AP*/
+	u16	bs_cfp_du_remain;
+	u16	bs_tim_offset;
+	u16	bs_sleep_duration;
+	u16	bs_bmiss_threshold;
+	u32  	bs_cfp_next;
+};
+
+
+
+
+/********************\
+  COMMON DEFINITIONS
+\********************/
+
+/*
+ * Atheros descriptor
+ */
+struct ath_desc {
+	u32	ds_link;
+	u32	ds_data;
+	u32	ds_ctl0;
+	u32	ds_ctl1;
+	u32	ds_hw[4];
+
+	union {
+		struct ath_rx_status rx;
+		struct ath_tx_status tx;
+	} ds_us;
+
+#define ds_rxstat ds_us.rx
+#define ds_txstat ds_us.tx
+
+} __packed;
+
+#define AR5K_RXDESC_INTREQ	0x0020
+
+#define AR5K_TXDESC_CLRDMASK	0x0001
+#define AR5K_TXDESC_NOACK	0x0002	/*[5211+]*/
+#define AR5K_TXDESC_RTSENA	0x0004
+#define AR5K_TXDESC_CTSENA	0x0008
+#define AR5K_TXDESC_INTREQ	0x0010
+#define AR5K_TXDESC_VEOL	0x0020	/*[5211+]*/
+
+#define AR5K_SLOT_TIME_9	396
+#define AR5K_SLOT_TIME_20	880
+#define AR5K_SLOT_TIME_MAX	0xffff
+
+/* channel_flags */
+#define	CHANNEL_CW_INT	0x0008	/* Contention Window interference detected */
+#define	CHANNEL_TURBO	0x0010	/* Turbo Channel */
+#define	CHANNEL_CCK	0x0020	/* CCK channel */
+#define	CHANNEL_OFDM	0x0040	/* OFDM channel */
+#define	CHANNEL_2GHZ	0x0080	/* 2GHz channel. */
+#define	CHANNEL_5GHZ	0x0100	/* 5GHz channel */
+#define	CHANNEL_PASSIVE	0x0200	/* Only passive scan allowed */
+#define	CHANNEL_DYN	0x0400	/* Dynamic CCK-OFDM channel (for g operation) */
+#define	CHANNEL_XR	0x0800	/* XR channel */
+
+#define	CHANNEL_A	(CHANNEL_5GHZ|CHANNEL_OFDM)
+#define	CHANNEL_B	(CHANNEL_2GHZ|CHANNEL_CCK)
+#define	CHANNEL_G	(CHANNEL_2GHZ|CHANNEL_OFDM)
+#define	CHANNEL_T	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_TG	(CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_108A	CHANNEL_T
+#define	CHANNEL_108G	CHANNEL_TG
+#define	CHANNEL_X	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR)
+
+#define	CHANNEL_ALL 	(CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_2GHZ|CHANNEL_5GHZ| \
+		CHANNEL_TURBO)
+
+#define	CHANNEL_ALL_NOTURBO 	(CHANNEL_ALL & ~CHANNEL_TURBO)
+#define CHANNEL_MODES		CHANNEL_ALL
+
+/*
+ * Used internaly in OpenHAL (ar5211.c/ar5212.c
+ * for reset_tx_queue). Also see struct struct ieee80211_channel.
+ */
+#define IS_CHAN_XR(_c)	((_c.val & CHANNEL_XR) != 0)
+#define IS_CHAN_B(_c)	((_c.val & CHANNEL_B) != 0)
+
+/*
+ * The following structure will be used to map 2GHz channels to
+ * 5GHz Atheros channels.
+ */
+struct ath5k_athchan_2ghz {
+	u32	a2_flags;
+	u16	a2_athchan;
+};
+
+/*
+ * Rate definitions
+ */
+
+#define AR5K_MAX_RATES	32 /*max number of rates on the rate table*/
+
+struct ath5k_rate {
+	u8	valid;		/* Valid for rate control */
+	u32	modulation;
+	u16	rate_kbps;
+	u8	rate_code;	/* Rate mapping for h/w descriptors */
+	u8	dot11_rate;
+	u8	control_rate;
+	u16	lp_ack_duration;/* long preamble ACK duration */
+	u16	sp_ack_duration;/* short preamble ACK duration*/
+};
+
+struct ath5k_rate_table {
+	u16	rate_count;
+	u8	rate_code_to_index[AR5K_MAX_RATES];	/* Back-mapping */
+	struct ath5k_rate rates[AR5K_MAX_RATES];
+};
+
+/*
+ * Rate tables...
+ */
+#define AR5K_RATES_11A { 8, {					\
+	255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0,	\
+	7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255,	\
+	255, 255, 255, 255, 255, 255, 255, 255 }, {		\
+	{ 1, IEEE80211_RATE_OFDM, 6000, 11, 140, 0 },		\
+	{ 1, IEEE80211_RATE_OFDM, 9000, 15, 18, 0 },		\
+	{ 1, IEEE80211_RATE_OFDM, 12000, 10, 152, 2 },		\
+	{ 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 2 },		\
+	{ 1, IEEE80211_RATE_OFDM, 24000, 9, 176, 4 },		\
+	{ 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 4 },		\
+	{ 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 4 },		\
+	{ 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 4 } }		\
+}
+
+#define AR5K_RATES_11B { 4, {						\
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,	\
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,	\
+	3, 2, 1, 0, 255, 255, 255, 255 }, {				\
+	{ 1, IEEE80211_RATE_CCK, 1000, 27, 130, 0 },	\
+	{ 1, IEEE80211_RATE_CCK_2, 2000, 26, 132, 1 },	\
+	{ 1, IEEE80211_RATE_CCK_2, 5500, 25, 139, 1 },	\
+	{ 1, IEEE80211_RATE_CCK_2, 11000, 24, 150, 1 } }	\
+}
+
+#define AR5K_RATES_11G { 12, {					\
+	255, 255, 255, 255, 255, 255, 255, 255, 10, 8, 6, 4,	\
+	11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255,	\
+	3, 2, 1, 0, 255, 255, 255, 255 }, {			\
+	{ 1, IEEE80211_RATE_CCK, 1000, 27, 2, 0 },		\
+	{ 1, IEEE80211_RATE_CCK_2, 2000, 26, 4, 1 },		\
+	{ 1, IEEE80211_RATE_CCK_2, 5500, 25, 11, 1 },		\
+	{ 1, IEEE80211_RATE_CCK_2, 11000, 24, 22, 1 },	\
+	{ 0, IEEE80211_RATE_OFDM, 6000, 11, 12, 4 },	\
+	{ 0, IEEE80211_RATE_OFDM, 9000, 15, 18, 4 },	\
+	{ 1, IEEE80211_RATE_OFDM, 12000, 10, 24, 6 },	\
+	{ 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 6 },	\
+	{ 1, IEEE80211_RATE_OFDM, 24000, 9, 48, 8 },	\
+	{ 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 8 },	\
+	{ 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 8 },	\
+	{ 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 8 } }	\
+}
+
+#define AR5K_RATES_TURBO { 8, {					\
+	255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0,	\
+	7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255,	\
+	255, 255, 255, 255, 255, 255, 255, 255 }, {		\
+	{ 1, MODULATION_TURBO, 6000, 11, 140, 0 },	\
+	{ 1, MODULATION_TURBO, 9000, 15, 18, 0 },	\
+	{ 1, MODULATION_TURBO, 12000, 10, 152, 2 },	\
+	{ 1, MODULATION_TURBO, 18000, 14, 36, 2 },	\
+	{ 1, MODULATION_TURBO, 24000, 9, 176, 4 },	\
+	{ 1, MODULATION_TURBO, 36000, 13, 72, 4 },	\
+	{ 1, MODULATION_TURBO, 48000, 8, 96, 4 },	\
+	{ 1, MODULATION_TURBO, 54000, 12, 108, 4 } }	\
+}
+
+#define AR5K_RATES_XR { 12, {					\
+	255, 3, 1, 255, 255, 255, 2, 0, 10, 8, 6, 4,		\
+	11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255,	\
+	255, 255, 255, 255, 255, 255, 255, 255 }, {		\
+	{ 1, MODULATION_XR, 500, 7, 129, 0 },		\
+	{ 1, MODULATION_XR, 1000, 2, 139, 1 },		\
+	{ 1, MODULATION_XR, 2000, 6, 150, 2 },		\
+	{ 1, MODULATION_XR, 3000, 1, 150, 3 },		\
+	{ 1, IEEE80211_RATE_OFDM, 6000, 11, 140, 4 },	\
+	{ 1, IEEE80211_RATE_OFDM, 9000, 15, 18, 4 },	\
+	{ 1, IEEE80211_RATE_OFDM, 12000, 10, 152, 6 },	\
+	{ 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 6 },	\
+	{ 1, IEEE80211_RATE_OFDM, 24000, 9, 176, 8 },	\
+	{ 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 8 },	\
+	{ 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 8 },	\
+	{ 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 8 } }	\
+}
+
+/*
+ * Crypto definitions
+ */
+
+#define AR5K_KEYCACHE_SIZE	8
+
+/***********************\
+ HW RELATED DEFINITIONS
+\***********************/
+
+/*
+ * Misc definitions
+ */
+#define	AR5K_RSSI_EP_MULTIPLIER	(1<<7)
+
+#define AR5K_ASSERT_ENTRY(_e, _s) do {		\
+	if (_e >= _s)				\
+		return (false);			\
+} while (0)
+
+
+enum ath5k_ant_setting {
+	AR5K_ANT_VARIABLE	= 0,	/* variable by programming */
+	AR5K_ANT_FIXED_A	= 1,	/* fixed to 11a frequencies */
+	AR5K_ANT_FIXED_B	= 2,	/* fixed to 11b frequencies */
+	AR5K_ANT_MAX		= 3,
+};
+
+/*
+ * HAL interrupt abstraction
+ */
+
+/*
+ * These are mapped to take advantage of some common bits
+ * between the MAC chips, to be able to set intr properties
+ * easier. Some of them are not used yet inside OpenHAL.
+ */
+enum ath5k_int {
+	AR5K_INT_RX	= 0x00000001,
+	AR5K_INT_RXDESC	= 0x00000002,
+	AR5K_INT_RXNOFRM = 0x00000008,
+	AR5K_INT_RXEOL	= 0x00000010,
+	AR5K_INT_RXORN	= 0x00000020,
+	AR5K_INT_TX	= 0x00000040,
+	AR5K_INT_TXDESC	= 0x00000080,
+	AR5K_INT_TXURN	= 0x00000800,
+	AR5K_INT_MIB	= 0x00001000,
+	AR5K_INT_RXPHY	= 0x00004000,
+	AR5K_INT_RXKCM	= 0x00008000,
+	AR5K_INT_SWBA	= 0x00010000,
+	AR5K_INT_BMISS	= 0x00040000,
+	AR5K_INT_BNR	= 0x00100000,
+	AR5K_INT_GPIO	= 0x01000000,
+	AR5K_INT_FATAL	= 0x40000000,
+	AR5K_INT_GLOBAL	= 0x80000000,
+
+	/*A sum of all the common bits*/
+	AR5K_INT_COMMON  = AR5K_INT_RXNOFRM
+			| AR5K_INT_RXDESC
+			| AR5K_INT_RXEOL
+			| AR5K_INT_RXORN
+			| AR5K_INT_TXURN
+			| AR5K_INT_TXDESC
+			| AR5K_INT_MIB
+			| AR5K_INT_RXPHY
+			| AR5K_INT_RXKCM
+			| AR5K_INT_SWBA
+			| AR5K_INT_BMISS
+			| AR5K_INT_GPIO,
+	AR5K_INT_NOCARD	= 0xffffffff /*Declare that the card
+				       has been removed*/
+};
+
+/*
+ * Power management
+ */
+enum ath5k_power_mode {
+	AR5K_PM_UNDEFINED = 0,
+	AR5K_PM_AUTO,
+	AR5K_PM_AWAKE,
+	AR5K_PM_FULL_SLEEP,
+	AR5K_PM_NETWORK_SLEEP,
+};
+
+/*
+ * These match net80211 definitions (not used in
+ * d80211).
+ */
+#define AR5K_LED_INIT	0 /*IEEE80211_S_INIT*/
+#define AR5K_LED_SCAN	1 /*IEEE80211_S_SCAN*/
+#define AR5K_LED_AUTH	2 /*IEEE80211_S_AUTH*/
+#define AR5K_LED_ASSOC	3 /*IEEE80211_S_ASSOC*/
+#define AR5K_LED_RUN	4 /*IEEE80211_S_RUN*/
+
+/* GPIO-controlled software LED */
+#define AR5K_SOFTLED_PIN	0
+#define AR5K_SOFTLED_ON		0
+#define AR5K_SOFTLED_OFF	1
+
+/*
+ * Chipset capabilities -see ath_hal_getcapability-
+ * get_capability function is not yet fully implemented
+ * in OpenHAL so most of these don't work yet...
+ */
+enum ath5k_capability_type {
+	AR5K_CAP_REG_DMN		= 0,	/* Used to get current reg. domain id */
+	AR5K_CAP_TKIP_MIC		= 2,	/* Can handle TKIP MIC in hardware */
+	AR5K_CAP_TKIP_SPLIT		= 3,	/* TKIP uses split keys */
+	AR5K_CAP_PHYCOUNTERS		= 4,	/* PHY error counters */
+	AR5K_CAP_DIVERSITY		= 5,	/* Supports fast diversity */
+	AR5K_CAP_NUM_TXQUEUES		= 6,	/* Used to get max number of hw txqueues */
+	AR5K_CAP_VEOL			= 7,	/* Supports virtual EOL */
+	AR5K_CAP_COMPRESSION		= 8,	/* Supports compression */
+	AR5K_CAP_BURST			= 9,	/* Supports packet bursting */
+	AR5K_CAP_FASTFRAME		= 10,	/* Supports fast frames */
+	AR5K_CAP_TXPOW			= 11,	/* Used to get global tx power limit */
+	AR5K_CAP_TPC			= 12,	/* Can do per-packet tx power control (needed for 802.11a) */
+	AR5K_CAP_BSSIDMASK		= 13,	/* Supports bssid mask */
+	AR5K_CAP_MCAST_KEYSRCH		= 14,	/* Supports multicast key search */
+	AR5K_CAP_TSF_ADJUST		= 15,	/* Supports beacon tsf adjust */
+	AR5K_CAP_XR			= 16,	/* Supports XR mode */
+	AR5K_CAP_WME_TKIPMIC 		= 17,	/* Supports TKIP MIC when using WMM */
+	AR5K_CAP_CHAN_HALFRATE 		= 18,	/* Supports half rate channels */
+	AR5K_CAP_CHAN_QUARTERRATE 	= 19,	/* Supports quarter rate channels */
+	AR5K_CAP_RFSILENT		= 20,	/* Supports RFsilent */
+};
+
+struct ath5k_capabilities {
+	/*
+	 * Supported PHY modes
+	 * (ie. CHANNEL_A, CHANNEL_B, ...)
+	 */
+	DECLARE_BITMAP(cap_mode, NUM_IEEE80211_MODES);
+
+	/*
+	 * Frequency range (without regulation restrictions)
+	 */
+	struct {
+		u16	range_2ghz_min;
+		u16	range_2ghz_max;
+		u16	range_5ghz_min;
+		u16	range_5ghz_max;
+	} cap_range;
+
+	/*
+	 * Active regulation domain settings
+	 */
+	struct {
+		enum ath5k_regdom reg_current;
+		enum ath5k_regdom reg_hw;
+	} cap_regdomain;
+
+	/*
+	 * Values stored in the EEPROM (some of them...)
+	 */
+	struct ath5k_eeprom_info	cap_eeprom;
+
+	/*
+	 * Queue information
+	 */
+	struct {
+		u8	q_tx_num;
+	} cap_queues;
+};
+
+
+/***************************************\
+  HARDWARE ABSTRACTION LAYER STRUCTURE
+\***************************************/
+
+/*
+ * Misc defines
+ */
+
+#define AR5K_MAX_GPIO		10
+#define AR5K_MAX_RF_BANKS	8
+
+struct ath_hw {
+	u32			ah_magic;
+
+	void			*ah_sc;
+	void __iomem		*ah_sh;
+	enum ath5k_countrycode	ah_country_code;
+
+	enum ath5k_int		ah_imr;
+
+	enum ieee80211_if_types	ah_op_mode;
+	enum ath5k_power_mode	ah_power_mode;
+	struct ieee80211_channel ah_current_channel;
+	bool			ah_turbo;
+	bool			ah_calibration;
+	bool			ah_running;
+	bool			ah_single_chip;
+	enum ath5k_rfgain	ah_rf_gain;
+
+	u32			ah_mac_srev;
+	u16			ah_mac_version;
+	u16			ah_mac_revision;
+	u16			ah_phy_revision;
+	u16			ah_radio_5ghz_revision;
+	u16			ah_radio_2ghz_revision;
+
+	enum ath5k_version	ah_version;
+	enum ath5k_radio	ah_radio;
+	u32			ah_phy;
+
+	bool			ah_5ghz;
+	bool			ah_2ghz;
+
+#define ah_regdomain		ah_capabilities.cap_regdomain.reg_current
+#define ah_regdomain_hw		ah_capabilities.cap_regdomain.reg_hw
+#define ah_modes		ah_capabilities.cap_mode
+#define ah_ee_version		ah_capabilities.cap_eeprom.ee_version
+
+	u32			ah_atim_window;
+	u32			ah_aifs;
+	u32			ah_cw_min;
+	u32			ah_cw_max;
+	bool			ah_software_retry;
+	u32			ah_limit_tx_retries;
+
+	u32			ah_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
+	bool			ah_ant_diversity;
+
+	u8			ah_sta_id[ETH_ALEN];
+	u8			ah_bssid[ETH_ALEN];
+
+	u32			ah_gpio[AR5K_MAX_GPIO];
+	int			ah_gpio_npins;
+
+	struct ath5k_capabilities ah_capabilities;
+
+	struct ath5k_txq_info	ah_txq[AR5K_NUM_TX_QUEUES];
+	u32			ah_txq_interrupts;
+
+	u32			*ah_rf_banks;
+	size_t			ah_rf_banks_size;
+	struct ath5k_gain	ah_gain;
+	u32			ah_offset[AR5K_MAX_RF_BANKS];
+
+	struct {
+		u16		txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE];
+		u16		txp_rates[AR5K_MAX_RATES];
+		s16		txp_min;
+		s16		txp_max;
+		bool		txp_tpc;
+		s16		txp_ofdm;
+	} ah_txpower;
+
+	struct {
+		bool		r_enabled;
+		int		r_last_alert;
+		struct ieee80211_channel r_last_channel;
+	} ah_radar;
+
+	/*
+	 * Function pointers
+	 */
+	int (*ah_setup_tx_desc)(struct ath_hw *, struct ath_desc *,
+		unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
+		unsigned int, unsigned int, unsigned int, unsigned int,
+		unsigned int, unsigned int, unsigned int);
+	bool (*ah_setup_xtx_desc)(struct ath_hw *, struct ath_desc *,
+		unsigned int, unsigned int, unsigned int, unsigned int,
+		unsigned int, unsigned int);
+	int (*ah_fill_tx_desc)(struct ath_hw *, struct ath_desc *,
+		unsigned int, bool, bool);
+	int (*ah_proc_tx_desc)(struct ath_hw *, struct ath_desc *);
+	int (*ah_proc_rx_desc)(struct ath_hw *, struct ath_desc *);
+};
+
+/*
+ * Prototypes
+ */
+
+/* General Functions */
+extern int ath5k_hw_register_timeout(struct ath_hw *hal, u32 reg, u32 flag, u32 val, bool is_set);
+/* Attach/Detach Functions */
+extern struct ath_hw *ath5k_hw_attach(u16 device, u8 mac_version, void *sc, void __iomem *sh);
+extern const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath_hw *hal, unsigned int mode);
+extern void ath5k_hw_detach(struct ath_hw *hal);
+/* Reset Functions */
+extern int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode, struct ieee80211_channel *channel, bool change_channel);
+/* Power management functions */
+extern int ath5k_hw_set_power(struct ath_hw *hal, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration);
+/* DMA Related Functions */
+extern void ath5k_hw_start_rx(struct ath_hw *hal);
+extern int ath5k_hw_stop_rx_dma(struct ath_hw *hal);
+extern u32 ath5k_hw_get_rx_buf(struct ath_hw *hal);
+extern void ath5k_hw_put_rx_buf(struct ath_hw *hal, u32 phys_addr);
+extern int ath5k_hw_tx_start(struct ath_hw *hal, unsigned int queue);
+extern int ath5k_hw_stop_tx_dma(struct ath_hw *hal, unsigned int queue);
+extern u32 ath5k_hw_get_tx_buf(struct ath_hw *hal, unsigned int queue);
+extern int ath5k_hw_put_tx_buf(struct ath_hw *hal, unsigned int queue, u32 phys_addr);
+extern int ath5k_hw_update_tx_triglevel(struct ath_hw *hal, bool increase);
+/* Interrupt handling */
+extern bool ath5k_hw_is_intr_pending(struct ath_hw *hal);
+extern int ath5k_hw_get_isr(struct ath_hw *hal, enum ath5k_int *interrupt_mask);
+extern enum ath5k_int ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask);
+/* EEPROM access functions */
+extern int ath5k_hw_set_regdomain(struct ath_hw *hal, u16 regdomain);
+/* Protocol Control Unit Functions */
+extern int ath5k_hw_set_opmode(struct ath_hw *hal);
+/* BSSID Functions */
+extern void ath5k_hw_get_lladdr(struct ath_hw *hal, u8 *mac);
+extern int ath5k_hw_set_lladdr(struct ath_hw *hal, const u8 *mac);
+extern void ath5k_hw_set_associd(struct ath_hw *hal, const u8 *bssid, u16 assoc_id);
+extern int ath5k_hw_set_bssid_mask(struct ath_hw *hal, const u8 *mask);
+/* Receive start/stop functions */
+extern void ath5k_hw_start_rx_pcu(struct ath_hw *hal);
+extern void ath5k_hw_stop_pcu_recv(struct ath_hw *hal);
+/* RX Filter functions */
+extern void ath5k_hw_set_mcast_filter(struct ath_hw *hal, u32 filter0, u32 filter1);
+extern int ath5k_hw_set_mcast_filterindex(struct ath_hw *hal, u32 index);
+extern int ath5k_hw_clear_mcast_filter_idx(struct ath_hw *hal, u32 index);
+extern u32 ath5k_hw_get_rx_filter(struct ath_hw *ah);
+extern void ath5k_hw_set_rx_filter(struct ath_hw *ah, u32 filter);
+/* Beacon related functions */
+extern u32 ath5k_hw_get_tsf32(struct ath_hw *hal);
+extern u64 ath5k_hw_get_tsf64(struct ath_hw *hal);
+extern void ath5k_hw_reset_tsf(struct ath_hw *hal);
+extern void ath5k_hw_init_beacon(struct ath_hw *hal, u32 next_beacon, u32 interval);
+extern int ath5k_hw_set_beacon_timers(struct ath_hw *hal, const struct ath5k_beacon_state *state);
+extern void ath5k_hw_reset_beacon(struct ath_hw *hal);
+extern int ath5k_hw_wait_for_beacon(struct ath_hw *hal, unsigned long phys_addr);
+extern void ath5k_hw_update_mib_counters(struct ath_hw *hal, struct ath5k_mib_stats *statistics);
+/* ACK/CTS Timeouts */
+extern int ath5k_hw_set_ack_timeout(struct ath_hw *hal, unsigned int timeout);
+extern unsigned int ath5k_hw_get_ack_timeout(struct ath_hw *hal);
+extern int ath5k_hw_set_cts_timeout(struct ath_hw *hal, unsigned int timeout);
+extern unsigned int ath5k_hw_get_cts_timeout(struct ath_hw *hal);
+/* Key table (WEP) functions */
+extern int ath5k_hw_reset_key(struct ath_hw *hal, u16 entry);
+extern int ath5k_hw_is_key_valid(struct ath_hw *hal, u16 entry);
+extern int ath5k_hw_set_key(struct ath_hw *hal, u16 entry, const struct ieee80211_key_conf *key, const u8 *mac);
+extern int ath5k_hw_set_key_lladdr(struct ath_hw *hal, u16 entry, const u8 *mac);
+/* Queue Control Unit, DFS Control Unit Functions */
+extern int ath5k_hw_setup_tx_queue(struct ath_hw *hal, enum ath5k_tx_queue queue_type, struct ath5k_txq_info *queue_info);
+extern int ath5k_hw_setup_tx_queueprops(struct ath_hw *hal, int queue, const struct ath5k_txq_info *queue_info);
+extern int ath5k_hw_get_tx_queueprops(struct ath_hw *hal, int queue, struct ath5k_txq_info *queue_info);
+extern void ath5k_hw_release_tx_queue(struct ath_hw *hal, unsigned int queue);
+extern int ath5k_hw_reset_tx_queue(struct ath_hw *hal, unsigned int queue);
+extern u32 ath5k_hw_num_tx_pending(struct ath_hw *hal, unsigned int queue);
+extern int ath5k_hw_set_slot_time(struct ath_hw *hal, unsigned int slot_time);
+extern unsigned int ath5k_hw_get_slot_time(struct ath_hw *hal);
+/* Hardware Descriptor Functions */
+extern int ath5k_hw_setup_rx_desc(struct ath_hw *hal, struct ath_desc *desc, u32 size, unsigned int flags);
+/* GPIO Functions */
+extern void ath5k_hw_set_ledstate(struct ath_hw *hal, unsigned int state);
+extern int ath5k_hw_set_gpio_output(struct ath_hw *hal, u32 gpio);
+extern int ath5k_hw_set_gpio_input(struct ath_hw *hal, u32 gpio);
+extern u32 ath5k_hw_get_gpio(struct ath_hw *hal, u32 gpio);
+extern int ath5k_hw_set_gpio(struct ath_hw *hal, u32 gpio, u32 val);
+extern void ath5k_hw_set_gpio_intr(struct ath_hw *hal, unsigned int gpio, u32 interrupt_level);
+/* Regulatory Domain/Channels Setup */
+extern u16 ath5k_get_regdomain(struct ath_hw *hal);
+/* Misc functions */
+extern void ath5k_hw_dump_state(struct ath_hw *hal);
+extern int ath5k_hw_get_capability(struct ath_hw *hal, enum ath5k_capability_type cap_type, u32 capability, u32 *result);
+
+
+/* Initial register settings functions */
+extern int ath5k_hw_write_initvals(struct ath_hw *hal, u8 mode, bool change_channel);
+/* Initialize RF */
+extern int ath5k_hw_rfregs(struct ath_hw *hal, struct ieee80211_channel *channel, unsigned int mode);
+extern int ath5k_hw_rfgain(struct ath_hw *hal, unsigned int freq);
+extern enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath_hw *hal);
+extern int ath5k_hw_set_rfgain_opt(struct ath_hw *hal);
+
+
+/* PHY/RF channel functions */
+extern bool ath5k_channel_ok(struct ath_hw *hal, u16 freq, unsigned int flags);
+extern int ath5k_hw_channel(struct ath_hw *hal, struct ieee80211_channel *channel);
+/* PHY calibration */
+extern int ath5k_hw_phy_calibrate(struct ath_hw *hal, struct ieee80211_channel *channel);
+extern int ath5k_hw_phy_disable(struct ath_hw *hal);
+/* Misc PHY functions */
+extern u16 ath5k_hw_radio_revision(struct ath_hw *hal, unsigned int chan);
+extern void ath5k_hw_set_def_antenna(struct ath_hw *hal, unsigned int ant);
+extern unsigned int ath5k_hw_get_def_antenna(struct ath_hw *hal);
+/* TX power setup */
+extern int ath5k_hw_txpower(struct ath_hw *hal, struct ieee80211_channel *channel, unsigned int txpower);
+extern int ath5k_hw_set_txpower_limit(struct ath_hw *hal, unsigned int power);
+
+
+static inline u32 ath5k_hw_reg_read(struct ath_hw *hal, u16 reg)
+{
+	return ioread32(hal->ah_sh + reg);
+}
+
+static inline void ath5k_hw_reg_write(struct ath_hw *hal, u32 val, u16 reg)
+{
+	iowrite32(val, hal->ah_sh + reg);
+}
+
+#endif
diff -puN /dev/null drivers/net/wireless/ath5k/base.c
--- /dev/null
+++ a/drivers/net/wireless/ath5k/base.c
@@ -0,0 +1,2579 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * Copyright (c) 2006 Devicescape Software, Inc.
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#define	ATH_PCI_VERSION	"0.9.5.0-BSD"
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/cache.h>
+#include <linux/pci.h>
+#include <linux/ethtool.h>
+#include <linux/uaccess.h>
+
+#include <net/ieee80211_radiotap.h>
+
+#include <asm/unaligned.h>
+
+#include "base.h"
+#include "reg.h"
+
+#define ATH_DEBUG_MODES		0 /* Show found modes in the log? */
+#define ATH_DUMP_SKB		0 /* show skb contents */
+#define AR_DEBUG		1
+
+/* unaligned little endian access */
+#define LE_READ_2(_p) (le16_to_cpu(get_unaligned((__le16 *)(_p))))
+#define LE_READ_4(_p) (le32_to_cpu(get_unaligned((__le32 *)(_p))))
+
+#if AR_DEBUG
+#define	DPRINTF(sc, _m, _fmt...) do {				\
+	if (unlikely(((sc)->debug & (_m)) && net_ratelimit()))	\
+		printk(KERN_DEBUG _fmt);			\
+} while (0)
+#else
+static inline int __attribute__ ((format (printf, 3, 4)))
+DPRINTF(struct ath_softc *sc, unsigned int m, const char *fmt, ...)
+{
+	return 0;
+}
+#endif
+enum {
+	ATH_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
+	ATH_DEBUG_RESET		= 0x00000020,	/* reset processing */
+	ATH_DEBUG_MODE		= 0x00000040,	/* mode init/setup */
+	ATH_DEBUG_BEACON	= 0x00000080,	/* beacon handling */
+	ATH_DEBUG_INTR		= 0x00001000,	/* ISR */
+	ATH_DEBUG_BEACON_PROC	= 0x00008000,	/* beacon ISR proc */
+	ATH_DEBUG_CALIBRATE	= 0x00010000,	/* periodic calibration */
+	ATH_DEBUG_LED		= 0x00100000,	/* led management */
+	ATH_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
+	ATH_DEBUG_ANY		= 0xffffffff
+};
+
+enum {
+	ATH_LED_TX,
+	ATH_LED_RX,
+};
+
+static int ath_calinterval = ATH_SHORT_CALIB;
+
+static int countrycode = CTRY_DEFAULT;
+static int outdoor = true;
+static int xchanmode = true;
+module_param(countrycode, int, 0);
+MODULE_PARM_DESC(countrycode, "Override default country code");
+module_param(outdoor, int, 0);
+MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use");
+module_param(xchanmode, int, 0);
+MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode");
+
+#if AR_DEBUG
+static unsigned int ath_debug;
+module_param_named(debug, ath_debug, uint, 0);
+#endif
+
+/*
+ * User a static table of PCI id's for now.  While this is the
+ * "new way" to do things, we may want to switch back to having
+ * the HAL check them by defining a probe method.
+ */
+static struct pci_device_id ath_pci_id_table[] __devinitdata = {
+	{ PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */
+	{ PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */
+	{ PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 */
+	{ PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */
+	{ PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */
+	{ PCI_VDEVICE(3COM_2,  0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */
+	{ PCI_VDEVICE(3COM,    0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */
+	{ PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */
+	{ PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 },
+	{ PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 },
+	{ PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 },
+	{ PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 },
+	{ PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 },
+	{ PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 },
+	{ PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */
+	{ PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */
+	{ PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* 5424 Condor (PCI-E)*/
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci, ath_pci_id_table);
+
+static void ath_led_event(struct ath_softc *, int);
+static int ath_reset(struct ieee80211_hw *);
+
+#if AR_DEBUG
+static void ath_printrxbuf(struct ath_buf *bf, int done)
+{
+	struct ath_desc *ds = bf->desc;
+
+	printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n",
+		ds, (unsigned long long)bf->daddr,
+		ds->ds_link, ds->ds_data, ds->ds_ctl0, ds->ds_ctl1,
+		ds->ds_hw[0], ds->ds_hw[1],
+		!done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!');
+}
+
+static void ath_printtxbuf(struct ath_buf *bf, int done)
+{
+	struct ath_desc *ds = bf->desc;
+
+	printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
+		"%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
+		ds->ds_data, ds->ds_ctl0, ds->ds_ctl1,
+		ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
+		!done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!');
+}
+#endif
+
+#if ATH_DUMP_SKB
+static inline void ath_dump_skb(struct sk_buff *skb, const char *prefix)
+{
+	print_hex_dump_bytes(prefix, DUMP_PREFIX_NONE, skb->data,
+			min(200U, skb->len));
+}
+#else
+static inline void ath_dump_skb(struct sk_buff *skb, const char *prefix) {}
+#endif
+
+static inline void ath_cleanup_txbuf(struct ath_softc *sc, struct ath_buf *bf)
+{
+	BUG_ON(!bf);
+	if (!bf->skb)
+		return;
+	pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len,
+			PCI_DMA_TODEVICE);
+	dev_kfree_skb(bf->skb);
+	bf->skb = NULL;
+}
+
+static void ath_tasklet_reset(unsigned long data)
+{
+	struct ath_softc *sc = (void *)data;
+
+	ath_reset(sc->hw);
+}
+
+static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ieee80211_tx_status txs = {};
+	struct ath_buf *bf, *bf0;
+	struct ath_desc *ds;
+	struct sk_buff *skb;
+	int ret;
+
+	spin_lock(&txq->lock);
+	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+		ds = bf->desc;
+
+		/* TODO only one segment */
+		pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr,
+				sc->desc_len, PCI_DMA_FROMDEVICE);
+		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds);
+		if (unlikely(ret == -EINPROGRESS))
+			break;
+		else if (unlikely(ret)) {
+			printk(KERN_ERR "ath: error %d while processing "
+				"queue %u\n", ret, txq->qnum);
+			break;
+		}
+
+		skb = bf->skb;
+		bf->skb = NULL;
+		pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
+				PCI_DMA_TODEVICE);
+
+		txs.control = bf->ctl;
+		txs.retry_count = ds->ds_txstat.ts_shortretry +
+			ds->ds_txstat.ts_longretry / 6;
+		if (unlikely(ds->ds_txstat.ts_status)) {
+			sc->ll_stats.dot11ACKFailureCount++;
+			if (ds->ds_txstat.ts_status & AR5K_TXERR_XRETRY)
+				txs.excessive_retries = 1;
+			else if (ds->ds_txstat.ts_status & AR5K_TXERR_FILT)
+				txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED;
+		} else {
+			txs.flags |= IEEE80211_TX_STATUS_ACK;
+			txs.ack_signal = ds->ds_txstat.ts_rssi;
+		}
+
+		ieee80211_tx_status(sc->hw, skb, &txs);
+		sc->tx_stats.data[txq->qnum].count++;
+
+		spin_lock(&sc->txbuflock);
+		sc->tx_stats.data[txq->qnum].len--;
+		list_move_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+		spin_unlock(&sc->txbuflock);
+	}
+	if (likely(list_empty(&txq->q)))
+		txq->link = NULL;
+	spin_unlock(&txq->lock);
+	if (sc->txbuf_len > ATH_TXBUF / 5)
+		ieee80211_wake_queues(sc->hw);
+}
+
+static void ath_tasklet_tx(unsigned long data)
+{
+	struct ath_softc *sc = (void *)data;
+
+	ath_tx_processq(sc, sc->txq);
+
+	ath_led_event(sc, ATH_LED_TX);
+}
+
+static int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ath_hw *ah = sc->ah;
+	struct sk_buff *skb = bf->skb;
+	struct ath_desc *ds;
+
+	if (likely(skb == NULL)) {
+		unsigned int off;
+
+		/*
+		 * Allocate buffer with headroom_needed space for the
+		 * fake physical layer header at the start.
+		 */
+		skb = dev_alloc_skb(sc->rxbufsize + sc->cachelsz - 1);
+		if (unlikely(skb == NULL)) {
+			printk(KERN_ERR "ath: can't alloc skbuff of size %u\n",
+					sc->rxbufsize + sc->cachelsz - 1);
+			return -ENOMEM;
+		}
+		/*
+		 * Cache-line-align.  This is important (for the
+		 * 5210 at least) as not doing so causes bogus data
+		 * in rx'd frames.
+		 */
+		off = ((unsigned long)skb->data) % sc->cachelsz;
+		if (off != 0)
+			skb_reserve(skb, sc->cachelsz - off);
+
+		bf->skb = skb;
+		bf->skbaddr = pci_map_single(sc->pdev,
+			skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE);
+		if (unlikely(pci_dma_mapping_error(bf->skbaddr))) {
+			printk(KERN_ERR "%s: DMA mapping failed\n", __func__);
+			dev_kfree_skb(skb);
+			bf->skb = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	/*
+	 * Setup descriptors.  For receive we always terminate
+	 * the descriptor list with a self-linked entry so we'll
+	 * not get overrun under high load (as can happen with a
+	 * 5212 when ANI processing enables PHY error frames).
+	 *
+	 * To insure the last descriptor is self-linked we create
+	 * each descriptor as self-linked and add it to the end.  As
+	 * each additional descriptor is added the previous self-linked
+	 * entry is ``fixed'' naturally.  This should be safe even
+	 * if DMA is happening.  When processing RX interrupts we
+	 * never remove/process the last, self-linked, entry on the
+	 * descriptor list.  This insures the hardware always has
+	 * someplace to write a new frame.
+	 */
+	ds = bf->desc;
+	ds->ds_link = bf->daddr;	/* link to self */
+	ds->ds_data = bf->skbaddr;
+	ath5k_hw_setup_rx_desc(ah, ds,
+		skb_tailroom(skb),	/* buffer size */
+		0);
+
+	if (sc->rxlink != NULL)
+		*sc->rxlink = bf->daddr;
+	sc->rxlink = &ds->ds_link;
+	return 0;
+}
+
+static unsigned int ath_rx_decrypted(struct ath_softc *sc,
+		struct ath_desc *ds, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+	unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb);
+
+	if (!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) &&
+			ds->ds_rxstat.rs_keyix != AR5K_RXKEYIX_INVALID)
+		return RX_FLAG_DECRYPTED;
+
+	/* Apparently when a default key is used to decrypt the packet
+	   the hal does not set the index used to decrypt.  In such cases
+	   get the index from the packet. */
+	if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) &&
+			!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) &&
+			skb->len >= hlen + 4) {
+		keyix = skb->data[hlen + 3] >> 6;
+
+		if (test_bit(keyix, sc->keymap))
+			return RX_FLAG_DECRYPTED;
+	}
+
+	return 0;
+}
+
+static inline u64 ath_extend_tsf(struct ath_hw *ah, u32 rstamp)
+{
+	u64 tsf = ath5k_hw_get_tsf64(ah);
+
+	if ((tsf & 0x7fff) < rstamp)
+		tsf -= 0x8000;
+
+	return (tsf & ~0x7fff) | rstamp;
+}
+
+static void ath_tasklet_rx(unsigned long data)
+{
+	struct ieee80211_rx_status rxs = {};
+	struct sk_buff *skb;
+	struct ath_softc *sc = (void *)data;
+	struct ath_buf *bf;
+	struct ath_desc *ds;
+	u16 len;
+	u8 stat;
+	int ret;
+
+	spin_lock(&sc->rxbuflock);
+	do {
+		if (unlikely(list_empty(&sc->rxbuf))) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "ath: empty rx buf pool\n");
+			break;
+		}
+		bf = list_first_entry(&sc->rxbuf, struct ath_buf, list);
+		BUG_ON(bf->skb == NULL);
+		skb = bf->skb;
+		ds = bf->desc;
+
+		/* TODO only one segment */
+		pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr,
+				sc->desc_len, PCI_DMA_FROMDEVICE);
+
+		if (unlikely(ds->ds_link == bf->daddr)) /* this is the end */
+			break;
+
+		ret = sc->ah->ah_proc_rx_desc(sc->ah, ds);
+		if (unlikely(ret == -EINPROGRESS))
+			break;
+		else if (unlikely(ret)) {
+			if (net_ratelimit())
+				printk(KERN_ERR "ath: error in processing rx "
+					"descriptor\n");
+			return;
+		}
+
+		if (unlikely(ds->ds_rxstat.rs_more)) {
+			if (net_ratelimit())
+				printk(KERN_INFO "ath: unsupported jumbo\n");
+			goto next;
+		}
+
+		stat = ds->ds_rxstat.rs_status;
+		if (unlikely(stat)) {
+			if (stat & AR5K_RXERR_PHY)
+				goto next;
+			if (stat & AR5K_RXERR_DECRYPT) {
+				/*
+				 * Decrypt error.  If the error occurred
+				 * because there was no hardware key, then
+				 * let the frame through so the upper layers
+				 * can process it.  This is necessary for 5210
+				 * parts which have no way to setup a ``clear''
+				 * key cache entry.
+				 *
+				 * XXX do key cache faulting
+				 */
+				if (ds->ds_rxstat.rs_keyix ==
+						AR5K_RXKEYIX_INVALID &&
+						!(stat & AR5K_RXERR_CRC))
+					goto accept;
+			}
+			if (stat & AR5K_RXERR_MIC) {
+				rxs.flag |= RX_FLAG_MMIC_ERROR;
+				goto accept;
+			}
+
+			/* let crypto-error packets fall through in MNTR */
+			if ((stat & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) ||
+					sc->opmode != IEEE80211_IF_TYPE_MNTR)
+				goto next;
+		}
+accept:
+		len = ds->ds_rxstat.rs_datalen;
+		pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, len,
+				PCI_DMA_FROMDEVICE);
+		pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
+				PCI_DMA_FROMDEVICE);
+		bf->skb = NULL;
+
+		if (unlikely((ieee80211_get_hdrlen_from_skb(skb) & 3) &&
+					net_ratelimit()))
+			printk(KERN_DEBUG "rx len is not %%4: %u\n",
+					ieee80211_get_hdrlen_from_skb(skb));
+
+		skb_put(skb, len);
+
+		if (sc->opmode == IEEE80211_IF_TYPE_MNTR)
+			rxs.mactime = ath_extend_tsf(sc->ah,
+					ds->ds_rxstat.rs_tstamp);
+		else
+			rxs.mactime = ds->ds_rxstat.rs_tstamp;
+		rxs.freq = sc->curchan->freq;
+		rxs.channel = sc->curchan->chan;
+		rxs.phymode = sc->curmode;
+		rxs.ssi = ds->ds_rxstat.rs_rssi;
+		rxs.antenna = ds->ds_rxstat.rs_antenna;
+		rxs.rate = ds->ds_rxstat.rs_rate;
+		rxs.flag |= ath_rx_decrypted(sc, ds, skb);
+
+		ath_dump_skb(skb, "r");
+
+		__ieee80211_rx(sc->hw, skb, &rxs);
+		sc->led_rxrate = ds->ds_rxstat.rs_rate;
+		ath_led_event(sc, ATH_LED_RX);
+next:
+		list_move_tail(&bf->list, &sc->rxbuf);
+	} while (ath_rxbuf_init(sc, bf) == 0);
+	spin_unlock(&sc->rxbuflock);
+}
+
+/*
+ * Setup the beacon frame for transmit.
+ */
+static int ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf,
+		struct ieee80211_tx_control *ctl)
+{
+	struct sk_buff *skb = bf->skb;
+	struct ath_hw *ah = sc->ah;
+	struct ath_desc *ds;
+	int ret, antenna = 0;
+	u32 flags;
+
+	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
+			PCI_DMA_TODEVICE);
+	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: skb %p [data %p len %u] "
+			"skbaddr %llx\n", __func__, skb, skb->data, skb->len,
+			(unsigned long long)bf->skbaddr);
+	if (pci_dma_mapping_error(bf->skbaddr)) {
+		printk(KERN_ERR "ath: beacon DMA mapping failed\n");
+		return -EIO;
+	}
+
+	ds = bf->desc;
+
+	flags = AR5K_TXDESC_NOACK;
+	if (sc->opmode == IEEE80211_IF_TYPE_IBSS && ath5k_hw_hasveol(ah)) {
+		ds->ds_link = bf->daddr;	/* self-linked */
+		flags |= AR5K_TXDESC_VEOL;
+		/*
+		 * Let hardware handle antenna switching if txantenna is not set
+		 */
+	} else {
+		ds->ds_link = 0;
+		/*
+		 * Switch antenna every 4 beacons if txantenna is not set
+		 * XXX assumes two antennas
+		 */
+		if (antenna == 0)
+			antenna = sc->bsent & 4 ? 2 : 1;
+	}
+
+	ds->ds_data = bf->skbaddr;
+	ret = ah->ah_setup_tx_desc(ah, ds, skb->len + FCS_LEN,
+			ieee80211_get_hdrlen_from_skb(skb),
+			AR5K_PKT_TYPE_BEACON, 0xffff, ctl->tx_rate, 1,
+			AR5K_TXKEYIX_INVALID, antenna, flags, 0, 0);
+	if (ret)
+		goto err_unmap;
+	/* NB: beacon's BufLen must be a multiple of 4 bytes */
+	ret = ah->ah_fill_tx_desc(ah, ds, roundup(skb->len, 4), true, true);
+	if (ret)
+		goto err_unmap;
+
+	return 0;
+err_unmap:
+	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
+	return ret;
+}
+
+/*
+ * Transmit a beacon frame at SWBA.  Dynamic updates to the
+ * frame contents are done as needed and the slot time is
+ * also adjusted based on current state.
+ *
+ * this is usually called from interrupt context (ath_intr())
+ * but also from ath_beacon_config() in IBSS mode which in turn
+ * can be called from a tasklet and user context
+ */
+static void ath_beacon_send(struct ath_softc *sc)
+{
+	struct ath_buf *bf = sc->bbuf;
+	struct ath_hw *ah = sc->ah;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s\n", __func__);
+
+	if (unlikely(bf->skb == NULL || sc->opmode == IEEE80211_IF_TYPE_STA ||
+			sc->opmode == IEEE80211_IF_TYPE_MNTR)) {
+		printk(KERN_WARNING "ath: bf=%p bf_skb=%p\n", bf,
+				bf ? bf->skb : NULL);
+		return;
+	}
+	/*
+	 * Check if the previous beacon has gone out.  If
+	 * not don't don't try to post another, skip this
+	 * period and wait for the next.  Missed beacons
+	 * indicate a problem and should not occur.  If we
+	 * miss too many consecutive beacons reset the device.
+	 */
+	if (unlikely(ath5k_hw_num_tx_pending(ah, sc->bhalq) != 0)) {
+		sc->bmisscount++;
+		DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+			"%s: missed %u consecutive beacons\n",
+			__func__, sc->bmisscount);
+		if (sc->bmisscount > 3) {		/* NB: 3 is a guess */
+			DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+				"%s: stuck beacon time (%u missed)\n",
+				__func__, sc->bmisscount);
+			tasklet_schedule(&sc->restq);
+		}
+		return;
+	}
+	if (unlikely(sc->bmisscount != 0)) {
+		DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+			"%s: resume beacon xmit after %u misses\n",
+			__func__, sc->bmisscount);
+		sc->bmisscount = 0;
+	}
+
+	/*
+	 * Stop any current dma and put the new frame on the queue.
+	 * This should never fail since we check above that no frames
+	 * are still pending on the queue.
+	 */
+	if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) {
+		printk(KERN_WARNING "ath: beacon queue %u didn't stop?\n",
+				sc->bhalq);
+		/* NB: the HAL still stops DMA, so proceed */
+	}
+	pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, bf->skb->len,
+			PCI_DMA_TODEVICE);
+
+	ath5k_hw_put_tx_buf(ah, sc->bhalq, bf->daddr);
+	ath5k_hw_tx_start(ah, sc->bhalq);
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: TXDP[%u] = %llx (%p)\n",
+		__func__, sc->bhalq, (unsigned long long)bf->daddr, bf->desc);
+
+	sc->bsent++;
+}
+
+static int ath_beaconq_config(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->ah;
+	struct ath5k_txq_info qi;
+	int ret;
+
+	ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
+	if (ret)
+		return ret;
+	if (sc->opmode == IEEE80211_IF_TYPE_AP ||
+			sc->opmode == IEEE80211_IF_TYPE_IBSS) {
+		/*
+		* Always burst out beacon and CAB traffic.
+		*/
+		qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT;
+		qi.tqi_cw_min = ATH_BEACON_CWMIN_DEFAULT;
+		qi.tqi_cw_max = ATH_BEACON_CWMAX_DEFAULT;
+	}
+
+	ret = ath5k_hw_setup_tx_queueprops(ah, sc->bhalq, &qi);
+	if (ret) {
+		printk(KERN_ERR "%s: unable to update parameters for beacon "
+			"hardware queue!\n", __func__);
+		return ret;
+	}
+
+	return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */;
+}
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wakeup in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+static void ath_beacon_config(struct ath_softc *sc)
+{
+#define TSF_TO_TU(_h, _l)	(((_h) << 22) | ((_l) >> 10))
+	struct ath_hw *ah = sc->ah;
+	u32 uninitialized_var(nexttbtt), intval, tsftu;
+	u64 tsf;
+
+	intval = sc->bintval & AR5K_BEACON_PERIOD;
+	if (WARN_ON(!intval))
+		return;
+
+	/* current TSF converted to TU */
+	tsf = ath5k_hw_get_tsf64(ah);
+	tsftu = TSF_TO_TU((u32)(tsf >> 32), (u32)tsf);
+
+	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: intval %u hw tsftu %u\n", __func__,
+			intval, tsftu);
+
+	if (sc->opmode == IEEE80211_IF_TYPE_STA ||
+			(sc->opmode == IEEE80211_IF_TYPE_IBSS &&
+				!sc->bbuf->skb)) {
+		ath5k_hw_set_intr(ah, 0);
+		sc->imask |= AR5K_INT_BMISS;
+		sc->bmisscount = 0;
+		ath5k_hw_set_intr(ah, sc->imask);
+	} else if (sc->opmode == IEEE80211_IF_TYPE_IBSS /* TODO || AP */) {
+		ath5k_hw_set_intr(ah, 0);
+		if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
+			/*
+			 * Pull nexttbtt forward to reflect the current
+			 * TSF. Add one intval otherwise the timespan
+			 * can be too short for ibss merges.
+			 */
+			nexttbtt = tsftu + 2 * intval;
+
+			DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u "
+				"intval %u\n", __func__, nexttbtt, intval);
+
+			/*
+			 * In IBSS mode enable the beacon timers but only
+			 * enable SWBA interrupts if we need to manually
+			 * prepare beacon frames.  Otherwise we use a
+			 * self-linked tx descriptor and let the hardware
+			 * deal with things.
+			 */
+			if (!ath5k_hw_hasveol(ah))
+				sc->imask |= AR5K_INT_SWBA;
+		} /* TODO else AP */
+
+		intval |= AR5K_BEACON_ENA;
+
+		ath_beaconq_config(sc);
+		ath5k_hw_init_beacon(ah, nexttbtt, intval);
+
+		sc->bmisscount = 0;
+		ath5k_hw_set_intr(ah, sc->imask);
+		/*
+		 * When using a self-linked beacon descriptor in
+		 * ibss mode load it once here.
+		 */
+		if (sc->opmode == IEEE80211_IF_TYPE_IBSS &&
+				ath5k_hw_hasveol(ah))
+			ath_beacon_send(sc);
+	}
+#undef TSF_TO_TU
+}
+
+static void ath_mode_init(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->ah;
+	u32 rfilt;
+
+	/* configure rx filter */
+	rfilt = sc->filter_flags;
+	ath5k_hw_set_rx_filter(ah, rfilt);
+
+	if (ath5k_hw_hasbssidmask(ah))
+		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
+
+	/* configure operational mode */
+	ath5k_hw_set_opmode(ah);
+
+	ath5k_hw_set_mcast_filter(ah, 0, 0);
+	DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt);
+}
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int ath_startrecv(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->ah;
+	struct ath_buf *bf;
+	int ret;
+
+	sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz);
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: cachelsz %u rxbufsize %u\n",
+		__func__, sc->cachelsz, sc->rxbufsize);
+
+	sc->rxlink = NULL;
+
+	spin_lock_bh(&sc->rxbuflock);
+	list_for_each_entry(bf, &sc->rxbuf, list) {
+		ret = ath_rxbuf_init(sc, bf);
+		if (ret != 0) {
+			spin_unlock_bh(&sc->rxbuflock);
+			goto err;
+		}
+	}
+	bf = list_first_entry(&sc->rxbuf, struct ath_buf, list);
+	spin_unlock_bh(&sc->rxbuflock);
+
+	ath5k_hw_put_rx_buf(ah, bf->daddr);
+	ath5k_hw_start_rx(ah);		/* enable recv descriptors */
+	ath_mode_init(sc);		/* set filters, etc. */
+	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */
+
+	return 0;
+err:
+	return ret;
+}
+
+static inline void ath_update_txpow(struct ath_softc *sc)
+{
+	ath5k_hw_set_txpower_limit(sc->ah, 0);
+}
+
+static int ath_stop_locked(struct ath_softc *);
+
+static int ath_init(struct ath_softc *sc)
+{
+	int ret;
+
+	mutex_lock(&sc->lock);
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: mode %d\n", __func__, sc->opmode);
+
+	/*
+	 * Stop anything previously setup.  This is safe
+	 * no matter this is the first time through or not.
+	 */
+	ath_stop_locked(sc);
+
+	/*
+	 * The basic interface to setting the hardware in a good
+	 * state is ``reset''.  On return the hardware is known to
+	 * be powered up and with interrupts disabled.  This must
+	 * be followed by initialization of the appropriate bits
+	 * and then setup of the interrupt mask.
+	 */
+	sc->curchan = sc->hw->conf.chan;
+	ret = ath5k_hw_reset(sc->ah, sc->opmode, sc->curchan, false);
+	if (ret) {
+		printk(KERN_ERR "unable to reset hardware: %d\n", ret);
+		goto done;
+	}
+	/*
+	 * This is needed only to setup initial state
+	 * but it's best done after a reset.
+	 */
+	ath_update_txpow(sc);
+
+	/*
+	 * Setup the hardware after reset: the key cache
+	 * is filled as needed and the receive engine is
+	 * set going.  Frame transmit is handled entirely
+	 * in the frame output path; there's nothing to do
+	 * here except setup the interrupt mask.
+	 */
+	ret = ath_startrecv(sc);
+	if (ret)
+		goto done;
+
+	/*
+	 * Enable interrupts.
+	 */
+	sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL |
+		AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL;
+
+	ath5k_hw_set_intr(sc->ah, sc->imask);
+
+	mod_timer(&sc->calib_tim, round_jiffies(jiffies +
+			msecs_to_jiffies(ath_calinterval * 1000)));
+
+	ret = 0;
+done:
+	mutex_unlock(&sc->lock);
+	return ret;
+}
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void ath_stoprecv(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->ah;
+
+	ath5k_hw_stop_pcu_recv(ah);	/* disable PCU */
+	ath5k_hw_set_rx_filter(ah, 0);	/* clear recv filter */
+	ath5k_hw_stop_rx_dma(ah);	/* disable DMA engine */
+	mdelay(3);			/* 3ms is long enough for 1 frame */
+#if AR_DEBUG
+	if (unlikely(sc->debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL))) {
+		struct ath_desc *ds;
+		struct ath_buf *bf;
+		int status;
+
+		printk(KERN_DEBUG "%s: rx queue %x, link %p\n", __func__,
+			ath5k_hw_get_rx_buf(ah), sc->rxlink);
+
+		spin_lock_bh(&sc->rxbuflock);
+		list_for_each_entry(bf, &sc->rxbuf, list) {
+			ds = bf->desc;
+			status = ah->ah_proc_rx_desc(ah, ds);
+			if (!status || (sc->debug & ATH_DEBUG_FATAL))
+				ath_printrxbuf(bf, status == 0);
+		}
+		spin_unlock_bh(&sc->rxbuflock);
+	}
+#endif
+	sc->rxlink = NULL;		/* just in case */
+}
+
+static void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_buf *bf, *bf0;
+
+	/*
+	 * NB: this assumes output has been stopped and
+	 *     we do not need to block ath_tx_tasklet
+	 */
+	spin_lock_bh(&txq->lock);
+	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+#if AR_DEBUG
+		if (sc->debug & ATH_DEBUG_RESET)
+			ath_printtxbuf(bf, !sc->ah->ah_proc_tx_desc(sc->ah,
+						bf->desc));
+#endif
+		ath_cleanup_txbuf(sc, bf);
+
+		spin_lock_bh(&sc->txbuflock);
+		sc->tx_stats.data[txq->qnum].len--;
+		list_move_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+		spin_unlock_bh(&sc->txbuflock);
+	}
+	txq->link = NULL;
+	spin_unlock_bh(&txq->lock);
+}
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void ath_draintxq(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->ah;
+	int i;
+
+	/* XXX return value */
+	if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
+		/* don't touch the hardware if marked invalid */
+		(void)ath5k_hw_stop_tx_dma(ah, sc->bhalq);
+		DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %x\n", __func__,
+			ath5k_hw_get_tx_buf(ah, sc->bhalq));
+		for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
+			if (sc->txqs[i].setup) {
+				ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
+				DPRINTF(sc, ATH_DEBUG_RESET, "%s: txq [%u] %x, "
+					"link %p\n", __func__,
+					sc->txqs[i].qnum,
+					ath5k_hw_get_tx_buf(ah,
+							sc->txqs[i].qnum),
+					sc->txqs[i].link);
+			}
+	}
+	ieee80211_start_queues(sc->hw); /* XXX move to callers */
+
+	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
+		if (sc->txqs[i].setup)
+			ath_tx_draintxq(sc, &sc->txqs[i]);
+}
+
+static int ath_stop_locked(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->ah;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: invalid %u\n", __func__,
+			test_bit(ATH_STAT_INVALID, sc->status));
+
+	/*
+	 * Shutdown the hardware and driver:
+	 *    stop output from above
+	 *    disable interrupts
+	 *    turn off timers
+	 *    turn off the radio
+	 *    clear transmit machinery
+	 *    clear receive machinery
+	 *    drain and release tx queues
+	 *    reclaim beacon resources
+	 *    power down hardware
+	 *
+	 * Note that some of this work is not possible if the
+	 * hardware is gone (invalid).
+	 */
+	ieee80211_stop_queues(sc->hw);
+
+	if (!test_bit(ATH_STAT_INVALID, sc->status)) {
+		if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
+			del_timer_sync(&sc->led_tim);
+			ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on);
+			__clear_bit(ATH_STAT_LEDBLINKING, sc->status);
+		}
+		ath5k_hw_set_intr(ah, 0);
+	}
+	ath_draintxq(sc);
+	if (!test_bit(ATH_STAT_INVALID, sc->status)) {
+		ath_stoprecv(sc);
+		ath5k_hw_phy_disable(ah);
+	} else
+		sc->rxlink = NULL;
+
+	return 0;
+}
+
+/*
+ * Stop the device, grabbing the top-level lock to protect
+ * against concurrent entry through ath_init (which can happen
+ * if another thread does a system call and the thread doing the
+ * stop is preempted).
+ */
+static int ath_stop_hw(struct ath_softc *sc)
+{
+	int ret;
+
+	mutex_lock(&sc->lock);
+	ret = ath_stop_locked(sc);
+	if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) {
+		/*
+		 * Set the chip in full sleep mode.  Note that we are
+		 * careful to do this only when bringing the interface
+		 * completely to a stop.  When the chip is in this state
+		 * it must be carefully woken up or references to
+		 * registers in the PCI clock domain may freeze the bus
+		 * (and system).  This varies by chip and is mostly an
+		 * issue with newer parts that go to sleep more quickly.
+		 */
+		if (sc->ah->ah_mac_version >= 7 &&
+				sc->ah->ah_mac_revision >= 8) {
+			/*
+			 * XXX
+			 * don't put newer MAC revisions > 7.8 to sleep because
+			 * of the above mentioned problems
+			 */
+			DPRINTF(sc, ATH_DEBUG_RESET, "%s: mac version > 7.8, "
+				"not putting device to sleep\n", __func__);
+		} else {
+			DPRINTF(sc, ATH_DEBUG_RESET,
+				"%s: putting device to full sleep\n", __func__);
+			ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0);
+		}
+	}
+	ath_cleanup_txbuf(sc, sc->bbuf);
+	mutex_unlock(&sc->lock);
+
+	del_timer_sync(&sc->calib_tim);
+
+	return ret;
+}
+
+static void ath_setcurmode(struct ath_softc *sc, unsigned int mode)
+{
+	if (unlikely(test_bit(ATH_STAT_LEDSOFT, sc->status))) {
+		/* from Atheros NDIS driver, w/ permission */
+		static const struct {
+			u16 rate;	/* tx/rx 802.11 rate */
+			u16 timeOn;	/* LED on time (ms) */
+			u16 timeOff;	/* LED off time (ms) */
+		} blinkrates[] = {
+			{ 108,  40,  10 },
+			{  96,  44,  11 },
+			{  72,  50,  13 },
+			{  48,  57,  14 },
+			{  36,  67,  16 },
+			{  24,  80,  20 },
+			{  22, 100,  25 },
+			{  18, 133,  34 },
+			{  12, 160,  40 },
+			{  10, 200,  50 },
+			{   6, 240,  58 },
+			{   4, 267,  66 },
+			{   2, 400, 100 },
+			{   0, 500, 130 }
+		};
+		const struct ath5k_rate_table *rt =
+				ath5k_hw_get_rate_table(sc->ah, mode);
+		unsigned int i, j;
+
+		BUG_ON(rt == NULL);
+
+		memset(sc->hwmap, 0, sizeof(sc->hwmap));
+		for (i = 0; i < 32; i++) {
+			u8 ix = rt->rate_code_to_index[i];
+			if (ix == 0xff) {
+				sc->hwmap[i].ledon = msecs_to_jiffies(500);
+				sc->hwmap[i].ledoff = msecs_to_jiffies(130);
+				continue;
+			}
+			sc->hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD;
+			if (SHPREAMBLE_FLAG(ix) || rt->rates[ix].modulation ==
+					IEEE80211_RATE_OFDM)
+				sc->hwmap[i].txflags |=
+						IEEE80211_RADIOTAP_F_SHORTPRE;
+			/* receive frames include FCS */
+			sc->hwmap[i].rxflags = sc->hwmap[i].txflags |
+					IEEE80211_RADIOTAP_F_FCS;
+			/* setup blink rate table to avoid per-packet lookup */
+			for (j = 0; j < ARRAY_SIZE(blinkrates) - 1; j++)
+				if (blinkrates[j].rate == /* XXX why 7f? */
+						(rt->rates[ix].dot11_rate&0x7f))
+					break;
+
+			sc->hwmap[i].ledon = msecs_to_jiffies(blinkrates[j].
+					timeOn);
+			sc->hwmap[i].ledoff = msecs_to_jiffies(blinkrates[j].
+					timeOff);
+		}
+	}
+
+	sc->curmode = mode;
+}
+
+/*
+ * Set/change channels.  If the channel is really being changed,
+ * it's done by reseting the chip.  To accomplish this we must
+ * first cleanup any pending DMA, then restart stuff after a la
+ * ath_init.
+ */
+static int ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+	struct ath_hw *ah = sc->ah;
+	int ret;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n",
+		__func__, sc->curchan->chan, sc->curchan->freq,
+		chan->chan, chan->freq);
+
+	if (chan->freq != sc->curchan->freq || chan->val != sc->curchan->val) {
+		/*
+		 * To switch channels clear any pending DMA operations;
+		 * wait long enough for the RX fifo to drain, reset the
+		 * hardware at the new frequency, and then re-enable
+		 * the relevant bits of the h/w.
+		 */
+		ath5k_hw_set_intr(ah, 0);	/* disable interrupts */
+		ath_draintxq(sc);		/* clear pending tx frames */
+		ath_stoprecv(sc);		/* turn off frame recv */
+		ret = ath5k_hw_reset(ah, sc->opmode, chan, true);
+		if (ret) {
+			printk(KERN_ERR "%s: unable to reset channel %u "
+				"(%u Mhz)\n", __func__, chan->chan, chan->freq);
+			return ret;
+		}
+		sc->curchan = chan;
+		ath_update_txpow(sc);
+
+		/*
+		 * Re-enable rx framework.
+		 */
+		ret = ath_startrecv(sc);
+		if (ret) {
+			printk(KERN_ERR "%s: unable to restart recv logic\n",
+					__func__);
+			return ret;
+		}
+
+		/*
+		 * Change channels and update the h/w rate map
+		 * if we're switching; e.g. 11a to 11b/g.
+		 *
+		 * XXX needed?
+		 */
+/*		ath_chan_change(sc, chan); */
+
+		/*
+		 * Re-enable interrupts.
+		 */
+		ath5k_hw_set_intr(ah, sc->imask);
+	}
+
+	return 0;
+}
+
+static int ath_tx_bf(struct ath_softc *sc, struct ath_buf *bf,
+		struct ieee80211_tx_control *ctl)
+{
+	struct ath_hw *ah = sc->ah;
+	struct ath_txq *txq = sc->txq;
+	struct ath_desc *ds = bf->desc;
+	struct sk_buff *skb = bf->skb;
+	unsigned int hdrpad, pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
+	int ret;
+
+	flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
+	bf->ctl = *ctl;
+	/* XXX endianness */
+	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
+			PCI_DMA_TODEVICE);
+
+	if (ctl->flags & IEEE80211_TXCTL_NO_ACK)
+		flags |= AR5K_TXDESC_NOACK;
+
+	if ((ieee80211_get_hdrlen_from_skb(skb) & 3) && net_ratelimit())
+		printk(KERN_DEBUG "tx len is not %%4: %u\n",
+				ieee80211_get_hdrlen_from_skb(skb));
+
+	hdrpad = 0;
+	pktlen = skb->len - hdrpad + FCS_LEN;
+
+	if (!(ctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) {
+		keyidx = ctl->key_idx;
+		pktlen += ctl->icv_len;
+	}
+
+	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
+		ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
+		0xffff, ctl->tx_rate, ctl->retry_limit, keyidx, 0, flags, 0, 0);
+	if (ret)
+		goto err_unmap;
+
+	ds->ds_link = 0;
+	ds->ds_data = bf->skbaddr;
+
+	ret = ah->ah_fill_tx_desc(ah, ds, skb->len, true, true);
+	if (ret)
+		goto err_unmap;
+
+	spin_lock_bh(&txq->lock);
+	list_add_tail(&bf->list, &txq->q);
+	sc->tx_stats.data[txq->qnum].len++;
+	if (txq->link == NULL) /* is this first packet? */
+		ath5k_hw_put_tx_buf(ah, txq->qnum, bf->daddr);
+	else /* no, so only link it */
+		*txq->link = bf->daddr;
+
+	txq->link = &ds->ds_link;
+	ath5k_hw_tx_start(ah, txq->qnum);
+	spin_unlock_bh(&txq->lock);
+
+	return 0;
+err_unmap:
+	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
+	return ret;
+}
+
+static int ath_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
+		struct ieee80211_tx_control *ctl)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_buf *bf;
+	unsigned long flags;
+
+	ath_dump_skb(skb, "t");
+
+	if (sc->opmode == IEEE80211_IF_TYPE_MNTR)
+		DPRINTF(sc, ATH_DEBUG_XMIT, "tx in monitor (scan?)\n");
+
+	sc->led_txrate = ctl->tx_rate;
+
+	spin_lock_irqsave(&sc->txbuflock, flags);
+	if (list_empty(&sc->txbuf)) {
+		if (net_ratelimit())
+			printk(KERN_ERR "ath: no further txbuf available, "
+				"dropping packet\n");
+		spin_unlock_irqrestore(&sc->txbuflock, flags);
+		ieee80211_stop_queue(hw, ctl->queue);
+		return -1;
+	}
+	bf = list_first_entry(&sc->txbuf, struct ath_buf, list);
+	list_del(&bf->list);
+	sc->txbuf_len--;
+	if (list_empty(&sc->txbuf))
+		ieee80211_stop_queues(hw);
+	spin_unlock_irqrestore(&sc->txbuflock, flags);
+
+	bf->skb = skb;
+
+	if (ath_tx_bf(sc, bf, ctl)) {
+		bf->skb = NULL;
+		spin_lock_irqsave(&sc->txbuflock, flags);
+		list_add_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+		spin_unlock_irqrestore(&sc->txbuflock, flags);
+		dev_kfree_skb_any(skb);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int ath_reset(struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_hw *ah = sc->ah;
+	int ret;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "resetting\n");
+	/*
+	 * Convert to a HAL channel description with the flags
+	 * constrained to reflect the current operating mode.
+	 */
+	sc->curchan = hw->conf.chan;
+
+	ath5k_hw_set_intr(ah, 0);
+	ath_draintxq(sc);
+	ath_stoprecv(sc);
+
+	ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true);
+	if (unlikely(ret)) {
+		printk(KERN_ERR "ath: can't reset hardware (%d)\n", ret);
+		goto err;
+	}
+	ath_update_txpow(sc);
+
+	ret = ath_startrecv(sc);
+	if (unlikely(ret)) {
+		printk(KERN_ERR "ath: can't start recv logic\n");
+		goto err;
+	}
+	/*
+	 * We may be doing a reset in response to an ioctl
+	 * that changes the channel so update any state that
+	 * might change as a result.
+	 *
+	 * XXX needed?
+	 */
+/*	ath_chan_change(sc, c); */
+	ath_beacon_config(sc);
+	/* intrs are started by ath_beacon_config */
+
+	ieee80211_wake_queues(hw);
+
+	return 0;
+err:
+	return ret;
+}
+
+static int ath_start(struct ieee80211_hw *hw)
+{
+	return ath_init(hw->priv);
+}
+
+void ath_stop(struct ieee80211_hw *hw)
+{
+	ath_stop_hw(hw->priv);
+}
+
+static int ath_add_interface(struct ieee80211_hw *hw,
+		struct ieee80211_if_init_conf *conf)
+{
+	struct ath_softc *sc = hw->priv;
+	int ret;
+
+	mutex_lock(&sc->lock);
+	if (sc->iface_id) {
+		ret = 0;
+		goto end;
+	}
+
+	sc->iface_id = conf->if_id;
+
+	switch (conf->type) {
+	case IEEE80211_IF_TYPE_STA:
+	case IEEE80211_IF_TYPE_IBSS:
+	case IEEE80211_IF_TYPE_MNTR:
+		sc->opmode = conf->type;
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		goto end;
+	}
+	ret = 0;
+end:
+	mutex_unlock(&sc->lock);
+	return ret;
+}
+
+static void ath_remove_interface(struct ieee80211_hw *hw,
+		struct ieee80211_if_init_conf *conf)
+{
+	struct ath_softc *sc = hw->priv;
+
+	mutex_lock(&sc->lock);
+	if (sc->iface_id != conf->if_id)
+		goto end;
+
+	sc->iface_id = 0;
+end:
+	mutex_unlock(&sc->lock);
+}
+
+static int ath_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+{
+	struct ath_softc *sc = hw->priv;
+
+	sc->bintval = conf->beacon_int * 1000 / 1024;
+	ath_setcurmode(sc, conf->phymode);
+
+	return ath_chan_set(sc, conf->chan);
+}
+
+static int ath_config_interface(struct ieee80211_hw *hw, int if_id,
+		struct ieee80211_if_conf *conf)
+{
+	struct ath_softc *sc = hw->priv;
+	int ret;
+
+	/* Set to a reasonable value. Note that this will
+	 * be set to mac80211's value at ath_config(). */
+	sc->bintval = 1000 * 1000 / 1024;
+	mutex_lock(&sc->lock);
+	if (sc->iface_id != if_id) {
+		ret = -EIO;
+		goto unlock;
+	}
+	if (conf->bssid)
+		ath5k_hw_set_associd(sc->ah, conf->bssid, 0 /* FIXME: aid */);
+	mutex_unlock(&sc->lock);
+
+	return ath_reset(hw);
+unlock:
+	mutex_unlock(&sc->lock);
+	return ret;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	FIF_PROMISC_IN_BSS |  FIF_ALLMULTI | FIF_FCSFAIL | \
+	FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
+	FIF_BCN_PRBRESP_PROMISC
+/*
+ * o always accept unicast, broadcast, and multicast traffic
+ * o maintain current state of phy error reception (the hal
+ *   may enable phy error frames for noise immunity work)
+ * o probe request frames are accepted only when operating in
+ *   hostap, adhoc, or monitor modes
+ * o enable promiscuous mode according to the interface state
+ * o accept beacons:
+ *   - when operating in adhoc mode so the 802.11 layer creates
+ *     node table entries for peers,
+ *   - when operating in station mode for collecting rssi data when
+ *     the station is otherwise quiet, or
+ *   - when scanning
+ */
+static void ath_configure_filter(struct ieee80211_hw *hw,
+               unsigned int changed_flags,
+               unsigned int *new_flags,
+               int mc_count, struct dev_mc_list *mclist)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_hw *ah = sc->ah;
+	u32 rfilt;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* XXX: Start by enabling broadcasts and Unicast, move this later
+	 * to mac802111 and add a flag for these */
+	rfilt = AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST;
+
+	if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) {
+		if (*new_flags & FIF_PROMISC_IN_BSS) {
+			rfilt |= AR5K_RX_FILTER_PROM;
+			__set_bit(ATH_STAT_PROMISC, sc->status);
+		}
+		else
+			__clear_bit(ATH_STAT_PROMISC, sc->status);
+	}
+
+	if (*new_flags & FIF_ALLMULTI)
+		rfilt |= AR5K_RX_FILTER_MCAST;
+	/* This is the best we can do */
+	if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL))
+		rfilt |= AR5K_RX_FILTER_PHYERR;
+	/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
+	* and probes for any BSSID, this needs testing */
+	if (*new_flags & FIF_BCN_PRBRESP_PROMISC)
+		rfilt |= AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ;
+	/* FIF_CONTROL doc says that FIF_PROMISC_IN_BSS is not set we should
+	* only pass on control frames for this station. This needs testing.
+	* I believe right now this enables *all* control frames */
+	if (*new_flags & FIF_CONTROL)
+		rfilt |= AR5K_RX_FILTER_CONTROL;
+
+	/* Additional settings per mode -- this is per ath5k */
+
+	/* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
+
+	if (sc->opmode == IEEE80211_IF_TYPE_MNTR)
+		rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON |
+			AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM;
+	if (sc->opmode != IEEE80211_IF_TYPE_STA)
+		rfilt |= AR5K_RX_FILTER_PROBEREQ;
+	if (sc->opmode != IEEE80211_IF_TYPE_AP &&
+		test_bit(ATH_STAT_PROMISC, sc->status))
+		rfilt |= AR5K_RX_FILTER_PROM;
+	if (sc->opmode == IEEE80211_IF_TYPE_STA ||
+		sc->opmode == IEEE80211_IF_TYPE_IBSS) {
+		rfilt |= AR5K_RX_FILTER_BEACON;
+		/* Note: AR5212 requires AR5K_RX_FILTER_PROM to receive broadcasts,
+		 * perhaps the flags are off, for now to be safe we'll enable it for
+		 * STA and ADHOC until we have this properly mapped */
+		if (ah->ah_version == AR5K_AR5212)
+			rfilt |= AR5K_RX_FILTER_PROM;
+	}
+
+	/* Set the cached hw filter flags, this will alter actually
+	 * be set in HW */
+	sc->filter_flags = rfilt;
+}
+
+static int ath_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+		const u8 *local_addr, const u8 *addr,
+		struct ieee80211_key_conf *key)
+{
+	struct ath_softc *sc = hw->priv;
+	int ret = 0;
+
+	switch(key->alg) {
+	case ALG_WEP:
+		break;
+	case ALG_TKIP:
+	case ALG_CCMP:
+		return -EOPNOTSUPP;
+	default:
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	mutex_lock(&sc->lock);
+
+	switch (cmd) {
+	case SET_KEY:
+		ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, addr);
+		if (ret) {
+			printk(KERN_ERR "ath: can't set the key\n");
+			goto unlock;
+		}
+		__set_bit(key->keyidx, sc->keymap);
+		key->hw_key_idx = key->keyidx;
+		break;
+	case DISABLE_KEY:
+		ath5k_hw_reset_key(sc->ah, key->keyidx);
+		__clear_bit(key->keyidx, sc->keymap);
+		break;
+	default:
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&sc->lock);
+	return ret;
+}
+
+static int ath_get_stats(struct ieee80211_hw *hw,
+		struct ieee80211_low_level_stats *stats)
+{
+	struct ath_softc *sc = hw->priv;
+
+	memcpy(stats, &sc->ll_stats, sizeof(sc->ll_stats));
+
+	return 0;
+}
+
+static int ath_get_tx_stats(struct ieee80211_hw *hw,
+		struct ieee80211_tx_queue_stats *stats)
+{
+	struct ath_softc *sc = hw->priv;
+
+	memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats));
+
+	return 0;
+}
+
+static u64 ath_get_tsf(struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+
+	return ath5k_hw_get_tsf64(sc->ah);
+}
+
+static void ath_reset_tsf(struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+
+	ath5k_hw_reset_tsf(sc->ah);
+}
+
+static int ath_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
+		struct ieee80211_tx_control *ctl)
+{
+	struct ath_softc *sc = hw->priv;
+	int ret;
+
+	ath_dump_skb(skb, "b");
+
+	mutex_lock(&sc->lock);
+
+	if (sc->opmode != IEEE80211_IF_TYPE_IBSS) {
+		ret = -EIO;
+		goto end;
+	}
+
+	ath_cleanup_txbuf(sc, sc->bbuf);
+	sc->bbuf->skb = skb;
+	ret = ath_beacon_setup(sc, sc->bbuf, ctl);
+	if (ret)
+		sc->bbuf->skb = NULL;
+
+end:
+	mutex_unlock(&sc->lock);
+	return ret;
+}
+
+static struct ieee80211_ops ath_hw_ops = {
+	.tx = ath_tx,
+	.start = ath_start,
+	.stop = ath_stop,
+	.add_interface = ath_add_interface,
+	.remove_interface = ath_remove_interface,
+	.config = ath_config,
+	.config_interface = ath_config_interface,
+	.configure_filter = ath_configure_filter,
+	.set_key = ath_set_key,
+	.get_stats = ath_get_stats,
+	.conf_tx = NULL,
+	.get_tx_stats = ath_get_tx_stats,
+	.get_tsf = ath_get_tsf,
+	.reset_tsf = ath_reset_tsf,
+	.beacon_update = ath_beacon_update,
+};
+
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void ath_calibrate(unsigned long data)
+{
+	struct ath_softc *sc = (void *)data;
+	struct ath_hw *ah = sc->ah;
+
+	DPRINTF(sc, ATH_DEBUG_CALIBRATE, "ath: channel %u/%x\n",
+		sc->curchan->chan, sc->curchan->val);
+
+	if (ath5k_hw_get_rf_gain(ah) == AR5K_RFGAIN_NEED_CHANGE) {
+		/*
+		 * Rfgain is out of bounds, reset the chip
+		 * to load new gain values.
+		 */
+		DPRINTF(sc, ATH_DEBUG_RESET, "calibration, resetting\n");
+		ath_reset(sc->hw);
+	}
+	if (ath5k_hw_phy_calibrate(ah, sc->curchan))
+		printk(KERN_ERR "ath: calibration of channel %u failed\n",
+				sc->curchan->chan);
+
+	mod_timer(&sc->calib_tim, round_jiffies(jiffies +
+			msecs_to_jiffies(ath_calinterval * 1000)));
+}
+
+static void ath_led_off(unsigned long data)
+{
+	struct ath_softc *sc = (void *)data;
+
+	if (test_bit(ATH_STAT_LEDENDBLINK, sc->status))
+		__clear_bit(ATH_STAT_LEDBLINKING, sc->status);
+	else {
+		__set_bit(ATH_STAT_LEDENDBLINK, sc->status);
+		ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on);
+		mod_timer(&sc->led_tim, jiffies + sc->led_off);
+	}
+}
+
+/*
+ * Blink the LED according to the specified on/off times.
+ */
+static void ath_led_blink(struct ath_softc *sc, unsigned int on,
+		unsigned int off)
+{
+	DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
+	ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on);
+	__set_bit(ATH_STAT_LEDBLINKING, sc->status);
+	__clear_bit(ATH_STAT_LEDENDBLINK, sc->status);
+	sc->led_off = off;
+	mod_timer(&sc->led_tim, jiffies + on);
+}
+
+static void ath_led_event(struct ath_softc *sc, int event)
+{
+	if (likely(!test_bit(ATH_STAT_LEDSOFT, sc->status)))
+		return;
+	if (unlikely(test_bit(ATH_STAT_LEDBLINKING, sc->status)))
+		return; /* don't interrupt active blink */
+	switch (event) {
+	case ATH_LED_TX:
+		ath_led_blink(sc, sc->hwmap[sc->led_txrate].ledon,
+			sc->hwmap[sc->led_txrate].ledoff);
+		break;
+	case ATH_LED_RX:
+		ath_led_blink(sc, sc->hwmap[sc->led_rxrate].ledon,
+			sc->hwmap[sc->led_rxrate].ledoff);
+		break;
+	}
+}
+
+static irqreturn_t ath_intr(int irq, void *dev_id)
+{
+	struct ath_softc *sc = dev_id;
+	struct ath_hw *ah = sc->ah;
+	enum ath5k_int status;
+	unsigned int counter = 1000;
+
+	if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
+				!ath5k_hw_is_intr_pending(ah)))
+		return IRQ_NONE;
+
+	do {
+		/*
+		* Figure out the reason(s) for the interrupt.  Note
+		* that the hal returns a pseudo-ISR that may include
+		* bits we haven't explicitly enabled so we mask the
+		* value to insure we only process bits we requested.
+		*/
+		ath5k_hw_get_isr(ah, &status);		/* NB: clears IRQ too */
+		DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x/0x%x\n", __func__,
+				status, sc->imask);
+		status &= sc->imask; /* discard unasked for bits */
+		if (unlikely(status & AR5K_INT_FATAL)) {
+			/*
+			* Fatal errors are unrecoverable.  Typically
+			* these are caused by DMA errors.  Unfortunately
+			* the exact reason is not (presently) returned
+			* by the hal.
+			*/
+			tasklet_schedule(&sc->restq);
+		} else if (unlikely(status & AR5K_INT_RXORN)) {
+			tasklet_schedule(&sc->restq);
+		} else {
+			if (status & AR5K_INT_SWBA) {
+				/*
+				* Software beacon alert--time to send a beacon.
+				* Handle beacon transmission directly; deferring
+				* this is too slow to meet timing constraints
+				* under load.
+				*/
+				ath_beacon_send(sc);
+			}
+			if (status & AR5K_INT_RXEOL) {
+				/*
+				* NB: the hardware should re-read the link when
+				*     RXE bit is written, but it doesn't work at
+				*     least on older hardware revs.
+				*/
+				sc->rxlink = NULL;
+			}
+			if (status & AR5K_INT_TXURN) {
+				/* bump tx trigger level */
+				ath5k_hw_update_tx_triglevel(ah, true);
+			}
+			if (status & AR5K_INT_RX)
+				tasklet_schedule(&sc->rxtq);
+			if (status & AR5K_INT_TX)
+				tasklet_schedule(&sc->txtq);
+			if (status & AR5K_INT_BMISS) {
+			}
+			if (status & AR5K_INT_MIB) {
+				/* TODO */
+			}
+		}
+	} while (ath5k_hw_is_intr_pending(ah) && counter-- > 0);
+
+	if (unlikely(!counter && net_ratelimit()))
+		printk(KERN_WARNING "ath: too many interrupts, giving up for "
+				"now\n");
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+static inline short ath_ieee2mhz(short chan)
+{
+	if (chan <= 14 || chan >= 27)
+		return ieee80211chan2mhz(chan);
+	else
+		return 2212 + chan * 20;
+}
+
+static unsigned int ath_copy_rates(struct ieee80211_rate *rates,
+		const struct ath5k_rate_table *rt, unsigned int max)
+{
+	unsigned int i, count;
+
+	if (rt == NULL)
+		return 0;
+
+	for (i = 0, count = 0; i < rt->rate_count && max > 0; i++) {
+		if (!rt->rates[i].valid)
+			continue;
+		rates->rate = rt->rates[i].rate_kbps / 100;
+		rates->val = rt->rates[i].rate_code;
+		rates->flags = rt->rates[i].modulation;
+		rates++;
+		count++;
+		max--;
+	}
+
+	return count;
+}
+
+static unsigned int ath_copy_channels(struct ath_hw *ah,
+		struct ieee80211_channel *channels, unsigned int mode,
+		unsigned int max)
+{
+	static const struct { unsigned int mode, mask, chan; } map[] = {
+		[MODE_IEEE80211A] = { CHANNEL_OFDM, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_A },
+		[MODE_ATHEROS_TURBO] = { CHANNEL_OFDM|CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_T },
+		[MODE_IEEE80211B] = { CHANNEL_CCK, CHANNEL_CCK, CHANNEL_B },
+		[MODE_IEEE80211G] = { CHANNEL_OFDM, CHANNEL_OFDM, CHANNEL_G },
+		[MODE_ATHEROS_TURBOG] = { CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_TG },
+	};
+	static const struct ath5k_regchannel chans_2ghz[] =
+		IEEE80211_CHANNELS_2GHZ;
+	static const struct ath5k_regchannel chans_5ghz[] =
+		IEEE80211_CHANNELS_5GHZ;
+	const struct ath5k_regchannel *chans;
+	enum ath5k_regdom dmn;
+	unsigned int i, count, size, chfreq, all, f, ch;
+
+	if (!test_bit(mode, ah->ah_modes))
+		return 0;
+
+	all = ah->ah_regdomain == DMN_DEFAULT || CHAN_DEBUG == 1;
+
+	switch (mode) {
+	case MODE_IEEE80211A:
+	case MODE_ATHEROS_TURBO:
+		/* 1..220, but 2GHz frequencies are filtered by check_channel */
+		size = all ? 220 : ARRAY_SIZE(chans_5ghz);
+		chans = chans_5ghz;
+		dmn = ath5k_regdom2flag(ah->ah_regdomain,
+				IEEE80211_CHANNELS_5GHZ_MIN);
+		chfreq = CHANNEL_5GHZ;
+		break;
+	case MODE_IEEE80211B:
+	case MODE_IEEE80211G:
+	case MODE_ATHEROS_TURBOG:
+		size = all ? 26 : ARRAY_SIZE(chans_2ghz);
+		chans = chans_2ghz;
+		dmn = ath5k_regdom2flag(ah->ah_regdomain,
+				IEEE80211_CHANNELS_2GHZ_MIN);
+		chfreq = CHANNEL_2GHZ;
+		break;
+	default:
+		printk(KERN_WARNING "bad mode, not copying channels\n");
+		return 0;
+	}
+
+	for (i = 0, count = 0; i < size && max > 0; i++) {
+		ch = all ? i + 1 : chans[i].chan;
+		f = ath_ieee2mhz(ch);
+		/* Check if channel is supported by the chipset */
+		if (!ath5k_channel_ok(ah, f, chfreq))
+			continue;
+
+		/* Match regulation domain */
+		if (!all && !(IEEE80211_DMN(chans[i].domain) &
+							IEEE80211_DMN(dmn)))
+			continue;
+
+		if (!all && (chans[i].mode & map[mode].mask) != map[mode].mode)
+			continue;
+
+		/* Write channel and increment counter */
+		channels->chan = ch;
+		channels->freq = f;
+		channels->val = map[mode].chan;
+		channels++;
+		count++;
+		max--;
+	}
+
+	return count;
+}
+
+#if ATH_DEBUG_MODES
+static void ath_dump_modes(struct ieee80211_hw_mode *modes)
+{
+	unsigned int m, i;
+
+	for (m = 0; m < NUM_IEEE80211_MODES; m++) {
+		printk(KERN_DEBUG "Mode %u: channels %d, rates %d\n", m,
+				modes[m].num_channels, modes[m].num_rates);
+		printk(KERN_DEBUG " channels:\n");
+		for (i = 0; i < modes[m].num_channels; i++)
+			printk(KERN_DEBUG "  %3d %d %.4x %.4x\n",
+					modes[m].channels[i].chan,
+					modes[m].channels[i].freq,
+					modes[m].channels[i].val,
+					modes[m].channels[i].flag);
+		printk(KERN_DEBUG " rates:\n");
+		for (i = 0; i < modes[m].num_rates; i++)
+			printk(KERN_DEBUG "  %4d %.4x %.4x %.4x\n",
+					modes[m].rates[i].rate,
+					modes[m].rates[i].val,
+					modes[m].rates[i].flags,
+					modes[m].rates[i].val2);
+	}
+}
+#else
+static inline void ath_dump_modes(struct ieee80211_hw_mode *modes) {}
+#endif
+
+static int ath_getchannels(struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_hw *ah = sc->ah;
+	struct ieee80211_hw_mode *modes = sc->modes;
+	unsigned int i, max;
+	int ret;
+	enum {
+		A = MODE_IEEE80211A,
+		B = MODE_IEEE80211G, /* this is not a typo, but workaround */
+		G = MODE_IEEE80211B, /* to prefer g over b */
+		T = MODE_ATHEROS_TURBO,
+		TG = MODE_ATHEROS_TURBOG,
+	};
+
+	BUILD_BUG_ON(ARRAY_SIZE(sc->modes) < 3);
+
+	ah->ah_country_code = countrycode;
+
+	modes[A].mode = MODE_IEEE80211A;
+	modes[B].mode = MODE_IEEE80211B;
+	modes[G].mode = MODE_IEEE80211G;
+
+	max = ARRAY_SIZE(sc->rates);
+	modes[A].rates = sc->rates;
+	max -= modes[A].num_rates = ath_copy_rates(modes[A].rates,
+			ath5k_hw_get_rate_table(ah, MODE_IEEE80211A), max);
+	modes[B].rates = &modes[A].rates[modes[A].num_rates];
+	max -= modes[B].num_rates = ath_copy_rates(modes[B].rates,
+			ath5k_hw_get_rate_table(ah, MODE_IEEE80211B), max);
+	modes[G].rates = &modes[B].rates[modes[B].num_rates];
+	max -= modes[G].num_rates = ath_copy_rates(modes[G].rates,
+			ath5k_hw_get_rate_table(ah, MODE_IEEE80211G), max);
+
+	if (!max)
+		printk(KERN_WARNING "yet another rates found, but there is not "
+				"sufficient space to store them\n");
+
+	max = ARRAY_SIZE(sc->channels);
+	modes[A].channels = sc->channels;
+	max -= modes[A].num_channels = ath_copy_channels(ah, modes[A].channels,
+			MODE_IEEE80211A, max);
+	modes[B].channels = &modes[A].channels[modes[A].num_channels];
+	max -= modes[B].num_channels = ath_copy_channels(ah, modes[B].channels,
+			MODE_IEEE80211B, max);
+	modes[G].channels = &modes[B].channels[modes[B].num_channels];
+	max -= modes[G].num_channels = ath_copy_channels(ah, modes[G].channels,
+			MODE_IEEE80211G, max);
+
+	if (!max)
+		printk(KERN_WARNING "yet another modes found, but there is not "
+				"sufficient space to store them\n");
+
+	for (i = 0; i < ARRAY_SIZE(sc->modes); i++)
+		if (modes[i].num_channels) {
+			ret = ieee80211_register_hwmode(hw, &modes[i]);
+			if (ret) {
+				printk(KERN_ERR "can't register hwmode %u\n",i);
+				goto err;
+			}
+		}
+	ath_dump_modes(modes);
+
+	return 0;
+err:
+	return ret;
+}
+
+static int ath_desc_alloc(struct ath_softc *sc, struct pci_dev *pdev)
+{
+	struct ath_desc *ds;
+	struct ath_buf *bf;
+	dma_addr_t da;
+	unsigned int i;
+	int ret;
+
+	/* allocate descriptors */
+	sc->desc_len = sizeof(struct ath_desc) *
+			(ATH_TXBUF * ATH_TXDESC + ATH_RXBUF + ATH_BCBUF + 1);
+	sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
+	if (sc->desc == NULL) {
+		dev_err(&pdev->dev, "can't allocate descriptors\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	ds = sc->desc;
+	da = sc->desc_daddr;
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: DMA map: %p (%zu) -> %llx\n",
+		__func__, ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
+
+	bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
+			sizeof(struct ath_buf), GFP_KERNEL);
+	if (bf == NULL) {
+		dev_err(&pdev->dev, "can't allocate bufptr\n");
+		ret = -ENOMEM;
+		goto err_free;
+	}
+	sc->bufptr = bf;
+
+	INIT_LIST_HEAD(&sc->rxbuf);
+	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+		bf->desc = ds;
+		bf->daddr = da;
+		list_add_tail(&bf->list, &sc->rxbuf);
+	}
+
+	INIT_LIST_HEAD(&sc->txbuf);
+	sc->txbuf_len = ATH_TXBUF;
+	for (i = 0; i < ATH_TXBUF; i++, bf++, ds += ATH_TXDESC,
+			da += ATH_TXDESC * sizeof(*ds)) {
+		bf->desc = ds;
+		bf->daddr = da;
+		list_add_tail(&bf->list, &sc->txbuf);
+	}
+
+	/* beacon buffer */
+	bf->desc = ds;
+	bf->daddr = da;
+	sc->bbuf = bf;
+
+	return 0;
+err_free:
+	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
+err:
+	sc->desc = NULL;
+	return ret;
+}
+
+static void ath_desc_free(struct ath_softc *sc, struct pci_dev *pdev)
+{
+	struct ath_buf *bf;
+
+	ath_cleanup_txbuf(sc, sc->bbuf);
+	list_for_each_entry(bf, &sc->txbuf, list)
+		ath_cleanup_txbuf(sc, bf);
+	list_for_each_entry(bf, &sc->rxbuf, list)
+		ath_cleanup_txbuf(sc, bf);
+
+	/* Free memory associated with all descriptors */
+	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
+
+	kfree(sc->bufptr);
+	sc->bufptr = NULL;
+}
+
+static int ath_beaconq_setup(struct ath_hw *ah)
+{
+	struct ath5k_txq_info qi = {
+		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_max = AR5K_TXQ_USEDEFAULT,
+		/* NB: for dynamic turbo, don't enable any other interrupts */
+		.tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
+	};
+
+	return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
+}
+
+static struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype,
+		int subtype)
+{
+	struct ath_hw *ah = sc->ah;
+	struct ath_txq *txq;
+	struct ath5k_txq_info qi = {
+		.tqi_subtype = subtype,
+		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_max = AR5K_TXQ_USEDEFAULT
+	};
+	int qnum;
+
+	/*
+	 * Enable interrupts only for EOL and DESC conditions.
+	 * We mark tx descriptors to receive a DESC interrupt
+	 * when a tx queue gets deep; otherwise waiting for the
+	 * EOL to reap descriptors.  Note that this is done to
+	 * reduce interrupt load and this only defers reaping
+	 * descriptors, never transmitting frames.  Aside from
+	 * reducing interrupts this also permits more concurrency.
+	 * The only potential downside is if the tx queue backs
+	 * up in which case the top half of the kernel may backup
+	 * due to a lack of tx descriptors.
+	 */
+	qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
+				AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
+	qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
+	if (qnum < 0) {
+		/*
+		 * NB: don't print a message, this happens
+		 * normally on parts with too few tx queues
+		 */
+		return ERR_PTR(qnum);
+	}
+	if (qnum >= ARRAY_SIZE(sc->txqs)) {
+		printk(KERN_ERR "hal qnum %u out of range, max %tu!\n",
+			qnum, ARRAY_SIZE(sc->txqs));
+		ath5k_hw_release_tx_queue(ah, qnum);
+		return ERR_PTR(-EINVAL);
+	}
+	txq = &sc->txqs[qnum];
+	if (!txq->setup) {
+		txq->qnum = qnum;
+		txq->link = NULL;
+		INIT_LIST_HEAD(&txq->q);
+		spin_lock_init(&txq->lock);
+		txq->setup = true;
+	}
+	return &sc->txqs[qnum];
+}
+
+static void ath_tx_cleanup(struct ath_softc *sc)
+{
+	struct ath_txq *txq = sc->txqs;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
+		if (txq->setup) {
+			ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
+			txq->setup = false;
+		}
+}
+
+static int ath_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_hw *ah = sc->ah;
+	u8 mac[ETH_ALEN];
+	unsigned int i;
+	int ret;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, pdev->device);
+
+	/*
+	 * Check if the MAC has multi-rate retry support.
+	 * We do this by trying to setup a fake extended
+	 * descriptor.  MAC's that don't have support will
+	 * return false w/o doing anything.  MAC's that do
+	 * support it will return true w/o doing anything.
+	 */
+	if (ah->ah_setup_xtx_desc(ah, NULL, 0, 0, 0, 0, 0, 0))
+		__set_bit(ATH_STAT_MRRETRY, sc->status);
+
+	/*
+	 * Reset the key cache since some parts do not
+	 * reset the contents on initial power up.
+	 */
+	for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
+		ath5k_hw_reset_key(ah, i);
+
+	/*
+	 * Collect the channel list using the default country
+	 * code and including outdoor channels.  The 802.11 layer
+	 * is resposible for filtering this list based on settings
+	 * like the phy mode.
+	 */
+	ret = ath_getchannels(hw);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get channels\n");
+		goto err;
+	}
+
+	/* NB: setup here so ath_rate_update is happy */
+	if (test_bit(MODE_IEEE80211A, ah->ah_modes))
+		ath_setcurmode(sc, MODE_IEEE80211A);
+	else
+		ath_setcurmode(sc, MODE_IEEE80211B);
+
+	/*
+	 * Allocate tx+rx descriptors and populate the lists.
+	 */
+	ret = ath_desc_alloc(sc, pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "can't allocate descriptors\n");
+		goto err;
+	}
+
+	/*
+	 * Allocate hardware transmit queues: one queue for
+	 * beacon frames and one data queue for each QoS
+	 * priority.  Note that the hal handles reseting
+	 * these queues at the needed time.
+	 */
+	ret = ath_beaconq_setup(ah);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't setup a beacon xmit queue\n");
+		goto err_desc;
+	}
+	sc->bhalq = ret;
+
+	sc->txq = ath_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
+	if (IS_ERR(sc->txq)) {
+		dev_err(&pdev->dev, "can't setup xmit queue\n");
+		ret = PTR_ERR(sc->txq);
+		goto err_bhal;
+	}
+
+	tasklet_init(&sc->rxtq, ath_tasklet_rx, (unsigned long)sc);
+	tasklet_init(&sc->txtq, ath_tasklet_tx, (unsigned long)sc);
+	tasklet_init(&sc->restq, ath_tasklet_reset, (unsigned long)sc);
+	setup_timer(&sc->calib_tim, ath_calibrate, (unsigned long)sc);
+	setup_timer(&sc->led_tim, ath_led_off, (unsigned long)sc);
+
+	sc->led_on = 0; /* low true */
+	/*
+	 * Auto-enable soft led processing for IBM cards and for
+	 * 5211 minipci cards.  Users can also manually enable/disable
+	 * support with a sysctl.
+	 */
+	if (pdev->device == PCI_DEVICE_ID_ATHEROS_AR5212_IBM ||
+			pdev->device == PCI_DEVICE_ID_ATHEROS_AR5211) {
+		__set_bit(ATH_STAT_LEDSOFT, sc->status);
+		sc->led_pin = 0;
+	}
+	/* Enable softled on PIN1 on HP Compaq nc6xx, nc4000 & nx5000 laptops */
+	if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ) {
+		__set_bit(ATH_STAT_LEDSOFT, sc->status);
+		sc->led_pin = 0;
+	}
+	if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
+		ath5k_hw_set_gpio_output(ah, sc->led_pin);
+		ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on);
+	}
+
+	ath5k_hw_get_lladdr(ah, mac);
+	SET_IEEE80211_PERM_ADDR(hw, mac);
+	if (ath5k_hw_hasbssidmask(ah)) {
+		memset(sc->bssidmask, 0xff, ETH_ALEN);
+		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
+	}
+
+	ret = ieee80211_register_hw(hw);
+	if (ret) {
+		dev_err(&pdev->dev, "can't register ieee80211 hw\n");
+		goto err_queues;
+	}
+
+	return 0;
+err_queues:
+	ath_tx_cleanup(sc);
+err_bhal:
+	ath5k_hw_release_tx_queue(ah, sc->bhalq);
+err_desc:
+	ath_desc_free(sc, pdev);
+err:
+	return ret;
+}
+
+static void ath_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+
+	/*
+	 * NB: the order of these is important:
+	 * o call the 802.11 layer before detaching the hal to
+	 *   insure callbacks into the driver to delete global
+	 *   key cache entries can be handled
+	 * o reclaim the tx queue data structures after calling
+	 *   the 802.11 layer as we'll get called back to reclaim
+	 *   node state and potentially want to use them
+	 * o to cleanup the tx queues the hal is called, so detach
+	 *   it last
+	 * Other than that, it's straightforward...
+	 */
+	ieee80211_unregister_hw(hw);
+	ath_desc_free(sc, pdev);
+	ath_tx_cleanup(sc);
+	ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
+
+	/*
+	 * NB: can't reclaim these until after ieee80211_ifdetach
+	 * returns because we'll get called back to reclaim node
+	 * state and potentially want to use them.
+	 */
+}
+
+static const char *ath_chip_name(u8 mac_version)
+{
+	switch (mac_version) {
+	case AR5K_AR5210:
+		return "AR5210";
+	case AR5K_AR5211:
+		return "AR5211";
+	case AR5K_AR5212:
+		return "AR5212";
+	}
+	return "Unknown";
+}
+
+static int __devinit ath_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *id)
+{
+	void __iomem *mem;
+	struct ath_softc *sc;
+	struct ieee80211_hw *hw;
+	int ret;
+	u8 csz;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "can't enable device\n");
+		goto err;
+	}
+
+	/* XXX 32-bit addressing only */
+	ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+	if (ret) {
+		dev_err(&pdev->dev, "32-bit DMA not available\n");
+		goto err_dis;
+	}
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
+	if (csz == 0) {
+		/*
+		 * Linux 2.4.18 (at least) writes the cache line size
+		 * register as a 16-bit wide register which is wrong.
+		 * We must have this setup properly for rx buffer
+		 * DMA to work so force a reasonable value here if it
+		 * comes up zero.
+		 */
+		csz = L1_CACHE_BYTES / sizeof(u32);
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
+	}
+	/*
+	 * The default setting of latency timer yields poor results,
+	 * set it to the value used by other systems.  It may be worth
+	 * tweaking this setting more.
+	 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+
+	pci_set_master(pdev);
+
+	/*
+	 * Disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state.
+	 */
+	pci_write_config_byte(pdev, 0x41, 0);
+
+	ret = pci_request_region(pdev, 0, "ath");
+	if (ret) {
+		dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+		goto err_dis;
+	}
+
+	mem = pci_iomap(pdev, 0, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
+		ret = -EIO;
+		goto err_reg;
+	}
+
+	hw = ieee80211_alloc_hw(sizeof(*sc), &ath_hw_ops);
+	if (hw == NULL) {
+		dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
+		ret = -ENOMEM;
+		goto err_map;
+	}
+
+	SET_IEEE80211_DEV(hw, &pdev->dev);
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS;
+	hw->extra_tx_headroom = 2;
+	hw->channel_change_time = 5000;
+	hw->max_rssi = 127; /* FIXME: get a real value for this. */
+	sc = hw->priv;
+	sc->hw = hw;
+	sc->pdev = pdev;
+
+	/*
+	 * Mark the device as detached to avoid processing
+	 * interrupts until setup is complete.
+	 */
+#if AR_DEBUG
+	sc->debug = ath_debug;
+#endif
+	__set_bit(ATH_STAT_INVALID, sc->status);
+	sc->iobase = mem;
+	sc->cachelsz = csz * sizeof(u32); /* convert to bytes */
+	sc->opmode = IEEE80211_IF_TYPE_STA;
+	mutex_init(&sc->lock);
+	spin_lock_init(&sc->rxbuflock);
+	spin_lock_init(&sc->txbuflock);
+
+	pci_set_drvdata(pdev, hw);
+
+	pci_enable_msi(pdev);
+
+	ret = request_irq(pdev->irq, ath_intr, IRQF_SHARED, "ath", sc);
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq failed\n");
+		goto err_free;
+	}
+
+	sc->ah = ath5k_hw_attach(pdev->device, id->driver_data, sc, sc->iobase);
+	if (IS_ERR(sc->ah)) {
+		ret = PTR_ERR(sc->ah);
+		goto err_irq;
+	}
+
+	ret = ath_attach(pdev, hw);
+	if (ret)
+		goto err_ah;
+
+	dev_info(&pdev->dev, "%s chip found: mac %d.%d phy %d.%d\n",
+			ath_chip_name(id->driver_data), sc->ah->ah_mac_version,
+			sc->ah->ah_mac_version, sc->ah->ah_phy_revision >> 4,
+			sc->ah->ah_phy_revision & 0xf);
+
+	/* ready to process interrupts */
+	__clear_bit(ATH_STAT_INVALID, sc->status);
+
+	return 0;
+err_ah:
+	ath5k_hw_detach(sc->ah);
+err_irq:
+	free_irq(pdev->irq, sc);
+err_free:
+	pci_disable_msi(pdev);
+	ieee80211_free_hw(hw);
+err_map:
+	pci_iounmap(pdev, mem);
+err_reg:
+	pci_release_region(pdev, 0);
+err_dis:
+	pci_disable_device(pdev);
+err:
+	return ret;
+}
+
+static void __devexit ath_pci_remove(struct pci_dev *pdev)
+{
+	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+	struct ath_softc *sc = hw->priv;
+
+	ath_detach(pdev, hw);
+	ath5k_hw_detach(sc->ah);
+	free_irq(pdev->irq, sc);
+	pci_disable_msi(pdev);
+	pci_iounmap(pdev, sc->iobase);
+	pci_release_region(pdev, 0);
+	pci_disable_device(pdev);
+	ieee80211_free_hw(hw);
+}
+
+#ifdef CONFIG_PM
+static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+	struct ath_softc *sc = hw->priv;
+
+	if (test_bit(ATH_STAT_LEDSOFT, sc->status))
+		ath5k_hw_set_gpio(sc->ah, sc->led_pin, 1);
+
+	ath_stop_hw(sc);
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, PCI_D3hot);
+
+	return 0;
+}
+
+static int ath_pci_resume(struct pci_dev *pdev)
+{
+	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+	struct ath_softc *sc = hw->priv;
+	int err;
+
+	err = pci_set_power_state(pdev, PCI_D0);
+	if (err)
+		return err;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	pci_restore_state(pdev);
+	/*
+	 * Suspend/Resume resets the PCI configuration space, so we have to
+	 * re-disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state
+	 */
+	pci_write_config_byte(pdev, 0x41, 0);
+
+	ath_init(sc);
+	if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
+		ath5k_hw_set_gpio_output(sc->ah, sc->led_pin);
+		ath5k_hw_set_gpio(sc->ah, sc->led_pin, 0);
+	}
+
+	return 0;
+}
+#else
+#define ath_pci_suspend NULL
+#define ath_pci_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_driver ath_pci_drv_id = {
+	.name		= "ath_pci",
+	.id_table	= ath_pci_id_table,
+	.probe		= ath_pci_probe,
+	.remove		= __devexit_p(ath_pci_remove),
+	.suspend	= ath_pci_suspend,
+	.resume		= ath_pci_resume,
+};
+
+static int mincalibrate = 1;
+static int maxcalibrate = INT_MAX / 1000;
+#define	CTL_AUTO	-2	/* cannot be CTL_ANY or CTL_NONE */
+
+static ctl_table ath_static_sysctls[] = {
+#if AR_DEBUG
+	{
+	  .procname	= "debug",
+	  .mode		= 0644,
+	  .data		= &ath_debug,
+	  .maxlen	= sizeof(ath_debug),
+	  .proc_handler	= proc_dointvec
+	},
+#endif
+	{
+	  .procname	= "countrycode",
+	  .mode		= 0444,
+	  .data		= &countrycode,
+	  .maxlen	= sizeof(countrycode),
+	  .proc_handler	= proc_dointvec
+	},
+	{
+	  .procname	= "outdoor",
+	  .mode		= 0444,
+	  .data		= &outdoor,
+	  .maxlen	= sizeof(outdoor),
+	  .proc_handler	= proc_dointvec
+	},
+	{
+	  .procname	= "xchanmode",
+	  .mode		= 0444,
+	  .data		= &xchanmode,
+	  .maxlen	= sizeof(xchanmode),
+	  .proc_handler	= proc_dointvec
+	},
+	{
+	  .procname	= "calibrate",
+	  .mode		= 0644,
+	  .data		= &ath_calinterval,
+	  .maxlen	= sizeof(ath_calinterval),
+	  .extra1	= &mincalibrate,
+	  .extra2	= &maxcalibrate,
+	  .proc_handler	= proc_dointvec_minmax
+	},
+	{ 0 }
+};
+static ctl_table ath_ath_table[] = {
+	{
+	  .procname	= "ath",
+	  .mode		= 0555,
+	  .child	= ath_static_sysctls
+	}, { 0 }
+};
+static ctl_table ath_root_table[] = {
+	{
+	  .ctl_name	= CTL_DEV,
+	  .procname	= "dev",
+	  .mode		= 0555,
+	  .child	= ath_ath_table
+	}, { 0 }
+};
+static struct ctl_table_header *ath_sysctl_header;
+
+static int __init init_ath_pci(void)
+{
+	int ret;
+
+	ret = pci_register_driver(&ath_pci_drv_id);
+	if (ret) {
+		printk(KERN_ERR "ath_pci: can't register pci driver\n");
+		return ret;
+	}
+	ath_sysctl_header = register_sysctl_table(ath_root_table);
+
+	return 0;
+}
+
+static void __exit exit_ath_pci(void)
+{
+	if (ath_sysctl_header)
+		unregister_sysctl_table(ath_sysctl_header);
+	pci_unregister_driver(&ath_pci_drv_id);
+}
+
+module_init(init_ath_pci);
+module_exit(exit_ath_pci);
+
+MODULE_AUTHOR("Jiri Slaby");
+MODULE_DESCRIPTION("Support for Atheros 802.11 wireless LAN cards.");
+MODULE_SUPPORTED_DEVICE("Atheros WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(ATH_PCI_VERSION " (EXPERIMENTAL)");
diff -puN /dev/null drivers/net/wireless/ath5k/base.h
--- /dev/null
+++ a/drivers/net/wireless/ath5k/base.h
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHVAR_H
+#define _DEV_ATH_ATHVAR_H
+
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/wireless.h>
+#include <linux/if_ether.h>
+
+#include "ath5k.h"
+
+#define	ATH_TIMEOUT		1000
+
+#define ATH_LONG_CALIB		30 /* seconds */
+#define ATH_SHORT_CALIB		1
+
+/*
+ * Maximum acceptable MTU
+ * MAXFRAMEBODY - WEP - QOS - RSN/WPA:
+ * 2312 - 8 - 2 - 12 = 2290
+ */
+#define ATH_MAX_MTU	2290
+#define ATH_MIN_MTU	32
+
+#define	ATH_RXBUF	40		/* number of RX buffers */
+#define	ATH_TXBUF	200		/* number of TX buffers */
+#define	ATH_TXDESC	1		/* number of descriptors per buffer */
+#define ATH_BCBUF	1		/* number of beacon buffers */
+#define	ATH_TXMAXTRY	11		/* max number of transmit attempts */
+#define	ATH_TXINTR_PERIOD 5		/* max number of batched tx descriptors */
+
+#define ATH_BEACON_AIFS_DEFAULT  0	/* default aifs for ap beacon q */
+#define ATH_BEACON_CWMIN_DEFAULT 0	/* default cwmin for ap beacon q */
+#define ATH_BEACON_CWMAX_DEFAULT 0	/* default cwmax for ap beacon q */
+
+#define ATH_RSSI_LPF_LEN	10
+#define ATH_RSSI_DUMMY_MARKER	0x127
+#define ATH_EP_MUL(x, mul)	((x) * (mul))
+#define ATH_RSSI_IN(x)		(ATH_EP_MUL((x), AR5K_RSSI_EP_MULTIPLIER))
+#define ATH_LPF_RSSI(x, y, len) \
+    ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define ATH_RSSI_LPF(x, y) do {						\
+	if ((y) >= -20)							\
+		x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \
+} while (0)
+
+struct ath_buf {
+	struct list_head	list;
+	unsigned int		flags;	/* tx descriptor flags */
+	struct ath_desc		*desc;	/* virtual addr of desc */
+	dma_addr_t		daddr;	/* physical addr of desc */
+	struct sk_buff		*skb;	/* skbuff for buf */
+	dma_addr_t		skbaddr;/* physical addr of skb data */
+	struct ieee80211_tx_control ctl;
+};
+
+/*
+ * Data transmit queue state.  One of these exists for each
+ * hardware transmit queue.  Packets sent to us from above
+ * are assigned to queues based on their priority.  Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath_txq {
+	unsigned int	qnum;		/* hardware q number */
+	u32		*link;		/* link ptr in last TX desc */
+	struct list_head q;		/* transmit queue */
+	spinlock_t	lock;		/* lock on q and link */
+	bool		setup;
+};
+
+#if CHAN_DEBUG
+#define ATH_CHAN_MAX	(26+26+26+200+200)
+#else
+#define ATH_CHAN_MAX	(14+14+14+252+20)	/* XXX what's the max? */
+#endif
+
+
+/* Software Carrier, keeps track of the driver state
+ * associated with an instance of a device */
+struct ath_softc {
+	struct pci_dev		*pdev;		/* for dma mapping */
+	void __iomem		*iobase;	/* address of the device */
+	struct mutex		lock;		/* dev-level lock */
+	struct ieee80211_tx_queue_stats tx_stats;
+	struct ieee80211_low_level_stats ll_stats;
+	struct ieee80211_hw	*hw;		/* IEEE 802.11 common */
+	struct ieee80211_hw_mode modes[NUM_IEEE80211_MODES];
+	struct ieee80211_channel channels[ATH_CHAN_MAX];
+	struct ieee80211_rate	rates[AR5K_MAX_RATES * NUM_IEEE80211_MODES];
+	enum ieee80211_if_types	opmode;
+	struct ath_hw		*ah;		/* Atheros HW */
+
+	int			debug;
+
+	struct ath_buf		*bufptr;	/* allocated buffer ptr */
+	struct ath_desc		*desc;		/* TX/RX descriptors */
+	dma_addr_t		desc_daddr;	/* DMA (physical) address */
+	size_t			desc_len;	/* size of TX/RX descriptors */
+	u16			cachelsz;	/* cache line size */
+
+	DECLARE_BITMAP(status, 6);
+#define ATH_STAT_INVALID	0		/* disable hardware accesses */
+#define ATH_STAT_MRRETRY	1		/* multi-rate retry support */
+#define ATH_STAT_PROMISC	2
+#define ATH_STAT_LEDBLINKING	3		/* LED blink operation active */
+#define ATH_STAT_LEDENDBLINK	4		/* finish LED blink operation */
+#define ATH_STAT_LEDSOFT	5		/* enable LED gpio status */
+
+	unsigned int		filter_flags;	/* HW flags, AR5K_RX_FILTER_* */
+	unsigned int		curmode;	/* current phy mode */
+	struct ieee80211_channel *curchan;	/* current h/w channel */
+
+	int 			iface_id;	/* add/remove_interface id */
+
+	struct {
+		u8	rxflags;	/* radiotap rx flags */
+		u8	txflags;	/* radiotap tx flags */
+		u16	ledon;		/* softled on time */
+		u16	ledoff;		/* softled off time */
+	} hwmap[32];				/* h/w rate ix mappings */
+
+	enum ath5k_int		imask;		/* interrupt mask copy */
+
+	DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */
+
+	u8			bssidmask[ETH_ALEN];
+
+	unsigned int		led_pin,	/* GPIO pin for driving LED */
+				led_on,		/* pin setting for LED on */
+				led_off;	/* off time for current blink */
+	struct timer_list	led_tim;	/* led off timer */
+	u8			led_rxrate;	/* current rx rate for LED */
+	u8			led_txrate;	/* current tx rate for LED */
+
+	struct tasklet_struct	restq;		/* reset tasklet */
+
+	unsigned int		rxbufsize;	/* rx size based on mtu */
+	struct list_head	rxbuf;		/* receive buffer */
+	spinlock_t		rxbuflock;
+	u32			*rxlink;	/* link ptr in last RX desc */
+	struct tasklet_struct	rxtq;		/* rx intr tasklet */
+
+	struct list_head	txbuf;		/* transmit buffer */
+	spinlock_t		txbuflock;
+	unsigned int		txbuf_len;	/* buf count in txbuf list */
+	struct ath_txq		txqs[2];	/* beacon and tx */
+
+	struct ath_txq		*txq;		/* beacon and tx*/
+	struct tasklet_struct	txtq;		/* tx intr tasklet */
+
+	struct ath_buf		*bbuf;		/* beacon buffer */
+	unsigned int		bhalq,		/* HAL q for outgoing beacons */
+				bmisscount,	/* missed beacon transmits */
+				bintval,	/* beacon interval */
+				bsent;
+
+	struct timer_list	calib_tim;	/* calibration timer */
+};
+
+#define ath5k_hw_hasbssidmask(_ah) \
+	(ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0)
+#define ath5k_hw_hasveol(_ah) \
+	(ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0)
+
+#endif
diff -puN /dev/null drivers/net/wireless/ath5k/hw.c
--- /dev/null
+++ a/drivers/net/wireless/ath5k/hw.c
@@ -0,0 +1,4322 @@
+ /*
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007 Matthew W. S. Bell  <mentor@madwifi.org>
+ * Copyright (c) 2007 Luis Rodriguez <mcgrof@winlab.rutgers.edu>
+ * Copyright (c) 2007 Pavel Roskin <proski@gnu.org>
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * HAL interface for Atheros Wireless LAN devices.
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "ath5k.h"
+#include "reg.h"
+
+/*Rate tables*/
+static const struct ath5k_rate_table ath5k_rt_11a = AR5K_RATES_11A;
+static const struct ath5k_rate_table ath5k_rt_11b = AR5K_RATES_11B;
+static const struct ath5k_rate_table ath5k_rt_11g = AR5K_RATES_11G;
+static const struct ath5k_rate_table ath5k_rt_turbo = AR5K_RATES_TURBO;
+static const struct ath5k_rate_table ath5k_rt_xr = AR5K_RATES_XR;
+
+/*Prototypes*/
+static int ath5k_hw_nic_reset(struct ath_hw *, u32);
+static int ath5k_hw_nic_wakeup(struct ath_hw *, int, bool);
+static int ath5k_hw_setup_4word_tx_desc(struct ath_hw *, struct ath_desc *,
+	unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
+	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
+	unsigned int, unsigned int);
+static bool ath5k_hw_setup_xr_tx_desc(struct ath_hw *, struct ath_desc *,
+	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
+	unsigned int);
+static int ath5k_hw_fill_4word_tx_desc(struct ath_hw *, struct ath_desc *,
+	unsigned int, bool, bool);
+static int ath5k_hw_proc_4word_tx_status(struct ath_hw *, struct ath_desc *);
+static int ath5k_hw_setup_2word_tx_desc(struct ath_hw *, struct ath_desc *,
+	unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
+	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
+	unsigned int, unsigned int);
+static int ath5k_hw_fill_2word_tx_desc(struct ath_hw *, struct ath_desc *,
+	unsigned int, bool, bool);
+static int ath5k_hw_proc_2word_tx_status(struct ath_hw *, struct ath_desc *);
+static int ath5k_hw_proc_new_rx_status(struct ath_hw *, struct ath_desc *);
+static int ath5k_hw_proc_old_rx_status(struct ath_hw *, struct ath_desc *);
+static int ath5k_hw_get_capabilities(struct ath_hw *);
+
+static int ath5k_eeprom_init(struct ath_hw *);
+static int ath5k_eeprom_read_mac(struct ath_hw *, u8 *);
+
+static int ath5k_hw_enable_pspoll(struct ath_hw *, u8 *, u16);
+static int ath5k_hw_disable_pspoll(struct ath_hw *);
+
+/*
+ * Enable to overwrite the country code (use "00" for debug)
+ */
+#if 0
+#define COUNTRYCODE "00"
+#endif
+
+/*******************\
+  General Functions
+\*******************/
+
+
+/*
+ * Calculate transmition time of a frame
+ * TODO: Left here for combatibility, change it in ath5k
+ */
+static u16 /*TODO: Is this really hardware dependent ?*/
+ath_hal_computetxtime(struct ath_hw *hal, const struct ath5k_rate_table *rates,
+		u32 frame_length, u16 rate_index, bool short_preamble)
+{
+	const struct ath5k_rate *rate;
+	u32 value;
+
+	AR5K_ASSERT_ENTRY(rate_index, rates->rate_count);
+
+	/*
+	 * Get rate by index
+	 */
+	rate = &rates->rates[rate_index];
+
+	/*
+	 * Calculate the transmission time by operation (PHY) mode
+	 */
+	switch (rate->modulation) {
+	/* Standard rates */
+	case IEEE80211_RATE_CCK:
+		/*
+		 * CCK / DS mode (802.11b)
+		 */
+		value = AR5K_CCK_TX_TIME(rate->rate_kbps, frame_length,
+			short_preamble &&
+			rate->modulation == IEEE80211_RATE_CCK_2);
+		break;
+
+	case IEEE80211_RATE_OFDM:
+		/*
+		 * Orthogonal Frequency Division Multiplexing
+		 */
+		if (AR5K_OFDM_NUM_BITS_PER_SYM(rate->rate_kbps) == 0)
+			return 0;
+		value = AR5K_OFDM_TX_TIME(rate->rate_kbps, frame_length);
+		break;
+
+	/* Vendor-specific rates */
+	case MODULATION_TURBO:
+		/*
+		 * Orthogonal Frequency Division Multiplexing
+		 * Atheros "Turbo Mode" (doubled rates)
+		 */
+		if (AR5K_TURBO_NUM_BITS_PER_SYM(rate->rate_kbps) == 0)
+			return 0;
+		value = AR5K_TURBO_TX_TIME(rate->rate_kbps, frame_length);
+		break;
+
+	case MODULATION_XR:
+		/*
+		 * Orthogonal Frequency Division Multiplexing
+		 * Atheros "eXtended Range" (XR)
+		 */
+		if (AR5K_XR_NUM_BITS_PER_SYM(rate->rate_kbps) == 0)
+			return 0;
+		value = AR5K_XR_TX_TIME(rate->rate_kbps, frame_length);
+		break;
+
+	default:
+		return 0;
+	}
+
+	return value;
+}
+
+/*
+ * Functions used internaly
+ */
+
+static inline unsigned int ath5k_hw_htoclock(unsigned int usec, bool turbo)
+{
+	return turbo == true ? (usec * 80) : (usec * 40);
+}
+
+static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo)
+{
+	return turbo == true ? (clock / 80) : (clock / 40);
+}
+
+/*
+ * Check if a register write has been completed
+ */
+int ath5k_hw_register_timeout(struct ath_hw *hal, u32 reg, u32 flag, u32 val,
+		bool is_set)
+{
+	int i;
+	u32 data;
+
+	for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) {
+		data = ath5k_hw_reg_read(hal, reg);
+		if ((is_set == true) && (data & flag))
+			break;
+		else if ((data & flag) == val)
+			break;
+		udelay(15);
+	}
+
+	return (i <= 0) ? -EAGAIN : 0;
+}
+
+
+/***************************************\
+	Attach/Detach Functions
+\***************************************/
+
+/*
+ * Check if the device is supported and initialize the needed structs
+ */
+struct ath_hw *ath5k_hw_attach(u16 device, u8 mac_version, void *sc,
+		void __iomem *sh)
+{
+	struct ath_hw *hal;
+	u8 mac[ETH_ALEN];
+	int ret;
+	u32 srev;
+
+	/*If we passed the test malloc a hal struct*/
+	hal = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
+	if (hal == NULL) {
+		ret = -ENOMEM;
+		AR5K_PRINT("out of memory\n");
+		goto err;
+	}
+
+	hal->ah_sc = sc;
+	hal->ah_sh = sh;
+
+	/*
+	 * HAL information
+	 */
+
+	/* Regulation Stuff */
+	hal->ah_country_code = AR5K_TUNE_CTRY;
+	ath5k_get_regdomain(hal);
+
+	hal->ah_op_mode = IEEE80211_IF_TYPE_STA;
+	hal->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT;
+	hal->ah_turbo = false;
+	hal->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
+	hal->ah_imr = 0;
+	hal->ah_atim_window = 0;
+	hal->ah_aifs = AR5K_TUNE_AIFS;
+	hal->ah_cw_min = AR5K_TUNE_CWMIN;
+	hal->ah_limit_tx_retries = AR5K_INIT_TX_RETRY;
+	hal->ah_software_retry = false;
+	hal->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY;
+
+	switch (device) {
+	case PCI_DEVICE_ID_ATHEROS_AR2413:
+	case PCI_DEVICE_ID_ATHEROS_AR5413:
+	case PCI_DEVICE_ID_ATHEROS_AR5424:
+		/*
+		 * Known single chip solutions
+		 */
+		hal->ah_single_chip = true;
+		break;
+	default:
+		/*
+		 * Multi chip solutions
+		 */
+		hal->ah_single_chip = false;
+		break;
+	}
+
+	/*
+	 * Set the mac revision based on the pci id
+	 */
+	hal->ah_version = mac_version;
+
+	/*Fill the hal struct with the needed functions*/
+	if (hal->ah_version == AR5K_AR5212)
+		hal->ah_magic = AR5K_EEPROM_MAGIC_5212;
+	else if (hal->ah_version == AR5K_AR5211)
+		hal->ah_magic = AR5K_EEPROM_MAGIC_5211;
+
+	if (hal->ah_version == AR5K_AR5212) {
+		hal->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc;
+		hal->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc;
+		hal->ah_fill_tx_desc = ath5k_hw_fill_4word_tx_desc;
+		hal->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status;
+	} else {
+		hal->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
+		hal->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc;
+		hal->ah_fill_tx_desc = ath5k_hw_fill_2word_tx_desc;
+		hal->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
+	}
+
+	if (hal->ah_version == AR5K_AR5212)
+		hal->ah_proc_rx_desc = ath5k_hw_proc_new_rx_status;
+	else if (hal->ah_version <= AR5K_AR5211)
+		hal->ah_proc_rx_desc = ath5k_hw_proc_old_rx_status;
+
+	/* Bring device out of sleep and reset it's units */
+	ret = ath5k_hw_nic_wakeup(hal, AR5K_INIT_MODE, true);
+	if (ret)
+		goto err_free;
+
+	/* Get MAC, PHY and RADIO revisions */
+	srev = ath5k_hw_reg_read(hal, AR5K_SREV);
+	hal->ah_mac_srev = srev;
+	hal->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
+	hal->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV);
+	hal->ah_phy_revision = ath5k_hw_reg_read(hal, AR5K_PHY_CHIP_ID) &
+			0xffffffff;
+	hal->ah_radio_5ghz_revision = ath5k_hw_radio_revision(hal,
+			CHANNEL_5GHZ);
+
+	if (hal->ah_version == AR5K_AR5210)
+		hal->ah_radio_2ghz_revision = 0;
+	else
+		hal->ah_radio_2ghz_revision = ath5k_hw_radio_revision(hal,
+				CHANNEL_2GHZ);
+
+	/* Single chip radio */
+	if (hal->ah_radio_2ghz_revision == hal->ah_radio_5ghz_revision)
+		hal->ah_radio_2ghz_revision = 0;
+
+	/* Identify the radio chip*/
+	if (hal->ah_version == AR5K_AR5210)
+		hal->ah_radio = AR5K_RF5110;
+	else
+		hal->ah_radio = hal->ah_radio_5ghz_revision <
+			AR5K_SREV_RAD_5112 ? AR5K_RF5111 : AR5K_RF5112;
+
+	hal->ah_phy = AR5K_PHY(0);
+
+	/* Set MAC to bcast: ff:ff:ff:ff:ff:ff, this is using 'mac' as a
+	 * temporary variable for setting our BSSID. Right bellow we update
+	 * it with ath5k_hw_get_lladdr() */
+	memset(mac, 0xff, ETH_ALEN);
+	ath5k_hw_set_associd(hal, mac, 0);
+
+	ath5k_hw_get_lladdr(hal, mac);
+	ath5k_hw_set_opmode(hal);
+
+#ifdef AR5K_DEBUG
+	ath5k_hw_dump_state(hal);
+#endif
+
+	/*
+	 * Get card capabilities, values, ...
+	 */
+
+	ret = ath5k_eeprom_init(hal);
+	if (ret) {
+		AR5K_PRINT("unable to init EEPROM\n");
+		goto err_free;
+	}
+
+	/* Get misc capabilities */
+	ret = ath5k_hw_get_capabilities(hal);
+	if (ret) {
+		AR5K_PRINTF("unable to get device capabilities: 0x%04x\n",
+			device);
+		goto err_free;
+	}
+
+	/* Get MAC address */
+	ret = ath5k_eeprom_read_mac(hal, mac);
+	if (ret) {
+		AR5K_PRINTF("unable to read address from EEPROM: 0x%04x\n",
+			device);
+		goto err_free;
+	}
+
+	ath5k_hw_set_lladdr(hal, mac);
+
+	ath5k_hw_set_rfgain_opt(hal);
+
+	return hal;
+err_free:
+	kfree(hal);
+err:
+	return ERR_PTR(ret);
+}
+
+/*
+ * Bring up MAC + PHY Chips
+ */
+static int ath5k_hw_nic_wakeup(struct ath_hw *hal, int flags, bool initial)
+{
+	u32 turbo, mode, clock;
+	int ret;
+
+	turbo = 0;
+	mode = 0;
+	clock = 0;
+
+	AR5K_TRACE;
+
+	if (hal->ah_version != AR5K_AR5210) {
+		/*
+		 * Get channel mode flags
+		 */
+
+		if (hal->ah_radio >= AR5K_RF5112) {
+			mode = AR5K_PHY_MODE_RAD_RF5112;
+			clock = AR5K_PHY_PLL_RF5112;
+		} else {
+			mode = AR5K_PHY_MODE_RAD_RF5111;	/*Zero*/
+			clock = AR5K_PHY_PLL_RF5111;		/*Zero*/
+		}
+
+		if (flags & CHANNEL_2GHZ) {
+			mode |= AR5K_PHY_MODE_FREQ_2GHZ;
+			clock |= AR5K_PHY_PLL_44MHZ;
+
+			if (flags & CHANNEL_CCK) {
+				mode |= AR5K_PHY_MODE_MOD_CCK;
+			} else if (flags & CHANNEL_OFDM) {
+				/* XXX Dynamic OFDM/CCK is not supported by the
+				 * AR5211 so we set MOD_OFDM for plain g (no
+				 * CCK headers) operation. We need to test
+				 * this, 5211 might support ofdm-only g after
+				 * all, there are also initial register values
+				 * in the code for g mode (see ath5k_hw.h). */
+				if (hal->ah_version == AR5K_AR5211)
+					mode |= AR5K_PHY_MODE_MOD_OFDM;
+				else
+					mode |= AR5K_PHY_MODE_MOD_DYN;
+			} else {
+				AR5K_PRINT("invalid radio modulation mode\n");
+				return -EINVAL;
+			}
+		} else if (flags & CHANNEL_5GHZ) {
+			mode |= AR5K_PHY_MODE_FREQ_5GHZ;
+			clock |= AR5K_PHY_PLL_40MHZ;
+
+			if (flags & CHANNEL_OFDM)
+				mode |= AR5K_PHY_MODE_MOD_OFDM;
+			else {
+				AR5K_PRINT("invalid radio modulation mode\n");
+				return -EINVAL;
+			}
+		} else {
+			AR5K_PRINT("invalid radio frequency mode\n");
+			return -EINVAL;
+		}
+
+		if (flags & CHANNEL_TURBO)
+			turbo = AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT;
+	} else { /* Reset and wakeup the device */
+		if (initial == true) {
+			/* ...reset hardware */
+			if (ath5k_hw_nic_reset(hal, AR5K_RESET_CTL_PCI)) {
+				AR5K_PRINT("failed to reset the PCI chipset\n");
+				return -EIO;
+			}
+
+			mdelay(1);
+		}
+
+		/* ...wakeup */
+		ret = ath5k_hw_set_power(hal, AR5K_PM_AWAKE, true, 0);
+		if (ret) {
+			AR5K_PRINT("failed to resume the MAC Chip\n");
+			return ret;
+		}
+
+		/* ...enable Atheros turbo mode if requested */
+		if (flags & CHANNEL_TURBO)
+			ath5k_hw_reg_write(hal, AR5K_PHY_TURBO_MODE,
+					AR5K_PHY_TURBO);
+
+		/* ...reset chipset */
+		if (ath5k_hw_nic_reset(hal, AR5K_RESET_CTL_CHIP)) {
+			AR5K_PRINT("failed to reset the AR5210 chipset\n");
+			return -EIO;
+		}
+
+		mdelay(1);
+	}
+
+	/* ...reset chipset and PCI device */
+	if (hal->ah_single_chip == false && ath5k_hw_nic_reset(hal,
+				AR5K_RESET_CTL_CHIP | AR5K_RESET_CTL_PCI)) {
+		AR5K_PRINT("failed to reset the MAC Chip + PCI\n");
+		return -EIO;
+	}
+
+	if (hal->ah_version == AR5K_AR5210)
+		udelay(2300);
+
+	/* ...wakeup */
+	ret = ath5k_hw_set_power(hal, AR5K_PM_AWAKE, true, 0);
+	if (ret) {
+		AR5K_PRINT("failed to resume the MAC Chip\n");
+		return ret;
+	}
+
+	/* ...final warm reset */
+	if (ath5k_hw_nic_reset(hal, 0)) {
+		AR5K_PRINT("failed to warm reset the MAC Chip\n");
+		return -EIO;
+	}
+
+	if (hal->ah_version != AR5K_AR5210) {
+		/* ...set the PHY operating mode */
+		ath5k_hw_reg_write(hal, clock, AR5K_PHY_PLL);
+		udelay(300);
+
+		ath5k_hw_reg_write(hal, mode, AR5K_PHY_MODE);
+		ath5k_hw_reg_write(hal, turbo, AR5K_PHY_TURBO);
+	}
+
+	return 0;
+}
+
+/*
+ * Get the rate table for a specific operation mode
+ */
+const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath_hw *hal,
+		unsigned int mode)
+{
+	AR5K_TRACE;
+
+	if (!test_bit(mode, hal->ah_capabilities.cap_mode))
+		return NULL;
+
+	/* Get rate tables */
+	switch (mode) {
+	case MODE_IEEE80211A:
+		return &ath5k_rt_11a;
+	case MODE_ATHEROS_TURBO:
+		return &ath5k_rt_turbo;
+	case MODE_IEEE80211B:
+		return &ath5k_rt_11b;
+	case MODE_IEEE80211G:
+		return &ath5k_rt_11g;
+	case MODE_ATHEROS_TURBOG:
+		return &ath5k_rt_xr;
+	}
+
+	return NULL;
+}
+
+/*
+ * Free the hal struct
+ */
+void ath5k_hw_detach(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+
+	if (hal->ah_rf_banks != NULL)
+		kfree(hal->ah_rf_banks);
+
+	/* assume interrupts are down */
+	kfree(hal);
+}
+
+/*******************************\
+	Reset Functions
+\*******************************/
+
+/*
+ * Main reset function
+ */
+int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode,
+	struct ieee80211_channel *channel, bool change_channel)
+{
+	const struct ath5k_rate_table *rt;
+	struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom;
+	u32 data, noise_floor, s_seq, s_ant, s_led[3];
+	u8 mac[ETH_ALEN];
+	unsigned int i, mode, freq, ee_mode, ant[2];
+	int ret;
+
+	AR5K_TRACE;
+
+	s_seq = 0;
+	s_ant = 1;
+	ee_mode = 0;
+	freq = 0;
+	mode = 0;
+
+	/*
+	 * Save some registers before a reset
+	 */
+	/*DCU/Antenna selection not available on 5210*/
+	if (hal->ah_version != AR5K_AR5210) {
+		if (change_channel == true) {
+			/* Seq number for queue 0 -do this for all queues ? */
+			s_seq = ath5k_hw_reg_read(hal,
+					AR5K_QUEUE_DFS_SEQNUM(0));
+			/*Default antenna*/
+			s_ant = ath5k_hw_reg_read(hal, AR5K_DEFAULT_ANTENNA);
+		}
+	}
+
+	/*GPIOs*/
+	s_led[0] = ath5k_hw_reg_read(hal, AR5K_PCICFG) & AR5K_PCICFG_LEDSTATE;
+	s_led[1] = ath5k_hw_reg_read(hal, AR5K_GPIOCR);
+	s_led[2] = ath5k_hw_reg_read(hal, AR5K_GPIODO);
+
+	if (change_channel == true && hal->ah_rf_banks != NULL)
+		ath5k_hw_get_rf_gain(hal);
+
+
+	/*Wakeup the device*/
+	ret = ath5k_hw_nic_wakeup(hal, channel->val, false);
+	if (ret)
+		return ret;
+
+	/*
+	 * Initialize operating mode
+	 */
+	hal->ah_op_mode = op_mode;
+
+	/*
+	 * 5111/5112 Settings
+	 * 5210 only comes with RF5110
+	 */
+	if (hal->ah_version != AR5K_AR5210) {
+		if (hal->ah_radio != AR5K_RF5111 &&
+				hal->ah_radio != AR5K_RF5112) {
+			AR5K_PRINTF("invalid phy radio: %u\n", hal->ah_radio);
+			return -EINVAL;
+		}
+
+		switch (channel->val & CHANNEL_MODES) {
+		case CHANNEL_A:
+			mode = AR5K_INI_VAL_11A;
+			freq = AR5K_INI_RFGAIN_5GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11A;
+			break;
+		case CHANNEL_B:
+			mode = AR5K_INI_VAL_11B;
+			freq = AR5K_INI_RFGAIN_2GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11B;
+			break;
+		/* Is this ok on 5211 too ? */
+		case CHANNEL_G:
+			mode = AR5K_INI_VAL_11G;
+			freq = AR5K_INI_RFGAIN_2GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11G;
+			break;
+		case CHANNEL_T:
+			mode = AR5K_INI_VAL_11A_TURBO;
+			freq = AR5K_INI_RFGAIN_5GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11A;
+			break;
+		/*Is this ok on 5211 too ?*/
+		case CHANNEL_TG:
+			mode = AR5K_INI_VAL_11G_TURBO;
+			freq = AR5K_INI_RFGAIN_2GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11G;
+			break;
+		case CHANNEL_XR:
+			if (hal->ah_version == AR5K_AR5211) {
+				AR5K_PRINTF("XR mode not available on 5211");
+				return -EINVAL;
+			}
+			mode = AR5K_INI_VAL_XR;
+			freq = AR5K_INI_RFGAIN_5GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11A;
+			break;
+		default:
+			AR5K_PRINTF("invalid channel: %d\n", channel->freq);
+			return -EINVAL;
+		}
+
+		/* PHY access enable */
+		ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+
+	}
+
+	ath5k_hw_write_initvals(hal, mode, change_channel);
+
+	/*
+	 * 5211/5212 Specific
+	 */
+	if (hal->ah_version != AR5K_AR5210) {
+		/*
+		 * Write initial RF gain settings
+		 * This should work for both 5111/5112
+		 */
+		ret = ath5k_hw_rfgain(hal, freq);
+		if (ret)
+			return ret;
+
+		mdelay(1);
+
+		/*
+		 * Set rate duration table on 5212
+		 */
+		if (hal->ah_version == AR5K_AR5212) {
+
+			/*For 802.11b*/
+			if (!(channel->val & CHANNEL_B)) {
+
+				/*Get rate table for this operation mode*/
+				rt = ath5k_hw_get_rate_table(hal,
+						MODE_IEEE80211B);
+
+				/*Write rate duration table*/
+				for (i = 0; i < rt->rate_count; i++) {
+					data = AR5K_RATE_DUR(rt->rates[i].rate_code);
+					ath5k_hw_reg_write(hal,
+						ath_hal_computetxtime(hal, rt,
+						14, rt->rates[i].control_rate,
+						false), data);
+					if (HAS_SHPREAMBLE(i))
+						ath5k_hw_reg_write(hal,
+						    ath_hal_computetxtime(hal,
+						    rt, 14,
+						    rt->rates[i].control_rate,
+						    false), data +
+						    (AR5K_SET_SHORT_PREAMBLE << 2));
+				}
+
+			} else {
+			/* For 802.11a/g Turbo/XR mode (AR5K_MODE_XR here is
+			 * O.K. for both a/g - OFDM) */
+
+				/* Get rate table for this operation mode */
+				rt = ath5k_hw_get_rate_table(hal,
+				    channel->val & CHANNEL_TURBO ?
+				    MODE_ATHEROS_TURBO : MODE_ATHEROS_TURBOG);
+
+				/* Write rate duration table */
+				for (i = 0; i < rt->rate_count; i++)
+					ath5k_hw_reg_write(hal,
+						ath_hal_computetxtime(hal, rt,
+						14, rt->rates[i].control_rate,
+						false),
+						AR5K_RATE_DUR(rt->rates[i].rate_code));
+
+			}
+		}
+
+		/* Fix for first revision of the RF5112 RF chipset */
+		if (hal->ah_radio >= AR5K_RF5112 &&
+				hal->ah_radio_5ghz_revision <
+				AR5K_SREV_RAD_5112A) {
+			ath5k_hw_reg_write(hal, AR5K_PHY_CCKTXCTL_WORLD,
+					AR5K_PHY_CCKTXCTL);
+			if (channel->val & CHANNEL_A)
+				data = 0xffb81020;
+			else
+				data = 0xffb80d20;
+			ath5k_hw_reg_write(hal, data, AR5K_PHY_FRAME_CTL);
+		}
+
+		/*
+		 * Set TX power (XXX use txpower from net80211)
+		 */
+		ret = ath5k_hw_txpower(hal, channel, AR5K_TUNE_DEFAULT_TXPOWER);
+		if (ret)
+			return ret;
+
+		/*
+		 * Write RF registers
+		 * TODO:Does this work on 5211 (5111) ?
+		 */
+		ret = ath5k_hw_rfregs(hal, channel, mode);
+		if (ret)
+			return ret;
+
+		/*
+		 * Configure additional registers
+		 */
+
+		/* Write OFDM timings on 5212*/
+		if (hal->ah_version == AR5K_AR5212) {
+			if (channel->val & CHANNEL_OFDM) {
+				u32 coef_scaled, coef_exp, coef_man,
+					ds_coef_exp, ds_coef_man, clock;
+
+				clock = channel->val & CHANNEL_T ? 80 : 40;
+				coef_scaled = ((5 * (clock << 24)) / 2) /
+					channel->freq;
+
+				for (coef_exp = 31; coef_exp > 0; coef_exp--)
+					if ((coef_scaled >> coef_exp) & 0x1)
+						break;
+
+				if (!coef_exp)
+					return -EINVAL;
+
+				coef_exp = 14 - (coef_exp - 24);
+				coef_man = coef_scaled +
+					(1 << (24 - coef_exp - 1));
+				ds_coef_man = coef_man >> (24 - coef_exp);
+				ds_coef_exp = coef_exp - 16;
+
+				AR5K_REG_WRITE_BITS(hal, AR5K_PHY_TIMING_3,
+				    AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man);
+				AR5K_REG_WRITE_BITS(hal, AR5K_PHY_TIMING_3,
+				    AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp);
+			}
+		}
+
+		/*Enable/disable 802.11b mode on 5111
+		(enable 2111 frequency converter + CCK)*/
+		if (hal->ah_radio == AR5K_RF5111) {
+			if (channel->val & CHANNEL_B)
+				AR5K_REG_ENABLE_BITS(hal, AR5K_TXCFG,
+				    AR5K_TXCFG_B_MODE);
+			else
+				AR5K_REG_DISABLE_BITS(hal, AR5K_TXCFG,
+				    AR5K_TXCFG_B_MODE);
+		}
+
+		/* Set antenna mode */
+		AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x44),
+			hal->ah_antenna[ee_mode][0], 0xfffffc06);
+
+		if (freq == AR5K_INI_RFGAIN_2GHZ)
+			ant[0] = ant[1] = AR5K_ANT_FIXED_B;
+		else
+			ant[0] = ant[1] = AR5K_ANT_FIXED_A;
+
+		ath5k_hw_reg_write(hal, hal->ah_antenna[ee_mode][ant[0]],
+			AR5K_PHY_ANT_SWITCH_TABLE_0);
+		ath5k_hw_reg_write(hal, hal->ah_antenna[ee_mode][ant[1]],
+			AR5K_PHY_ANT_SWITCH_TABLE_1);
+
+		/* Commit values from EEPROM */
+		if (hal->ah_radio == AR5K_RF5111)
+			AR5K_REG_WRITE_BITS(hal, AR5K_PHY_FRAME_CTL,
+			    AR5K_PHY_FRAME_CTL_TX_CLIP, ee->ee_tx_clip);
+
+		ath5k_hw_reg_write(hal,
+			AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]),
+			AR5K_PHY(0x5a));
+
+		AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x11),
+			(ee->ee_switch_settling[ee_mode] << 7) & 0x3f80,
+			0xffffc07f);
+		AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x12),
+			(ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000,
+			0xfffc0fff);
+		AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x14),
+			(ee->ee_adc_desired_size[ee_mode] & 0x00ff) |
+			((ee->ee_pga_desired_size[ee_mode] << 8) & 0xff00),
+			0xffff0000);
+
+		ath5k_hw_reg_write(hal,
+			(ee->ee_tx_end2xpa_disable[ee_mode] << 24) |
+			(ee->ee_tx_end2xpa_disable[ee_mode] << 16) |
+			(ee->ee_tx_frm2xpa_enable[ee_mode] << 8) |
+			(ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY(0x0d));
+
+		AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x0a),
+			ee->ee_tx_end2xlna_enable[ee_mode] << 8, 0xffff00ff);
+		AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x19),
+			(ee->ee_thr_62[ee_mode] << 12) & 0x7f000, 0xfff80fff);
+		AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x49), 4, 0xffffff01);
+
+		AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_IQ,
+		    AR5K_PHY_IQ_CORR_ENABLE |
+		    (ee->ee_i_cal[ee_mode] << AR5K_PHY_IQ_CORR_Q_I_COFF_S) |
+		    ee->ee_q_cal[ee_mode]);
+
+		if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+			AR5K_REG_WRITE_BITS(hal, AR5K_PHY_GAIN_2GHZ,
+				AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX,
+				ee->ee_margin_tx_rx[ee_mode]);
+
+	} else {
+		mdelay(1);
+		/* Disable phy and wait */
+		ath5k_hw_reg_write(hal, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
+		mdelay(1);
+	}
+
+	/*
+	 * Restore saved values
+	 */
+	/*DCU/Antenna selection not available on 5210*/
+	if (hal->ah_version != AR5K_AR5210) {
+		ath5k_hw_reg_write(hal, s_seq, AR5K_QUEUE_DFS_SEQNUM(0));
+		ath5k_hw_reg_write(hal, s_ant, AR5K_DEFAULT_ANTENNA);
+	}
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, s_led[0]);
+	ath5k_hw_reg_write(hal, s_led[1], AR5K_GPIOCR);
+	ath5k_hw_reg_write(hal, s_led[2], AR5K_GPIODO);
+
+	/*
+	 * Misc
+	 */
+	memset(mac, 0xff, ETH_ALEN);
+	ath5k_hw_set_associd(hal, mac, 0);
+	ath5k_hw_set_opmode(hal);
+	/*PISR/SISR Not available on 5210*/
+	if (hal->ah_version != AR5K_AR5210) {
+		ath5k_hw_reg_write(hal, 0xffffffff, AR5K_PISR);
+		/* XXX: AR5K_RSSI_THR has masks and shifts defined for it, so
+		 * direct write using ath5k_hw_reg_write seems wrong. Test with:
+		 * AR5K_REG_WRITE_BITS(hal, AR5K_RSSI_THR,
+		 *   AR5K_RSSI_THR_BMISS, AR5K_TUNE_RSSI_THRES);
+		 * with different variables and check results compared
+		 * to ath5k_hw_reg_write(hal, )  */
+		ath5k_hw_reg_write(hal, AR5K_TUNE_RSSI_THRES, AR5K_RSSI_THR);
+	}
+
+	/*
+	 * Set Rx/Tx DMA Configuration
+	 *(passing dma size not available on 5210)
+	 */
+	if (hal->ah_version != AR5K_AR5210) {
+		AR5K_REG_WRITE_BITS(hal, AR5K_TXCFG, AR5K_TXCFG_SDMAMR,
+				AR5K_DMASIZE_512B | AR5K_TXCFG_DMASIZE);
+		AR5K_REG_WRITE_BITS(hal, AR5K_RXCFG, AR5K_RXCFG_SDMAMW,
+				AR5K_DMASIZE_512B);
+	}
+
+	/*
+	 * Set channel and calibrate the PHY
+	 */
+	ret = ath5k_hw_channel(hal, channel);
+	if (ret)
+		return ret;
+
+	/*
+	 * Enable the PHY and wait until completion
+	 */
+	ath5k_hw_reg_write(hal, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
+
+	/*
+	 * 5111/5112 Specific
+	 */
+	if (hal->ah_version != AR5K_AR5210) {
+		data = ath5k_hw_reg_read(hal, AR5K_PHY_RX_DELAY) &
+			AR5K_PHY_RX_DELAY_M;
+		data = (channel->val & CHANNEL_CCK) ?
+			((data << 2) / 22) : (data / 10);
+
+		udelay(100 + data);
+	} else {
+		mdelay(1);
+	}
+
+	/*
+	 * Enable calibration and wait until completion
+	 */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL,
+				AR5K_PHY_AGCCTL_CAL);
+
+	if (ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL,
+			AR5K_PHY_AGCCTL_CAL, 0, false)) {
+		AR5K_PRINTF("calibration timeout (%uMHz)\n", channel->freq);
+		return -EAGAIN;
+	}
+
+	/*
+	 * Enable noise floor calibration and wait until completion
+	 */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL,
+				AR5K_PHY_AGCCTL_NF);
+
+	if (ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL,
+			AR5K_PHY_AGCCTL_NF, 0, false)) {
+		AR5K_PRINTF("noise floor calibration timeout (%uMHz)\n",
+				channel->freq);
+		return -EAGAIN;
+	}
+
+	/* Wait until the noise floor is calibrated and read the value */
+	for (i = 20; i > 0; i--) {
+		mdelay(1);
+		noise_floor = ath5k_hw_reg_read(hal, AR5K_PHY_NF);
+
+		if (AR5K_PHY_NF_RVAL(noise_floor) &
+		AR5K_PHY_NF_ACTIVE)
+			noise_floor = AR5K_PHY_NF_AVAL(noise_floor);
+
+		if (noise_floor <= AR5K_TUNE_NOISE_FLOOR)
+			break;
+	}
+
+	if (noise_floor > AR5K_TUNE_NOISE_FLOOR) {
+		AR5K_PRINTF("noise floor calibration failed (%uMHz)\n",
+			channel->freq);
+		return -EIO;
+	}
+
+	hal->ah_calibration = false;
+
+	if (!(channel->val & CHANNEL_B)) {
+		hal->ah_calibration = true;
+		AR5K_REG_WRITE_BITS(hal, AR5K_PHY_IQ,
+				AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
+		AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_IQ,
+				AR5K_PHY_IQ_RUN);
+	}
+
+	/*
+	 * Reset queues and start beacon timers at the end of the reset routine
+	 */
+	for (i = 0; i < hal->ah_capabilities.cap_queues.q_tx_num; i++) {
+		/*No QCU on 5210*/
+		if (hal->ah_version != AR5K_AR5210)
+			AR5K_REG_WRITE_Q(hal, AR5K_QUEUE_QCUMASK(i), i);
+
+		ret = ath5k_hw_reset_tx_queue(hal, i);
+		if (ret) {
+			AR5K_PRINTF("failed to reset TX queue #%d\n", i);
+			return ret;
+		}
+	}
+
+	/* Pre-enable interrupts on 5211/5212*/
+	if (hal->ah_version != AR5K_AR5210)
+		ath5k_hw_set_intr(hal, AR5K_INT_RX | AR5K_INT_TX |
+				AR5K_INT_FATAL);
+
+	/*
+	 * Set RF kill flags if supported by the device (read from the EEPROM)
+	 * Disable gpio_intr for now since it results system hang.
+	 * TODO: Handle this in ath_intr
+	 */
+#if 0
+	if (AR5K_EEPROM_HDR_RFKILL(hal->ah_capabilities.cap_eeprom.ee_header)) {
+		ath5k_hw_set_gpio_input(hal, 0);
+		hal->ah_gpio[0] = ath5k_hw_get_gpio(hal, 0);
+		if (hal->ah_gpio[0] == 0)
+			ath5k_hw_set_gpio_intr(hal, 0, 1);
+		else
+			ath5k_hw_set_gpio_intr(hal, 0, 0);
+	}
+#endif
+
+	/*
+	 * Set the 32MHz reference clock on 5212 phy clock sleep register
+	 */
+	if (hal->ah_version == AR5K_AR5212) {
+		ath5k_hw_reg_write(hal, AR5K_PHY_SCR_32MHZ, AR5K_PHY_SCR);
+		ath5k_hw_reg_write(hal, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT);
+		ath5k_hw_reg_write(hal, AR5K_PHY_SCAL_32MHZ, AR5K_PHY_SCAL);
+		ath5k_hw_reg_write(hal, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
+		ath5k_hw_reg_write(hal, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
+		ath5k_hw_reg_write(hal, hal->ah_radio == AR5K_RF5111 ?
+			AR5K_PHY_SPENDING_RF5111 : AR5K_PHY_SPENDING_RF5112,
+			AR5K_PHY_SPENDING);
+	}
+
+	/*
+	 * Disable beacons and reset the register
+	 */
+	AR5K_REG_DISABLE_BITS(hal, AR5K_BEACON, AR5K_BEACON_ENABLE |
+			AR5K_BEACON_RESET_TSF);
+
+	return 0;
+}
+
+/*
+ * Reset chipset
+ */
+static int ath5k_hw_nic_reset(struct ath_hw *hal, u32 val)
+{
+	int ret;
+	u32 mask = val ? val : ~0;
+
+	AR5K_TRACE;
+
+	/* Read-and-clear RX Descriptor Pointer*/
+	ath5k_hw_reg_read(hal, AR5K_RXDP);
+
+	/*
+	 * Reset the device and wait until success
+	 */
+	ath5k_hw_reg_write(hal, val, AR5K_RESET_CTL);
+
+	/* Wait at least 128 PCI clocks */
+	udelay(15);
+
+	if (hal->ah_version == AR5K_AR5210) {
+		val &= AR5K_RESET_CTL_CHIP;
+		mask &= AR5K_RESET_CTL_CHIP;
+	} else {
+		val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
+		mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
+	}
+
+	ret = ath5k_hw_register_timeout(hal, AR5K_RESET_CTL, mask, val, false);
+
+	/*
+	 * Reset configuration register (for hw byte-swap)
+	 */
+	if ((val & AR5K_RESET_CTL_PCU) == 0)
+		ath5k_hw_reg_write(hal, AR5K_INIT_CFG, AR5K_CFG);
+
+	return ret;
+}
+
+/*
+ * Power management functions
+ */
+
+/*
+ * Sleep control
+ */
+int ath5k_hw_set_power(struct ath_hw *hal, enum ath5k_power_mode mode,
+		bool set_chip, u16 sleep_duration)
+{
+	unsigned int i;
+	u32 staid;
+
+	AR5K_TRACE;
+	staid = ath5k_hw_reg_read(hal, AR5K_STA_ID1);
+
+	switch (mode) {
+	case AR5K_PM_AUTO:
+		staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA;
+		/* fallthrough */
+	case AR5K_PM_NETWORK_SLEEP:
+		if (set_chip == true)
+			ath5k_hw_reg_write(hal,
+				AR5K_SLEEP_CTL_SLE | sleep_duration,
+				AR5K_SLEEP_CTL);
+
+		staid |= AR5K_STA_ID1_PWR_SV;
+		break;
+
+	case AR5K_PM_FULL_SLEEP:
+		if (set_chip == true)
+			ath5k_hw_reg_write(hal, AR5K_SLEEP_CTL_SLE_SLP,
+				AR5K_SLEEP_CTL);
+
+		staid |= AR5K_STA_ID1_PWR_SV;
+		break;
+
+	case AR5K_PM_AWAKE:
+		if (set_chip == false)
+			goto commit;
+
+		ath5k_hw_reg_write(hal, AR5K_SLEEP_CTL_SLE_WAKE,
+				AR5K_SLEEP_CTL);
+
+		for (i = 5000; i > 0; i--) {
+			/* Check if the chip did wake up */
+			if ((ath5k_hw_reg_read(hal, AR5K_PCICFG) &
+					AR5K_PCICFG_SPWR_DN) == 0)
+				break;
+
+			/* Wait a bit and retry */
+			udelay(200);
+			ath5k_hw_reg_write(hal, AR5K_SLEEP_CTL_SLE_WAKE,
+				AR5K_SLEEP_CTL);
+		}
+
+		/* Fail if the chip didn't wake up */
+		if (i <= 0)
+			return -EIO;
+
+		staid &= ~AR5K_STA_ID1_PWR_SV;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+commit:
+	hal->ah_power_mode = mode;
+	ath5k_hw_reg_write(hal, staid, AR5K_STA_ID1);
+
+	return 0;
+}
+
+/***********************\
+  DMA Related Functions
+\***********************/
+
+/*
+ * Receive functions
+ */
+
+/*
+ * Start DMA receive
+ */
+void ath5k_hw_start_rx(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	ath5k_hw_reg_write(hal, AR5K_CR_RXE, AR5K_CR);
+}
+
+/*
+ * Stop DMA receive
+ */
+int ath5k_hw_stop_rx_dma(struct ath_hw *hal)
+{
+	unsigned int i;
+
+	AR5K_TRACE;
+	ath5k_hw_reg_write(hal, AR5K_CR_RXD, AR5K_CR);
+
+	/*
+	 * It may take some time to disable the DMA receive unit
+	 */
+	for (i = 2000; i > 0 &&
+			(ath5k_hw_reg_read(hal, AR5K_CR) & AR5K_CR_RXE) != 0;
+			i--)
+		udelay(10);
+
+	return i ? 0 : -EBUSY;
+}
+
+/*
+ * Get the address of the RX Descriptor
+ */
+u32 ath5k_hw_get_rx_buf(struct ath_hw *hal)
+{
+	return ath5k_hw_reg_read(hal, AR5K_RXDP);
+}
+
+/*
+ * Set the address of the RX Descriptor
+ */
+void ath5k_hw_put_rx_buf(struct ath_hw *hal, u32 phys_addr)
+{
+	AR5K_TRACE;
+
+	/*TODO:Shouldn't we check if RX is enabled first ?*/
+	ath5k_hw_reg_write(hal, phys_addr, AR5K_RXDP);
+}
+
+/*
+ * Transmit functions
+ */
+
+/*
+ * Start DMA transmit for a specific queue
+ * (see also QCU/DCU functions)
+ */
+int ath5k_hw_tx_start(struct ath_hw *hal, unsigned int queue)
+{
+	u32 tx_queue;
+
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+	/* Return if queue is declared inactive */
+	if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return -EIO;
+
+	if (hal->ah_version == AR5K_AR5210) {
+		tx_queue = ath5k_hw_reg_read(hal, AR5K_CR);
+
+		/*
+		 * Set the queue by type on 5210
+		 */
+		switch (hal->ah_txq[queue].tqi_type) {
+		case AR5K_TX_QUEUE_DATA:
+			tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0;
+			break;
+		case AR5K_TX_QUEUE_BEACON:
+			tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
+			ath5k_hw_reg_write(hal, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
+					AR5K_BSR);
+			break;
+		case AR5K_TX_QUEUE_CAB:
+			tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
+			ath5k_hw_reg_write(hal, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V |
+				AR5K_BCR_BDMAE, AR5K_BSR);
+			break;
+		default:
+			return -EINVAL;
+		}
+		/* Start queue */
+		ath5k_hw_reg_write(hal, tx_queue, AR5K_CR);
+	} else {
+		/* Return if queue is disabled */
+		if (AR5K_REG_READ_Q(hal, AR5K_QCU_TXD, queue))
+			return -EIO;
+
+		/* Start queue */
+		AR5K_REG_WRITE_Q(hal, AR5K_QCU_TXE, queue);
+	}
+
+	return 0;
+}
+
+/*
+ * Stop DMA transmit for a specific queue
+ * (see also QCU/DCU functions)
+ */
+int ath5k_hw_stop_tx_dma(struct ath_hw *hal, unsigned int queue)
+{
+	unsigned int i = 100;
+	u32 tx_queue, pending;
+
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+	/* Return if queue is declared inactive */
+	if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return -EIO;
+
+	if (hal->ah_version == AR5K_AR5210) {
+		tx_queue = ath5k_hw_reg_read(hal, AR5K_CR);
+
+		/*
+		 * Set by queue type
+		 */
+		switch (hal->ah_txq[queue].tqi_type) {
+		case AR5K_TX_QUEUE_DATA:
+			tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0;
+			break;
+		case AR5K_TX_QUEUE_BEACON:
+		case AR5K_TX_QUEUE_CAB:
+			/* XXX Fix me... */
+			tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1;
+			ath5k_hw_reg_write(hal, 0, AR5K_BSR);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* Stop queue */
+		ath5k_hw_reg_write(hal, tx_queue, AR5K_CR);
+	} else {
+		/*
+		 * Schedule TX disable and wait until queue is empty
+		 */
+		AR5K_REG_WRITE_Q(hal, AR5K_QCU_TXD, queue);
+
+		/*Check for pending frames*/
+		do {
+			pending = ath5k_hw_reg_read(hal,
+				AR5K_QUEUE_STATUS(queue)) &
+				AR5K_QCU_STS_FRMPENDCNT;
+			udelay(100);
+		} while (--i && pending);
+
+		/* Clear register */
+		ath5k_hw_reg_write(hal, 0, AR5K_QCU_TXD);
+	}
+
+	/* TODO: Check for success else return error */
+	return 0;
+}
+
+/*
+ * Get the address of the TX Descriptor for a specific queue
+ * (see also QCU/DCU functions)
+ */
+u32 ath5k_hw_get_tx_buf(struct ath_hw *hal, unsigned int queue)
+{
+	u16 tx_reg;
+
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+	/*
+	 * Get the transmit queue descriptor pointer from the selected queue
+	 */
+	/*5210 doesn't have QCU*/
+	if (hal->ah_version == AR5K_AR5210) {
+		switch (hal->ah_txq[queue].tqi_type) {
+		case AR5K_TX_QUEUE_DATA:
+			tx_reg = AR5K_NOQCU_TXDP0;
+			break;
+		case AR5K_TX_QUEUE_BEACON:
+		case AR5K_TX_QUEUE_CAB:
+			tx_reg = AR5K_NOQCU_TXDP1;
+			break;
+		default:
+			return 0xffffffff;
+		}
+	} else {
+		tx_reg = AR5K_QUEUE_TXDP(queue);
+	}
+
+	return ath5k_hw_reg_read(hal, tx_reg);
+}
+
+/*
+ * Set the address of the TX Descriptor for a specific queue
+ * (see also QCU/DCU functions)
+ */
+int ath5k_hw_put_tx_buf(struct ath_hw *hal, unsigned int queue, u32 phys_addr)
+{
+	u16 tx_reg;
+
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+	/*
+	 * Set the transmit queue descriptor pointer register by type
+	 * on 5210
+	 */
+	if (hal->ah_version == AR5K_AR5210) {
+		switch (hal->ah_txq[queue].tqi_type) {
+		case AR5K_TX_QUEUE_DATA:
+			tx_reg = AR5K_NOQCU_TXDP0;
+			break;
+		case AR5K_TX_QUEUE_BEACON:
+		case AR5K_TX_QUEUE_CAB:
+			tx_reg = AR5K_NOQCU_TXDP1;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		/*
+		 * Set the transmit queue descriptor pointer for
+		 * the selected queue on QCU for 5211+
+		 * (this won't work if the queue is still active)
+		 */
+		if (AR5K_REG_READ_Q(hal, AR5K_QCU_TXE, queue))
+			return -EIO;
+
+		tx_reg = AR5K_QUEUE_TXDP(queue);
+	}
+
+	/* Set descriptor pointer */
+	ath5k_hw_reg_write(hal, phys_addr, tx_reg);
+
+	return 0;
+}
+
+/*
+ * Update tx trigger level
+ */
+int ath5k_hw_update_tx_triglevel(struct ath_hw *hal, bool increase)
+{
+	u32 trigger_level, imr;
+	int ret = -EIO;
+
+	AR5K_TRACE;
+
+	/*
+	 * Disable interrupts by setting the mask
+	 */
+	imr = ath5k_hw_set_intr(hal, hal->ah_imr & ~AR5K_INT_GLOBAL);
+
+	/*TODO: Boundary check on trigger_level*/
+	trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(hal, AR5K_TXCFG),
+			AR5K_TXCFG_TXFULL);
+
+	if (increase == false) {
+		if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES)
+			goto done;
+	} else
+		trigger_level +=
+			((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2);
+
+	/*
+	 * Update trigger level on success
+	 */
+	if (hal->ah_version == AR5K_AR5210)
+		ath5k_hw_reg_write(hal, trigger_level, AR5K_TRIG_LVL);
+	else
+		AR5K_REG_WRITE_BITS(hal, AR5K_TXCFG,
+				AR5K_TXCFG_TXFULL, trigger_level);
+
+	ret = 0;
+
+done:
+	/*
+	 * Restore interrupt mask
+	 */
+	ath5k_hw_set_intr(hal, imr);
+
+	return ret;
+}
+
+/*
+ * Interrupt handling
+ */
+
+/*
+ * Check if we have pending interrupts
+ */
+bool ath5k_hw_is_intr_pending(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	return ath5k_hw_reg_read(hal, AR5K_INTPEND);
+}
+
+/*
+ * Get interrupt mask (ISR)
+ */
+int ath5k_hw_get_isr(struct ath_hw *hal, enum ath5k_int *interrupt_mask)
+{
+	u32 data;
+
+	AR5K_TRACE;
+
+	/*
+	 * Read interrupt status from the Interrupt Status register
+	 * on 5210
+	 */
+	if (hal->ah_version == AR5K_AR5210) {
+		data = ath5k_hw_reg_read(hal, AR5K_ISR);
+		if (unlikely(data == AR5K_INT_NOCARD)) {
+			*interrupt_mask = data;
+			return -ENODEV;
+		}
+	}
+
+	/*
+	 * Read interrupt status from the Read-And-Clear shadow register
+	 */
+	data = ath5k_hw_reg_read(hal, AR5K_RAC_PISR);
+
+	/*
+	 * Get abstract interrupt mask (HAL-compatible)
+	 */
+	*interrupt_mask = (data & AR5K_INT_COMMON) & hal->ah_imr;
+
+	if (unlikely(data == AR5K_INT_NOCARD))
+		return -ENODEV;
+
+	if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR))
+		*interrupt_mask |= AR5K_INT_RX;
+
+	if (data & (AR5K_ISR_TXOK | AR5K_ISR_TXERR))
+		*interrupt_mask |= AR5K_INT_TX;
+
+	if (hal->ah_version != AR5K_AR5210) {
+		/*HIU = Host Interface Unit (PCI etc)*/
+		if (unlikely(data & (AR5K_ISR_HIUERR)))
+			*interrupt_mask |= AR5K_INT_FATAL;
+
+		/*Beacon Not Ready*/
+		if (unlikely(data & (AR5K_ISR_BNR)))
+			*interrupt_mask |= AR5K_INT_BNR;
+	}
+
+	/*
+	 * XXX: BMISS interrupts may occur after association.
+	 * I found this on 5210 code but it needs testing
+	 */
+#if 0
+	interrupt_mask &= ~AR5K_INT_BMISS;
+#endif
+
+	/*
+	 * In case we didn't handle anything,
+	 * print the register value.
+	 */
+	if (unlikely(*interrupt_mask == 0 && net_ratelimit()))
+		AR5K_PRINTF("0x%08x\n", data);
+
+	return 0;
+}
+
+/*
+ * Set interrupt mask
+ */
+enum ath5k_int ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask)
+{
+	enum ath5k_int old_mask, int_mask;
+
+	/*
+	 * Disable card interrupts to prevent any race conditions
+	 * (they will be re-enabled afterwards).
+	 */
+	ath5k_hw_reg_write(hal, AR5K_IER_DISABLE, AR5K_IER);
+
+	old_mask = hal->ah_imr;
+
+	/*
+	 * Add additional, chipset-dependent interrupt mask flags
+	 * and write them to the IMR (interrupt mask register).
+	 */
+	int_mask = new_mask & AR5K_INT_COMMON;
+
+	if (new_mask & AR5K_INT_RX)
+		int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN |
+			AR5K_IMR_RXDESC;
+
+	if (new_mask & AR5K_INT_TX)
+		int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC |
+			AR5K_IMR_TXURN;
+
+	if (hal->ah_version != AR5K_AR5210) {
+		if (new_mask & AR5K_INT_FATAL) {
+			int_mask |= AR5K_IMR_HIUERR;
+			AR5K_REG_ENABLE_BITS(hal, AR5K_SIMR2, AR5K_SIMR2_MCABT |
+					AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR);
+		}
+	}
+
+	ath5k_hw_reg_write(hal, int_mask, AR5K_PIMR);
+
+	/* Store new interrupt mask */
+	hal->ah_imr = new_mask;
+
+	/* ..re-enable interrupts */
+	ath5k_hw_reg_write(hal, AR5K_IER_ENABLE, AR5K_IER);
+
+	return old_mask;
+}
+
+
+/*************************\
+  EEPROM access functions
+\*************************/
+
+/*
+ * Read from eeprom
+ */
+static int ath5k_hw_eeprom_read(struct ath_hw *hal, u32 offset, u16 *data)
+{
+	u32 status, timeout;
+
+	AR5K_TRACE;
+	/*
+	 * Initialize EEPROM access
+	 */
+	if (hal->ah_version == AR5K_AR5210) {
+		AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, AR5K_PCICFG_EEAE);
+		(void)ath5k_hw_reg_read(hal, AR5K_EEPROM_BASE + (4 * offset));
+	} else {
+		ath5k_hw_reg_write(hal, offset, AR5K_EEPROM_BASE);
+		AR5K_REG_ENABLE_BITS(hal, AR5K_EEPROM_CMD,
+				AR5K_EEPROM_CMD_READ);
+	}
+
+	for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
+		status = ath5k_hw_reg_read(hal, AR5K_EEPROM_STATUS);
+		if (status & AR5K_EEPROM_STAT_RDDONE) {
+			if (status & AR5K_EEPROM_STAT_RDERR)
+				return -EIO;
+			*data = (u16)(ath5k_hw_reg_read(hal, AR5K_EEPROM_DATA) &
+					0xffff);
+			return 0;
+		}
+		udelay(15);
+	}
+
+	return -ETIMEDOUT;
+}
+
+/*
+ * Write to eeprom - currently disabled, use at your own risk
+ */
+static int ath5k_hw_eeprom_write(struct ath_hw *hal, u32 offset, u16 data)
+{
+#if 0
+	u32 status, timeout;
+
+	AR5K_TRACE;
+
+	/*
+	 * Initialize eeprom access
+	 */
+
+	if (hal->ah_version == AR5K_AR5210) {
+		AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, AR5K_PCICFG_EEAE);
+	} else {
+		AR5K_REG_ENABLE_BITS(hal, AR5K_EEPROM_CMD,
+				AR5K_EEPROM_CMD_RESET);
+	}
+
+	/*
+	 * Write data to data register
+	 */
+
+	if (hal->ah_version == AR5K_AR5210) {
+		ath5k_hw_reg_write(hal, data, AR5K_EEPROM_BASE + (4 * offset));
+	} else {
+		ath5k_hw_reg_write(hal, offset, AR5K_EEPROM_BASE);
+		ath5k_hw_reg_write(hal, data, AR5K_EEPROM_DATA);
+		AR5K_REG_ENABLE_BITS(hal, AR5K_EEPROM_CMD,
+				AR5K_EEPROM_CMD_WRITE);
+	}
+
+	/*
+	 * Check status
+	 */
+
+	for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
+		status = ath5k_hw_reg_read(hal, AR5K_EEPROM_STATUS);
+		if (status & AR5K_EEPROM_STAT_WRDONE) {
+			if (status & AR5K_EEPROM_STAT_WRERR)
+				return EIO;
+			return 0;
+		}
+		udelay(15);
+	}
+#endif
+	AR5K_PRINTF("EEPROM Write is disabled!");
+	return -EIO;
+}
+
+/*
+ * Translate binary channel representation in EEPROM to frequency
+ */
+static u16 ath5k_eeprom_bin2freq(struct ath_hw *hal, u16 bin, unsigned int mode)
+{
+	u16 val;
+
+	if (bin == AR5K_EEPROM_CHANNEL_DIS)
+		return bin;
+
+	if (mode == AR5K_EEPROM_MODE_11A) {
+		if (hal->ah_ee_version > AR5K_EEPROM_VERSION_3_2)
+			val = (5 * bin) + 4800;
+		else
+			val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 :
+				(bin * 10) + 5100;
+	} else {
+		if (hal->ah_ee_version > AR5K_EEPROM_VERSION_3_2)
+			val = bin + 2300;
+		else
+			val = bin + 2400;
+	}
+
+	return val;
+}
+
+/*
+ * Read antenna infos from eeprom
+ */
+static int ath5k_eeprom_read_ants(struct ath_hw *hal, u32 *offset,
+		unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom;
+	u32 o = *offset;
+	u16 val;
+	int ret, i = 0;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_switch_settling[mode]	= (val >> 8) & 0x7f;
+	ee->ee_ant_tx_rx[mode]		= (val >> 2) & 0x3f;
+	ee->ee_ant_control[mode][i]	= (val << 4) & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	|= (val >> 12) & 0xf;
+	ee->ee_ant_control[mode][i++]	= (val >> 6) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= val & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	= (val >> 10) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= (val >> 4) & 0x3f;
+	ee->ee_ant_control[mode][i]	= (val << 2) & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	|= (val >> 14) & 0x3;
+	ee->ee_ant_control[mode][i++]	= (val >> 8) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= (val >> 2) & 0x3f;
+	ee->ee_ant_control[mode][i]	= (val << 4) & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	|= (val >> 12) & 0xf;
+	ee->ee_ant_control[mode][i++]	= (val >> 6) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= val & 0x3f;
+
+	/* Get antenna modes */
+	hal->ah_antenna[mode][0] =
+	    (ee->ee_ant_control[mode][0] << 4) | 0x1;
+	hal->ah_antenna[mode][AR5K_ANT_FIXED_A] =
+	     ee->ee_ant_control[mode][1] 	|
+	    (ee->ee_ant_control[mode][2] << 6) 	|
+	    (ee->ee_ant_control[mode][3] << 12) |
+	    (ee->ee_ant_control[mode][4] << 18) |
+	    (ee->ee_ant_control[mode][5] << 24);
+	hal->ah_antenna[mode][AR5K_ANT_FIXED_B] =
+	     ee->ee_ant_control[mode][6] 	|
+	    (ee->ee_ant_control[mode][7] << 6) 	|
+	    (ee->ee_ant_control[mode][8] << 12) |
+	    (ee->ee_ant_control[mode][9] << 18) |
+	    (ee->ee_ant_control[mode][10] << 24);
+
+	/* return new offset */
+	*offset = o;
+
+	return 0;
+}
+
+/*
+ * Read supported modes from eeprom
+ */
+static int ath5k_eeprom_read_modes(struct ath_hw *hal, u32 *offset,
+		unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom;
+	u32 o = *offset;
+	u16 val;
+	int ret;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_tx_end2xlna_enable[mode]	= (val >> 8) & 0xff;
+	ee->ee_thr_62[mode]		= val & 0xff;
+
+	if (hal->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
+		ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_tx_end2xpa_disable[mode]	= (val >> 8) & 0xff;
+	ee->ee_tx_frm2xpa_enable[mode]	= val & 0xff;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_pga_desired_size[mode]	= (val >> 8) & 0xff;
+
+	if ((val & 0xff) & 0x80)
+		ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1);
+	else
+		ee->ee_noise_floor_thr[mode] = val & 0xff;
+
+	if (hal->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
+		ee->ee_noise_floor_thr[mode] =
+		    mode == AR5K_EEPROM_MODE_11A ? -54 : -1;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_xlna_gain[mode]		= (val >> 5) & 0xff;
+	ee->ee_x_gain[mode]		= (val >> 1) & 0xf;
+	ee->ee_xpd[mode]		= val & 0x1;
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0)
+		ee->ee_fixed_bias[mode] = (val >> 13) & 0x1;
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) {
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_false_detect[mode] = (val >> 6) & 0x7f;
+
+		if (mode == AR5K_EEPROM_MODE_11A)
+			ee->ee_xr_power[mode] = val & 0x3f;
+		else {
+			ee->ee_ob[mode][0] = val & 0x7;
+			ee->ee_db[mode][0] = (val >> 3) & 0x7;
+		}
+	}
+
+	if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_4) {
+		ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN;
+		ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA;
+	} else {
+		ee->ee_i_gain[mode] = (val >> 13) & 0x7;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_i_gain[mode] |= (val << 3) & 0x38;
+
+		if (mode == AR5K_EEPROM_MODE_11G)
+			ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff;
+	}
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 &&
+			mode == AR5K_EEPROM_MODE_11A) {
+		ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
+		ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
+	}
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 &&
+	    mode == AR5K_EEPROM_MODE_11G)
+		ee->ee_scaled_cck_delta = (val >> 11) & 0x1f;
+
+	/* return new offset */
+	*offset = o;
+
+	return 0;
+}
+
+/*
+ * Initialize eeprom & capabilities structs
+ */
+static int ath5k_eeprom_init(struct ath_hw *hal)
+{
+	struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom;
+	unsigned int mode, i;
+	int ret;
+	u32 offset;
+	u16 val;
+
+	/* Initial TX thermal adjustment values */
+	ee->ee_tx_clip = 4;
+	ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
+	ee->ee_gain_select = 1;
+
+	/*
+	 * Read values from EEPROM and store them in the capability structure
+	 */
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header);
+
+	/* Return if we have an old EEPROM */
+	if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_0)
+		return 0;
+
+#ifdef notyet
+	/*
+	 * Validate the checksum of the EEPROM date. There are some
+	 * devices with invalid EEPROMs.
+	 */
+	for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) {
+		AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val);
+		cksum ^= val;
+	}
+	if (cksum != AR5K_EEPROM_INFO_CKSUM) {
+		AR5K_PRINTF("Invalid EEPROM checksum 0x%04x\n", cksum);
+		return -EIO;
+	}
+#endif
+
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(hal->ah_ee_version),
+	    ee_ant_gain);
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+		AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0);
+		AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1);
+	}
+
+	if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_3) {
+		AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val);
+		ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7;
+		ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7;
+
+		AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val);
+		ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7;
+		ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7;
+	}
+
+	/*
+	 * Get conformance test limit values
+	 */
+	offset = AR5K_EEPROM_CTL(hal->ah_ee_version);
+	ee->ee_ctls = AR5K_EEPROM_N_CTLS(hal->ah_ee_version);
+
+	for (i = 0; i < ee->ee_ctls; i++) {
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_ctl[i] = (val >> 8) & 0xff;
+		ee->ee_ctl[i + 1] = val & 0xff;
+	}
+
+	/*
+	 * Get values for 802.11a (5GHz)
+	 */
+	mode = AR5K_EEPROM_MODE_11A;
+
+	ee->ee_turbo_max_power[mode] =
+			AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header);
+
+	offset = AR5K_EEPROM_MODES_11A(hal->ah_ee_version);
+
+	ret = ath5k_eeprom_read_ants(hal, &offset, mode);
+	if (ret)
+		return ret;
+
+	AR5K_EEPROM_READ(offset++, val);
+	ee->ee_adc_desired_size[mode]	= (s8)((val >> 8) & 0xff);
+	ee->ee_ob[mode][3]		= (val >> 5) & 0x7;
+	ee->ee_db[mode][3]		= (val >> 2) & 0x7;
+	ee->ee_ob[mode][2]		= (val << 1) & 0x7;
+
+	AR5K_EEPROM_READ(offset++, val);
+	ee->ee_ob[mode][2]		|= (val >> 15) & 0x1;
+	ee->ee_db[mode][2]		= (val >> 12) & 0x7;
+	ee->ee_ob[mode][1]		= (val >> 9) & 0x7;
+	ee->ee_db[mode][1]		= (val >> 6) & 0x7;
+	ee->ee_ob[mode][0]		= (val >> 3) & 0x7;
+	ee->ee_db[mode][0]		= val & 0x7;
+
+	ret = ath5k_eeprom_read_modes(hal, &offset, mode);
+	if (ret)
+		return ret;
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) {
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_margin_tx_rx[mode] = val & 0x3f;
+	}
+
+	/*
+	 * Get values for 802.11b (2.4GHz)
+	 */
+	mode = AR5K_EEPROM_MODE_11B;
+	offset = AR5K_EEPROM_MODES_11B(hal->ah_ee_version);
+
+	ret = ath5k_eeprom_read_ants(hal, &offset, mode);
+	if (ret)
+		return ret;
+
+	AR5K_EEPROM_READ(offset++, val);
+	ee->ee_adc_desired_size[mode]	= (s8)((val >> 8) & 0xff);
+	ee->ee_ob[mode][1]		= (val >> 4) & 0x7;
+	ee->ee_db[mode][1]		= val & 0x7;
+
+	ret = ath5k_eeprom_read_modes(hal, &offset, mode);
+	if (ret)
+		return ret;
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_cal_pier[mode][0] =
+			ath5k_eeprom_bin2freq(hal, val & 0xff, mode);
+		ee->ee_cal_pier[mode][1] =
+			ath5k_eeprom_bin2freq(hal, (val >> 8) & 0xff, mode);
+
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_cal_pier[mode][2] =
+			ath5k_eeprom_bin2freq(hal, val & 0xff, mode);
+	}
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+		ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+
+	/*
+	 * Get values for 802.11g (2.4GHz)
+	 */
+	mode = AR5K_EEPROM_MODE_11G;
+	offset = AR5K_EEPROM_MODES_11G(hal->ah_ee_version);
+
+	ret = ath5k_eeprom_read_ants(hal, &offset, mode);
+	if (ret)
+		return ret;
+
+	AR5K_EEPROM_READ(offset++, val);
+	ee->ee_adc_desired_size[mode]	= (s8)((val >> 8) & 0xff);
+	ee->ee_ob[mode][1]		= (val >> 4) & 0x7;
+	ee->ee_db[mode][1]		= val & 0x7;
+
+	ret = ath5k_eeprom_read_modes(hal, &offset, mode);
+	if (ret)
+		return ret;
+
+	if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_cal_pier[mode][0] =
+			ath5k_eeprom_bin2freq(hal, val & 0xff, mode);
+		ee->ee_cal_pier[mode][1] =
+			ath5k_eeprom_bin2freq(hal, (val >> 8) & 0xff, mode);
+
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_turbo_max_power[mode] = val & 0x7f;
+		ee->ee_xr_power[mode] = (val >> 7) & 0x3f;
+
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_cal_pier[mode][2] =
+			ath5k_eeprom_bin2freq(hal, val & 0xff, mode);
+
+		if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+			ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
+		ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
+
+		if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) {
+			AR5K_EEPROM_READ(offset++, val);
+			ee->ee_cck_ofdm_gain_delta = val & 0xff;
+		}
+	}
+
+	/*
+	 * Read 5GHz EEPROM channels
+	 */
+
+	return 0;
+}
+
+/*
+ * Read the MAC address from eeprom
+ */
+static int ath5k_eeprom_read_mac(struct ath_hw *hal, u8 *mac)
+{
+	u8 mac_d[ETH_ALEN];
+	u32 total, offset;
+	u16 data;
+	int octet, ret;
+
+	memset(mac, 0, ETH_ALEN);
+	memset(mac_d, 0, ETH_ALEN);
+
+	ret = ath5k_hw_eeprom_read(hal, 0x20, &data);
+	if (ret)
+		return ret;
+
+	for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
+		ret = ath5k_hw_eeprom_read(hal, offset, &data);
+		if (ret)
+			return ret;
+
+		total += data;
+		mac_d[octet + 1] = data & 0xff;
+		mac_d[octet] = data >> 8;
+		octet += 2;
+	}
+
+	memcpy(mac, mac_d, ETH_ALEN);
+
+	if (!total || total == 3 * 0xffff)
+		return -EINVAL;
+
+	return 0;
+}
+
+/*
+ * Read/Write regulatory domain
+ */
+static bool ath5k_eeprom_regulation_domain(struct ath_hw *hal, bool write,
+	enum ath5k_regdom *regdomain)
+{
+	u16 ee_regdomain;
+
+	/* Read current value */
+	if (write != true) {
+		ee_regdomain = hal->ah_capabilities.cap_eeprom.ee_regdomain;
+		*regdomain = ath5k_regdom_to_ieee(ee_regdomain);
+		return true;
+	}
+
+	ee_regdomain = ath5k_regdom_from_ieee(*regdomain);
+
+	/* Try to write a new value */
+	if (hal->ah_capabilities.cap_eeprom.ee_protect &
+			AR5K_EEPROM_PROTECT_WR_128_191)
+		return false;
+	if (ath5k_hw_eeprom_write(hal, AR5K_EEPROM_REG_DOMAIN, ee_regdomain)!=0)
+		return false;
+
+	hal->ah_capabilities.cap_eeprom.ee_regdomain = ee_regdomain;
+
+	return true;
+}
+
+/*
+ * Use the above to write a new regulatory domain
+ */
+int ath5k_hw_set_regdomain(struct ath_hw *hal, u16 regdomain)
+{
+	enum ath5k_regdom ieee_regdomain;
+
+	ieee_regdomain = ath5k_regdom_to_ieee(regdomain);
+
+	if (ath5k_eeprom_regulation_domain(hal, true, &ieee_regdomain) == true)
+		return 0;
+
+	return -EIO;
+}
+
+/*
+ * Fill the capabilities struct
+ */
+static int ath5k_hw_get_capabilities(struct ath_hw *hal)
+{
+	u16 ee_header;
+
+	AR5K_TRACE;
+	/* Capabilities stored in the EEPROM */
+	ee_header = hal->ah_capabilities.cap_eeprom.ee_header;
+
+	if (hal->ah_version == AR5K_AR5210) {
+		/*
+		 * Set radio capabilities
+		 * (The AR5110 only supports the middle 5GHz band)
+		 */
+		hal->ah_capabilities.cap_range.range_5ghz_min = 5120;
+		hal->ah_capabilities.cap_range.range_5ghz_max = 5430;
+		hal->ah_capabilities.cap_range.range_2ghz_min = 0;
+		hal->ah_capabilities.cap_range.range_2ghz_max = 0;
+
+		/* Set supported modes */
+		set_bit(MODE_IEEE80211A, hal->ah_capabilities.cap_mode);
+		set_bit(MODE_ATHEROS_TURBO, hal->ah_capabilities.cap_mode);
+	} else {
+		/*
+		 * XXX The tranceiver supports frequencies from 4920 to 6100GHz
+		 * XXX and from 2312 to 2732GHz. There are problems with the
+		 * XXX current ieee80211 implementation because the IEEE
+		 * XXX channel mapping does not support negative channel
+		 * XXX numbers (2312MHz is channel -19). Of course, this
+		 * XXX doesn't matter because these channels are out of range
+		 * XXX but some regulation domains like MKK (Japan) will
+		 * XXX support frequencies somewhere around 4.8GHz.
+		 */
+
+		/*
+		 * Set radio capabilities
+		 */
+
+		if (AR5K_EEPROM_HDR_11A(ee_header)) {
+			hal->ah_capabilities.cap_range.range_5ghz_min = 5005; /* 4920 */
+			hal->ah_capabilities.cap_range.range_5ghz_max = 6100;
+
+			/* Set supported modes */
+			set_bit(MODE_IEEE80211A, hal->ah_capabilities.cap_mode);
+			set_bit(MODE_ATHEROS_TURBO,
+					hal->ah_capabilities.cap_mode);
+			if (hal->ah_version == AR5K_AR5212)
+				set_bit(MODE_ATHEROS_TURBOG,
+						hal->ah_capabilities.cap_mode);
+		}
+
+		/* Enable  802.11b if a 2GHz capable radio (2111/5112) is
+		 * connected */
+		if (AR5K_EEPROM_HDR_11B(ee_header) ||
+				AR5K_EEPROM_HDR_11G(ee_header)) {
+			hal->ah_capabilities.cap_range.range_2ghz_min = 2412; /* 2312 */
+			hal->ah_capabilities.cap_range.range_2ghz_max = 2732;
+
+			if (AR5K_EEPROM_HDR_11B(ee_header))
+				set_bit(MODE_IEEE80211B,
+						hal->ah_capabilities.cap_mode);
+
+			if (AR5K_EEPROM_HDR_11G(ee_header))
+				set_bit(MODE_IEEE80211G,
+						hal->ah_capabilities.cap_mode);
+		}
+	}
+
+	/* GPIO */
+	hal->ah_gpio_npins = AR5K_NUM_GPIO;
+
+	/* Set number of supported TX queues */
+	if (hal->ah_version == AR5K_AR5210)
+		hal->ah_capabilities.cap_queues.q_tx_num =
+			AR5K_NUM_TX_QUEUES_NOQCU;
+	else
+		hal->ah_capabilities.cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES;
+
+	return 0;
+}
+
+/*********************************\
+  Protocol Control Unit Functions
+\*********************************/
+
+/*
+ * Set Operation mode
+ */
+int ath5k_hw_set_opmode(struct ath_hw *hal)
+{
+	u32 pcu_reg, beacon_reg, low_id, high_id;
+
+	pcu_reg = 0;
+	beacon_reg = 0;
+
+	AR5K_TRACE;
+
+	switch (hal->ah_op_mode) {
+	case IEEE80211_IF_TYPE_IBSS:
+		pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA |
+			(hal->ah_version == AR5K_AR5210 ?
+				AR5K_STA_ID1_NO_PSPOLL : 0);
+		beacon_reg |= AR5K_BCR_ADHOC;
+		break;
+
+	case IEEE80211_IF_TYPE_AP:
+		pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA |
+			(hal->ah_version == AR5K_AR5210 ?
+				AR5K_STA_ID1_NO_PSPOLL : 0);
+		beacon_reg |= AR5K_BCR_AP;
+		break;
+
+	case IEEE80211_IF_TYPE_STA:
+		pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA |
+			(hal->ah_version == AR5K_AR5210 ?
+				AR5K_STA_ID1_PWR_SV : 0);
+	case IEEE80211_IF_TYPE_MNTR:
+		pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA |
+			(hal->ah_version == AR5K_AR5210 ?
+				AR5K_STA_ID1_NO_PSPOLL : 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * Set PCU registers
+	 */
+	low_id = AR5K_LOW_ID(hal->ah_sta_id);
+	high_id = AR5K_HIGH_ID(hal->ah_sta_id);
+	ath5k_hw_reg_write(hal, low_id, AR5K_STA_ID0);
+	ath5k_hw_reg_write(hal, pcu_reg | high_id, AR5K_STA_ID1);
+
+	/*
+	 * Set Beacon Control Register on 5210
+	 */
+	if (hal->ah_version == AR5K_AR5210)
+		ath5k_hw_reg_write(hal, beacon_reg, AR5K_BCR);
+
+	return 0;
+}
+
+/*
+ * BSSID Functions
+ */
+
+/*
+ * Get station id
+ */
+void ath5k_hw_get_lladdr(struct ath_hw *hal, u8 *mac)
+{
+	AR5K_TRACE;
+	memcpy(mac, hal->ah_sta_id, ETH_ALEN);
+}
+
+/*
+ * Set station id
+ */
+int ath5k_hw_set_lladdr(struct ath_hw *hal, const u8 *mac)
+{
+	u32 low_id, high_id;
+
+	AR5K_TRACE;
+	/* Set new station ID */
+	memcpy(hal->ah_sta_id, mac, ETH_ALEN);
+
+	low_id = AR5K_LOW_ID(mac);
+	high_id = AR5K_HIGH_ID(mac);
+
+	ath5k_hw_reg_write(hal, low_id, AR5K_STA_ID0);
+	ath5k_hw_reg_write(hal, high_id, AR5K_STA_ID1);
+
+	return 0;
+}
+
+/*
+ * Set BSSID
+ */
+void ath5k_hw_set_associd(struct ath_hw *hal, const u8 *bssid, u16 assoc_id)
+{
+	u32 low_id, high_id;
+	u16 tim_offset = 0;
+
+	/*
+	 * Set simple BSSID mask on 5212
+	 */
+	if (hal->ah_version == AR5K_AR5212) {
+		ath5k_hw_reg_write(hal, 0xfffffff, AR5K_BSS_IDM0);
+		ath5k_hw_reg_write(hal, 0xfffffff, AR5K_BSS_IDM1);
+	}
+
+	/*
+	 * Set BSSID which triggers the "SME Join" operation
+	 */
+	low_id = AR5K_LOW_ID(bssid);
+	high_id = AR5K_HIGH_ID(bssid);
+	ath5k_hw_reg_write(hal, low_id, AR5K_BSS_ID0);
+	ath5k_hw_reg_write(hal, high_id | ((assoc_id & 0x3fff) <<
+				AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1);
+	memcpy(&hal->ah_bssid, bssid, ETH_ALEN);
+
+	if (assoc_id == 0) {
+		ath5k_hw_disable_pspoll(hal);
+		return;
+	}
+
+	AR5K_REG_WRITE_BITS(hal, AR5K_BEACON, AR5K_BEACON_TIM,
+			tim_offset ? tim_offset + 4 : 0);
+
+	ath5k_hw_enable_pspoll(hal, NULL, 0);
+}
+
+/*
+ * Set BSSID mask on 5212
+ */
+int ath5k_hw_set_bssid_mask(struct ath_hw *hal, const u8 *mask)
+{
+	u32 low_id, high_id;
+	AR5K_TRACE;
+
+	if (hal->ah_version == AR5K_AR5212) {
+		low_id = AR5K_LOW_ID(mask);
+		high_id = AR5K_HIGH_ID(mask);
+
+		ath5k_hw_reg_write(hal, low_id, AR5K_BSS_IDM0);
+		ath5k_hw_reg_write(hal, high_id, AR5K_BSS_IDM1);
+
+		return 0;
+	}
+
+	return -EIO;
+}
+
+/*
+ * Receive start/stop functions
+ */
+
+/*
+ * Start receive on PCU
+ */
+void ath5k_hw_start_rx_pcu(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	AR5K_REG_DISABLE_BITS(hal, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+}
+
+/*
+ * Stop receive on PCU
+ */
+void ath5k_hw_stop_pcu_recv(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	AR5K_REG_ENABLE_BITS(hal, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+}
+
+/*
+ * RX Filter functions
+ */
+
+/*
+ * Set multicast filter
+ */
+void ath5k_hw_set_mcast_filter(struct ath_hw *hal, u32 filter0, u32 filter1)
+{
+	AR5K_TRACE;
+	/* Set the multicat filter */
+	ath5k_hw_reg_write(hal, filter0, AR5K_MCAST_FILTER0);
+	ath5k_hw_reg_write(hal, filter1, AR5K_MCAST_FILTER1);
+}
+
+/*
+ * Set multicast filter by index
+ */
+int ath5k_hw_set_mcast_filterindex(struct ath_hw *hal, u32 index)
+{
+
+	AR5K_TRACE;
+	if (index >= 64)
+		return -EINVAL;
+	else if (index >= 32)
+		AR5K_REG_ENABLE_BITS(hal, AR5K_MCAST_FILTER1,
+				(1 << (index - 32)));
+	else
+		AR5K_REG_ENABLE_BITS(hal, AR5K_MCAST_FILTER0, (1 << index));
+
+	return 0;
+}
+
+/*
+ * Clear Multicast filter by index
+ */
+int ath5k_hw_clear_mcast_filter_idx(struct ath_hw *hal, u32 index)
+{
+
+	AR5K_TRACE;
+	if (index >= 64)
+		return -EINVAL;
+	else if (index >= 32)
+		AR5K_REG_DISABLE_BITS(hal, AR5K_MCAST_FILTER1,
+				(1 << (index - 32)));
+	else
+		AR5K_REG_DISABLE_BITS(hal, AR5K_MCAST_FILTER0, (1 << index));
+
+	return 0;
+}
+
+/*
+ * Get current rx filter
+ */
+u32 ath5k_hw_get_rx_filter(struct ath_hw *ah)
+{
+	u32 data, filter = 0;
+
+	AR5K_TRACE;
+	filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER);
+
+	/*Radar detection for 5212*/
+	if (ah->ah_version == AR5K_AR5212) {
+		data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL);
+
+		if (data & AR5K_PHY_ERR_FIL_RADAR)
+			filter |= AR5K_RX_FILTER_RADARERR;
+		if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK))
+			filter |= AR5K_RX_FILTER_PHYERR;
+	}
+
+	return filter;
+}
+
+/*
+ * Set rx filter
+ */
+void ath5k_hw_set_rx_filter(struct ath_hw *ah, u32 filter)
+{
+	u32 data = 0;
+
+	AR5K_TRACE;
+
+	/* Set PHY error filter register on 5212*/
+	if (ah->ah_version == AR5K_AR5212) {
+		if (filter & AR5K_RX_FILTER_RADARERR)
+			data |= AR5K_PHY_ERR_FIL_RADAR;
+		if (filter & AR5K_RX_FILTER_PHYERR)
+			data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK;
+	}
+
+	/*
+	 * The AR5210 uses promiscous mode to detect radar activity
+	 */
+	if (ah->ah_version == AR5K_AR5210 &&
+			(filter & AR5K_RX_FILTER_RADARERR)) {
+		filter &= ~AR5K_RX_FILTER_RADARERR;
+		filter |= AR5K_RX_FILTER_PROM;
+	}
+
+	/*Zero length DMA*/
+	if (data)
+		AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
+	else
+		AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
+
+	/*Write RX Filter register*/
+	ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER);
+
+	/*Write PHY error filter register on 5212*/
+	if (ah->ah_version == AR5K_AR5212)
+		ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL);
+
+}
+
+/*
+ * Beacon related functions
+ */
+
+/*
+ * Get a 32bit TSF
+ */
+u32 ath5k_hw_get_tsf32(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	return ath5k_hw_reg_read(hal, AR5K_TSF_L32);
+}
+
+/*
+ * Get the full 64bit TSF
+ */
+u64 ath5k_hw_get_tsf64(struct ath_hw *hal)
+{
+	u64 tsf = ath5k_hw_reg_read(hal, AR5K_TSF_U32);
+	AR5K_TRACE;
+
+	return ath5k_hw_reg_read(hal, AR5K_TSF_L32) | (tsf << 32);
+}
+
+/*
+ * Force a TSF reset
+ */
+void ath5k_hw_reset_tsf(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	AR5K_REG_ENABLE_BITS(hal, AR5K_BEACON, AR5K_BEACON_RESET_TSF);
+}
+
+/*
+ * Initialize beacon timers
+ */
+void ath5k_hw_init_beacon(struct ath_hw *hal, u32 next_beacon, u32 interval)
+{
+	u32 timer1, timer2, timer3;
+
+	AR5K_TRACE;
+	/*
+	 * Set the additional timers by mode
+	 */
+	switch (hal->ah_op_mode) {
+	case IEEE80211_IF_TYPE_STA:
+		if (hal->ah_version == AR5K_AR5210) {
+			timer1 = 0xffffffff;
+			timer2 = 0xffffffff;
+		} else {
+			timer1 = 0x0000ffff;
+			timer2 = 0x0007ffff;
+		}
+		break;
+
+	default:
+		timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) <<
+		    0x00000003;
+		timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) <<
+		    0x00000003;
+	}
+
+	timer3 = next_beacon + (hal->ah_atim_window ? hal->ah_atim_window : 1);
+
+	/*
+	 * Set the beacon register and enable all timers.
+	 * (next beacon, DMA beacon, software beacon, ATIM window time)
+	 */
+	ath5k_hw_reg_write(hal, next_beacon, AR5K_TIMER0);
+	ath5k_hw_reg_write(hal, timer1, AR5K_TIMER1);
+	ath5k_hw_reg_write(hal, timer2, AR5K_TIMER2);
+	ath5k_hw_reg_write(hal, timer3, AR5K_TIMER3);
+
+	ath5k_hw_reg_write(hal, interval & (AR5K_BEACON_PERIOD |
+			AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE),
+		AR5K_BEACON);
+}
+
+/*
+ * Set beacon timers
+ */
+int ath5k_hw_set_beacon_timers(struct ath_hw *hal,
+		const struct ath5k_beacon_state *state)
+{
+	u32 cfp_period, next_cfp, dtim, interval, next_beacon;
+
+	/*
+	 * TODO: should be changed through *state
+	 * review struct ath5k_beacon_state struct
+	 *
+	 * XXX: These are used for cfp period bellow, are they
+	 * ok ? Is it O.K. for tsf here to be 0 or should we use
+	 * get_tsf ?
+	 */
+	u32 dtim_count = 0; /* XXX */
+	u32 cfp_count = 0; /* XXX */
+	u32 tsf = 0; /* XXX */
+
+	AR5K_TRACE;
+	/* Return on an invalid beacon state */
+	if (state->bs_interval < 1)
+		return -EINVAL;
+
+	interval = state->bs_interval;
+	dtim = state->bs_dtim_period;
+
+	/*
+	 * PCF support?
+	 */
+	if (state->bs_cfp_period > 0) {
+		/*
+		 * Enable PCF mode and set the CFP
+		 * (Contention Free Period) and timer registers
+		 */
+		cfp_period = state->bs_cfp_period * state->bs_dtim_period *
+			state->bs_interval;
+		next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) *
+			state->bs_interval;
+
+		AR5K_REG_ENABLE_BITS(hal, AR5K_STA_ID1,
+				AR5K_STA_ID1_DEFAULT_ANTENNA |
+				AR5K_STA_ID1_PCF);
+		ath5k_hw_reg_write(hal, cfp_period, AR5K_CFP_PERIOD);
+		ath5k_hw_reg_write(hal, state->bs_cfp_max_duration,
+				AR5K_CFP_DUR);
+		ath5k_hw_reg_write(hal, (tsf + (next_cfp == 0 ? cfp_period :
+						next_cfp)) << 3, AR5K_TIMER2);
+	} else {
+		/* Disable PCF mode */
+		AR5K_REG_DISABLE_BITS(hal, AR5K_STA_ID1,
+				AR5K_STA_ID1_DEFAULT_ANTENNA |
+				AR5K_STA_ID1_PCF);
+	}
+
+	/*
+	 * Enable the beacon timer register
+	 */
+	ath5k_hw_reg_write(hal, state->bs_next_beacon, AR5K_TIMER0);
+
+	/*
+	 * Start the beacon timers
+	 */
+	ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, AR5K_BEACON) &~
+		(AR5K_BEACON_PERIOD | AR5K_BEACON_TIM)) |
+		AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0,
+		AR5K_BEACON_TIM) | AR5K_REG_SM(state->bs_interval,
+		AR5K_BEACON_PERIOD), AR5K_BEACON);
+
+	/*
+	 * Write new beacon miss threshold, if it appears to be valid
+	 * XXX: Figure out right values for min <= bs_bmiss_threshold <= max
+	 * and return if its not in range. We can test this by reading value and
+	 * setting value to a largest value and seeing which values register.
+	 */
+
+	AR5K_REG_WRITE_BITS(hal, AR5K_RSSI_THR, AR5K_RSSI_THR_BMISS,
+			state->bs_bmiss_threshold);
+
+	/*
+	 * Set sleep control register
+	 * XXX: Didn't find this in 5210 code but since this register
+	 * exists also in ar5k's 5210 headers i leave it as common code.
+	 */
+	AR5K_REG_WRITE_BITS(hal, AR5K_SLEEP_CTL, AR5K_SLEEP_CTL_SLDUR,
+			(state->bs_sleep_duration - 3) << 3);
+
+	/*
+	 * Set enhanced sleep registers on 5212
+	 */
+	if (hal->ah_version == AR5K_AR5212) {
+		if (state->bs_sleep_duration > state->bs_interval &&
+				roundup(state->bs_sleep_duration, interval) ==
+				state->bs_sleep_duration)
+			interval = state->bs_sleep_duration;
+
+		if (state->bs_sleep_duration > dtim && (dtim == 0 ||
+				roundup(state->bs_sleep_duration, dtim) ==
+				state->bs_sleep_duration))
+			dtim = state->bs_sleep_duration;
+
+		if (interval > dtim)
+			return -EINVAL;
+
+		next_beacon = interval == dtim ? state->bs_next_dtim :
+			state->bs_next_beacon;
+
+		ath5k_hw_reg_write(hal,
+			AR5K_REG_SM((state->bs_next_dtim - 3) << 3,
+			AR5K_SLEEP0_NEXT_DTIM) |
+			AR5K_REG_SM(10, AR5K_SLEEP0_CABTO) |
+			AR5K_SLEEP0_ENH_SLEEP_EN |
+			AR5K_SLEEP0_ASSUME_DTIM, AR5K_SLEEP0);
+
+		ath5k_hw_reg_write(hal, AR5K_REG_SM((next_beacon - 3) << 3,
+			AR5K_SLEEP1_NEXT_TIM) |
+			AR5K_REG_SM(10, AR5K_SLEEP1_BEACON_TO), AR5K_SLEEP1);
+
+		ath5k_hw_reg_write(hal,
+			AR5K_REG_SM(interval, AR5K_SLEEP2_TIM_PER) |
+			AR5K_REG_SM(dtim, AR5K_SLEEP2_DTIM_PER), AR5K_SLEEP2);
+	}
+
+	return 0;
+}
+
+/*
+ * Reset beacon timers
+ */
+void ath5k_hw_reset_beacon(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	/*
+	 * Disable beacon timer
+	 */
+	ath5k_hw_reg_write(hal, 0, AR5K_TIMER0);
+
+	/*
+	 * Disable some beacon register values
+	 */
+	AR5K_REG_DISABLE_BITS(hal, AR5K_STA_ID1,
+			AR5K_STA_ID1_DEFAULT_ANTENNA | AR5K_STA_ID1_PCF);
+	ath5k_hw_reg_write(hal, AR5K_BEACON_PERIOD, AR5K_BEACON);
+}
+
+/*
+ * Wait for beacon queue to finish
+ * TODO: This function's name is misleading, rename
+ */
+int ath5k_hw_wait_for_beacon(struct ath_hw *hal, unsigned long phys_addr)
+{
+	unsigned int i;
+	int ret;
+
+	AR5K_TRACE;
+
+	/* 5210 doesn't have QCU*/
+	if (hal->ah_version == AR5K_AR5210) {
+		/*
+		 * Wait for beaconn queue to finish by checking
+		 * Control Register and Beacon Status Register.
+		 */
+		for (i = AR5K_TUNE_BEACON_INTERVAL / 2; i > 0; i--) {
+			if (!(ath5k_hw_reg_read(hal, AR5K_BSR) & AR5K_BSR_TXQ1F)
+					||
+			    !(ath5k_hw_reg_read(hal, AR5K_CR) & AR5K_BSR_TXQ1F))
+				break;
+			udelay(10);
+		}
+
+		/* Timeout... */
+		if (i <= 0) {
+			/*
+			 * Re-schedule the beacon queue
+			 */
+			ath5k_hw_reg_write(hal, phys_addr, AR5K_NOQCU_TXDP1);
+			ath5k_hw_reg_write(hal, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
+					AR5K_BCR);
+
+			return -EIO;
+		}
+		ret = 0;
+	} else {
+	/*5211/5212*/
+		ret = ath5k_hw_register_timeout(hal,
+			AR5K_QUEUE_STATUS(AR5K_TX_QUEUE_ID_BEACON),
+			AR5K_QCU_STS_FRMPENDCNT, 0, false);
+
+		if (AR5K_REG_READ_Q(hal, AR5K_QCU_TXE, AR5K_TX_QUEUE_ID_BEACON))
+			return -EIO;
+	}
+
+	return ret;
+}
+
+/*
+ * Update mib counters (statistics)
+ */
+void ath5k_hw_update_mib_counters(struct ath_hw *hal,
+		struct ath5k_mib_stats *statistics)
+{
+	AR5K_TRACE;
+	/* Read-And-Clear */
+	statistics->ackrcv_bad += ath5k_hw_reg_read(hal, AR5K_ACK_FAIL);
+	statistics->rts_bad += ath5k_hw_reg_read(hal, AR5K_RTS_FAIL);
+	statistics->rts_good += ath5k_hw_reg_read(hal, AR5K_RTS_OK);
+	statistics->fcs_bad += ath5k_hw_reg_read(hal, AR5K_FCS_FAIL);
+	statistics->beacons += ath5k_hw_reg_read(hal, AR5K_BEACON_CNT);
+
+	/* Reset profile count registers on 5212*/
+	if (hal->ah_version == AR5K_AR5212) {
+		ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_TX);
+		ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_RX);
+		ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_RXCLR);
+		ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_CYCLE);
+	}
+}
+
+/*
+ * ACK/CTS Timeouts
+ */
+
+/*
+ * Set ACK timeout on PCU
+ */
+int ath5k_hw_set_ack_timeout(struct ath_hw *hal, unsigned int timeout)
+{
+	AR5K_TRACE;
+	if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK),
+			hal->ah_turbo) <= timeout)
+		return -EINVAL;
+
+	AR5K_REG_WRITE_BITS(hal, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK,
+		ath5k_hw_htoclock(timeout, hal->ah_turbo));
+
+	return 0;
+}
+
+/*
+ * Read the ACK timeout from PCU
+ */
+unsigned int ath5k_hw_get_ack_timeout(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+
+	return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(hal,
+			AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), hal->ah_turbo);
+}
+
+/*
+ * Set CTS timeout on PCU
+ */
+int ath5k_hw_set_cts_timeout(struct ath_hw *hal, unsigned int timeout)
+{
+	AR5K_TRACE;
+	if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS),
+			hal->ah_turbo) <= timeout)
+		return -EINVAL;
+
+	AR5K_REG_WRITE_BITS(hal, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS,
+			ath5k_hw_htoclock(timeout, hal->ah_turbo));
+
+	return 0;
+}
+
+/*
+ * Read CTS timeout from PCU
+ */
+unsigned int ath5k_hw_get_cts_timeout(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(hal,
+			AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), hal->ah_turbo);
+}
+
+/*
+ * Key table (WEP) functions
+ */
+
+int ath5k_hw_reset_key(struct ath_hw *hal, u16 entry)
+{
+	unsigned int i;
+
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
+
+	for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
+		ath5k_hw_reg_write(hal, 0, AR5K_KEYTABLE_OFF(entry, i));
+
+	/* Set NULL encryption on non-5210*/
+	if (hal->ah_version != AR5K_AR5210)
+		ath5k_hw_reg_write(hal, AR5K_KEYTABLE_TYPE_NULL,
+				AR5K_KEYTABLE_TYPE(entry));
+
+	return 0;
+}
+
+int ath5k_hw_is_key_valid(struct ath_hw *hal, u16 entry)
+{
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
+
+	/* Check the validation flag at the end of the entry */
+	return ath5k_hw_reg_read(hal, AR5K_KEYTABLE_MAC1(entry)) &
+		AR5K_KEYTABLE_VALID;
+}
+
+int ath5k_hw_set_key(struct ath_hw *hal, u16 entry,
+		const struct ieee80211_key_conf *key, const u8 *mac)
+{
+	unsigned int i;
+	__le32 key_v[5] = {};
+	u32 keytype;
+
+	AR5K_TRACE;
+
+	/* key->keylen comes in from mac80211 in bytes */
+
+	if (key->keylen > AR5K_KEYTABLE_SIZE / 8)
+		return -EOPNOTSUPP;
+
+	switch (key->keylen) {
+	/* WEP 40-bit   = 40-bit  entered key + 24 bit IV = 64-bit */
+	case 40 / 8:
+		memcpy(&key_v[0], key->key, 5);
+		keytype = AR5K_KEYTABLE_TYPE_40;
+		break;
+
+	/* WEP 104-bit  = 104-bit entered key + 24-bit IV = 128-bit */
+	case 104 / 8:
+		memcpy(&key_v[0], &key->key[0], 6);
+		memcpy(&key_v[2], &key->key[6], 6);
+		memcpy(&key_v[4], &key->key[12], 1);
+		keytype = AR5K_KEYTABLE_TYPE_104;
+		break;
+	/* WEP 128-bit  = 128-bit entered key + 24 bit IV = 152-bit */
+	case 128 / 8:
+		memcpy(&key_v[0], &key->key[0], 6);
+		memcpy(&key_v[2], &key->key[6], 6);
+		memcpy(&key_v[4], &key->key[12], 4);
+		keytype = AR5K_KEYTABLE_TYPE_128;
+		break;
+
+	default:
+		return -EINVAL; /* shouldn't happen */
+	}
+
+	for (i = 0; i < ARRAY_SIZE(key_v); i++)
+		ath5k_hw_reg_write(hal, le32_to_cpu(key_v[i]),
+				AR5K_KEYTABLE_OFF(entry, i));
+
+	ath5k_hw_reg_write(hal, keytype, AR5K_KEYTABLE_TYPE(entry));
+
+	return ath5k_hw_set_key_lladdr(hal, entry, mac);
+}
+
+int ath5k_hw_set_key_lladdr(struct ath_hw *hal, u16 entry, const u8 *mac)
+{
+	u32 low_id, high_id;
+
+	AR5K_TRACE;
+	 /* Invalid entry (key table overflow) */
+	AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
+
+	/* MAC may be NULL if it's a broadcast key. In this case no need to
+	 * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */
+	if (unlikely(mac == NULL)) {
+		low_id = 0xffffffff;
+		high_id = 0xffff | AR5K_KEYTABLE_VALID;
+	} else {
+		low_id = AR5K_LOW_ID(mac);
+		high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID;
+	}
+
+	ath5k_hw_reg_write(hal, low_id, AR5K_KEYTABLE_MAC0(entry));
+	ath5k_hw_reg_write(hal, high_id, AR5K_KEYTABLE_MAC1(entry));
+
+	return 0;
+}
+
+
+/********************************************\
+Queue Control Unit, DFS Control Unit Functions
+\********************************************/
+
+/*
+ * Initialize a transmit queue
+ */
+int ath5k_hw_setup_tx_queue(struct ath_hw *hal, enum ath5k_tx_queue queue_type,
+		struct ath5k_txq_info *queue_info)
+{
+	unsigned int queue;
+	int ret;
+
+	AR5K_TRACE;
+
+	/*
+	 * Get queue by type
+	 */
+	/*5210 only has 2 queues*/
+	if (hal->ah_version == AR5K_AR5210) {
+		switch (queue_type) {
+		case AR5K_TX_QUEUE_DATA:
+			queue = AR5K_TX_QUEUE_ID_NOQCU_DATA;
+			break;
+		case AR5K_TX_QUEUE_BEACON:
+		case AR5K_TX_QUEUE_CAB:
+			queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (queue_type) {
+		case AR5K_TX_QUEUE_DATA:
+			for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
+				hal->ah_txq[queue].tqi_type !=
+				AR5K_TX_QUEUE_INACTIVE; queue++) {
+
+				if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
+					return -EINVAL;
+			}
+			break;
+		case AR5K_TX_QUEUE_UAPSD:
+			queue = AR5K_TX_QUEUE_ID_UAPSD;
+			break;
+		case AR5K_TX_QUEUE_BEACON:
+			queue = AR5K_TX_QUEUE_ID_BEACON;
+			break;
+		case AR5K_TX_QUEUE_CAB:
+			queue = AR5K_TX_QUEUE_ID_CAB;
+			break;
+		case AR5K_TX_QUEUE_XR_DATA:
+			if (hal->ah_version != AR5K_AR5212)
+				AR5K_PRINTF("XR data queues only supported in "
+						"5212!\n");
+			queue = AR5K_TX_QUEUE_ID_XR_DATA;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * Setup internal queue structure
+	 */
+	memset(&hal->ah_txq[queue], 0, sizeof(struct ath5k_txq_info));
+	hal->ah_txq[queue].tqi_type = queue_type;
+
+	if (queue_info != NULL) {
+		queue_info->tqi_type = queue_type;
+		ret = ath5k_hw_setup_tx_queueprops(hal, queue, queue_info);
+		if (ret)
+			return ret;
+	}
+	/*
+	 * We use ah_txq_interrupts to hold a temp value for
+	 * the Secondary interrupt mask registers on 5211+
+	 * check out ath5k_hw_reset_tx_queue
+	 */
+	AR5K_Q_ENABLE_BITS(hal->ah_txq_interrupts, queue);
+
+	return queue;
+}
+
+/*
+ * Setup a transmit queue
+ */
+int ath5k_hw_setup_tx_queueprops(struct ath_hw *hal, int queue,
+				const struct ath5k_txq_info *queue_info)
+{
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+	if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return -EIO;
+
+	memcpy(&hal->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info));
+
+	/*XXX: Is this supported on 5210 ?*/
+	if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA &&
+			((queue_info->tqi_subtype == AR5K_WME_AC_VI) ||
+			(queue_info->tqi_subtype == AR5K_WME_AC_VO))) ||
+			queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD)
+		hal->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
+
+	return 0;
+}
+
+/*
+ * Get properties for a specific transmit queue
+ */
+int ath5k_hw_get_tx_queueprops(struct ath_hw *hal, int queue,
+		struct ath5k_txq_info *queue_info)
+{
+	AR5K_TRACE;
+	memcpy(queue_info, &hal->ah_txq[queue], sizeof(struct ath5k_txq_info));
+	return 0;
+}
+
+/*
+ * Set a transmit queue inactive
+ */
+void ath5k_hw_release_tx_queue(struct ath_hw *hal, unsigned int queue)
+{
+	AR5K_TRACE;
+	if (WARN_ON(queue >= hal->ah_capabilities.cap_queues.q_tx_num))
+		return;
+
+	/* This queue will be skipped in further operations */
+	hal->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE;
+	/*For SIMR setup*/
+	AR5K_Q_DISABLE_BITS(hal->ah_txq_interrupts, queue);
+}
+
+/*
+ * Set DFS params for a transmit queue
+ */
+int ath5k_hw_reset_tx_queue(struct ath_hw *hal, unsigned int queue)
+{
+	u32 cw_min, cw_max, retry_lg, retry_sh;
+	struct ath5k_txq_info *tq = &hal->ah_txq[queue];
+
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+	tq = &hal->ah_txq[queue];
+
+	if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return 0;
+
+	if (hal->ah_version == AR5K_AR5210) {
+		/* Only handle data queues, others will be ignored */
+		if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
+			return -EINVAL;
+
+		/* Set Slot time */
+		ath5k_hw_reg_write(hal, hal->ah_turbo == true ?
+			AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
+			AR5K_SLOT_TIME);
+		/* Set ACK_CTS timeout */
+		ath5k_hw_reg_write(hal, hal->ah_turbo == true ?
+			AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
+			AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
+		/* Set Transmit Latency */
+		ath5k_hw_reg_write(hal, hal->ah_turbo == true ?
+			AR5K_INIT_TRANSMIT_LATENCY_TURBO :
+			AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210);
+		/* Set IFS0 */
+		if (hal->ah_turbo == true)
+			 ath5k_hw_reg_write(hal, ((AR5K_INIT_SIFS_TURBO +
+				(hal->ah_aifs + tq->tqi_aifs) *
+				AR5K_INIT_SLOT_TIME_TURBO) <<
+				AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
+				AR5K_IFS0);
+		else
+			ath5k_hw_reg_write(hal, ((AR5K_INIT_SIFS +
+				(hal->ah_aifs + tq->tqi_aifs) *
+				AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) |
+				AR5K_INIT_SIFS, AR5K_IFS0);
+
+		/* Set IFS1 */
+		ath5k_hw_reg_write(hal, hal->ah_turbo == true ?
+			AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
+			AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
+		/* Set PHY register 0x9844 (??) */
+		ath5k_hw_reg_write(hal, hal->ah_turbo == true ?
+			(ath5k_hw_reg_read(hal, AR5K_PHY(17)) & ~0x7F) | 0x38 :
+			(ath5k_hw_reg_read(hal, AR5K_PHY(17)) & ~0x7F) | 0x1C,
+			AR5K_PHY(17));
+		/* Set Frame Control Register */
+		ath5k_hw_reg_write(hal, hal->ah_turbo == true ?
+			(AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE |
+			AR5K_PHY_TURBO_SHORT | 0x2020) :
+			(AR5K_PHY_FRAME_CTL_INI | 0x1020),
+			AR5K_PHY_FRAME_CTL_5210);
+	}
+
+	/*
+	 * Calculate cwmin/max by channel mode
+	 */
+	cw_min = hal->ah_cw_min = AR5K_TUNE_CWMIN;
+	cw_max = hal->ah_cw_max = AR5K_TUNE_CWMAX;
+	hal->ah_aifs = AR5K_TUNE_AIFS;
+	/*XR is only supported on 5212*/
+	if (IS_CHAN_XR(hal->ah_current_channel) &&
+			hal->ah_version == AR5K_AR5212) {
+		cw_min = hal->ah_cw_min = AR5K_TUNE_CWMIN_XR;
+		cw_max = hal->ah_cw_max = AR5K_TUNE_CWMAX_XR;
+		hal->ah_aifs = AR5K_TUNE_AIFS_XR;
+	/*B mode is not supported on 5210*/
+	} else if (IS_CHAN_B(hal->ah_current_channel) &&
+			hal->ah_version != AR5K_AR5210) {
+		cw_min = hal->ah_cw_min = AR5K_TUNE_CWMIN_11B;
+		cw_max = hal->ah_cw_max = AR5K_TUNE_CWMAX_11B;
+		hal->ah_aifs = AR5K_TUNE_AIFS_11B;
+	}
+
+	cw_min = 1;
+	while (cw_min < hal->ah_cw_min)
+		cw_min = (cw_min << 1) | 1;
+
+	cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) :
+		((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1);
+	cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) :
+		((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1);
+
+	/*
+	 * Calculate and set retry limits
+	 */
+	if (hal->ah_software_retry == true) {
+		/* XXX Need to test this */
+		retry_lg = hal->ah_limit_tx_retries;
+		retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
+			AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
+	} else {
+		retry_lg = AR5K_INIT_LG_RETRY;
+		retry_sh = AR5K_INIT_SH_RETRY;
+	}
+
+	/*No QCU/DCU [5210]*/
+	if (hal->ah_version == AR5K_AR5210) {
+		ath5k_hw_reg_write(hal,
+			(cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
+			| AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
+				AR5K_NODCU_RETRY_LMT_SLG_RETRY)
+			| AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
+				AR5K_NODCU_RETRY_LMT_SSH_RETRY)
+			| AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
+			| AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
+			AR5K_NODCU_RETRY_LMT);
+	} else {
+		/*QCU/DCU [5211+]*/
+		ath5k_hw_reg_write(hal,
+			AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
+				AR5K_DCU_RETRY_LMT_SLG_RETRY) |
+			AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
+				AR5K_DCU_RETRY_LMT_SSH_RETRY) |
+			AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
+			AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
+			AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
+
+	/*===Rest is also for QCU/DCU only [5211+]===*/
+
+		/*
+		 * Set initial content window (cw_min/cw_max)
+		 * and arbitrated interframe space (aifs)...
+		 */
+		ath5k_hw_reg_write(hal,
+			AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
+			AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
+			AR5K_REG_SM(hal->ah_aifs + tq->tqi_aifs,
+				AR5K_DCU_LCL_IFS_AIFS),
+			AR5K_QUEUE_DFS_LOCAL_IFS(queue));
+
+		/*
+		 * Set misc registers
+		 */
+		ath5k_hw_reg_write(hal, AR5K_QCU_MISC_DCU_EARLY,
+			AR5K_QUEUE_MISC(queue));
+
+		if (tq->tqi_cbr_period) {
+			ath5k_hw_reg_write(hal, AR5K_REG_SM(tq->tqi_cbr_period,
+				AR5K_QCU_CBRCFG_INTVAL) |
+				AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
+				AR5K_QCU_CBRCFG_ORN_THRES),
+				AR5K_QUEUE_CBRCFG(queue));
+			AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue),
+				AR5K_QCU_MISC_FRSHED_CBR);
+			if (tq->tqi_cbr_overflow_limit)
+				AR5K_REG_ENABLE_BITS(hal,
+					AR5K_QUEUE_MISC(queue),
+					AR5K_QCU_MISC_CBR_THRES_ENABLE);
+		}
+
+		if (tq->tqi_ready_time)
+			ath5k_hw_reg_write(hal, AR5K_REG_SM(tq->tqi_ready_time,
+				AR5K_QCU_RDYTIMECFG_INTVAL) |
+				AR5K_QCU_RDYTIMECFG_ENABLE,
+				AR5K_QUEUE_RDYTIMECFG(queue));
+
+		if (tq->tqi_burst_time) {
+			ath5k_hw_reg_write(hal, AR5K_REG_SM(tq->tqi_burst_time,
+				AR5K_DCU_CHAN_TIME_DUR) |
+				AR5K_DCU_CHAN_TIME_ENABLE,
+				AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
+
+			if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
+				AR5K_REG_ENABLE_BITS(hal,
+					AR5K_QUEUE_MISC(queue),
+					AR5K_QCU_MISC_TXE);
+		}
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
+			ath5k_hw_reg_write(hal, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
+				AR5K_QUEUE_DFS_MISC(queue));
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
+			ath5k_hw_reg_write(hal, AR5K_DCU_MISC_BACKOFF_FRAG,
+				AR5K_QUEUE_DFS_MISC(queue));
+
+		/*
+		 * Set registers by queue type
+		 */
+		switch (tq->tqi_type) {
+		case AR5K_TX_QUEUE_BEACON:
+			AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue),
+				AR5K_QCU_MISC_FRSHED_DBA_GT |
+				AR5K_QCU_MISC_CBREXP_BCN |
+				AR5K_QCU_MISC_BCN_ENABLE);
+
+			AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_DFS_MISC(queue),
+				(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
+				AR5K_DCU_MISC_ARBLOCK_CTL_S) |
+				AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
+				AR5K_DCU_MISC_BCN_ENABLE);
+
+			ath5k_hw_reg_write(hal, ((AR5K_TUNE_BEACON_INTERVAL -
+				(AR5K_TUNE_SW_BEACON_RESP -
+				AR5K_TUNE_DMA_BEACON_RESP) -
+				AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) |
+				AR5K_QCU_RDYTIMECFG_ENABLE,
+				AR5K_QUEUE_RDYTIMECFG(queue));
+			break;
+
+		case AR5K_TX_QUEUE_CAB:
+			AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue),
+				AR5K_QCU_MISC_FRSHED_DBA_GT |
+				AR5K_QCU_MISC_CBREXP |
+				AR5K_QCU_MISC_CBREXP_BCN);
+
+			AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_DFS_MISC(queue),
+				(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
+				AR5K_DCU_MISC_ARBLOCK_CTL_S));
+			break;
+
+		case AR5K_TX_QUEUE_UAPSD:
+			AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue),
+				AR5K_QCU_MISC_CBREXP);
+			break;
+
+		case AR5K_TX_QUEUE_DATA:
+		default:
+			break;
+		}
+
+		/*
+		 * Enable tx queue in the secondary interrupt mask registers
+		 */
+		ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txq_interrupts,
+			AR5K_SIMR0_QCU_TXOK) |
+			AR5K_REG_SM(hal->ah_txq_interrupts,
+			AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0);
+		ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txq_interrupts,
+			AR5K_SIMR1_QCU_TXERR), AR5K_SIMR1);
+		ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txq_interrupts,
+			AR5K_SIMR2_QCU_TXURN), AR5K_SIMR2);
+	}
+
+	return 0;
+}
+
+/*
+ * Get number of pending frames
+ * for a specific queue [5211+]
+ */
+u32 ath5k_hw_num_tx_pending(struct ath_hw *hal, unsigned int queue) {
+	AR5K_TRACE;
+	AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+	/* Return if queue is declared inactive */
+	if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return false;
+
+	/* XXX: How about AR5K_CFG_TXCNT ? */
+	if (hal->ah_version == AR5K_AR5210)
+		return false;
+
+	return AR5K_QUEUE_STATUS(queue) & AR5K_QCU_STS_FRMPENDCNT;
+}
+
+/*
+ * Set slot time
+ */
+int ath5k_hw_set_slot_time(struct ath_hw *hal, unsigned int slot_time)
+{
+	AR5K_TRACE;
+	if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX)
+		return -EINVAL;
+
+	if (hal->ah_version == AR5K_AR5210)
+		ath5k_hw_reg_write(hal, ath5k_hw_htoclock(slot_time,
+				hal->ah_turbo), AR5K_SLOT_TIME);
+	else
+		ath5k_hw_reg_write(hal, slot_time, AR5K_DCU_GBL_IFS_SLOT);
+
+	return 0;
+}
+
+/*
+ * Get slot time
+ */
+unsigned int ath5k_hw_get_slot_time(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	if (hal->ah_version == AR5K_AR5210)
+		return ath5k_hw_clocktoh(ath5k_hw_reg_read(hal,
+				AR5K_SLOT_TIME) & 0xffff, hal->ah_turbo);
+	else
+		return ath5k_hw_reg_read(hal, AR5K_DCU_GBL_IFS_SLOT) & 0xffff;
+}
+
+
+/******************************\
+ Hardware Descriptor Functions
+\******************************/
+
+/*
+ * TX Descriptor
+ */
+
+/*
+ * Initialize the 2-word tx descriptor on 5210/5211
+ */
+static int
+ath5k_hw_setup_2word_tx_desc(struct ath_hw *hal, struct ath_desc *desc,
+	unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type,
+	unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0,
+	unsigned int key_index, unsigned int antenna_mode, unsigned int flags,
+	unsigned int rtscts_rate, unsigned int rtscts_duration)
+{
+	u32 frame_type;
+	struct ath5k_hw_2w_tx_desc *tx_desc;
+
+	tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
+
+	if (tx_tries0 == 0)
+		return -EINVAL;
+
+	/* Initialize control descriptor */
+	tx_desc->tx_control_0 = 0;
+	tx_desc->tx_control_1 = 0;
+
+	/* Setup control descriptor */
+
+	/*Verify packet length*/
+	tx_desc->tx_control_0 = pkt_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
+	if (tx_desc->tx_control_0 != pkt_len)
+		return -EINVAL;
+	/*
+	 * Verify header length
+	 * XXX: I only found that on 5210 code, does it work on 5211 ?
+	 */
+	if (hal->ah_version == AR5K_AR5210) {
+		tx_desc->tx_control_0 = hdr_len &
+				AR5K_2W_TX_DESC_CTL0_HEADER_LEN;
+		if (tx_desc->tx_control_0 != hdr_len)
+			return -EINVAL;
+	}
+
+	/*Diferences between 5210-5211*/
+	if (hal->ah_version == AR5K_AR5210) {
+		switch (type) {
+		case AR5K_PKT_TYPE_BEACON:
+		case AR5K_PKT_TYPE_PROBE_RESP:
+			frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY;
+		case AR5K_PKT_TYPE_PIFS:
+			frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
+		default:
+			frame_type = type /*<< 2 ?*/;
+		}
+
+		tx_desc->tx_control_0 =
+			AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
+			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
+	} else {
+		tx_desc->tx_control_0 |=
+			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
+			AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
+		tx_desc->tx_control_1 =
+			AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE);
+	}
+#define _TX_FLAGS(_c, _flag)						\
+	if (flags & AR5K_TXDESC_##_flag)				\
+		tx_desc->tx_control_##_c |=				\
+			AR5K_2W_TX_DESC_CTL##_c##_##_flag
+
+	_TX_FLAGS(0, CLRDMASK);
+	_TX_FLAGS(0, VEOL);
+	_TX_FLAGS(0, INTREQ);
+	_TX_FLAGS(0, RTSENA);
+	_TX_FLAGS(1, NOACK);
+
+#undef _TX_FLAGS
+
+	/*
+	 * WEP crap
+	 */
+	if (key_index != AR5K_TXKEYIX_INVALID) {
+		tx_desc->tx_control_0 |=
+			AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
+		tx_desc->tx_control_1 |=
+			AR5K_REG_SM(key_index,
+			AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
+	}
+
+	/*
+	 * RTS/CTS Duration [5210 ?]
+	 */
+	if ((hal->ah_version == AR5K_AR5210) &&
+			(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
+		tx_desc->tx_control_1 |= rtscts_duration &
+				AR5K_2W_TX_DESC_CTL1_RTS_DURATION;
+
+	return 0;
+}
+
+/*
+ * Initialize the 4-word tx descriptor on 5212
+ */
+static int ath5k_hw_setup_4word_tx_desc(struct ath_hw *hal,
+	struct ath_desc *desc, unsigned int pkt_len, unsigned int hdr_len,
+	enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0,
+	unsigned int tx_tries0, unsigned int key_index,
+	unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate,
+	unsigned int rtscts_duration)
+{
+	struct ath5k_hw_4w_tx_desc *tx_desc;
+
+	AR5K_TRACE;
+
+	tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
+
+	/*
+	 * Validate input
+	 */
+	if (tx_tries0 == 0)
+		return -EINVAL;
+
+	/* Initialize status descriptor */
+	tx_desc->tx_control_0 = 0;
+	tx_desc->tx_control_1 = 0;
+	tx_desc->tx_control_2 = 0;
+	tx_desc->tx_control_3 = 0;
+
+	/* Setup status descriptor */
+	tx_desc->tx_control_0 = pkt_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
+	if (tx_desc->tx_control_0 != pkt_len)
+		return -EINVAL;
+
+	tx_desc->tx_control_0 |=
+		AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
+		AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
+	tx_desc->tx_control_1 = AR5K_REG_SM(type,
+					AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
+	tx_desc->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
+					AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
+	tx_desc->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
+
+#define _TX_FLAGS(_c, _flag)			\
+	if (flags & AR5K_TXDESC_##_flag)	\
+		tx_desc->tx_control_##_c |=	\
+			AR5K_4W_TX_DESC_CTL##_c##_##_flag
+
+	_TX_FLAGS(0, CLRDMASK);
+	_TX_FLAGS(0, VEOL);
+	_TX_FLAGS(0, INTREQ);
+	_TX_FLAGS(0, RTSENA);
+	_TX_FLAGS(0, CTSENA);
+	_TX_FLAGS(1, NOACK);
+
+#undef _TX_FLAGS
+
+	/*
+	 * WEP crap
+	 */
+	if (key_index != AR5K_TXKEYIX_INVALID) {
+		tx_desc->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
+		tx_desc->tx_control_1 |= AR5K_REG_SM(key_index,
+				AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
+	}
+
+	/*
+	 * RTS/CTS
+	 */
+	if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) {
+		if ((flags & AR5K_TXDESC_RTSENA) &&
+				(flags & AR5K_TXDESC_CTSENA))
+			return -EINVAL;
+		tx_desc->tx_control_2 |= rtscts_duration &
+				AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
+		tx_desc->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
+				AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize a 4-word XR tx descriptor on 5212
+ */
+static bool
+ath5k_hw_setup_xr_tx_desc(struct ath_hw *hal, struct ath_desc *desc,
+	unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2,
+	unsigned int tx_rate3, u_int tx_tries3)
+{
+	struct ath5k_hw_4w_tx_desc *tx_desc;
+
+	if (hal->ah_version == AR5K_AR5212) {
+		tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
+
+#define _XTX_TRIES(_n)							\
+	if (tx_tries##_n) {						\
+		tx_desc->tx_control_2 |=				\
+		    AR5K_REG_SM(tx_tries##_n,				\
+		    AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n);		\
+		tx_desc->tx_control_3 |=				\
+		    AR5K_REG_SM(tx_rate##_n,				\
+		    AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n);		\
+	}
+
+		_XTX_TRIES(1);
+		_XTX_TRIES(2);
+		_XTX_TRIES(3);
+
+#undef _XTX_TRIES
+
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Fill the 2-word tx descriptor on 5210/5211
+ */
+static int ath5k_hw_fill_2word_tx_desc(struct ath_hw *hal,
+	struct ath_desc *desc, unsigned int segment_length,
+	bool first_segment, bool last_segment)
+{
+	struct ath5k_hw_2w_tx_desc *tx_desc;
+
+	tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
+
+	/* Clear status descriptor */
+	memset(desc->ds_hw, 0, sizeof(desc->ds_hw));
+
+	/* Validate segment length and initialize the descriptor */
+	tx_desc->tx_control_1 = segment_length & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
+	if (tx_desc->tx_control_1 != segment_length)
+		return -EINVAL;
+
+	if (first_segment != true)
+		tx_desc->tx_control_0 &= ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
+
+	if (last_segment != true)
+		tx_desc->tx_control_1 |= AR5K_2W_TX_DESC_CTL1_MORE;
+
+	return 0;
+}
+
+/*
+ * Fill the 4-word tx descriptor on 5212
+ * XXX: Added an argument *last_desc -need revision
+ */
+static int ath5k_hw_fill_4word_tx_desc(struct ath_hw *hal,
+	struct ath_desc *desc, unsigned int segment_length,
+	bool first_segment, bool last_segment)
+{
+	struct ath5k_hw_4w_tx_desc *tx_desc;
+	struct ath5k_hw_tx_status *tx_status;
+
+	AR5K_TRACE;
+	tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
+	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
+
+	/* Clear status descriptor */
+	memset(tx_status, 0, sizeof(struct ath5k_hw_tx_status));
+
+	/* Validate segment length and initialize the descriptor */
+	tx_desc->tx_control_1 = segment_length & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
+	if (tx_desc->tx_control_1 != segment_length)
+		return -EINVAL;
+
+	if (first_segment != true)
+		tx_desc->tx_control_0 &= ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
+
+	if (last_segment != true)
+		tx_desc->tx_control_1 |= AR5K_4W_TX_DESC_CTL1_MORE;
+
+	return 0;
+}
+
+/*
+ * Proccess the tx status descriptor on 5210/5211
+ */
+static int ath5k_hw_proc_2word_tx_status(struct ath_hw *hal,
+		struct ath_desc *desc)
+{
+	struct ath5k_hw_tx_status *tx_status;
+	struct ath5k_hw_2w_tx_desc *tx_desc;
+
+	tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
+	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[0];
+
+	/* No frame has been send or error */
+	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
+		return -EINPROGRESS;
+
+	/*
+	 * Get descriptor status
+	 */
+	desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
+	desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
+	desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
+	/*TODO: desc->ds_us.tx.ts_virtcol + test*/
+	desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_SEQ_NUM);
+	desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
+	desc->ds_us.tx.ts_antenna = 1;
+	desc->ds_us.tx.ts_status = 0;
+	desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_0,
+		AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
+
+	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
+		if (tx_status->tx_status_0 &
+				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
+			desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
+			desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
+			desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
+	}
+
+	return 0;
+}
+
+/*
+ * Proccess a tx descriptor on 5212
+ */
+static int ath5k_hw_proc_4word_tx_status(struct ath_hw *hal,
+		struct ath_desc *desc)
+{
+	struct ath5k_hw_tx_status *tx_status;
+	struct ath5k_hw_4w_tx_desc *tx_desc;
+
+	AR5K_TRACE;
+	tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
+	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
+
+	/* No frame has been send or error */
+	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
+		return -EINPROGRESS;
+
+	/*
+	 * Get descriptor status
+	 */
+	desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
+	desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
+	desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
+	desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_SEQ_NUM);
+	desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
+	desc->ds_us.tx.ts_antenna = (tx_status->tx_status_1 &
+		AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
+	desc->ds_us.tx.ts_status = 0;
+
+	switch (AR5K_REG_MS(tx_status->tx_status_1,
+			AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) {
+	case 0:
+		desc->ds_us.tx.ts_rate = tx_desc->tx_control_3 &
+			AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
+		break;
+	case 1:
+		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
+			AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
+		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
+			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
+		break;
+	case 2:
+		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
+			AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
+		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
+			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
+		break;
+	case 3:
+		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
+			AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
+		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
+			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3);
+		break;
+	}
+
+	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
+		if (tx_status->tx_status_0 &
+				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
+			desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
+			desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
+			desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
+	}
+
+	return 0;
+}
+
+/*
+ * RX Descriptor
+ */
+
+/*
+ * Initialize an rx descriptor
+ */
+int ath5k_hw_setup_rx_desc(struct ath_hw *hal, struct ath_desc *desc,
+			u32 size, unsigned int flags)
+{
+	struct ath5k_rx_desc *rx_desc;
+
+	AR5K_TRACE;
+	rx_desc = (struct ath5k_rx_desc *)&desc->ds_ctl0;
+
+	/*
+	 *Clear ds_hw
+	 * If we don't clean the status descriptor,
+	 * while scanning we get too many results,
+	 * most of them virtual, after some secs
+	 * of scanning system hangs. M.F.
+	*/
+	memset(desc->ds_hw, 0, sizeof(desc->ds_hw));
+
+	/*Initialize rx descriptor*/
+	rx_desc->rx_control_0 = 0;
+	rx_desc->rx_control_1 = 0;
+
+	/* Setup descriptor */
+	rx_desc->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
+	if (unlikely(rx_desc->rx_control_1 != size))
+		return -EINVAL;
+
+	if (flags & AR5K_RXDESC_INTREQ)
+		rx_desc->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
+
+	return 0;
+}
+
+/*
+ * Proccess the rx status descriptor on 5210/5211
+ */
+static int ath5k_hw_proc_old_rx_status(struct ath_hw *hal,
+		struct ath_desc *desc)
+{
+	struct ath5k_hw_old_rx_status *rx_status;
+
+	rx_status = (struct ath5k_hw_old_rx_status *)&desc->ds_hw[0];
+
+	/* No frame received / not ready */
+	if (unlikely((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_DONE)
+				== 0))
+		return -EINPROGRESS;
+
+	/*
+	 * Frame receive status
+	 */
+	desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
+		AR5K_OLD_RX_DESC_STATUS0_DATA_LEN;
+	desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+	desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE);
+	desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
+		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA;
+	desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
+		AR5K_OLD_RX_DESC_STATUS0_MORE;
+	desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
+		AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+	desc->ds_us.rx.rs_status = 0;
+
+	/*
+	 * Key table status
+	 */
+	if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID)
+		desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
+			AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX);
+	else
+		desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
+
+	/*
+	 * Receive/descriptor errors
+	 */
+	if ((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK)
+			== 0) {
+		if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR)
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
+
+		if (rx_status->rx_status_1 &
+				AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN)
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_FIFO;
+
+		if (rx_status->rx_status_1 &
+				AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR) {
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
+			desc->ds_us.rx.rs_phyerr =
+				AR5K_REG_MS(rx_status->rx_status_1,
+					AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR);
+		}
+
+		if (rx_status->rx_status_1 &
+				AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
+	}
+
+	return 0;
+}
+
+/*
+ * Proccess the rx status descriptor on 5212
+ */
+static int ath5k_hw_proc_new_rx_status(struct ath_hw *hal,
+		struct ath_desc *desc)
+{
+	struct ath5k_hw_new_rx_status *rx_status;
+	struct ath5k_hw_rx_error *rx_err;
+
+	AR5K_TRACE;
+	rx_status = (struct ath5k_hw_new_rx_status *)&desc->ds_hw[0];
+
+	/* Overlay on error */
+	rx_err = (struct ath5k_hw_rx_error *)&desc->ds_hw[0];
+
+	/* No frame received / not ready */
+	if (unlikely((rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_DONE)
+				== 0))
+		return -EINPROGRESS;
+
+	/*
+	 * Frame receive status
+	 */
+	desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
+		AR5K_NEW_RX_DESC_STATUS0_DATA_LEN;
+	desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+	desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE);
+	desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
+		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA;
+	desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
+		AR5K_NEW_RX_DESC_STATUS0_MORE;
+	desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
+		AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+	desc->ds_us.rx.rs_status = 0;
+
+	/*
+	 * Key table status
+	 */
+	if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID)
+		desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
+				AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX);
+	else
+		desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
+
+	/*
+	 * Receive/descriptor errors
+	 */
+	if ((rx_status->rx_status_1 &
+			AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
+		if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR)
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
+
+		if (rx_status->rx_status_1 &
+				AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR) {
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
+			desc->ds_us.rx.rs_phyerr =
+				AR5K_REG_MS(rx_err->rx_error_1,
+					AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
+		}
+
+		if (rx_status->rx_status_1 &
+				AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
+
+		if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR)
+			desc->ds_us.rx.rs_status |= AR5K_RXERR_MIC;
+	}
+
+	return 0;
+}
+
+
+/****************\
+  GPIO Functions
+\****************/
+
+/*
+ * Set led state
+ */
+void ath5k_hw_set_ledstate(struct ath_hw *hal, unsigned int state)
+{
+	u32 led;
+	/*5210 has different led mode handling*/
+	u32 led_5210;
+
+	AR5K_TRACE;
+
+	/*Reset led status*/
+	if (hal->ah_version != AR5K_AR5210)
+		AR5K_REG_DISABLE_BITS(hal, AR5K_PCICFG,
+			AR5K_PCICFG_LEDMODE |  AR5K_PCICFG_LED);
+	else
+		AR5K_REG_DISABLE_BITS(hal, AR5K_PCICFG, AR5K_PCICFG_LED);
+
+	/*
+	 * Some blinking values, define at your wish
+	 */
+	switch (state) {
+	case AR5K_LED_SCAN:
+	case AR5K_LED_AUTH:
+		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND;
+		led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL;
+		break;
+
+	case AR5K_LED_INIT:
+		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE;
+		led_5210 = AR5K_PCICFG_LED_PEND;
+		break;
+
+	case AR5K_LED_ASSOC:
+	case AR5K_LED_RUN:
+		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC;
+		led_5210 = AR5K_PCICFG_LED_ASSOC;
+		break;
+
+	default:
+		led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE;
+		led_5210 = AR5K_PCICFG_LED_PEND;
+		break;
+	}
+
+	/*Write new status to the register*/
+	if (hal->ah_version != AR5K_AR5210)
+		AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, led);
+	else
+		AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, led_5210);
+}
+
+/*
+ * Set GPIO outputs
+ */
+int ath5k_hw_set_gpio_output(struct ath_hw *hal, u32 gpio)
+{
+	AR5K_TRACE;
+	if (gpio > AR5K_NUM_GPIO)
+		return -EINVAL;
+
+	ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, AR5K_GPIOCR) &~
+		AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR);
+
+	return 0;
+}
+
+/*
+ * Set GPIO inputs
+ */
+int ath5k_hw_set_gpio_input(struct ath_hw *hal, u32 gpio)
+{
+	AR5K_TRACE;
+	if (gpio > AR5K_NUM_GPIO)
+		return -EINVAL;
+
+	ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, AR5K_GPIOCR) &~
+		AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR);
+
+	return 0;
+}
+
+/*
+ * Get GPIO state
+ */
+u32 ath5k_hw_get_gpio(struct ath_hw *hal, u32 gpio)
+{
+	AR5K_TRACE;
+	if (gpio > AR5K_NUM_GPIO)
+		return 0xffffffff;
+
+	/* GPIO input magic */
+	return ((ath5k_hw_reg_read(hal, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
+		0x1;
+}
+
+/*
+ * Set GPIO state
+ */
+int ath5k_hw_set_gpio(struct ath_hw *hal, u32 gpio, u32 val)
+{
+	u32 data;
+	AR5K_TRACE;
+
+	if (gpio > AR5K_NUM_GPIO)
+		return -EINVAL;
+
+	/* GPIO output magic */
+	data = ath5k_hw_reg_read(hal, AR5K_GPIODO);
+
+	data &= ~(1 << gpio);
+	data |= (val & 1) << gpio;
+
+	ath5k_hw_reg_write(hal, data, AR5K_GPIODO);
+
+	return 0;
+}
+
+/*
+ * Initialize the GPIO interrupt (RFKill switch)
+ */
+void ath5k_hw_set_gpio_intr(struct ath_hw *hal, unsigned int gpio,
+		u32 interrupt_level)
+{
+	u32 data;
+
+	AR5K_TRACE;
+	if (gpio > AR5K_NUM_GPIO)
+		return;
+
+	/*
+	 * Set the GPIO interrupt
+	 */
+	data = (ath5k_hw_reg_read(hal, AR5K_GPIOCR) &
+		~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH |
+		AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) |
+		(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA);
+
+	ath5k_hw_reg_write(hal, interrupt_level ? data :
+		(data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR);
+
+	hal->ah_imr |= AR5K_IMR_GPIO;
+
+	/* Enable GPIO interrupts */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PIMR, AR5K_IMR_GPIO);
+}
+
+
+/*********************************\
+ Regulatory Domain/Channels Setup
+\*********************************/
+
+u16 ath5k_get_regdomain(struct ath_hw *hal)
+{
+	u16 regdomain;
+	enum ath5k_regdom ieee_regdomain;
+#ifdef COUNTRYCODE
+	u16 code;
+#endif
+
+	ath5k_eeprom_regulation_domain(hal, false, &ieee_regdomain);
+	hal->ah_capabilities.cap_regdomain.reg_hw = ieee_regdomain;
+
+#ifdef COUNTRYCODE
+	/*
+	 * Get the regulation domain by country code. This will ignore
+	 * the settings found in the EEPROM.
+	 */
+	code = ieee80211_name2countrycode(COUNTRYCODE);
+	ieee_regdomain = ieee80211_countrycode2regdomain(code);
+#endif
+
+	regdomain = ath5k_regdom_from_ieee(ieee_regdomain);
+	hal->ah_capabilities.cap_regdomain.reg_current = regdomain;
+
+	return regdomain;
+}
+
+
+
+/****************\
+  Misc functions
+\****************/
+
+void /*O.K.*/
+ath5k_hw_dump_state(struct ath_hw *hal)
+{
+#ifdef AR5K_DEBUG
+#define AR5K_PRINT_REGISTER(_x)						\
+	AR5K_PRINTF("(%s: %08x) ", #_x, ath5k_hw_reg_read(hal, AR5K_##_x));
+
+	AR5K_PRINT("MAC registers:\n");
+	AR5K_PRINT_REGISTER(CR);
+	AR5K_PRINT_REGISTER(CFG);
+	AR5K_PRINT_REGISTER(IER);
+	AR5K_PRINT_REGISTER(TXCFG);
+	AR5K_PRINT_REGISTER(RXCFG);
+	AR5K_PRINT_REGISTER(MIBC);
+	AR5K_PRINT_REGISTER(TOPS);
+	AR5K_PRINT_REGISTER(RXNOFRM);
+	AR5K_PRINT_REGISTER(RPGTO);
+	AR5K_PRINT_REGISTER(RFCNT);
+	AR5K_PRINT_REGISTER(MISC);
+	AR5K_PRINT_REGISTER(PISR);
+	AR5K_PRINT_REGISTER(SISR0);
+	AR5K_PRINT_REGISTER(SISR1);
+	AR5K_PRINT_REGISTER(SISR3);
+	AR5K_PRINT_REGISTER(SISR4);
+	AR5K_PRINT_REGISTER(DCM_ADDR);
+	AR5K_PRINT_REGISTER(DCM_DATA);
+	AR5K_PRINT_REGISTER(DCCFG);
+	AR5K_PRINT_REGISTER(CCFG);
+	AR5K_PRINT_REGISTER(CCFG_CUP);
+	AR5K_PRINT_REGISTER(CPC0);
+	AR5K_PRINT_REGISTER(CPC1);
+	AR5K_PRINT_REGISTER(CPC2);
+	AR5K_PRINT_REGISTER(CPCORN);
+	AR5K_PRINT_REGISTER(QCU_TXE);
+	AR5K_PRINT_REGISTER(QCU_TXD);
+	AR5K_PRINT_REGISTER(DCU_GBL_IFS_SIFS);
+	AR5K_PRINT_REGISTER(DCU_GBL_IFS_SLOT);
+	AR5K_PRINT_REGISTER(DCU_FP);
+	AR5K_PRINT_REGISTER(DCU_TXP);
+	AR5K_PRINT_REGISTER(DCU_TX_FILTER);
+	AR5K_PRINT_REGISTER(INTPEND);
+	AR5K_PRINT_REGISTER(PCICFG);
+	AR5K_PRINT_REGISTER(GPIOCR);
+	AR5K_PRINT_REGISTER(GPIODO);
+	AR5K_PRINT_REGISTER(SREV);
+	AR5K_PRINT_REGISTER(EEPROM_BASE);
+	AR5K_PRINT_REGISTER(EEPROM_DATA);
+	AR5K_PRINT_REGISTER(EEPROM_CMD);
+	AR5K_PRINT_REGISTER(EEPROM_CFG);
+	AR5K_PRINT_REGISTER(PCU_MIN);
+	AR5K_PRINT_REGISTER(STA_ID0);
+	AR5K_PRINT_REGISTER(STA_ID1);
+	AR5K_PRINT_REGISTER(BSS_ID0);
+	AR5K_PRINT_REGISTER(SLOT_TIME);
+	AR5K_PRINT_REGISTER(TIME_OUT);
+	AR5K_PRINT_REGISTER(RSSI_THR);
+	AR5K_PRINT_REGISTER(BEACON);
+	AR5K_PRINT_REGISTER(CFP_PERIOD);
+	AR5K_PRINT_REGISTER(TIMER0);
+	AR5K_PRINT_REGISTER(TIMER2);
+	AR5K_PRINT_REGISTER(TIMER3);
+	AR5K_PRINT_REGISTER(CFP_DUR);
+	AR5K_PRINT_REGISTER(MCAST_FILTER0);
+	AR5K_PRINT_REGISTER(MCAST_FILTER1);
+	AR5K_PRINT_REGISTER(DIAG_SW);
+	AR5K_PRINT_REGISTER(TSF_U32);
+	AR5K_PRINT_REGISTER(ADDAC_TEST);
+	AR5K_PRINT_REGISTER(DEFAULT_ANTENNA);
+	AR5K_PRINT_REGISTER(LAST_TSTP);
+	AR5K_PRINT_REGISTER(NAV);
+	AR5K_PRINT_REGISTER(RTS_OK);
+	AR5K_PRINT_REGISTER(ACK_FAIL);
+	AR5K_PRINT_REGISTER(FCS_FAIL);
+	AR5K_PRINT_REGISTER(BEACON_CNT);
+	AR5K_PRINT_REGISTER(TSF_PARM);
+	AR5K_PRINT("\n");
+
+	AR5K_PRINT("PHY registers:\n");
+	AR5K_PRINT_REGISTER(PHY_TURBO);
+	AR5K_PRINT_REGISTER(PHY_AGC);
+	AR5K_PRINT_REGISTER(PHY_TIMING_3);
+	AR5K_PRINT_REGISTER(PHY_CHIP_ID);
+	AR5K_PRINT_REGISTER(PHY_AGCCTL);
+	AR5K_PRINT_REGISTER(PHY_NF);
+	AR5K_PRINT_REGISTER(PHY_SCR);
+	AR5K_PRINT_REGISTER(PHY_SLMT);
+	AR5K_PRINT_REGISTER(PHY_SCAL);
+	AR5K_PRINT_REGISTER(PHY_RX_DELAY);
+	AR5K_PRINT_REGISTER(PHY_IQ);
+	AR5K_PRINT_REGISTER(PHY_PAPD_PROBE);
+	AR5K_PRINT_REGISTER(PHY_TXPOWER_RATE1);
+	AR5K_PRINT_REGISTER(PHY_TXPOWER_RATE2);
+	AR5K_PRINT_REGISTER(PHY_RADAR);
+	AR5K_PRINT_REGISTER(PHY_ANT_SWITCH_TABLE_0);
+	AR5K_PRINT_REGISTER(PHY_ANT_SWITCH_TABLE_1);
+	AR5K_PRINT("\n");
+#endif
+}
+
+int ath5k_hw_get_capability(struct ath_hw *hal,
+		enum ath5k_capability_type cap_type,
+		u32 capability, u32 *result)
+{
+	AR5K_TRACE;
+
+	switch (cap_type) {
+	case AR5K_CAP_NUM_TXQUEUES:
+		if (result) {
+			if (hal->ah_version == AR5K_AR5210)
+				*result = AR5K_NUM_TX_QUEUES_NOQCU;
+			else
+				*result = AR5K_NUM_TX_QUEUES;
+			goto yes;
+		}
+	case AR5K_CAP_VEOL:
+		goto yes;
+	case AR5K_CAP_COMPRESSION:
+		if (hal->ah_version == AR5K_AR5212)
+			goto yes;
+		else
+			goto no;
+	case AR5K_CAP_BURST:
+		goto yes;
+	case AR5K_CAP_TPC:
+		goto yes;
+	case AR5K_CAP_BSSIDMASK:
+		if (hal->ah_version == AR5K_AR5212)
+			goto yes;
+		else
+			goto no;
+	case AR5K_CAP_XR:
+		if (hal->ah_version == AR5K_AR5212)
+			goto yes;
+		else
+			goto no;
+	default:
+		goto no;
+	}
+
+no:
+	return -EINVAL;
+yes:
+	return 0;
+}
+
+static int ath5k_hw_enable_pspoll(struct ath_hw *hal, u8 *bssid,
+		u16 assoc_id)
+{
+	AR5K_TRACE;
+
+	if (hal->ah_version == AR5K_AR5210) {
+		AR5K_REG_DISABLE_BITS(hal, AR5K_STA_ID1,
+			AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
+		return 0;
+	}
+
+	return -EIO;
+}
+
+static int ath5k_hw_disable_pspoll(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+
+	if (hal->ah_version == AR5K_AR5210) {
+		AR5K_REG_ENABLE_BITS(hal, AR5K_STA_ID1,
+			AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
+		return 0;
+	}
+
+	return -EIO;
+}
diff -puN /dev/null drivers/net/wireless/ath5k/hw.h
--- /dev/null
+++ a/drivers/net/wireless/ath5k/hw.h
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007 Matthew W. S. Bell  <mentor@madwifi.org>
+ * Copyright (c) 2007 Luis Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/delay.h>
+
+/*
+ * Gain settings
+ */
+
+enum ath5k_rfgain {
+	AR5K_RFGAIN_INACTIVE = 0,
+	AR5K_RFGAIN_READ_REQUESTED,
+	AR5K_RFGAIN_NEED_CHANGE,
+};
+
+#define AR5K_GAIN_CRN_FIX_BITS_5111		4
+#define AR5K_GAIN_CRN_FIX_BITS_5112		7
+#define AR5K_GAIN_CRN_MAX_FIX_BITS		AR5K_GAIN_CRN_FIX_BITS_5112
+#define AR5K_GAIN_DYN_ADJUST_HI_MARGIN		15
+#define AR5K_GAIN_DYN_ADJUST_LO_MARGIN		20
+#define AR5K_GAIN_CCK_PROBE_CORR		5
+#define AR5K_GAIN_CCK_OFDM_GAIN_DELTA		15
+#define AR5K_GAIN_STEP_COUNT			10
+#define AR5K_GAIN_PARAM_TX_CLIP			0
+#define AR5K_GAIN_PARAM_PD_90			1
+#define AR5K_GAIN_PARAM_PD_84			2
+#define AR5K_GAIN_PARAM_GAIN_SEL		3
+#define AR5K_GAIN_PARAM_MIX_ORN			0
+#define AR5K_GAIN_PARAM_PD_138			1
+#define AR5K_GAIN_PARAM_PD_137			2
+#define AR5K_GAIN_PARAM_PD_136			3
+#define AR5K_GAIN_PARAM_PD_132			4
+#define AR5K_GAIN_PARAM_PD_131			5
+#define AR5K_GAIN_PARAM_PD_130			6
+#define AR5K_GAIN_CHECK_ADJUST(_g) 		\
+	((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high)
+
+struct ath5k_gain_opt_step {
+	s16				gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS];
+	s32				gos_gain;
+};
+
+struct ath5k_gain {
+	u32			g_step_idx;
+	u32			g_current;
+	u32			g_target;
+	u32			g_low;
+	u32			g_high;
+	u32			g_f_corr;
+	u32			g_active;
+	const struct ath5k_gain_opt_step	*g_step;
+};
+
+
+/*
+ * HW SPECIFIC STRUCTS
+ */
+
+/* Some EEPROM defines */
+#define AR5K_EEPROM_EEP_SCALE		100
+#define AR5K_EEPROM_EEP_DELTA		10
+#define AR5K_EEPROM_N_MODES		3
+#define AR5K_EEPROM_N_5GHZ_CHAN		10
+#define AR5K_EEPROM_N_2GHZ_CHAN		3
+#define AR5K_EEPROM_MAX_CHAN		10
+#define AR5K_EEPROM_N_PCDAC		11
+#define AR5K_EEPROM_N_TEST_FREQ		8
+#define AR5K_EEPROM_N_EDGES		8
+#define AR5K_EEPROM_N_INTERCEPTS	11
+#define AR5K_EEPROM_FREQ_M(_v)		AR5K_EEPROM_OFF(_v, 0x7f, 0xff)
+#define AR5K_EEPROM_PCDAC_M		0x3f
+#define AR5K_EEPROM_PCDAC_START		1
+#define AR5K_EEPROM_PCDAC_STOP		63
+#define AR5K_EEPROM_PCDAC_STEP		1
+#define AR5K_EEPROM_NON_EDGE_M		0x40
+#define AR5K_EEPROM_CHANNEL_POWER	8
+#define AR5K_EEPROM_N_OBDB		4
+#define AR5K_EEPROM_OBDB_DIS		0xffff
+#define AR5K_EEPROM_CHANNEL_DIS		0xff
+#define AR5K_EEPROM_SCALE_OC_DELTA(_x)	(((_x) * 2) / 10)
+#define AR5K_EEPROM_N_CTLS(_v)		AR5K_EEPROM_OFF(_v, 16, 32)
+#define AR5K_EEPROM_MAX_CTLS		32
+#define AR5K_EEPROM_N_XPD_PER_CHANNEL	4
+#define AR5K_EEPROM_N_XPD0_POINTS	4
+#define AR5K_EEPROM_N_XPD3_POINTS	3
+#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ	35
+#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ	55
+#define AR5K_EEPROM_POWER_M		0x3f
+#define AR5K_EEPROM_POWER_MIN		0
+#define AR5K_EEPROM_POWER_MAX		3150
+#define AR5K_EEPROM_POWER_STEP		50
+#define AR5K_EEPROM_POWER_TABLE_SIZE	64
+#define AR5K_EEPROM_N_POWER_LOC_11B	4
+#define AR5K_EEPROM_N_POWER_LOC_11G	6
+#define AR5K_EEPROM_I_GAIN		10
+#define AR5K_EEPROM_CCK_OFDM_DELTA	15
+#define AR5K_EEPROM_N_IQ_CAL		2
+
+/* Struct to hold EEPROM calibration data */
+struct ath5k_eeprom_info {
+	u16	ee_magic;
+	u16	ee_protect;
+	u16	ee_regdomain;
+	u16	ee_version;
+	u16	ee_header;
+	u16	ee_ant_gain;
+	u16	ee_misc0;
+	u16	ee_misc1;
+	u16	ee_cck_ofdm_gain_delta;
+	u16	ee_cck_ofdm_power_delta;
+	u16	ee_scaled_cck_delta;
+
+	/* Used for tx thermal adjustment (eeprom_init, rfregs) */
+	u16	ee_tx_clip;
+	u16	ee_pwd_84;
+	u16	ee_pwd_90;
+	u16	ee_gain_select;
+
+	/* RF Calibration settings (reset, rfregs) */
+	u16	ee_i_cal[AR5K_EEPROM_N_MODES];
+	u16	ee_q_cal[AR5K_EEPROM_N_MODES];
+	u16	ee_fixed_bias[AR5K_EEPROM_N_MODES];
+	u16	ee_turbo_max_power[AR5K_EEPROM_N_MODES];
+	u16	ee_xr_power[AR5K_EEPROM_N_MODES];
+	u16	ee_switch_settling[AR5K_EEPROM_N_MODES];
+	u16	ee_ant_tx_rx[AR5K_EEPROM_N_MODES];
+	u16	ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC];
+	u16	ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
+	u16	ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
+	u16	ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES];
+	u16	ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES];
+	u16	ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES];
+	u16	ee_thr_62[AR5K_EEPROM_N_MODES];
+	u16	ee_xlna_gain[AR5K_EEPROM_N_MODES];
+	u16	ee_xpd[AR5K_EEPROM_N_MODES];
+	u16	ee_x_gain[AR5K_EEPROM_N_MODES];
+	u16	ee_i_gain[AR5K_EEPROM_N_MODES];
+	u16	ee_margin_tx_rx[AR5K_EEPROM_N_MODES];
+
+	/* Unused */
+	u16	ee_false_detect[AR5K_EEPROM_N_MODES];
+	u16	ee_cal_pier[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_2GHZ_CHAN];
+	u16	ee_channel[AR5K_EEPROM_N_MODES][AR5K_EEPROM_MAX_CHAN]; /*empty*/
+
+	/* Conformance test limits (Unused) */
+	u16	ee_ctls;
+	u16	ee_ctl[AR5K_EEPROM_MAX_CTLS];
+
+	/* Noise Floor Calibration settings */
+	s16	ee_noise_floor_thr[AR5K_EEPROM_N_MODES];
+	s8	ee_adc_desired_size[AR5K_EEPROM_N_MODES];
+	s8	ee_pga_desired_size[AR5K_EEPROM_N_MODES];
+};
+
+/*
+ * Internal RX/TX descriptor structures
+ * (rX: reserved fields possibily used by future versions of the ar5k chipset)
+ */
+
+struct ath5k_rx_desc {
+	u32	rx_control_0; /* RX control word 0 */
+
+#define AR5K_DESC_RX_CTL0			0x00000000
+
+	u32	rx_control_1; /* RX control word 1 */
+
+#define AR5K_DESC_RX_CTL1_BUF_LEN		0x00000fff
+#define AR5K_DESC_RX_CTL1_INTREQ		0x00002000
+} __packed;
+
+/*
+ * 5210/5211 rx status descriptor
+ */
+struct ath5k_hw_old_rx_status {
+	u32	rx_status_0; /* RX status word 0 */
+
+#define AR5K_OLD_RX_DESC_STATUS0_DATA_LEN		0x00000fff
+#define AR5K_OLD_RX_DESC_STATUS0_MORE			0x00001000
+#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE		0x00078000
+#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE_S		15
+#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL		0x07f80000
+#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	19
+#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA	0x38000000
+#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	27
+
+	u32	rx_status_1; /* RX status word 1 */
+
+#define AR5K_OLD_RX_DESC_STATUS1_DONE			0x00000001
+#define AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
+#define AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR		0x00000004
+#define AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN		0x00000008
+#define AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000010
+#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR		0x000000e0
+#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR_S		5
+#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
+#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX		0x00007e00
+#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_S		9
+#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x0fff8000
+#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	15
+#define AR5K_OLD_RX_DESC_STATUS1_KEY_CACHE_MISS		0x10000000
+} __packed;
+
+/*
+ * 5212 rx status descriptor
+ */
+struct ath5k_hw_new_rx_status {
+	u32	rx_status_0; /* RX status word 0 */
+
+#define AR5K_NEW_RX_DESC_STATUS0_DATA_LEN		0x00000fff
+#define AR5K_NEW_RX_DESC_STATUS0_MORE			0x00001000
+#define AR5K_NEW_RX_DESC_STATUS0_DECOMP_CRC_ERROR	0x00002000
+#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE		0x000f8000
+#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE_S		15
+#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL		0x0ff00000
+#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	20
+#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA	0xf0000000
+#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	28
+
+	u32	rx_status_1; /* RX status word 1 */
+
+#define AR5K_NEW_RX_DESC_STATUS1_DONE			0x00000001
+#define AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
+#define AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR		0x00000004
+#define AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000008
+#define AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR		0x00000010
+#define AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR		0x00000020
+#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
+#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX		0x0000fe00
+#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_S		9
+#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x7fff0000
+#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	16
+#define AR5K_NEW_RX_DESC_STATUS1_KEY_CACHE_MISS		0x80000000
+} __packed;
+
+struct ath5k_hw_rx_error {
+	u32	rx_error_0; /* RX error word 0 */
+
+#define AR5K_RX_DESC_ERROR0			0x00000000
+
+	u32	rx_error_1; /* RX error word 1 */
+
+#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE	0x0000ff00
+#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE_S	8
+} __packed;
+
+#define AR5K_DESC_RX_PHY_ERROR_NONE		0x00
+#define AR5K_DESC_RX_PHY_ERROR_TIMING		0x20
+#define AR5K_DESC_RX_PHY_ERROR_PARITY		0x40
+#define AR5K_DESC_RX_PHY_ERROR_RATE		0x60
+#define AR5K_DESC_RX_PHY_ERROR_LENGTH		0x80
+#define AR5K_DESC_RX_PHY_ERROR_64QAM		0xa0
+#define AR5K_DESC_RX_PHY_ERROR_SERVICE		0xc0
+#define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR	0xe0
+
+struct ath5k_hw_2w_tx_desc {
+	u32	tx_control_0; /* TX control word 0 */
+
+#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
+#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN		0x0003f000 /*[5210 ?]*/
+#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_S	12
+#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE		0x003c0000
+#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S	18
+#define AR5K_2W_TX_DESC_CTL0_RTSENA		0x00400000
+#define AR5K_2W_TX_DESC_CTL0_CLRDMASK		0x01000000
+#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET	0x00800000 /*[5210]*/
+#define AR5K_2W_TX_DESC_CTL0_VEOL		0x00800000 /*[5211]*/
+#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE		0x1c000000 /*[5210]*/
+#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_S	26
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210	0x02000000
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211	0x1e000000
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT	(hal->ah_version == AR5K_AR5210 ? \
+						AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 : \
+						AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211)
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S	25
+#define AR5K_2W_TX_DESC_CTL0_INTREQ		0x20000000
+#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID	0x40000000
+
+	u32	tx_control_1; /* TX control word 1 */
+
+#define AR5K_2W_TX_DESC_CTL1_BUF_LEN		0x00000fff
+#define AR5K_2W_TX_DESC_CTL1_MORE		0x00001000
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210	0x0007e000
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211	0x000fe000
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX	(hal->ah_version == AR5K_AR5210 ? \
+						AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 : \
+						AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211)
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S	13
+#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE		0x00700000 /*[5211]*/
+#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_S	20
+#define AR5K_2W_TX_DESC_CTL1_NOACK		0x00800000 /*[5211]*/
+#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION	0xfff80000 /*[5210 ?]*/
+} __packed;
+
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL   0x00
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM     0x04
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL   0x08
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 0x0c
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS     0x10
+
+/*
+ * 5212 4-word tx control descriptor
+ */
+struct ath5k_hw_4w_tx_desc {
+	u32	tx_control_0; /* TX control word 0 */
+
+#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
+#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER		0x003f0000
+#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S	16
+#define AR5K_4W_TX_DESC_CTL0_RTSENA		0x00400000
+#define AR5K_4W_TX_DESC_CTL0_VEOL		0x00800000
+#define AR5K_4W_TX_DESC_CTL0_CLRDMASK		0x01000000
+#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT	0x1e000000
+#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S	25
+#define AR5K_4W_TX_DESC_CTL0_INTREQ		0x20000000
+#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID	0x40000000
+#define AR5K_4W_TX_DESC_CTL0_CTSENA		0x80000000
+
+	u32	tx_control_1; /* TX control word 1 */
+
+#define AR5K_4W_TX_DESC_CTL1_BUF_LEN		0x00000fff
+#define AR5K_4W_TX_DESC_CTL1_MORE		0x00001000
+#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX	0x000fe000
+#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S	13
+#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE		0x00f00000
+#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S	20
+#define AR5K_4W_TX_DESC_CTL1_NOACK		0x01000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_PROC		0x06000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S	25
+#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN	0x18000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S	27
+#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN	0x60000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S	29
+
+	u32	tx_control_2; /* TX control word 2 */
+
+#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION		0x00007fff
+#define AR5K_4W_TX_DESC_CTL2_DURATION_UPDATE_ENABLE	0x00008000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0		0x000f0000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S		16
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1		0x00f00000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S		20
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2		0x0f000000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S		24
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3		0xf0000000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S		28
+
+	u32	tx_control_3; /* TX control word 3 */
+
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0		0x0000001f
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1		0x000003e0
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S	5
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2		0x00007c00
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S	10
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3		0x000f8000
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S	15
+#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE	0x01f00000
+#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S	20
+} __packed;
+
+/*
+ * Common tx status descriptor
+ */
+struct ath5k_hw_tx_status {
+	u32	tx_status_0; /* TX status word 0 */
+
+#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK	0x00000001
+#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES	0x00000002
+#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN	0x00000004
+#define AR5K_DESC_TX_STATUS0_FILTERED		0x00000008
+/*???
+#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT	0x000000f0
+#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT_S	4
+*/
+#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT	0x000000f0
+#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S	4
+/*???
+#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT	0x00000f00
+#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT_S	8
+*/
+#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT	0x00000f00
+#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S	8
+#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT	0x0000f000
+#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT_S	12
+#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP	0xffff0000
+#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S	16
+
+	u32	tx_status_1; /* TX status word 1 */
+
+#define AR5K_DESC_TX_STATUS1_DONE		0x00000001
+#define AR5K_DESC_TX_STATUS1_SEQ_NUM		0x00001ffe
+#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S		1
+#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH	0x001fe000
+#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S	13
+#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX	0x00600000
+#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX_S	21
+#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS	0x00800000
+#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA	0x01000000
+} __packed;
+
+
+/*
+ * AR5K REGISTER ACCESS
+ */
+
+/*Swap RX/TX Descriptor for big endian archs*/
+#if defined(__BIG_ENDIAN)
+#define AR5K_INIT_CFG	(		\
+	AR5K_CFG_SWTD | AR5K_CFG_SWRD	\
+)
+#else
+#define AR5K_INIT_CFG	0x00000000
+#endif
+
+/*#define AR5K_REG_READ(_reg)	ath5k_hw_reg_read(hal, _reg)
+
+#define AR5K_REG_WRITE(_reg, _val)	ath5k_hw_reg_write(hal, _val, _reg)*/
+
+#define AR5K_REG_SM(_val, _flags)					\
+	(((_val) << _flags##_S) & (_flags))
+
+#define AR5K_REG_MS(_val, _flags)					\
+	(((_val) & (_flags)) >> _flags##_S)
+
+/* Some registers can hold multiple values of interest. For this
+ * reason when we want to write to these registers we must first
+ * retrieve the values which we do not want to clear (lets call this
+ * old_data) and then set the register with this and our new_value:
+ * ( old_data | new_value) */
+#define AR5K_REG_WRITE_BITS(hal, _reg, _flags, _val)			\
+	ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, _reg) & ~(_flags)) | \
+	    (((_val) << _flags##_S) & (_flags)), _reg)
+
+#define AR5K_REG_MASKED_BITS(hal, _reg, _flags, _mask)			\
+	ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, _reg) &		\
+			(_mask)) | (_flags), _reg)
+
+#define AR5K_REG_ENABLE_BITS(hal, _reg, _flags)				\
+	ath5k_hw_reg_write(hal, ath5k_hw_reg_read(hal, _reg) | (_flags), _reg)
+
+#define AR5K_REG_DISABLE_BITS(hal, _reg, _flags)			\
+	ath5k_hw_reg_write(hal, ath5k_hw_reg_read(hal, _reg) & ~(_flags), _reg)
+
+#define AR5K_PHY_WRITE(hal, _reg, _val)					\
+	ath5k_hw_reg_write(hal, _val, (hal)->ah_phy + ((_reg) << 2))
+
+#define AR5K_PHY_READ(hal, _reg)					\
+	ath5k_hw_reg_read(hal, (hal)->ah_phy + ((_reg) << 2))
+
+#define AR5K_REG_WAIT(_i) do {						\
+	if (_i % 64)							\
+		udelay(1);						\
+} while (0)
+
+#define AR5K_EEPROM_READ(_o, _v) do {					\
+	if ((ret = ath5k_hw_eeprom_read(hal, (_o), &(_v))) != 0)	\
+		return (ret);						\
+} while (0)
+
+#define AR5K_EEPROM_READ_HDR(_o, _v)					\
+	AR5K_EEPROM_READ(_o, hal->ah_capabilities.cap_eeprom._v);	\
+
+/* Read status of selected queue */
+#define AR5K_REG_READ_Q(hal, _reg, _queue)				\
+	(ath5k_hw_reg_read(hal, _reg) & (1 << _queue))			\
+
+#define AR5K_REG_WRITE_Q(hal, _reg, _queue)				\
+	ath5k_hw_reg_write(hal, (1 << _queue), _reg)
+
+#define AR5K_Q_ENABLE_BITS(_reg, _queue) do {				\
+	_reg |= 1 << _queue;						\
+} while (0)
+
+#define AR5K_Q_DISABLE_BITS(_reg, _queue) do {				\
+	_reg &= ~(1 << _queue);						\
+} while (0)
+
+#define AR5K_LOW_ID(_a)(				\
+(_a)[0] | (_a)[1] << 8 | (_a)[2] << 16 | (_a)[3] << 24	\
+)
+
+#define AR5K_HIGH_ID(_a)	((_a)[4] | (_a)[5] << 8)
+
+/*
+ * Initial register values
+ */
+
+/*
+ * Common initial register values
+ */
+#define AR5K_INIT_MODE				CHANNEL_B
+
+#define AR5K_INIT_TX_LATENCY			502
+#define AR5K_INIT_USEC				39
+#define AR5K_INIT_USEC_TURBO			79
+#define AR5K_INIT_USEC_32			31
+#define AR5K_INIT_CARR_SENSE_EN			1
+#define AR5K_INIT_PROG_IFS			920
+#define AR5K_INIT_PROG_IFS_TURBO		960
+#define AR5K_INIT_EIFS				3440
+#define AR5K_INIT_EIFS_TURBO			6880
+#define AR5K_INIT_SLOT_TIME			396
+#define AR5K_INIT_SLOT_TIME_TURBO		480
+#define AR5K_INIT_ACK_CTS_TIMEOUT		1024
+#define AR5K_INIT_ACK_CTS_TIMEOUT_TURBO		0x08000800
+#define AR5K_INIT_SIFS				560
+#define AR5K_INIT_SIFS_TURBO			480
+#define AR5K_INIT_SH_RETRY			10
+#define AR5K_INIT_LG_RETRY			AR5K_INIT_SH_RETRY
+#define AR5K_INIT_SSH_RETRY			32
+#define AR5K_INIT_SLG_RETRY			AR5K_INIT_SSH_RETRY
+#define AR5K_INIT_TX_RETRY			10
+#define AR5K_INIT_TOPS				8
+#define AR5K_INIT_RXNOFRM			8
+#define AR5K_INIT_RPGTO				0
+#define AR5K_INIT_TXNOFRM			0
+#define AR5K_INIT_BEACON_PERIOD			65535
+#define AR5K_INIT_TIM_OFFSET			0
+#define AR5K_INIT_BEACON_EN			0
+#define AR5K_INIT_RESET_TSF			0
+
+#define AR5K_INIT_TRANSMIT_LATENCY		(			\
+	(AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) |	\
+	(AR5K_INIT_USEC)						\
+)
+#define AR5K_INIT_TRANSMIT_LATENCY_TURBO	(			\
+	(AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) |	\
+	(AR5K_INIT_USEC_TURBO)						\
+)
+#define AR5K_INIT_PROTO_TIME_CNTRL		(			\
+	(AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS << 12) |	\
+	(AR5K_INIT_PROG_IFS)						\
+)
+#define AR5K_INIT_PROTO_TIME_CNTRL_TURBO	(			\
+	(AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS_TURBO << 12) | \
+	(AR5K_INIT_PROG_IFS_TURBO)					\
+)
+#define AR5K_INIT_BEACON_CONTROL		(			\
+	(AR5K_INIT_RESET_TSF << 24) | (AR5K_INIT_BEACON_EN << 23) |	\
+	(AR5K_INIT_TIM_OFFSET << 16) | (AR5K_INIT_BEACON_PERIOD)	\
+)
+
+/*
+ * Non-common initial register values which have to be loaded into the
+ * card at boot time and after each reset.
+ */
+
+/* Register dumps are done per operation mode */
+#define AR5K_INI_RFGAIN_5GHZ		0
+#define AR5K_INI_RFGAIN_2GHZ		1
+
+#define AR5K_INI_VAL_11A		0
+#define AR5K_INI_VAL_11A_TURBO		1
+#define AR5K_INI_VAL_11B		2
+#define AR5K_INI_VAL_11G		3
+#define AR5K_INI_VAL_11G_TURBO		4
+#define AR5K_INI_VAL_XR			0
+#define AR5K_INI_VAL_MAX		5
+
+#define AR5K_RF5111_INI_RF_MAX_BANKS	AR5K_MAX_RF_BANKS
+#define AR5K_RF5112_INI_RF_MAX_BANKS	AR5K_MAX_RF_BANKS
+
+static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits)
+{
+	u32 retval = 0, bit, i;
+
+	for (i = 0; i < bits; i++) {
+		bit = (val >> i) & 1;
+		retval = (retval << 1) | bit;
+	}
+
+	return retval;
+}
diff -puN /dev/null drivers/net/wireless/ath5k/initvals.c
--- /dev/null
+++ a/drivers/net/wireless/ath5k/initvals.c
@@ -0,0 +1,1102 @@
+/*
+ * Initial register settings functions
+ *
+ * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006, 2007 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ath5k.h"
+#include "reg.h"
+
+/*
+ * MAC/PHY REGISTERS
+ */
+
+
+/*
+ * Mode-independent initial register writes
+ */
+
+struct ath5k_ini {
+	u16	ini_register;
+	u32	ini_value;
+
+	enum {
+		AR5K_INI_WRITE = 0,	/* Default */
+		AR5K_INI_READ = 1,	/* Cleared on read */
+	} ini_mode;
+};
+
+/*
+ * Mode specific initial register values
+ */
+
+struct ath5k_ini_mode {
+	u16	mode_register;
+	u32	mode_value[5];
+};
+
+/* Initial register settings for AR5210 */
+static const struct ath5k_ini ar5210_ini[] = {
+	/* PCU and MAC registers */
+	{ AR5K_NOQCU_TXDP0,	0 },
+	{ AR5K_NOQCU_TXDP1,	0 },
+	{ AR5K_RXDP,		0 },
+	{ AR5K_CR,		0 },
+	{ AR5K_ISR,		0, AR5K_INI_READ },
+	{ AR5K_IMR,		0 },
+	{ AR5K_IER,		AR5K_IER_DISABLE },
+	{ AR5K_BSR,		0, AR5K_INI_READ },
+	{ AR5K_TXCFG,		AR5K_DMASIZE_128B },
+	{ AR5K_RXCFG,		AR5K_DMASIZE_128B },
+	{ AR5K_CFG,		AR5K_INIT_CFG },
+	{ AR5K_TOPS,		AR5K_INIT_TOPS },
+	{ AR5K_RXNOFRM,		AR5K_INIT_RXNOFRM },
+	{ AR5K_RPGTO,		AR5K_INIT_RPGTO },
+	{ AR5K_TXNOFRM,		AR5K_INIT_TXNOFRM },
+	{ AR5K_SFR,		0 },
+	{ AR5K_MIBC,		0 },
+	{ AR5K_MISC,		0 },
+	{ AR5K_RX_FILTER_5210,	0 },
+	{ AR5K_MCAST_FILTER0_5210, 0 },
+	{ AR5K_MCAST_FILTER1_5210, 0 },
+	{ AR5K_TX_MASK0,	0 },
+	{ AR5K_TX_MASK1,	0 },
+	{ AR5K_CLR_TMASK,	0 },
+	{ AR5K_TRIG_LVL,	AR5K_TUNE_MIN_TX_FIFO_THRES },
+	{ AR5K_DIAG_SW_5210,	0 },
+	{ AR5K_RSSI_THR,	AR5K_TUNE_RSSI_THRES },
+	{ AR5K_TSF_L32_5210,	0 },
+	{ AR5K_TIMER0_5210,	0 },
+	{ AR5K_TIMER1_5210,	0xffffffff },
+	{ AR5K_TIMER2_5210,	0xffffffff },
+	{ AR5K_TIMER3_5210,	1 },
+	{ AR5K_CFP_DUR_5210,	0 },
+	{ AR5K_CFP_PERIOD_5210,	0 },
+	/* PHY registers */
+	{ AR5K_PHY(0),	0x00000047 },
+	{ AR5K_PHY_AGC,	0x00000000 },
+	{ AR5K_PHY(3),	0x09848ea6 },
+	{ AR5K_PHY(4),	0x3d32e000 },
+	{ AR5K_PHY(5),	0x0000076b },
+	{ AR5K_PHY_ACT,	AR5K_PHY_ACT_DISABLE },
+	{ AR5K_PHY(8),	0x02020200 },
+	{ AR5K_PHY(9),	0x00000e0e },
+	{ AR5K_PHY(10),	0x0a020201 },
+	{ AR5K_PHY(11),	0x00036ffc },
+	{ AR5K_PHY(12),	0x00000000 },
+	{ AR5K_PHY(13),	0x00000e0e },
+	{ AR5K_PHY(14),	0x00000007 },
+	{ AR5K_PHY(15),	0x00020100 },
+	{ AR5K_PHY(16),	0x89630000 },
+	{ AR5K_PHY(17),	0x1372169c },
+	{ AR5K_PHY(18),	0x0018b633 },
+	{ AR5K_PHY(19),	0x1284613c },
+	{ AR5K_PHY(20),	0x0de8b8e0 },
+	{ AR5K_PHY(21),	0x00074859 },
+	{ AR5K_PHY(22),	0x7e80beba },
+	{ AR5K_PHY(23),	0x313a665e },
+	{ AR5K_PHY_AGCCTL, 0x00001d08 },
+	{ AR5K_PHY(25),	0x0001ce00 },
+	{ AR5K_PHY(26),	0x409a4190 },
+	{ AR5K_PHY(28),	0x0000000f },
+	{ AR5K_PHY(29),	0x00000080 },
+	{ AR5K_PHY(30),	0x00000004 },
+	{ AR5K_PHY(31),	0x00000018 }, 	/* 0x987c */
+	{ AR5K_PHY(64),	0x00000000 }, 	/* 0x9900 */
+	{ AR5K_PHY(65),	0x00000000 },
+	{ AR5K_PHY(66),	0x00000000 },
+	{ AR5K_PHY(67),	0x00800000 },
+	{ AR5K_PHY(68),	0x00000003 },
+	/* BB gain table (64bytes) */
+	{ AR5K_BB_GAIN(0), 0x00000000 },
+	{ AR5K_BB_GAIN(1), 0x00000020 },
+	{ AR5K_BB_GAIN(2), 0x00000010 },
+	{ AR5K_BB_GAIN(3), 0x00000030 },
+	{ AR5K_BB_GAIN(4), 0x00000008 },
+	{ AR5K_BB_GAIN(5), 0x00000028 },
+	{ AR5K_BB_GAIN(6), 0x00000028 },
+	{ AR5K_BB_GAIN(7), 0x00000004 },
+	{ AR5K_BB_GAIN(8), 0x00000024 },
+	{ AR5K_BB_GAIN(9), 0x00000014 },
+	{ AR5K_BB_GAIN(10), 0x00000034 },
+	{ AR5K_BB_GAIN(11), 0x0000000c },
+	{ AR5K_BB_GAIN(12), 0x0000002c },
+	{ AR5K_BB_GAIN(13), 0x00000002 },
+	{ AR5K_BB_GAIN(14), 0x00000022 },
+	{ AR5K_BB_GAIN(15), 0x00000012 },
+	{ AR5K_BB_GAIN(16), 0x00000032 },
+	{ AR5K_BB_GAIN(17), 0x0000000a },
+	{ AR5K_BB_GAIN(18), 0x0000002a },
+	{ AR5K_BB_GAIN(19), 0x00000001 },
+	{ AR5K_BB_GAIN(20), 0x00000021 },
+	{ AR5K_BB_GAIN(21), 0x00000011 },
+	{ AR5K_BB_GAIN(22), 0x00000031 },
+	{ AR5K_BB_GAIN(23), 0x00000009 },
+	{ AR5K_BB_GAIN(24), 0x00000029 },
+	{ AR5K_BB_GAIN(25), 0x00000005 },
+	{ AR5K_BB_GAIN(26), 0x00000025 },
+	{ AR5K_BB_GAIN(27), 0x00000015 },
+	{ AR5K_BB_GAIN(28), 0x00000035 },
+	{ AR5K_BB_GAIN(29), 0x0000000d },
+	{ AR5K_BB_GAIN(30), 0x0000002d },
+	{ AR5K_BB_GAIN(31), 0x00000003 },
+	{ AR5K_BB_GAIN(32), 0x00000023 },
+	{ AR5K_BB_GAIN(33), 0x00000013 },
+	{ AR5K_BB_GAIN(34), 0x00000033 },
+	{ AR5K_BB_GAIN(35), 0x0000000b },
+	{ AR5K_BB_GAIN(36), 0x0000002b },
+	{ AR5K_BB_GAIN(37), 0x00000007 },
+	{ AR5K_BB_GAIN(38), 0x00000027 },
+	{ AR5K_BB_GAIN(39), 0x00000017 },
+	{ AR5K_BB_GAIN(40), 0x00000037 },
+	{ AR5K_BB_GAIN(41), 0x0000000f },
+	{ AR5K_BB_GAIN(42), 0x0000002f },
+	{ AR5K_BB_GAIN(43), 0x0000002f },
+	{ AR5K_BB_GAIN(44), 0x0000002f },
+	{ AR5K_BB_GAIN(45), 0x0000002f },
+	{ AR5K_BB_GAIN(46), 0x0000002f },
+	{ AR5K_BB_GAIN(47), 0x0000002f },
+	{ AR5K_BB_GAIN(48), 0x0000002f },
+	{ AR5K_BB_GAIN(49), 0x0000002f },
+	{ AR5K_BB_GAIN(50), 0x0000002f },
+	{ AR5K_BB_GAIN(51), 0x0000002f },
+	{ AR5K_BB_GAIN(52), 0x0000002f },
+	{ AR5K_BB_GAIN(53), 0x0000002f },
+	{ AR5K_BB_GAIN(54), 0x0000002f },
+	{ AR5K_BB_GAIN(55), 0x0000002f },
+	{ AR5K_BB_GAIN(56), 0x0000002f },
+	{ AR5K_BB_GAIN(57), 0x0000002f },
+	{ AR5K_BB_GAIN(58), 0x0000002f },
+	{ AR5K_BB_GAIN(59), 0x0000002f },
+	{ AR5K_BB_GAIN(60), 0x0000002f },
+	{ AR5K_BB_GAIN(61), 0x0000002f },
+	{ AR5K_BB_GAIN(62), 0x0000002f },
+	{ AR5K_BB_GAIN(63), 0x0000002f },
+	/* 5110 RF gain table (64btes) */
+	{ AR5K_RF_GAIN(0), 0x0000001d },
+	{ AR5K_RF_GAIN(1), 0x0000005d },
+	{ AR5K_RF_GAIN(2), 0x0000009d },
+	{ AR5K_RF_GAIN(3), 0x000000dd },
+	{ AR5K_RF_GAIN(4), 0x0000011d },
+	{ AR5K_RF_GAIN(5), 0x00000021 },
+	{ AR5K_RF_GAIN(6), 0x00000061 },
+	{ AR5K_RF_GAIN(7), 0x000000a1 },
+	{ AR5K_RF_GAIN(8), 0x000000e1 },
+	{ AR5K_RF_GAIN(9), 0x00000031 },
+	{ AR5K_RF_GAIN(10), 0x00000071 },
+	{ AR5K_RF_GAIN(11), 0x000000b1 },
+	{ AR5K_RF_GAIN(12), 0x0000001c },
+	{ AR5K_RF_GAIN(13), 0x0000005c },
+	{ AR5K_RF_GAIN(14), 0x00000029 },
+	{ AR5K_RF_GAIN(15), 0x00000069 },
+	{ AR5K_RF_GAIN(16), 0x000000a9 },
+	{ AR5K_RF_GAIN(17), 0x00000020 },
+	{ AR5K_RF_GAIN(18), 0x00000019 },
+	{ AR5K_RF_GAIN(19), 0x00000059 },
+	{ AR5K_RF_GAIN(20), 0x00000099 },
+	{ AR5K_RF_GAIN(21), 0x00000030 },
+	{ AR5K_RF_GAIN(22), 0x00000005 },
+	{ AR5K_RF_GAIN(23), 0x00000025 },
+	{ AR5K_RF_GAIN(24), 0x00000065 },
+	{ AR5K_RF_GAIN(25), 0x000000a5 },
+	{ AR5K_RF_GAIN(26), 0x00000028 },
+	{ AR5K_RF_GAIN(27), 0x00000068 },
+	{ AR5K_RF_GAIN(28), 0x0000001f },
+	{ AR5K_RF_GAIN(29), 0x0000001e },
+	{ AR5K_RF_GAIN(30), 0x00000018 },
+	{ AR5K_RF_GAIN(31), 0x00000058 },
+	{ AR5K_RF_GAIN(32), 0x00000098 },
+	{ AR5K_RF_GAIN(33), 0x00000003 },
+	{ AR5K_RF_GAIN(34), 0x00000004 },
+	{ AR5K_RF_GAIN(35), 0x00000044 },
+	{ AR5K_RF_GAIN(36), 0x00000084 },
+	{ AR5K_RF_GAIN(37), 0x00000013 },
+	{ AR5K_RF_GAIN(38), 0x00000012 },
+	{ AR5K_RF_GAIN(39), 0x00000052 },
+	{ AR5K_RF_GAIN(40), 0x00000092 },
+	{ AR5K_RF_GAIN(41), 0x000000d2 },
+	{ AR5K_RF_GAIN(42), 0x0000002b },
+	{ AR5K_RF_GAIN(43), 0x0000002a },
+	{ AR5K_RF_GAIN(44), 0x0000006a },
+	{ AR5K_RF_GAIN(45), 0x000000aa },
+	{ AR5K_RF_GAIN(46), 0x0000001b },
+	{ AR5K_RF_GAIN(47), 0x0000001a },
+	{ AR5K_RF_GAIN(48), 0x0000005a },
+	{ AR5K_RF_GAIN(49), 0x0000009a },
+	{ AR5K_RF_GAIN(50), 0x000000da },
+	{ AR5K_RF_GAIN(51), 0x00000006 },
+	{ AR5K_RF_GAIN(52), 0x00000006 },
+	{ AR5K_RF_GAIN(53), 0x00000006 },
+	{ AR5K_RF_GAIN(54), 0x00000006 },
+	{ AR5K_RF_GAIN(55), 0x00000006 },
+	{ AR5K_RF_GAIN(56), 0x00000006 },
+	{ AR5K_RF_GAIN(57), 0x00000006 },
+	{ AR5K_RF_GAIN(58), 0x00000006 },
+	{ AR5K_RF_GAIN(59), 0x00000006 },
+	{ AR5K_RF_GAIN(60), 0x00000006 },
+	{ AR5K_RF_GAIN(61), 0x00000006 },
+	{ AR5K_RF_GAIN(62), 0x00000006 },
+	{ AR5K_RF_GAIN(63), 0x00000006 },
+	/* PHY activation */
+	{ AR5K_PHY(53), 0x00000020 },
+	{ AR5K_PHY(51), 0x00000004 },
+	{ AR5K_PHY(50), 0x00060106 },
+	{ AR5K_PHY(39), 0x0000006d },
+	{ AR5K_PHY(48), 0x00000000 },
+	{ AR5K_PHY(52), 0x00000014 },
+	{ AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE },
+};
+
+/* Initial register settings for AR5211 */
+static const struct ath5k_ini ar5211_ini[] = {
+	{ AR5K_RXDP,		0x00000000 },
+	{ AR5K_RTSD0,		0x84849c9c },
+	{ AR5K_RTSD1,		0x7c7c7c7c },
+	{ AR5K_RXCFG,		0x00000005 },
+	{ AR5K_MIBC,		0x00000000 },
+	{ AR5K_TOPS,		0x00000008 },
+	{ AR5K_RXNOFRM,		0x00000008 },
+	{ AR5K_TXNOFRM,		0x00000010 },
+	{ AR5K_RPGTO,		0x00000000 },
+	{ AR5K_RFCNT,		0x0000001f },
+	{ AR5K_QUEUE_TXDP(0),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(1),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(2),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(3),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(4),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(5),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(6),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(7),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(8),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(9),	0x00000000 },
+	{ AR5K_DCU_FP,		0x00000000 },
+	{ AR5K_STA_ID1,		0x00000000 },
+	{ AR5K_BSS_ID0,		0x00000000 },
+	{ AR5K_BSS_ID1,		0x00000000 },
+	{ AR5K_RSSI_THR,	0x00000000 },
+	{ AR5K_CFP_PERIOD_5211,	0x00000000 },
+	{ AR5K_TIMER0_5211,	0x00000030 },
+	{ AR5K_TIMER1_5211,	0x0007ffff },
+	{ AR5K_TIMER2_5211,	0x01ffffff },
+	{ AR5K_TIMER3_5211,	0x00000031 },
+	{ AR5K_CFP_DUR_5211,	0x00000000 },
+	{ AR5K_RX_FILTER_5211,	0x00000000 },
+	{ AR5K_MCAST_FILTER0_5211, 0x00000000 },
+	{ AR5K_MCAST_FILTER1_5211, 0x00000002 },
+	{ AR5K_DIAG_SW_5211,	0x00000000 },
+	{ AR5K_ADDAC_TEST,	0x00000000 },
+	{ AR5K_DEFAULT_ANTENNA,	0x00000000 },
+	/* PHY registers */
+	{ AR5K_PHY_AGC,	0x00000000 },
+	{ AR5K_PHY(3),	0x2d849093 },
+	{ AR5K_PHY(4),	0x7d32e000 },
+	{ AR5K_PHY(5),	0x00000f6b },
+	{ AR5K_PHY_ACT,	0x00000000 },
+	{ AR5K_PHY(11),	0x00026ffe },
+	{ AR5K_PHY(12),	0x00000000 },
+	{ AR5K_PHY(15),	0x00020100 },
+	{ AR5K_PHY(16),	0x206a017a },
+	{ AR5K_PHY(19),	0x1284613c },
+	{ AR5K_PHY(21),	0x00000859 },
+	{ AR5K_PHY(26),	0x409a4190 },	/* 0x9868 */
+	{ AR5K_PHY(27),	0x050cb081 },
+	{ AR5K_PHY(28),	0x0000000f },
+	{ AR5K_PHY(29),	0x00000080 },
+	{ AR5K_PHY(30),	0x0000000c },
+	{ AR5K_PHY(64),	0x00000000 },
+	{ AR5K_PHY(65),	0x00000000 },
+	{ AR5K_PHY(66),	0x00000000 },
+	{ AR5K_PHY(67),	0x00800000 },
+	{ AR5K_PHY(68),	0x00000001 },
+	{ AR5K_PHY(71),	0x0000092a },
+	{ AR5K_PHY_IQ,	0x00000000 },
+	{ AR5K_PHY(73),	0x00058a05 },
+	{ AR5K_PHY(74),	0x00000001 },
+	{ AR5K_PHY(75),	0x00000000 },
+	{ AR5K_PHY_PAPD_PROBE, 0x00000000 },
+	{ AR5K_PHY(77),	0x00000000 },	/* 0x9934 */
+	{ AR5K_PHY(78),	0x00000000 },	/* 0x9938 */
+	{ AR5K_PHY(79),	0x0000003f },	/* 0x993c */
+	{ AR5K_PHY(80),	0x00000004 },
+	{ AR5K_PHY(82),	0x00000000 },
+	{ AR5K_PHY(83),	0x00000000 },
+	{ AR5K_PHY(84),	0x00000000 },
+	{ AR5K_PHY_RADAR, 0x5d50f14c },
+	{ AR5K_PHY(86),	0x00000018 },
+	{ AR5K_PHY(87),	0x004b6a8e },
+	/* Power table (32bytes) */
+	{ AR5K_PHY_PCDAC_TXPOWER(1), 0x06ff05ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(2), 0x07ff07ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(3), 0x08ff08ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(4), 0x09ff09ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(5), 0x0aff0aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(6), 0x0bff0bff },
+	{ AR5K_PHY_PCDAC_TXPOWER(7), 0x0cff0cff },
+	{ AR5K_PHY_PCDAC_TXPOWER(8), 0x0dff0dff },
+	{ AR5K_PHY_PCDAC_TXPOWER(9), 0x0fff0eff },
+	{ AR5K_PHY_PCDAC_TXPOWER(10), 0x12ff12ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(11), 0x14ff13ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(12), 0x16ff15ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(13), 0x19ff17ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(14), 0x1bff1aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(15), 0x1eff1dff },
+	{ AR5K_PHY_PCDAC_TXPOWER(16), 0x23ff20ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(17), 0x27ff25ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(18), 0x2cff29ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(19), 0x31ff2fff },
+	{ AR5K_PHY_PCDAC_TXPOWER(20), 0x37ff34ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(21), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(22), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(23), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(24), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(25), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(26), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(27), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(28), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(29), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(30), 0x3aff3aff },
+	{ AR5K_PHY_PCDAC_TXPOWER(31), 0x3aff3aff },
+	{ AR5K_PHY_CCKTXCTL, 0x00000000 },
+	{ AR5K_PHY(642), 0x503e4646 },
+	{ AR5K_PHY_GAIN_2GHZ, 0x6480416c },
+	{ AR5K_PHY(644), 0x0199a003 },
+	{ AR5K_PHY(645), 0x044cd610 },
+	{ AR5K_PHY(646), 0x13800040 },
+	{ AR5K_PHY(647), 0x1be00060 },
+	{ AR5K_PHY(648), 0x0c53800a },
+	{ AR5K_PHY(649), 0x0014df3b },
+	{ AR5K_PHY(650), 0x000001b5 },
+	{ AR5K_PHY(651), 0x00000020 },
+};
+
+/* Initial mode-specific settings for AR5211
+ * XXX: how about gTurbo ? RF5111 supports it, how about AR5211 ?
+ */
+static const struct ath5k_ini_mode ar5211_ini_mode[] = {
+	{ AR5K_TXCFG,
+	/*	  a/XR	      aTurbo	  b	      g(OFDM?)	  gTurbo (N/A) */
+		{ 0x00000017, 0x00000017, 0x00000017, 0x00000017, 0x00000017 } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(0),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(1),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(2),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(3),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(4),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(5),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(6),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(7),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(8),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(9),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_DCU_GBL_IFS_SLOT,
+		{ 0x00000168, 0x000001e0, 0x000001b8, 0x00000168, 0x00000168 } },
+	{ AR5K_DCU_GBL_IFS_SIFS,
+		{ 0x00000230, 0x000001e0, 0x000000b0, 0x00000230, 0x00000230 } },
+	{ AR5K_DCU_GBL_IFS_EIFS,
+		{ 0x00000d98, 0x00001180, 0x00001f48, 0x00000d98, 0x00000d98 } },
+	{ AR5K_DCU_GBL_IFS_MISC,
+		{ 0x0000a0e0, 0x00014068, 0x00005880, 0x0000a0e0, 0x0000a0e0 } },
+	{ AR5K_TIME_OUT,
+		{ 0x04000400, 0x08000800, 0x20003000, 0x04000400, 0x04000400 } },
+	{ AR5K_USEC_5211,
+		{ 0x0e8d8fa7, 0x0e8d8fcf, 0x01608f95, 0x0e8d8fa7, 0x0e8d8fa7 } },
+	{ AR5K_PHY_TURBO,
+		{ 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x9820,
+		{ 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } },
+	{ 0x9824,
+		{ 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e, 0x00000e0e } },
+	{ 0x9828,
+		{ 0x0a020001, 0x0a020001, 0x05010000, 0x0a020001, 0x0a020001 } },
+	{ 0x9834,
+		{ 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ 0x9838,
+		{ 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } },
+	{ 0x9844,
+		{ 0x1372169c, 0x137216a5, 0x137216a8, 0x1372169c, 0x1372169c } },
+	{ 0x9848,
+		{ 0x0018ba67, 0x0018ba67, 0x0018ba69, 0x0018ba69, 0x0018ba69 } },
+	{ 0x9850,
+		{ 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0 } },
+	{ AR5K_PHY_SIG,
+		{ 0x7e800d2e, 0x7e800d2e, 0x7ec00d2e, 0x7e800d2e, 0x7e800d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+		{ 0x31375d5e, 0x31375d5e, 0x313a5d5e, 0x31375d5e, 0x31375d5e } },
+	{ AR5K_PHY_AGCCTL,
+		{ 0x0000bd10, 0x0000bd10, 0x0000bd38, 0x0000bd10, 0x0000bd10 } },
+	{ AR5K_PHY_NF,
+		{ 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } },
+	{ AR5K_PHY_RX_DELAY,
+		{ 0x00002710, 0x00002710, 0x0000157c, 0x00002710, 0x00002710 } },
+	{ 0x9918,
+		{ 0x00000190, 0x00000190, 0x00000084, 0x00000190, 0x00000190 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+		{ 0x6fe01020, 0x6fe01020, 0x6fe00920, 0x6fe01020, 0x6fe01020 } },
+	{ AR5K_PHY_PCDAC_TXPOWER(0),
+		{ 0x05ff14ff, 0x05ff14ff, 0x05ff14ff, 0x05ff19ff, 0x05ff19ff } },
+	{ AR5K_RF_BUFFER_CONTROL_4,
+		{ 0x00000010, 0x00000014, 0x00000010, 0x00000010, 0x00000010 } },
+};
+
+/* Initial register settings for AR5212 */
+static const struct ath5k_ini ar5212_ini[] = {
+	{ AR5K_RXDP,		0x00000000 },
+	{ AR5K_RXCFG,		0x00000005 },
+	{ AR5K_MIBC,		0x00000000 },
+	{ AR5K_TOPS,		0x00000008 },
+	{ AR5K_RXNOFRM,		0x00000008 },
+	{ AR5K_TXNOFRM,		0x00000010 },
+	{ AR5K_RPGTO,		0x00000000 },
+	{ AR5K_RFCNT,		0x0000001f },
+	{ AR5K_QUEUE_TXDP(0),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(1),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(2),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(3),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(4),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(5),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(6),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(7),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(8),	0x00000000 },
+	{ AR5K_QUEUE_TXDP(9),	0x00000000 },
+	{ AR5K_DCU_FP,		0x00000000 },
+	{ AR5K_DCU_TXP,		0x00000000 },
+	{ AR5K_DCU_TX_FILTER,	0x00000000 },
+	/* Unknown table */
+	{ 0x1078, 0x00000000 },
+	{ 0x10b8, 0x00000000 },
+	{ 0x10f8, 0x00000000 },
+	{ 0x1138, 0x00000000 },
+	{ 0x1178, 0x00000000 },
+	{ 0x11b8, 0x00000000 },
+	{ 0x11f8, 0x00000000 },
+	{ 0x1238, 0x00000000 },
+	{ 0x1278, 0x00000000 },
+	{ 0x12b8, 0x00000000 },
+	{ 0x12f8, 0x00000000 },
+	{ 0x1338, 0x00000000 },
+	{ 0x1378, 0x00000000 },
+	{ 0x13b8, 0x00000000 },
+	{ 0x13f8, 0x00000000 },
+	{ 0x1438, 0x00000000 },
+	{ 0x1478, 0x00000000 },
+	{ 0x14b8, 0x00000000 },
+	{ 0x14f8, 0x00000000 },
+	{ 0x1538, 0x00000000 },
+	{ 0x1578, 0x00000000 },
+	{ 0x15b8, 0x00000000 },
+	{ 0x15f8, 0x00000000 },
+	{ 0x1638, 0x00000000 },
+	{ 0x1678, 0x00000000 },
+	{ 0x16b8, 0x00000000 },
+	{ 0x16f8, 0x00000000 },
+	{ 0x1738, 0x00000000 },
+	{ 0x1778, 0x00000000 },
+	{ 0x17b8, 0x00000000 },
+	{ 0x17f8, 0x00000000 },
+	{ 0x103c, 0x00000000 },
+	{ 0x107c, 0x00000000 },
+	{ 0x10bc, 0x00000000 },
+	{ 0x10fc, 0x00000000 },
+	{ 0x113c, 0x00000000 },
+	{ 0x117c, 0x00000000 },
+	{ 0x11bc, 0x00000000 },
+	{ 0x11fc, 0x00000000 },
+	{ 0x123c, 0x00000000 },
+	{ 0x127c, 0x00000000 },
+	{ 0x12bc, 0x00000000 },
+	{ 0x12fc, 0x00000000 },
+	{ 0x133c, 0x00000000 },
+	{ 0x137c, 0x00000000 },
+	{ 0x13bc, 0x00000000 },
+	{ 0x13fc, 0x00000000 },
+	{ 0x143c, 0x00000000 },
+	{ 0x147c, 0x00000000 },
+	{ AR5K_STA_ID1,		0x00000000 },
+	{ AR5K_BSS_ID0,		0x00000000 },
+	{ AR5K_BSS_ID1,		0x00000000 },
+	{ AR5K_RSSI_THR,	0x00000000 },
+	{ AR5K_BEACON_5211,	0x00000000 },
+	{ AR5K_CFP_PERIOD_5211,	0x00000000 },
+	{ AR5K_TIMER0_5211,	0x00000030 },
+	{ AR5K_TIMER1_5211,	0x0007ffff },
+	{ AR5K_TIMER2_5211,	0x01ffffff },
+	{ AR5K_TIMER3_5211,	0x00000031 },
+	{ AR5K_CFP_DUR_5211,	0x00000000 },
+	{ AR5K_RX_FILTER_5211,	0x00000000 },
+	{ AR5K_DIAG_SW_5211,	0x00000000 },
+	{ AR5K_ADDAC_TEST,	0x00000000 },
+	{ AR5K_DEFAULT_ANTENNA,	0x00000000 },
+	{ 0x805c, 0xffffc7ff },
+	{ 0x8080, 0x00000000 },
+	{ AR5K_NAV_5211,	0x00000000 },
+	{ AR5K_RTS_OK_5211,	0x00000000 },
+	{ AR5K_RTS_FAIL_5211,	0x00000000 },
+	{ AR5K_ACK_FAIL_5211,	0x00000000 },
+	{ AR5K_FCS_FAIL_5211,	0x00000000 },
+	{ AR5K_BEACON_CNT_5211,	0x00000000 },
+	{ AR5K_XRMODE,		0x2a82301a },
+	{ AR5K_XRDELAY,		0x05dc01e0 },
+	{ AR5K_XRTIMEOUT,	0x1f402710 },
+	{ AR5K_XRCHIRP,		0x01f40000 },
+	{ AR5K_XRSTOMP,		0x00001e1c },
+	{ AR5K_SLEEP0,		0x0002aaaa },
+	{ AR5K_SLEEP1,		0x02005555 },
+	{ AR5K_SLEEP2,		0x00000000 },
+	{ AR5K_BSS_IDM0,	0xffffffff },
+	{ AR5K_BSS_IDM1,	0x0000ffff },
+	{ AR5K_TXPC,		0x00000000 },
+	{ AR5K_PROFCNT_TX,	0x00000000 },
+	{ AR5K_PROFCNT_RX,	0x00000000 },
+	{ AR5K_PROFCNT_RXCLR,	0x00000000 },
+	{ AR5K_PROFCNT_CYCLE,	0x00000000 },
+	{ 0x80fc, 0x00000088 },
+	{ AR5K_RATE_DUR(0),	0x00000000 },
+	{ AR5K_RATE_DUR(1),	0x0000008c },
+	{ AR5K_RATE_DUR(2),	0x000000e4 },
+	{ AR5K_RATE_DUR(3),	0x000002d5 },
+	{ AR5K_RATE_DUR(4),	0x00000000 },
+	{ AR5K_RATE_DUR(5),	0x00000000 },
+	{ AR5K_RATE_DUR(6),	0x000000a0 },
+	{ AR5K_RATE_DUR(7),	0x000001c9 },
+	{ AR5K_RATE_DUR(8),	0x0000002c },
+	{ AR5K_RATE_DUR(9),	0x0000002c },
+	{ AR5K_RATE_DUR(10),	0x00000030 },
+	{ AR5K_RATE_DUR(11),	0x0000003c },
+	{ AR5K_RATE_DUR(12),	0x0000002c },
+	{ AR5K_RATE_DUR(13),	0x0000002c },
+	{ AR5K_RATE_DUR(14),	0x00000030 },
+	{ AR5K_RATE_DUR(15),	0x0000003c },
+	{ AR5K_RATE_DUR(16),	0x00000000 },
+	{ AR5K_RATE_DUR(17),	0x00000000 },
+	{ AR5K_RATE_DUR(18),	0x00000000 },
+	{ AR5K_RATE_DUR(19),	0x00000000 },
+	{ AR5K_RATE_DUR(20),	0x00000000 },
+	{ AR5K_RATE_DUR(21),	0x00000000 },
+	{ AR5K_RATE_DUR(22),	0x00000000 },
+	{ AR5K_RATE_DUR(23),	0x00000000 },
+	{ AR5K_RATE_DUR(24),	0x000000d5 },
+	{ AR5K_RATE_DUR(25),	0x000000df },
+	{ AR5K_RATE_DUR(26),	0x00000102 },
+	{ AR5K_RATE_DUR(27),	0x0000013a },
+	{ AR5K_RATE_DUR(28),	0x00000075 },
+	{ AR5K_RATE_DUR(29),	0x0000007f },
+	{ AR5K_RATE_DUR(30),	0x000000a2 },
+	{ AR5K_RATE_DUR(31),	0x00000000 },
+	{ 0x8100, 0x00010002},
+	{ AR5K_TSF_PARM,	0x00000001 },
+	{ 0x8108, 0x000000c0 },
+	{ AR5K_PHY_ERR_FIL,	0x00000000 },
+	{ 0x8110, 0x00000168 },
+	{ 0x8114, 0x00000000 },
+	/* Some kind of table
+	 * also notice ...03<-02<-01<-00) */
+	{ 0x87c0, 0x03020100 },
+	{ 0x87c4, 0x07060504 },
+	{ 0x87c8, 0x0b0a0908 },
+	{ 0x87cc, 0x0f0e0d0c },
+	{ 0x87d0, 0x13121110 },
+	{ 0x87d4, 0x17161514 },
+	{ 0x87d8, 0x1b1a1918 },
+	{ 0x87dc, 0x1f1e1d1c },
+	/* loop ? */
+	{ 0x87e0, 0x03020100 },
+	{ 0x87e4, 0x07060504 },
+	{ 0x87e8, 0x0b0a0908 },
+	{ 0x87ec, 0x0f0e0d0c },
+	{ 0x87f0, 0x13121110 },
+	{ 0x87f4, 0x17161514 },
+	{ 0x87f8, 0x1b1a1918 },
+	{ 0x87fc, 0x1f1e1d1c },
+	/* PHY registers */
+	{ AR5K_PHY_AGC,	0x00000000 },
+	{ AR5K_PHY(3),	0xad848e19 },
+	{ AR5K_PHY(4),	0x7d28e000 },
+	{ AR5K_PHY_TIMING_3, 0x9c0a9f6b },
+	{ AR5K_PHY_ACT,	0x00000000 },
+	{ AR5K_PHY(11),	0x00022ffe },
+	{ AR5K_PHY(15),	0x00020100 },
+	{ AR5K_PHY(16),	0x206a017a },
+	{ AR5K_PHY(19),	0x1284613c },
+	{ AR5K_PHY(21),	0x00000859 },
+	{ AR5K_PHY(64),	0x00000000 },
+	{ AR5K_PHY(65),	0x00000000 },
+	{ AR5K_PHY(66),	0x00000000 },
+	{ AR5K_PHY(67),	0x00800000 },
+	{ AR5K_PHY(68),	0x00000001 },
+	{ AR5K_PHY(71),	0x0000092a },
+	{ AR5K_PHY_IQ,	0x05100000 },
+	{ AR5K_PHY(74), 0x00000001 },
+	{ AR5K_PHY(75), 0x00000004 },
+	{ AR5K_PHY_TXPOWER_RATE1, 0x1e1f2022 },
+	{ AR5K_PHY_TXPOWER_RATE2, 0x0a0b0c0d },
+	{ AR5K_PHY_TXPOWER_RATE_MAX, 0x0000003f },
+	{ AR5K_PHY(80), 0x00000004 },
+	{ AR5K_PHY(82), 0x9280b212 },
+	{ AR5K_PHY_RADAR, 0x5d50e188 },
+	{ AR5K_PHY(86),	0x000000ff },
+	{ AR5K_PHY(87),	0x004b6a8e },
+	{ AR5K_PHY(90),	0x000003ce },
+	{ AR5K_PHY(92),	0x192fb515 },
+	{ AR5K_PHY(93),	0x00000000 },
+	{ AR5K_PHY(94),	0x00000001 },
+	{ AR5K_PHY(95),	0x00000000 },
+	/* Power table (32bytes) */
+	{ AR5K_PHY_PCDAC_TXPOWER(1), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(2), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(3), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(4), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(5), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(6), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(7), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(8), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(9), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(10), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(11), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(12), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(13), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(14), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(15), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(16), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(17), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(18), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(19), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(20), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(21), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(22), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(23), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(24), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(25), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(26), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(27), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(28), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(29), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(30), 0x10ff10ff },
+	{ AR5K_PHY_PCDAC_TXPOWER(31), 0x10ff10ff },
+	{ AR5K_PHY(644), 0x0080a333 },
+	{ AR5K_PHY(645), 0x00206c10 },
+	{ AR5K_PHY(646), 0x009c4060 },
+	{ AR5K_PHY(647), 0x1483800a },
+	{ AR5K_PHY(648), 0x01831061 },
+	{ AR5K_PHY(649), 0x00000400 },
+	{ AR5K_PHY(650), 0x000001b5 },
+	{ AR5K_PHY(651), 0x00000000 },
+	{ AR5K_PHY_TXPOWER_RATE3, 0x20202020 },
+	{ AR5K_PHY_TXPOWER_RATE2, 0x20202020 },
+	{ AR5K_PHY(655), 0x13c889af },
+	{ AR5K_PHY(656), 0x38490a20 },
+	{ AR5K_PHY(657), 0x00007bb6 },
+	{ AR5K_PHY(658), 0x0fff3ffc },
+	{ AR5K_PHY_CCKTXCTL, 0x00000000 },
+};
+
+/* Initial mode-specific settings for AR5212 */
+static const struct ath5k_ini_mode ar5212_ini_mode[] = {
+	{ AR5K_TXCFG,
+	/*	  a/XR	      aTurbo	  b	      g (DYN)	  gTurbo */
+		{ 0x00008107, 0x00008107, 0x00008107, 0x00008107, 0x00008107 } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(0),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(1),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(2),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(3),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(4),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(5),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(6),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(7),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(8),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(9),
+		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_DCU_GBL_IFS_SIFS,
+		{ 0x00000230, 0x000001e0, 0x000000b0, 0x00000160, 0x000001e0 } },
+	{ AR5K_DCU_GBL_IFS_SLOT,
+		{ 0x00000168, 0x000001e0, 0x000001b8, 0x0000018c, 0x000001e0 } },
+	{ AR5K_DCU_GBL_IFS_EIFS,
+		{ 0x00000e60, 0x00001180, 0x00001f1c, 0x00003e38, 0x00001180 } },
+	{ AR5K_DCU_GBL_IFS_MISC,
+		{ 0x0000a0e0, 0x00014068, 0x00005880, 0x0000b0e0, 0x00014068 } },
+	{ AR5K_TIME_OUT,
+		{ 0x03e803e8, 0x06e006e0, 0x04200420, 0x08400840, 0x06e006e0 } },
+};
+
+/* Initial mode-specific settings for AR5212 + RF5111 */
+static const struct ath5k_ini_mode ar5212_rf5111_ini_mode[] = {
+	{ AR5K_USEC_5211,
+	/*	  a/XR	      aTurbo	  b	      g		  gTurbo */
+		{ 0x128d8fa7, 0x09880fcf, 0x04e00f95, 0x128d8fab, 0x09880fcf } },
+	{ AR5K_PHY_TURBO,
+		{ 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003 } },
+	{ 0x9820,
+		{ 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } },
+	{ 0x9824,
+		{ 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e, 0x00000e0e } },
+	{ 0x9828,
+		{ 0x0a020001, 0x0a020001, 0x05010100, 0x0a020001, 0x0a020001 } },
+	{ 0x9834,
+		{ 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ 0x9838,
+		{ 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } },
+	{ 0x9844,
+		{ 0x1372161c, 0x13721c25, 0x13721728, 0x137216a2, 0x13721c25 } },
+	{ 0x9848,
+		{ 0x0018da5a, 0x0018da5a, 0x0018ca69, 0x0018ca69, 0x0018ca69 } },
+	{ 0x9850,
+		{ 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } },
+	{ AR5K_PHY_SIG,
+		{ 0x7e800d2e, 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e, 0x7e800d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+		{ 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137615e } },
+	{ AR5K_PHY_AGCCTL,
+		{ 0x00009d10, 0x00009d10, 0x00009d18, 0x00009d10, 0x00009d10 } },
+	{ AR5K_PHY_NF,
+		{ 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } },
+	{ AR5K_PHY_ADCSAT,
+		{ 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 } },
+	{ 0x986c,
+		{ 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb080, 0x050cb080 } },
+	{ AR5K_PHY_RX_DELAY,
+		{ 0x00002710, 0x00002710, 0x0000157c, 0x00002af8, 0x00002710 } },
+	{ 0x9918,
+		{ 0x000001b8, 0x000001b8, 0x00000084, 0x00000108, 0x000001b8 } },
+	{ 0x9924,
+		{ 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+		{ 0xffb81020, 0xffb81020, 0xffb80d20, 0xffb81020, 0xffb81020 } },
+	{ AR5K_PHY_PCDAC_TXPOWER(0),
+		{ 0x10ff14ff, 0x10ff14ff, 0x10ff10ff, 0x10ff19ff, 0x10ff19ff } },
+	{ 0xa230,
+		{ 0x00000000, 0x00000000, 0x00000000, 0x00000108, 0x00000000 } },
+	{ 0xa208,
+		{ 0xd03e6788, 0xd03e6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
+};
+
+/* Initial mode-specific settings for AR5212 + RF5112 */
+static const struct ath5k_ini_mode ar5212_rf5112_ini_mode[] = {
+	{ AR5K_USEC_5211,
+	/*	  a/XR	      aTurbo	  b	      g		  gTurbo */
+		{ 0x128d93a7, 0x098813cf, 0x04e01395, 0x128d93ab, 0x098813cf } },
+	{ AR5K_PHY_TURBO,
+		{ 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003 } },
+	{ 0x9820,
+		{ 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } },
+	{ 0x9824,
+		{ 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ 0x9828,
+		{ 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } },
+	{ 0x9834,
+		{ 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ 0x9838,
+		{ 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } },
+	{ 0x9844,
+		{ 0x1372161c, 0x13721c25, 0x13721728, 0x137216a2, 0x13721c25 } },
+	{ 0x9848,
+		{ 0x0018da6d, 0x0018da6d, 0x0018ca75, 0x0018ca75, 0x0018ca75 } },
+	{ 0x9850,
+		{ 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } },
+	{ AR5K_PHY_SIG,
+		{ 0x7e800d2e, 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e, 0x7e800d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+		{ 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e } },
+	{ AR5K_PHY_AGCCTL,
+		{ 0x00009d10, 0x00009d10, 0x00009d18, 0x00009d10, 0x00009d10 } },
+	{ AR5K_PHY_NF,
+		{ 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } },
+	{ AR5K_PHY_ADCSAT,
+		{ 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 } },
+	{ 0x986c,
+		{ 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } },
+	{ AR5K_PHY_RX_DELAY,
+		{ 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } },
+	{ 0x9918,
+		{ 0x000001b8, 0x000001b8, 0x00000084, 0x00000108, 0x000001b8 } },
+	{ 0x9924,
+		{ 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+		{ 0xffb81020, 0xffb81020, 0xffb80d10, 0xffb81010, 0xffb81010 } },
+	{ AR5K_PHY_PCDAC_TXPOWER(0),
+		{ 0x10ff14ff, 0x10ff14ff, 0x10ff10ff, 0x10ff19ff, 0x10ff19ff } },
+	{ 0xa230,
+		{ 0x00000000, 0x00000000, 0x00000000, 0x00000108, 0x00000000 } },
+	{ AR5K_PHY_CCKTXCTL,
+		{ 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x00000004 } },
+	{ 0xa208,
+		{ 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
+	{ AR5K_PHY_GAIN_2GHZ,
+		{ 0x642c0140, 0x642c0140, 0x6442c160, 0x6442c160, 0x6442c160 } },
+};
+
+/*
+ * Initial BaseBand Gain settings for RF5111/5112 (only AR5210 comes with
+ * RF5110 so initial BB Gain settings are included in AR5K_AR5210_INI)
+ */
+
+/* RF5111 Initial BaseBand Gain settings */
+static const struct ath5k_ini rf5111_ini_bbgain[] = {
+	{ AR5K_BB_GAIN(0), 0x00000000 },
+	{ AR5K_BB_GAIN(1), 0x00000020 },
+	{ AR5K_BB_GAIN(2), 0x00000010 },
+	{ AR5K_BB_GAIN(3), 0x00000030 },
+	{ AR5K_BB_GAIN(4), 0x00000008 },
+	{ AR5K_BB_GAIN(5), 0x00000028 },
+	{ AR5K_BB_GAIN(6), 0x00000004 },
+	{ AR5K_BB_GAIN(7), 0x00000024 },
+	{ AR5K_BB_GAIN(8), 0x00000014 },
+	{ AR5K_BB_GAIN(9), 0x00000034 },
+	{ AR5K_BB_GAIN(10), 0x0000000c },
+	{ AR5K_BB_GAIN(11), 0x0000002c },
+	{ AR5K_BB_GAIN(12), 0x00000002 },
+	{ AR5K_BB_GAIN(13), 0x00000022 },
+	{ AR5K_BB_GAIN(14), 0x00000012 },
+	{ AR5K_BB_GAIN(15), 0x00000032 },
+	{ AR5K_BB_GAIN(16), 0x0000000a },
+	{ AR5K_BB_GAIN(17), 0x0000002a },
+	{ AR5K_BB_GAIN(18), 0x00000006 },
+	{ AR5K_BB_GAIN(19), 0x00000026 },
+	{ AR5K_BB_GAIN(20), 0x00000016 },
+	{ AR5K_BB_GAIN(21), 0x00000036 },
+	{ AR5K_BB_GAIN(22), 0x0000000e },
+	{ AR5K_BB_GAIN(23), 0x0000002e },
+	{ AR5K_BB_GAIN(24), 0x00000001 },
+	{ AR5K_BB_GAIN(25), 0x00000021 },
+	{ AR5K_BB_GAIN(26), 0x00000011 },
+	{ AR5K_BB_GAIN(27), 0x00000031 },
+	{ AR5K_BB_GAIN(28), 0x00000009 },
+	{ AR5K_BB_GAIN(29), 0x00000029 },
+	{ AR5K_BB_GAIN(30), 0x00000005 },
+	{ AR5K_BB_GAIN(31), 0x00000025 },
+	{ AR5K_BB_GAIN(32), 0x00000015 },
+	{ AR5K_BB_GAIN(33), 0x00000035 },
+	{ AR5K_BB_GAIN(34), 0x0000000d },
+	{ AR5K_BB_GAIN(35), 0x0000002d },
+	{ AR5K_BB_GAIN(36), 0x00000003 },
+	{ AR5K_BB_GAIN(37), 0x00000023 },
+	{ AR5K_BB_GAIN(38), 0x00000013 },
+	{ AR5K_BB_GAIN(39), 0x00000033 },
+	{ AR5K_BB_GAIN(40), 0x0000000b },
+	{ AR5K_BB_GAIN(41), 0x0000002b },
+	{ AR5K_BB_GAIN(42), 0x0000002b },
+	{ AR5K_BB_GAIN(43), 0x0000002b },
+	{ AR5K_BB_GAIN(44), 0x0000002b },
+	{ AR5K_BB_GAIN(45), 0x0000002b },
+	{ AR5K_BB_GAIN(46), 0x0000002b },
+	{ AR5K_BB_GAIN(47), 0x0000002b },
+	{ AR5K_BB_GAIN(48), 0x0000002b },
+	{ AR5K_BB_GAIN(49), 0x0000002b },
+	{ AR5K_BB_GAIN(50), 0x0000002b },
+	{ AR5K_BB_GAIN(51), 0x0000002b },
+	{ AR5K_BB_GAIN(52), 0x0000002b },
+	{ AR5K_BB_GAIN(53), 0x0000002b },
+	{ AR5K_BB_GAIN(54), 0x0000002b },
+	{ AR5K_BB_GAIN(55), 0x0000002b },
+	{ AR5K_BB_GAIN(56), 0x0000002b },
+	{ AR5K_BB_GAIN(57), 0x0000002b },
+	{ AR5K_BB_GAIN(58), 0x0000002b },
+	{ AR5K_BB_GAIN(59), 0x0000002b },
+	{ AR5K_BB_GAIN(60), 0x0000002b },
+	{ AR5K_BB_GAIN(61), 0x0000002b },
+	{ AR5K_BB_GAIN(62), 0x00000002 },
+	{ AR5K_BB_GAIN(63), 0x00000016 },
+};
+
+/* RF 5112 Initial BaseBand Gain settings */
+static const struct ath5k_ini rf5112_ini_bbgain[] = {
+	{ AR5K_BB_GAIN(0), 0x00000000 },
+	{ AR5K_BB_GAIN(1), 0x00000001 },
+	{ AR5K_BB_GAIN(2), 0x00000002 },
+	{ AR5K_BB_GAIN(3), 0x00000003 },
+	{ AR5K_BB_GAIN(4), 0x00000004 },
+	{ AR5K_BB_GAIN(5), 0x00000005 },
+	{ AR5K_BB_GAIN(6), 0x00000008 },
+	{ AR5K_BB_GAIN(7), 0x00000009 },
+	{ AR5K_BB_GAIN(8), 0x0000000a },
+	{ AR5K_BB_GAIN(9), 0x0000000b },
+	{ AR5K_BB_GAIN(10), 0x0000000c },
+	{ AR5K_BB_GAIN(11), 0x0000000d },
+	{ AR5K_BB_GAIN(12), 0x00000010 },
+	{ AR5K_BB_GAIN(13), 0x00000011 },
+	{ AR5K_BB_GAIN(14), 0x00000012 },
+	{ AR5K_BB_GAIN(15), 0x00000013 },
+	{ AR5K_BB_GAIN(16), 0x00000014 },
+	{ AR5K_BB_GAIN(17), 0x00000015 },
+	{ AR5K_BB_GAIN(18), 0x00000018 },
+	{ AR5K_BB_GAIN(19), 0x00000019 },
+	{ AR5K_BB_GAIN(20), 0x0000001a },
+	{ AR5K_BB_GAIN(21), 0x0000001b },
+	{ AR5K_BB_GAIN(22), 0x0000001c },
+	{ AR5K_BB_GAIN(23), 0x0000001d },
+	{ AR5K_BB_GAIN(24), 0x00000020 },
+	{ AR5K_BB_GAIN(25), 0x00000021 },
+	{ AR5K_BB_GAIN(26), 0x00000022 },
+	{ AR5K_BB_GAIN(27), 0x00000023 },
+	{ AR5K_BB_GAIN(28), 0x00000024 },
+	{ AR5K_BB_GAIN(29), 0x00000025 },
+	{ AR5K_BB_GAIN(30), 0x00000028 },
+	{ AR5K_BB_GAIN(31), 0x00000029 },
+	{ AR5K_BB_GAIN(32), 0x0000002a },
+	{ AR5K_BB_GAIN(33), 0x0000002b },
+	{ AR5K_BB_GAIN(34), 0x0000002c },
+	{ AR5K_BB_GAIN(35), 0x0000002d },
+	{ AR5K_BB_GAIN(36), 0x00000030 },
+	{ AR5K_BB_GAIN(37), 0x00000031 },
+	{ AR5K_BB_GAIN(38), 0x00000032 },
+	{ AR5K_BB_GAIN(39), 0x00000033 },
+	{ AR5K_BB_GAIN(40), 0x00000034 },
+	{ AR5K_BB_GAIN(41), 0x00000035 },
+	{ AR5K_BB_GAIN(42), 0x00000035 },
+	{ AR5K_BB_GAIN(43), 0x00000035 },
+	{ AR5K_BB_GAIN(44), 0x00000035 },
+	{ AR5K_BB_GAIN(45), 0x00000035 },
+	{ AR5K_BB_GAIN(46), 0x00000035 },
+	{ AR5K_BB_GAIN(47), 0x00000035 },
+	{ AR5K_BB_GAIN(48), 0x00000035 },
+	{ AR5K_BB_GAIN(49), 0x00000035 },
+	{ AR5K_BB_GAIN(50), 0x00000035 },
+	{ AR5K_BB_GAIN(51), 0x00000035 },
+	{ AR5K_BB_GAIN(52), 0x00000035 },
+	{ AR5K_BB_GAIN(53), 0x00000035 },
+	{ AR5K_BB_GAIN(54), 0x00000035 },
+	{ AR5K_BB_GAIN(55), 0x00000035 },
+	{ AR5K_BB_GAIN(56), 0x00000035 },
+	{ AR5K_BB_GAIN(57), 0x00000035 },
+	{ AR5K_BB_GAIN(58), 0x00000035 },
+	{ AR5K_BB_GAIN(59), 0x00000035 },
+	{ AR5K_BB_GAIN(60), 0x00000035 },
+	{ AR5K_BB_GAIN(61), 0x00000035 },
+	{ AR5K_BB_GAIN(62), 0x00000010 },
+	{ AR5K_BB_GAIN(63), 0x0000001a },
+};
+
+/*
+ * Write initial register dump
+ */
+static void ath5k_hw_ini_registers(struct ath_hw *hal, unsigned int size,
+		const struct ath5k_ini *ini_regs, bool change_channel)
+{
+	unsigned int i;
+
+	/* Write initial registers */
+	for (i = 0; i < size; i++) {
+		/* On channel change there is
+		 * no need to mess with PCU */
+		if (change_channel &&
+				ini_regs[i].ini_register >= AR5K_PCU_MIN &&
+				ini_regs[i].ini_register <= AR5K_PCU_MAX)
+			continue;
+
+		switch (ini_regs[i].ini_mode) {
+		case AR5K_INI_READ:
+			/* Cleared on read */
+			ath5k_hw_reg_read(hal, ini_regs[i].ini_register);
+			break;
+		case AR5K_INI_WRITE:
+		default:
+			AR5K_REG_WAIT(i);
+			ath5k_hw_reg_write(hal, ini_regs[i].ini_value,
+					ini_regs[i].ini_register);
+		}
+	}
+}
+
+static void ath5k_hw_ini_mode_registers(struct ath_hw *hal,
+		unsigned int size, const struct ath5k_ini_mode *ini_mode,
+		u8 mode)
+{
+	unsigned int i;
+
+	for (i = 0; i < size; i++) {
+		AR5K_REG_WAIT(i);
+		ath5k_hw_reg_write(hal, ini_mode[i].mode_value[mode],
+			(u32)ini_mode[i].mode_register);
+	}
+
+}
+
+int ath5k_hw_write_initvals(struct ath_hw *hal, u8 mode, bool change_channel)
+{
+	/*
+	 * Write initial mode-specific settings
+	 */
+	/*For 5212*/
+	if (hal->ah_version == AR5K_AR5212) {
+		ath5k_hw_ini_mode_registers(hal, ARRAY_SIZE(ar5212_ini_mode),
+				ar5212_ini_mode, mode);
+		if (hal->ah_radio == AR5K_RF5111)
+			ath5k_hw_ini_mode_registers(hal,
+					ARRAY_SIZE(ar5212_rf5111_ini_mode),
+					ar5212_rf5111_ini_mode, mode);
+		else if (hal->ah_radio == AR5K_RF5112)
+			ath5k_hw_ini_mode_registers(hal,
+					ARRAY_SIZE(ar5212_rf5112_ini_mode),
+					ar5212_rf5112_ini_mode, mode);
+	}
+	/*For 5211*/
+	if (hal->ah_version == AR5K_AR5211)
+		ath5k_hw_ini_mode_registers(hal, ARRAY_SIZE(ar5211_ini_mode),
+				ar5211_ini_mode, mode);
+	/* For 5210 mode settings check out ath5k_hw_reset_tx_queue */
+
+	/*
+	 * Write initial settings common for all modes
+	 */
+	/*For 5212*/
+	if (hal->ah_version == AR5K_AR5212) {
+		ath5k_hw_ini_registers(hal, ARRAY_SIZE(ar5212_ini),
+				ar5212_ini, change_channel);
+		if (hal->ah_radio == AR5K_RF5112) {
+			ath5k_hw_reg_write(hal, AR5K_PHY_PAPD_PROBE_INI_5112,
+					AR5K_PHY_PAPD_PROBE);
+			ath5k_hw_ini_registers(hal,
+					ARRAY_SIZE(rf5112_ini_bbgain),
+					rf5112_ini_bbgain, change_channel);
+		} else if (hal->ah_radio == AR5K_RF5111) {
+			ath5k_hw_reg_write(hal, AR5K_PHY_GAIN_2GHZ_INI_5111,
+					AR5K_PHY_GAIN_2GHZ);
+			ath5k_hw_reg_write(hal, AR5K_PHY_PAPD_PROBE_INI_5111,
+					AR5K_PHY_PAPD_PROBE);
+			ath5k_hw_ini_registers(hal,
+					ARRAY_SIZE(rf5111_ini_bbgain),
+					rf5111_ini_bbgain, change_channel);
+		}
+	} else if (hal->ah_version == AR5K_AR5211) {
+		ath5k_hw_ini_registers(hal, ARRAY_SIZE(ar5211_ini),
+				ar5211_ini, change_channel);
+		/* AR5211 only comes with 5111 */
+		ath5k_hw_ini_registers(hal, ARRAY_SIZE(rf5111_ini_bbgain),
+				rf5111_ini_bbgain, change_channel);
+	} else if (hal->ah_version == AR5K_AR5210) {
+		ath5k_hw_ini_registers(hal, ARRAY_SIZE(ar5210_ini),
+				ar5210_ini, change_channel);
+	}
+
+	return 0;
+}
diff -puN /dev/null drivers/net/wireless/ath5k/phy.c
--- /dev/null
+++ a/drivers/net/wireless/ath5k/phy.c
@@ -0,0 +1,1686 @@
+/*
+ * PHY functions
+ *
+ * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006, 2007 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/delay.h>
+
+#include "ath5k.h"
+#include "reg.h"
+
+/* Struct to hold initial RF register values (RF Banks) */
+struct ath5k_ini_rf {
+	u8	rf_bank;	/* check out ath5k_reg.h */
+	u16	rf_register;	/* register address */
+	u32	rf_value[5];	/* register value for different modes (above) */
+};
+
+/*
+ * Mode-specific RF Gain table (64bytes) for RF5111/5112
+ * (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial
+ * RF Gain values are included in AR5K_AR5210_INI)
+ */
+struct ath5k_ini_rfgain {
+	u16	rfg_register;	/* RF Gain register address */
+	u32	rfg_value[2];	/* [freq (see below)] */
+};
+
+struct ath5k_gain_opt {
+	u32			go_default;
+	u32			go_steps_count;
+	const struct ath5k_gain_opt_step	go_step[AR5K_GAIN_STEP_COUNT];
+};
+
+/* RF5111 mode-specific init registers */
+static const struct ath5k_ini_rf rfregs_5111[] = {
+	{ 0, 0x989c,
+	/*    mode a/XR   mode aTurbo mode b      mode g      mode gTurbo */
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00380000, 0x00380000, 0x00380000, 0x00380000, 0x00380000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x000000c0, 0x00000080, 0x00000080 } },
+	{ 0, 0x989c,
+	    { 0x000400f9, 0x000400f9, 0x000400ff, 0x000400fd, 0x000400fd } },
+	{ 0, 0x98d4,
+	    { 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x00000004 } },
+	{ 1, 0x98d4,
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d4,
+	    { 0x00000010, 0x00000014, 0x00000010, 0x00000010, 0x00000014 } },
+	{ 3, 0x98d8,
+	    { 0x00601068, 0x00601068, 0x00601068, 0x00601068, 0x00601068 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } },
+	{ 6, 0x989c,
+	    { 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x0a000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x003800c0, 0x00380080, 0x023800c0, 0x003800c0, 0x003800c0 } },
+	{ 6, 0x989c,
+	    { 0x00020006, 0x00020006, 0x00000006, 0x00020006, 0x00020006 } },
+	{ 6, 0x989c,
+	    { 0x00000089, 0x00000089, 0x00000089, 0x00000089, 0x00000089 } },
+	{ 6, 0x989c,
+	    { 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0 } },
+	{ 6, 0x989c,
+	    { 0x00040007, 0x00040007, 0x00040007, 0x00040007, 0x00040007 } },
+	{ 6, 0x98d4,
+	    { 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a } },
+	{ 7, 0x989c,
+	    { 0x00000040, 0x00000048, 0x00000040, 0x00000040, 0x00000040 } },
+	{ 7, 0x989c,
+	    { 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 } },
+	{ 7, 0x989c,
+	    { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } },
+	{ 7, 0x989c,
+	    { 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f } },
+	{ 7, 0x989c,
+	    { 0x000000f1, 0x000000f1, 0x00000061, 0x000000f1, 0x000000f1 } },
+	{ 7, 0x989c,
+	    { 0x0000904f, 0x0000904f, 0x0000904c, 0x0000904f, 0x0000904f } },
+	{ 7, 0x989c,
+	    { 0x0000125a, 0x0000125a, 0x0000129a, 0x0000125a, 0x0000125a } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000f, 0x0000000e, 0x0000000e } },
+};
+
+/* Initial RF Gain settings for RF5111 */
+static const struct ath5k_ini_rfgain rfgain_5111[] = {
+	/*			      5Ghz	2Ghz	*/
+	{ AR5K_RF_GAIN(0),	{ 0x000001a9, 0x00000000 } },
+	{ AR5K_RF_GAIN(1),	{ 0x000001e9, 0x00000040 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000029, 0x00000080 } },
+	{ AR5K_RF_GAIN(3),	{ 0x00000069, 0x00000150 } },
+	{ AR5K_RF_GAIN(4),	{ 0x00000199, 0x00000190 } },
+	{ AR5K_RF_GAIN(5),	{ 0x000001d9, 0x000001d0 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000019, 0x00000010 } },
+	{ AR5K_RF_GAIN(7),	{ 0x00000059, 0x00000044 } },
+	{ AR5K_RF_GAIN(8),	{ 0x00000099, 0x00000084 } },
+	{ AR5K_RF_GAIN(9),	{ 0x000001a5, 0x00000148 } },
+	{ AR5K_RF_GAIN(10),	{ 0x000001e5, 0x00000188 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000025, 0x000001c8 } },
+	{ AR5K_RF_GAIN(12),	{ 0x000001c8, 0x00000014 } },
+	{ AR5K_RF_GAIN(13),	{ 0x00000008, 0x00000042 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000048, 0x00000082 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000088, 0x00000178 } },
+	{ AR5K_RF_GAIN(16),	{ 0x00000198, 0x000001b8 } },
+	{ AR5K_RF_GAIN(17),	{ 0x000001d8, 0x000001f8 } },
+	{ AR5K_RF_GAIN(18),	{ 0x00000018, 0x00000012 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000058, 0x00000052 } },
+	{ AR5K_RF_GAIN(20),	{ 0x00000098, 0x00000092 } },
+	{ AR5K_RF_GAIN(21),	{ 0x000001a4, 0x0000017c } },
+	{ AR5K_RF_GAIN(22),	{ 0x000001e4, 0x000001bc } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000024, 0x000001fc } },
+	{ AR5K_RF_GAIN(24),	{ 0x00000064, 0x0000000a } },
+	{ AR5K_RF_GAIN(25),	{ 0x000000a4, 0x0000004a } },
+	{ AR5K_RF_GAIN(26),	{ 0x000000e4, 0x0000008a } },
+	{ AR5K_RF_GAIN(27),	{ 0x0000010a, 0x0000015a } },
+	{ AR5K_RF_GAIN(28),	{ 0x0000014a, 0x0000019a } },
+	{ AR5K_RF_GAIN(29),	{ 0x0000018a, 0x000001da } },
+	{ AR5K_RF_GAIN(30),	{ 0x000001ca, 0x0000000e } },
+	{ AR5K_RF_GAIN(31),	{ 0x0000000a, 0x0000004e } },
+	{ AR5K_RF_GAIN(32),	{ 0x0000004a, 0x0000008e } },
+	{ AR5K_RF_GAIN(33),	{ 0x0000008a, 0x0000015e } },
+	{ AR5K_RF_GAIN(34),	{ 0x000001ba, 0x0000019e } },
+	{ AR5K_RF_GAIN(35),	{ 0x000001fa, 0x000001de } },
+	{ AR5K_RF_GAIN(36),	{ 0x0000003a, 0x00000009 } },
+	{ AR5K_RF_GAIN(37),	{ 0x0000007a, 0x00000049 } },
+	{ AR5K_RF_GAIN(38),	{ 0x00000186, 0x00000089 } },
+	{ AR5K_RF_GAIN(39),	{ 0x000001c6, 0x00000179 } },
+	{ AR5K_RF_GAIN(40),	{ 0x00000006, 0x000001b9 } },
+	{ AR5K_RF_GAIN(41),	{ 0x00000046, 0x000001f9 } },
+	{ AR5K_RF_GAIN(42),	{ 0x00000086, 0x00000039 } },
+	{ AR5K_RF_GAIN(43),	{ 0x000000c6, 0x00000079 } },
+	{ AR5K_RF_GAIN(44),	{ 0x000000c6, 0x000000b9 } },
+	{ AR5K_RF_GAIN(45),	{ 0x000000c6, 0x000001bd } },
+	{ AR5K_RF_GAIN(46),	{ 0x000000c6, 0x000001fd } },
+	{ AR5K_RF_GAIN(47),	{ 0x000000c6, 0x0000003d } },
+	{ AR5K_RF_GAIN(48),	{ 0x000000c6, 0x0000007d } },
+	{ AR5K_RF_GAIN(49),	{ 0x000000c6, 0x000000bd } },
+	{ AR5K_RF_GAIN(50),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(51),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(52),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(53),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(54),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(55),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(56),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(57),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(58),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(59),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(60),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(61),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(62),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(63),	{ 0x000000c6, 0x000000fd } },
+};
+
+static const struct ath5k_gain_opt rfgain_opt_5111 = {
+	4,
+	9,
+	{
+		{ { 4, 1, 1, 1 }, 6 },
+		{ { 4, 0, 1, 1 }, 4 },
+		{ { 3, 1, 1, 1 }, 3 },
+		{ { 4, 0, 0, 1 }, 1 },
+		{ { 4, 1, 1, 0 }, 0 },
+		{ { 4, 0, 1, 0 }, -2 },
+		{ { 3, 1, 1, 0 }, -3 },
+		{ { 4, 0, 0, 0 }, -4 },
+		{ { 2, 1, 1, 0 }, -6 }
+	}
+};
+
+/* RF5112 mode-specific init registers */
+static const struct ath5k_ini_rf rfregs_5112[] = {
+	{ 1, 0x98d4,
+	/*    mode a/XR   mode aTurbo mode b      mode g      mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } },
+	{ 3, 0x98dc,
+	    { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } },
+	{ 6, 0x989c,
+	    { 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000 } },
+	{ 6, 0x989c,
+	    { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00660000, 0x00660000, 0x00660000, 0x00660000, 0x00660000 } },
+	{ 6, 0x989c,
+	    { 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000 } },
+	{ 6, 0x989c,
+	    { 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000 } },
+	{ 6, 0x989c,
+	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } },
+	{ 6, 0x989c,
+	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } },
+	{ 6, 0x989c,
+	    { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000 } },
+	{ 6, 0x989c,
+	    { 0x00600000, 0x00600000, 0x00600000, 0x00600000, 0x00600000 } },
+	{ 6, 0x989c,
+	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } },
+	{ 6, 0x989c,
+	    { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } },
+	{ 6, 0x989c,
+	    { 0x00640000, 0x00640000, 0x00640000, 0x00640000, 0x00640000 } },
+	{ 6, 0x989c,
+	    { 0x00200000, 0x00200000, 0x00200000, 0x00200000, 0x00200000 } },
+	{ 6, 0x989c,
+	    { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } },
+	{ 6, 0x989c,
+	    { 0x00250000, 0x00250000, 0x00250000, 0x00250000, 0x00250000 } },
+	{ 6, 0x989c,
+	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } },
+	{ 6, 0x989c,
+	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } },
+	{ 6, 0x989c,
+	    { 0x00510000, 0x00510000, 0x00510000, 0x00510000, 0x00510000 } },
+	{ 6, 0x989c,
+	    { 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000 } },
+	{ 6, 0x989c,
+	    { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } },
+	{ 6, 0x989c,
+	    { 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000 } },
+	{ 6, 0x989c,
+	    { 0x00400000, 0x00400000, 0x00400000, 0x00400000, 0x00400000 } },
+	{ 6, 0x989c,
+	    { 0x03090000, 0x03090000, 0x03090000, 0x03090000, 0x03090000 } },
+	{ 6, 0x989c,
+	    { 0x06000000, 0x06000000, 0x06000000, 0x06000000, 0x06000000 } },
+	{ 6, 0x989c,
+	    { 0x000000b0, 0x000000b0, 0x000000a8, 0x000000a8, 0x000000a8 } },
+	{ 6, 0x989c,
+	    { 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e } },
+	{ 6, 0x989c,
+	    { 0x006c4a41, 0x006c4a41, 0x006c4af1, 0x006c4a61, 0x006c4a61 } },
+	{ 6, 0x989c,
+	    { 0x0050892a, 0x0050892a, 0x0050892b, 0x0050892b, 0x0050892b } },
+	{ 6, 0x989c,
+	    { 0x00842400, 0x00842400, 0x00842400, 0x00842400, 0x00842400 } },
+	{ 6, 0x989c,
+	    { 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200 } },
+	{ 6, 0x98d0,
+	    { 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c } },
+	{ 7, 0x989c,
+	    { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } },
+	{ 7, 0x989c,
+	    { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } },
+	{ 7, 0x989c,
+	    { 0x0000000a, 0x0000000a, 0x00000012, 0x00000012, 0x00000012 } },
+	{ 7, 0x989c,
+	    { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } },
+	{ 7, 0x989c,
+	    { 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1 } },
+	{ 7, 0x989c,
+	    { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } },
+	{ 7, 0x989c,
+	    { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } },
+	{ 7, 0x989c,
+	    { 0x00000022, 0x00000022, 0x00000022, 0x00000022, 0x00000022 } },
+	{ 7, 0x989c,
+	    { 0x00000092, 0x00000092, 0x00000092, 0x00000092, 0x00000092 } },
+	{ 7, 0x989c,
+	    { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } },
+	{ 7, 0x989c,
+	    { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } },
+	{ 7, 0x989c,
+	    { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } },
+	{ 7, 0x98c4,
+	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } },
+};
+
+/* RF5112A mode-specific init registers */
+static const struct ath5k_ini_rf rfregs_5112a[] = {
+	{ 1, 0x98d4,
+	/*    mode a/XR   mode aTurbo mode b      mode g      mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } },
+	{ 3, 0x98dc,
+	    { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } },
+	{ 6, 0x989c,
+	    { 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00800000, 0x00800000, 0x00800000, 0x00800000, 0x00800000 } },
+	{ 6, 0x989c,
+	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } },
+	{ 6, 0x989c,
+	    { 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00180000, 0x00180000, 0x00180000, 0x00180000, 0x00180000 } },
+	{ 6, 0x989c,
+	    { 0x00600000, 0x00600000, 0x006e0000, 0x006e0000, 0x006e0000 } },
+	{ 6, 0x989c,
+	    { 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000 } },
+	{ 6, 0x989c,
+	    { 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000 } },
+	{ 6, 0x989c,
+	    { 0x04480000, 0x04480000, 0x04480000, 0x04480000, 0x04480000 } },
+	{ 6, 0x989c,
+	    { 0x00220000, 0x00220000, 0x00220000, 0x00220000, 0x00220000 } },
+	{ 6, 0x989c,
+	    { 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000 } },
+	{ 6, 0x989c,
+	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } },
+	{ 6, 0x989c,
+	    { 0x00190000, 0x00190000, 0x00190000, 0x00190000, 0x00190000 } },
+	{ 6, 0x989c,
+	    { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } },
+	{ 6, 0x989c,
+	    { 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000 } },
+	{ 6, 0x989c,
+	    { 0x00990000, 0x00990000, 0x00990000, 0x00990000, 0x00990000 } },
+	{ 6, 0x989c,
+	    { 0x00500000, 0x00500000, 0x00500000, 0x00500000, 0x00500000 } },
+	{ 6, 0x989c,
+	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } },
+	{ 6, 0x989c,
+	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } },
+	{ 6, 0x989c,
+	    { 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000 } },
+	{ 6, 0x989c,
+	    { 0x01740000, 0x01740000, 0x01740000, 0x01740000, 0x01740000 } },
+	{ 6, 0x989c,
+	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } },
+	{ 6, 0x989c,
+	    { 0x86280000, 0x86280000, 0x86280000, 0x86280000, 0x86280000 } },
+	{ 6, 0x989c,
+	    { 0x31840000, 0x31840000, 0x31840000, 0x31840000, 0x31840000 } },
+	{ 6, 0x989c,
+	    { 0x00020080, 0x00020080, 0x00020080, 0x00020080, 0x00020080 } },
+	{ 6, 0x989c,
+	    { 0x00080009, 0x00080009, 0x00080009, 0x00080009, 0x00080009 } },
+	{ 6, 0x989c,
+	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2 } },
+	{ 6, 0x989c,
+	    { 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084 } },
+	{ 6, 0x989c,
+	    { 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4 } },
+	{ 6, 0x989c,
+	    { 0x00119220, 0x00119220, 0x00119220, 0x00119220, 0x00119220 } },
+	{ 6, 0x989c,
+	    { 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800 } },
+	{ 6, 0x98d8,
+	    { 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230 } },
+	{ 7, 0x989c,
+	    { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } },
+	{ 7, 0x989c,
+	    { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } },
+	{ 7, 0x989c,
+	    { 0x00000012, 0x00000012, 0x00000012, 0x00000012, 0x00000012 } },
+	{ 7, 0x989c,
+	    { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } },
+	{ 7, 0x989c,
+	    { 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9 } },
+	{ 7, 0x989c,
+	    { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } },
+	{ 7, 0x989c,
+	    { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } },
+	{ 7, 0x989c,
+	    { 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2 } },
+	{ 7, 0x989c,
+	    { 0x00000052, 0x00000052, 0x00000052, 0x00000052, 0x00000052 } },
+	{ 7, 0x989c,
+	    { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } },
+	{ 7, 0x989c,
+	    { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } },
+	{ 7, 0x989c,
+	    { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } },
+	{ 7, 0x98c4,
+	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } },
+};
+
+
+/* Initial RF Gain settings for RF5112 */
+static const struct ath5k_ini_rfgain rfgain_5112[] = {
+	/*			      5Ghz	2Ghz	*/
+	{ AR5K_RF_GAIN(0),	{ 0x00000007, 0x00000007 } },
+	{ AR5K_RF_GAIN(1),	{ 0x00000047, 0x00000047 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000087, 0x00000087 } },
+	{ AR5K_RF_GAIN(3),	{ 0x000001a0, 0x000001a0 } },
+	{ AR5K_RF_GAIN(4),	{ 0x000001e0, 0x000001e0 } },
+	{ AR5K_RF_GAIN(5),	{ 0x00000020, 0x00000020 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000060, 0x00000060 } },
+	{ AR5K_RF_GAIN(7),	{ 0x000001a1, 0x000001a1 } },
+	{ AR5K_RF_GAIN(8),	{ 0x000001e1, 0x000001e1 } },
+	{ AR5K_RF_GAIN(9),	{ 0x00000021, 0x00000021 } },
+	{ AR5K_RF_GAIN(10),	{ 0x00000061, 0x00000061 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000162, 0x00000162 } },
+	{ AR5K_RF_GAIN(12),	{ 0x000001a2, 0x000001a2 } },
+	{ AR5K_RF_GAIN(13),	{ 0x000001e2, 0x000001e2 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000022, 0x00000022 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000062, 0x00000062 } },
+	{ AR5K_RF_GAIN(16),	{ 0x00000163, 0x00000163 } },
+	{ AR5K_RF_GAIN(17),	{ 0x000001a3, 0x000001a3 } },
+	{ AR5K_RF_GAIN(18),	{ 0x000001e3, 0x000001e3 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000023, 0x00000023 } },
+	{ AR5K_RF_GAIN(20),	{ 0x00000063, 0x00000063 } },
+	{ AR5K_RF_GAIN(21),	{ 0x00000184, 0x00000184 } },
+	{ AR5K_RF_GAIN(22),	{ 0x000001c4, 0x000001c4 } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000004, 0x00000004 } },
+	{ AR5K_RF_GAIN(24),	{ 0x000001ea, 0x0000000b } },
+	{ AR5K_RF_GAIN(25),	{ 0x0000002a, 0x0000004b } },
+	{ AR5K_RF_GAIN(26),	{ 0x0000006a, 0x0000008b } },
+	{ AR5K_RF_GAIN(27),	{ 0x000000aa, 0x000001ac } },
+	{ AR5K_RF_GAIN(28),	{ 0x000001ab, 0x000001ec } },
+	{ AR5K_RF_GAIN(29),	{ 0x000001eb, 0x0000002c } },
+	{ AR5K_RF_GAIN(30),	{ 0x0000002b, 0x00000012 } },
+	{ AR5K_RF_GAIN(31),	{ 0x0000006b, 0x00000052 } },
+	{ AR5K_RF_GAIN(32),	{ 0x000000ab, 0x00000092 } },
+	{ AR5K_RF_GAIN(33),	{ 0x000001ac, 0x00000193 } },
+	{ AR5K_RF_GAIN(34),	{ 0x000001ec, 0x000001d3 } },
+	{ AR5K_RF_GAIN(35),	{ 0x0000002c, 0x00000013 } },
+	{ AR5K_RF_GAIN(36),	{ 0x0000003a, 0x00000053 } },
+	{ AR5K_RF_GAIN(37),	{ 0x0000007a, 0x00000093 } },
+	{ AR5K_RF_GAIN(38),	{ 0x000000ba, 0x00000194 } },
+	{ AR5K_RF_GAIN(39),	{ 0x000001bb, 0x000001d4 } },
+	{ AR5K_RF_GAIN(40),	{ 0x000001fb, 0x00000014 } },
+	{ AR5K_RF_GAIN(41),	{ 0x0000003b, 0x0000003a } },
+	{ AR5K_RF_GAIN(42),	{ 0x0000007b, 0x0000007a } },
+	{ AR5K_RF_GAIN(43),	{ 0x000000bb, 0x000000ba } },
+	{ AR5K_RF_GAIN(44),	{ 0x000001bc, 0x000001bb } },
+	{ AR5K_RF_GAIN(45),	{ 0x000001fc, 0x000001fb } },
+	{ AR5K_RF_GAIN(46),	{ 0x0000003c, 0x0000003b } },
+	{ AR5K_RF_GAIN(47),	{ 0x0000007c, 0x0000007b } },
+	{ AR5K_RF_GAIN(48),	{ 0x000000bc, 0x000000bb } },
+	{ AR5K_RF_GAIN(49),	{ 0x000000fc, 0x000001bc } },
+	{ AR5K_RF_GAIN(50),	{ 0x000000fc, 0x000001fc } },
+	{ AR5K_RF_GAIN(51),	{ 0x000000fc, 0x0000003c } },
+	{ AR5K_RF_GAIN(52),	{ 0x000000fc, 0x0000007c } },
+	{ AR5K_RF_GAIN(53),	{ 0x000000fc, 0x000000bc } },
+	{ AR5K_RF_GAIN(54),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(55),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(56),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(57),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(58),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(59),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(60),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(61),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(62),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(63),	{ 0x000000fc, 0x000000fc } },
+};
+
+static const struct ath5k_gain_opt rfgain_opt_5112 = {
+	1,
+	8,
+	{
+		{ { 3, 0, 0, 0, 0, 0, 0 }, 6 },
+		{ { 2, 0, 0, 0, 0, 0, 0 }, 0 },
+		{ { 1, 0, 0, 0, 0, 0, 0 }, -3 },
+		{ { 0, 0, 0, 0, 0, 0, 0 }, -6 },
+		{ { 0, 1, 1, 0, 0, 0, 0 }, -8 },
+		{ { 0, 1, 1, 0, 1, 1, 0 }, -10 },
+		{ { 0, 1, 0, 1, 1, 1, 0 }, -13 },
+		{ { 0, 1, 0, 1, 1, 0, 1 }, -16 },
+	}
+};
+
+/*
+ * Used to modify RF Banks before writing them to AR5K_RF_BUFFER
+ */
+static unsigned int ath5k_hw_rfregs_op(u32 *rf, u32 offset, u32 reg, u32 bits,
+		u32 first, u32 col, bool set)
+{
+	u32 mask, entry, last, data, shift, position;
+	s32 left;
+	int i;
+
+	data = 0;
+
+	if (rf == NULL)
+		/* should not happen */
+		return 0;
+
+	if (!(col <= 3 && bits <= 32 && first + bits <= 319)) {
+		AR5K_PRINTF("invalid values at offset %u\n", offset);
+		return 0;
+	}
+
+	entry = ((first - 1) / 8) + offset;
+	position = (first - 1) % 8;
+
+	if (set == true)
+		data = ath5k_hw_bitswap(reg, bits);
+
+	for (i = shift = 0, left = bits; left > 0; position = 0, entry++, i++) {
+		last = (position + left > 8) ? 8 : position + left;
+		mask = (((1 << last) - 1) ^ ((1 << position) - 1)) << (col * 8);
+
+		if (set == true) {
+			rf[entry] &= ~mask;
+			rf[entry] |= ((data << position) << (col * 8)) & mask;
+			data >>= (8 - position);
+		} else {
+			data = (((rf[entry] & mask) >> (col * 8)) >> position)
+				<< shift;
+			shift += last - position;
+		}
+
+		left -= 8 - position;
+	}
+
+	data = set == true ? 1 : ath5k_hw_bitswap(data, bits);
+
+	return data;
+}
+
+static u32 ath5k_hw_rfregs_gainf_corr(struct ath_hw *hal)
+{
+	u32 mix, step;
+	u32 *rf;
+
+	if (hal->ah_rf_banks == NULL)
+		return 0;
+
+	rf = hal->ah_rf_banks;
+	hal->ah_gain.g_f_corr = 0;
+
+	if (ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 1, 36, 0, false) != 1)
+		return 0;
+
+	step = ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 4, 32, 0, false);
+	mix = hal->ah_gain.g_step->gos_param[0];
+
+	switch (mix) {
+	case 3:
+		hal->ah_gain.g_f_corr = step * 2;
+		break;
+	case 2:
+		hal->ah_gain.g_f_corr = (step - 5) * 2;
+		break;
+	case 1:
+		hal->ah_gain.g_f_corr = step;
+		break;
+	default:
+		hal->ah_gain.g_f_corr = 0;
+		break;
+	}
+
+	return hal->ah_gain.g_f_corr;
+}
+
+static bool ath5k_hw_rfregs_gain_readback(struct ath_hw *hal)
+{
+	u32 step, mix, level[4];
+	u32 *rf;
+
+	if (hal->ah_rf_banks == NULL)
+		return false;
+
+	rf = hal->ah_rf_banks;
+
+	if (hal->ah_radio == AR5K_RF5111) {
+		step = ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 6, 37, 0,
+				false);
+		level[0] = 0;
+		level[1] = (step == 0x3f) ? 0x32 : step + 4;
+		level[2] = (step != 0x3f) ? 0x40 : level[0];
+		level[3] = level[2] + 0x32;
+
+		hal->ah_gain.g_high = level[3] -
+			(step == 0x3f ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5);
+		hal->ah_gain.g_low = level[0] +
+			(step == 0x3f ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0);
+	} else {
+		mix = ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 1, 36, 0,
+				false);
+		level[0] = level[2] = 0;
+
+		if (mix == 1) {
+			level[1] = level[3] = 83;
+		} else {
+			level[1] = level[3] = 107;
+			hal->ah_gain.g_high = 55;
+		}
+	}
+
+	return (hal->ah_gain.g_current >= level[0] &&
+			hal->ah_gain.g_current <= level[1]) ||
+		(hal->ah_gain.g_current >= level[2] &&
+			hal->ah_gain.g_current <= level[3]);
+}
+
+static s32 ath5k_hw_rfregs_gain_adjust(struct ath_hw *hal)
+{
+	const struct ath5k_gain_opt *go;
+	int ret = 0;
+
+	switch (hal->ah_radio) {
+	case AR5K_RF5111:
+		go = &rfgain_opt_5111;
+		break;
+	case AR5K_RF5112:
+		go = &rfgain_opt_5112;
+		break;
+	default:
+		return 0;
+	}
+
+	hal->ah_gain.g_step = &go->go_step[hal->ah_gain.g_step_idx];
+
+	if (hal->ah_gain.g_current >= hal->ah_gain.g_high) {
+		if (hal->ah_gain.g_step_idx == 0)
+			return -1;
+		for (hal->ah_gain.g_target = hal->ah_gain.g_current;
+				hal->ah_gain.g_target >=  hal->ah_gain.g_high &&
+				hal->ah_gain.g_step_idx > 0;
+				hal->ah_gain.g_step =
+					&go->go_step[hal->ah_gain.g_step_idx])
+			hal->ah_gain.g_target -= 2 *
+			    (go->go_step[--(hal->ah_gain.g_step_idx)].gos_gain -
+			    hal->ah_gain.g_step->gos_gain);
+
+		ret = 1;
+		goto done;
+	}
+
+	if (hal->ah_gain.g_current <= hal->ah_gain.g_low) {
+		if (hal->ah_gain.g_step_idx == (go->go_steps_count - 1))
+			return -2;
+		for (hal->ah_gain.g_target = hal->ah_gain.g_current;
+				hal->ah_gain.g_target <= hal->ah_gain.g_low &&
+				hal->ah_gain.g_step_idx < go->go_steps_count-1;
+				hal->ah_gain.g_step =
+					&go->go_step[hal->ah_gain.g_step_idx])
+			hal->ah_gain.g_target -= 2 *
+			    (go->go_step[++hal->ah_gain.g_step_idx].gos_gain -
+			    hal->ah_gain.g_step->gos_gain);
+
+		ret = 2;
+		goto done;
+	}
+
+done:
+#ifdef AR5K_DEBUG
+	AR5K_PRINTF("ret %d, gain step %u, current gain %u, target gain %u\n",
+		ret, hal->ah_gain.g_step_idx, hal->ah_gain.g_current,
+		hal->ah_gain.g_target);
+#endif
+
+	return ret;
+}
+
+/*
+ * Read EEPROM Calibration data, modify RF Banks and Initialize RF5111
+ */
+static int ath5k_hw_rf5111_rfregs(struct ath_hw *hal,
+		struct ieee80211_channel *channel, unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom;
+	u32 *rf;
+	const unsigned int rf_size = ARRAY_SIZE(rfregs_5111);
+	unsigned int i;
+	int obdb = -1, bank = -1;
+	u32 ee_mode;
+
+	AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX);
+
+	rf = hal->ah_rf_banks;
+
+	/* Copy values to modify them */
+	for (i = 0; i < rf_size; i++) {
+		if (rfregs_5111[i].rf_bank >= AR5K_RF5111_INI_RF_MAX_BANKS) {
+			AR5K_PRINT("invalid bank\n");
+			return -EINVAL;
+		}
+
+		if (bank != rfregs_5111[i].rf_bank) {
+			bank = rfregs_5111[i].rf_bank;
+			hal->ah_offset[bank] = i;
+		}
+
+		rf[i] = rfregs_5111[i].rf_value[mode];
+	}
+
+	/* Modify bank 0 */
+	if (channel->val & CHANNEL_2GHZ) {
+		if (channel->val & CHANNEL_B)
+			ee_mode = AR5K_EEPROM_MODE_11B;
+		else
+			ee_mode = AR5K_EEPROM_MODE_11G;
+		obdb = 0;
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[0],
+				ee->ee_ob[ee_mode][obdb], 3, 119, 0, true))
+			return -EINVAL;
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[0],
+				ee->ee_ob[ee_mode][obdb], 3, 122, 0, true))
+			return -EINVAL;
+
+		obdb = 1;
+	/* Modify bank 6 */
+	} else {
+		/* For 11a, Turbo and XR */
+		ee_mode = AR5K_EEPROM_MODE_11A;
+		obdb =	 channel->freq >= 5725 ? 3 :
+			(channel->freq >= 5500 ? 2 :
+			(channel->freq >= 5260 ? 1 :
+			 (channel->freq > 4000 ? 0 : -1)));
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+				ee->ee_pwd_84, 1, 51, 3, true))
+			return -EINVAL;
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+				ee->ee_pwd_90, 1, 45, 3, true))
+			return -EINVAL;
+	}
+
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+			!ee->ee_xpd[ee_mode], 1, 95, 0, true))
+		return -EINVAL;
+
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+			ee->ee_x_gain[ee_mode], 4, 96, 0, true))
+		return -EINVAL;
+
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], obdb >= 0 ?
+			ee->ee_ob[ee_mode][obdb] : 0, 3, 104, 0, true))
+		return -EINVAL;
+
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], obdb >= 0 ?
+			ee->ee_db[ee_mode][obdb] : 0, 3, 107, 0, true))
+		return -EINVAL;
+
+	/* Modify bank 7 */
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[7],
+			ee->ee_i_gain[ee_mode], 6, 29, 0, true))
+		return -EINVAL;
+
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[7],
+			ee->ee_xpd[ee_mode], 1, 4, 0, true))
+		return -EINVAL;
+
+	/* Write RF values */
+	for (i = 0; i < rf_size; i++) {
+		AR5K_REG_WAIT(i);
+		ath5k_hw_reg_write(hal, rf[i], rfregs_5111[i].rf_register);
+	}
+
+	return 0;
+}
+
+/*
+ * Read EEPROM Calibration data, modify RF Banks and Initialize RF5112
+ */
+static int ath5k_hw_rf5112_rfregs(struct ath_hw *hal,
+		struct ieee80211_channel *channel, unsigned int mode)
+{
+	const struct ath5k_ini_rf *rf_ini;
+	struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom;
+	u32 *rf;
+	unsigned int rf_size, i;
+	int obdb = -1, bank = -1;
+	u32 ee_mode;
+
+	AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX);
+
+	rf = hal->ah_rf_banks;
+
+	if (hal->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
+		rf_ini = rfregs_5112a;
+		rf_size = ARRAY_SIZE(rfregs_5112a);
+	} else {
+		rf_ini = rfregs_5112;
+		rf_size = ARRAY_SIZE(rfregs_5112);
+	}
+
+	/* Copy values to modify them */
+	for (i = 0; i < rf_size; i++) {
+		if (rf_ini[i].rf_bank >= AR5K_RF5112_INI_RF_MAX_BANKS) {
+			AR5K_PRINT("invalid bank\n");
+			return -EINVAL;
+		}
+
+		if (bank != rf_ini[i].rf_bank) {
+			bank = rf_ini[i].rf_bank;
+			hal->ah_offset[bank] = i;
+		}
+
+		rf[i] = rf_ini[i].rf_value[mode];
+	}
+
+	/* Modify bank 6 */
+	if (channel->val & CHANNEL_2GHZ) {
+		if (channel->val & CHANNEL_B)
+			ee_mode = AR5K_EEPROM_MODE_11B;
+		else
+			ee_mode = AR5K_EEPROM_MODE_11G;
+		obdb = 0;
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+				ee->ee_ob[ee_mode][obdb], 3, 287, 0, true))
+			return -EINVAL;
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+				ee->ee_ob[ee_mode][obdb], 3, 290, 0, true))
+			return -EINVAL;
+	} else {
+		/* For 11a, Turbo and XR */
+		ee_mode = AR5K_EEPROM_MODE_11A;
+		obdb = channel->freq >= 5725 ? 3 :
+		    (channel->freq >= 5500 ? 2 :
+			(channel->freq >= 5260 ? 1 :
+			    (channel->freq > 4000 ? 0 : -1)));
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+				ee->ee_ob[ee_mode][obdb], 3, 279, 0, true))
+			return -EINVAL;
+
+		if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+				ee->ee_ob[ee_mode][obdb], 3, 282, 0, true))
+			return -EINVAL;
+	}
+
+#ifdef notyet
+	ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+	    ee->ee_x_gain[ee_mode], 2, 270, 0, true);
+	ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+	    ee->ee_x_gain[ee_mode], 2, 257, 0, true);
+#endif
+
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6],
+			ee->ee_xpd[ee_mode], 1, 302, 0, true))
+		return -EINVAL;
+
+	/* Modify bank 7 */
+	if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[7],
+			ee->ee_i_gain[ee_mode], 6, 14, 0, true))
+		return -EINVAL;
+
+	/* Write RF values */
+	for (i = 0; i < rf_size; i++)
+		ath5k_hw_reg_write(hal, rf[i], rf_ini[i].rf_register);
+
+	return 0;
+}
+
+/*
+ * Initialize RF
+ */
+int ath5k_hw_rfregs(struct ath_hw *hal, struct ieee80211_channel *channel,
+		unsigned int mode)
+{
+	int (*func)(struct ath_hw *, struct ieee80211_channel *, unsigned int);
+	int ret;
+
+	switch (hal->ah_radio) {
+	case AR5K_RF5111:
+		hal->ah_rf_banks_size = sizeof(rfregs_5111);
+		func = ath5k_hw_rf5111_rfregs;
+		break;
+	case AR5K_RF5112:
+		if (hal->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A)
+			hal->ah_rf_banks_size = sizeof(rfregs_5112a);
+		else
+			hal->ah_rf_banks_size = sizeof(rfregs_5112);
+		func = ath5k_hw_rf5112_rfregs;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (hal->ah_rf_banks == NULL) {
+		/* XXX do extra checks? */
+		hal->ah_rf_banks = kmalloc(hal->ah_rf_banks_size, GFP_KERNEL);
+		if (hal->ah_rf_banks == NULL) {
+			AR5K_PRINT("out of memory\n");
+			return -ENOMEM;
+		}
+	}
+
+	ret = func(hal, channel, mode);
+	if (!ret)
+		hal->ah_rf_gain = AR5K_RFGAIN_INACTIVE;
+
+	return ret;
+}
+
+int ath5k_hw_rfgain(struct ath_hw *hal, unsigned int freq)
+{
+	const struct ath5k_ini_rfgain *ath5k_rfg;
+	unsigned int i, size;
+
+	switch (hal->ah_radio) {
+	case AR5K_RF5111:
+		ath5k_rfg = rfgain_5111;
+		size = ARRAY_SIZE(rfgain_5111);
+		break;
+	case AR5K_RF5112:
+		ath5k_rfg = rfgain_5112;
+		size = ARRAY_SIZE(rfgain_5112);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (freq) {
+	case AR5K_INI_RFGAIN_2GHZ:
+	case AR5K_INI_RFGAIN_5GHZ:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < size; i++) {
+		AR5K_REG_WAIT(i);
+		ath5k_hw_reg_write(hal, ath5k_rfg[i].rfg_value[freq],
+			(u32)ath5k_rfg[i].rfg_register);
+	}
+
+	return 0;
+}
+
+enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath_hw *hal)
+{
+	u32 data, type;
+
+	AR5K_TRACE;
+
+	if (hal->ah_rf_banks == NULL || !hal->ah_gain.g_active ||
+			hal->ah_version <= AR5K_AR5211)
+		return AR5K_RFGAIN_INACTIVE;
+
+	if (hal->ah_rf_gain != AR5K_RFGAIN_READ_REQUESTED)
+		goto done;
+
+	data = ath5k_hw_reg_read(hal, AR5K_PHY_PAPD_PROBE);
+
+	if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) {
+		hal->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S;
+		type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE);
+
+		if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK)
+			hal->ah_gain.g_current += AR5K_GAIN_CCK_PROBE_CORR;
+
+		if (hal->ah_radio == AR5K_RF5112) {
+			ath5k_hw_rfregs_gainf_corr(hal);
+			hal->ah_gain.g_current =
+				hal->ah_gain.g_current>=hal->ah_gain.g_f_corr ?
+				(hal->ah_gain.g_current-hal->ah_gain.g_f_corr) :
+				0;
+		}
+
+		if (ath5k_hw_rfregs_gain_readback(hal) &&
+				AR5K_GAIN_CHECK_ADJUST(&hal->ah_gain) &&
+				ath5k_hw_rfregs_gain_adjust(hal))
+			hal->ah_rf_gain = AR5K_RFGAIN_NEED_CHANGE;
+	}
+
+done:
+	return hal->ah_rf_gain;
+}
+
+int ath5k_hw_set_rfgain_opt(struct ath_hw *hal)
+{
+	/* Initialize the gain optimization values */
+	switch (hal->ah_radio) {
+	case AR5K_RF5111:
+		hal->ah_gain.g_step_idx = rfgain_opt_5111.go_default;
+		hal->ah_gain.g_step =
+		    &rfgain_opt_5111.go_step[hal->ah_gain.g_step_idx];
+		hal->ah_gain.g_low = 20;
+		hal->ah_gain.g_high = 35;
+		hal->ah_gain.g_active = 1;
+		break;
+	case AR5K_RF5112:
+		hal->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
+		hal->ah_gain.g_step =
+		    &rfgain_opt_5112.go_step[hal->ah_gain.g_step_idx];
+		hal->ah_gain.g_low = 20;
+		hal->ah_gain.g_high = 85;
+		hal->ah_gain.g_active = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**************************\
+  PHY/RF channel functions
+\**************************/
+
+/*
+ * Check if a channel is supported
+ */
+bool ath5k_channel_ok(struct ath_hw *hal, u16 freq, unsigned int flags)
+{
+	/* Check if the channel is in our supported range */
+	if (flags & CHANNEL_2GHZ) {
+		if ((freq >= hal->ah_capabilities.cap_range.range_2ghz_min) &&
+		    (freq <= hal->ah_capabilities.cap_range.range_2ghz_max))
+			return true;
+	} else if (flags & CHANNEL_5GHZ)
+		if ((freq >= hal->ah_capabilities.cap_range.range_5ghz_min) &&
+		    (freq <= hal->ah_capabilities.cap_range.range_5ghz_max))
+			return true;
+
+	return false;
+}
+
+/*
+ * Convertion needed for RF5110
+ */
+static u32 ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel)
+{
+	u32 athchan;
+
+	/*
+	 * Convert IEEE channel/MHz to an internal channel value used
+	 * by the AR5210 chipset. This has not been verified with
+	 * newer chipsets like the AR5212A who have a completely
+	 * different RF/PHY part.
+	 */
+	athchan = (ath5k_hw_bitswap((channel->chan - 24) / 2, 5) << 1) |
+		(1 << 6) | 0x1;
+
+	return athchan;
+}
+
+/*
+ * Set channel on RF5110
+ */
+static int ath5k_hw_rf5110_channel(struct ath_hw *hal,
+		struct ieee80211_channel *channel)
+{
+	u32 data;
+
+	/*
+	 * Set the channel and wait
+	 */
+	data = ath5k_hw_rf5110_chan2athchan(channel);
+	ath5k_hw_reg_write(hal, data, AR5K_RF_BUFFER);
+	ath5k_hw_reg_write(hal, 0, AR5K_RF_BUFFER_CONTROL_0);
+	mdelay(1);
+
+	return 0;
+}
+
+/*
+ * Convertion needed for 5111
+ */
+static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee,
+		struct ath5k_athchan_2ghz *athchan)
+{
+	int channel;
+
+	/* Cast this value to catch negative channel numbers (>= -19) */
+	channel = (int)ieee;
+
+	/*
+	 * Map 2GHz IEEE channel to 5GHz Atheros channel
+	 */
+	if (channel <= 13) {
+		athchan->a2_athchan = 115 + channel;
+		athchan->a2_flags = 0x46;
+	} else if (channel == 14) {
+		athchan->a2_athchan = 124;
+		athchan->a2_flags = 0x44;
+	} else if (channel >= 15 && channel <= 26) {
+		athchan->a2_athchan = ((channel - 14) * 4) + 132;
+		athchan->a2_flags = 0x46;
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+/*
+ * Set channel on 5111
+ */
+static int ath5k_hw_rf5111_channel(struct ath_hw *hal,
+		struct ieee80211_channel *channel)
+{
+	struct ath5k_athchan_2ghz ath_channel_2ghz;
+	unsigned int ath_channel = channel->chan;
+	u32 data0, data1, clock;
+	int ret;
+
+	/*
+	 * Set the channel on the RF5111 radio
+	 */
+	data0 = data1 = 0;
+
+	if (channel->val & CHANNEL_2GHZ) {
+		/* Map 2GHz channel to 5GHz Atheros channel ID */
+		ret = ath5k_hw_rf5111_chan2athchan(channel->chan,
+				&ath_channel_2ghz);
+		if (ret)
+			return ret;
+
+		ath_channel = ath_channel_2ghz.a2_athchan;
+		data0 = ((ath5k_hw_bitswap(ath_channel_2ghz.a2_flags, 8) & 0xff)
+		    << 5) | (1 << 4);
+	}
+
+	if (ath_channel < 145 || !(ath_channel & 1)) {
+		clock = 1;
+		data1 = ((ath5k_hw_bitswap(ath_channel - 24, 8) & 0xff) << 2) |
+			(clock << 1) | (1 << 10) | 1;
+	} else {
+		clock = 0;
+		data1 = ((ath5k_hw_bitswap((ath_channel - 24) / 2, 8) & 0xff)
+			<< 2) | (clock << 1) | (1 << 10) | 1;
+	}
+
+	ath5k_hw_reg_write(hal, (data1 & 0xff) | ((data0 & 0xff) << 8),
+			AR5K_RF_BUFFER);
+	ath5k_hw_reg_write(hal, ((data1 >> 8) & 0xff) | (data0 & 0xff00),
+			AR5K_RF_BUFFER_CONTROL_3);
+
+	return 0;
+}
+
+/*
+ * Set channel on 5112
+ */
+static int ath5k_hw_rf5112_channel(struct ath_hw *hal,
+		struct ieee80211_channel *channel)
+{
+	u32 data, data0, data1, data2;
+	u16 c;
+
+	data = data0 = data1 = data2 = 0;
+	c = channel->freq;
+
+	/*
+	 * Set the channel on the RF5112 or newer
+	 */
+	if (c < 4800) {
+		if (!((c - 2224) % 5)) {
+			data0 = ((2 * (c - 704)) - 3040) / 10;
+			data1 = 1;
+		} else if (!((c - 2192) % 5)) {
+			data0 = ((2 * (c - 672)) - 3040) / 10;
+			data1 = 0;
+		} else
+			return -EINVAL;
+
+		data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8);
+	} else {
+		if (!(c % 20) && c >= 5120) {
+			data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
+			data2 = ath5k_hw_bitswap(3, 2);
+		} else if (!(c % 10)) {
+			data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
+			data2 = ath5k_hw_bitswap(2, 2);
+		} else if (!(c % 5)) {
+			data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
+			data2 = ath5k_hw_bitswap(1, 2);
+		} else
+			return -EINVAL;
+	}
+
+	data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001;
+
+	ath5k_hw_reg_write(hal, data & 0xff, AR5K_RF_BUFFER);
+	ath5k_hw_reg_write(hal, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);
+
+	return 0;
+}
+
+/*
+ * Set a channel on the radio chip
+ */
+int ath5k_hw_channel(struct ath_hw *hal, struct ieee80211_channel *channel)
+{
+	int ret;
+
+	/*
+	 * Check bounds supported by the PHY
+	 * (don't care about regulation restrictions at this point)
+	 */
+	if ((channel->freq < hal->ah_capabilities.cap_range.range_2ghz_min ||
+	    channel->freq > hal->ah_capabilities.cap_range.range_2ghz_max) &&
+	    (channel->freq < hal->ah_capabilities.cap_range.range_5ghz_min ||
+	    channel->freq > hal->ah_capabilities.cap_range.range_5ghz_max)) {
+		AR5K_PRINTF("channel out of supported range (%u MHz)\n",
+			channel->freq);
+		return -EINVAL;
+	}
+
+	/*
+	 * Set the channel and wait
+	 */
+	switch (hal->ah_radio) {
+	case AR5K_RF5110:
+		ret = ath5k_hw_rf5110_channel(hal, channel);
+		break;
+	case AR5K_RF5111:
+		ret = ath5k_hw_rf5111_channel(hal, channel);
+		break;
+	default:
+		ret = ath5k_hw_rf5112_channel(hal, channel);
+		break;
+	}
+
+	if (ret)
+		return ret;
+
+	hal->ah_current_channel.freq = channel->freq;
+	hal->ah_current_channel.val = channel->val;
+	hal->ah_turbo = channel->val == CHANNEL_T ? true : false;
+
+	return 0;
+}
+
+/*****************\
+  PHY calibration
+\*****************/
+
+/*
+ * Perform a PHY calibration on RF5110
+ * -Fix BPSK/QAM Constellation (I/Q correction)
+ * -Calculate Noise Floor
+ */
+static int ath5k_hw_rf5110_calibrate(struct ath_hw *hal,
+		struct ieee80211_channel *channel)
+{
+	u32 phy_sig, phy_agc, phy_sat, beacon, noise_floor;
+	unsigned int i;
+	int ret;
+
+	/*
+	 * Disable beacons and RX/TX queues, wait
+	 */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_DIAG_SW_5210,
+		AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
+	beacon = ath5k_hw_reg_read(hal, AR5K_BEACON_5210);
+	ath5k_hw_reg_write(hal, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210);
+
+	udelay(2300);
+
+	/*
+	 * Set the channel (with AGC turned off)
+	 */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+	udelay(10);
+	ret = ath5k_hw_channel(hal, channel);
+
+	/*
+	 * Activate PHY and wait
+	 */
+	ath5k_hw_reg_write(hal, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
+	mdelay(1);
+
+	AR5K_REG_DISABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+
+	if (ret)
+		return ret;
+
+	/*
+	 * Calibrate the radio chip
+	 */
+
+	/* Remember normal state */
+	phy_sig = ath5k_hw_reg_read(hal, AR5K_PHY_SIG);
+	phy_agc = ath5k_hw_reg_read(hal, AR5K_PHY_AGCCOARSE);
+	phy_sat = ath5k_hw_reg_read(hal, AR5K_PHY_ADCSAT);
+
+	/* Update radio registers */
+	ath5k_hw_reg_write(hal, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) |
+		AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG);
+
+	ath5k_hw_reg_write(hal, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI |
+			AR5K_PHY_AGCCOARSE_LO)) |
+		AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) |
+		AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE);
+
+	ath5k_hw_reg_write(hal, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT |
+			AR5K_PHY_ADCSAT_THR)) |
+		AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) |
+		AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT);
+
+	udelay(20);
+
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+	udelay(10);
+	ath5k_hw_reg_write(hal, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG);
+	AR5K_REG_DISABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+
+	mdelay(1);
+
+	/*
+	 * Enable calibration and wait until completion
+	 */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL);
+
+	ret = ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL,
+			AR5K_PHY_AGCCTL_CAL, 0, false);
+
+	/* Reset to normal state */
+	ath5k_hw_reg_write(hal, phy_sig, AR5K_PHY_SIG);
+	ath5k_hw_reg_write(hal, phy_agc, AR5K_PHY_AGCCOARSE);
+	ath5k_hw_reg_write(hal, phy_sat, AR5K_PHY_ADCSAT);
+
+	if (ret) {
+		AR5K_PRINTF("calibration timeout (%uMHz)\n", channel->freq);
+		return ret;
+	}
+
+	/*
+	 * Enable noise floor calibration and wait until completion
+	 */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF);
+
+	ret = ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL,
+			AR5K_PHY_AGCCTL_NF, 0, false);
+	if (ret) {
+		AR5K_PRINTF("noise floor calibration timeout (%uMHz)\n",
+				channel->freq);
+		return ret;
+	}
+
+	/* Wait until the noise floor is calibrated */
+	for (i = 20; i > 0; i--) {
+		mdelay(1);
+		noise_floor = ath5k_hw_reg_read(hal, AR5K_PHY_NF);
+
+		if (AR5K_PHY_NF_RVAL(noise_floor) & AR5K_PHY_NF_ACTIVE)
+			noise_floor = AR5K_PHY_NF_AVAL(noise_floor);
+
+		if (noise_floor <= AR5K_TUNE_NOISE_FLOOR)
+			break;
+	}
+
+	if (noise_floor > AR5K_TUNE_NOISE_FLOOR) {
+		AR5K_PRINTF("noise floor calibration failed (%uMHz)\n",
+			channel->freq);
+		return -EIO;
+	}
+
+	/*
+	 * Re-enable RX/TX and beacons
+	 */
+	AR5K_REG_DISABLE_BITS(hal, AR5K_DIAG_SW_5210,
+		AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
+	ath5k_hw_reg_write(hal, beacon, AR5K_BEACON_5210);
+
+	return 0;
+}
+
+/*
+ * Perform a PHY calibration on RF5111/5112
+ */
+static int ath5k_hw_rf511x_calibrate(struct ath_hw *hal,
+		struct ieee80211_channel *channel)
+{
+	u32 i_pwr, q_pwr;
+	s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd;
+	AR5K_TRACE;
+
+	if (hal->ah_calibration == false ||
+			ath5k_hw_reg_read(hal, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN)
+		goto done;
+
+	hal->ah_calibration = false;
+
+	iq_corr = ath5k_hw_reg_read(hal, AR5K_PHY_IQRES_CAL_CORR);
+	i_pwr = ath5k_hw_reg_read(hal, AR5K_PHY_IQRES_CAL_PWR_I);
+	q_pwr = ath5k_hw_reg_read(hal, AR5K_PHY_IQRES_CAL_PWR_Q);
+	i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7;
+	q_coffd = q_pwr >> 6;
+
+	if (i_coffd == 0 || q_coffd == 0)
+		goto done;
+
+	i_coff = ((-iq_corr) / i_coffd) & 0x3f;
+	q_coff = (((s32)i_pwr / q_coffd) - 64) & 0x1f;
+
+	/* Commit new IQ value */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE |
+		((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S));
+
+done:
+	/* Start noise floor calibration */
+	AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF);
+
+	/* Request RF gain */
+	if (channel->val & CHANNEL_5GHZ) {
+		ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txpower.txp_max,
+			AR5K_PHY_PAPD_PROBE_TXPOWER) |
+			AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);
+		hal->ah_rf_gain = AR5K_RFGAIN_READ_REQUESTED;
+	}
+
+	return 0;
+}
+
+/*
+ * Perform a PHY calibration
+ */
+int ath5k_hw_phy_calibrate(struct ath_hw *hal,
+		struct ieee80211_channel *channel)
+{
+	int ret;
+
+	if (hal->ah_radio == AR5K_RF5110)
+		ret = ath5k_hw_rf5110_calibrate(hal, channel);
+	else
+		ret = ath5k_hw_rf511x_calibrate(hal, channel);
+
+	return ret;
+}
+
+int ath5k_hw_phy_disable(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	/*Just a try M.F.*/
+	ath5k_hw_reg_write(hal, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
+
+	return 0;
+}
+
+/********************\
+  Misc PHY functions
+\********************/
+
+/*
+ * Get the PHY Chip revision
+ */
+u16 ath5k_hw_radio_revision(struct ath_hw *hal, unsigned int chan)
+{
+	unsigned int i;
+	u32 srev;
+	u16 ret;
+
+	AR5K_TRACE;
+
+	/*
+	 * Set the radio chip access register
+	 */
+	switch (chan) {
+	case CHANNEL_2GHZ:
+		ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
+		break;
+	case CHANNEL_5GHZ:
+		ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+		break;
+	default:
+		return 0;
+	}
+
+	mdelay(2);
+
+	/* ...wait until PHY is ready and read the selected radio revision */
+	ath5k_hw_reg_write(hal, 0x00001c16, AR5K_PHY(0x34));
+
+	for (i = 0; i < 8; i++)
+		ath5k_hw_reg_write(hal, 0x00010000, AR5K_PHY(0x20));
+
+	if (hal->ah_version == AR5K_AR5210) {
+		srev = ath5k_hw_reg_read(hal, AR5K_PHY(256) >> 28) & 0xf;
+		ret = (u16)ath5k_hw_bitswap(srev, 4) + 1;
+	} else {
+		srev = (ath5k_hw_reg_read(hal, AR5K_PHY(0x100)) >> 24) & 0xff;
+		ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) |
+				((srev & 0x0f) << 4), 8);
+	}
+
+	/* Reset to the 5GHz mode */
+	ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+
+	return ret;
+}
+
+void /*TODO:Boundary check*/
+ath5k_hw_set_def_antenna(struct ath_hw *hal, unsigned int ant)
+{
+	AR5K_TRACE;
+	/*Just a try M.F.*/
+	if (hal->ah_version != AR5K_AR5210)
+		ath5k_hw_reg_write(hal, ant, AR5K_DEFAULT_ANTENNA);
+}
+
+unsigned int ath5k_hw_get_def_antenna(struct ath_hw *hal)
+{
+	AR5K_TRACE;
+	/*Just a try M.F.*/
+	if (hal->ah_version != AR5K_AR5210)
+		return ath5k_hw_reg_read(hal, AR5K_DEFAULT_ANTENNA);
+
+	return false; /*XXX: What do we return for 5210 ?*/
+}
+
+/*
+ * TX power setup
+ */
+
+/*
+ * Initialize the tx power table (not fully implemented)
+ */
+static void ath5k_txpower_table(struct ath_hw *hal,
+		struct ieee80211_channel *channel, s16 max_power)
+{
+	unsigned int i, min, max, n;
+	u16 txpower, *rates;
+
+	rates = hal->ah_txpower.txp_rates;
+
+	txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2;
+	if (max_power > txpower)
+		txpower = max_power > AR5K_TUNE_MAX_TXPOWER ?
+		    AR5K_TUNE_MAX_TXPOWER : max_power;
+
+	for (i = 0; i < AR5K_MAX_RATES; i++)
+		rates[i] = txpower;
+
+	/* XXX setup target powers by rate */
+
+	hal->ah_txpower.txp_min = rates[7];
+	hal->ah_txpower.txp_max = rates[0];
+	hal->ah_txpower.txp_ofdm = rates[0];
+
+	/* Calculate the power table */
+	n = ARRAY_SIZE(hal->ah_txpower.txp_pcdac);
+	min = AR5K_EEPROM_PCDAC_START;
+	max = AR5K_EEPROM_PCDAC_STOP;
+	for (i = 0; i < n; i += AR5K_EEPROM_PCDAC_STEP)
+		hal->ah_txpower.txp_pcdac[i] =
+#ifdef notyet
+		min + ((i * (max - min)) / n);
+#else
+		min;
+#endif
+}
+
+/*
+ * Set transmition power
+ */
+int /*O.K. - txpower_table is unimplemented so this doesn't work*/
+ath5k_hw_txpower(struct ath_hw *hal, struct ieee80211_channel *channel,
+		unsigned int txpower)
+{
+	bool tpc = hal->ah_txpower.txp_tpc;
+	unsigned int i;
+
+	AR5K_TRACE;
+	if (txpower > AR5K_TUNE_MAX_TXPOWER) {
+		AR5K_PRINTF("invalid tx power: %u\n", txpower);
+		return -EINVAL;
+	}
+
+	/* Reset TX power values */
+	memset(&hal->ah_txpower, 0, sizeof(hal->ah_txpower));
+	hal->ah_txpower.txp_tpc = tpc;
+
+	/* Initialize TX power table */
+	ath5k_txpower_table(hal, channel, txpower);
+
+	/*
+	 * Write TX power values
+	 */
+	for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
+		ath5k_hw_reg_write(hal,
+			((((hal->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) |
+			(((hal->ah_txpower.txp_pcdac[(i << 1)    ] << 8) | 0xff) & 0xffff),
+			AR5K_PHY_PCDAC_TXPOWER(i));
+	}
+
+	ath5k_hw_reg_write(hal, AR5K_TXPOWER_OFDM(3, 24) |
+		AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) |
+		AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1);
+
+	ath5k_hw_reg_write(hal, AR5K_TXPOWER_OFDM(7, 24) |
+		AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) |
+		AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2);
+
+	ath5k_hw_reg_write(hal, AR5K_TXPOWER_CCK(10, 24) |
+		AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) |
+		AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3);
+
+	ath5k_hw_reg_write(hal, AR5K_TXPOWER_CCK(14, 24) |
+		AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
+		AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
+
+	if (hal->ah_txpower.txp_tpc == true)
+		ath5k_hw_reg_write(hal, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
+			AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+	else
+		ath5k_hw_reg_write(hal, AR5K_PHY_TXPOWER_RATE_MAX |
+			AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+
+	return 0;
+}
+
+int ath5k_hw_set_txpower_limit(struct ath_hw *hal, unsigned int power)
+{
+	/*Just a try M.F.*/
+	struct ieee80211_channel *channel = &hal->ah_current_channel;
+
+	AR5K_TRACE;
+#ifdef AR5K_DEBUG
+	AR5K_PRINTF("changing txpower to %d\n", power);
+#endif
+	return ath5k_hw_txpower(hal, channel, power);
+}
diff -puN /dev/null drivers/net/wireless/ath5k/reg.h
--- /dev/null
+++ a/drivers/net/wireless/ath5k/reg.h
@@ -0,0 +1,1978 @@
+/*
+ * Copyright (c) 2007 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2007 Michael Taylor <mike.taylor@apprion.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * Register values for Atheros 5210/5211/5212 cards from OpenBSD's ar5k
+ * maintained by Reyk Floeter
+ *
+ * I tried to document those registers by looking at ar5k code, some
+ * 802.11 (802.11e mostly) papers and by reading various public available
+ * Atheros presentations and papers like these:
+ *
+ * 5210 - http://nova.stanford.edu/~bbaas/ps/isscc2002_slides.pdf
+ *        http://www.it.iitb.ac.in/~janak/wifire/01222734.pdf
+ *
+ * 5211 - http://www.hotchips.org/archives/hc14/3_Tue/16_mcfarland.pdf
+ */
+
+
+
+/*====MAC DMA REGISTERS====*/
+
+/*
+ * AR5210-Specific TXDP registers
+ * 5210 has only 2 transmit queues so no DCU/QCU, just
+ * 2 transmit descriptor pointers...
+ */
+#define AR5K_NOQCU_TXDP0	0x0000		/* Queue 0 - data */
+#define AR5K_NOQCU_TXDP1	0x0004		/* Queue 1 - beacons */
+
+/*
+ * Mac Control Register
+ */
+#define	AR5K_CR		0x0008			/* Register Address */
+#define AR5K_CR_TXE0	0x00000001	/* TX Enable for queue 0 on 5210 */
+#define AR5K_CR_TXE1	0x00000002	/* TX Enable for queue 1 on 5210 */
+#define	AR5K_CR_RXE	0x00000004	/* RX Enable */
+#define AR5K_CR_TXD0	0x00000008	/* TX Disable for queue 0 on 5210 */
+#define AR5K_CR_TXD1	0x00000010	/* TX Disable for queue 1 on 5210 */
+#define	AR5K_CR_RXD	0x00000020	/* RX Disable */
+#define	AR5K_CR_SWI	0x00000040
+
+/*
+ * RX Descriptor Pointer register
+ */
+#define	AR5K_RXDP	0x000c
+
+/*
+ * Configuration and status register
+ */
+#define	AR5K_CFG		0x0014			/* Register Address */
+#define	AR5K_CFG_SWTD		0x00000001	/* Byte-swap TX descriptor (for big endian archs) */
+#define	AR5K_CFG_SWTB		0x00000002	/* Byte-swap TX buffer (?) */
+#define	AR5K_CFG_SWRD		0x00000004	/* Byte-swap RX descriptor */
+#define	AR5K_CFG_SWRB		0x00000008	/* Byte-swap RX buffer (?) */
+#define	AR5K_CFG_SWRG		0x00000010	/* Byte-swap Register values (?) */
+#define AR5K_CFG_ADHOC		0x00000020 	/* [5211+] */
+#define AR5K_CFG_PHY_OK		0x00000100	/* [5211+] */
+#define AR5K_CFG_EEBS		0x00000200	/* EEPROM is busy */
+#define	AR5K_CFG_CLKGD		0x00000400	/* Clock gated (?) */
+#define AR5K_CFG_TXCNT		0x00007800	/* Tx frame count (?) [5210] */
+#define AR5K_CFG_TXCNT_S	11
+#define AR5K_CFG_TXFSTAT	0x00008000	/* Tx frame status (?) [5210] */
+#define AR5K_CFG_TXFSTRT	0x00010000	/* [5210] */
+#define	AR5K_CFG_PCI_THRES	0x00060000	/* [5211+] */
+#define	AR5K_CFG_PCI_THRES_S	17
+
+/*
+ * Interrupt enable register
+ */
+#define AR5K_IER		0x0024			/* Register Address */
+#define AR5K_IER_DISABLE	0x00000000	/* Disable card interrupts */
+#define AR5K_IER_ENABLE		0x00000001	/* Enable card interrupts */
+
+
+/*
+ * 0x0028 is Beacon Control Register on 5210
+ * and first RTS duration register on 5211
+ */
+
+/*
+ * Beacon control register [5210]
+ */
+#define AR5K_BCR		0x0028			/* Register Address */
+#define AR5K_BCR_AP		0x00000000	/* AP mode */
+#define AR5K_BCR_ADHOC		0x00000001	/* Ad-Hoc mode */
+#define AR5K_BCR_BDMAE		0x00000002	/* DMA enable */
+#define AR5K_BCR_TQ1FV		0x00000004	/* Use Queue1 for CAB traffic */
+#define AR5K_BCR_TQ1V		0x00000008	/* Use Queue1 for Beacon traffic */
+#define AR5K_BCR_BCGET		0x00000010
+
+/*
+ * First RTS duration register [5211]
+ */
+#define AR5K_RTSD0		0x0028			/* Register Address */
+#define	AR5K_RTSD0_6		0x000000ff	/* 6Mb RTS duration mask (?) */
+#define	AR5K_RTSD0_6_S		0		/* 6Mb RTS duration shift (?) */
+#define	AR5K_RTSD0_9		0x0000ff00	/* 9Mb*/
+#define	AR5K_RTSD0_9_S		8
+#define	AR5K_RTSD0_12		0x00ff0000	/* 12Mb*/
+#define	AR5K_RTSD0_12_S		16
+#define	AR5K_RTSD0_18		0xff000000	/* 16Mb*/
+#define	AR5K_RTSD0_18_S		24
+
+
+/*
+ * 0x002c is Beacon Status Register on 5210
+ * and second RTS duration register on 5211
+ */
+
+/*
+ * Beacon status register [5210]
+ *
+ * As i can see in ar5k_ar5210_tx_start Reyk uses some of the values of BCR
+ * for this register, so i guess TQ1V,TQ1FV and BDMAE have the same meaning
+ * here and SNP/SNAP means "snapshot" (so this register gets synced with BCR).
+ * So SNAPPEDBCRVALID sould also stand for "snapped BCR -values- valid", so i
+ * renamed it to SNAPSHOTSVALID to make more sense. I realy have no idea what
+ * else can it be. I also renamed SNPBCMD to SNPADHOC to match BCR.
+ */
+#define AR5K_BSR		0x002c			/* Register Address */
+#define AR5K_BSR_BDLYSW		0x00000001	/* SW Beacon delay (?) */
+#define AR5K_BSR_BDLYDMA	0x00000002	/* DMA Beacon delay (?) */
+#define AR5K_BSR_TXQ1F		0x00000004	/* Beacon queue (1) finished */
+#define AR5K_BSR_ATIMDLY	0x00000008	/* ATIM delay (?) */
+#define AR5K_BSR_SNPADHOC	0x00000100	/* Ad-hoc mode set (?) */
+#define AR5K_BSR_SNPBDMAE	0x00000200	/* Beacon DMA enabled (?) */
+#define AR5K_BSR_SNPTQ1FV	0x00000400	/* Queue1 is used for CAB traffic (?) */
+#define AR5K_BSR_SNPTQ1V	0x00000800	/* Queue1 is used for Beacon traffic (?) */
+#define AR5K_BSR_SNAPSHOTSVALID	0x00001000	/* BCR snapshots are valid (?) */
+#define AR5K_BSR_SWBA_CNT	0x00ff0000
+
+/*
+ * Second RTS duration register [5211]
+ */
+#define AR5K_RTSD1		0x002c			/* Register Address */
+#define	AR5K_RTSD1_24		0x000000ff	/* 24Mb */
+#define	AR5K_RTSD1_24_S		0
+#define	AR5K_RTSD1_36		0x0000ff00	/* 36Mb */
+#define	AR5K_RTSD1_36_S		8
+#define	AR5K_RTSD1_48		0x00ff0000	/* 48Mb */
+#define	AR5K_RTSD1_48_S		16
+#define	AR5K_RTSD1_54		0xff000000	/* 54Mb */
+#define	AR5K_RTSD1_54_S		24
+
+
+/*
+ * Transmit configuration register
+ */
+#define AR5K_TXCFG		0x0030			/* Register Address */
+#define AR5K_TXCFG_SDMAMR	0x00000007	/* DMA size */
+#define AR5K_TXCFG_SDMAMR_S	0
+#define AR5K_TXCFG_B_MODE	0x00000008	/* Set b mode for 5111 (enable 2111) */
+#define AR5K_TXCFG_TXFSTP	0x00000008	/* TX DMA full Stop [5210] */
+#define AR5K_TXCFG_TXFULL	0x000003f0	/* TX Triger level mask */
+#define AR5K_TXCFG_TXFULL_S	4
+#define AR5K_TXCFG_TXFULL_0B	0x00000000
+#define AR5K_TXCFG_TXFULL_64B	0x00000010
+#define AR5K_TXCFG_TXFULL_128B	0x00000020
+#define AR5K_TXCFG_TXFULL_192B	0x00000030
+#define AR5K_TXCFG_TXFULL_256B	0x00000040
+#define AR5K_TXCFG_TXCONT_EN	0x00000080
+#define AR5K_TXCFG_DMASIZE	0x00000100	/* Flag for passing DMA size [5210] */
+#define AR5K_TXCFG_JUMBO_TXE	0x00000400	/* Enable jumbo frames transmition (?) [5211+] */
+#define AR5K_TXCFG_RTSRND	0x00001000	/* [5211+] */
+#define AR5K_TXCFG_FRMPAD_DIS	0x00002000	/* [5211+] */
+#define AR5K_TXCFG_RDY_DIS	0x00004000	/* [5211+] */
+
+/*
+ * Receive configuration register
+ */
+#define AR5K_RXCFG		0x0034			/* Register Address */
+#define AR5K_RXCFG_SDMAMW	0x00000007	/* DMA size */
+#define AR5K_RXCFG_SDMAMW_S	0
+#define	AR5K_RXCFG_DEF_ANTENNA	0x00000008	/* Default antenna */
+#define AR5K_RXCFG_ZLFDMA	0x00000010	/* Zero-length DMA */
+#define AR5K_RXCFG_JUMBO_RXE	0x00000020	/* Enable jumbo frames reception (?) [5211+] */
+#define AR5K_RXCFG_JUMBO_WRAP	0x00000040	/* Wrap jumbo frames (?) [5211+] */
+
+/*
+ * Receive jumbo descriptor last address register
+ * Only found in 5211 (?)
+ */
+#define AR5K_RXJLA		0x0038
+
+/*
+ * MIB control register
+ */
+#define AR5K_MIBC		0x0040			/* Register Address */
+#define AR5K_MIBC_COW		0x00000001
+#define AR5K_MIBC_FMC		0x00000002	/* Freeze Mib Counters (?) */
+#define AR5K_MIBC_CMC		0x00000004	/* Clean Mib Counters (?) */
+#define AR5K_MIBC_MCS		0x00000008
+
+/*
+ * Timeout prescale register
+ */
+#define AR5K_TOPS		0x0044
+#define	AR5K_TOPS_M		0x0000ffff	/* [5211+] (?) */
+
+/*
+ * Receive timeout register (no frame received)
+ */
+#define AR5K_RXNOFRM		0x0048
+#define	AR5K_RXNOFRM_M		0x000003ff	/* [5211+] (?) */
+
+/*
+ * Transmit timeout register (no frame sent)
+ */
+#define AR5K_TXNOFRM		0x004c
+#define	AR5K_TXNOFRM_M		0x000003ff	/* [5211+] (?) */
+#define	AR5K_TXNOFRM_QCU	0x000ffc00	/* [5211+] (?) */
+
+/*
+ * Receive frame gap timeout register
+ */
+#define AR5K_RPGTO		0x0050
+#define AR5K_RPGTO_M		0x000003ff	/* [5211+] (?) */
+
+/*
+ * Receive frame count limit register
+ */
+#define AR5K_RFCNT		0x0054
+#define AR5K_RFCNT_M		0x0000001f	/* [5211+] (?) */
+#define AR5K_RFCNT_RFCL		0x0000000f	/* [5210] */
+
+/*
+ * Misc settings register
+ */
+#define AR5K_MISC		0x0058			/* Register Address */
+#define	AR5K_MISC_DMA_OBS_M	0x000001e0
+#define	AR5K_MISC_DMA_OBS_S	5
+#define	AR5K_MISC_MISC_OBS_M	0x00000e00
+#define	AR5K_MISC_MISC_OBS_S	9
+#define	AR5K_MISC_MAC_OBS_LSB_M	0x00007000
+#define	AR5K_MISC_MAC_OBS_LSB_S	12
+#define	AR5K_MISC_MAC_OBS_MSB_M	0x00038000
+#define	AR5K_MISC_MAC_OBS_MSB_S	15
+#define AR5K_MISC_LED_DECAY	0x001c0000	/* [5210] */
+#define AR5K_MISC_LED_BLINK	0x00e00000	/* [5210] */
+
+/*
+ * QCU/DCU clock gating register (5311)
+ */
+#define	AR5K_QCUDCU_CLKGT	0x005c			/* Register Address (?) */
+#define	AR5K_QCUDCU_CLKGT_QCU	0x0000ffff	/* Mask for QCU clock */
+#define	AR5K_QCUDCU_CLKGT_DCU	0x07ff0000	/* Mask for DCU clock */
+
+/*
+ * Interrupt Status Registers
+ *
+ * For 5210 there is only one status register but for
+ * 5211/5212 we have one primary and 4 secondary registers.
+ * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212.
+ * Most of these bits are common for all chipsets.
+ */
+#define AR5K_ISR		0x001c			/* Register Address [5210] */
+#define AR5K_PISR		0x0080			/* Register Address [5211+] */
+#define AR5K_ISR_RXOK		0x00000001	/* Frame successfuly recieved */
+#define AR5K_ISR_RXDESC		0x00000002	/* RX descriptor request */
+#define AR5K_ISR_RXERR		0x00000004	/* Receive error */
+#define AR5K_ISR_RXNOFRM	0x00000008	/* No frame received (receive timeout) */
+#define AR5K_ISR_RXEOL		0x00000010	/* Empty RX descriptor */
+#define AR5K_ISR_RXORN		0x00000020	/* Receive FIFO overrun */
+#define AR5K_ISR_TXOK		0x00000040	/* Frame successfuly transmited */
+#define AR5K_ISR_TXDESC		0x00000080	/* TX descriptor request */
+#define AR5K_ISR_TXERR		0x00000100	/* Transmit error */
+#define AR5K_ISR_TXNOFRM	0x00000200	/* No frame transmited (transmit timeout) */
+#define AR5K_ISR_TXEOL		0x00000400	/* Empty TX descriptor */
+#define AR5K_ISR_TXURN		0x00000800	/* Transmit FIFO underrun */
+#define AR5K_ISR_MIB		0x00001000	/* Update MIB counters */
+#define AR5K_ISR_SWI		0x00002000	/* Software interrupt (?) */
+#define AR5K_ISR_RXPHY		0x00004000	/* PHY error */
+#define AR5K_ISR_RXKCM		0x00008000
+#define AR5K_ISR_SWBA		0x00010000	/* Software beacon alert */
+#define AR5K_ISR_BRSSI		0x00020000
+#define AR5K_ISR_BMISS		0x00040000	/* Beacon missed */
+#define AR5K_ISR_HIUERR		0x00080000	/* Host Interface Unit error [5211+] */
+#define AR5K_ISR_BNR		0x00100000 	/* Beacon not ready [5211+] */
+#define AR5K_ISR_MCABT		0x00100000	/* [5210] */
+#define AR5K_ISR_RXCHIRP	0x00200000	/* [5212+] */
+#define AR5K_ISR_SSERR		0x00200000	/* [5210] */
+#define AR5K_ISR_DPERR		0x00400000	/* [5210] */
+#define AR5K_ISR_TIM		0x00800000	/* [5210] */
+#define AR5K_ISR_BCNMISC	0x00800000	/* [5212+] */
+#define AR5K_ISR_GPIO		0x01000000	/* GPIO (rf kill)*/
+#define AR5K_ISR_QCBRORN	0x02000000	/* CBR overrun (?)  [5211+] */
+#define AR5K_ISR_QCBRURN	0x04000000	/* CBR underrun (?) [5211+] */
+#define AR5K_ISR_QTRIG		0x08000000	/* [5211+] */
+
+/*
+ * Secondary status registers [5211+] (0 - 4)
+ *
+ * I guess from the names that these give the status for each
+ * queue, that's why only masks are defined here, haven't got
+ * any info about them (couldn't find them anywhere in ar5k code).
+ */
+#define AR5K_SISR0		0x0084			/* Register Address [5211+] */
+#define AR5K_SISR0_QCU_TXOK	0x000003ff	/* Mask for QCU_TXOK */
+#define AR5K_SISR0_QCU_TXDESC	0x03ff0000	/* Mask for QCU_TXDESC */
+
+#define AR5K_SISR1		0x0088			/* Register Address [5211+] */
+#define AR5K_SISR1_QCU_TXERR	0x000003ff	/* Mask for QCU_TXERR */
+#define AR5K_SISR1_QCU_TXEOL	0x03ff0000	/* Mask for QCU_TXEOL */
+
+#define AR5K_SISR2		0x008c			/* Register Address [5211+] */
+#define AR5K_SISR2_QCU_TXURN	0x000003ff	/* Mask for QCU_TXURN */
+#define	AR5K_SISR2_MCABT	0x00100000
+#define	AR5K_SISR2_SSERR	0x00200000
+#define	AR5K_SISR2_DPERR	0x00400000
+#define	AR5K_SISR2_TIM		0x01000000	/* [5212+] */
+#define	AR5K_SISR2_CAB_END	0x02000000	/* [5212+] */
+#define	AR5K_SISR2_DTIM_SYNC	0x04000000	/* [5212+] */
+#define	AR5K_SISR2_BCN_TIMEOUT	0x08000000	/* [5212+] */
+#define	AR5K_SISR2_CAB_TIMEOUT	0x10000000	/* [5212+] */
+#define	AR5K_SISR2_DTIM		0x20000000	/* [5212+] */
+
+#define AR5K_SISR3		0x0090			/* Register Address [5211+] */
+#define AR5K_SISR3_QCBRORN	0x000003ff	/* Mask for QCBRORN */
+#define AR5K_SISR3_QCBRURN	0x03ff0000	/* Mask for QCBRURN */
+
+#define AR5K_SISR4		0x0094			/* Register Address [5211+] */
+#define AR5K_SISR4_QTRIG	0x000003ff	/* Mask for QTRIG */
+
+/*
+ * Shadow read-and-clear interrupt status registers [5211+]
+ */
+#define AR5K_RAC_PISR		0x00c0		/* Read and clear PISR */
+#define AR5K_RAC_SISR0		0x00c4		/* Read and clear SISR0 */
+#define AR5K_RAC_SISR1		0x00c8		/* Read and clear SISR1 */
+#define AR5K_RAC_SISR2		0x00cc		/* Read and clear SISR2 */
+#define AR5K_RAC_SISR3		0x00d0		/* Read and clear SISR3 */
+#define AR5K_RAC_SISR4		0x00d4		/* Read and clear SISR4 */
+
+/*
+ * Interrupt Mask Registers
+ *
+ * As whith ISRs 5210 has one IMR (AR5K_IMR) and 5211/5212 has one primary
+ * (AR5K_PIMR) and 4 secondary IMRs (AR5K_SIMRx). Note that ISR/IMR flags match.
+ */
+#define	AR5K_IMR		0x0020			/* Register Address [5210] */
+#define AR5K_PIMR		0x00a0			/* Register Address [5211+] */
+#define AR5K_IMR_RXOK		0x00000001	/* Frame successfuly recieved*/
+#define AR5K_IMR_RXDESC		0x00000002	/* RX descriptor request*/
+#define AR5K_IMR_RXERR		0x00000004	/* Receive error*/
+#define AR5K_IMR_RXNOFRM	0x00000008	/* No frame received (receive timeout)*/
+#define AR5K_IMR_RXEOL		0x00000010	/* Empty RX descriptor*/
+#define AR5K_IMR_RXORN		0x00000020	/* Receive FIFO overrun*/
+#define AR5K_IMR_TXOK		0x00000040	/* Frame successfuly transmited*/
+#define AR5K_IMR_TXDESC		0x00000080	/* TX descriptor request*/
+#define AR5K_IMR_TXERR		0x00000100	/* Transmit error*/
+#define AR5K_IMR_TXNOFRM	0x00000200	/* No frame transmited (transmit timeout)*/
+#define AR5K_IMR_TXEOL		0x00000400	/* Empty TX descriptor*/
+#define AR5K_IMR_TXURN		0x00000800	/* Transmit FIFO underrun*/
+#define AR5K_IMR_MIB		0x00001000	/* Update MIB counters*/
+#define AR5K_IMR_SWI		0x00002000
+#define AR5K_IMR_RXPHY		0x00004000	/* PHY error*/
+#define AR5K_IMR_RXKCM		0x00008000
+#define AR5K_IMR_SWBA		0x00010000	/* Software beacon alert*/
+#define AR5K_IMR_BRSSI		0x00020000
+#define AR5K_IMR_BMISS		0x00040000	/* Beacon missed*/
+#define AR5K_IMR_HIUERR		0x00080000	/* Host Interface Unit error [5211+] */
+#define AR5K_IMR_BNR		0x00100000 	/* Beacon not ready [5211+] */
+#define AR5K_IMR_MCABT		0x00100000	/* [5210] */
+#define AR5K_IMR_RXCHIRP	0x00200000	/* [5212+]*/
+#define AR5K_IMR_SSERR		0x00200000	/* [5210] */
+#define AR5K_IMR_DPERR		0x00400000	/* [5210] */
+#define AR5K_IMR_TIM		0x00800000	/* [5211+] */
+#define AR5K_IMR_BCNMISC	0x00800000	/* [5212+] */
+#define AR5K_IMR_GPIO		0x01000000	/* GPIO (rf kill)*/
+#define AR5K_IMR_QCBRORN	0x02000000	/* CBR overrun (?) [5211+] */
+#define AR5K_IMR_QCBRURN	0x04000000	/* CBR underrun (?) [5211+] */
+#define AR5K_IMR_QTRIG		0x08000000	/* [5211+] */
+
+/*
+ * Secondary interrupt mask registers [5211+] (0 - 4)
+ */
+#define AR5K_SIMR0		0x00a4			/* Register Address [5211+] */
+#define AR5K_SIMR0_QCU_TXOK	0x000003ff	/* Mask for QCU_TXOK */
+#define AR5K_SIMR0_QCU_TXOK_S	0
+#define AR5K_SIMR0_QCU_TXDESC	0x03ff0000	/* Mask for QCU_TXDESC */
+#define AR5K_SIMR0_QCU_TXDESC_S	16
+
+#define AR5K_SIMR1		0x00a8			/* Register Address [5211+] */
+#define AR5K_SIMR1_QCU_TXERR	0x000003ff	/* Mask for QCU_TXERR */
+#define AR5K_SIMR1_QCU_TXERR_S	0
+#define AR5K_SIMR1_QCU_TXEOL	0x03ff0000	/* Mask for QCU_TXEOL */
+#define AR5K_SIMR1_QCU_TXEOL_S	16
+
+#define AR5K_SIMR2		0x00ac			/* Register Address [5211+] */
+#define AR5K_SIMR2_QCU_TXURN	0x000003ff	/* Mask for QCU_TXURN */
+#define AR5K_SIMR2_QCU_TXURN_S	0
+#define	AR5K_SIMR2_MCABT	0x00100000
+#define	AR5K_SIMR2_SSERR	0x00200000
+#define	AR5K_SIMR2_DPERR	0x00400000
+#define	AR5K_SIMR2_TIM		0x01000000	/* [5212+] */
+#define	AR5K_SIMR2_CAB_END	0x02000000	/* [5212+] */
+#define	AR5K_SIMR2_DTIM_SYNC	0x04000000	/* [5212+] */
+#define	AR5K_SIMR2_BCN_TIMEOUT	0x08000000	/* [5212+] */
+#define	AR5K_SIMR2_CAB_TIMEOUT	0x10000000	/* [5212+] */
+#define	AR5K_SIMR2_DTIM		0x20000000	/* [5212+] */
+
+#define AR5K_SIMR3		0x00b0			/* Register Address [5211+] */
+#define AR5K_SIMR3_QCBRORN	0x000003ff	/* Mask for QCBRORN */
+#define AR5K_SIMR3_QCBRORN_S	0
+#define AR5K_SIMR3_QCBRURN	0x03ff0000	/* Mask for QCBRURN */
+#define AR5K_SIMR3_QCBRURN_S	16
+
+#define AR5K_SIMR4		0x00b4			/* Register Address [5211+] */
+#define AR5K_SIMR4_QTRIG	0x000003ff	/* Mask for QTRIG */
+#define AR5K_SIMR4_QTRIG_S	0
+
+
+/*
+ * Decompression mask registers [5212+]
+ */
+#define AR5K_DCM_ADDR		0x0400		/*Decompression mask address (?)*/
+#define AR5K_DCM_DATA		0x0404		/*Decompression mask data (?)*/
+
+/*
+ * Decompression configuration registers [5212+]
+ */
+#define AR5K_DCCFG		0x0420
+
+/*
+ * Compression configuration registers [5212+]
+ */
+#define AR5K_CCFG		0x0600
+#define AR5K_CCFG_CUP		0x0604
+
+/*
+ * Compression performance counter registers [5212+]
+ */
+#define AR5K_CPC0		0x0610		/* Compression performance counter 0 */
+#define AR5K_CPC1		0x0614		/* Compression performance counter 1*/
+#define AR5K_CPC2		0x0618		/* Compression performance counter 2 */
+#define AR5K_CPC3		0x061c		/* Compression performance counter 3 */
+#define AR5K_CPCORN		0x0620		/* Compression performance overrun (?) */
+
+
+/*
+ * Queue control unit (QCU) registers [5211+]
+ *
+ * Card has 12 TX Queues but i see that only 0-9 are used (?)
+ * both in binary HAL (see ah.h) and ar5k. Each queue has it's own
+ * TXDP at addresses 0x0800 - 0x082c, a CBR (Constant Bit Rate)
+ * configuration register (0x08c0 - 0x08ec), a ready time configuration
+ * register (0x0900 - 0x092c), a misc configuration register (0x09c0 -
+ * 0x09ec) and a status register (0x0a00 - 0x0a2c). We also have some
+ * global registers, QCU transmit enable/disable and "one shot arm (?)"
+ * set/clear, which contain status for all queues (we shift by 1 for each
+ * queue). To access these registers easily we define some macros here
+ * that are used inside HAL. For more infos check out *_tx_queue functs.
+ *
+ * TODO: Boundary checking on macros (here?)
+ */
+
+/*
+ * Generic QCU Register access macros
+ */
+#define	AR5K_QUEUE_REG(_r, _q)		(((_q) << 2) + _r)
+#define AR5K_QCU_GLOBAL_READ(_r, _q)	(AR5K_REG_READ(_r) & (1 << _q))
+#define AR5K_QCU_GLOBAL_WRITE(_r, _q)	AR5K_REG_WRITE(_r, (1 << _q))
+
+/*
+ * QCU Transmit descriptor pointer registers
+ */
+#define AR5K_QCU_TXDP_BASE	0x0800		/* Register Address - Queue0 TXDP */
+#define AR5K_QUEUE_TXDP(_q)	AR5K_QUEUE_REG(AR5K_QCU_TXDP_BASE, _q)
+
+/*
+ * QCU Transmit enable register
+ */
+#define AR5K_QCU_TXE		0x0840
+#define AR5K_ENABLE_QUEUE(_q)	AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXE, _q)
+#define AR5K_QUEUE_ENABLED(_q)	AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXE, _q)
+
+/*
+ * QCU Transmit disable register
+ */
+#define AR5K_QCU_TXD		0x0880
+#define AR5K_DISABLE_QUEUE(_q)	AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXD, _q)
+#define AR5K_QUEUE_DISABLED(_q)	AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXD, _q)
+
+/*
+ * QCU Constant Bit Rate configuration registers
+ */
+#define	AR5K_QCU_CBRCFG_BASE		0x08c0	/* Register Address - Queue0 CBRCFG */
+#define	AR5K_QCU_CBRCFG_INTVAL		0x00ffffff	/* CBR Interval mask */
+#define AR5K_QCU_CBRCFG_INTVAL_S	0
+#define	AR5K_QCU_CBRCFG_ORN_THRES	0xff000000	/* CBR overrun threshold mask */
+#define AR5K_QCU_CBRCFG_ORN_THRES_S	24
+#define	AR5K_QUEUE_CBRCFG(_q)		AR5K_QUEUE_REG(AR5K_QCU_CBRCFG_BASE, _q)
+
+/*
+ * QCU Ready time configuration registers
+ */
+#define	AR5K_QCU_RDYTIMECFG_BASE	0x0900	/* Register Address - Queue0 RDYTIMECFG */
+#define	AR5K_QCU_RDYTIMECFG_INTVAL	0x00ffffff	/* Ready time interval mask */
+#define AR5K_QCU_RDYTIMECFG_INTVAL_S	0
+#define	AR5K_QCU_RDYTIMECFG_DURATION	0x00ffffff	/* Ready time duration mask */
+#define	AR5K_QCU_RDYTIMECFG_ENABLE	0x01000000	/* Ready time enable mask */
+#define AR5K_QUEUE_RDYTIMECFG(_q)	AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q)
+
+/*
+ * QCU one shot arm set registers
+ */
+#define	AR5K_QCU_ONESHOTARM_SET		0x0940	/* Register Address -QCU "one shot arm set (?)" */
+#define	AR5K_QCU_ONESHOTARM_SET_M	0x0000ffff
+
+/*
+ * QCU one shot arm clear registers
+ */
+#define	AR5K_QCU_ONESHOTARM_CLEAR	0x0980	/* Register Address -QCU "one shot arm clear (?)" */
+#define	AR5K_QCU_ONESHOTARM_CLEAR_M	0x0000ffff
+
+/*
+ * QCU misc registers
+ */
+#define AR5K_QCU_MISC_BASE		0x09c0			/* Register Address -Queue0 MISC */
+#define	AR5K_QCU_MISC_FRSHED_M		0x0000000f	/* Frame sheduling mask */
+#define	AR5K_QCU_MISC_FRSHED_ASAP	0		/* ASAP */
+#define	AR5K_QCU_MISC_FRSHED_CBR	1		/* Constant Bit Rate */
+#define	AR5K_QCU_MISC_FRSHED_DBA_GT	2		/* DMA Beacon alert gated (?) */
+#define	AR5K_QCU_MISC_FRSHED_TIM_GT	3		/* Time gated (?) */
+#define	AR5K_QCU_MISC_FRSHED_BCN_SENT_GT	4	/* Beacon sent gated (?) */
+#define	AR5K_QCU_MISC_ONESHOT_ENABLE	0x00000010	/* Oneshot enable */
+#define	AR5K_QCU_MISC_CBREXP		0x00000020	/* CBR expired (normal queue) */
+#define	AR5K_QCU_MISC_CBREXP_BCN	0x00000040	/* CBR expired (beacon queue) */
+#define	AR5K_QCU_MISC_BCN_ENABLE	0x00000080	/* Beacons enabled */
+#define	AR5K_QCU_MISC_CBR_THRES_ENABLE	0x00000100	/* CBR threshold enabled (?) */
+#define	AR5K_QCU_MISC_TXE		0x00000200	/* TXE reset when RDYTIME enalbed (?) */
+#define	AR5K_QCU_MISC_CBR		0x00000400	/* CBR threshold reset (?) */
+#define	AR5K_QCU_MISC_DCU_EARLY		0x00000800	/* DCU reset (?) */
+#define AR5K_QUEUE_MISC(_q)		AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q)
+
+
+/*
+ * QCU status registers
+ */
+#define AR5K_QCU_STS_BASE	0x0a00			/* Register Address - Queue0 STS */
+#define	AR5K_QCU_STS_FRMPENDCNT	0x00000003	/* Frames pending counter */
+#define	AR5K_QCU_STS_CBREXPCNT	0x0000ff00	/* CBR expired counter (?) */
+#define	AR5K_QUEUE_STATUS(_q)	AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q)
+
+/*
+ * QCU ready time shutdown register
+ */
+#define AR5K_QCU_RDYTIMESHDN	0x0a40
+#define AR5K_QCU_RDYTIMESHDN_M	0x000003ff
+
+/*
+ * QCU compression buffer base registers [5212+]
+ */
+#define AR5K_QCU_CBB_SELECT	0x0b00
+#define AR5K_QCU_CBB_ADDR	0x0b04
+
+/*
+ * QCU compression buffer configuration register [5212+]
+ */
+#define AR5K_QCU_CBCFG		0x0b08
+
+
+
+/*
+ * Distributed Coordination Function (DCF) control unit (DCU)
+ * registers [5211+]
+ *
+ * These registers control the various characteristics of each queue
+ * for 802.11e (WME) combatibility so they go together with
+ * QCU registers in pairs. For each queue we have a QCU mask register,
+ * (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c),
+ * a retry limit register (0x1080 - 0x10ac), a channel time register
+ * (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and
+ * a sequence number register (0x1140 - 0x116c). It seems that "global"
+ * registers here afect all queues (see use of DCU_GBL_IFS_SLOT in ar5k).
+ * We use the same macros here for easier register access.
+ *
+ */
+
+/*
+ * DCU QCU mask registers
+ */
+#define AR5K_DCU_QCUMASK_BASE	0x1000		/* Register Address -Queue0 DCU_QCUMASK */
+#define AR5K_DCU_QCUMASK_M	0x000003ff
+#define AR5K_QUEUE_QCUMASK(_q)	AR5K_QUEUE_REG(AR5K_DCU_QCUMASK_BASE, _q)
+
+/*
+ * DCU local Inter Frame Space settings register
+ */
+#define AR5K_DCU_LCL_IFS_BASE		0x1040			/* Register Address -Queue0 DCU_LCL_IFS */
+#define	AR5K_DCU_LCL_IFS_CW_MIN	        0x000003ff	/* Minimum Contention Window */
+#define	AR5K_DCU_LCL_IFS_CW_MIN_S	0
+#define	AR5K_DCU_LCL_IFS_CW_MAX	        0x000ffc00	/* Maximum Contention Window */
+#define	AR5K_DCU_LCL_IFS_CW_MAX_S	10
+#define	AR5K_DCU_LCL_IFS_AIFS		0x0ff00000	/* Arbitrated Interframe Space */
+#define	AR5K_DCU_LCL_IFS_AIFS_S		20
+#define	AR5K_QUEUE_DFS_LOCAL_IFS(_q)	AR5K_QUEUE_REG(AR5K_DCU_LCL_IFS_BASE, _q)
+
+/*
+ * DCU retry limit registers
+ */
+#define AR5K_DCU_RETRY_LMT_BASE		0x1080			/* Register Address -Queue0 DCU_RETRY_LMT */
+#define AR5K_DCU_RETRY_LMT_SH_RETRY	0x0000000f	/* Short retry limit mask */
+#define AR5K_DCU_RETRY_LMT_SH_RETRY_S	0
+#define AR5K_DCU_RETRY_LMT_LG_RETRY	0x000000f0	/* Long retry limit mask */
+#define AR5K_DCU_RETRY_LMT_LG_RETRY_S	4
+#define AR5K_DCU_RETRY_LMT_SSH_RETRY	0x00003f00	/* Station short retry limit mask (?) */
+#define AR5K_DCU_RETRY_LMT_SSH_RETRY_S	8
+#define AR5K_DCU_RETRY_LMT_SLG_RETRY	0x000fc000	/* Station long retry limit mask (?) */
+#define AR5K_DCU_RETRY_LMT_SLG_RETRY_S	14
+#define	AR5K_QUEUE_DFS_RETRY_LIMIT(_q)	AR5K_QUEUE_REG(AR5K_DCU_RETRY_LMT_BASE, _q)
+
+/*
+ * DCU channel time registers
+ */
+#define AR5K_DCU_CHAN_TIME_BASE		0x10c0			/* Register Address -Queue0 DCU_CHAN_TIME */
+#define	AR5K_DCU_CHAN_TIME_DUR		0x000fffff	/* Channel time duration */
+#define	AR5K_DCU_CHAN_TIME_DUR_S	0
+#define	AR5K_DCU_CHAN_TIME_ENABLE	0x00100000	/* Enable channel time */
+#define AR5K_QUEUE_DFS_CHANNEL_TIME(_q)	AR5K_QUEUE_REG(AR5K_DCU_CHAN_TIME_BASE, _q)
+
+/*
+ * DCU misc registers [5211+]
+ *
+ * For some of the registers i couldn't find in the code
+ * (only backoff stuff is there realy) i tried to match the
+ * names with 802.11e parameters etc, so i guess VIRTCOL here
+ * means Virtual Collision and HCFPOLL means Hybrid Coordination
+ * factor Poll (CF- Poll). Arbiter lockout control controls the
+ * behaviour on low priority queues when we have multiple queues
+ * with pending frames. Intra-frame lockout means we wait until
+ * the queue's current frame transmits (with post frame backoff and bursting)
+ * before we transmit anything else and global lockout means we
+ * wait for the whole queue to finish before higher priority queues
+ * can transmit (this is used on beacon and CAB queues).
+ * No lockout means there is no special handling.
+ */
+#define AR5K_DCU_MISC_BASE		0x1100			/* Register Address -Queue0 DCU_MISC */
+#define	AR5K_DCU_MISC_BACKOFF		0x000007ff	/* Mask for backoff setting (?) */
+#define AR5K_DCU_MISC_BACKOFF_FRAG	0x00000200	/* Enable backoff while bursting */
+#define	AR5K_DCU_MISC_HCFPOLL_ENABLE	0x00000800	/* CF - Poll (?) */
+#define	AR5K_DCU_MISC_BACKOFF_PERSIST	0x00001000	/* Persistent backoff (?) */
+#define	AR5K_DCU_MISC_FRMPRFTCH_ENABLE	0x00002000	/* Enable frame pre-fetch (?) */
+#define	AR5K_DCU_MISC_VIRTCOL		0x0000c000	/* Mask for Virtual Collision (?) */
+#define	AR5K_DCU_MISC_VIRTCOL_NORMAL	0
+#define	AR5K_DCU_MISC_VIRTCOL_MODIFIED	1
+#define	AR5K_DCU_MISC_VIRTCOL_IGNORE	2
+#define	AR5K_DCU_MISC_BCN_ENABLE	0x00010000	/* Beacon enable (?) */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL	0x00060000	/* Arbiter lockout control mask */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_S	17
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_NONE	0		/* No arbiter lockout */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM	1	/* Intra-frame lockout */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL	2	/* Global lockout */
+#define	AR5K_DCU_MISC_ARBLOCK_IGNORE	0x00080000
+#define	AR5K_DCU_MISC_SEQ_NUM_INCR_DIS	0x00100000	/* Disable sequence number increment (?) */
+#define	AR5K_DCU_MISC_POST_FR_BKOFF_DIS	0x00200000	/* Disable post-frame backoff (?) */
+#define	AR5K_DCU_MISC_VIRT_COLL_POLICY	0x00400000	/* Virtual Collision policy (?) */
+#define	AR5K_DCU_MISC_BLOWN_IFS_POLICY	0x00800000
+#define	AR5K_DCU_MISC_SEQNUM_CTL	0x01000000	/* Sequence number control (?) */
+#define AR5K_QUEUE_DFS_MISC(_q)		AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q)
+
+/*
+ * DCU frame sequence number registers
+ */
+#define AR5K_DCU_SEQNUM_BASE	0x1140
+#define	AR5K_DCU_SEQNUM_M	0x00000fff
+#define	AR5K_QUEUE_DFS_SEQNUM(_q)	AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q)
+
+/*
+ * DCU global IFS SIFS registers
+ */
+#define AR5K_DCU_GBL_IFS_SIFS	0x1030
+#define AR5K_DCU_GBL_IFS_SIFS_M	0x0000ffff
+
+/*
+ * DCU global IFS slot interval registers
+ */
+#define AR5K_DCU_GBL_IFS_SLOT	0x1070
+#define AR5K_DCU_GBL_IFS_SLOT_M	0x0000ffff
+
+/*
+ * DCU global IFS EIFS registers
+ */
+#define AR5K_DCU_GBL_IFS_EIFS	0x10b0
+#define AR5K_DCU_GBL_IFS_EIFS_M	0x0000ffff
+
+/*
+ * DCU global IFS misc registers
+ */
+#define AR5K_DCU_GBL_IFS_MISC			0x10f0			/* Register Address */
+#define	AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE	0x00000007
+#define	AR5K_DCU_GBL_IFS_MISC_TURBO_MODE	0x00000008	/* Turbo mode (?) */
+#define	AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC	0x000003f0	/* SIFS Duration mask (?) */
+#define	AR5K_DCU_GBL_IFS_MISC_USEC_DUR		0x000ffc00
+#define	AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY	0x00300000
+
+/*
+ * DCU frame prefetch control register
+ */
+#define AR5K_DCU_FP		0x1230
+
+/*
+ * DCU transmit pause control/status register
+ */
+#define AR5K_DCU_TXP		0x1270			/* Register Address */
+#define	AR5K_DCU_TXP_M		0x000003ff	/* Tx pause mask (?) */
+#define	AR5K_DCU_TXP_STATUS	0x00010000	/* Tx pause status (?) */
+
+/*
+ * DCU transmit filter register
+ */
+#define AR5K_DCU_TX_FILTER	0x1038
+
+/*
+ * DCU clear transmit filter register
+ */
+#define AR5K_DCU_TX_FILTER_CLR	0x143c
+
+/*
+ * DCU set transmit filter register
+ */
+#define AR5K_DCU_TX_FILTER_SET	0x147c
+
+/*
+ * Reset control register
+ *
+ * 4 and 8 are not used in 5211/5212 and
+ * 2 means "baseband reset" on 5211/5212.
+ */
+#define AR5K_RESET_CTL		0x4000			/* Register Address */
+#define AR5K_RESET_CTL_PCU	0x00000001	/* Protocol Control Unit reset */
+#define AR5K_RESET_CTL_DMA	0x00000002	/* DMA (Rx/Tx) reset [5210] */
+#define	AR5K_RESET_CTL_BASEBAND	0x00000002	/* Baseband reset [5211+] */
+#define AR5K_RESET_CTL_MAC	0x00000004	/* MAC reset (PCU+Baseband ?) [5210] */
+#define AR5K_RESET_CTL_PHY	0x00000008	/* PHY reset [5210] */
+#define AR5K_RESET_CTL_PCI	0x00000010	/* PCI Core reset (interrupts etc) */
+#define AR5K_RESET_CTL_CHIP	(AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA |	\
+				AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY)
+
+/*
+ * Sleep control register
+ */
+#define AR5K_SLEEP_CTL			0x4004			/* Register Address */
+#define AR5K_SLEEP_CTL_SLDUR		0x0000ffff	/* Sleep duration mask */
+#define AR5K_SLEEP_CTL_SLDUR_S		0
+#define AR5K_SLEEP_CTL_SLE		0x00030000	/* Sleep enable mask */
+#define AR5K_SLEEP_CTL_SLE_S		16
+#define AR5K_SLEEP_CTL_SLE_WAKE		0x00000000	/* Force chip awake */
+#define AR5K_SLEEP_CTL_SLE_SLP		0x00010000	/* Force chip sleep */
+#define AR5K_SLEEP_CTL_SLE_ALLOW	0x00020000
+#define AR5K_SLEEP_CTL_SLE_UNITS	0x00000008	/* [5211+] */
+
+/*
+ * Interrupt pending register
+ */
+#define AR5K_INTPEND	0x4008
+#define AR5K_INTPEND_M	0x00000001
+
+/*
+ * Sleep force register
+ */
+#define AR5K_SFR	0x400c
+#define AR5K_SFR_M	0x00000001
+
+/*
+ * PCI configuration register
+ */
+#define AR5K_PCICFG			0x4010			/* Register Address */
+#define AR5K_PCICFG_EEAE		0x00000001	/* Eeprom access enable [5210] */
+#define AR5K_PCICFG_CLKRUNEN		0x00000004	/* CLKRUN enable [5211+] */
+#define AR5K_PCICFG_EESIZE		0x00000018	/* Mask for EEPROM size [5211+] */
+#define AR5K_PCICFG_EESIZE_S		3
+#define AR5K_PCICFG_EESIZE_4K		0		/* 4K */
+#define AR5K_PCICFG_EESIZE_8K		1		/* 8K */
+#define AR5K_PCICFG_EESIZE_16K		2		/* 16K */
+#define AR5K_PCICFG_EESIZE_FAIL		3		/* Failed to get size (?) [5211+] */
+#define AR5K_PCICFG_LED			0x00000060	/* Led status [5211+] */
+#define AR5K_PCICFG_LED_NONE		0x00000000	/* Default [5211+] */
+#define AR5K_PCICFG_LED_PEND		0x00000020	/* Scan / Auth pending */
+#define AR5K_PCICFG_LED_ASSOC		0x00000040	/* Associated */
+#define	AR5K_PCICFG_BUS_SEL		0x00000380	/* Mask for "bus select" [5211+] (?) */
+#define	AR5K_PCICFG_CBEFIX_DIS		0x00000400	/* Disable CBE fix (?) */
+#define AR5K_PCICFG_SL_INTEN		0x00000800	/* Enable interrupts when asleep (?) */
+#define AR5K_PCICFG_LED_BCTL		0x00001000	/* Led blink (?) [5210] */
+#define AR5K_PCICFG_SL_INPEN		0x00002800	/* Sleep even whith pending interrupts (?) */
+#define AR5K_PCICFG_SPWR_DN		0x00010000	/* Mask for power status */
+#define AR5K_PCICFG_LEDMODE		0x000e0000	/* Ledmode [5211+] */
+#define AR5K_PCICFG_LEDMODE_PROP	0x00000000	/* Blink on standard traffic [5211+] */
+#define AR5K_PCICFG_LEDMODE_PROM	0x00020000	/* Default mode (blink on any traffic) [5211+] */
+#define AR5K_PCICFG_LEDMODE_PWR		0x00040000	/* Some other blinking mode  (?) [5211+] */
+#define AR5K_PCICFG_LEDMODE_RAND	0x00060000	/* Random blinking (?) [5211+] */
+#define AR5K_PCICFG_LEDBLINK		0x00700000
+#define AR5K_PCICFG_LEDBLINK_S		20
+#define AR5K_PCICFG_LEDSLOW		0x00800000	/* Slow led blink rate (?) [5211+] */
+#define AR5K_PCICFG_LEDSTATE				\
+	(AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE |	\
+	AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW)
+
+/*
+ * "General Purpose Input/Output" (GPIO) control register
+ *
+ * I'm not sure about this but after looking at the code
+ * for all chipsets here is what i got.
+ *
+ * We have 6 GPIOs (pins), each GPIO has 4 modes (2 bits)
+ * Mode 0 -> always input
+ * Mode 1 -> output when GPIODO for this GPIO is set to 0
+ * Mode 2 -> output when GPIODO for this GPIO is set to 1
+ * Mode 3 -> always output
+ *
+ * For more infos check out get_gpio/set_gpio and
+ * set_gpio_input/set_gpio_output functs.
+ * For more infos on gpio interrupt check out set_gpio_intr.
+ */
+#define AR5K_NUM_GPIO	6
+
+#define AR5K_GPIOCR		0x4014				/* Register Address */
+#define AR5K_GPIOCR_INT_ENA	0x00008000		/* Enable GPIO interrupt */
+#define AR5K_GPIOCR_INT_SELL	0x00000000		/* Generate interrupt when pin is off (?) */
+#define AR5K_GPIOCR_INT_SELH	0x00010000		/* Generate interrupt when pin is on */
+#define AR5K_GPIOCR_IN(n)	(0 << ((n) * 2))	/* Mode 0 for pin n */
+#define AR5K_GPIOCR_OUT0(n)	(1 << ((n) * 2))	/* Mode 1 for pin n */
+#define AR5K_GPIOCR_OUT1(n)	(2 << ((n) * 2))	/* Mode 2 for pin n */
+#define AR5K_GPIOCR_OUT(n)	(3 << ((n) * 2))	/* Mode 3 for pin n */
+#define AR5K_GPIOCR_INT_SEL(n)	((n) << 12)		/* Interrupt for GPIO pin n */
+
+/*
+ * "General Purpose Input/Output" (GPIO) data output register
+ */
+#define AR5K_GPIODO	0x4018
+
+/*
+ * "General Purpose Input/Output" (GPIO) data input register
+ */
+#define AR5K_GPIODI	0x401c
+#define AR5K_GPIODI_M	0x0000002f
+
+
+/*
+ * Silicon revision register
+ */
+#define AR5K_SREV		0x4020			/* Register Address */
+#define AR5K_SREV_REV		0x0000000f	/* Mask for revision */
+#define AR5K_SREV_REV_S		0
+#define AR5K_SREV_VER		0x000000ff	/* Mask for version */
+#define AR5K_SREV_VER_S		4
+
+
+
+/*====EEPROM REGISTERS====*/
+
+/*
+ * EEPROM access registers
+ *
+ * Here we got a difference between 5210/5211-12
+ * read data register for 5210 is at 0x6800 and
+ * status register is at 0x6c00. There is also
+ * no eeprom command register on 5210 and the
+ * offsets are different.
+ *
+ * To read eeprom data for a specific offset:
+ * 5210 - enable eeprom access (AR5K_PCICFG_EEAE)
+ *        read AR5K_EEPROM_BASE +(4 * offset)
+ *        check the eeprom status register
+ *        and read eeprom data register.
+ *
+ * 5211 - write offset to AR5K_EEPROM_BASE
+ * 5212   write AR5K_EEPROM_CMD_READ on AR5K_EEPROM_CMD
+ *        check the eeprom status register
+ *        and read eeprom data register.
+ *
+ * To write eeprom data for a specific offset:
+ * 5210 - enable eeprom access (AR5K_PCICFG_EEAE)
+ *        write data to AR5K_EEPROM_BASE +(4 * offset)
+ *        check the eeprom status register
+ * 5211 - write AR5K_EEPROM_CMD_RESET on AR5K_EEPROM_CMD
+ * 5212   write offset to AR5K_EEPROM_BASE
+ *        write data to data register
+ *	  write AR5K_EEPROM_CMD_WRITE on AR5K_EEPROM_CMD
+ *        check the eeprom status register
+ *
+ * For more infos check eeprom_* functs and the ar5k.c
+ * file posted in madwifi-devel mailing list.
+ * http://sourceforge.net/mailarchive/message.php?msg_id=8966525
+ *
+ */
+#define AR5K_EEPROM_BASE	0x6000
+
+/*
+ * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE)
+ */
+#define AR5K_EEPROM_MAGIC		0x003d	/* EEPROM Magic number */
+#define AR5K_EEPROM_MAGIC_VALUE		0x5aa5	/* Default - found on EEPROM */
+#define AR5K_EEPROM_MAGIC_5212		0x0000145c /* 5212 */
+#define AR5K_EEPROM_MAGIC_5211		0x0000145b /* 5211 */
+#define AR5K_EEPROM_MAGIC_5210		0x0000145a /* 5210 */
+
+#define AR5K_EEPROM_PROTECT		0x003f	/* EEPROM protect status */
+#define AR5K_EEPROM_PROTECT_RD_0_31	0x0001	/* Read protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_WR_0_31	0x0002	/* Write protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_RD_32_63	0x0004	/* 0x20 - 0x3f */
+#define AR5K_EEPROM_PROTECT_WR_32_63	0x0008
+#define AR5K_EEPROM_PROTECT_RD_64_127	0x0010	/* 0x40 - 0x7f */
+#define AR5K_EEPROM_PROTECT_WR_64_127	0x0020
+#define AR5K_EEPROM_PROTECT_RD_128_191	0x0040	/* 0x80 - 0xbf (regdom) */
+#define AR5K_EEPROM_PROTECT_WR_128_191	0x0080
+#define AR5K_EEPROM_PROTECT_RD_192_207	0x0100	/* 0xc0 - 0xcf */
+#define AR5K_EEPROM_PROTECT_WR_192_207	0x0200
+#define AR5K_EEPROM_PROTECT_RD_208_223	0x0400	/* 0xd0 - 0xdf */
+#define AR5K_EEPROM_PROTECT_WR_208_223	0x0800
+#define AR5K_EEPROM_PROTECT_RD_224_239	0x1000	/* 0xe0 - 0xef */
+#define AR5K_EEPROM_PROTECT_WR_224_239	0x2000
+#define AR5K_EEPROM_PROTECT_RD_240_255	0x4000	/* 0xf0 - 0xff */
+#define AR5K_EEPROM_PROTECT_WR_240_255	0x8000
+#define AR5K_EEPROM_REG_DOMAIN		0x00bf	/* EEPROM regdom */
+#define AR5K_EEPROM_INFO_BASE		0x00c0	/* EEPROM header */
+#define AR5K_EEPROM_INFO_MAX		(0x400 - AR5K_EEPROM_INFO_BASE)
+#define AR5K_EEPROM_INFO_CKSUM		0xffff
+#define AR5K_EEPROM_INFO(_n)		(AR5K_EEPROM_INFO_BASE + (_n))
+
+#define AR5K_EEPROM_VERSION		AR5K_EEPROM_INFO(1)	/* EEPROM Version */
+#define AR5K_EEPROM_VERSION_3_0		0x3000	/* No idea what's going on before this version */
+#define AR5K_EEPROM_VERSION_3_1		0x3001	/* ob/db values for 2Ghz (ar5211_rfregs) */
+#define AR5K_EEPROM_VERSION_3_2		0x3002	/* different frequency representation (eeprom_bin2freq) */
+#define AR5K_EEPROM_VERSION_3_3		0x3003	/* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
+#define AR5K_EEPROM_VERSION_3_4		0x3004	/* has ee_i_gain ee_cck_ofdm_power_delta (eeprom_read_modes) */
+#define AR5K_EEPROM_VERSION_4_0		0x4000	/* has ee_misc*, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_1		0x4001	/* has ee_margin_tx_rx (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_2		0x4002	/* has ee_cck_ofdm_gain_delta (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_3		0x4003
+#define AR5K_EEPROM_VERSION_4_4		0x4004
+#define AR5K_EEPROM_VERSION_4_5		0x4005
+#define AR5K_EEPROM_VERSION_4_6		0x4006	/* has ee_scaled_cck_delta */
+#define AR5K_EEPROM_VERSION_4_7		0x3007
+
+#define AR5K_EEPROM_MODE_11A		0
+#define AR5K_EEPROM_MODE_11B		1
+#define AR5K_EEPROM_MODE_11G		2
+
+#define AR5K_EEPROM_HDR			AR5K_EEPROM_INFO(2)	/* Header that contains the device caps */
+#define AR5K_EEPROM_HDR_11A(_v)		(((_v) >> AR5K_EEPROM_MODE_11A) & 0x1)
+#define AR5K_EEPROM_HDR_11B(_v)		(((_v) >> AR5K_EEPROM_MODE_11B) & 0x1)
+#define AR5K_EEPROM_HDR_11G(_v)		(((_v) >> AR5K_EEPROM_MODE_11G) & 0x1)
+#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v)	(((_v) >> 3) & 0x1)	/* Disable turbo for 2Ghz (?) */
+#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v)	(((_v) >> 4) & 0x7f)	/* Max turbo power for a/XR mode (eeprom_init) */
+#define AR5K_EEPROM_HDR_DEVICE(_v)	(((_v) >> 11) & 0x7)
+#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v)	(((_v) >> 15) & 0x1)	/* Disable turbo for 5Ghz (?) */
+#define AR5K_EEPROM_HDR_RFKILL(_v)	(((_v) >> 14) & 0x1)	/* Device has RFKill support */
+
+#define AR5K_EEPROM_RFKILL_GPIO_SEL	0x0000001c
+#define AR5K_EEPROM_RFKILL_GPIO_SEL_S	2
+#define AR5K_EEPROM_RFKILL_POLARITY	0x00000002
+#define AR5K_EEPROM_RFKILL_POLARITY_S	1
+
+/* Newer EEPROMs are using a different offset */
+#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \
+	(((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0)
+
+#define AR5K_EEPROM_ANT_GAIN(_v)	AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3)
+#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v)	((int8_t)(((_v) >> 8) & 0xff))
+#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v)	((int8_t)((_v) & 0xff))
+
+/* calibration settings */
+#define AR5K_EEPROM_MODES_11A(_v)	AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4)
+#define AR5K_EEPROM_MODES_11B(_v)	AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2)
+#define AR5K_EEPROM_MODES_11G(_v)	AR5K_EEPROM_OFF(_v, 0x00da, 0x010d)
+#define AR5K_EEPROM_CTL(_v)		AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128)	/* Conformance test limits */
+
+/* [3.1 - 3.3] */
+#define AR5K_EEPROM_OBDB0_2GHZ		0x00ec
+#define AR5K_EEPROM_OBDB1_2GHZ		0x00ed
+
+/* Misc values available since EEPROM 4.0 */
+#define AR5K_EEPROM_MISC0		0x00c4
+#define AR5K_EEPROM_EARSTART(_v)	((_v) & 0xfff)
+#define AR5K_EEPROM_EEMAP(_v)		(((_v) >> 14) & 0x3)
+#define AR5K_EEPROM_MISC1		0x00c5
+#define AR5K_EEPROM_TARGET_PWRSTART(_v)	((_v) & 0xfff)
+#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v)	(((_v) >> 14) & 0x1)
+
+/*
+ * EEPROM data register
+ */
+#define AR5K_EEPROM_DATA_5211	0x6004
+#define AR5K_EEPROM_DATA_5210	0x6800
+#define	AR5K_EEPROM_DATA	(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211)
+
+/*
+ * EEPROM command register
+ */
+#define AR5K_EEPROM_CMD		0x6008			/* Register Addres */
+#define AR5K_EEPROM_CMD_READ	0x00000001	/* EEPROM read */
+#define AR5K_EEPROM_CMD_WRITE	0x00000002	/* EEPROM write */
+#define AR5K_EEPROM_CMD_RESET	0x00000004	/* EEPROM reset */
+
+/*
+ * EEPROM status register
+ */
+#define AR5K_EEPROM_STAT_5210	0x6c00			/* Register Address [5210] */
+#define AR5K_EEPROM_STAT_5211	0x600c			/* Register Address [5211+] */
+#define	AR5K_EEPROM_STATUS	(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211)
+#define AR5K_EEPROM_STAT_RDERR	0x00000001	/* EEPROM read failed */
+#define AR5K_EEPROM_STAT_RDDONE	0x00000002	/* EEPROM read successful */
+#define AR5K_EEPROM_STAT_WRERR	0x00000004	/* EEPROM write failed */
+#define AR5K_EEPROM_STAT_WRDONE	0x00000008	/* EEPROM write successful */
+
+/*
+ * EEPROM config register (?)
+ */
+#define AR5K_EEPROM_CFG	0x6010
+
+
+
+/*
+ * Protocol Control Unit (PCU) registers
+ */
+/*
+ * Used for checking initial register writes
+ * during channel reset (see reset func)
+ */
+#define AR5K_PCU_MIN	0x8000
+#define AR5K_PCU_MAX	0x8fff
+
+/*
+ * First station id register (MAC address in lower 32 bits)
+ */
+#define AR5K_STA_ID0	0x8000
+
+/*
+ * Second station id register (MAC address in upper 16 bits)
+ */
+#define AR5K_STA_ID1			0x8004			/* Register Address */
+#define AR5K_STA_ID1_AP			0x00010000	/* Set AP mode */
+#define AR5K_STA_ID1_ADHOC		0x00020000	/* Set Ad-Hoc mode */
+#define AR5K_STA_ID1_PWR_SV		0x00040000	/* Power save reporting (?) */
+#define AR5K_STA_ID1_NO_KEYSRCH		0x00080000	/* No key search */
+#define AR5K_STA_ID1_NO_PSPOLL		0x00100000	/* No power save polling [5210] */
+#define AR5K_STA_ID1_PCF_5211		0x00100000	/* Enable PCF on [5211+] */
+#define AR5K_STA_ID1_PCF_5210		0x00200000	/* Enable PCF on [5210]*/
+#define	AR5K_STA_ID1_PCF		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211)
+#define AR5K_STA_ID1_DEFAULT_ANTENNA	0x00200000	/* Use default antenna */
+#define AR5K_STA_ID1_DESC_ANTENNA	0x00400000	/* Update antenna from descriptor */
+#define AR5K_STA_ID1_RTS_DEF_ANTENNA	0x00800000	/* Use default antenna for RTS (?) */
+#define AR5K_STA_ID1_ACKCTS_6MB		0x01000000	/* Use 6Mbit/s for ACK/CTS (?) */
+#define AR5K_STA_ID1_BASE_RATE_11B	0x02000000	/* Use 11b base rate (for ACK/CTS ?) [5211+] */
+
+/*
+ * First BSSID register (MAC address, lower 32bits)
+ */
+#define AR5K_BSS_ID0	0x8008
+
+/*
+ * Second BSSID register (MAC address in upper 16 bits)
+ *
+ * AID: Association ID
+ */
+#define AR5K_BSS_ID1		0x800c
+#define AR5K_BSS_ID1_AID	0xffff0000
+#define AR5K_BSS_ID1_AID_S	16
+
+/*
+ * Backoff slot time register
+ */
+#define AR5K_SLOT_TIME	0x8010
+
+/*
+ * ACK/CTS timeout register
+ */
+#define AR5K_TIME_OUT		0x8014			/* Register Address */
+#define AR5K_TIME_OUT_ACK	0x00001fff	/* ACK timeout mask */
+#define AR5K_TIME_OUT_ACK_S	0
+#define AR5K_TIME_OUT_CTS	0x1fff0000	/* CTS timeout mask */
+#define AR5K_TIME_OUT_CTS_S	16
+
+/*
+ * RSSI threshold register
+ */
+#define AR5K_RSSI_THR			0x8018		/* Register Address */
+#define AR5K_RSSI_THR_M			0x000000ff	/* Mask for RSSI threshold [5211+] */
+#define AR5K_RSSI_THR_BMISS_5210	0x00000700	/* Mask for Beacon Missed threshold [5210] */
+#define AR5K_RSSI_THR_BMISS_5210_S	8
+#define AR5K_RSSI_THR_BMISS_5211	0x0000ff00	/* Mask for Beacon Missed threshold [5211+] */
+#define AR5K_RSSI_THR_BMISS_5211_S	8
+#define	AR5K_RSSI_THR_BMISS		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_RSSI_THR_BMISS_5210 : AR5K_RSSI_THR_BMISS_5211)
+#define	AR5K_RSSI_THR_BMISS_S		8
+
+/*
+ * 5210 has more PCU registers because there is no QCU/DCU
+ * so queue parameters are set here, this way a lot common
+ * registers have different address for 5210. To make things
+ * easier we define a macro based on hal->ah_version for common
+ * registers with different addresses and common flags.
+ */
+
+/*
+ * Retry limit register
+ *
+ * Retry limit register for 5210 (no QCU/DCU so it's done in PCU)
+ */
+#define AR5K_NODCU_RETRY_LMT		0x801c			/*Register Address */
+#define AR5K_NODCU_RETRY_LMT_SH_RETRY	0x0000000f	/* Short retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SH_RETRY_S	0
+#define AR5K_NODCU_RETRY_LMT_LG_RETRY	0x000000f0	/* Long retry mask */
+#define AR5K_NODCU_RETRY_LMT_LG_RETRY_S	4
+#define AR5K_NODCU_RETRY_LMT_SSH_RETRY	0x00003f00	/* Station short retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SSH_RETRY_S	8
+#define AR5K_NODCU_RETRY_LMT_SLG_RETRY	0x000fc000	/* Station long retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SLG_RETRY_S	14
+#define AR5K_NODCU_RETRY_LMT_CW_MIN	0x3ff00000	/* Minimum contention window mask */
+#define AR5K_NODCU_RETRY_LMT_CW_MIN_S	20
+
+/*
+ * Transmit latency register
+ */
+#define AR5K_USEC_5210			0x8020			/* Register Address [5210] */
+#define AR5K_USEC_5211			0x801c			/* Register Address [5211+] */
+#define AR5K_USEC			(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_USEC_5210 : AR5K_USEC_5211)
+#define AR5K_USEC_1			0x0000007f
+#define AR5K_USEC_1_S			0
+#define AR5K_USEC_32			0x00003f80
+#define AR5K_USEC_32_S			7
+#define AR5K_USEC_TX_LATENCY_5211	0x007fc000
+#define AR5K_USEC_TX_LATENCY_5211_S	14
+#define AR5K_USEC_RX_LATENCY_5211	0x1f800000
+#define AR5K_USEC_RX_LATENCY_5211_S	23
+#define AR5K_USEC_TX_LATENCY_5210	0x000fc000	/* also for 5311 */
+#define AR5K_USEC_TX_LATENCY_5210_S	14
+#define AR5K_USEC_RX_LATENCY_5210	0x03f00000	/* also for 5311 */
+#define AR5K_USEC_RX_LATENCY_5210_S	20
+
+/*
+ * PCU beacon control register
+ */
+#define AR5K_BEACON_5210	0x8024
+#define AR5K_BEACON_5211	0x8020
+#define AR5K_BEACON		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_BEACON_5210 : AR5K_BEACON_5211)
+#define AR5K_BEACON_PERIOD	0x0000ffff
+#define AR5K_BEACON_PERIOD_S	0
+#define AR5K_BEACON_TIM		0x007f0000
+#define AR5K_BEACON_TIM_S	16
+#define AR5K_BEACON_ENABLE	0x00800000
+#define AR5K_BEACON_RESET_TSF	0x01000000
+
+/*
+ * CFP period register
+ */
+#define AR5K_CFP_PERIOD_5210	0x8028
+#define AR5K_CFP_PERIOD_5211	0x8024
+#define AR5K_CFP_PERIOD		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_CFP_PERIOD_5210 : AR5K_CFP_PERIOD_5211)
+
+/*
+ * Next beacon time register
+ */
+#define AR5K_TIMER0_5210	0x802c
+#define AR5K_TIMER0_5211	0x8028
+#define AR5K_TIMER0		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER0_5210 : AR5K_TIMER0_5211)
+
+/*
+ * Next DMA beacon alert register
+ */
+#define AR5K_TIMER1_5210	0x8030
+#define AR5K_TIMER1_5211	0x802c
+#define AR5K_TIMER1		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER1_5210 : AR5K_TIMER1_5211)
+
+/*
+ * Next software beacon alert register
+ */
+#define AR5K_TIMER2_5210	0x8034
+#define AR5K_TIMER2_5211	0x8030
+#define AR5K_TIMER2		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER2_5210 : AR5K_TIMER2_5211)
+
+/*
+ * Next ATIM window time register
+ */
+#define AR5K_TIMER3_5210	0x8038
+#define AR5K_TIMER3_5211	0x8034
+#define AR5K_TIMER3		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER3_5210 : AR5K_TIMER3_5211)
+
+
+/*
+ * 5210 First inter frame spacing register (IFS)
+ */
+#define AR5K_IFS0		0x8040
+#define AR5K_IFS0_SIFS		0x000007ff
+#define AR5K_IFS0_SIFS_S	0
+#define AR5K_IFS0_DIFS		0x007ff800
+#define AR5K_IFS0_DIFS_S	11
+
+/*
+ * 5210 Second inter frame spacing register (IFS)
+ */
+#define AR5K_IFS1		0x8044
+#define AR5K_IFS1_PIFS		0x00000fff
+#define AR5K_IFS1_PIFS_S	0
+#define AR5K_IFS1_EIFS		0x03fff000
+#define AR5K_IFS1_EIFS_S	12
+#define AR5K_IFS1_CS_EN		0x04000000
+
+
+/*
+ * CFP duration register
+ */
+#define AR5K_CFP_DUR_5210	0x8048
+#define AR5K_CFP_DUR_5211	0x8038
+#define AR5K_CFP_DUR		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_CFP_DUR_5210 : AR5K_CFP_DUR_5211)
+
+/*
+ * Receive filter register
+ * TODO: Get these out of ar5xxx.h on ath5k
+ */
+#define AR5K_RX_FILTER_5210	0x804c			/* Register Address [5210] */
+#define AR5K_RX_FILTER_5211	0x803c			/* Register Address [5211+] */
+#define AR5K_RX_FILTER		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211)
+#define	AR5K_RX_FILTER_UCAST 	0x00000001	/* Don't filter unicast frames */
+#define	AR5K_RX_FILTER_MCAST 	0x00000002	/* Don't filter multicast frames */
+#define	AR5K_RX_FILTER_BCAST 	0x00000004	/* Don't filter broadcast frames */
+#define	AR5K_RX_FILTER_CONTROL 	0x00000008	/* Don't filter control frames */
+#define	AR5K_RX_FILTER_BEACON 	0x00000010	/* Don't filter beacon frames */
+#define	AR5K_RX_FILTER_PROM 	0x00000020	/* Set promiscuous mode */
+#define	AR5K_RX_FILTER_XRPOLL 	0x00000040	/* Don't filter XR poll frame [5212+] */
+#define	AR5K_RX_FILTER_PROBEREQ 0x00000080	/* Don't filter probe requests [5212+] */
+#define	AR5K_RX_FILTER_PHYERR_5212	0x00000100	/* Don't filter phy errors [5212+] */
+#define	AR5K_RX_FILTER_RADARERR_5212 	0x00000200	/* Don't filter phy radar errors [5212+] */
+#define AR5K_RX_FILTER_PHYERR_5211	0x00000040	/* [5211] */
+#define AR5K_RX_FILTER_RADARERR_5211	0x00000080	/* [5211] */
+#define AR5K_RX_FILTER_PHYERR	(ah->ah_version == AR5K_AR5211 ? \
+				AR5K_RX_FILTER_PHYERR_5211 : AR5K_RX_FILTER_PHYERR_5212)
+#define	AR5K_RX_FILTER_RADARERR	(ah->ah_version == AR5K_AR5211 ? \
+				AR5K_RX_FILTER_RADARERR_5211 : AR5K_RX_FILTER_RADARERR_5212)
+/*
+ * Multicast filter register (lower 32 bits)
+ */
+#define AR5K_MCAST_FILTER0_5210	0x8050
+#define AR5K_MCAST_FILTER0_5211	0x8040
+#define AR5K_MCAST_FILTER0	(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_MCAST_FILTER0_5210 : AR5K_MCAST_FILTER0_5211)
+
+/*
+ * Multicast filter register (higher 16 bits)
+ */
+#define AR5K_MCAST_FILTER1_5210	0x8054
+#define AR5K_MCAST_FILTER1_5211	0x8044
+#define AR5K_MCAST_FILTER1	(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_MCAST_FILTER1_5210 : AR5K_MCAST_FILTER1_5211)
+
+
+/*
+ * Transmit mask register (lower 32 bits) [5210]
+ */
+#define AR5K_TX_MASK0	0x8058
+
+/*
+ * Transmit mask register (higher 16 bits) [5210]
+ */
+#define AR5K_TX_MASK1	0x805c
+
+/*
+ * Clear transmit mask [5210]
+ */
+#define AR5K_CLR_TMASK	0x8060
+
+/*
+ * Trigger level register (before transmission) [5210]
+ */
+#define AR5K_TRIG_LVL	0x8064
+
+
+/*
+ * PCU control register
+ *
+ * Only DIS_RX is used in the code, the rest i guess are
+ * for tweaking/diagnostics.
+ */
+#define AR5K_DIAG_SW_5210		0x8068			/* Register Address [5210] */
+#define AR5K_DIAG_SW_5211		0x8048			/* Register Address [5211+] */
+#define AR5K_DIAG_SW			(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211)
+#define AR5K_DIAG_SW_DIS_WEP_ACK	0x00000001
+#define AR5K_DIAG_SW_DIS_ACK		0x00000002	/* Disable ACKs (?) */
+#define AR5K_DIAG_SW_DIS_CTS		0x00000004	/* Disable CTSs (?) */
+#define AR5K_DIAG_SW_DIS_ENC		0x00000008	/* Disable encryption (?) */
+#define AR5K_DIAG_SW_DIS_DEC		0x00000010	/* Disable decryption (?) */
+#define AR5K_DIAG_SW_DIS_TX		0x00000020	/* Disable transmit [5210] */
+#define AR5K_DIAG_SW_DIS_RX_5210	0x00000040	/* Disable recieve */
+#define AR5K_DIAG_SW_DIS_RX_5211	0x00000020
+#define	AR5K_DIAG_SW_DIS_RX		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211)
+#define AR5K_DIAG_SW_LOOP_BACK_5210	0x00000080	/* Loopback (i guess it goes with DIS_TX) [5210] */
+#define AR5K_DIAG_SW_LOOP_BACK_5211	0x00000040
+#define AR5K_DIAG_SW_LOOP_BACK		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211)
+#define AR5K_DIAG_SW_CORR_FCS_5210	0x00000100
+#define AR5K_DIAG_SW_CORR_FCS_5211	0x00000080
+#define AR5K_DIAG_SW_CORR_FCS		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211)
+#define AR5K_DIAG_SW_CHAN_INFO_5210	0x00000200
+#define AR5K_DIAG_SW_CHAN_INFO_5211	0x00000100
+#define AR5K_DIAG_SW_CHAN_INFO		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211)
+#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211	0x00000200	/* Scrambler seed (?) */
+#define AR5K_DIAG_SW_EN_SCRAM_SEED_5210	0x00000400
+#define AR5K_DIAG_SW_EN_SCRAM_SEED	(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211)
+#define AR5K_DIAG_SW_ECO_ENABLE		0x00000400	/* [5211+] */
+#define AR5K_DIAG_SW_SCVRAM_SEED	0x0003f800	/* [5210] */
+#define AR5K_DIAG_SW_SCRAM_SEED_M	0x0001fc00	/* Scrambler seed mask (?) */
+#define AR5K_DIAG_SW_SCRAM_SEED_S	10
+#define AR5K_DIAG_SW_DIS_SEQ_INC	0x00040000	/* Disable seqnum increment (?)[5210] */
+#define AR5K_DIAG_SW_FRAME_NV0_5210	0x00080000
+#define AR5K_DIAG_SW_FRAME_NV0_5211	0x00020000
+#define	AR5K_DIAG_SW_FRAME_NV0		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211)
+#define AR5K_DIAG_SW_OBSPT_M		0x000c0000
+#define AR5K_DIAG_SW_OBSPT_S		18
+
+/*
+ * TSF (clock) register (lower 32 bits)
+ */
+#define AR5K_TSF_L32_5210	0x806c
+#define AR5K_TSF_L32_5211	0x804c
+#define	AR5K_TSF_L32		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211)
+
+/*
+ * TSF (clock) register (higher 32 bits)
+ */
+#define AR5K_TSF_U32_5210	0x8070
+#define AR5K_TSF_U32_5211	0x8050
+#define	AR5K_TSF_U32		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211)
+
+/*
+ * Last beacon timestamp register
+ */
+#define AR5K_LAST_TSTP	0x8080
+
+/*
+ * ADDAC test register [5211+]
+ */
+#define AR5K_ADDAC_TEST	0x8054
+#define AR5K_ADDAC_TEST_TXCONT 0x00000001
+
+/*
+ * Default antenna register [5211+]
+ */
+#define AR5K_DEFAULT_ANTENNA	0x8058
+
+
+
+/*
+ * Retry count register [5210]
+ */
+#define AR5K_RETRY_CNT		0x8084			/* Register Address [5210] */
+#define AR5K_RETRY_CNT_SSH	0x0000003f	/* Station short retry count (?) */
+#define AR5K_RETRY_CNT_SLG	0x00000fc0	/* Station long retry count (?) */
+
+/*
+ * Back-off status register [5210]
+ */
+#define AR5K_BACKOFF		0x8088			/* Register Address [5210] */
+#define AR5K_BACKOFF_CW		0x000003ff	/* Backoff Contention Window (?) */
+#define AR5K_BACKOFF_CNT	0x03ff0000	/* Backoff count (?) */
+
+
+
+/*
+ * NAV register (current)
+ */
+#define AR5K_NAV_5210		0x808c
+#define AR5K_NAV_5211		0x8084
+#define	AR5K_NAV		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_NAV_5210 : AR5K_NAV_5211)
+
+/*
+ * RTS success register
+ */
+#define AR5K_RTS_OK_5210	0x8090
+#define AR5K_RTS_OK_5211	0x8088
+#define	AR5K_RTS_OK		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211)
+
+/*
+ * RTS failure register
+ */
+#define AR5K_RTS_FAIL_5210	0x8094
+#define AR5K_RTS_FAIL_5211	0x808c
+#define	AR5K_RTS_FAIL		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211)
+
+/*
+ * ACK failure register
+ */
+#define AR5K_ACK_FAIL_5210	0x8098
+#define AR5K_ACK_FAIL_5211	0x8090
+#define	AR5K_ACK_FAIL		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211)
+
+/*
+ * FCS failure register
+ */
+#define AR5K_FCS_FAIL_5210	0x809c
+#define AR5K_FCS_FAIL_5211	0x8094
+#define	AR5K_FCS_FAIL		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_FCS_FAIL_5210 : AR5K_FCS_FAIL_5211)
+
+/*
+ * Beacon count register
+ */
+#define AR5K_BEACON_CNT_5210	0x80a0
+#define AR5K_BEACON_CNT_5211	0x8098
+#define	AR5K_BEACON_CNT		(hal->ah_version == AR5K_AR5210 ? \
+				AR5K_BEACON_CNT_5210 : AR5K_BEACON_CNT_5211)
+
+
+/*===5212 Specific PCU registers===*/
+
+/*
+ * XR (eXtended Range) mode register
+ */
+#define AR5K_XRMODE			0x80c0
+#define	AR5K_XRMODE_POLL_TYPE_M		0x0000003f
+#define	AR5K_XRMODE_POLL_TYPE_S		0
+#define	AR5K_XRMODE_POLL_SUBTYPE_M	0x0000003c
+#define	AR5K_XRMODE_POLL_SUBTYPE_S	2
+#define	AR5K_XRMODE_POLL_WAIT_ALL	0x00000080
+#define	AR5K_XRMODE_SIFS_DELAY		0x000fff00
+#define	AR5K_XRMODE_FRAME_HOLD_M	0xfff00000
+#define	AR5K_XRMODE_FRAME_HOLD_S	20
+
+/*
+ * XR delay register
+ */
+#define AR5K_XRDELAY			0x80c4
+#define AR5K_XRDELAY_SLOT_DELAY_M	0x0000ffff
+#define AR5K_XRDELAY_SLOT_DELAY_S	0
+#define AR5K_XRDELAY_CHIRP_DELAY_M	0xffff0000
+#define AR5K_XRDELAY_CHIRP_DELAY_S	16
+
+/*
+ * XR timeout register
+ */
+#define AR5K_XRTIMEOUT			0x80c8
+#define AR5K_XRTIMEOUT_CHIRP_M		0x0000ffff
+#define AR5K_XRTIMEOUT_CHIRP_S		0
+#define AR5K_XRTIMEOUT_POLL_M		0xffff0000
+#define AR5K_XRTIMEOUT_POLL_S		16
+
+/*
+ * XR chirp register
+ */
+#define AR5K_XRCHIRP			0x80cc
+#define AR5K_XRCHIRP_SEND		0x00000001
+#define AR5K_XRCHIRP_GAP		0xffff0000
+
+/*
+ * XR stomp register
+ */
+#define AR5K_XRSTOMP			0x80d0
+#define AR5K_XRSTOMP_TX			0x00000001
+#define AR5K_XRSTOMP_RX_ABORT		0x00000002
+#define AR5K_XRSTOMP_RSSI_THRES		0x0000ff00
+
+/*
+ * First enhanced sleep register
+ */
+#define AR5K_SLEEP0			0x80d4
+#define AR5K_SLEEP0_NEXT_DTIM		0x0007ffff
+#define AR5K_SLEEP0_NEXT_DTIM_S		0
+#define AR5K_SLEEP0_ASSUME_DTIM		0x00080000
+#define AR5K_SLEEP0_ENH_SLEEP_EN	0x00100000
+#define AR5K_SLEEP0_CABTO		0xff000000
+#define AR5K_SLEEP0_CABTO_S		24
+
+/*
+ * Second enhanced sleep register
+ */
+#define AR5K_SLEEP1			0x80d8
+#define AR5K_SLEEP1_NEXT_TIM		0x0007ffff
+#define AR5K_SLEEP1_NEXT_TIM_S		0
+#define AR5K_SLEEP1_BEACON_TO		0xff000000
+#define AR5K_SLEEP1_BEACON_TO_S		24
+
+/*
+ * Third enhanced sleep register
+ */
+#define AR5K_SLEEP2			0x80dc
+#define AR5K_SLEEP2_TIM_PER		0x0000ffff
+#define AR5K_SLEEP2_TIM_PER_S		0
+#define AR5K_SLEEP2_DTIM_PER		0xffff0000
+#define AR5K_SLEEP2_DTIM_PER_S		16
+
+/*
+ * BSSID mask registers
+ */
+#define AR5K_BSS_IDM0			0x80e0
+#define AR5K_BSS_IDM1			0x80e4
+
+/*
+ * TX power control (TPC) register
+ */
+#define AR5K_TXPC			0x80e8
+#define AR5K_TXPC_ACK_M			0x0000003f
+#define AR5K_TXPC_ACK_S			0
+#define AR5K_TXPC_CTS_M			0x00003f00
+#define AR5K_TXPC_CTS_S			8
+#define AR5K_TXPC_CHIRP_M		0x003f0000
+#define AR5K_TXPC_CHIRP_S		22
+
+/*
+ * Profile count registers
+ */
+#define AR5K_PROFCNT_TX			0x80ec
+#define AR5K_PROFCNT_RX			0x80f0
+#define AR5K_PROFCNT_RXCLR		0x80f4
+#define AR5K_PROFCNT_CYCLE		0x80f8
+
+/*
+ * TSF parameter register
+ */
+#define AR5K_TSF_PARM			0x8104
+#define AR5K_TSF_PARM_INC_M		0x000000ff
+#define AR5K_TSF_PARM_INC_S		0
+
+/*
+ * PHY error filter register
+ */
+#define AR5K_PHY_ERR_FIL		0x810c
+#define AR5K_PHY_ERR_FIL_RADAR		0x00000020
+#define AR5K_PHY_ERR_FIL_OFDM		0x00020000
+#define AR5K_PHY_ERR_FIL_CCK		0x02000000
+
+/*
+ * Rate duration register
+ */
+#define AR5K_RATE_DUR_BASE		0x8700
+#define AR5K_RATE_DUR(_n)		(AR5K_RATE_DUR_BASE + ((_n) << 2))
+
+/*===5212 end===*/
+
+/*
+ * Key table (WEP) register
+ */
+#define AR5K_KEYTABLE_0_5210		0x9000
+#define AR5K_KEYTABLE_0_5211		0x8800
+#define AR5K_KEYTABLE_5210(_n)		(AR5K_KEYTABLE_0_5210 + ((_n) << 5))
+#define AR5K_KEYTABLE_5211(_n)		(AR5K_KEYTABLE_0_5211 + ((_n) << 5))
+#define	AR5K_KEYTABLE(_n)		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_KEYTABLE_5210(_n) : AR5K_KEYTABLE_5211(_n))
+#define AR5K_KEYTABLE_OFF(_n, x)	(AR5K_KEYTABLE(_n) + (x << 2))
+#define AR5K_KEYTABLE_TYPE(_n)		AR5K_KEYTABLE_OFF(_n, 5)
+#define AR5K_KEYTABLE_TYPE_40		0x00000000
+#define AR5K_KEYTABLE_TYPE_104		0x00000001
+#define AR5K_KEYTABLE_TYPE_128		0x00000003
+#define AR5K_KEYTABLE_TYPE_TKIP		0x00000004	/* [5212+] */
+#define AR5K_KEYTABLE_TYPE_AES		0x00000005	/* [5211+] */
+#define AR5K_KEYTABLE_TYPE_CCM		0x00000006	/* [5212+] */
+#define AR5K_KEYTABLE_TYPE_NULL		0x00000007	/* [5211+] */
+#define AR5K_KEYTABLE_ANTENNA		0x00000008	/* [5212+] */
+#define AR5K_KEYTABLE_MAC0(_n)		AR5K_KEYTABLE_OFF(_n, 6)
+#define AR5K_KEYTABLE_MAC1(_n)		AR5K_KEYTABLE_OFF(_n, 7)
+#define AR5K_KEYTABLE_VALID		0x00008000
+
+/* WEP 40-bit	= 40-bit  entered key + 24 bit IV = 64-bit
+ * WEP 104-bit	= 104-bit entered key + 24-bit IV = 128-bit
+ * WEP 128-bit	= 128-bit entered key + 24 bit IV = 152-bit
+ *
+ * Some vendors have introduced bigger WEP keys to address
+ * security vulnerabilities in WEP. This includes:
+ *
+ * WEP 232-bit = 232-bit entered key + 24 bit IV = 256-bit
+ *
+ * We can expand this if we find ar5k Atheros cards with a larger
+ * key table size.
+ */
+#define AR5K_KEYTABLE_SIZE_5210		64
+#define AR5K_KEYTABLE_SIZE_5211		128
+#define	AR5K_KEYTABLE_SIZE		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211)
+
+
+/*===PHY REGISTERS===*/
+
+/*
+ * PHY register
+ */
+#define	AR5K_PHY_BASE			0x9800
+#define	AR5K_PHY(_n)			(AR5K_PHY_BASE + ((_n) << 2))
+#define AR5K_PHY_SHIFT_2GHZ		0x00004007
+#define AR5K_PHY_SHIFT_5GHZ		0x00000007
+
+/*
+ * PHY frame control register [5110] /turbo mode register [5111+]
+ *
+ * There is another frame control register for [5111+]
+ * at address 0x9944 (see below) but the 2 first flags
+ * are common here between 5110 frame control register
+ * and [5111+] turbo mode register, so this also works as
+ * a "turbo mode register" for 5110. We treat this one as
+ * a frame control register for 5110 below.
+ */
+#define	AR5K_PHY_TURBO			0x9804
+#define	AR5K_PHY_TURBO_MODE		0x00000001
+#define	AR5K_PHY_TURBO_SHORT		0x00000002
+
+/*
+ * PHY agility command register
+ */
+#define	AR5K_PHY_AGC			0x9808
+#define	AR5K_PHY_AGC_DISABLE		0x08000000
+
+/*
+ * PHY timing register [5112+]
+ */
+#define	AR5K_PHY_TIMING_3		0x9814
+#define	AR5K_PHY_TIMING_3_DSC_MAN	0xfffe0000
+#define	AR5K_PHY_TIMING_3_DSC_MAN_S	17
+#define	AR5K_PHY_TIMING_3_DSC_EXP	0x0001e000
+#define	AR5K_PHY_TIMING_3_DSC_EXP_S	13
+
+/*
+ * PHY chip revision register
+ */
+#define	AR5K_PHY_CHIP_ID		0x9818
+
+/*
+ * PHY activation register
+ */
+#define	AR5K_PHY_ACT			0x981c
+#define	AR5K_PHY_ACT_ENABLE		0x00000001
+#define	AR5K_PHY_ACT_DISABLE		0x00000002
+
+/*
+ * PHY signal register
+ */
+#define	AR5K_PHY_SIG			0x9858
+#define	AR5K_PHY_SIG_FIRSTEP		0x0003f000
+#define	AR5K_PHY_SIG_FIRSTEP_S		12
+#define	AR5K_PHY_SIG_FIRPWR		0x03fc0000
+#define	AR5K_PHY_SIG_FIRPWR_S		18
+
+/*
+ * PHY coarse agility control register
+ */
+#define	AR5K_PHY_AGCCOARSE		0x985c
+#define	AR5K_PHY_AGCCOARSE_LO		0x00007f80
+#define	AR5K_PHY_AGCCOARSE_LO_S		7
+#define	AR5K_PHY_AGCCOARSE_HI		0x003f8000
+#define	AR5K_PHY_AGCCOARSE_HI_S		15
+
+/*
+ * PHY agility control register
+ */
+#define	AR5K_PHY_AGCCTL			0x9860			/* Register address */
+#define	AR5K_PHY_AGCCTL_CAL		0x00000001	/* Enable PHY calibration */
+#define	AR5K_PHY_AGCCTL_NF		0x00000002	/* Enable Noise Floor calibration */
+
+/*
+ * PHY noise floor status register
+ */
+#define AR5K_PHY_NF			0x9864
+#define AR5K_PHY_NF_M			0x000001ff
+#define AR5K_PHY_NF_ACTIVE		0x00000100
+#define AR5K_PHY_NF_RVAL(_n)		(((_n) >> 19) & AR5K_PHY_NF_M)
+#define AR5K_PHY_NF_AVAL(_n)		(-((_n) ^ AR5K_PHY_NF_M) + 1)
+#define AR5K_PHY_NF_SVAL(_n)		(((_n) & AR5K_PHY_NF_M) | (1 << 9))
+
+/*
+ * PHY ADC saturation register [5110]
+ */
+#define	AR5K_PHY_ADCSAT			0x9868
+#define	AR5K_PHY_ADCSAT_ICNT		0x0001f800
+#define	AR5K_PHY_ADCSAT_ICNT_S		11
+#define	AR5K_PHY_ADCSAT_THR		0x000007e0
+#define	AR5K_PHY_ADCSAT_THR_S		5
+
+/*
+ * PHY sleep registers [5112+]
+ */
+#define AR5K_PHY_SCR			0x9870
+#define AR5K_PHY_SCR_32MHZ		0x0000001f
+#define AR5K_PHY_SLMT			0x9874
+#define AR5K_PHY_SLMT_32MHZ		0x0000007f
+#define AR5K_PHY_SCAL			0x9878
+#define AR5K_PHY_SCAL_32MHZ		0x0000000e
+
+/*
+ * PHY PLL (Phase Locked Loop) control register
+ */
+#define	AR5K_PHY_PLL			0x987c
+#define	AR5K_PHY_PLL_20MHZ		0x13	/* For half rate (?) [5111+] */
+#define	AR5K_PHY_PLL_40MHZ_5211		0x18	/* For 802.11a */
+#define	AR5K_PHY_PLL_40MHZ_5212		0x000000aa
+#define	AR5K_PHY_PLL_40MHZ		(hal->ah_version == AR5K_AR5211 ? \
+					AR5K_PHY_PLL_40MHZ_5211 : AR5K_PHY_PLL_40MHZ_5212)
+#define	AR5K_PHY_PLL_44MHZ_5211		0x19	/* For 802.11b/g */
+#define	AR5K_PHY_PLL_44MHZ_5212		0x000000ab
+#define	AR5K_PHY_PLL_44MHZ		(hal->ah_version == AR5K_AR5211 ? \
+					AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212)
+#define AR5K_PHY_PLL_RF5111		0x00000000
+#define AR5K_PHY_PLL_RF5112		0x00000040
+
+/*
+ * RF Buffer register
+ *
+ * There are some special control registers on the RF chip
+ * that hold various operation settings related mostly to
+ * the analog parts (channel, gain adjustment etc).
+ *
+ * We don't write on those registers directly but
+ * we send a data packet on the buffer register and
+ * then write on another special register to notify hw
+ * to apply the settings. This is done so that control registers
+ * can be dynamicaly programmed during operation and the settings
+ * are applied faster on the hw.
+ *
+ * We sent such data packets during rf initialization and channel change
+ * through ath5k_hw_rf*_rfregs and ath5k_hw_rf*_channel functions.
+ *
+ * The data packets we send during initializadion are inside ath5k_ini_rf
+ * struct (see ath5k_hw.h) and each one is related to an "rf register bank".
+ * We use *rfregs functions to modify them  acording to current operation
+ * mode and eeprom values and pass them all together to the chip.
+ *
+ * It's obvious from the code that 0x989c is the buffer register but
+ * for the other special registers that we write to after sending each
+ * packet, i have no idea. So i'll name them BUFFER_CONTROL_X registers
+ * for now. It's interesting that they are also used for some other operations.
+ *
+ * Also check out ath5k_hw.h and U.S. Patent 6677779 B1 (about buffer
+ * registers and control registers)
+ */
+
+#define AR5K_RF_BUFFER			0x989c
+#define AR5K_RF_BUFFER_CONTROL_0	0x98c0	/* Channel on 5110 */
+#define AR5K_RF_BUFFER_CONTROL_1	0x98c4	/* Bank 7 on 5112 */
+#define AR5K_RF_BUFFER_CONTROL_2	0x98cc	/* Bank 7 on 5111 */
+
+#define AR5K_RF_BUFFER_CONTROL_3	0x98d0	/* Bank 2 on 5112 */
+						/* Channel set on 5111 */
+						/* Used to read radio revision*/
+
+#define AR5K_RF_BUFFER_CONTROL_4	0x98d4  /* RF Stage register on 5110 */
+						/* Bank 0,1,2,6 on 5111 */
+						/* Bank 1 on 5112 */
+						/* Used during activation on 5111 */
+
+#define AR5K_RF_BUFFER_CONTROL_5	0x98d8	/* Bank 3 on 5111 */
+						/* Used during activation on 5111 */
+						/* Channel on 5112 */
+						/* Bank 6 on 5112 */
+
+#define AR5K_RF_BUFFER_CONTROL_6	0x98dc	/* Bank 3 on 5112 */
+
+/*
+ * PHY RF stage register [5210]
+ */
+#define AR5K_PHY_RFSTG			0x98d4
+#define AR5K_PHY_RFSTG_DISABLE		0x00000021
+
+/*
+ * PHY receiver delay register [5111+]
+ */
+#define	AR5K_PHY_RX_DELAY		0x9914
+#define	AR5K_PHY_RX_DELAY_M		0x00003fff
+
+/*
+ * PHY timing I(nphase) Q(adrature) control register [5111+]
+ */
+#define	AR5K_PHY_IQ			0x9920		/* Register address */
+#define	AR5K_PHY_IQ_CORR_Q_Q_COFF	0x0000001f	/* Mask for q correction info */
+#define	AR5K_PHY_IQ_CORR_Q_I_COFF	0x000007e0	/* Mask for i correction info */
+#define	AR5K_PHY_IQ_CORR_Q_I_COFF_S	5
+#define	AR5K_PHY_IQ_CORR_ENABLE		0x00000800	/* Enable i/q correction */
+#define	AR5K_PHY_IQ_CAL_NUM_LOG_MAX	0x0000f000
+#define	AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S	12
+#define	AR5K_PHY_IQ_RUN			0x00010000	/* Run i/q calibration */
+
+
+/*
+ * PHY PAPD probe register [5111+ (?)]
+ * Is this only present in 5212 ?
+ * Because it's always 0 in 5211 initialization code
+ */
+#define	AR5K_PHY_PAPD_PROBE		0x9930
+#define	AR5K_PHY_PAPD_PROBE_TXPOWER	0x00007e00
+#define	AR5K_PHY_PAPD_PROBE_TXPOWER_S	9
+#define	AR5K_PHY_PAPD_PROBE_TX_NEXT	0x00008000
+#define	AR5K_PHY_PAPD_PROBE_TYPE	0x01800000	/* [5112+] */
+#define	AR5K_PHY_PAPD_PROBE_TYPE_S	23
+#define	AR5K_PHY_PAPD_PROBE_TYPE_OFDM	0
+#define	AR5K_PHY_PAPD_PROBE_TYPE_XR	1
+#define	AR5K_PHY_PAPD_PROBE_TYPE_CCK	2
+#define	AR5K_PHY_PAPD_PROBE_GAINF	0xfe000000
+#define	AR5K_PHY_PAPD_PROBE_GAINF_S	25
+#define	AR5K_PHY_PAPD_PROBE_INI_5111	0x00004883	/* [5212+] */
+#define	AR5K_PHY_PAPD_PROBE_INI_5112	0x00004882	/* [5212+] */
+
+
+/*
+ * PHY TX rate power registers [5112+]
+ */
+#define	AR5K_PHY_TXPOWER_RATE1			0x9934
+#define	AR5K_PHY_TXPOWER_RATE2			0x9938
+#define	AR5K_PHY_TXPOWER_RATE_MAX		0x993c
+#define	AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE	0x00000040
+#define	AR5K_PHY_TXPOWER_RATE3			0xa234
+#define	AR5K_PHY_TXPOWER_RATE4			0xa238
+
+/*
+ * PHY frame control register [5111+]
+ */
+#define	AR5K_PHY_FRAME_CTL_5210		0x9804
+#define	AR5K_PHY_FRAME_CTL_5211		0x9944
+#define	AR5K_PHY_FRAME_CTL		(hal->ah_version == AR5K_AR5210 ? \
+					AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211)
+/*---[5111+]---*/
+#define	AR5K_PHY_FRAME_CTL_TX_CLIP	0x00000038
+#define	AR5K_PHY_FRAME_CTL_TX_CLIP_S	3
+/*---[5110/5111]---*/
+#define	AR5K_PHY_FRAME_CTL_TIMING_ERR	0x01000000
+#define	AR5K_PHY_FRAME_CTL_PARITY_ERR	0x02000000
+#define	AR5K_PHY_FRAME_CTL_ILLRATE_ERR	0x04000000	/* illegal rate */
+#define	AR5K_PHY_FRAME_CTL_ILLLEN_ERR	0x08000000	/* illegal length */
+#define	AR5K_PHY_FRAME_CTL_SERVICE_ERR	0x20000000
+#define	AR5K_PHY_FRAME_CTL_TXURN_ERR	0x40000000	/* tx underrun */
+#define AR5K_PHY_FRAME_CTL_INI		AR5K_PHY_FRAME_CTL_SERVICE_ERR | \
+			AR5K_PHY_FRAME_CTL_TXURN_ERR | \
+			AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \
+			AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \
+			AR5K_PHY_FRAME_CTL_PARITY_ERR | \
+			AR5K_PHY_FRAME_CTL_TIMING_ERR
+
+/*
+ * PHY radar detection register [5111+]
+ */
+#define	AR5K_PHY_RADAR			0x9954
+
+/* Radar enable 			........ ........ ........ .......1 */
+#define	AR5K_PHY_RADAR_ENABLE		0x00000001
+#define	AR5K_PHY_RADAR_DISABLE          0x00000000
+#define	AR5K_PHY_RADAR_ENABLE_S		0
+
+/* This is the value found on the card  .1.111.1 .1.1.... 111....1 1...1...
+at power on. */
+#define	AR5K_PHY_RADAR_PWONDEF_AR5213	0x5d50e188
+
+/* This is the value found on the card 	.1.1.111 ..11...1 .1...1.1 1...11.1
+after DFS is enabled */
+#define	AR5K_PHY_RADAR_ENABLED_AR5213	0x5731458d
+
+/* Finite Impulse Response (FIR) filter .1111111 ........ ........ ........
+ * power out threshold.
+ * 7-bits, standard power range {0..127} in 1/2 dBm units. */
+#define AR5K_PHY_RADAR_FIRPWROUTTHR    	0x7f000000
+#define AR5K_PHY_RADAR_FIRPWROUTTHR_S	24
+
+/* Radar RSSI/SNR threshold.		........ 111111.. ........ ........
+ * 6-bits, dBm range {0..63} in dBm units. */
+#define AR5K_PHY_RADAR_RADARRSSITHR    	0x00fc0000
+#define AR5K_PHY_RADAR_RADARRSSITHR_S	18
+
+/* Pulse height threshold 		........ ......11 1111.... ........
+ * 6-bits, dBm range {0..63} in dBm units. */
+#define AR5K_PHY_RADAR_PULSEHEIGHTTHR   0x0003f000
+#define AR5K_PHY_RADAR_PULSEHEIGHTTHR_S	12
+
+/* Pulse RSSI/SNR threshold		........ ........ ....1111 11......
+ * 6-bits, dBm range {0..63} in dBm units. */
+#define AR5K_PHY_RADAR_PULSERSSITHR    	0x00000fc0
+#define AR5K_PHY_RADAR_PULSERSSITHR_S	6
+
+/* Inband threshold  			........ ........ ........ ..11111.
+ * 5-bits, units unknown {0..31} (? MHz ?) */
+#define AR5K_PHY_RADAR_INBANDTHR    	0x0000003e
+#define AR5K_PHY_RADAR_INBANDTHR_S	1
+
+/*
+ * PHY antenna switch table registers [5110]
+ */
+#define AR5K_PHY_ANT_SWITCH_TABLE_0	0x9960
+#define AR5K_PHY_ANT_SWITCH_TABLE_1	0x9964
+
+/*
+ * PHY clock sleep registers [5112+]
+ */
+#define AR5K_PHY_SCLOCK			0x99f0
+#define AR5K_PHY_SCLOCK_32MHZ		0x0000000c
+#define AR5K_PHY_SDELAY			0x99f4
+#define AR5K_PHY_SDELAY_32MHZ		0x000000ff
+#define AR5K_PHY_SPENDING		0x99f8
+#define AR5K_PHY_SPENDING_RF5111	0x00000018
+#define AR5K_PHY_SPENDING_RF5112	0x00000014
+
+/*
+ * Misc PHY/radio registers [5110 - 5111]
+ */
+#define	AR5K_BB_GAIN_BASE		0x9b00 /* BaseBand Amplifier Gain table base address */
+#define AR5K_BB_GAIN(_n)		(AR5K_BB_GAIN_BASE + ((_n) << 2))
+#define	AR5K_RF_GAIN_BASE		0x9a00 /* RF Amplrifier Gain table base address */
+#define AR5K_RF_GAIN(_n)		(AR5K_RF_GAIN_BASE + ((_n) << 2))
+
+/*
+ * PHY timing IQ calibration result register [5111+]
+ */
+#define	AR5K_PHY_IQRES_CAL_PWR_I	0x9c10 /* I (Inphase) power value */
+#define	AR5K_PHY_IQRES_CAL_PWR_Q	0x9c14 /* Q (Quadrature) power value */
+#define	AR5K_PHY_IQRES_CAL_CORR		0x9c18	/* I/Q Correlation */
+
+/*
+ * PHY current RSSI register [5111+]
+ */
+#define	AR5K_PHY_CURRENT_RSSI		0x9c1c
+
+/*
+ * PHY PCDAC TX power table
+ */
+#define	AR5K_PHY_PCDAC_TXPOWER_BASE	0xa180
+#define	AR5K_PHY_PCDAC_TXPOWER(_n)	(AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2))
+
+/*
+ * PHY mode register [5111+]
+ */
+#define	AR5K_PHY_MODE			0x0a200		/* Register address */
+#define	AR5K_PHY_MODE_MOD		0x00000001	/* PHY Modulation mask*/
+#define AR5K_PHY_MODE_MOD_OFDM		0
+#define AR5K_PHY_MODE_MOD_CCK		1
+#define AR5K_PHY_MODE_FREQ		0x00000002	/* Freq mode mask */
+#define	AR5K_PHY_MODE_FREQ_5GHZ		0
+#define	AR5K_PHY_MODE_FREQ_2GHZ		2
+#define AR5K_PHY_MODE_MOD_DYN		0x00000004	/* Dynamic OFDM/CCK mode mask [5112+] */
+#define AR5K_PHY_MODE_RAD		0x00000008	/* [5212+] */
+#define AR5K_PHY_MODE_RAD_RF5111	0
+#define AR5K_PHY_MODE_RAD_RF5112	8
+#define AR5K_PHY_MODE_XR		0x00000010	/* [5112+] */
+
+/*
+ * PHY CCK transmit control register [5111+ (?)]
+ */
+#define AR5K_PHY_CCKTXCTL		0xa204
+#define AR5K_PHY_CCKTXCTL_WORLD		0x00000000
+#define AR5K_PHY_CCKTXCTL_JAPAN		0x00000010
+
+/*
+ * PHY 2GHz gain register [5111+]
+ */
+#define	AR5K_PHY_GAIN_2GHZ		0xa20c
+#define	AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX	0x00fc0000
+#define	AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX_S	18
+#define	AR5K_PHY_GAIN_2GHZ_INI_5111	0x6480416c
diff -puN /dev/null drivers/net/wireless/ath5k/regdom.c
--- /dev/null
+++ a/drivers/net/wireless/ath5k/regdom.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Basic regulation domain extensions for the IEEE 802.11 stack
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "regdom.h"
+
+static const struct ath5k_regdommap {
+	enum ath5k_regdom dmn;
+	enum ath5k_regdom dmn5;
+	enum ath5k_regdom dmn2;
+} r_map[] = {
+	{ DMN_DEFAULT,		DMN_DEBUG,	DMN_DEBUG },
+	{ DMN_NULL_WORLD,	DMN_NULL,	DMN_WORLD },
+	{ DMN_NULL_ETSIB,	DMN_NULL,	DMN_ETSIB },
+	{ DMN_NULL_ETSIC,	DMN_NULL,	DMN_ETSIC },
+	{ DMN_FCC1_FCCA,	DMN_FCC1,	DMN_FCCA },
+	{ DMN_FCC1_WORLD,	DMN_FCC1,	DMN_WORLD },
+	{ DMN_FCC2_FCCA,	DMN_FCC2,	DMN_FCCA },
+	{ DMN_FCC2_WORLD,	DMN_FCC2,	DMN_WORLD },
+	{ DMN_FCC2_ETSIC,	DMN_FCC2,	DMN_ETSIC },
+	{ DMN_FRANCE_NULL,	DMN_ETSI3,	DMN_ETSI3 },
+	{ DMN_FCC3_FCCA,	DMN_FCC3,	DMN_WORLD },
+	{ DMN_ETSI1_WORLD,	DMN_ETSI1,	DMN_WORLD },
+	{ DMN_ETSI3_ETSIA,	DMN_ETSI3,	DMN_WORLD },
+	{ DMN_ETSI2_WORLD,	DMN_ETSI2,	DMN_WORLD },
+	{ DMN_ETSI3_WORLD,	DMN_ETSI3,	DMN_WORLD },
+	{ DMN_ETSI4_WORLD,	DMN_ETSI4,	DMN_WORLD },
+	{ DMN_ETSI4_ETSIC,	DMN_ETSI4,	DMN_ETSIC },
+	{ DMN_ETSI5_WORLD,	DMN_ETSI5,	DMN_WORLD },
+	{ DMN_ETSI6_WORLD,	DMN_ETSI6,	DMN_WORLD },
+	{ DMN_ETSI_NULL,	DMN_ETSI1,	DMN_ETSI1 },
+	{ DMN_MKK1_MKKA,	DMN_MKK1,	DMN_MKKA },
+	{ DMN_MKK1_MKKB,	DMN_MKK1,	DMN_MKKA },
+	{ DMN_APL4_WORLD,	DMN_APL4,	DMN_WORLD },
+	{ DMN_MKK2_MKKA,	DMN_MKK2,	DMN_MKKA },
+	{ DMN_APL_NULL,		DMN_APL1,	DMN_NULL },
+	{ DMN_APL2_WORLD,	DMN_APL2,	DMN_WORLD },
+	{ DMN_APL2_APLC,	DMN_APL2,	DMN_WORLD },
+	{ DMN_APL3_WORLD,	DMN_APL3,	DMN_WORLD },
+	{ DMN_MKK1_FCCA,	DMN_MKK1,	DMN_FCCA },
+	{ DMN_APL2_APLD,	DMN_APL2,	DMN_APLD },
+	{ DMN_MKK1_MKKA1,	DMN_MKK1,	DMN_MKKA },
+	{ DMN_MKK1_MKKA2,	DMN_MKK1,	DMN_MKKA },
+	{ DMN_APL1_WORLD,	DMN_APL1,	DMN_WORLD },
+	{ DMN_APL1_FCCA,	DMN_APL1,	DMN_FCCA },
+	{ DMN_APL1_APLA,	DMN_APL1,	DMN_WORLD },
+	{ DMN_APL1_ETSIC,	DMN_APL1,	DMN_ETSIC },
+	{ DMN_APL2_ETSIC,	DMN_APL2,	DMN_ETSIC },
+	{ DMN_APL5_WORLD,	DMN_APL5,	DMN_WORLD },
+	{ DMN_WOR0_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WOR1_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WOR2_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WOR3_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WOR4_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WOR5_ETSIC,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WOR01_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WOR02_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_EU1_WORLD,	DMN_ETSI1,	DMN_WORLD },
+	{ DMN_WOR9_WORLD,	DMN_WORLD,	DMN_WORLD },
+	{ DMN_WORA_WORLD,	DMN_WORLD,	DMN_WORLD },
+};
+
+enum ath5k_regdom ath5k_regdom2flag(enum ath5k_regdom dmn, u16 mhz)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(r_map); i++) {
+		if (r_map[i].dmn == dmn) {
+			if (mhz >= 2000 && mhz <= 3000)
+				return r_map[i].dmn2;
+			if (mhz >= IEEE80211_CHANNELS_5GHZ_MIN &&
+					mhz <= IEEE80211_CHANNELS_5GHZ_MAX)
+				return r_map[i].dmn5;
+		}
+	}
+
+	return DMN_DEBUG;
+}
+
+u16 ath5k_regdom_from_ieee(enum ath5k_regdom ieee)
+{
+	u32 regdomain = (u32)ieee;
+
+	/*
+	 * Use the default regulation domain if the value is empty
+	 * or not supported by the net80211 regulation code.
+	 */
+	if (ath5k_regdom2flag(regdomain, IEEE80211_CHANNELS_5GHZ_MIN) ==
+			DMN_DEBUG)
+		return (u16)AR5K_TUNE_REGDOMAIN;
+
+	/* It is supported, just return the value */
+	return regdomain;
+}
+
+enum ath5k_regdom ath5k_regdom_to_ieee(u16 regdomain)
+{
+	enum ath5k_regdom ieee = (enum ath5k_regdom)regdomain;
+
+	return ieee;
+}
+
diff -puN /dev/null drivers/net/wireless/ath5k/regdom.h
--- /dev/null
+++ a/drivers/net/wireless/ath5k/regdom.h
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IEEE80211_REGDOMAIN_H_
+#define _IEEE80211_REGDOMAIN_H_
+
+#include <linux/types.h>
+
+/* Default regulation domain if stored value EEPROM value is invalid */
+#define AR5K_TUNE_REGDOMAIN	DMN_FCC2_FCCA	/* Canada */
+#define AR5K_TUNE_CTRY		CTRY_DEFAULT
+
+
+enum ath5k_regdom {
+	DMN_DEFAULT		= 0x00,
+	DMN_NULL_WORLD		= 0x03,
+	DMN_NULL_ETSIB		= 0x07,
+	DMN_NULL_ETSIC		= 0x08,
+	DMN_FCC1_FCCA		= 0x10,
+	DMN_FCC1_WORLD		= 0x11,
+	DMN_FCC2_FCCA		= 0x20,
+	DMN_FCC2_WORLD		= 0x21,
+	DMN_FCC2_ETSIC		= 0x22,
+	DMN_FRANCE_NULL		= 0x31,
+	DMN_FCC3_FCCA		= 0x3A,
+	DMN_ETSI1_WORLD		= 0x37,
+	DMN_ETSI3_ETSIA		= 0x32,
+	DMN_ETSI2_WORLD		= 0x35,
+	DMN_ETSI3_WORLD		= 0x36,
+	DMN_ETSI4_WORLD		= 0x30,
+	DMN_ETSI4_ETSIC		= 0x38,
+	DMN_ETSI5_WORLD		= 0x39,
+	DMN_ETSI6_WORLD		= 0x34,
+	DMN_ETSI_NULL		= 0x33,
+	DMN_MKK1_MKKA		= 0x40,
+	DMN_MKK1_MKKB		= 0x41,
+	DMN_APL4_WORLD		= 0x42,
+	DMN_MKK2_MKKA		= 0x43,
+	DMN_APL_NULL		= 0x44,
+	DMN_APL2_WORLD		= 0x45,
+	DMN_APL2_APLC		= 0x46,
+	DMN_APL3_WORLD		= 0x47,
+	DMN_MKK1_FCCA		= 0x48,
+	DMN_APL2_APLD		= 0x49,
+	DMN_MKK1_MKKA1		= 0x4A,
+	DMN_MKK1_MKKA2		= 0x4B,
+	DMN_APL1_WORLD		= 0x52,
+	DMN_APL1_FCCA		= 0x53,
+	DMN_APL1_APLA		= 0x54,
+	DMN_APL1_ETSIC		= 0x55,
+	DMN_APL2_ETSIC		= 0x56,
+	DMN_APL5_WORLD		= 0x58,
+	DMN_WOR0_WORLD		= 0x60,
+	DMN_WOR1_WORLD		= 0x61,
+	DMN_WOR2_WORLD		= 0x62,
+	DMN_WOR3_WORLD		= 0x63,
+	DMN_WOR4_WORLD		= 0x64,
+	DMN_WOR5_ETSIC		= 0x65,
+	DMN_WOR01_WORLD		= 0x66,
+	DMN_WOR02_WORLD		= 0x67,
+	DMN_EU1_WORLD		= 0x68,
+	DMN_WOR9_WORLD		= 0x69,
+	DMN_WORA_WORLD		= 0x6A,
+
+	DMN_APL1		= 0xf0000001,
+	DMN_APL2		= 0xf0000002,
+	DMN_APL3		= 0xf0000004,
+	DMN_APL4		= 0xf0000008,
+	DMN_APL5		= 0xf0000010,
+	DMN_ETSI1		= 0xf0000020,
+	DMN_ETSI2		= 0xf0000040,
+	DMN_ETSI3		= 0xf0000080,
+	DMN_ETSI4		= 0xf0000100,
+	DMN_ETSI5		= 0xf0000200,
+	DMN_ETSI6		= 0xf0000400,
+	DMN_ETSIA		= 0xf0000800,
+	DMN_ETSIB		= 0xf0001000,
+	DMN_ETSIC		= 0xf0002000,
+	DMN_FCC1		= 0xf0004000,
+	DMN_FCC2		= 0xf0008000,
+	DMN_FCC3		= 0xf0010000,
+	DMN_FCCA		= 0xf0020000,
+	DMN_APLD		= 0xf0040000,
+	DMN_MKK1		= 0xf0080000,
+	DMN_MKK2		= 0xf0100000,
+	DMN_MKKA		= 0xf0200000,
+	DMN_NULL		= 0xf0400000,
+	DMN_WORLD		= 0xf0800000,
+	DMN_DEBUG               = 0xf1000000	/* used for debugging */
+};
+
+#define IEEE80211_DMN(_d)	((_d) & ~0xf0000000)
+
+enum ath5k_countrycode {
+	CTRY_DEFAULT            = 0,   /* Default domain (NA) */
+	CTRY_ALBANIA            = 8,   /* Albania */
+	CTRY_ALGERIA            = 12,  /* Algeria */
+	CTRY_ARGENTINA          = 32,  /* Argentina */
+	CTRY_ARMENIA            = 51,  /* Armenia */
+	CTRY_AUSTRALIA          = 36,  /* Australia */
+	CTRY_AUSTRIA            = 40,  /* Austria */
+	CTRY_AZERBAIJAN         = 31,  /* Azerbaijan */
+	CTRY_BAHRAIN            = 48,  /* Bahrain */
+	CTRY_BELARUS            = 112, /* Belarus */
+	CTRY_BELGIUM            = 56,  /* Belgium */
+	CTRY_BELIZE             = 84,  /* Belize */
+	CTRY_BOLIVIA            = 68,  /* Bolivia */
+	CTRY_BRAZIL             = 76,  /* Brazil */
+	CTRY_BRUNEI_DARUSSALAM  = 96,  /* Brunei Darussalam */
+	CTRY_BULGARIA           = 100, /* Bulgaria */
+	CTRY_CANADA             = 124, /* Canada */
+	CTRY_CHILE              = 152, /* Chile */
+	CTRY_CHINA              = 156, /* People's Republic of China */
+	CTRY_COLOMBIA           = 170, /* Colombia */
+	CTRY_COSTA_RICA         = 188, /* Costa Rica */
+	CTRY_CROATIA            = 191, /* Croatia */
+	CTRY_CYPRUS             = 196, /* Cyprus */
+	CTRY_CZECH              = 203, /* Czech Republic */
+	CTRY_DENMARK            = 208, /* Denmark */
+	CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */
+	CTRY_ECUADOR            = 218, /* Ecuador */
+	CTRY_EGYPT              = 818, /* Egypt */
+	CTRY_EL_SALVADOR        = 222, /* El Salvador */
+	CTRY_ESTONIA            = 233, /* Estonia */
+	CTRY_FAEROE_ISLANDS     = 234, /* Faeroe Islands */
+	CTRY_FINLAND            = 246, /* Finland */
+	CTRY_FRANCE             = 250, /* France */
+	CTRY_FRANCE2            = 255, /* France2 */
+	CTRY_GEORGIA            = 268, /* Georgia */
+	CTRY_GERMANY            = 276, /* Germany */
+	CTRY_GREECE             = 300, /* Greece */
+	CTRY_GUATEMALA          = 320, /* Guatemala */
+	CTRY_HONDURAS           = 340, /* Honduras */
+	CTRY_HONG_KONG          = 344, /* Hong Kong S.A.R., P.R.C. */
+	CTRY_HUNGARY            = 348, /* Hungary */
+	CTRY_ICELAND            = 352, /* Iceland */
+	CTRY_INDIA              = 356, /* India */
+	CTRY_INDONESIA          = 360, /* Indonesia */
+	CTRY_IRAN               = 364, /* Iran */
+	CTRY_IRAQ               = 368, /* Iraq */
+	CTRY_IRELAND            = 372, /* Ireland */
+	CTRY_ISRAEL             = 376, /* Israel */
+	CTRY_ITALY              = 380, /* Italy */
+	CTRY_JAMAICA            = 388, /* Jamaica */
+	CTRY_JAPAN              = 392, /* Japan */
+	CTRY_JAPAN1             = 393, /* Japan (JP1) */
+	CTRY_JAPAN2             = 394, /* Japan (JP0) */
+	CTRY_JAPAN3             = 395, /* Japan (JP1-1) */
+	CTRY_JAPAN4             = 396, /* Japan (JE1) */
+	CTRY_JAPAN5             = 397, /* Japan (JE2) */
+	CTRY_JORDAN             = 400, /* Jordan */
+	CTRY_KAZAKHSTAN         = 398, /* Kazakhstan */
+	CTRY_KENYA              = 404, /* Kenya */
+	CTRY_KOREA_NORTH        = 408, /* North Korea */
+	CTRY_KOREA_ROC          = 410, /* South Korea */
+	CTRY_KOREA_ROC2         = 411, /* South Korea */
+	CTRY_KUWAIT             = 414, /* Kuwait */
+	CTRY_LATVIA             = 428, /* Latvia */
+	CTRY_LEBANON            = 422, /* Lebanon */
+	CTRY_LIBYA              = 434, /* Libya */
+	CTRY_LIECHTENSTEIN      = 438, /* Liechtenstein */
+	CTRY_LITHUANIA          = 440, /* Lithuania */
+	CTRY_LUXEMBOURG         = 442, /* Luxembourg */
+	CTRY_MACAU              = 446, /* Macau */
+	CTRY_MACEDONIA          = 807, /* Republic of Macedonia */
+	CTRY_MALAYSIA           = 458, /* Malaysia */
+	CTRY_MEXICO             = 484, /* Mexico */
+	CTRY_MONACO             = 492, /* Principality of Monaco */
+	CTRY_MOROCCO            = 504, /* Morocco */
+	CTRY_NETHERLANDS        = 528, /* Netherlands */
+	CTRY_NEW_ZEALAND        = 554, /* New Zealand */
+	CTRY_NICARAGUA          = 558, /* Nicaragua */
+	CTRY_NORWAY             = 578, /* Norway */
+	CTRY_OMAN               = 512, /* Oman */
+	CTRY_PAKISTAN           = 586, /* Islamic Republic of Pakistan */
+	CTRY_PANAMA             = 591, /* Panama */
+	CTRY_PARAGUAY           = 600, /* Paraguay */
+	CTRY_PERU               = 604, /* Peru */
+	CTRY_PHILIPPINES        = 608, /* Republic of the Philippines */
+	CTRY_POLAND             = 616, /* Poland */
+	CTRY_PORTUGAL           = 620, /* Portugal */
+	CTRY_PUERTO_RICO        = 630, /* Puerto Rico */
+	CTRY_QATAR              = 634, /* Qatar */
+	CTRY_ROMANIA            = 642, /* Romania */
+	CTRY_RUSSIA             = 643, /* Russia */
+	CTRY_SAUDI_ARABIA       = 682, /* Saudi Arabia */
+	CTRY_SINGAPORE          = 702, /* Singapore */
+	CTRY_SLOVAKIA           = 703, /* Slovak Republic */
+	CTRY_SLOVENIA           = 705, /* Slovenia */
+	CTRY_SOUTH_AFRICA       = 710, /* South Africa */
+	CTRY_SPAIN              = 724, /* Spain */
+	CTRY_SRI_LANKA          = 728, /* Sri Lanka */
+	CTRY_SWEDEN             = 752, /* Sweden */
+	CTRY_SWITZERLAND        = 756, /* Switzerland */
+	CTRY_SYRIA              = 760, /* Syria */
+	CTRY_TAIWAN             = 158, /* Taiwan */
+	CTRY_THAILAND           = 764, /* Thailand */
+	CTRY_TRINIDAD_Y_TOBAGO  = 780, /* Trinidad y Tobago */
+	CTRY_TUNISIA            = 788, /* Tunisia */
+	CTRY_TURKEY             = 792, /* Turkey */
+	CTRY_UAE                = 784, /* U.A.E. */
+	CTRY_UKRAINE            = 804, /* Ukraine */
+	CTRY_UNITED_KINGDOM     = 826, /* United Kingdom */
+	CTRY_UNITED_STATES      = 840, /* United States */
+	CTRY_URUGUAY            = 858, /* Uruguay */
+	CTRY_UZBEKISTAN         = 860, /* Uzbekistan */
+	CTRY_VENEZUELA          = 862, /* Venezuela */
+	CTRY_VIET_NAM           = 704, /* Viet Nam */
+	CTRY_YEMEN              = 887, /* Yemen */
+	CTRY_ZIMBABWE           = 716, /* Zimbabwe */
+};
+
+#define IEEE80211_CHANNELS_2GHZ_MIN	2412	/* 2GHz channel 1 */
+#define IEEE80211_CHANNELS_2GHZ_MAX	2732	/* 2GHz channel 26 */
+#define IEEE80211_CHANNELS_5GHZ_MIN	5005	/* 5GHz channel 1 */
+#define IEEE80211_CHANNELS_5GHZ_MAX	6100	/* 5GHz channel 220 */
+
+struct ath5k_regchannel {
+	u16 chan;
+	enum ath5k_regdom domain;
+	u32 mode;
+};
+
+#define IEEE80211_CHANNELS_2GHZ {					\
+/*2412*/ {   1, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2417*/ {   2, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2422*/ {   3, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2427*/ {   4, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2432*/ {   5, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2437*/ {   6, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2442*/ {   7, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2447*/ {   8, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2452*/ {   9, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2457*/ {  10, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2462*/ {  11, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2467*/ {  12, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2472*/ {  13, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+									\
+/*2432*/ {   5, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2437*/ {   6, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*2442*/ {   7, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM },			\
+									\
+/*2412*/ {   1, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2417*/ {   2, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2422*/ {   3, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2427*/ {   4, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2432*/ {   5, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2437*/ {   6, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*2442*/ {   7, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2447*/ {   8, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2452*/ {   9, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2457*/ {  10, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2462*/ {  11, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2467*/ {  12, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2472*/ {  13, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\
+									\
+/*2412*/ {   1, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2417*/ {   2, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2422*/ {   3, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2427*/ {   4, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2432*/ {   5, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2437*/ {   6, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*2442*/ {   7, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2447*/ {   8, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2452*/ {   9, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2457*/ {  10, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2462*/ {  11, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\
+									\
+/*2412*/ {   1, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2417*/ {   2, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2422*/ {   3, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2427*/ {   4, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2432*/ {   5, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2437*/ {   6, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2442*/ {   7, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2447*/ {   8, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2452*/ {   9, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2457*/ {  10, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2462*/ {  11, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2467*/ {  12, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2472*/ {  13, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2484*/ {  14, DMN_MKKA, CHANNEL_CCK },				\
+									\
+/*2412*/ {   1, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2417*/ {   2, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2422*/ {   3, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2427*/ {   4, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2432*/ {   5, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2437*/ {   6, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*2442*/ {   7, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2447*/ {   8, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2452*/ {   9, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2457*/ {  10, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2462*/ {  11, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2467*/ {  12, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+/*2472*/ {  13, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\
+}
+
+#define IEEE80211_CHANNELS_5GHZ {			\
+/*5745*/ { 149, DMN_APL1, CHANNEL_OFDM },		\
+/*5765*/ { 153, DMN_APL1, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_APL1, CHANNEL_OFDM },		\
+/*5805*/ { 161, DMN_APL1, CHANNEL_OFDM },		\
+/*5825*/ { 165, DMN_APL1, CHANNEL_OFDM },		\
+							\
+/*5745*/ { 149, DMN_APL2, CHANNEL_OFDM },		\
+/*5765*/ { 153, DMN_APL2, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_APL2, CHANNEL_OFDM },		\
+/*5805*/ { 161, DMN_APL2, CHANNEL_OFDM },		\
+							\
+/*5280*/ {  56, DMN_APL3, CHANNEL_OFDM },		\
+/*5300*/ {  60, DMN_APL3, CHANNEL_OFDM },		\
+/*5320*/ {  64, DMN_APL3, CHANNEL_OFDM },		\
+/*5745*/ { 149, DMN_APL3, CHANNEL_OFDM },		\
+/*5765*/ { 153, DMN_APL3, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_APL3, CHANNEL_OFDM },		\
+/*5805*/ { 161, DMN_APL3, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_APL4, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_APL4, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_APL4, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_APL4, CHANNEL_OFDM },		\
+/*5745*/ { 149, DMN_APL4, CHANNEL_OFDM },		\
+/*5765*/ { 153, DMN_APL4, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_APL4, CHANNEL_OFDM },		\
+/*5805*/ { 161, DMN_APL4, CHANNEL_OFDM },		\
+/*5825*/ { 165, DMN_APL4, CHANNEL_OFDM },		\
+							\
+/*5745*/ { 149, DMN_APL5, CHANNEL_OFDM },		\
+/*5765*/ { 153, DMN_APL5, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_APL5, CHANNEL_OFDM },		\
+/*5805*/ { 161, DMN_APL5, CHANNEL_OFDM },		\
+/*5825*/ { 165, DMN_APL5, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5260*/ {  52, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5280*/ {  56, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5300*/ {  60, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5320*/ {  64, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5500*/ { 100, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5520*/ { 104, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5540*/ { 108, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5560*/ { 112, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5580*/ { 116, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5600*/ { 120, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5620*/ { 124, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5640*/ { 128, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5660*/ { 132, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5680*/ { 136, DMN_ETSI1, CHANNEL_OFDM },		\
+/*5700*/ { 140, DMN_ETSI1, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_ETSI2, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_ETSI2, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_ETSI2, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_ETSI2, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_ETSI3, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_ETSI3, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_ETSI3, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_ETSI3, CHANNEL_OFDM },		\
+/*5260*/ {  52, DMN_ETSI3, CHANNEL_OFDM },		\
+/*5280*/ {  56, DMN_ETSI3, CHANNEL_OFDM },		\
+/*5300*/ {  60, DMN_ETSI3, CHANNEL_OFDM },		\
+/*5320*/ {  64, DMN_ETSI3, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_ETSI4, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_ETSI4, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_ETSI4, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_ETSI4, CHANNEL_OFDM },		\
+/*5260*/ {  52, DMN_ETSI4, CHANNEL_OFDM },		\
+/*5280*/ {  56, DMN_ETSI4, CHANNEL_OFDM },		\
+/*5300*/ {  60, DMN_ETSI4, CHANNEL_OFDM },		\
+/*5320*/ {  64, DMN_ETSI4, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_ETSI5, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_ETSI5, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_ETSI5, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_ETSI5, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5260*/ {  52, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5280*/ {  56, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5500*/ { 100, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5520*/ { 104, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5540*/ { 108, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5560*/ { 112, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5580*/ { 116, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5600*/ { 120, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5620*/ { 124, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5640*/ { 128, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5660*/ { 132, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5680*/ { 136, DMN_ETSI6, CHANNEL_OFDM },		\
+/*5700*/ { 140, DMN_ETSI6, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_FCC1, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_FCC1, CHANNEL_OFDM },		\
+/*5210*/ {  42, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5220*/ {  44, DMN_FCC1, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_FCC1, CHANNEL_OFDM },		\
+/*5250*/ {  50, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5260*/ {  52, DMN_FCC1, CHANNEL_OFDM },		\
+/*5280*/ {  56, DMN_FCC1, CHANNEL_OFDM },		\
+/*5290*/ {  58, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5300*/ {  60, DMN_FCC1, CHANNEL_OFDM },		\
+/*5320*/ {  64, DMN_FCC1, CHANNEL_OFDM },		\
+/*5745*/ { 149, DMN_FCC1, CHANNEL_OFDM },		\
+/*5760*/ { 152, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5765*/ { 153, DMN_FCC1, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_FCC1, CHANNEL_OFDM },		\
+/*5800*/ { 160, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5805*/ { 161, DMN_FCC1, CHANNEL_OFDM },		\
+/*5825*/ { 165, DMN_FCC1, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_FCC2, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_FCC2, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_FCC2, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_FCC2, CHANNEL_OFDM },		\
+/*5260*/ {  52, DMN_FCC2, CHANNEL_OFDM },		\
+/*5280*/ {  56, DMN_FCC2, CHANNEL_OFDM },		\
+/*5300*/ {  60, DMN_FCC2, CHANNEL_OFDM },		\
+/*5320*/ {  64, DMN_FCC2, CHANNEL_OFDM },		\
+/*5745*/ { 149, DMN_FCC2, CHANNEL_OFDM },		\
+/*5765*/ { 153, DMN_FCC2, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_FCC2, CHANNEL_OFDM },		\
+/*5805*/ { 161, DMN_FCC2, CHANNEL_OFDM },		\
+/*5825*/ { 165, DMN_FCC2, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_FCC3, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_FCC3, CHANNEL_OFDM },		\
+/*5210*/ {  42, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5220*/ {  44, DMN_FCC3, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_FCC3, CHANNEL_OFDM },		\
+/*5250*/ {  50, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5260*/ {  52, DMN_FCC3, CHANNEL_OFDM },		\
+/*5280*/ {  56, DMN_FCC3, CHANNEL_OFDM },		\
+/*5290*/ {  58, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5300*/ {  60, DMN_FCC3, CHANNEL_OFDM },		\
+/*5320*/ {  64, DMN_FCC3, CHANNEL_OFDM },		\
+/*5500*/ { 100, DMN_FCC3, CHANNEL_OFDM },		\
+/*5520*/ { 104, DMN_FCC3, CHANNEL_OFDM },		\
+/*5540*/ { 108, DMN_FCC3, CHANNEL_OFDM },		\
+/*5560*/ { 112, DMN_FCC3, CHANNEL_OFDM },		\
+/*5580*/ { 116, DMN_FCC3, CHANNEL_OFDM },		\
+/*5600*/ { 120, DMN_FCC3, CHANNEL_OFDM },		\
+/*5620*/ { 124, DMN_FCC3, CHANNEL_OFDM },		\
+/*5640*/ { 128, DMN_FCC3, CHANNEL_OFDM },		\
+/*5660*/ { 132, DMN_FCC3, CHANNEL_OFDM },		\
+/*5680*/ { 136, DMN_FCC3, CHANNEL_OFDM },		\
+/*5700*/ { 140, DMN_FCC3, CHANNEL_OFDM },		\
+/*5745*/ { 149, DMN_FCC3, CHANNEL_OFDM },		\
+/*5760*/ { 152, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5765*/ { 153, DMN_FCC3, CHANNEL_OFDM },		\
+/*5785*/ { 157, DMN_FCC3, CHANNEL_OFDM },		\
+/*5800*/ { 160, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\
+/*5805*/ { 161, DMN_FCC3, CHANNEL_OFDM },		\
+/*5825*/ { 165, DMN_FCC3, CHANNEL_OFDM },		\
+							\
+/*5170*/ {  34, DMN_MKK1, CHANNEL_OFDM },		\
+/*5190*/ {  38, DMN_MKK1, CHANNEL_OFDM },		\
+/*5210*/ {  42, DMN_MKK1, CHANNEL_OFDM },		\
+/*5230*/ {  46, DMN_MKK1, CHANNEL_OFDM },		\
+							\
+/*5040*/ {   8, DMN_MKK2, CHANNEL_OFDM },		\
+/*5060*/ {  12, DMN_MKK2, CHANNEL_OFDM },		\
+/*5080*/ {  16, DMN_MKK2, CHANNEL_OFDM },		\
+/*5170*/ {  34, DMN_MKK2, CHANNEL_OFDM },		\
+/*5190*/ {  38, DMN_MKK2, CHANNEL_OFDM },		\
+/*5210*/ {  42, DMN_MKK2, CHANNEL_OFDM },		\
+/*5230*/ {  46, DMN_MKK2, CHANNEL_OFDM },		\
+							\
+/*5180*/ {  36, DMN_WORLD, CHANNEL_OFDM },		\
+/*5200*/ {  40, DMN_WORLD, CHANNEL_OFDM },		\
+/*5220*/ {  44, DMN_WORLD, CHANNEL_OFDM },		\
+/*5240*/ {  48, DMN_WORLD, CHANNEL_OFDM },		\
+}
+
+enum ath5k_regdom ath5k_regdom2flag(enum ath5k_regdom, u16);
+u16 ath5k_regdom_from_ieee(enum ath5k_regdom ieee);
+enum ath5k_regdom ath5k_regdom_to_ieee(u16 regdomain);
+
+#endif
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/Kconfig
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/Kconfig
@@ -0,0 +1,18 @@
+config ZD1211RW_MAC80211
+	tristate "ZyDAS ZD1211/ZD1211B USB support (mac80211 stack)"
+	depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL
+	depends on m || !ZD1211RW
+	select FW_LOADER
+	---help---
+	  This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
+	  chip, present in many USB-wireless adapters.
+
+	  Device firmware is required alongside this driver. You can download
+	  the firmware distribution from http://zd1211.ath.cx/get-firmware
+
+config ZD1211RW_MAC80211_DEBUG
+	bool "ZyDAS ZD1211 debugging"
+	depends on ZD1211RW_MAC80211
+	---help---
+	  ZD1211 debugging messages. Choosing Y will result in additional debug
+	  messages being saved to your kernel logs, which may help debug any
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/Makefile
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_ZD1211RW_MAC80211) += zd1211rw-mac80211.o
+
+zd1211rw-mac80211-objs := zd_chip.o zd_mac.o \
+		zd_rf_al2230.o zd_rf_rf2959.o \
+		zd_rf_al7230b.o zd_rf_uw2453.o \
+		zd_ieee80211.o zd_rf.o zd_usb.o
+
+ifeq ($(CONFIG_ZD1211RW_MAC80211_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_chip.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_chip.c
@@ -0,0 +1,1622 @@
+/* zd_chip.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* This file implements all the hardware specific functions for the ZD1211
+ * and ZD1211B chips. Support for the ZD1211B was possible after Timothy
+ * Legge sent me a ZD1211B device. Thank you Tim. -- Uli
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "zd_def.h"
+#include "zd_chip.h"
+#include "zd_ieee80211.h"
+#include "zd_mac.h"
+#include "zd_rf.h"
+
+void zd_chip_init(struct zd_chip *chip,
+	         struct ieee80211_hw *hw,
+		 struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	zd_usb_init(&chip->usb, hw, intf);
+	zd_rf_init(&chip->rf);
+}
+
+void zd_chip_clear(struct zd_chip *chip)
+{
+	ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+	zd_usb_clear(&chip->usb);
+	zd_rf_clear(&chip->rf);
+	mutex_destroy(&chip->mutex);
+	ZD_MEMCLEAR(chip, sizeof(*chip));
+}
+
+static int scnprint_mac_oui(struct zd_chip *chip, char *buffer, size_t size)
+{
+	u8 *addr = zd_mac_get_perm_addr(zd_chip_to_mac(chip));
+	return scnprintf(buffer, size, "%02x-%02x-%02x",
+		         addr[0], addr[1], addr[2]);
+}
+
+/* Prints an identifier line, which will support debugging. */
+static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size)
+{
+	int i = 0;
+
+	i = scnprintf(buffer, size, "zd1211%s chip ",
+		      zd_chip_is_zd1211b(chip) ? "b" : "");
+	i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i);
+	i += scnprintf(buffer+i, size-i, " ");
+	i += scnprint_mac_oui(chip, buffer+i, size-i);
+	i += scnprintf(buffer+i, size-i, " ");
+	i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i);
+	i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type,
+		chip->patch_cck_gain ? 'g' : '-',
+		chip->patch_cr157 ? '7' : '-',
+		chip->patch_6m_band_edge ? '6' : '-',
+		chip->new_phy_layout ? 'N' : '-',
+		chip->al2230s_bit ? 'S' : '-');
+	return i;
+}
+
+static void print_id(struct zd_chip *chip)
+{
+	char buffer[80];
+
+	scnprint_id(chip, buffer, sizeof(buffer));
+	buffer[sizeof(buffer)-1] = 0;
+	dev_info(zd_chip_dev(chip), "%s\n", buffer);
+}
+
+static zd_addr_t inc_addr(zd_addr_t addr)
+{
+	u16 a = (u16)addr;
+	/* Control registers use byte addressing, but everything else uses word
+	 * addressing. */
+	if ((a & 0xf000) == CR_START)
+		a += 2;
+	else
+		a += 1;
+	return (zd_addr_t)a;
+}
+
+/* Read a variable number of 32-bit values. Parameter count is not allowed to
+ * exceed USB_MAX_IOREAD32_COUNT.
+ */
+int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr,
+		 unsigned int count)
+{
+	int r;
+	int i;
+	zd_addr_t *a16 = (zd_addr_t *)NULL;
+	u16 *v16;
+	unsigned int count16;
+
+	if (count > USB_MAX_IOREAD32_COUNT)
+		return -EINVAL;
+
+	/* Allocate a single memory block for values and addresses. */
+	count16 = 2*count;
+	a16 = (zd_addr_t *)kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)),
+		                   GFP_KERNEL);
+	if (!a16) {
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error ENOMEM in allocation of a16\n");
+		r = -ENOMEM;
+		goto out;
+	}
+	v16 = (u16 *)(a16 + count16);
+
+	for (i = 0; i < count; i++) {
+		int j = 2*i;
+		/* We read the high word always first. */
+		a16[j] = inc_addr(addr[i]);
+		a16[j+1] = addr[i];
+	}
+
+	r = zd_ioread16v_locked(chip, v16, a16, count16);
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error: zd_ioread16v_locked. Error number %d\n", r);
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		int j = 2*i;
+		values[i] = (v16[j] << 16) | v16[j+1];
+	}
+
+out:
+	kfree((void *)a16);
+	return r;
+}
+
+int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+	           unsigned int count)
+{
+	int i, j, r;
+	struct zd_ioreq16 *ioreqs16;
+	unsigned int count16;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+
+	if (count == 0)
+		return 0;
+	if (count > USB_MAX_IOWRITE32_COUNT)
+		return -EINVAL;
+
+	/* Allocate a single memory block for values and addresses. */
+	count16 = 2*count;
+	ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_KERNEL);
+	if (!ioreqs16) {
+		r = -ENOMEM;
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error %d in ioreqs16 allocation\n", r);
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		j = 2*i;
+		/* We write the high word always first. */
+		ioreqs16[j].value   = ioreqs[i].value >> 16;
+		ioreqs16[j].addr    = inc_addr(ioreqs[i].addr);
+		ioreqs16[j+1].value = ioreqs[i].value;
+		ioreqs16[j+1].addr  = ioreqs[i].addr;
+	}
+
+	r = zd_usb_iowrite16v(&chip->usb, ioreqs16, count16);
+#ifdef DEBUG
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error %d in zd_usb_write16v\n", r);
+	}
+#endif /* DEBUG */
+out:
+	kfree(ioreqs16);
+	return r;
+}
+
+int zd_iowrite16a_locked(struct zd_chip *chip,
+                  const struct zd_ioreq16 *ioreqs, unsigned int count)
+{
+	int r;
+	unsigned int i, j, t, max;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	for (i = 0; i < count; i += j + t) {
+		t = 0;
+		max = count-i;
+		if (max > USB_MAX_IOWRITE16_COUNT)
+			max = USB_MAX_IOWRITE16_COUNT;
+		for (j = 0; j < max; j++) {
+			if (!ioreqs[i+j].addr) {
+				t = 1;
+				break;
+			}
+		}
+
+		r = zd_usb_iowrite16v(&chip->usb, &ioreqs[i], j);
+		if (r) {
+			dev_dbg_f(zd_chip_dev(chip),
+				  "error zd_usb_iowrite16v. Error number %d\n",
+				  r);
+			return r;
+		}
+	}
+
+	return 0;
+}
+
+/* Writes a variable number of 32 bit registers. The functions will split
+ * that in several USB requests. A split can be forced by inserting an IO
+ * request with an zero address field.
+ */
+int zd_iowrite32a_locked(struct zd_chip *chip,
+	          const struct zd_ioreq32 *ioreqs, unsigned int count)
+{
+	int r;
+	unsigned int i, j, t, max;
+
+	for (i = 0; i < count; i += j + t) {
+		t = 0;
+		max = count-i;
+		if (max > USB_MAX_IOWRITE32_COUNT)
+			max = USB_MAX_IOWRITE32_COUNT;
+		for (j = 0; j < max; j++) {
+			if (!ioreqs[i+j].addr) {
+				t = 1;
+				break;
+			}
+		}
+
+		r = _zd_iowrite32v_locked(chip, &ioreqs[i], j);
+		if (r) {
+			dev_dbg_f(zd_chip_dev(chip),
+				"error _zd_iowrite32v_locked."
+				" Error number %d\n", r);
+			return r;
+		}
+	}
+
+	return 0;
+}
+
+int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_ioread16_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_ioread32_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite16_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
+	          u32 *values, unsigned int count)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_ioread32v_locked(chip, values, addresses, count);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+	          unsigned int count)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32a_locked(chip, ioreqs, count);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int read_pod(struct zd_chip *chip, u8 *rf_type)
+{
+	int r;
+	u32 value;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &value, E2P_POD);
+	if (r)
+		goto error;
+	dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value);
+
+	/* FIXME: AL2230 handling (Bit 7 in POD) */
+	*rf_type = value & 0x0f;
+	chip->pa_type = (value >> 16) & 0x0f;
+	chip->patch_cck_gain = (value >> 8) & 0x1;
+	chip->patch_cr157 = (value >> 13) & 0x1;
+	chip->patch_6m_band_edge = (value >> 21) & 0x1;
+	chip->new_phy_layout = (value >> 31) & 0x1;
+	chip->al2230s_bit = (value >> 7) & 0x1;
+	chip->link_led = ((value >> 4) & 1) ? LED1 : LED2;
+	chip->supports_tx_led = 1;
+	if (value & (1 << 24)) { /* LED scenario */
+		if (value & (1 << 29))
+			chip->supports_tx_led = 0;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip),
+		"RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d "
+		"patch 6M %d new PHY %d link LED%d tx led %d\n",
+		zd_rf_name(*rf_type), *rf_type,
+		chip->pa_type, chip->patch_cck_gain,
+		chip->patch_cr157, chip->patch_6m_band_edge,
+		chip->new_phy_layout,
+		chip->link_led == LED1 ? 1 : 2,
+		chip->supports_tx_led);
+	return 0;
+error:
+	*rf_type = 0;
+	chip->pa_type = 0;
+	chip->patch_cck_gain = 0;
+	chip->patch_cr157 = 0;
+	chip->patch_6m_band_edge = 0;
+	chip->new_phy_layout = 0;
+	return r;
+}
+
+/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and
+ *              CR_MAC_ADDR_P2 must be overwritten
+ */
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
+{
+	int r;
+	struct zd_ioreq32 reqs[2] = {
+		[0] = { .addr = CR_MAC_ADDR_P1 },
+		[1] = { .addr = CR_MAC_ADDR_P2 },
+	};
+	DECLARE_MAC_BUF(mac);
+
+	if (mac_addr) {
+		reqs[0].value = (mac_addr[3] << 24)
+			      | (mac_addr[2] << 16)
+			      | (mac_addr[1] <<  8)
+			      |  mac_addr[0];
+		reqs[1].value = (mac_addr[5] <<  8)
+			      |  mac_addr[4];
+		dev_dbg_f(zd_chip_dev(chip),
+			"mac addr %s\n", print_mac(mac, mac_addr));
+	} else {
+		dev_dbg_f(zd_chip_dev(chip), "set NULL mac\n");
+	}
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain)
+{
+	int r;
+	u32 value;
+
+	mutex_lock(&chip->mutex);
+	r = zd_ioread32_locked(chip, &value, E2P_SUBID);
+	mutex_unlock(&chip->mutex);
+	if (r)
+		return r;
+
+	*regdomain = value >> 16;
+	dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain);
+
+	return 0;
+}
+
+static int read_values(struct zd_chip *chip, u8 *values, size_t count,
+	               zd_addr_t e2p_addr, u32 guard)
+{
+	int r;
+	int i;
+	u32 v;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	for (i = 0;;) {
+		r = zd_ioread32_locked(chip, &v,
+			               (zd_addr_t)((u16)e2p_addr+i/2));
+		if (r)
+			return r;
+		v -= guard;
+		if (i+4 < count) {
+			values[i++] = v;
+			values[i++] = v >>  8;
+			values[i++] = v >> 16;
+			values[i++] = v >> 24;
+			continue;
+		}
+		for (;i < count; i++)
+			values[i] = v >> (8*(i%3));
+		return 0;
+	}
+}
+
+static int read_pwr_cal_values(struct zd_chip *chip)
+{
+	return read_values(chip, chip->pwr_cal_values,
+		        E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1,
+			0);
+}
+
+static int read_pwr_int_values(struct zd_chip *chip)
+{
+	return read_values(chip, chip->pwr_int_values,
+		        E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1,
+			E2P_PWR_INT_GUARD);
+}
+
+static int read_ofdm_cal_values(struct zd_chip *chip)
+{
+	int r;
+	int i;
+	static const zd_addr_t addresses[] = {
+		E2P_36M_CAL_VALUE1,
+		E2P_48M_CAL_VALUE1,
+		E2P_54M_CAL_VALUE1,
+	};
+
+	for (i = 0; i < 3; i++) {
+		r = read_values(chip, chip->ofdm_cal_values[i],
+				E2P_CHANNEL_COUNT, addresses[i], 0);
+		if (r)
+			return r;
+	}
+	return 0;
+}
+
+static int read_cal_int_tables(struct zd_chip *chip)
+{
+	int r;
+
+	r = read_pwr_cal_values(chip);
+	if (r)
+		return r;
+	r = read_pwr_int_values(chip);
+	if (r)
+		return r;
+	r = read_ofdm_cal_values(chip);
+	if (r)
+		return r;
+	return 0;
+}
+
+/* phy means physical registers */
+int zd_chip_lock_phy_regs(struct zd_chip *chip)
+{
+	int r;
+	u32 tmp;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &tmp, CR_REG1);
+	if (r) {
+		dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r);
+		return r;
+	}
+
+	tmp &= ~UNLOCK_PHY_REGS;
+
+	r = zd_iowrite32_locked(chip, tmp, CR_REG1);
+	if (r)
+		dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r);
+	return r;
+}
+
+int zd_chip_unlock_phy_regs(struct zd_chip *chip)
+{
+	int r;
+	u32 tmp;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &tmp, CR_REG1);
+	if (r) {
+		dev_err(zd_chip_dev(chip),
+			"error ioread32(CR_REG1): %d\n", r);
+		return r;
+	}
+
+	tmp |= UNLOCK_PHY_REGS;
+
+	r = zd_iowrite32_locked(chip, tmp, CR_REG1);
+	if (r)
+		dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r);
+	return r;
+}
+
+/* CR157 can be optionally patched by the EEPROM for original ZD1211 */
+static int patch_cr157(struct zd_chip *chip)
+{
+	int r;
+	u16 value;
+
+	if (!chip->patch_cr157)
+		return 0;
+
+	r = zd_ioread16_locked(chip, &value, E2P_PHY_REG);
+	if (r)
+		return r;
+
+	dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8);
+	return zd_iowrite32_locked(chip, value >> 8, CR157);
+}
+
+/*
+ * 6M band edge can be optionally overwritten for certain RF's
+ * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge
+ * bit (for AL2230, AL2230S)
+ */
+static int patch_6m_band_edge(struct zd_chip *chip, u8 channel)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	if (!chip->patch_6m_band_edge)
+		return 0;
+
+	return zd_rf_patch_6m_band_edge(&chip->rf, channel);
+}
+
+/* Generic implementation of 6M band edge patching, used by most RFs via
+ * zd_rf_generic_patch_6m() */
+int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel)
+{
+	struct zd_ioreq16 ioreqs[] = {
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+		{ CR47,  0x1e },
+	};
+
+	/* FIXME: Channel 11 is not the edge for all regulatory domains. */
+	if (channel == 1 || channel == 11)
+		ioreqs[0].value = 0x12;
+
+	dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel);
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211_hw_reset_phy(struct zd_chip *chip)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR0,   0x0a }, { CR1,   0x06 }, { CR2,   0x26 },
+		{ CR3,   0x38 }, { CR4,   0x80 }, { CR9,   0xa0 },
+		{ CR10,  0x81 }, { CR11,  0x00 }, { CR12,  0x7f },
+		{ CR13,  0x8c }, { CR14,  0x80 }, { CR15,  0x3d },
+		{ CR16,  0x20 }, { CR17,  0x1e }, { CR18,  0x0a },
+		{ CR19,  0x48 }, { CR20,  0x0c }, { CR21,  0x0c },
+		{ CR22,  0x23 }, { CR23,  0x90 }, { CR24,  0x14 },
+		{ CR25,  0x40 }, { CR26,  0x10 }, { CR27,  0x19 },
+		{ CR28,  0x7f }, { CR29,  0x80 }, { CR30,  0x4b },
+		{ CR31,  0x60 }, { CR32,  0x43 }, { CR33,  0x08 },
+		{ CR34,  0x06 }, { CR35,  0x0a }, { CR36,  0x00 },
+		{ CR37,  0x00 }, { CR38,  0x38 }, { CR39,  0x0c },
+		{ CR40,  0x84 }, { CR41,  0x2a }, { CR42,  0x80 },
+		{ CR43,  0x10 }, { CR44,  0x12 }, { CR46,  0xff },
+		{ CR47,  0x1E }, { CR48,  0x26 }, { CR49,  0x5b },
+		{ CR64,  0xd0 }, { CR65,  0x04 }, { CR66,  0x58 },
+		{ CR67,  0xc9 }, { CR68,  0x88 }, { CR69,  0x41 },
+		{ CR70,  0x23 }, { CR71,  0x10 }, { CR72,  0xff },
+		{ CR73,  0x32 }, { CR74,  0x30 }, { CR75,  0x65 },
+		{ CR76,  0x41 }, { CR77,  0x1b }, { CR78,  0x30 },
+		{ CR79,  0x68 }, { CR80,  0x64 }, { CR81,  0x64 },
+		{ CR82,  0x00 }, { CR83,  0x00 }, { CR84,  0x00 },
+		{ CR85,  0x02 }, { CR86,  0x00 }, { CR87,  0x00 },
+		{ CR88,  0xff }, { CR89,  0xfc }, { CR90,  0x00 },
+		{ CR91,  0x00 }, { CR92,  0x00 }, { CR93,  0x08 },
+		{ CR94,  0x00 }, { CR95,  0x00 }, { CR96,  0xff },
+		{ CR97,  0xe7 }, { CR98,  0x00 }, { CR99,  0x00 },
+		{ CR100, 0x00 }, { CR101, 0xae }, { CR102, 0x02 },
+		{ CR103, 0x00 }, { CR104, 0x03 }, { CR105, 0x65 },
+		{ CR106, 0x04 }, { CR107, 0x00 }, { CR108, 0x0a },
+		{ CR109, 0xaa }, { CR110, 0xaa }, { CR111, 0x25 },
+		{ CR112, 0x25 }, { CR113, 0x00 }, { CR119, 0x1e },
+		{ CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 },
+		{ },
+		{ CR5,   0x00 }, { CR6,   0x00 }, { CR7,   0x00 },
+		{ CR8,   0x00 }, { CR9,   0x20 }, { CR12,  0xf0 },
+		{ CR20,  0x0e }, { CR21,  0x0e }, { CR27,  0x10 },
+		{ CR44,  0x33 }, { CR47,  0x1E }, { CR83,  0x24 },
+		{ CR84,  0x04 }, { CR85,  0x00 }, { CR86,  0x0C },
+		{ CR87,  0x12 }, { CR88,  0x0C }, { CR89,  0x00 },
+		{ CR90,  0x10 }, { CR91,  0x08 }, { CR93,  0x00 },
+		{ CR94,  0x01 }, { CR95,  0x00 }, { CR96,  0x50 },
+		{ CR97,  0x37 }, { CR98,  0x35 }, { CR101, 0x13 },
+		{ CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 },
+		{ CR105, 0x12 }, { CR109, 0x27 }, { CR110, 0x27 },
+		{ CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 },
+		{ CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 },
+		{ CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f },
+		{ CR125, 0xaa }, { CR127, 0x03 }, { CR128, 0x14 },
+		{ CR129, 0x12 }, { CR130, 0x10 }, { CR131, 0x0C },
+		{ CR136, 0xdf }, { CR137, 0x40 }, { CR138, 0xa0 },
+		{ CR139, 0xb0 }, { CR140, 0x99 }, { CR141, 0x82 },
+		{ CR142, 0x54 }, { CR143, 0x1c }, { CR144, 0x6c },
+		{ CR147, 0x07 }, { CR148, 0x4c }, { CR149, 0x50 },
+		{ CR150, 0x0e }, { CR151, 0x18 }, { CR160, 0xfe },
+		{ CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa },
+		{ CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe },
+		{ CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba },
+		{ CR170, 0xba }, { CR171, 0xba },
+		/* Note: CR204 must lead the CR203 */
+		{ CR204, 0x7d },
+		{ },
+		{ CR203, 0x30 },
+	};
+
+	int r, t;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		goto out;
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		goto unlock;
+
+	r = patch_cr157(chip);
+unlock:
+	t = zd_chip_unlock_phy_regs(chip);
+	if (t && !r)
+		r = t;
+out:
+	return r;
+}
+
+static int zd1211b_hw_reset_phy(struct zd_chip *chip)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR0,   0x14 }, { CR1,   0x06 }, { CR2,   0x26 },
+		{ CR3,   0x38 }, { CR4,   0x80 }, { CR9,   0xe0 },
+		{ CR10,  0x81 },
+		/* power control { { CR11,  1 << 6 }, */
+		{ CR11,  0x00 },
+		{ CR12,  0xf0 }, { CR13,  0x8c }, { CR14,  0x80 },
+		{ CR15,  0x3d }, { CR16,  0x20 }, { CR17,  0x1e },
+		{ CR18,  0x0a }, { CR19,  0x48 },
+		{ CR20,  0x10 }, /* Org:0x0E, ComTrend:RalLink AP */
+		{ CR21,  0x0e }, { CR22,  0x23 }, { CR23,  0x90 },
+		{ CR24,  0x14 }, { CR25,  0x40 }, { CR26,  0x10 },
+		{ CR27,  0x10 }, { CR28,  0x7f }, { CR29,  0x80 },
+		{ CR30,  0x4b }, /* ASIC/FWT, no jointly decoder */
+		{ CR31,  0x60 }, { CR32,  0x43 }, { CR33,  0x08 },
+		{ CR34,  0x06 }, { CR35,  0x0a }, { CR36,  0x00 },
+		{ CR37,  0x00 }, { CR38,  0x38 }, { CR39,  0x0c },
+		{ CR40,  0x84 }, { CR41,  0x2a }, { CR42,  0x80 },
+		{ CR43,  0x10 }, { CR44,  0x33 }, { CR46,  0xff },
+		{ CR47,  0x1E }, { CR48,  0x26 }, { CR49,  0x5b },
+		{ CR64,  0xd0 }, { CR65,  0x04 }, { CR66,  0x58 },
+		{ CR67,  0xc9 }, { CR68,  0x88 }, { CR69,  0x41 },
+		{ CR70,  0x23 }, { CR71,  0x10 }, { CR72,  0xff },
+		{ CR73,  0x32 }, { CR74,  0x30 }, { CR75,  0x65 },
+		{ CR76,  0x41 }, { CR77,  0x1b }, { CR78,  0x30 },
+		{ CR79,  0xf0 }, { CR80,  0x64 }, { CR81,  0x64 },
+		{ CR82,  0x00 }, { CR83,  0x24 }, { CR84,  0x04 },
+		{ CR85,  0x00 }, { CR86,  0x0c }, { CR87,  0x12 },
+		{ CR88,  0x0c }, { CR89,  0x00 }, { CR90,  0x58 },
+		{ CR91,  0x04 }, { CR92,  0x00 }, { CR93,  0x00 },
+		{ CR94,  0x01 },
+		{ CR95,  0x20 }, /* ZD1211B */
+		{ CR96,  0x50 }, { CR97,  0x37 }, { CR98,  0x35 },
+		{ CR99,  0x00 }, { CR100, 0x01 }, { CR101, 0x13 },
+		{ CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 },
+		{ CR105, 0x12 }, { CR106, 0x04 }, { CR107, 0x00 },
+		{ CR108, 0x0a }, { CR109, 0x27 }, { CR110, 0x27 },
+		{ CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 },
+		{ CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 },
+		{ CR117, 0xfc }, { CR118, 0xfa }, { CR119, 0x1e },
+		{ CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 },
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+		{ CR131, 0x0c }, { CR136, 0xdf }, { CR137, 0xa0 },
+		{ CR138, 0xa8 }, { CR139, 0xb4 }, { CR140, 0x98 },
+		{ CR141, 0x82 }, { CR142, 0x53 }, { CR143, 0x1c },
+		{ CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x40 },
+		{ CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */
+		{ CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */
+		{ CR151, 0x18 }, { CR159, 0x70 }, { CR160, 0xfe },
+		{ CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa },
+		{ CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe },
+		{ CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba },
+		{ CR170, 0xba }, { CR171, 0xba },
+		/* Note: CR204 must lead the CR203 */
+		{ CR204, 0x7d },
+		{},
+		{ CR203, 0x30 },
+	};
+
+	int r, t;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		goto out;
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	t = zd_chip_unlock_phy_regs(chip);
+	if (t && !r)
+		r = t;
+out:
+	return r;
+}
+
+static int hw_reset_phy(struct zd_chip *chip)
+{
+	return zd_chip_is_zd1211b(chip) ? zd1211b_hw_reset_phy(chip) :
+		                  zd1211_hw_reset_phy(chip);
+}
+
+static int zd1211_hw_init_hmac(struct zd_chip *chip)
+{
+	static const struct zd_ioreq32 ioreqs[] = {
+		{ CR_ZD1211_RETRY_MAX,		0x2 },
+		{ CR_RX_THRESHOLD,		0x000c0640 },
+	};
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_hw_init_hmac(struct zd_chip *chip)
+{
+	static const struct zd_ioreq32 ioreqs[] = {
+		{ CR_ZD1211B_RETRY_MAX,		0x02020202 },
+		{ CR_ZD1211B_TX_PWR_CTL4,	0x007f003f },
+		{ CR_ZD1211B_TX_PWR_CTL3,	0x007f003f },
+		{ CR_ZD1211B_TX_PWR_CTL2,       0x003f001f },
+		{ CR_ZD1211B_TX_PWR_CTL1,       0x001f000f },
+		{ CR_ZD1211B_AIFS_CTL1,		0x00280028 },
+		{ CR_ZD1211B_AIFS_CTL2,		0x008C003C },
+		{ CR_ZD1211B_TXOP,		0x01800824 },
+		{ CR_RX_THRESHOLD,		0x000c0eff, },
+	};
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int hw_init_hmac(struct zd_chip *chip)
+{
+	int r;
+	static const struct zd_ioreq32 ioreqs[] = {
+		{ CR_ACK_TIMEOUT_EXT,		0x20 },
+		{ CR_ADDA_MBIAS_WARMTIME,	0x30000808 },
+		{ CR_SNIFFER_ON,		0 },
+		{ CR_RX_FILTER,			STA_RX_FILTER },
+		{ CR_GROUP_HASH_P1,		0x00 },
+		{ CR_GROUP_HASH_P2,		0x80000000 },
+		{ CR_REG1,			0xa4 },
+		{ CR_ADDA_PWR_DWN,		0x7f },
+		{ CR_BCN_PLCP_CFG,		0x00f00401 },
+		{ CR_PHY_DELAY,			0x00 },
+		{ CR_ACK_TIMEOUT_EXT,		0x80 },
+		{ CR_ADDA_PWR_DWN,		0x00 },
+		{ CR_ACK_TIME_80211,		0x100 },
+		{ CR_RX_PE_DELAY,		0x70 },
+		{ CR_PS_CTRL,			0x10000000 },
+		{ CR_RTS_CTS_RATE,		0x02030203 },
+		{ CR_AFTER_PNP,			0x1 },
+		{ CR_WEP_PROTECT,		0x114 },
+		{ CR_IFS_VALUE,			IFS_VALUE_DEFAULT },
+	};
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	return zd_chip_is_zd1211b(chip) ?
+		zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip);
+}
+
+struct aw_pt_bi {
+	u32 atim_wnd_period;
+	u32 pre_tbtt;
+	u32 beacon_interval;
+};
+
+static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
+{
+	int r;
+	static const zd_addr_t aw_pt_bi_addr[] =
+		{ CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL };
+	u32 values[3];
+
+	r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr,
+		         ARRAY_SIZE(aw_pt_bi_addr));
+	if (r) {
+		memset(s, 0, sizeof(*s));
+		return r;
+	}
+
+	s->atim_wnd_period = values[0];
+	s->pre_tbtt = values[1];
+	s->beacon_interval = values[2];
+	return 0;
+}
+
+static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
+{
+	struct zd_ioreq32 reqs[3];
+
+	if (s->beacon_interval <= 5)
+		s->beacon_interval = 5;
+	if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval)
+		s->pre_tbtt = s->beacon_interval - 1;
+	if (s->atim_wnd_period >= s->pre_tbtt)
+		s->atim_wnd_period = s->pre_tbtt - 1;
+
+	reqs[0].addr = CR_ATIM_WND_PERIOD;
+	reqs[0].value = s->atim_wnd_period;
+	reqs[1].addr = CR_PRE_TBTT;
+	reqs[1].value = s->pre_tbtt;
+	reqs[2].addr = CR_BCN_INTERVAL;
+	reqs[2].value = s->beacon_interval;
+
+	return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
+}
+
+
+static int set_beacon_interval(struct zd_chip *chip, u32 interval)
+{
+	int r;
+	struct aw_pt_bi s;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = get_aw_pt_bi(chip, &s);
+	if (r)
+		return r;
+	s.beacon_interval = interval;
+	return set_aw_pt_bi(chip, &s);
+}
+
+int zd_set_beacon_interval(struct zd_chip *chip, u32 interval)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = set_beacon_interval(chip, interval);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int hw_init(struct zd_chip *chip)
+{
+	int r;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = hw_reset_phy(chip);
+	if (r)
+		return r;
+
+	r = hw_init_hmac(chip);
+	if (r)
+		return r;
+
+	return set_beacon_interval(chip, 100);
+}
+
+static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset)
+{
+	return (zd_addr_t)((u16)chip->fw_regs_base + offset);
+}
+
+#ifdef DEBUG
+static int dump_cr(struct zd_chip *chip, const zd_addr_t addr,
+	           const char *addr_string)
+{
+	int r;
+	u32 value;
+
+	r = zd_ioread32_locked(chip, &value, addr);
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			"error reading %s. Error number %d\n", addr_string, r);
+		return r;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n",
+		addr_string, (unsigned int)value);
+	return 0;
+}
+
+static int test_init(struct zd_chip *chip)
+{
+	int r;
+
+	r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP");
+	if (r)
+		return r;
+	r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN");
+	if (r)
+		return r;
+	return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT");
+}
+
+static void dump_fw_registers(struct zd_chip *chip)
+{
+	const zd_addr_t addr[4] = {
+		fw_reg_addr(chip, FW_REG_FIRMWARE_VER),
+		fw_reg_addr(chip, FW_REG_USB_SPEED),
+		fw_reg_addr(chip, FW_REG_FIX_TX_RATE),
+		fw_reg_addr(chip, FW_REG_LED_LINK_STATUS),
+	};
+
+	int r;
+	u16 values[4];
+
+	r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr,
+		         ARRAY_SIZE(addr));
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n",
+			 r);
+		return;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]);
+	dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]);
+	dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]);
+	dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]);
+}
+#endif /* DEBUG */
+
+static int print_fw_version(struct zd_chip *chip)
+{
+	int r;
+	u16 version;
+
+	r = zd_ioread16_locked(chip, &version,
+		fw_reg_addr(chip, FW_REG_FIRMWARE_VER));
+	if (r)
+		return r;
+
+	dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version);
+	return 0;
+}
+
+static int set_mandatory_rates(struct zd_chip *chip, int mode)
+{
+	u32 rates;
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	/* This sets the mandatory rates, which only depend from the standard
+	 * that the device is supporting. Until further notice we should try
+	 * to support 802.11g also for full speed USB.
+	 */
+	switch (mode) {
+	case MODE_IEEE80211B:
+		rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M;
+		break;
+	case MODE_IEEE80211G:
+		rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M|
+			CR_RATE_6M|CR_RATE_12M|CR_RATE_24M;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL);
+}
+
+int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip,
+				    int preamble)
+{
+	u32 value = 0;
+
+	dev_dbg_f(zd_chip_dev(chip), "preamble=%x\n", preamble);
+	value |= preamble << RTSCTS_SH_RTS_PMB_TYPE;
+	value |= preamble << RTSCTS_SH_CTS_PMB_TYPE;
+
+	/* We always send 11M RTS/self-CTS messages, like the vendor driver. */
+	value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_RTS_RATE;
+	value |= ZD_RX_CCK << RTSCTS_SH_RTS_MOD_TYPE;
+	value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_CTS_RATE;
+	value |= ZD_RX_CCK << RTSCTS_SH_CTS_MOD_TYPE;
+
+	return zd_iowrite32_locked(chip, value, CR_RTS_CTS_RATE);
+}
+
+int zd_chip_enable_hwint(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int disable_hwint(struct zd_chip *chip)
+{
+	return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT);
+}
+
+int zd_chip_disable_hwint(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = disable_hwint(chip);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int read_fw_regs_offset(struct zd_chip *chip)
+{
+	int r;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread16_locked(chip, (u16*)&chip->fw_regs_base,
+		               FWRAW_REGS_ADDR);
+	if (r)
+		return r;
+	dev_dbg_f(zd_chip_dev(chip), "fw_regs_base: %#06hx\n",
+		  (u16)chip->fw_regs_base);
+
+	return 0;
+}
+
+/* Read mac address using pre-firmware interface */
+int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr)
+{
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	return zd_usb_read_fw(&chip->usb, E2P_MAC_ADDR_P1, addr,
+		ETH_ALEN);
+}
+
+int zd_chip_init_hw(struct zd_chip *chip)
+{
+	int r;
+	u8 rf_type;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+
+	mutex_lock(&chip->mutex);
+
+#ifdef DEBUG
+	r = test_init(chip);
+	if (r)
+		goto out;
+#endif
+	r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP);
+	if (r)
+		goto out;
+
+	r = read_fw_regs_offset(chip);
+	if (r)
+		goto out;
+
+	/* GPI is always disabled, also in the other driver.
+	 */
+	r = zd_iowrite32_locked(chip, 0, CR_GPI_EN);
+	if (r)
+		goto out;
+	r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX);
+	if (r)
+		goto out;
+	/* Currently we support IEEE 802.11g for full and high speed USB.
+	 * It might be discussed, whether we should suppport pure b mode for
+	 * full speed USB.
+	 */
+	r = set_mandatory_rates(chip, MODE_IEEE80211G);
+	if (r)
+		goto out;
+	/* Disabling interrupts is certainly a smart thing here.
+	 */
+	r = disable_hwint(chip);
+	if (r)
+		goto out;
+	r = read_pod(chip, &rf_type);
+	if (r)
+		goto out;
+	r = hw_init(chip);
+	if (r)
+		goto out;
+	r = zd_rf_init_hw(&chip->rf, rf_type);
+	if (r)
+		goto out;
+
+	r = print_fw_version(chip);
+	if (r)
+		goto out;
+
+#ifdef DEBUG
+	dump_fw_registers(chip);
+	r = test_init(chip);
+	if (r)
+		goto out;
+#endif /* DEBUG */
+
+	r = read_cal_int_tables(chip);
+	if (r)
+		goto out;
+
+	print_id(chip);
+out:
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int update_pwr_int(struct zd_chip *chip, u8 channel)
+{
+	u8 value = chip->pwr_int_values[channel - 1];
+	return zd_iowrite16_locked(chip, value, CR31);
+}
+
+static int update_pwr_cal(struct zd_chip *chip, u8 channel)
+{
+	u8 value = chip->pwr_cal_values[channel-1];
+	return zd_iowrite16_locked(chip, value, CR68);
+}
+
+static int update_ofdm_cal(struct zd_chip *chip, u8 channel)
+{
+	struct zd_ioreq16 ioreqs[3];
+
+	ioreqs[0].addr = CR67;
+	ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1];
+	ioreqs[1].addr = CR66;
+	ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1];
+	ioreqs[2].addr = CR65;
+	ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1];
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int update_channel_integration_and_calibration(struct zd_chip *chip,
+	                                              u8 channel)
+{
+	int r;
+
+	if (!zd_rf_should_update_pwr_int(&chip->rf))
+		return 0;
+
+	r = update_pwr_int(chip, channel);
+	if (r)
+		return r;
+	if (zd_chip_is_zd1211b(chip)) {
+		static const struct zd_ioreq16 ioreqs[] = {
+			{ CR69, 0x28 },
+			{},
+			{ CR69, 0x2a },
+		};
+
+		r = update_ofdm_cal(chip, channel);
+		if (r)
+			return r;
+		r = update_pwr_cal(chip, channel);
+		if (r)
+			return r;
+		r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+/* The CCK baseband gain can be optionally patched by the EEPROM */
+static int patch_cck_gain(struct zd_chip *chip)
+{
+	int r;
+	u32 value;
+
+	if (!chip->patch_cck_gain || !zd_rf_should_patch_cck_gain(&chip->rf))
+		return 0;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &value, E2P_PHY_REG);
+	if (r)
+		return r;
+	dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff);
+	return zd_iowrite16_locked(chip, value & 0xff, CR47);
+}
+
+int zd_chip_set_channel(struct zd_chip *chip, u8 channel)
+{
+	int r, t;
+
+	mutex_lock(&chip->mutex);
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		goto out;
+	r = zd_rf_set_channel(&chip->rf, channel);
+	if (r)
+		goto unlock;
+	r = update_channel_integration_and_calibration(chip, channel);
+	if (r)
+		goto unlock;
+	r = patch_cck_gain(chip);
+	if (r)
+		goto unlock;
+	r = patch_6m_band_edge(chip, channel);
+	if (r)
+		goto unlock;
+	r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS);
+unlock:
+	t = zd_chip_unlock_phy_regs(chip);
+	if (t && !r)
+		r = t;
+out:
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+u8 zd_chip_get_channel(struct zd_chip *chip)
+{
+	u8 channel;
+
+	mutex_lock(&chip->mutex);
+	channel = chip->rf.channel;
+	mutex_unlock(&chip->mutex);
+	return channel;
+}
+
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status)
+{
+	const zd_addr_t a[] = {
+		fw_reg_addr(chip, FW_REG_LED_LINK_STATUS),
+		CR_LED,
+	};
+
+	int r;
+	u16 v[ARRAY_SIZE(a)];
+	struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = {
+		[0] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS) },
+		[1] = { CR_LED },
+	};
+	u16 other_led;
+
+	mutex_lock(&chip->mutex);
+	r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a));
+	if (r)
+		goto out;
+
+	other_led = chip->link_led == LED1 ? LED2 : LED1;
+
+	switch (status) {
+	case LED_OFF:
+		ioreqs[0].value = FW_LINK_OFF;
+		ioreqs[1].value = v[1] & ~(LED1|LED2);
+		break;
+	case LED_SCANNING:
+		ioreqs[0].value = FW_LINK_OFF;
+		ioreqs[1].value = v[1] & ~other_led;
+		if (get_seconds() % 3 == 0) {
+			ioreqs[1].value &= ~chip->link_led;
+		} else {
+			ioreqs[1].value |= chip->link_led;
+		}
+		break;
+	case LED_ASSOCIATED:
+		ioreqs[0].value = FW_LINK_TX;
+		ioreqs[1].value = v[1] & ~other_led;
+		ioreqs[1].value |= chip->link_led;
+		break;
+	default:
+		r = -EINVAL;
+		goto out;
+	}
+
+	if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) {
+		r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+		if (r)
+			goto out;
+	}
+	r = 0;
+out:
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates)
+{
+	int r;
+
+	if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G))
+		return -EINVAL;
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int ofdm_qual_db(u8 status_quality, u8 zd_rate, unsigned int size)
+{
+	static const u16 constants[] = {
+		715, 655, 585, 540, 470, 410, 360, 315,
+		270, 235, 205, 175, 150, 125, 105,  85,
+		 65,  50,  40,  25,  15
+	};
+
+	int i;
+	u32 x;
+
+	/* It seems that their quality parameter is somehow per signal
+	 * and is now transferred per bit.
+	 */
+	switch (zd_rate) {
+	case ZD_OFDM_RATE_6M:
+	case ZD_OFDM_RATE_12M:
+	case ZD_OFDM_RATE_24M:
+		size *= 2;
+		break;
+	case ZD_OFDM_RATE_9M:
+	case ZD_OFDM_RATE_18M:
+	case ZD_OFDM_RATE_36M:
+	case ZD_OFDM_RATE_54M:
+		size *= 4;
+		size /= 3;
+		break;
+	case ZD_OFDM_RATE_48M:
+		size *= 3;
+		size /= 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	x = (10000 * status_quality)/size;
+	for (i = 0; i < ARRAY_SIZE(constants); i++) {
+		if (x > constants[i])
+			break;
+	}
+
+	switch (zd_rate) {
+	case ZD_OFDM_RATE_6M:
+	case ZD_OFDM_RATE_9M:
+		i += 3;
+		break;
+	case ZD_OFDM_RATE_12M:
+	case ZD_OFDM_RATE_18M:
+		i += 5;
+		break;
+	case ZD_OFDM_RATE_24M:
+	case ZD_OFDM_RATE_36M:
+		i += 9;
+		break;
+	case ZD_OFDM_RATE_48M:
+	case ZD_OFDM_RATE_54M:
+		i += 15;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return i;
+}
+
+static int ofdm_qual_percent(u8 status_quality, u8 zd_rate, unsigned int size)
+{
+	int r;
+
+	r = ofdm_qual_db(status_quality, zd_rate, size);
+	ZD_ASSERT(r >= 0);
+	if (r < 0)
+		r = 0;
+
+	r = (r * 100)/29;
+	return r <= 100 ? r : 100;
+}
+
+static unsigned int log10times100(unsigned int x)
+{
+	static const u8 log10[] = {
+		  0,
+		  0,   30,   47,   60,   69,   77,   84,   90,   95,  100,
+		104,  107,  111,  114,  117,  120,  123,  125,  127,  130,
+		132,  134,  136,  138,  139,  141,  143,  144,  146,  147,
+		149,  150,  151,  153,  154,  155,  156,  157,  159,  160,
+		161,  162,  163,  164,  165,  166,  167,  168,  169,  169,
+		170,  171,  172,  173,  174,  174,  175,  176,  177,  177,
+		178,  179,  179,  180,  181,  181,  182,  183,  183,  184,
+		185,  185,  186,  186,  187,  188,  188,  189,  189,  190,
+		190,  191,  191,  192,  192,  193,  193,  194,  194,  195,
+		195,  196,  196,  197,  197,  198,  198,  199,  199,  200,
+		200,  200,  201,  201,  202,  202,  202,  203,  203,  204,
+		204,  204,  205,  205,  206,  206,  206,  207,  207,  207,
+		208,  208,  208,  209,  209,  210,  210,  210,  211,  211,
+		211,  212,  212,  212,  213,  213,  213,  213,  214,  214,
+		214,  215,  215,  215,  216,  216,  216,  217,  217,  217,
+		217,  218,  218,  218,  219,  219,  219,  219,  220,  220,
+		220,  220,  221,  221,  221,  222,  222,  222,  222,  223,
+		223,  223,  223,  224,  224,  224,  224,
+	};
+
+	return x < ARRAY_SIZE(log10) ? log10[x] : 225;
+}
+
+enum {
+	MAX_CCK_EVM_DB = 45,
+};
+
+static int cck_evm_db(u8 status_quality)
+{
+	return (20 * log10times100(status_quality)) / 100;
+}
+
+static int cck_snr_db(u8 status_quality)
+{
+	int r = MAX_CCK_EVM_DB - cck_evm_db(status_quality);
+	ZD_ASSERT(r >= 0);
+	return r;
+}
+
+static int cck_qual_percent(u8 status_quality)
+{
+	int r;
+
+	r = cck_snr_db(status_quality);
+	r = (100*r)/17;
+	return r <= 100 ? r : 100;
+}
+
+static inline u8 zd_rate_from_ofdm_plcp_header(const void *rx_frame)
+{
+	return ZD_OFDM | zd_ofdm_plcp_header_rate(rx_frame);
+}
+
+u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size,
+	              const struct rx_status *status)
+{
+	return (status->frame_status&ZD_RX_OFDM) ?
+		ofdm_qual_percent(status->signal_quality_ofdm,
+				  zd_rate_from_ofdm_plcp_header(rx_frame),
+			          size) :
+		cck_qual_percent(status->signal_quality_cck);
+}
+
+/**
+ * zd_rx_rate - report zd-rate
+ * @rx_frame - received frame
+ * @rx_status - rx_status as given by the device
+ *
+ * This function converts the rate as encoded in the received packet to the
+ * zd-rate, we are using on other places in the driver.
+ */
+u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status)
+{
+	u8 zd_rate;
+	if (status->frame_status & ZD_RX_OFDM) {
+		zd_rate = zd_rate_from_ofdm_plcp_header(rx_frame);
+	} else {
+		switch (zd_cck_plcp_header_signal(rx_frame)) {
+		case ZD_CCK_PLCP_SIGNAL_1M:
+			zd_rate = ZD_CCK_RATE_1M;
+			break;
+		case ZD_CCK_PLCP_SIGNAL_2M:
+			zd_rate = ZD_CCK_RATE_2M;
+			break;
+		case ZD_CCK_PLCP_SIGNAL_5M5:
+			zd_rate = ZD_CCK_RATE_5_5M;
+			break;
+		case ZD_CCK_PLCP_SIGNAL_11M:
+			zd_rate = ZD_CCK_RATE_11M;
+			break;
+		default:
+			zd_rate = 0;
+		}
+	}
+
+	return zd_rate;
+}
+
+int zd_chip_switch_radio_on(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_switch_radio_on(&chip->rf);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_chip_switch_radio_off(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_switch_radio_off(&chip->rf);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_chip_enable_int(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_usb_enable_int(&chip->usb);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+void zd_chip_disable_int(struct zd_chip *chip)
+{
+	mutex_lock(&chip->mutex);
+	zd_usb_disable_int(&chip->usb);
+	mutex_unlock(&chip->mutex);
+}
+
+int zd_chip_enable_rxtx(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	zd_usb_enable_tx(&chip->usb);
+	r = zd_usb_enable_rx(&chip->usb);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+void zd_chip_disable_rxtx(struct zd_chip *chip)
+{
+	mutex_lock(&chip->mutex);
+	zd_usb_disable_rx(&chip->usb);
+	zd_usb_disable_tx(&chip->usb);
+	mutex_unlock(&chip->mutex);
+}
+
+int zd_rfwritev_locked(struct zd_chip *chip,
+	               const u32* values, unsigned int count, u8 bits)
+{
+	int r;
+	unsigned int i;
+
+	for (i = 0; i < count; i++) {
+		r = zd_rfwrite_locked(chip, values[i], bits);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+/*
+ * We can optionally program the RF directly through CR regs, if supported by
+ * the hardware. This is much faster than the older method.
+ */
+int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value)
+{
+	struct zd_ioreq16 ioreqs[] = {
+		{ CR244, (value >> 16) & 0xff },
+		{ CR243, (value >>  8) & 0xff },
+		{ CR242,  value        & 0xff },
+	};
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rfwritev_cr_locked(struct zd_chip *chip,
+	                  const u32 *values, unsigned int count)
+{
+	int r;
+	unsigned int i;
+
+	for (i = 0; i < count; i++) {
+		r = zd_rfwrite_cr_locked(chip, values[i]);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+int zd_chip_set_multicast_hash(struct zd_chip *chip,
+	                       struct zd_mc_hash *hash)
+{
+	struct zd_ioreq32 ioreqs[] = {
+		{ CR_GROUP_HASH_P1, hash->low },
+		{ CR_GROUP_HASH_P2, hash->high },
+	};
+
+	return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_chip.h
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_chip.h
@@ -0,0 +1,943 @@
+/* zd_chip.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_CHIP_H
+#define _ZD_CHIP_H
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+
+/* Header for the Media Access Controller (MAC) and the Baseband Processor
+ * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and
+ * adds a processor for handling the USB protocol.
+ */
+
+/* Address space */
+enum {
+	/* CONTROL REGISTERS */
+	CR_START			= 0x9000,
+
+
+	/* FIRMWARE */
+	FW_START			= 0xee00,
+
+
+	/* EEPROM */
+	E2P_START			= 0xf800,
+	E2P_LEN				= 0x800,
+
+	/* EEPROM layout */
+	E2P_LOAD_CODE_LEN		= 0xe,		/* base 0xf800 */
+	E2P_LOAD_VECT_LEN		= 0x9,		/* base 0xf80e */
+	/* E2P_DATA indexes into this */
+	E2P_DATA_LEN			= 0x7e,		/* base 0xf817 */
+	E2P_BOOT_CODE_LEN		= 0x760,	/* base 0xf895 */
+	E2P_INTR_VECT_LEN		= 0xb,		/* base 0xfff5 */
+
+	/* Some precomputed offsets into the EEPROM */
+	E2P_DATA_OFFSET			= E2P_LOAD_CODE_LEN + E2P_LOAD_VECT_LEN,
+	E2P_BOOT_CODE_OFFSET		= E2P_DATA_OFFSET + E2P_DATA_LEN,
+};
+
+#define CTL_REG(offset) ((zd_addr_t)(CR_START + (offset)))
+#define E2P_DATA(offset) ((zd_addr_t)(E2P_START + E2P_DATA_OFFSET + (offset)))
+#define FWRAW_DATA(offset) ((zd_addr_t)(FW_START + (offset)))
+
+/* 8-bit hardware registers */
+#define CR0   CTL_REG(0x0000)
+#define CR1   CTL_REG(0x0004)
+#define CR2   CTL_REG(0x0008)
+#define CR3   CTL_REG(0x000C)
+
+#define CR5   CTL_REG(0x0010)
+/*	bit 5: if set short preamble used
+ *	bit 6: filter band - Japan channel 14 on, else off
+ */
+#define CR6   CTL_REG(0x0014)
+#define CR7   CTL_REG(0x0018)
+#define CR8   CTL_REG(0x001C)
+
+#define CR4   CTL_REG(0x0020)
+
+#define CR9   CTL_REG(0x0024)
+/*	bit 2: antenna switch (together with CR10) */
+#define CR10  CTL_REG(0x0028)
+/*	bit 1: antenna switch (together with CR9)
+ *	RF2959 controls with CR11 radion on and off
+ */
+#define CR11  CTL_REG(0x002C)
+/*	bit 6:  TX power control for OFDM
+ *	RF2959 controls with CR10 radio on and off
+ */
+#define CR12  CTL_REG(0x0030)
+#define CR13  CTL_REG(0x0034)
+#define CR14  CTL_REG(0x0038)
+#define CR15  CTL_REG(0x003C)
+#define CR16  CTL_REG(0x0040)
+#define CR17  CTL_REG(0x0044)
+#define CR18  CTL_REG(0x0048)
+#define CR19  CTL_REG(0x004C)
+#define CR20  CTL_REG(0x0050)
+#define CR21  CTL_REG(0x0054)
+#define CR22  CTL_REG(0x0058)
+#define CR23  CTL_REG(0x005C)
+#define CR24  CTL_REG(0x0060)	/* CCA threshold */
+#define CR25  CTL_REG(0x0064)
+#define CR26  CTL_REG(0x0068)
+#define CR27  CTL_REG(0x006C)
+#define CR28  CTL_REG(0x0070)
+#define CR29  CTL_REG(0x0074)
+#define CR30  CTL_REG(0x0078)
+#define CR31  CTL_REG(0x007C)	/* TX power control for RF in CCK mode */
+#define CR32  CTL_REG(0x0080)
+#define CR33  CTL_REG(0x0084)
+#define CR34  CTL_REG(0x0088)
+#define CR35  CTL_REG(0x008C)
+#define CR36  CTL_REG(0x0090)
+#define CR37  CTL_REG(0x0094)
+#define CR38  CTL_REG(0x0098)
+#define CR39  CTL_REG(0x009C)
+#define CR40  CTL_REG(0x00A0)
+#define CR41  CTL_REG(0x00A4)
+#define CR42  CTL_REG(0x00A8)
+#define CR43  CTL_REG(0x00AC)
+#define CR44  CTL_REG(0x00B0)
+#define CR45  CTL_REG(0x00B4)
+#define CR46  CTL_REG(0x00B8)
+#define CR47  CTL_REG(0x00BC)	/* CCK baseband gain
+	                         * (patch value might be in EEPROM)
+				 */
+#define CR48  CTL_REG(0x00C0)
+#define CR49  CTL_REG(0x00C4)
+#define CR50  CTL_REG(0x00C8)
+#define CR51  CTL_REG(0x00CC)	/* TX power control for RF in 6-36M modes */
+#define CR52  CTL_REG(0x00D0)	/* TX power control for RF in 48M mode */
+#define CR53  CTL_REG(0x00D4)	/* TX power control for RF in 54M mode */
+#define CR54  CTL_REG(0x00D8)
+#define CR55  CTL_REG(0x00DC)
+#define CR56  CTL_REG(0x00E0)
+#define CR57  CTL_REG(0x00E4)
+#define CR58  CTL_REG(0x00E8)
+#define CR59  CTL_REG(0x00EC)
+#define CR60  CTL_REG(0x00F0)
+#define CR61  CTL_REG(0x00F4)
+#define CR62  CTL_REG(0x00F8)
+#define CR63  CTL_REG(0x00FC)
+#define CR64  CTL_REG(0x0100)
+#define CR65  CTL_REG(0x0104) /* OFDM 54M calibration */
+#define CR66  CTL_REG(0x0108) /* OFDM 48M calibration */
+#define CR67  CTL_REG(0x010C) /* OFDM 36M calibration */
+#define CR68  CTL_REG(0x0110) /* CCK calibration */
+#define CR69  CTL_REG(0x0114)
+#define CR70  CTL_REG(0x0118)
+#define CR71  CTL_REG(0x011C)
+#define CR72  CTL_REG(0x0120)
+#define CR73  CTL_REG(0x0124)
+#define CR74  CTL_REG(0x0128)
+#define CR75  CTL_REG(0x012C)
+#define CR76  CTL_REG(0x0130)
+#define CR77  CTL_REG(0x0134)
+#define CR78  CTL_REG(0x0138)
+#define CR79  CTL_REG(0x013C)
+#define CR80  CTL_REG(0x0140)
+#define CR81  CTL_REG(0x0144)
+#define CR82  CTL_REG(0x0148)
+#define CR83  CTL_REG(0x014C)
+#define CR84  CTL_REG(0x0150)
+#define CR85  CTL_REG(0x0154)
+#define CR86  CTL_REG(0x0158)
+#define CR87  CTL_REG(0x015C)
+#define CR88  CTL_REG(0x0160)
+#define CR89  CTL_REG(0x0164)
+#define CR90  CTL_REG(0x0168)
+#define CR91  CTL_REG(0x016C)
+#define CR92  CTL_REG(0x0170)
+#define CR93  CTL_REG(0x0174)
+#define CR94  CTL_REG(0x0178)
+#define CR95  CTL_REG(0x017C)
+#define CR96  CTL_REG(0x0180)
+#define CR97  CTL_REG(0x0184)
+#define CR98  CTL_REG(0x0188)
+#define CR99  CTL_REG(0x018C)
+#define CR100 CTL_REG(0x0190)
+#define CR101 CTL_REG(0x0194)
+#define CR102 CTL_REG(0x0198)
+#define CR103 CTL_REG(0x019C)
+#define CR104 CTL_REG(0x01A0)
+#define CR105 CTL_REG(0x01A4)
+#define CR106 CTL_REG(0x01A8)
+#define CR107 CTL_REG(0x01AC)
+#define CR108 CTL_REG(0x01B0)
+#define CR109 CTL_REG(0x01B4)
+#define CR110 CTL_REG(0x01B8)
+#define CR111 CTL_REG(0x01BC)
+#define CR112 CTL_REG(0x01C0)
+#define CR113 CTL_REG(0x01C4)
+#define CR114 CTL_REG(0x01C8)
+#define CR115 CTL_REG(0x01CC)
+#define CR116 CTL_REG(0x01D0)
+#define CR117 CTL_REG(0x01D4)
+#define CR118 CTL_REG(0x01D8)
+#define CR119 CTL_REG(0x01DC)
+#define CR120 CTL_REG(0x01E0)
+#define CR121 CTL_REG(0x01E4)
+#define CR122 CTL_REG(0x01E8)
+#define CR123 CTL_REG(0x01EC)
+#define CR124 CTL_REG(0x01F0)
+#define CR125 CTL_REG(0x01F4)
+#define CR126 CTL_REG(0x01F8)
+#define CR127 CTL_REG(0x01FC)
+#define CR128 CTL_REG(0x0200)
+#define CR129 CTL_REG(0x0204)
+#define CR130 CTL_REG(0x0208)
+#define CR131 CTL_REG(0x020C)
+#define CR132 CTL_REG(0x0210)
+#define CR133 CTL_REG(0x0214)
+#define CR134 CTL_REG(0x0218)
+#define CR135 CTL_REG(0x021C)
+#define CR136 CTL_REG(0x0220)
+#define CR137 CTL_REG(0x0224)
+#define CR138 CTL_REG(0x0228)
+#define CR139 CTL_REG(0x022C)
+#define CR140 CTL_REG(0x0230)
+#define CR141 CTL_REG(0x0234)
+#define CR142 CTL_REG(0x0238)
+#define CR143 CTL_REG(0x023C)
+#define CR144 CTL_REG(0x0240)
+#define CR145 CTL_REG(0x0244)
+#define CR146 CTL_REG(0x0248)
+#define CR147 CTL_REG(0x024C)
+#define CR148 CTL_REG(0x0250)
+#define CR149 CTL_REG(0x0254)
+#define CR150 CTL_REG(0x0258)
+#define CR151 CTL_REG(0x025C)
+#define CR152 CTL_REG(0x0260)
+#define CR153 CTL_REG(0x0264)
+#define CR154 CTL_REG(0x0268)
+#define CR155 CTL_REG(0x026C)
+#define CR156 CTL_REG(0x0270)
+#define CR157 CTL_REG(0x0274)
+#define CR158 CTL_REG(0x0278)
+#define CR159 CTL_REG(0x027C)
+#define CR160 CTL_REG(0x0280)
+#define CR161 CTL_REG(0x0284)
+#define CR162 CTL_REG(0x0288)
+#define CR163 CTL_REG(0x028C)
+#define CR164 CTL_REG(0x0290)
+#define CR165 CTL_REG(0x0294)
+#define CR166 CTL_REG(0x0298)
+#define CR167 CTL_REG(0x029C)
+#define CR168 CTL_REG(0x02A0)
+#define CR169 CTL_REG(0x02A4)
+#define CR170 CTL_REG(0x02A8)
+#define CR171 CTL_REG(0x02AC)
+#define CR172 CTL_REG(0x02B0)
+#define CR173 CTL_REG(0x02B4)
+#define CR174 CTL_REG(0x02B8)
+#define CR175 CTL_REG(0x02BC)
+#define CR176 CTL_REG(0x02C0)
+#define CR177 CTL_REG(0x02C4)
+#define CR178 CTL_REG(0x02C8)
+#define CR179 CTL_REG(0x02CC)
+#define CR180 CTL_REG(0x02D0)
+#define CR181 CTL_REG(0x02D4)
+#define CR182 CTL_REG(0x02D8)
+#define CR183 CTL_REG(0x02DC)
+#define CR184 CTL_REG(0x02E0)
+#define CR185 CTL_REG(0x02E4)
+#define CR186 CTL_REG(0x02E8)
+#define CR187 CTL_REG(0x02EC)
+#define CR188 CTL_REG(0x02F0)
+#define CR189 CTL_REG(0x02F4)
+#define CR190 CTL_REG(0x02F8)
+#define CR191 CTL_REG(0x02FC)
+#define CR192 CTL_REG(0x0300)
+#define CR193 CTL_REG(0x0304)
+#define CR194 CTL_REG(0x0308)
+#define CR195 CTL_REG(0x030C)
+#define CR196 CTL_REG(0x0310)
+#define CR197 CTL_REG(0x0314)
+#define CR198 CTL_REG(0x0318)
+#define CR199 CTL_REG(0x031C)
+#define CR200 CTL_REG(0x0320)
+#define CR201 CTL_REG(0x0324)
+#define CR202 CTL_REG(0x0328)
+#define CR203 CTL_REG(0x032C)	/* I2C bus template value & flash control */
+#define CR204 CTL_REG(0x0330)
+#define CR205 CTL_REG(0x0334)
+#define CR206 CTL_REG(0x0338)
+#define CR207 CTL_REG(0x033C)
+#define CR208 CTL_REG(0x0340)
+#define CR209 CTL_REG(0x0344)
+#define CR210 CTL_REG(0x0348)
+#define CR211 CTL_REG(0x034C)
+#define CR212 CTL_REG(0x0350)
+#define CR213 CTL_REG(0x0354)
+#define CR214 CTL_REG(0x0358)
+#define CR215 CTL_REG(0x035C)
+#define CR216 CTL_REG(0x0360)
+#define CR217 CTL_REG(0x0364)
+#define CR218 CTL_REG(0x0368)
+#define CR219 CTL_REG(0x036C)
+#define CR220 CTL_REG(0x0370)
+#define CR221 CTL_REG(0x0374)
+#define CR222 CTL_REG(0x0378)
+#define CR223 CTL_REG(0x037C)
+#define CR224 CTL_REG(0x0380)
+#define CR225 CTL_REG(0x0384)
+#define CR226 CTL_REG(0x0388)
+#define CR227 CTL_REG(0x038C)
+#define CR228 CTL_REG(0x0390)
+#define CR229 CTL_REG(0x0394)
+#define CR230 CTL_REG(0x0398)
+#define CR231 CTL_REG(0x039C)
+#define CR232 CTL_REG(0x03A0)
+#define CR233 CTL_REG(0x03A4)
+#define CR234 CTL_REG(0x03A8)
+#define CR235 CTL_REG(0x03AC)
+#define CR236 CTL_REG(0x03B0)
+
+#define CR240 CTL_REG(0x03C0)
+/*	bit 7:  host-controlled RF register writes
+ * CR241-CR245: for hardware controlled writing of RF bits, not needed for
+ *              USB
+ */
+#define CR241 CTL_REG(0x03C4)
+#define CR242 CTL_REG(0x03C8)
+#define CR243 CTL_REG(0x03CC)
+#define CR244 CTL_REG(0x03D0)
+#define CR245 CTL_REG(0x03D4)
+
+#define CR251 CTL_REG(0x03EC)	/* only used for activation and deactivation of
+				 * Airoha RFs AL2230 and AL7230B
+				 */
+#define CR252 CTL_REG(0x03F0)
+#define CR253 CTL_REG(0x03F4)
+#define CR254 CTL_REG(0x03F8)
+#define CR255 CTL_REG(0x03FC)
+
+#define CR_MAX_PHY_REG 255
+
+/* Taken from the ZYDAS driver, not all of them are relevant for the ZD1211
+ * driver.
+ */
+
+#define CR_RF_IF_CLK			CTL_REG(0x0400)
+#define CR_RF_IF_DATA			CTL_REG(0x0404)
+#define CR_PE1_PE2			CTL_REG(0x0408)
+#define CR_PE2_DLY			CTL_REG(0x040C)
+#define CR_LE1				CTL_REG(0x0410)
+#define CR_LE2				CTL_REG(0x0414)
+/* Seems to enable/disable GPI (General Purpose IO?) */
+#define CR_GPI_EN			CTL_REG(0x0418)
+#define CR_RADIO_PD			CTL_REG(0x042C)
+#define CR_RF2948_PD			CTL_REG(0x042C)
+#define CR_ENABLE_PS_MANUAL_AGC		CTL_REG(0x043C)
+#define CR_CONFIG_PHILIPS		CTL_REG(0x0440)
+#define CR_SA2400_SER_AP		CTL_REG(0x0444)
+#define CR_I2C_WRITE			CTL_REG(0x0444)
+#define CR_SA2400_SER_RP		CTL_REG(0x0448)
+#define CR_RADIO_PE			CTL_REG(0x0458)
+#define CR_RST_BUS_MASTER		CTL_REG(0x045C)
+#define CR_RFCFG			CTL_REG(0x0464)
+#define CR_HSTSCHG			CTL_REG(0x046C)
+#define CR_PHY_ON			CTL_REG(0x0474)
+#define CR_RX_DELAY			CTL_REG(0x0478)
+#define CR_RX_PE_DELAY			CTL_REG(0x047C)
+#define CR_GPIO_1			CTL_REG(0x0490)
+#define CR_GPIO_2			CTL_REG(0x0494)
+#define CR_EncryBufMux			CTL_REG(0x04A8)
+#define CR_PS_CTRL			CTL_REG(0x0500)
+#define CR_ADDA_PWR_DWN			CTL_REG(0x0504)
+#define CR_ADDA_MBIAS_WARMTIME		CTL_REG(0x0508)
+#define CR_MAC_PS_STATE			CTL_REG(0x050C)
+
+#define CR_INTERRUPT			CTL_REG(0x0510)
+#define INT_TX_COMPLETE			(1 <<  0)
+#define INT_RX_COMPLETE			(1 <<  1)
+#define INT_RETRY_FAIL			(1 <<  2)
+#define INT_WAKEUP			(1 <<  3)
+#define INT_DTIM_NOTIFY			(1 <<  5)
+#define INT_CFG_NEXT_BCN		(1 <<  6)
+#define INT_BUS_ABORT			(1 <<  7)
+#define INT_TX_FIFO_READY		(1 <<  8)
+#define INT_UART			(1 <<  9)
+#define INT_TX_COMPLETE_EN		(1 << 16)
+#define INT_RX_COMPLETE_EN		(1 << 17)
+#define INT_RETRY_FAIL_EN		(1 << 18)
+#define INT_WAKEUP_EN			(1 << 19)
+#define INT_DTIM_NOTIFY_EN		(1 << 21)
+#define INT_CFG_NEXT_BCN_EN		(1 << 22)
+#define INT_BUS_ABORT_EN		(1 << 23)
+#define INT_TX_FIFO_READY_EN		(1 << 24)
+#define INT_UART_EN			(1 << 25)
+
+#define CR_TSF_LOW_PART			CTL_REG(0x0514)
+#define CR_TSF_HIGH_PART		CTL_REG(0x0518)
+
+/* Following three values are in time units (1024us)
+ * Following condition must be met:
+ * atim < tbtt < bcn
+ */
+#define CR_ATIM_WND_PERIOD		CTL_REG(0x051C)
+#define CR_BCN_INTERVAL			CTL_REG(0x0520)
+#define CR_PRE_TBTT			CTL_REG(0x0524)
+/* in units of TU(1024us) */
+
+/* for UART support */
+#define CR_UART_RBR_THR_DLL		CTL_REG(0x0540)
+#define CR_UART_DLM_IER			CTL_REG(0x0544)
+#define CR_UART_IIR_FCR			CTL_REG(0x0548)
+#define CR_UART_LCR			CTL_REG(0x054c)
+#define CR_UART_MCR			CTL_REG(0x0550)
+#define CR_UART_LSR			CTL_REG(0x0554)
+#define CR_UART_MSR			CTL_REG(0x0558)
+#define CR_UART_ECR			CTL_REG(0x055c)
+#define CR_UART_STATUS			CTL_REG(0x0560)
+
+#define CR_PCI_TX_ADDR_P1		CTL_REG(0x0600)
+#define CR_PCI_TX_AddR_P2		CTL_REG(0x0604)
+#define CR_PCI_RX_AddR_P1		CTL_REG(0x0608)
+#define CR_PCI_RX_AddR_P2		CTL_REG(0x060C)
+
+/* must be overwritten if custom MAC address will be used */
+#define CR_MAC_ADDR_P1			CTL_REG(0x0610)
+#define CR_MAC_ADDR_P2			CTL_REG(0x0614)
+#define CR_BSSID_P1			CTL_REG(0x0618)
+#define CR_BSSID_P2			CTL_REG(0x061C)
+#define CR_BCN_PLCP_CFG			CTL_REG(0x0620)
+
+/* Group hash table for filtering incoming packets.
+ *
+ * The group hash table is 64 bit large and split over two parts. The first
+ * part is the lower part. The upper 6 bits of the last byte of the target
+ * address are used as index. Packets are received if the hash table bit is
+ * set. This is used for multicast handling, but for broadcasts (address
+ * ff:ff:ff:ff:ff:ff) the highest bit in the second table must also be set.
+ */
+#define CR_GROUP_HASH_P1		CTL_REG(0x0624)
+#define CR_GROUP_HASH_P2		CTL_REG(0x0628)
+
+#define CR_RX_TIMEOUT			CTL_REG(0x062C)
+
+/* Basic rates supported by the BSS. When producing ACK or CTS messages, the
+ * device will use a rate in this table that is less than or equal to the rate
+ * of the incoming frame which prompted the response. */
+#define CR_BASIC_RATE_TBL		CTL_REG(0x0630)
+#define CR_RATE_1M	(1 <<  0)	/* 802.11b */
+#define CR_RATE_2M	(1 <<  1)	/* 802.11b */
+#define CR_RATE_5_5M	(1 <<  2)	/* 802.11b */
+#define CR_RATE_11M	(1 <<  3)	/* 802.11b */
+#define CR_RATE_6M      (1 <<  8)	/* 802.11g */
+#define CR_RATE_9M      (1 <<  9)	/* 802.11g */
+#define CR_RATE_12M	(1 << 10)	/* 802.11g */
+#define CR_RATE_18M	(1 << 11)	/* 802.11g */
+#define CR_RATE_24M     (1 << 12)	/* 802.11g */
+#define CR_RATE_36M     (1 << 13)	/* 802.11g */
+#define CR_RATE_48M     (1 << 14)	/* 802.11g */
+#define CR_RATE_54M     (1 << 15)	/* 802.11g */
+#define CR_RATES_80211G	0xff00
+#define CR_RATES_80211B	0x000f
+
+/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if
+ * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will
+ * look for a rate in this table that is less than or equal to the rate of
+ * the incoming frame. */
+#define CR_MANDATORY_RATE_TBL		CTL_REG(0x0634)
+#define CR_RTS_CTS_RATE			CTL_REG(0x0638)
+
+/* These are all bit indexes in CR_RTS_CTS_RATE, so remember to shift. */
+#define RTSCTS_SH_RTS_RATE		0
+#define RTSCTS_SH_EXP_CTS_RATE		4
+#define RTSCTS_SH_RTS_MOD_TYPE		8
+#define RTSCTS_SH_RTS_PMB_TYPE		9
+#define RTSCTS_SH_CTS_RATE		16
+#define RTSCTS_SH_CTS_MOD_TYPE		24
+#define RTSCTS_SH_CTS_PMB_TYPE		25
+
+#define CR_WEP_PROTECT			CTL_REG(0x063C)
+#define CR_RX_THRESHOLD			CTL_REG(0x0640)
+
+/* register for controlling the LEDS */
+#define CR_LED				CTL_REG(0x0644)
+/* masks for controlling LEDs */
+#define LED1				(1 <<  8)
+#define LED2				(1 <<  9)
+#define LED_SW				(1 << 10)
+
+/* Seems to indicate that the configuration is over.
+ */
+#define CR_AFTER_PNP			CTL_REG(0x0648)
+#define CR_ACK_TIME_80211		CTL_REG(0x0658)
+
+#define CR_RX_OFFSET			CTL_REG(0x065c)
+
+#define CR_PHY_DELAY			CTL_REG(0x066C)
+#define CR_BCN_FIFO			CTL_REG(0x0670)
+#define CR_SNIFFER_ON			CTL_REG(0x0674)
+
+#define CR_ENCRYPTION_TYPE		CTL_REG(0x0678)
+#define NO_WEP				0
+#define WEP64				1
+#define WEP128				5
+#define WEP256				6
+#define ENC_SNIFFER			8
+
+#define CR_ZD1211_RETRY_MAX		CTL_REG(0x067C)
+
+#define CR_REG1				CTL_REG(0x0680)
+/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical
+ * registers, so one could argue it is a LOCK bit. But calling it
+ * LOCK_PHY_REGS makes it confusing.
+ */
+#define UNLOCK_PHY_REGS			(1 << 7)
+
+#define CR_DEVICE_STATE			CTL_REG(0x0684)
+#define CR_UNDERRUN_CNT			CTL_REG(0x0688)
+
+#define CR_RX_FILTER			CTL_REG(0x068c)
+#define RX_FILTER_ASSOC_REQUEST		(1 <<  0)
+#define RX_FILTER_ASSOC_RESPONSE	(1 <<  1)
+#define RX_FILTER_REASSOC_REQUEST	(1 <<  2)
+#define RX_FILTER_REASSOC_RESPONSE	(1 <<  3)
+#define RX_FILTER_PROBE_REQUEST		(1 <<  4)
+#define RX_FILTER_PROBE_RESPONSE	(1 <<  5)
+/* bits 6 and 7 reserved */
+#define RX_FILTER_BEACON		(1 <<  8)
+#define RX_FILTER_ATIM			(1 <<  9)
+#define RX_FILTER_DISASSOC		(1 << 10)
+#define RX_FILTER_AUTH			(1 << 11)
+#define RX_FILTER_DEAUTH		(1 << 12)
+#define RX_FILTER_PSPOLL		(1 << 26)
+#define RX_FILTER_RTS			(1 << 27)
+#define RX_FILTER_CTS			(1 << 28)
+#define RX_FILTER_ACK			(1 << 29)
+#define RX_FILTER_CFEND			(1 << 30)
+#define RX_FILTER_CFACK			(1 << 31)
+
+/* Enable bits for all frames you are interested in. */
+#define STA_RX_FILTER	(RX_FILTER_ASSOC_REQUEST | RX_FILTER_ASSOC_RESPONSE | \
+	RX_FILTER_REASSOC_REQUEST | RX_FILTER_REASSOC_RESPONSE | \
+	RX_FILTER_PROBE_REQUEST | RX_FILTER_PROBE_RESPONSE | \
+	(0x3 << 6) /* vendor driver sets these reserved bits */ | \
+	RX_FILTER_BEACON | RX_FILTER_ATIM | RX_FILTER_DISASSOC | \
+	RX_FILTER_AUTH | RX_FILTER_DEAUTH | \
+	(0x7 << 13) /* vendor driver sets these reserved bits */ | \
+	RX_FILTER_PSPOLL | RX_FILTER_ACK) /* 0x2400ffff */
+
+#define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \
+	RX_FILTER_CFEND | RX_FILTER_CFACK)
+
+/* Monitor mode sets filter to 0xfffff */
+
+#define CR_ACK_TIMEOUT_EXT		CTL_REG(0x0690)
+#define CR_BCN_FIFO_SEMAPHORE		CTL_REG(0x0694)
+
+#define CR_IFS_VALUE			CTL_REG(0x0698)
+#define IFS_VALUE_DIFS_SH		0
+#define IFS_VALUE_EIFS_SH		12
+#define IFS_VALUE_SIFS_SH		24
+#define IFS_VALUE_DEFAULT		((  50 << IFS_VALUE_DIFS_SH) | \
+					 (1148 << IFS_VALUE_EIFS_SH) | \
+					 (  10 << IFS_VALUE_SIFS_SH))
+
+#define CR_RX_TIME_OUT			CTL_REG(0x069C)
+#define CR_TOTAL_RX_FRM			CTL_REG(0x06A0)
+#define CR_CRC32_CNT			CTL_REG(0x06A4)
+#define CR_CRC16_CNT			CTL_REG(0x06A8)
+#define CR_DECRYPTION_ERR_UNI		CTL_REG(0x06AC)
+#define CR_RX_FIFO_OVERRUN		CTL_REG(0x06B0)
+
+#define CR_DECRYPTION_ERR_MUL		CTL_REG(0x06BC)
+
+#define CR_NAV_CNT			CTL_REG(0x06C4)
+#define CR_NAV_CCA			CTL_REG(0x06C8)
+#define CR_RETRY_CNT			CTL_REG(0x06CC)
+
+#define CR_READ_TCB_ADDR		CTL_REG(0x06E8)
+#define CR_READ_RFD_ADDR		CTL_REG(0x06EC)
+#define CR_CWMIN_CWMAX			CTL_REG(0x06F0)
+#define CR_TOTAL_TX_FRM			CTL_REG(0x06F4)
+
+/* CAM: Continuous Access Mode (power management) */
+#define CR_CAM_MODE			CTL_REG(0x0700)
+#define CR_CAM_ROLL_TB_LOW		CTL_REG(0x0704)
+#define CR_CAM_ROLL_TB_HIGH		CTL_REG(0x0708)
+#define CR_CAM_ADDRESS			CTL_REG(0x070C)
+#define CR_CAM_DATA			CTL_REG(0x0710)
+
+#define CR_ROMDIR			CTL_REG(0x0714)
+
+#define CR_DECRY_ERR_FLG_LOW		CTL_REG(0x0714)
+#define CR_DECRY_ERR_FLG_HIGH		CTL_REG(0x0718)
+
+#define CR_WEPKEY0			CTL_REG(0x0720)
+#define CR_WEPKEY1			CTL_REG(0x0724)
+#define CR_WEPKEY2			CTL_REG(0x0728)
+#define CR_WEPKEY3			CTL_REG(0x072C)
+#define CR_WEPKEY4			CTL_REG(0x0730)
+#define CR_WEPKEY5			CTL_REG(0x0734)
+#define CR_WEPKEY6			CTL_REG(0x0738)
+#define CR_WEPKEY7			CTL_REG(0x073C)
+#define CR_WEPKEY8			CTL_REG(0x0740)
+#define CR_WEPKEY9			CTL_REG(0x0744)
+#define CR_WEPKEY10			CTL_REG(0x0748)
+#define CR_WEPKEY11			CTL_REG(0x074C)
+#define CR_WEPKEY12			CTL_REG(0x0750)
+#define CR_WEPKEY13			CTL_REG(0x0754)
+#define CR_WEPKEY14			CTL_REG(0x0758)
+#define CR_WEPKEY15			CTL_REG(0x075c)
+#define CR_TKIP_MODE			CTL_REG(0x0760)
+
+#define CR_EEPROM_PROTECT0		CTL_REG(0x0758)
+#define CR_EEPROM_PROTECT1		CTL_REG(0x075C)
+
+#define CR_DBG_FIFO_RD			CTL_REG(0x0800)
+#define CR_DBG_SELECT			CTL_REG(0x0804)
+#define CR_FIFO_Length			CTL_REG(0x0808)
+
+
+#define CR_RSSI_MGC			CTL_REG(0x0810)
+
+#define CR_PON				CTL_REG(0x0818)
+#define CR_RX_ON			CTL_REG(0x081C)
+#define CR_TX_ON			CTL_REG(0x0820)
+#define CR_CHIP_EN			CTL_REG(0x0824)
+#define CR_LO_SW			CTL_REG(0x0828)
+#define CR_TXRX_SW			CTL_REG(0x082C)
+#define CR_S_MD				CTL_REG(0x0830)
+
+#define CR_USB_DEBUG_PORT		CTL_REG(0x0888)
+
+#define CR_ZD1211B_TX_PWR_CTL1		CTL_REG(0x0b00)
+#define CR_ZD1211B_TX_PWR_CTL2		CTL_REG(0x0b04)
+#define CR_ZD1211B_TX_PWR_CTL3		CTL_REG(0x0b08)
+#define CR_ZD1211B_TX_PWR_CTL4		CTL_REG(0x0b0c)
+#define CR_ZD1211B_AIFS_CTL1		CTL_REG(0x0b10)
+#define CR_ZD1211B_AIFS_CTL2		CTL_REG(0x0b14)
+#define CR_ZD1211B_TXOP			CTL_REG(0x0b20)
+#define CR_ZD1211B_RETRY_MAX		CTL_REG(0x0b28)
+
+/* Used to detect PLL lock */
+#define UW2453_INTR_REG			((zd_addr_t)0x85c1)
+
+#define CWIN_SIZE			0x007f043f
+
+
+#define HWINT_ENABLED			0x004f0000
+#define HWINT_DISABLED			0
+
+#define E2P_PWR_INT_GUARD		8
+#define E2P_CHANNEL_COUNT		14
+
+/* If you compare this addresses with the ZYDAS orignal driver, please notify
+ * that we use word mapping for the EEPROM.
+ */
+
+/*
+ * Upper 16 bit contains the regulatory domain.
+ */
+#define E2P_SUBID		E2P_DATA(0x00)
+#define E2P_POD			E2P_DATA(0x02)
+#define E2P_MAC_ADDR_P1		E2P_DATA(0x04)
+#define E2P_MAC_ADDR_P2		E2P_DATA(0x06)
+#define E2P_PWR_CAL_VALUE1	E2P_DATA(0x08)
+#define E2P_PWR_CAL_VALUE2	E2P_DATA(0x0a)
+#define E2P_PWR_CAL_VALUE3	E2P_DATA(0x0c)
+#define E2P_PWR_CAL_VALUE4      E2P_DATA(0x0e)
+#define E2P_PWR_INT_VALUE1	E2P_DATA(0x10)
+#define E2P_PWR_INT_VALUE2	E2P_DATA(0x12)
+#define E2P_PWR_INT_VALUE3	E2P_DATA(0x14)
+#define E2P_PWR_INT_VALUE4	E2P_DATA(0x16)
+
+/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30)
+ * also only 11 channels. */
+#define E2P_ALLOWED_CHANNEL	E2P_DATA(0x18)
+
+#define E2P_DEVICE_VER		E2P_DATA(0x20)
+#define E2P_PHY_REG		E2P_DATA(0x25)
+#define E2P_36M_CAL_VALUE1	E2P_DATA(0x28)
+#define E2P_36M_CAL_VALUE2      E2P_DATA(0x2a)
+#define E2P_36M_CAL_VALUE3      E2P_DATA(0x2c)
+#define E2P_36M_CAL_VALUE4	E2P_DATA(0x2e)
+#define E2P_11A_INT_VALUE1	E2P_DATA(0x30)
+#define E2P_11A_INT_VALUE2	E2P_DATA(0x32)
+#define E2P_11A_INT_VALUE3	E2P_DATA(0x34)
+#define E2P_11A_INT_VALUE4	E2P_DATA(0x36)
+#define E2P_48M_CAL_VALUE1	E2P_DATA(0x38)
+#define E2P_48M_CAL_VALUE2	E2P_DATA(0x3a)
+#define E2P_48M_CAL_VALUE3	E2P_DATA(0x3c)
+#define E2P_48M_CAL_VALUE4	E2P_DATA(0x3e)
+#define E2P_48M_INT_VALUE1	E2P_DATA(0x40)
+#define E2P_48M_INT_VALUE2	E2P_DATA(0x42)
+#define E2P_48M_INT_VALUE3	E2P_DATA(0x44)
+#define E2P_48M_INT_VALUE4	E2P_DATA(0x46)
+#define E2P_54M_CAL_VALUE1	E2P_DATA(0x48)	/* ??? */
+#define E2P_54M_CAL_VALUE2	E2P_DATA(0x4a)
+#define E2P_54M_CAL_VALUE3	E2P_DATA(0x4c)
+#define E2P_54M_CAL_VALUE4	E2P_DATA(0x4e)
+#define E2P_54M_INT_VALUE1	E2P_DATA(0x50)
+#define E2P_54M_INT_VALUE2	E2P_DATA(0x52)
+#define E2P_54M_INT_VALUE3	E2P_DATA(0x54)
+#define E2P_54M_INT_VALUE4	E2P_DATA(0x56)
+
+/* This word contains the base address of the FW_REG_ registers below */
+#define FWRAW_REGS_ADDR		FWRAW_DATA(0x1d)
+
+/* All 16 bit values, offset from the address in FWRAW_REGS_ADDR */
+enum {
+	FW_REG_FIRMWARE_VER	= 0,
+	/* non-zero if USB high speed connection */
+	FW_REG_USB_SPEED	= 1,
+	FW_REG_FIX_TX_RATE	= 2,
+	/* Seems to be able to control LEDs over the firmware */
+	FW_REG_LED_LINK_STATUS	= 3,
+	FW_REG_SOFT_RESET	= 4,
+	FW_REG_FLASH_CHK	= 5,
+};
+
+/* Values for FW_LINK_STATUS */
+#define FW_LINK_OFF		0x0
+#define FW_LINK_TX		0x1
+/* 0x2 - link led on? */
+
+enum {
+	/* indices for ofdm_cal_values */
+	OFDM_36M_INDEX = 0,
+	OFDM_48M_INDEX = 1,
+	OFDM_54M_INDEX = 2,
+};
+
+struct zd_chip {
+	struct zd_usb usb;
+	struct zd_rf rf;
+	struct mutex mutex;
+	/* Base address of FW_REG_ registers */
+	zd_addr_t fw_regs_base;
+	/* EepSetPoint in the vendor driver */
+	u8 pwr_cal_values[E2P_CHANNEL_COUNT];
+	/* integration values in the vendor driver */
+	u8 pwr_int_values[E2P_CHANNEL_COUNT];
+	/* SetPointOFDM in the vendor driver */
+	u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT];
+	u16 link_led;
+	unsigned int pa_type:4,
+		patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
+		new_phy_layout:1, al2230s_bit:1,
+		supports_tx_led:1;
+};
+
+static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb)
+{
+	return container_of(usb, struct zd_chip, usb);
+}
+
+static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf)
+{
+	return container_of(rf, struct zd_chip, rf);
+}
+
+#define zd_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void zd_chip_init(struct zd_chip *chip,
+	         struct ieee80211_hw *hw,
+	         struct usb_interface *intf);
+void zd_chip_clear(struct zd_chip *chip);
+int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr);
+int zd_chip_init_hw(struct zd_chip *chip);
+int zd_chip_reset(struct zd_chip *chip);
+
+static inline int zd_chip_is_zd1211b(struct zd_chip *chip)
+{
+	return chip->usb.is_zd1211b;
+}
+
+static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values,
+	                              const zd_addr_t *addresses,
+				      unsigned int count)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_usb_ioread16v(&chip->usb, values, addresses, count);
+}
+
+static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value,
+	                             const zd_addr_t addr)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_usb_ioread16(&chip->usb, value, addr);
+}
+
+int zd_ioread32v_locked(struct zd_chip *chip, u32 *values,
+	                const zd_addr_t *addresses, unsigned int count);
+
+static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value,
+	                             const zd_addr_t addr)
+{
+	return zd_ioread32v_locked(chip, value, (const zd_addr_t *)&addr, 1);
+}
+
+static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value,
+	                              zd_addr_t addr)
+{
+	struct zd_ioreq16 ioreq;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	ioreq.addr = addr;
+	ioreq.value = value;
+
+	return zd_usb_iowrite16v(&chip->usb, &ioreq, 1);
+}
+
+int zd_iowrite16a_locked(struct zd_chip *chip,
+                         const struct zd_ioreq16 *ioreqs, unsigned int count);
+
+int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+			  unsigned int count);
+
+static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value,
+	                              zd_addr_t addr)
+{
+	struct zd_ioreq32 ioreq;
+
+	ioreq.addr = addr;
+	ioreq.value = value;
+
+	return _zd_iowrite32v_locked(chip, &ioreq, 1);
+}
+
+int zd_iowrite32a_locked(struct zd_chip *chip,
+	                 const struct zd_ioreq32 *ioreqs, unsigned int count);
+
+static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_usb_rfwrite(&chip->usb, value, bits);
+}
+
+int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value);
+
+int zd_rfwritev_locked(struct zd_chip *chip,
+	               const u32* values, unsigned int count, u8 bits);
+int zd_rfwritev_cr_locked(struct zd_chip *chip,
+	                  const u32* values, unsigned int count);
+
+/* Locking functions for reading and writing registers.
+ * The different parameters are intentional.
+ */
+int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value);
+int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value);
+int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value);
+int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value);
+int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
+	          u32 *values, unsigned int count);
+int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+	           unsigned int count);
+
+int zd_chip_set_channel(struct zd_chip *chip, u8 channel);
+static inline u8 _zd_chip_get_channel(struct zd_chip *chip)
+{
+	return chip->rf.channel;
+}
+u8  zd_chip_get_channel(struct zd_chip *chip);
+int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain);
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr);
+int zd_chip_switch_radio_on(struct zd_chip *chip);
+int zd_chip_switch_radio_off(struct zd_chip *chip);
+int zd_chip_enable_int(struct zd_chip *chip);
+void zd_chip_disable_int(struct zd_chip *chip);
+int zd_chip_enable_rxtx(struct zd_chip *chip);
+void zd_chip_disable_rxtx(struct zd_chip *chip);
+int zd_chip_enable_hwint(struct zd_chip *chip);
+int zd_chip_disable_hwint(struct zd_chip *chip);
+int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel);
+int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, int preamble);
+
+static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type)
+{
+	return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type);
+}
+
+static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type)
+{
+	return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type);
+}
+
+static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates)
+{
+	return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates);
+}
+
+int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates);
+
+int zd_chip_lock_phy_regs(struct zd_chip *chip);
+int zd_chip_unlock_phy_regs(struct zd_chip *chip);
+
+enum led_status {
+	LED_OFF = 0,
+	LED_SCANNING = 1,
+	LED_ASSOCIATED = 2,
+};
+
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status);
+
+int zd_set_beacon_interval(struct zd_chip *chip, u32 interval);
+
+static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval)
+{
+	return zd_ioread32(chip, CR_BCN_INTERVAL, interval);
+}
+
+struct rx_status;
+
+u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size,
+	               const struct rx_status *status);
+
+u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status);
+
+struct zd_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+static inline void zd_mc_clear(struct zd_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void zd_mc_add_all(struct zd_mc_hash *hash)
+{
+	hash->low = hash->high = 0xffffffff;
+}
+
+static inline void zd_mc_add_addr(struct zd_mc_hash *hash, u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+	if (i < 32) {
+		hash->low |= 1 << i;
+	} else {
+		hash->high |= 1 << (i-32);
+	}
+}
+
+int zd_chip_set_multicast_hash(struct zd_chip *chip,
+	                       struct zd_mc_hash *hash);
+
+#endif /* _ZD_CHIP_H */
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_def.h
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_def.h
@@ -0,0 +1,57 @@
+/* zd_def.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_DEF_H
+#define _ZD_DEF_H
+
+#include <linux/kernel.h>
+#include <linux/stringify.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+typedef u16 __nocast zd_addr_t;
+
+#define dev_printk_f(level, dev, fmt, args...) \
+	dev_printk(level, dev, "%s() " fmt, __func__, ##args)
+
+#ifdef DEBUG
+#  define dev_dbg_f(dev, fmt, args...) \
+	  dev_printk_f(KERN_DEBUG, dev, fmt, ## args)
+#else
+#  define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0)
+#endif /* DEBUG */
+
+#ifdef DEBUG
+#  define ZD_ASSERT(x) \
+do { \
+	if (!(x)) { \
+		pr_debug("%s:%d ASSERT %s VIOLATED!\n", \
+			__FILE__, __LINE__, __stringify(x)); \
+		dump_stack(); \
+	} \
+} while (0)
+#else
+#  define ZD_ASSERT(x) do { } while (0)
+#endif
+
+#ifdef DEBUG
+#  define ZD_MEMCLEAR(pointer, size) memset((pointer), 0xff, (size))
+#else
+#  define ZD_MEMCLEAR(pointer, size) do { } while (0)
+#endif
+
+#endif /* _ZD_DEF_H */
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.c
@@ -0,0 +1,100 @@
+/* zd_ieee80211.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * In the long term, we'll probably find a better way of handling regulatory
+ * requirements outside of the driver.
+ */
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "zd_ieee80211.h"
+#include "zd_mac.h"
+
+struct channel_range {
+	u8 regdomain;
+	u8 start;
+	u8 end; /* exclusive (channel must be less than end) */
+};
+
+static const struct channel_range channel_ranges[] = {
+	{ ZD_REGDOMAIN_FCC,		1, 12 },
+	{ ZD_REGDOMAIN_IC,		1, 12 },
+	{ ZD_REGDOMAIN_ETSI,		1, 14 },
+	{ ZD_REGDOMAIN_JAPAN,		1, 14 },
+	{ ZD_REGDOMAIN_SPAIN,		1, 14 },
+	{ ZD_REGDOMAIN_FRANCE,		1, 14 },
+
+	/* Japan originally only had channel 14 available (see CHNL_ID 0x40 in
+	 * 802.11). However, in 2001 the range was extended to include channels
+	 * 1-13. The ZyDAS devices still use the old region code but are
+	 * designed to allow the extra channel access in Japan. */
+	{ ZD_REGDOMAIN_JAPAN_ADD,	1, 15 },
+};
+
+static const struct channel_range *zd_channel_range(u8 regdomain)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) {
+		const struct channel_range *range = &channel_ranges[i];
+		if (range->regdomain == regdomain)
+			return range;
+	}
+	return NULL;
+}
+
+#define CHAN_TO_IDX(chan) ((chan) - 1)
+
+static void unmask_bg_channels(struct ieee80211_hw *hw,
+	const struct channel_range *range,
+	struct ieee80211_hw_mode *mode)
+{
+	u8 channel;
+
+	for (channel = range->start; channel < range->end; channel++) {
+		struct ieee80211_channel *chan =
+			&mode->channels[CHAN_TO_IDX(channel)];
+		chan->flag |= IEEE80211_CHAN_W_SCAN |
+			IEEE80211_CHAN_W_ACTIVE_SCAN |
+			IEEE80211_CHAN_W_IBSS;
+	}
+}
+
+void zd_geo_init(struct ieee80211_hw *hw, u8 regdomain)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	const struct channel_range *range;
+
+	dev_dbg(zd_mac_dev(mac), "regdomain %#02x\n", regdomain);
+
+	range = zd_channel_range(regdomain);
+	if (!range) {
+		/* The vendor driver overrides the regulatory domain and
+		 * allowed channel registers and unconditionally restricts
+		 * available channels to 1-11 everywhere. Match their
+		 * questionable behaviour only for regdomains which we don't
+		 * recognise. */
+		dev_warn(zd_mac_dev(mac), "Unrecognised regulatory domain: "
+			"%#02x. Defaulting to FCC.\n", regdomain);
+		range = zd_channel_range(ZD_REGDOMAIN_FCC);
+	}
+
+	unmask_bg_channels(hw, range, &mac->modes[0]);
+	unmask_bg_channels(hw, range, &mac->modes[1]);
+}
+
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.h
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.h
@@ -0,0 +1,75 @@
+#ifndef _ZD_IEEE80211_H
+#define _ZD_IEEE80211_H
+
+#include <net/mac80211.h>
+
+/* Additional definitions from the standards.
+ */
+
+#define ZD_REGDOMAIN_FCC	0x10
+#define ZD_REGDOMAIN_IC		0x20
+#define ZD_REGDOMAIN_ETSI	0x30
+#define ZD_REGDOMAIN_SPAIN	0x31
+#define ZD_REGDOMAIN_FRANCE	0x32
+#define ZD_REGDOMAIN_JAPAN_ADD	0x40
+#define ZD_REGDOMAIN_JAPAN	0x41
+
+enum {
+	MIN_CHANNEL24 = 1,
+	MAX_CHANNEL24 = 14,
+};
+
+void zd_geo_init(struct ieee80211_hw *hw, u8 regdomain);
+
+#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80
+
+struct ofdm_plcp_header {
+	u8 prefix[3];
+	__le16 service;
+} __attribute__((packed));
+
+static inline u8 zd_ofdm_plcp_header_rate(const struct ofdm_plcp_header *header)
+{
+	return header->prefix[0] & 0xf;
+}
+
+/* The following defines give the encoding of the 4-bit rate field in the
+ * OFDM (802.11a/802.11g) PLCP header. Notify that these values are used to
+ * define the zd-rate values for OFDM.
+ *
+ * See the struct zd_ctrlset definition in zd_mac.h.
+ */
+#define ZD_OFDM_PLCP_RATE_6M	0xb
+#define ZD_OFDM_PLCP_RATE_9M	0xf
+#define ZD_OFDM_PLCP_RATE_12M	0xa
+#define ZD_OFDM_PLCP_RATE_18M	0xe
+#define ZD_OFDM_PLCP_RATE_24M	0x9
+#define ZD_OFDM_PLCP_RATE_36M	0xd
+#define ZD_OFDM_PLCP_RATE_48M	0x8
+#define ZD_OFDM_PLCP_RATE_54M	0xc
+
+struct cck_plcp_header {
+	u8 signal;
+	u8 service;
+	__le16 length;
+	__le16 crc16;
+} __attribute__((packed));
+
+static inline u8 zd_cck_plcp_header_signal(const struct cck_plcp_header *header)
+{
+	return header->signal;
+}
+
+/* These defines give the encodings of the signal field in the 802.11b PLCP
+ * header. The signal field gives the bit rate of the following packet. Even
+ * if technically wrong we use CCK here also for the 1 MBit/s and 2 MBit/s
+ * rate to stay consistent with Zydas and our use of the term.
+ *
+ * Notify that these values are *not* used in the zd-rates.
+ */
+#define ZD_CCK_PLCP_SIGNAL_1M	0x0a
+#define ZD_CCK_PLCP_SIGNAL_2M	0x14
+#define ZD_CCK_PLCP_SIGNAL_5M5	0x37
+#define ZD_CCK_PLCP_SIGNAL_11M	0x6e
+
+#endif /* _ZD_IEEE80211_H */
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_mac.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_mac.c
@@ -0,0 +1,978 @@
+/* zd_mac.c
+ *
+ * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "zd_def.h"
+#include "zd_chip.h"
+#include "zd_mac.h"
+#include "zd_ieee80211.h"
+#include "zd_rf.h"
+
+/* This table contains the hardware specific values for the modulation rates. */
+static const struct ieee80211_rate zd_rates[] = {
+	{ .rate = 10,
+	  .val = ZD_CCK_RATE_1M,
+	  .flags = IEEE80211_RATE_CCK },
+	{ .rate = 20,
+	  .val = ZD_CCK_RATE_2M,
+	  .val2 = ZD_CCK_RATE_2M | ZD_CCK_PREA_SHORT,
+	  .flags = IEEE80211_RATE_CCK_2 },
+	{ .rate = 55,
+	  .val = ZD_CCK_RATE_5_5M,
+	  .val2 = ZD_CCK_RATE_5_5M | ZD_CCK_PREA_SHORT,
+	  .flags = IEEE80211_RATE_CCK_2 },
+	{ .rate = 110,
+	  .val = ZD_CCK_RATE_11M,
+	  .val2 = ZD_CCK_RATE_11M | ZD_CCK_PREA_SHORT,
+	  .flags = IEEE80211_RATE_CCK_2 },
+	{ .rate = 60,
+	  .val = ZD_OFDM_RATE_6M,
+	  .flags = IEEE80211_RATE_OFDM },
+	{ .rate = 90,
+	  .val = ZD_OFDM_RATE_9M,
+	  .flags = IEEE80211_RATE_OFDM },
+	{ .rate = 120,
+	  .val = ZD_OFDM_RATE_12M,
+	  .flags = IEEE80211_RATE_OFDM },
+	{ .rate = 180,
+	  .val = ZD_OFDM_RATE_18M,
+	  .flags = IEEE80211_RATE_OFDM },
+	{ .rate = 240,
+	  .val = ZD_OFDM_RATE_24M,
+	  .flags = IEEE80211_RATE_OFDM },
+	{ .rate = 360,
+	  .val = ZD_OFDM_RATE_36M,
+	  .flags = IEEE80211_RATE_OFDM },
+	{ .rate = 480,
+	  .val = ZD_OFDM_RATE_48M,
+	  .flags = IEEE80211_RATE_OFDM },
+	{ .rate = 540,
+	  .val = ZD_OFDM_RATE_54M,
+	  .flags = IEEE80211_RATE_OFDM },
+};
+
+static const struct ieee80211_channel zd_channels[] = {
+	{ .chan = 1,
+	  .freq = 2412},
+	{ .chan = 2,
+	  .freq = 2417},
+	{ .chan = 3,
+	  .freq = 2422},
+	{ .chan = 4,
+	  .freq = 2427},
+	{ .chan = 5,
+	  .freq = 2432},
+	{ .chan = 6,
+	  .freq = 2437},
+	{ .chan = 7,
+	  .freq = 2442},
+	{ .chan = 8,
+	  .freq = 2447},
+	{ .chan = 9,
+	  .freq = 2452},
+	{ .chan = 10,
+	  .freq = 2457},
+	{ .chan = 11,
+	  .freq = 2462},
+	{ .chan = 12,
+	  .freq = 2467},
+	{ .chan = 13,
+	  .freq = 2472},
+	{ .chan = 14,
+	  .freq = 2484}
+};
+
+static void housekeeping_init(struct zd_mac *mac);
+static void housekeeping_enable(struct zd_mac *mac);
+static void housekeeping_disable(struct zd_mac *mac);
+
+int zd_mac_preinit_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	u8 addr[ETH_ALEN];
+	struct zd_mac *mac = zd_hw_mac(hw);
+
+	r = zd_chip_read_mac_addr_fw(&mac->chip, addr);
+	if (r)
+		return r;
+
+	SET_IEEE80211_PERM_ADDR(hw, addr);
+
+	return 0;
+}
+
+int zd_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct zd_mac *mac = zd_hw_mac(hw);
+	struct zd_chip *chip = &mac->chip;
+	u8 default_regdomain;
+
+	r = zd_chip_enable_int(chip);
+	if (r)
+		goto out;
+	r = zd_chip_init_hw(chip);
+	if (r)
+		goto disable_int;
+
+	ZD_ASSERT(!irqs_disabled());
+
+	r = zd_read_regdomain(chip, &default_regdomain);
+	if (r)
+		goto disable_int;
+	spin_lock_irq(&mac->lock);
+	mac->regdomain = mac->default_regdomain = default_regdomain;
+	spin_unlock_irq(&mac->lock);
+
+	/* We must inform the device that we are doing encryption/decryption in
+	 * software at the moment. */
+	r = zd_set_encryption_type(chip, ENC_SNIFFER);
+	if (r)
+		goto disable_int;
+
+	zd_geo_init(hw, mac->regdomain);
+
+	r = 0;
+disable_int:
+	zd_chip_disable_int(chip);
+out:
+	return r;
+}
+
+void zd_mac_clear(struct zd_mac *mac)
+{
+	flush_workqueue(zd_workqueue);
+	zd_chip_clear(&mac->chip);
+	ZD_ASSERT(!spin_is_locked(&mac->lock));
+	ZD_MEMCLEAR(mac, sizeof(struct zd_mac));
+}
+
+static int set_rx_filter(struct zd_mac *mac)
+{
+	unsigned long flags;
+	u32 filter = STA_RX_FILTER;
+
+	spin_lock_irqsave(&mac->lock, flags);
+	if (mac->pass_ctrl)
+		filter |= RX_FILTER_CTRL;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
+}
+
+static int set_mc_hash(struct zd_mac *mac)
+{
+	struct zd_mc_hash hash;
+	zd_mc_clear(&hash);
+	return zd_chip_set_multicast_hash(&mac->chip, &hash);
+}
+
+static int zd_op_start(struct ieee80211_hw *hw)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	struct zd_chip *chip = &mac->chip;
+	struct zd_usb *usb = &chip->usb;
+	int r;
+
+	if (!usb->initialized) {
+		r = zd_usb_init_hw(usb);
+		if (r)
+			goto out;
+	}
+
+	r = zd_chip_enable_int(chip);
+	if (r < 0)
+		goto out;
+
+	r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G);
+	if (r < 0)
+		goto disable_int;
+	r = set_rx_filter(mac);
+	if (r)
+		goto disable_int;
+	r = set_mc_hash(mac);
+	if (r)
+		goto disable_int;
+	r = zd_chip_switch_radio_on(chip);
+	if (r < 0)
+		goto disable_int;
+	r = zd_chip_enable_rxtx(chip);
+	if (r < 0)
+		goto disable_radio;
+	r = zd_chip_enable_hwint(chip);
+	if (r < 0)
+		goto disable_rxtx;
+
+	housekeeping_enable(mac);
+	return 0;
+disable_rxtx:
+	zd_chip_disable_rxtx(chip);
+disable_radio:
+	zd_chip_switch_radio_off(chip);
+disable_int:
+	zd_chip_disable_int(chip);
+out:
+	return r;
+}
+
+/**
+ * clear_tx_skb_control_block - clears the control block of tx skbuffs
+ * @skb: a &struct sk_buff pointer
+ *
+ * This clears the control block of skbuff buffers, which were transmitted to
+ * the device. Notify that the function is not thread-safe, so prevent
+ * multiple calls.
+ */
+static void clear_tx_skb_control_block(struct sk_buff *skb)
+{
+	struct zd_tx_skb_control_block *cb =
+		(struct zd_tx_skb_control_block *)skb->cb;
+
+	kfree(cb->control);
+	cb->control = NULL;
+}
+
+/**
+ * kfree_tx_skb - frees a tx skbuff
+ * @skb: a &struct sk_buff pointer
+ *
+ * Frees the tx skbuff. Frees also the allocated control structure in the
+ * control block if necessary.
+ */
+static void kfree_tx_skb(struct sk_buff *skb)
+{
+	clear_tx_skb_control_block(skb);
+	dev_kfree_skb_any(skb);
+}
+
+static void zd_op_stop(struct ieee80211_hw *hw)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	struct zd_chip *chip = &mac->chip;
+	struct sk_buff *skb;
+	struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue;
+
+	/* The order here deliberately is a little different from the open()
+	 * method, since we need to make sure there is no opportunity for RX
+	 * frames to be processed by mac80211 after we have stopped it.
+	 */
+
+	zd_chip_disable_rxtx(chip);
+	housekeeping_disable(mac);
+	flush_workqueue(zd_workqueue);
+
+	zd_chip_disable_hwint(chip);
+	zd_chip_switch_radio_off(chip);
+	zd_chip_disable_int(chip);
+
+
+	while ((skb = skb_dequeue(ack_wait_queue)))
+		kfree_tx_skb(skb);
+}
+
+/**
+ * init_tx_skb_control_block - initializes skb control block
+ * @skb: a &sk_buff pointer
+ * @dev: pointer to the mac80221 device
+ * @control: mac80211 tx control applying for the frame in @skb
+ *
+ * Initializes the control block of the skbuff to be transmitted.
+ */
+static int init_tx_skb_control_block(struct sk_buff *skb,
+				     struct ieee80211_hw *hw,
+	                             struct ieee80211_tx_control *control)
+{
+	struct zd_tx_skb_control_block *cb =
+		(struct zd_tx_skb_control_block *)skb->cb;
+
+	ZD_ASSERT(sizeof(*cb) <= sizeof(skb->cb));
+	memset(cb, 0, sizeof(*cb));
+	cb->hw= hw;
+	cb->control = kmalloc(sizeof(*control), GFP_ATOMIC);
+	if (cb->control == NULL)
+		return -ENOMEM;
+	memcpy(cb->control, control, sizeof(*control));
+
+	return 0;
+}
+
+/**
+ * tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @status - the tx status of the packet without control information
+ * @success - True for successfull transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
+	              struct ieee80211_tx_status *status,
+		      bool success)
+{
+	struct zd_tx_skb_control_block *cb = (struct zd_tx_skb_control_block *)
+		skb->cb;
+
+	ZD_ASSERT(cb->control != NULL);
+	memcpy(&status->control, cb->control, sizeof(status->control));
+	if (!success)
+		status->excessive_retries = 1;
+	clear_tx_skb_control_block(skb);
+	ieee80211_tx_status_irqsafe(hw, skb, status);
+}
+
+/**
+ * zd_mac_tx_failed - callback for failed frames
+ * @dev: the mac80211 wireless device
+ *
+ * This function is called if a frame couldn't be succesfully be
+ * transferred. The first frame from the tx queue, will be selected and
+ * reported as error to the upper layers.
+ */
+void zd_mac_tx_failed(struct ieee80211_hw *hw)
+{
+	struct sk_buff_head *q = &zd_hw_mac(hw)->ack_wait_queue;
+	struct sk_buff *skb;
+	struct ieee80211_tx_status status = {{0}};
+
+	skb = skb_dequeue(q);
+	if (skb == NULL)
+		return;
+	tx_status(hw, skb, &status, 0);
+}
+
+/**
+ * zd_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void zd_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct zd_tx_skb_control_block *cb =
+		(struct zd_tx_skb_control_block *)skb->cb;
+	struct ieee80211_hw *hw = cb->hw;
+
+	if (likely(cb->control)) {
+		skb_pull(skb, sizeof(struct zd_ctrlset));
+		if (unlikely(error ||
+		    (cb->control->flags & IEEE80211_TXCTL_NO_ACK)))
+		{
+			struct ieee80211_tx_status status = {{0}};
+			tx_status(hw, skb, &status, !error);
+		} else {
+			struct sk_buff_head *q =
+				&zd_hw_mac(hw)->ack_wait_queue;
+
+			skb_queue_tail(q, skb);
+			while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS)
+				zd_mac_tx_failed(hw);
+		}
+	} else {
+		kfree_tx_skb(skb);
+	}
+}
+
+static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length)
+{
+	/* ZD_PURE_RATE() must be used to remove the modulation type flag of
+	 * the zd-rate values.
+	 */
+	static const u8 rate_divisor[] = {
+		[ZD_PURE_RATE(ZD_CCK_RATE_1M)]   =  1,
+		[ZD_PURE_RATE(ZD_CCK_RATE_2M)]	 =  2,
+		/* Bits must be doubled. */
+		[ZD_PURE_RATE(ZD_CCK_RATE_5_5M)] = 11,
+		[ZD_PURE_RATE(ZD_CCK_RATE_11M)]	 = 11,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_6M)]  =  6,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_9M)]  =  9,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_12M)] = 12,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_18M)] = 18,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_24M)] = 24,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_36M)] = 36,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_48M)] = 48,
+		[ZD_PURE_RATE(ZD_OFDM_RATE_54M)] = 54,
+	};
+
+	u32 bits = (u32)tx_length * 8;
+	u32 divisor;
+
+	divisor = rate_divisor[ZD_PURE_RATE(zd_rate)];
+	if (divisor == 0)
+		return -EINVAL;
+
+	switch (zd_rate) {
+	case ZD_CCK_RATE_5_5M:
+		bits = (2*bits) + 10; /* round up to the next integer */
+		break;
+	case ZD_CCK_RATE_11M:
+		if (service) {
+			u32 t = bits % 11;
+			*service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION;
+			if (0 < t && t <= 3) {
+				*service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION;
+			}
+		}
+		bits += 10; /* round up to the next integer */
+		break;
+	}
+
+	return bits/divisor;
+}
+
+static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
+	                   struct ieee80211_hdr *header, u32 flags)
+{
+	u16 fctl = le16_to_cpu(header->frame_control);
+
+	/*
+	 * CONTROL:
+	 * - start at 0x00
+	 * - if fragment 0, enable bit 0
+	 * - if backoff needed, enable bit 0
+	 * - if burst (backoff not needed) disable bit 0
+	 * - if multicast, enable bit 1
+	 * - if PS-POLL frame, enable bit 2
+	 * - if in INDEPENDENT_BSS mode and zd1205_DestPowerSave, then enable
+	 *   bit 4 (FIXME: wtf)
+	 * - if frag_len > RTS threshold, set bit 5 as long if it isnt
+	 *   multicast or mgt
+	 * - if bit 5 is set, and we are in OFDM mode, unset bit 5 and set bit
+	 *   7
+	 */
+
+	cs->control = 0;
+
+	/* First fragment */
+	if (flags & IEEE80211_TXCTL_FIRST_FRAGMENT)
+		cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
+
+	/* Multicast */
+	if (is_multicast_ether_addr(header->addr1))
+		cs->control |= ZD_CS_MULTICAST;
+
+	/* PS-POLL */
+	if ((fctl & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE)) ==
+	    (IEEE80211_FTYPE_CTL|IEEE80211_STYPE_PSPOLL))
+		cs->control |= ZD_CS_PS_POLL_FRAME;
+
+	if (flags & IEEE80211_TXCTL_USE_RTS_CTS)
+		cs->control |= ZD_CS_RTS;
+
+	if (flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
+		cs->control |= ZD_CS_SELF_CTS;
+
+	/* FIXME: Management frame? */
+}
+
+static int fill_ctrlset(struct zd_mac *mac,
+			struct sk_buff *skb,
+			struct ieee80211_tx_control *control)
+{
+	int r;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	unsigned int frag_len = skb->len + FCS_LEN;
+	unsigned int packet_length;
+	struct zd_ctrlset *cs = (struct zd_ctrlset *)
+		skb_push(skb, sizeof(struct zd_ctrlset));
+
+	ZD_ASSERT(frag_len <= 0xffff);
+
+	cs->modulation = control->tx_rate;
+
+	cs->tx_length = cpu_to_le16(frag_len);
+
+	cs_set_control(mac, cs, hdr, control->flags);
+
+	packet_length = frag_len + sizeof(struct zd_ctrlset) + 10;
+	ZD_ASSERT(packet_length <= 0xffff);
+	/* ZD1211B: Computing the length difference this way, gives us
+	 * flexibility to compute the packet length.
+	 */
+	cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ?
+			packet_length - frag_len : packet_length);
+
+	/*
+	 * CURRENT LENGTH:
+	 * - transmit frame length in microseconds
+	 * - seems to be derived from frame length
+	 * - see Cal_Us_Service() in zdinlinef.h
+	 * - if macp->bTxBurstEnable is enabled, then multiply by 4
+	 *  - bTxBurstEnable is never set in the vendor driver
+	 *
+	 * SERVICE:
+	 * - "for PLCP configuration"
+	 * - always 0 except in some situations at 802.11b 11M
+	 * - see line 53 of zdinlinef.h
+	 */
+	cs->service = 0;
+	r = zd_calc_tx_length_us(&cs->service, ZD_RATE(cs->modulation),
+		                 le16_to_cpu(cs->tx_length));
+	if (r < 0)
+		return r;
+	cs->current_length = cpu_to_le16(r);
+	cs->next_frame_length = 0;
+
+	return 0;
+}
+
+/**
+ * zd_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
+		     struct ieee80211_tx_control *control)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	int r;
+
+	r = fill_ctrlset(mac, skb, control);
+	if (r)
+		return r;
+
+	r = init_tx_skb_control_block(skb, hw, control);
+	if (r)
+		return r;
+	r = zd_usb_tx(&mac->chip.usb, skb);
+	if (r) {
+		clear_tx_skb_control_block(skb);
+		return r;
+	}
+	return 0;
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	u16 fc = le16_to_cpu(rx_hdr->frame_control);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+
+	if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) !=
+	    (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK))
+		return 0;
+
+	q = &zd_hw_mac(hw)->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+	for (skb = q->next; skb != (struct sk_buff *)q; skb = skb->next) {
+		struct ieee80211_hdr *tx_hdr;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(!compare_ether_addr(tx_hdr->addr2, rx_hdr->addr1)))
+		{
+			struct ieee80211_tx_status status = {{0}};
+			status.flags = IEEE80211_TX_STATUS_ACK;
+			status.ack_signal = stats->ssi;
+			__skb_unlink(skb, q);
+			tx_status(hw, skb, &status, 1);
+			goto out;
+		}
+	}
+out:
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+
+	if (length < ZD_PLCP_HEADER_SIZE + 10 /* IEEE80211_1ADDR_LEN */ +
+	             FCS_LEN + sizeof(struct rx_status))
+		return -EINVAL;
+
+	memset(&stats, 0, sizeof(stats));
+
+	/* Note about pass_failed_fcs and pass_ctrl access below:
+	 * mac locking intentionally omitted here, as this is the only unlocked
+	 * reader and the only writer is configure_filter. Plus, if there were
+	 * any races accessing these variables, it wouldn't really matter.
+	 * If mac80211 ever provides a way for us to access filter flags
+	 * from outside configure_filter, we could improve on this. Also, this
+	 * situation may change once we implement some kind of DMA-into-skb
+	 * RX path. */
+
+	/* Caller has to ensure that length >= sizeof(struct rx_status). */
+	status = (struct rx_status *)
+		(buffer + (length - sizeof(struct rx_status)));
+	if (status->frame_status & ZD_RX_ERROR) {
+		if (mac->pass_failed_fcs &&
+				(status->frame_status & ZD_RX_CRC32_ERROR)) {
+			stats.flag |= RX_FLAG_FAILED_FCS_CRC;
+			bad_frame = 1;
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	stats.channel = _zd_chip_get_channel(&mac->chip);
+	stats.freq = zd_channels[stats.channel - 1].freq;
+	stats.phymode = MODE_IEEE80211G;
+	stats.ssi = status->signal_strength;
+	stats.signal = zd_rx_qual_percent(buffer,
+		                          length - sizeof(struct rx_status),
+		                          status);
+	stats.rate = zd_rx_rate(buffer, status);
+
+	length -= ZD_PLCP_HEADER_SIZE + sizeof(struct rx_status);
+	buffer += ZD_PLCP_HEADER_SIZE;
+
+	/* Except for bad frames, filter each frame to see if it is an ACK, in
+	 * which case our internal TX tracking is updated. Normally we then
+	 * bail here as there's no need to pass ACKs on up to the stack, but
+	 * there is also the case where the stack has requested us to pass
+	 * control frames on up (pass_ctrl) which we must consider. */
+	if (!bad_frame &&
+			filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats)
+			&& !mac->pass_ctrl)
+		return 0;
+
+	skb = dev_alloc_skb(length);
+	if (skb == NULL)
+		return -ENOMEM;
+	memcpy(skb_put(skb, length), buffer, length);
+
+	ieee80211_rx_irqsafe(hw, skb, &stats);
+	return 0;
+}
+
+static int zd_op_add_interface(struct ieee80211_hw *hw,
+				struct ieee80211_if_init_conf *conf)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+
+	/* using IEEE80211_IF_TYPE_INVALID to indicate no mode selected */
+	if (mac->type != IEEE80211_IF_TYPE_INVALID)
+		return -EOPNOTSUPP;
+
+	switch (conf->type) {
+	case IEEE80211_IF_TYPE_MNTR:
+	case IEEE80211_IF_TYPE_STA:
+		mac->type = conf->type;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return zd_write_mac_addr(&mac->chip, conf->mac_addr);
+}
+
+static void zd_op_remove_interface(struct ieee80211_hw *hw,
+				    struct ieee80211_if_init_conf *conf)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	mac->type = IEEE80211_IF_TYPE_INVALID;
+	zd_write_mac_addr(&mac->chip, NULL);
+}
+
+static int zd_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	return zd_chip_set_channel(&mac->chip, conf->channel);
+}
+
+static int zd_op_config_interface(struct ieee80211_hw *hw, int if_id,
+				   struct ieee80211_if_conf *conf)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+
+	spin_lock_irq(&mac->lock);
+	mac->associated = is_valid_ether_addr(conf->bssid);
+	spin_unlock_irq(&mac->lock);
+
+	/* TODO: do hardware bssid filtering */
+	return 0;
+}
+
+static void set_multicast_hash_handler(struct work_struct *work)
+{
+	struct zd_mac *mac =
+		container_of(work, struct zd_mac, set_multicast_hash_work);
+	struct zd_mc_hash hash;
+
+	spin_lock_irq(&mac->lock);
+	hash = mac->multicast_hash;
+	spin_unlock_irq(&mac->lock);
+
+	zd_chip_set_multicast_hash(&mac->chip, &hash);
+}
+
+static void set_rx_filter_handler(struct work_struct *work)
+{
+	struct zd_mac *mac =
+		container_of(work, struct zd_mac, set_rx_filter_work);
+	int r;
+
+	dev_dbg_f(zd_mac_dev(mac), "\n");
+	r = set_rx_filter(mac);
+	if (r)
+		dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r);
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	FIF_OTHER_BSS)
+static void zd_op_configure_filter(struct ieee80211_hw *hw,
+			unsigned int changed_flags,
+			unsigned int *new_flags,
+			int mc_count, struct dev_mc_list *mclist)
+{
+	struct zd_mc_hash hash;
+	struct zd_mac *mac = zd_hw_mac(hw);
+	unsigned long flags;
+	int i;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* changed_flags is always populated but this driver
+	 * doesn't support all FIF flags so its possible we don't
+	 * need to do anything */
+	if (!changed_flags)
+		return;
+
+	if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) {
+		zd_mc_add_all(&hash);
+	} else {
+		DECLARE_MAC_BUF(macbuf);
+
+		zd_mc_clear(&hash);
+		for (i = 0; i < mc_count; i++) {
+			if (!mclist)
+				break;
+			dev_dbg_f(zd_mac_dev(mac), "mc addr %s\n",
+				  print_mac(macbuf, mclist->dmi_addr));
+			zd_mc_add_addr(&hash, mclist->dmi_addr);
+			mclist = mclist->next;
+		}
+	}
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+	queue_work(zd_workqueue, &mac->set_multicast_hash_work);
+
+	if (changed_flags & FIF_CONTROL)
+		queue_work(zd_workqueue, &mac->set_rx_filter_work);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time. */
+}
+
+static void set_rts_cts_work(struct work_struct *work)
+{
+	struct zd_mac *mac =
+		container_of(work, struct zd_mac, set_rts_cts_work);
+	unsigned long flags;
+	unsigned int short_preamble;
+
+	mutex_lock(&mac->chip.mutex);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->updating_rts_rate = 0;
+	short_preamble = mac->short_preamble;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble);
+	mutex_unlock(&mac->chip.mutex);
+}
+
+static void zd_op_erp_ie_changed(struct ieee80211_hw *hw, u8 changes,
+				 int cts_protection, int preamble)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	unsigned long flags;
+
+	dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes);
+
+	if (changes & IEEE80211_ERP_CHANGE_PREAMBLE) {
+		spin_lock_irqsave(&mac->lock, flags);
+		mac->short_preamble = !preamble;
+		if (!mac->updating_rts_rate) {
+			mac->updating_rts_rate = 1;
+			/* FIXME: should disable TX here, until work has
+			 * completed and RTS_CTS reg is updated */
+			queue_work(zd_workqueue, &mac->set_rts_cts_work);
+		}
+		spin_unlock_irqrestore(&mac->lock, flags);
+	}
+}
+
+static const struct ieee80211_ops zd_ops = {
+	.tx			= zd_op_tx,
+	.start			= zd_op_start,
+	.stop			= zd_op_stop,
+	.add_interface		= zd_op_add_interface,
+	.remove_interface	= zd_op_remove_interface,
+	.config			= zd_op_config,
+	.config_interface	= zd_op_config_interface,
+	.configure_filter	= zd_op_configure_filter,
+	.erp_ie_changed		= zd_op_erp_ie_changed,
+};
+
+struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct zd_mac *mac;
+	struct ieee80211_hw *hw;
+	int i;
+
+	hw = ieee80211_alloc_hw(sizeof(struct zd_mac), &zd_ops);
+	if (!hw) {
+		dev_dbg_f(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+
+	mac = zd_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = IEEE80211_IF_TYPE_INVALID;
+
+	memcpy(mac->channels, zd_channels, sizeof(zd_channels));
+	memcpy(mac->rates, zd_rates, sizeof(zd_rates));
+	mac->modes[0].mode = MODE_IEEE80211G;
+	mac->modes[0].num_rates = ARRAY_SIZE(zd_rates);
+	mac->modes[0].rates = mac->rates;
+	mac->modes[0].num_channels = ARRAY_SIZE(zd_channels);
+	mac->modes[0].channels = mac->channels;
+	mac->modes[1].mode = MODE_IEEE80211B;
+	mac->modes[1].num_rates = 4;
+	mac->modes[1].rates = mac->rates;
+	mac->modes[1].num_channels = ARRAY_SIZE(zd_channels);
+	mac->modes[1].channels = mac->channels;
+
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		     IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED;
+	hw->max_rssi = 100;
+	hw->max_signal = 100;
+
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct zd_ctrlset);
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+
+	for (i = 0; i < 2; i++) {
+		if (ieee80211_register_hwmode(hw, &mac->modes[i])) {
+			dev_dbg_f(&intf->dev, "cannot register hwmode\n");
+			ieee80211_free_hw(hw);
+			return NULL;
+		}
+	}
+
+	zd_chip_init(&mac->chip, hw, intf);
+	housekeeping_init(mac);
+	INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
+	INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
+	INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+#define LINK_LED_WORK_DELAY HZ
+
+static void link_led_handler(struct work_struct *work)
+{
+	struct zd_mac *mac =
+		container_of(work, struct zd_mac, housekeeping.link_led_work.work);
+	struct zd_chip *chip = &mac->chip;
+	int is_associated;
+	int r;
+
+	spin_lock_irq(&mac->lock);
+	is_associated = mac->associated;
+	spin_unlock_irq(&mac->lock);
+
+	r = zd_chip_control_leds(chip,
+		                 is_associated ? LED_ASSOCIATED : LED_SCANNING);
+	if (r)
+		dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r);
+
+	queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+		           LINK_LED_WORK_DELAY);
+}
+
+static void housekeeping_init(struct zd_mac *mac)
+{
+	INIT_DELAYED_WORK(&mac->housekeeping.link_led_work, link_led_handler);
+}
+
+static void housekeeping_enable(struct zd_mac *mac)
+{
+	dev_dbg_f(zd_mac_dev(mac), "\n");
+	queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+			   0);
+}
+
+static void housekeeping_disable(struct zd_mac *mac)
+{
+	dev_dbg_f(zd_mac_dev(mac), "\n");
+	cancel_rearming_delayed_workqueue(zd_workqueue,
+		&mac->housekeeping.link_led_work);
+	zd_chip_control_leds(&mac->chip, LED_OFF);
+}
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_mac.h
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_mac.h
@@ -0,0 +1,238 @@
+/* zd_mac.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_MAC_H
+#define _ZD_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "zd_chip.h"
+#include "zd_ieee80211.h"
+
+struct zd_ctrlset {
+	u8     modulation;
+	__le16 tx_length;
+	u8     control;
+	/* stores only the difference to tx_length on ZD1211B */
+	__le16 packet_length;
+	__le16 current_length;
+	u8     service;
+	__le16  next_frame_length;
+} __attribute__((packed));
+
+#define ZD_CS_RESERVED_SIZE	25
+
+/* The field modulation of struct zd_ctrlset controls the bit rate, the use
+ * of short or long preambles in 802.11b (CCK mode) or the use of 802.11a or
+ * 802.11g in OFDM mode.
+ *
+ * The term zd-rate is used for the combination of the modulation type flag
+ * and the "pure" rate value.
+ */
+#define ZD_PURE_RATE_MASK       0x0f
+#define ZD_MODULATION_TYPE_MASK 0x10
+#define ZD_RATE_MASK            (ZD_PURE_RATE_MASK|ZD_MODULATION_TYPE_MASK)
+#define ZD_PURE_RATE(modulation) ((modulation) & ZD_PURE_RATE_MASK)
+#define ZD_MODULATION_TYPE(modulation) ((modulation) & ZD_MODULATION_TYPE_MASK)
+#define ZD_RATE(modulation) ((modulation) & ZD_RATE_MASK)
+
+/* The two possible modulation types. Notify that 802.11b doesn't use the CCK
+ * codeing for the 1 and 2 MBit/s rate. We stay with the term here to remain
+ * consistent with uses the term at other places.
+ */
+#define ZD_CCK                  0x00
+#define ZD_OFDM                 0x10
+
+/* The ZD1211 firmware uses proprietary encodings of the 802.11b (CCK) rates.
+ * For OFDM the PLCP rate encodings are used. We combine these "pure" rates
+ * with the modulation type flag and call the resulting values zd-rates.
+ */
+#define ZD_CCK_RATE_1M          (ZD_CCK|0x00)
+#define ZD_CCK_RATE_2M          (ZD_CCK|0x01)
+#define ZD_CCK_RATE_5_5M        (ZD_CCK|0x02)
+#define ZD_CCK_RATE_11M         (ZD_CCK|0x03)
+#define ZD_OFDM_RATE_6M         (ZD_OFDM|ZD_OFDM_PLCP_RATE_6M)
+#define ZD_OFDM_RATE_9M         (ZD_OFDM|ZD_OFDM_PLCP_RATE_9M)
+#define ZD_OFDM_RATE_12M        (ZD_OFDM|ZD_OFDM_PLCP_RATE_12M)
+#define ZD_OFDM_RATE_18M        (ZD_OFDM|ZD_OFDM_PLCP_RATE_18M)
+#define ZD_OFDM_RATE_24M        (ZD_OFDM|ZD_OFDM_PLCP_RATE_24M)
+#define ZD_OFDM_RATE_36M        (ZD_OFDM|ZD_OFDM_PLCP_RATE_36M)
+#define ZD_OFDM_RATE_48M        (ZD_OFDM|ZD_OFDM_PLCP_RATE_48M)
+#define ZD_OFDM_RATE_54M        (ZD_OFDM|ZD_OFDM_PLCP_RATE_54M)
+
+/* The bit 5 of the zd_ctrlset modulation field controls the preamble in CCK
+ * mode or the 802.11a/802.11g selection in OFDM mode.
+ */
+#define ZD_CCK_PREA_LONG        0x00
+#define ZD_CCK_PREA_SHORT       0x20
+#define ZD_OFDM_MODE_11G        0x00
+#define ZD_OFDM_MODE_11A        0x20
+
+/* zd_ctrlset control field */
+#define ZD_CS_NEED_RANDOM_BACKOFF	0x01
+#define ZD_CS_MULTICAST			0x02
+
+#define ZD_CS_FRAME_TYPE_MASK		0x0c
+#define ZD_CS_DATA_FRAME		0x00
+#define ZD_CS_PS_POLL_FRAME		0x04
+#define ZD_CS_MANAGEMENT_FRAME		0x08
+#define ZD_CS_NO_SEQUENCE_CTL_FRAME	0x0c
+
+#define ZD_CS_WAKE_DESTINATION		0x10
+#define ZD_CS_RTS			0x20
+#define ZD_CS_ENCRYPT			0x40
+#define ZD_CS_SELF_CTS			0x80
+
+/* Incoming frames are prepended by a PLCP header */
+#define ZD_PLCP_HEADER_SIZE		5
+
+struct rx_length_info {
+	__le16 length[3];
+	__le16 tag;
+} __attribute__((packed));
+
+#define RX_LENGTH_INFO_TAG		0x697e
+
+struct rx_status {
+	u8 signal_quality_cck;
+	/* rssi */
+	u8 signal_strength;
+	u8 signal_quality_ofdm;
+	u8 decryption_type;
+	u8 frame_status;
+} __attribute__((packed));
+
+/* rx_status field decryption_type */
+#define ZD_RX_NO_WEP	0
+#define ZD_RX_WEP64	1
+#define ZD_RX_TKIP	2
+#define ZD_RX_AES	4
+#define ZD_RX_WEP128	5
+#define ZD_RX_WEP256	6
+
+/* rx_status field frame_status */
+#define ZD_RX_FRAME_MODULATION_MASK	0x01
+#define ZD_RX_CCK			0x00
+#define ZD_RX_OFDM			0x01
+
+#define ZD_RX_TIMEOUT_ERROR		0x02
+#define ZD_RX_FIFO_OVERRUN_ERROR	0x04
+#define ZD_RX_DECRYPTION_ERROR		0x08
+#define ZD_RX_CRC32_ERROR		0x10
+#define ZD_RX_NO_ADDR1_MATCH_ERROR	0x20
+#define ZD_RX_CRC16_ERROR		0x40
+#define ZD_RX_ERROR			0x80
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct housekeeping {
+	struct delayed_work link_led_work;
+};
+
+/**
+ * struct zd_tx_skb_control_block - control block for tx skbuffs
+ * @control: &struct ieee80211_tx_control pointer
+ * @context: context pointer
+ *
+ * This structure is used to fill the cb field in an &sk_buff to transmit.
+ * The control field is NULL, if there is no requirement from the mac80211
+ * stack to report about the packet ACK. This is the case if the flag
+ * IEEE80211_TXCTL_NO_ACK is not set in &struct ieee80211_tx_control.
+ */
+struct zd_tx_skb_control_block {
+	struct ieee80211_tx_control *control;
+	struct ieee80211_hw *hw;
+	void *context;
+};
+
+#define ZD_MAC_STATS_BUFFER_SIZE 16
+
+#define ZD_MAC_MAX_ACK_WAITERS 10
+
+struct zd_mac {
+	struct zd_chip chip;
+	spinlock_t lock;
+	struct ieee80211_hw *hw;
+	struct housekeeping housekeeping;
+	struct work_struct set_multicast_hash_work;
+	struct work_struct set_rts_cts_work;
+	struct work_struct set_rx_filter_work;
+	struct zd_mc_hash multicast_hash;
+	u8 regdomain;
+	u8 default_regdomain;
+	int type;
+	int associated;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_hw_mode modes[2];
+
+	/* Short preamble (used for RTS/CTS) */
+	unsigned int short_preamble:1;
+
+	/* flags to indicate update in progress */
+	unsigned int updating_rts_rate:1;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+};
+
+static inline struct zd_mac *zd_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip)
+{
+	return container_of(chip, struct zd_mac, chip);
+}
+
+static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb)
+{
+	return zd_chip_to_mac(zd_usb_to_chip(usb));
+}
+
+static inline u8 *zd_mac_get_perm_addr(struct zd_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip))
+
+struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf);
+void zd_mac_clear(struct zd_mac *mac);
+
+int zd_mac_preinit_hw(struct ieee80211_hw *hw);
+int zd_mac_init_hw(struct ieee80211_hw *hw);
+
+int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length);
+void zd_mac_tx_failed(struct ieee80211_hw *hw);
+void zd_mac_tx_to_dev(struct sk_buff *skb, int error);
+
+#ifdef DEBUG
+void zd_dump_rx_status(const struct rx_status *status);
+#else
+#define zd_dump_rx_status(status)
+#endif /* DEBUG */
+
+#endif /* _ZD_MAC_H */
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf.c
@@ -0,0 +1,178 @@
+/* zd_rf.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include "zd_def.h"
+#include "zd_rf.h"
+#include "zd_ieee80211.h"
+#include "zd_chip.h"
+
+static const char * const rfs[] = {
+	[0]		= "unknown RF0",
+	[1]		= "unknown RF1",
+	[UW2451_RF]	= "UW2451_RF",
+	[UCHIP_RF]	= "UCHIP_RF",
+	[AL2230_RF]	= "AL2230_RF",
+	[AL7230B_RF]	= "AL7230B_RF",
+	[THETA_RF]	= "THETA_RF",
+	[AL2210_RF]	= "AL2210_RF",
+	[MAXIM_NEW_RF]	= "MAXIM_NEW_RF",
+	[UW2453_RF]	= "UW2453_RF",
+	[AL2230S_RF]	= "AL2230S_RF",
+	[RALINK_RF]	= "RALINK_RF",
+	[INTERSIL_RF]	= "INTERSIL_RF",
+	[RF2959_RF]	= "RF2959_RF",
+	[MAXIM_NEW2_RF]	= "MAXIM_NEW2_RF",
+	[PHILIPS_RF]	= "PHILIPS_RF",
+};
+
+const char *zd_rf_name(u8 type)
+{
+	if (type & 0xf0)
+		type = 0;
+	return rfs[type];
+}
+
+void zd_rf_init(struct zd_rf *rf)
+{
+	memset(rf, 0, sizeof(*rf));
+
+	/* default to update channel integration, as almost all RF's do want
+	 * this */
+	rf->update_channel_int = 1;
+}
+
+void zd_rf_clear(struct zd_rf *rf)
+{
+	if (rf->clear)
+		rf->clear(rf);
+	ZD_MEMCLEAR(rf, sizeof(*rf));
+}
+
+int zd_rf_init_hw(struct zd_rf *rf, u8 type)
+{
+	int r = 0;
+	int t;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	switch (type) {
+	case RF2959_RF:
+		r = zd_rf_init_rf2959(rf);
+		break;
+	case AL2230_RF:
+	case AL2230S_RF:
+		r = zd_rf_init_al2230(rf);
+		break;
+	case AL7230B_RF:
+		r = zd_rf_init_al7230b(rf);
+		break;
+	case UW2453_RF:
+		r = zd_rf_init_uw2453(rf);
+		break;
+	default:
+		dev_err(zd_chip_dev(chip),
+			"RF %s %#x is not supported\n", zd_rf_name(type), type);
+		rf->type = 0;
+		return -ENODEV;
+	}
+
+	if (r)
+		return r;
+
+	rf->type = type;
+
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		return r;
+	t = rf->init_hw(rf);
+	r = zd_chip_unlock_phy_regs(chip);
+	if (t)
+		r = t;
+	return r;
+}
+
+int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size)
+{
+	return scnprintf(buffer, size, "%s", zd_rf_name(rf->type));
+}
+
+int zd_rf_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+
+	ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex));
+	if (channel < MIN_CHANNEL24)
+		return -EINVAL;
+	if (channel > MAX_CHANNEL24)
+		return -EINVAL;
+	dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel);
+
+	r = rf->set_channel(rf, channel);
+	if (r >= 0)
+		rf->channel = channel;
+	return r;
+}
+
+int zd_switch_radio_on(struct zd_rf *rf)
+{
+	int r, t;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		return r;
+	t = rf->switch_radio_on(rf);
+	r = zd_chip_unlock_phy_regs(chip);
+	if (t)
+		r = t;
+	return r;
+}
+
+int zd_switch_radio_off(struct zd_rf *rf)
+{
+	int r, t;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	/* TODO: move phy regs handling to zd_chip */
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		return r;
+	t = rf->switch_radio_off(rf);
+	r = zd_chip_unlock_phy_regs(chip);
+	if (t)
+		r = t;
+	return r;
+}
+
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel)
+{
+	if (!rf->patch_6m_band_edge)
+		return 0;
+
+	return rf->patch_6m_band_edge(rf, channel);
+}
+
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel)
+{
+	return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel);
+}
+
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf.h
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf.h
@@ -0,0 +1,108 @@
+/* zd_rf.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_RF_H
+#define _ZD_RF_H
+
+#define UW2451_RF			0x2
+#define UCHIP_RF			0x3
+#define AL2230_RF			0x4
+#define AL7230B_RF			0x5	/* a,b,g */
+#define THETA_RF			0x6
+#define AL2210_RF			0x7
+#define MAXIM_NEW_RF			0x8
+#define UW2453_RF			0x9
+#define AL2230S_RF			0xa
+#define RALINK_RF			0xb
+#define INTERSIL_RF			0xc
+#define RF2959_RF			0xd
+#define MAXIM_NEW2_RF			0xe
+#define PHILIPS_RF			0xf
+
+#define RF_CHANNEL(ch) [(ch)-1]
+
+/* Provides functions of the RF transceiver. */
+
+enum {
+	RF_REG_BITS = 6,
+	RF_VALUE_BITS = 18,
+	RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS,
+};
+
+struct zd_rf {
+	u8 type;
+
+	u8 channel;
+
+	/* whether channel integration and calibration should be updated
+	 * defaults to 1 (yes) */
+	u8 update_channel_int:1;
+
+	/* whether CR47 should be patched from the EEPROM, if the appropriate
+	 * flag is set in the POD. The vendor driver suggests that this should
+	 * be done for all RF's, but a bug in their code prevents but their
+	 * HW_OverWritePhyRegFromE2P() routine from ever taking effect. */
+	u8 patch_cck_gain:1;
+
+	/* private RF driver data */
+	void *priv;
+
+	/* RF-specific functions */
+	int (*init_hw)(struct zd_rf *rf);
+	int (*set_channel)(struct zd_rf *rf, u8 channel);
+	int (*switch_radio_on)(struct zd_rf *rf);
+	int (*switch_radio_off)(struct zd_rf *rf);
+	int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel);
+	void (*clear)(struct zd_rf *rf);
+};
+
+const char *zd_rf_name(u8 type);
+void zd_rf_init(struct zd_rf *rf);
+void zd_rf_clear(struct zd_rf *rf);
+int zd_rf_init_hw(struct zd_rf *rf, u8 type);
+
+int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size);
+
+int zd_rf_set_channel(struct zd_rf *rf, u8 channel);
+
+int zd_switch_radio_on(struct zd_rf *rf);
+int zd_switch_radio_off(struct zd_rf *rf);
+
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel);
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel);
+
+static inline int zd_rf_should_update_pwr_int(struct zd_rf *rf)
+{
+	return rf->update_channel_int;
+}
+
+static inline int zd_rf_should_patch_cck_gain(struct zd_rf *rf)
+{
+	return rf->patch_cck_gain;
+}
+
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel);
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel);
+
+/* Functions for individual RF chips */
+
+int zd_rf_init_rf2959(struct zd_rf *rf);
+int zd_rf_init_al2230(struct zd_rf *rf);
+int zd_rf_init_al7230b(struct zd_rf *rf);
+int zd_rf_init_uw2453(struct zd_rf *rf);
+
+#endif /* _ZD_RF_H */
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_al2230.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_al2230.c
@@ -0,0 +1,439 @@
+/* zd_rf_al2230.c: Functions for the AL2230 RF controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+#define IS_AL2230S(chip) ((chip)->al2230s_bit || (chip)->rf.type == AL2230S_RF)
+
+static const u32 zd1211_al2230_table[][3] = {
+	RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, },
+	RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, },
+	RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, },
+	RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, },
+	RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, },
+	RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, },
+	RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, },
+	RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, },
+};
+
+static const u32 zd1211b_al2230_table[][3] = {
+	RF_CHANNEL( 1) = { 0x09efc0, 0x8cccc0, 0xb00000, },
+	RF_CHANNEL( 2) = { 0x09efc0, 0x8cccd0, 0xb00000, },
+	RF_CHANNEL( 3) = { 0x09e7c0, 0x8cccc0, 0xb00000, },
+	RF_CHANNEL( 4) = { 0x09e7c0, 0x8cccd0, 0xb00000, },
+	RF_CHANNEL( 5) = { 0x05efc0, 0x8cccc0, 0xb00000, },
+	RF_CHANNEL( 6) = { 0x05efc0, 0x8cccd0, 0xb00000, },
+	RF_CHANNEL( 7) = { 0x05e7c0, 0x8cccc0, 0xb00000, },
+	RF_CHANNEL( 8) = { 0x05e7c0, 0x8cccd0, 0xb00000, },
+	RF_CHANNEL( 9) = { 0x0defc0, 0x8cccc0, 0xb00000, },
+	RF_CHANNEL(10) = { 0x0defc0, 0x8cccd0, 0xb00000, },
+	RF_CHANNEL(11) = { 0x0de7c0, 0x8cccc0, 0xb00000, },
+	RF_CHANNEL(12) = { 0x0de7c0, 0x8cccd0, 0xb00000, },
+	RF_CHANNEL(13) = { 0x03efc0, 0x8cccc0, 0xb00000, },
+	RF_CHANNEL(14) = { 0x03e7c0, 0x866660, 0xb00000, },
+};
+
+static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = {
+	{ CR240, 0x57 }, { CR9,   0xe0 },
+};
+
+static const struct zd_ioreq16 ioreqs_init_al2230s[] = {
+	{ CR47,   0x1e }, /* MARK_002 */
+	{ CR106,  0x22 },
+	{ CR107,  0x2a }, /* MARK_002 */
+	{ CR109,  0x13 }, /* MARK_002 */
+	{ CR118,  0xf8 }, /* MARK_002 */
+	{ CR119,  0x12 }, { CR122,  0xe0 },
+	{ CR128,  0x10 }, /* MARK_001 from 0xe->0x10 */
+	{ CR129,  0x0e }, /* MARK_001 from 0xd->0x0e */
+	{ CR130,  0x10 }, /* MARK_001 from 0xb->0x0d */
+};
+
+static int zd1211b_al2230_finalize_rf(struct zd_chip *chip)
+{
+	int r;
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR80,  0x30 }, { CR81,  0x30 }, { CR79,  0x58 },
+		{ CR12,  0xf0 }, { CR77,  0x1b }, { CR78,  0x58 },
+		{ CR203, 0x06 },
+		{ },
+
+		{ CR240, 0x80 },
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	/* related to antenna selection? */
+	if (chip->new_phy_layout) {
+		r = zd_iowrite16_locked(chip, 0xe1, CR9);
+		if (r)
+			return r;
+	}
+
+	return zd_iowrite16_locked(chip, 0x06, CR203);
+}
+
+static int zd1211_al2230_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs_init[] = {
+		{ CR15,   0x20 }, { CR23,   0x40 }, { CR24,  0x20 },
+		{ CR26,   0x11 }, { CR28,   0x3e }, { CR29,  0x00 },
+		{ CR44,   0x33 }, { CR106,  0x2a }, { CR107, 0x1a },
+		{ CR109,  0x09 }, { CR110,  0x27 }, { CR111, 0x2b },
+		{ CR112,  0x2b }, { CR119,  0x0a }, { CR10,  0x89 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR17,   0x28 },
+		{ CR26,   0x93 }, { CR34,   0x30 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR35,   0x3e },
+		{ CR41,   0x24 }, { CR44,   0x32 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR46,   0x96 },
+		{ CR47,   0x1e }, { CR79,   0x58 }, { CR80,  0x30 },
+		{ CR81,   0x30 }, { CR87,   0x0a }, { CR89,  0x04 },
+		{ CR92,   0x0a }, { CR99,   0x28 }, { CR100, 0x00 },
+		{ CR101,  0x13 }, { CR102,  0x27 }, { CR106, 0x24 },
+		{ CR107,  0x2a }, { CR109,  0x09 }, { CR110, 0x13 },
+		{ CR111,  0x1f }, { CR112,  0x1f }, { CR113, 0x27 },
+		{ CR114,  0x27 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR115,  0x24 },
+		{ CR116,  0x24 }, { CR117,  0xf4 }, { CR118, 0xfc },
+		{ CR119,  0x10 }, { CR120,  0x4f }, { CR121, 0x77 },
+		{ CR122,  0xe0 }, { CR137,  0x88 }, { CR252, 0xff },
+		{ CR253,  0xff },
+	};
+
+	static const struct zd_ioreq16 ioreqs_pll[] = {
+		/* shdnb(PLL_ON)=0 */
+		{ CR251,  0x2f },
+		/* shdnb(PLL_ON)=1 */
+		{ CR251,  0x3f },
+		{ CR138,  0x28 }, { CR203,  0x06 },
+	};
+
+	static const u32 rv1[] = {
+		/* Channel 1 */
+		0x03f790,
+		0x033331,
+		0x00000d,
+
+		0x0b3331,
+		0x03b812,
+		0x00fff3,
+	};
+
+	static const u32 rv2[] = {
+		0x000da4,
+		0x0f4dc5, /* fix freq shift, 0x04edc5 */
+		0x0805b6,
+		0x011687,
+		0x000688,
+		0x0403b9, /* external control TX power (CR31) */
+		0x00dbba,
+		0x00099b,
+		0x0bdffc,
+		0x00000d,
+		0x00500f,
+	};
+
+	static const u32 rv3[] = {
+		0x00d00f,
+		0x004c0f,
+		0x00540f,
+		0x00700f,
+		0x00500f,
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init));
+	if (r)
+		return r;
+
+	if (IS_AL2230S(chip)) {
+		r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s,
+			ARRAY_SIZE(ioreqs_init_al2230s));
+		if (r)
+			return r;
+	}
+
+	r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS);
+	if (r)
+		return r;
+
+	/* improve band edge for AL2230S */
+	if (IS_AL2230S(chip))
+		r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS);
+	else
+		r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS);
+	if (r)
+		return r;
+
+	r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS);
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS);
+	if (r)
+		return r;
+
+	return 0;
+}
+
+static int zd1211b_al2230_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs1[] = {
+		{ CR10,  0x89 }, { CR15,  0x20 },
+		{ CR17,  0x2B }, /* for newest(3rd cut) AL2230 */
+		{ CR23,  0x40 }, { CR24,  0x20 }, { CR26,  0x93 },
+		{ CR28,  0x3e }, { CR29,  0x00 },
+		{ CR33,  0x28 }, /* 5621 */
+		{ CR34,  0x30 },
+		{ CR35,  0x3e }, /* for newest(3rd cut) AL2230 */
+		{ CR41,  0x24 }, { CR44,  0x32 },
+		{ CR46,  0x99 }, /* for newest(3rd cut) AL2230 */
+		{ CR47,  0x1e },
+
+		/* ZD1211B 05.06.10 */
+		{ CR48,  0x06 }, { CR49,  0xf9 }, { CR51,  0x01 },
+		{ CR52,  0x80 }, { CR53,  0x7e }, { CR65,  0x00 },
+		{ CR66,  0x00 }, { CR67,  0x00 }, { CR68,  0x00 },
+		{ CR69,  0x28 },
+
+		{ CR79,  0x58 }, { CR80,  0x30 }, { CR81,  0x30 },
+		{ CR87,  0x0a }, { CR89,  0x04 },
+		{ CR91,  0x00 }, /* 5621 */
+		{ CR92,  0x0a },
+		{ CR98,  0x8d }, /* 4804,  for 1212 new algorithm */
+		{ CR99,  0x00 }, /* 5621 */
+		{ CR101, 0x13 }, { CR102, 0x27 },
+		{ CR106, 0x24 }, /* for newest(3rd cut) AL2230 */
+		{ CR107, 0x2a },
+		{ CR109, 0x13 }, /* 4804, for 1212 new algorithm */
+		{ CR110, 0x1f }, /* 4804, for 1212 new algorithm */
+		{ CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 },
+		{ CR114, 0x27 },
+		{ CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) AL2230 */
+		{ CR116, 0x24 },
+		{ CR117, 0xfa }, /* for 1211b */
+		{ CR118, 0xfa }, /* for 1211b */
+		{ CR119, 0x10 },
+		{ CR120, 0x4f },
+		{ CR121, 0x6c }, /* for 1211b */
+		{ CR122, 0xfc }, /* E0->FC at 4902 */
+		{ CR123, 0x57 }, /* 5623 */
+		{ CR125, 0xad }, /* 4804, for 1212 new algorithm */
+		{ CR126, 0x6c }, /* 5614 */
+		{ CR127, 0x03 }, /* 4804, for 1212 new algorithm */
+		{ CR137, 0x50 }, /* 5614 */
+		{ CR138, 0xa8 },
+		{ CR144, 0xac }, /* 5621 */
+		{ CR150, 0x0d }, { CR252, 0x34 }, { CR253, 0x34 },
+	};
+
+	static const u32 rv1[] = {
+		0x8cccd0,
+		0x481dc0,
+		0xcfff00,
+		0x25a000,
+	};
+
+	static const u32 rv2[] = {
+		/* To improve AL2230 yield, improve phase noise, 4713 */
+		0x25a000,
+		0xa3b2f0,
+
+		0x6da010, /* Reg6 update for MP versio */
+		0xe36280, /* Modified by jxiao for Bor-Chin on 2004/08/02 */
+		0x116000,
+		0x9dc020, /* External control TX power (CR31) */
+		0x5ddb00, /* RegA update for MP version */
+		0xd99000, /* RegB update for MP version */
+		0x3ffbd0, /* RegC update for MP version */
+		0xb00000, /* RegD update for MP version */
+
+		/* improve phase noise and remove phase calibration,4713 */
+		0xf01a00,
+	};
+
+	static const struct zd_ioreq16 ioreqs2[] = {
+		{ CR251, 0x2f }, /* shdnb(PLL_ON)=0 */
+		{ CR251, 0x7f }, /* shdnb(PLL_ON)=1 */
+	};
+
+	static const u32 rv3[] = {
+		/* To improve AL2230 yield, 4713 */
+		0xf01b00,
+		0xf01e00,
+		0xf01a00,
+	};
+
+	static const struct zd_ioreq16 ioreqs3[] = {
+		/* related to 6M band edge patching, happens unconditionally */
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+	};
+
+	r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1,
+		ARRAY_SIZE(zd1211b_ioreqs_shared_1));
+	if (r)
+		return r;
+	r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1));
+	if (r)
+		return r;
+
+	if (IS_AL2230S(chip)) {
+		r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s,
+			ARRAY_SIZE(ioreqs_init_al2230s));
+		if (r)
+			return r;
+	}
+
+	r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3);
+	if (r)
+		return r;
+	r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1));
+	if (r)
+		return r;
+
+	if (IS_AL2230S(chip))
+		r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS);
+	else
+		r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS);
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2));
+	if (r)
+		return r;
+	r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2));
+	if (r)
+		return r;
+	r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3));
+	if (r)
+		return r;
+	r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3));
+	if (r)
+		return r;
+	return zd1211b_al2230_finalize_rf(chip);
+}
+
+static int zd1211_al2230_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	const u32 *rv = zd1211_al2230_table[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR138, 0x28 },
+		{ CR203, 0x06 },
+	};
+
+	r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS);
+	if (r)
+		return r;
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_al2230_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	const u32 *rv = zd1211b_al2230_table[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1,
+		ARRAY_SIZE(zd1211b_ioreqs_shared_1));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv, 3);
+	if (r)
+		return r;
+
+	return zd1211b_al2230_finalize_rf(chip);
+}
+
+static int zd1211_al2230_switch_radio_on(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 },
+		{ CR251, 0x3f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 },
+		{ CR251, 0x7f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int al2230_switch_radio_off(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x04 },
+		{ CR251, 0x2f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rf_init_al2230(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	rf->switch_radio_off = al2230_switch_radio_off;
+	if (zd_chip_is_zd1211b(chip)) {
+		rf->init_hw = zd1211b_al2230_init_hw;
+		rf->set_channel = zd1211b_al2230_set_channel;
+		rf->switch_radio_on = zd1211b_al2230_switch_radio_on;
+	} else {
+		rf->init_hw = zd1211_al2230_init_hw;
+		rf->set_channel = zd1211_al2230_set_channel;
+		rf->switch_radio_on = zd1211_al2230_switch_radio_on;
+	}
+	rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
+	rf->patch_cck_gain = 1;
+	return 0;
+}
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_al7230b.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_al7230b.c
@@ -0,0 +1,492 @@
+/* zd_rf_al7230b.c: Functions for the AL7230B RF controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+static const u32 chan_rv[][2] = {
+	RF_CHANNEL( 1) = { 0x09ec00, 0x8cccc8 },
+	RF_CHANNEL( 2) = { 0x09ec00, 0x8cccd8 },
+	RF_CHANNEL( 3) = { 0x09ec00, 0x8cccc0 },
+	RF_CHANNEL( 4) = { 0x09ec00, 0x8cccd0 },
+	RF_CHANNEL( 5) = { 0x05ec00, 0x8cccc8 },
+	RF_CHANNEL( 6) = { 0x05ec00, 0x8cccd8 },
+	RF_CHANNEL( 7) = { 0x05ec00, 0x8cccc0 },
+	RF_CHANNEL( 8) = { 0x05ec00, 0x8cccd0 },
+	RF_CHANNEL( 9) = { 0x0dec00, 0x8cccc8 },
+	RF_CHANNEL(10) = { 0x0dec00, 0x8cccd8 },
+	RF_CHANNEL(11) = { 0x0dec00, 0x8cccc0 },
+	RF_CHANNEL(12) = { 0x0dec00, 0x8cccd0 },
+	RF_CHANNEL(13) = { 0x03ec00, 0x8cccc8 },
+	RF_CHANNEL(14) = { 0x03ec00, 0x866660 },
+};
+
+static const u32 std_rv[] = {
+	0x4ff821,
+	0xc5fbfc,
+	0x21ebfe,
+	0xafd401, /* freq shift 0xaad401 */
+	0x6cf56a,
+	0xe04073,
+	0x193d76,
+	0x9dd844,
+	0x500007,
+	0xd8c010,
+};
+
+static const u32 rv_init1[] = {
+	0x3c9000,
+	0xbfffff,
+	0x700000,
+	0xf15d58,
+};
+
+static const u32 rv_init2[] = {
+	0xf15d59,
+	0xf15d5c,
+	0xf15d58,
+};
+
+static const struct zd_ioreq16 ioreqs_sw[] = {
+	{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+	{ CR38,  0x38 }, { CR136, 0xdf },
+};
+
+static int zd1211b_al7230b_finalize(struct zd_chip *chip)
+{
+	int r;
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR80,  0x30 }, { CR81,  0x30 }, { CR79,  0x58 },
+		{ CR12,  0xf0 }, { CR77,  0x1b }, { CR78,  0x58 },
+		{ CR203, 0x04 },
+		{ },
+		{ CR240, 0x80 },
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	if (chip->new_phy_layout) {
+		/* antenna selection? */
+		r = zd_iowrite16_locked(chip, 0xe5, CR9);
+		if (r)
+			return r;
+	}
+
+	return zd_iowrite16_locked(chip, 0x04, CR203);
+}
+
+static int zd1211_al7230b_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	/* All of these writes are identical to AL2230 unless otherwise
+	 * specified */
+	static const struct zd_ioreq16 ioreqs_1[] = {
+		/* This one is 7230-specific, and happens before the rest */
+		{ CR240,  0x57 },
+		{ },
+
+		{ CR15,   0x20 }, { CR23,   0x40 }, { CR24,  0x20 },
+		{ CR26,   0x11 }, { CR28,   0x3e }, { CR29,  0x00 },
+		{ CR44,   0x33 },
+		/* This value is different for 7230 (was: 0x2a) */
+		{ CR106,  0x22 },
+		{ CR107,  0x1a }, { CR109,  0x09 }, { CR110,  0x27 },
+		{ CR111,  0x2b }, { CR112,  0x2b }, { CR119,  0x0a },
+		/* This happened further down in AL2230,
+		 * and the value changed (was: 0xe0) */
+		{ CR122,  0xfc },
+		{ CR10,   0x89 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR17,   0x28 },
+		{ CR26,   0x93 }, { CR34,   0x30 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR35,   0x3e },
+		{ CR41,   0x24 }, { CR44,   0x32 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR46,   0x96 },
+		{ CR47,   0x1e }, { CR79,   0x58 }, { CR80,  0x30 },
+		{ CR81,   0x30 }, { CR87,   0x0a }, { CR89,  0x04 },
+		{ CR92,   0x0a }, { CR99,   0x28 },
+		/* This value is different for 7230 (was: 0x00) */
+		{ CR100,  0x02 },
+		{ CR101,  0x13 }, { CR102,  0x27 },
+		/* This value is different for 7230 (was: 0x24) */
+		{ CR106,  0x22 },
+		/* This value is different for 7230 (was: 0x2a) */
+		{ CR107,  0x3f },
+		{ CR109,  0x09 },
+		/* This value is different for 7230 (was: 0x13) */
+		{ CR110,  0x1f },
+		{ CR111,  0x1f }, { CR112,  0x1f }, { CR113, 0x27 },
+		{ CR114,  0x27 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR115,  0x24 },
+		/* This value is different for 7230 (was: 0x24) */
+		{ CR116,  0x3f },
+		/* This value is different for 7230 (was: 0xf4) */
+		{ CR117,  0xfa },
+		{ CR118,  0xfc }, { CR119,  0x10 }, { CR120, 0x4f },
+		{ CR121,  0x77 }, { CR137,  0x88 },
+		/* This one is 7230-specific */
+		{ CR138,  0xa8 },
+		/* This value is different for 7230 (was: 0xff) */
+		{ CR252,  0x34 },
+		/* This value is different for 7230 (was: 0xff) */
+		{ CR253,  0x34 },
+
+		/* PLL_OFF */
+		{ CR251, 0x2f },
+	};
+
+	static const struct zd_ioreq16 ioreqs_2[] = {
+		{ CR251, 0x3f }, /* PLL_ON */
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+		{ CR38,  0x38 }, { CR136, 0xdf },
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0]));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1));
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2));
+	if (r)
+		return r;
+
+	r = zd_iowrite16_locked(chip, 0x06, CR203);
+	if (r)
+		return r;
+	r = zd_iowrite16_locked(chip, 0x80, CR240);
+	if (r)
+		return r;
+
+	return 0;
+}
+
+static int zd1211b_al7230b_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs_1[] = {
+		{ CR240, 0x57 }, { CR9,   0x9 },
+		{ },
+		{ CR10,  0x8b }, { CR15,  0x20 },
+		{ CR17,  0x2B }, /* for newest (3rd cut) AL2230 */
+		{ CR20,  0x10 }, /* 4N25->Stone Request */
+		{ CR23,  0x40 }, { CR24,  0x20 }, { CR26,  0x93 },
+		{ CR28,  0x3e }, { CR29,  0x00 },
+		{ CR33,  0x28 }, /* 5613 */
+		{ CR34,  0x30 },
+		{ CR35,  0x3e }, /* for newest (3rd cut) AL2230 */
+		{ CR41,  0x24 }, { CR44,  0x32 },
+		{ CR46,  0x99 }, /* for newest (3rd cut) AL2230 */
+		{ CR47,  0x1e },
+
+		/* ZD1215 5610 */
+		{ CR48,  0x00 }, { CR49,  0x00 }, { CR51,  0x01 },
+		{ CR52,  0x80 }, { CR53,  0x7e }, { CR65,  0x00 },
+		{ CR66,  0x00 }, { CR67,  0x00 }, { CR68,  0x00 },
+		{ CR69,  0x28 },
+
+		{ CR79,  0x58 }, { CR80,  0x30 }, { CR81,  0x30 },
+		{ CR87,  0x0A }, { CR89,  0x04 },
+		{ CR90,  0x58 }, /* 5112 */
+		{ CR91,  0x00 }, /* 5613 */
+		{ CR92,  0x0a },
+		{ CR98,  0x8d }, /* 4804, for 1212 new algorithm */
+		{ CR99,  0x00 }, { CR100, 0x02 }, { CR101, 0x13 },
+		{ CR102, 0x27 },
+		{ CR106, 0x20 }, /* change to 0x24 for AL7230B */
+		{ CR109, 0x13 }, /* 4804, for 1212 new algorithm */
+		{ CR112, 0x1f },
+	};
+
+	static const struct zd_ioreq16 ioreqs_new_phy[] = {
+		{ CR107, 0x28 },
+		{ CR110, 0x1f }, /* 5127, 0x13->0x1f */
+		{ CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */
+		{ CR116, 0x2a }, { CR118, 0xfa }, { CR119, 0x12 },
+		{ CR121, 0x6c }, /* 5613 */
+	};
+
+	static const struct zd_ioreq16 ioreqs_old_phy[] = {
+		{ CR107, 0x24 },
+		{ CR110, 0x13 }, /* 5127, 0x13->0x1f */
+		{ CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */
+		{ CR116, 0x24 }, { CR118, 0xfc }, { CR119, 0x11 },
+		{ CR121, 0x6a }, /* 5613 */
+	};
+
+	static const struct zd_ioreq16 ioreqs_2[] = {
+		{ CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x24 },
+		{ CR117, 0xfa }, { CR120, 0x4f },
+		{ CR122, 0xfc }, /* E0->FCh at 4901 */
+		{ CR123, 0x57 }, /* 5613 */
+		{ CR125, 0xad }, /* 4804, for 1212 new algorithm */
+		{ CR126, 0x6c }, /* 5613 */
+		{ CR127, 0x03 }, /* 4804, for 1212 new algorithm */
+		{ CR130, 0x10 },
+		{ CR131, 0x00 }, /* 5112 */
+		{ CR137, 0x50 }, /* 5613 */
+		{ CR138, 0xa8 }, /* 5112 */
+		{ CR144, 0xac }, /* 5613 */
+		{ CR148, 0x40 }, /* 5112 */
+		{ CR149, 0x40 }, /* 4O07, 50->40 */
+		{ CR150, 0x1a }, /* 5112, 0C->1A */
+		{ CR252, 0x34 }, { CR253, 0x34 },
+		{ CR251, 0x2f }, /* PLL_OFF */
+	};
+
+	static const struct zd_ioreq16 ioreqs_3[] = {
+		{ CR251, 0x7f }, /* PLL_ON */
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+		{ CR38,  0x38 }, { CR136, 0xdf },
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1));
+	if (r)
+		return r;
+
+	if (chip->new_phy_layout)
+		r = zd_iowrite16a_locked(chip, ioreqs_new_phy,
+			ARRAY_SIZE(ioreqs_new_phy));
+	else
+		r = zd_iowrite16a_locked(chip, ioreqs_old_phy,
+			ARRAY_SIZE(ioreqs_old_phy));
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0]));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1));
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2));
+	if (r)
+		return r;
+
+	return zd1211b_al7230b_finalize(chip);
+}
+
+static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	const u32 *rv = chan_rv[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs[] = {
+		/* PLL_ON */
+		{ CR251, 0x3f },
+		{ CR203, 0x06 }, { CR240, 0x08 },
+	};
+
+	r = zd_iowrite16_locked(chip, 0x57, CR240);
+	if (r)
+		return r;
+
+	/* PLL_OFF */
+	r = zd_iowrite16_locked(chip, 0x2f, CR251);
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
+
+	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+	if (r)
+		return r;
+	r = zd_rfwrite_cr_locked(chip, 0xf15d58);
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv, 2);
+	if (r)
+		return r;
+
+	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+	if (r)
+		return r;
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	const u32 *rv = chan_rv[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	r = zd_iowrite16_locked(chip, 0x57, CR240);
+	if (r)
+		return r;
+	r = zd_iowrite16_locked(chip, 0xe4, CR9);
+	if (r)
+		return r;
+
+	/* PLL_OFF */
+	r = zd_iowrite16_locked(chip, 0x2f, CR251);
+	if (r)
+		return r;
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
+
+	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+	if (r)
+		return r;
+	r = zd_rfwrite_cr_locked(chip, 0xf15d58);
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv, 2);
+	if (r)
+		return r;
+
+	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+	if (r)
+		return r;
+
+	r = zd_iowrite16_locked(chip, 0x7f, CR251);
+	if (r)
+		return r;
+
+	return zd1211b_al7230b_finalize(chip);
+}
+
+static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 },
+		{ CR251, 0x3f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 },
+		{ CR251, 0x7f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int al7230b_switch_radio_off(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x04 },
+		{ CR251, 0x2f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+/* ZD1211B+AL7230B 6m band edge patching differs slightly from other
+ * configurations */
+static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	struct zd_ioreq16 ioreqs[] = {
+		{ CR128, 0x14 }, { CR129, 0x12 },
+	};
+
+	/* FIXME: Channel 11 is not the edge for all regulatory domains. */
+	if (channel == 1) {
+		ioreqs[0].value = 0x0e;
+		ioreqs[1].value = 0x10;
+	} else if (channel == 11) {
+		ioreqs[0].value = 0x10;
+		ioreqs[1].value = 0x10;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel);
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rf_init_al7230b(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	if (zd_chip_is_zd1211b(chip)) {
+		rf->init_hw = zd1211b_al7230b_init_hw;
+		rf->switch_radio_on = zd1211b_al7230b_switch_radio_on;
+		rf->set_channel = zd1211b_al7230b_set_channel;
+		rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m;
+	} else {
+		rf->init_hw = zd1211_al7230b_init_hw;
+		rf->switch_radio_on = zd1211_al7230b_switch_radio_on;
+		rf->set_channel = zd1211_al7230b_set_channel;
+		rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
+		rf->patch_cck_gain = 1;
+	}
+
+	rf->switch_radio_off = al7230b_switch_radio_off;
+
+	return 0;
+}
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_rf2959.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_rf2959.c
@@ -0,0 +1,279 @@
+/* zd_rf_rfmd.c: Functions for the RFMD RF controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+static const u32 rf2959_table[][2] = {
+	RF_CHANNEL( 1) = { 0x181979, 0x1e6666 },
+	RF_CHANNEL( 2) = { 0x181989, 0x1e6666 },
+	RF_CHANNEL( 3) = { 0x181999, 0x1e6666 },
+	RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 },
+	RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 },
+	RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 },
+	RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 },
+	RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 },
+	RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 },
+	RF_CHANNEL(10) = { 0x181a09, 0x1e6666 },
+	RF_CHANNEL(11) = { 0x181a19, 0x1e6666 },
+	RF_CHANNEL(12) = { 0x181a29, 0x1e6666 },
+	RF_CHANNEL(13) = { 0x181a39, 0x1e6666 },
+	RF_CHANNEL(14) = { 0x181a60, 0x1c0000 },
+};
+
+#if 0
+static int bits(u32 rw, int from, int to)
+{
+	rw &= ~(0xffffffffU << (to+1));
+	rw >>= from;
+	return rw;
+}
+
+static int bit(u32 rw, int bit)
+{
+	return bits(rw, bit, bit);
+}
+
+static void dump_regwrite(u32 rw)
+{
+	int reg = bits(rw, 18, 22);
+	int rw_flag = bits(rw, 23, 23);
+	PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag);
+
+	switch (reg) {
+	case 0:
+		PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d"
+		       " if_vco_reg_en %d if_vga_en %d",
+		       bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1),
+		       bit(rw, 0));
+		break;
+	case 1:
+		PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d"
+		       " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d"
+		       " ifloopc %d dac1 %d",
+		       bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14),
+		       bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10),
+		       bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3));
+		break;
+	case 2:
+		PDEBUG("reg2 IFPLL2 n1 %d num1 %d",
+		       bits(rw, 6, 17), bits(rw, 0, 5));
+		break;
+	case 3:
+		PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17));
+		break;
+	case 4:
+		PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d",
+		       bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3));
+		break;
+	case 5:
+		PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d"
+		       " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d"
+		       " dac %d",
+		       bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14),
+		       bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10),
+		       bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3));
+		break;
+	case 6:
+		PDEBUG("reg6 RFPLL2 n %d num %d",
+		       bits(rw, 6, 17), bits(rw, 0, 5));
+		break;
+	case 7:
+		PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17));
+		break;
+	case 8:
+		PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d",
+		       bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3));
+		break;
+	case 9:
+		PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d",
+		       bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7),
+		       bits(rw, 0, 2));
+		break;
+	case 10:
+		PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d"
+		       " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d"
+		       " intbiasen %d tybypass %d",
+		       bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14),
+		       bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2),
+		       bit(rw, 1), bit(rw, 0));
+		break;
+	case 11:
+		PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d"
+			" tx_delay %d",
+			bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8),
+			bits(rw, 0, 2));
+		break;
+	case 12:
+		PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d",
+		       bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5));
+		break;
+	case 13:
+		PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d"
+		       " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d"
+		       " rf_biasvco %d",
+		       bit(rw, 17), bit(rw, 16), bit(rw, 15),
+		       bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4),
+		       bits(rw, 0, 2));
+		break;
+	case 14:
+		PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d"
+		       " tx_acal %d tx_pcal %d",
+		       bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8),
+		       bits(rw, 0, 3));
+		break;
+	}
+}
+#endif /* 0 */
+
+static int rf2959_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR2,   0x1E }, { CR9,   0x20 }, { CR10,  0x89 },
+		{ CR11,  0x00 }, { CR15,  0xD0 }, { CR17,  0x68 },
+		{ CR19,  0x4a }, { CR20,  0x0c }, { CR21,  0x0E },
+		{ CR23,  0x48 },
+		/* normal size for cca threshold */
+		{ CR24,  0x14 },
+		/* { CR24,  0x20 }, */
+		{ CR26,  0x90 }, { CR27,  0x30 }, { CR29,  0x20 },
+		{ CR31,  0xb2 }, { CR32,  0x43 }, { CR33,  0x28 },
+		{ CR38,  0x30 }, { CR34,  0x0f }, { CR35,  0xF0 },
+		{ CR41,  0x2a }, { CR46,  0x7F }, { CR47,  0x1E },
+		{ CR51,  0xc5 }, { CR52,  0xc5 }, { CR53,  0xc5 },
+		{ CR79,  0x58 }, { CR80,  0x30 }, { CR81,  0x30 },
+		{ CR82,  0x00 }, { CR83,  0x24 }, { CR84,  0x04 },
+		{ CR85,  0x00 }, { CR86,  0x10 }, { CR87,  0x2A },
+		{ CR88,  0x10 }, { CR89,  0x24 }, { CR90,  0x18 },
+		/* { CR91,  0x18 }, */
+		/* should solve continous CTS frame problems */
+		{ CR91,  0x00 },
+		{ CR92,  0x0a }, { CR93,  0x00 }, { CR94,  0x01 },
+		{ CR95,  0x00 }, { CR96,  0x40 }, { CR97,  0x37 },
+		{ CR98,  0x05 }, { CR99,  0x28 }, { CR100, 0x00 },
+		{ CR101, 0x13 }, { CR102, 0x27 }, { CR103, 0x27 },
+		{ CR104, 0x18 }, { CR105, 0x12 },
+		/* normal size */
+		{ CR106, 0x1a },
+		/* { CR106, 0x22 }, */
+		{ CR107, 0x24 }, { CR108, 0x0a }, { CR109, 0x13 },
+		{ CR110, 0x2F }, { CR111, 0x27 }, { CR112, 0x27 },
+		{ CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x40 },
+		{ CR116, 0x40 }, { CR117, 0xF0 }, { CR118, 0xF0 },
+		{ CR119, 0x16 },
+		/* no TX continuation */
+		{ CR122, 0x00 },
+		/* { CR122, 0xff }, */
+		{ CR127, 0x03 }, { CR131, 0x08 }, { CR138, 0x28 },
+		{ CR148, 0x44 }, { CR150, 0x10 }, { CR169, 0xBB },
+		{ CR170, 0xBB },
+	};
+
+	static const u32 rv[] = {
+		0x000007,  /* REG0(CFG1) */
+		0x07dd43,  /* REG1(IFPLL1) */
+		0x080959,  /* REG2(IFPLL2) */
+		0x0e6666,
+		0x116a57,  /* REG4 */
+		0x17dd43,  /* REG5 */
+		0x1819f9,  /* REG6 */
+		0x1e6666,
+		0x214554,
+		0x25e7fa,
+		0x27fffa,
+		/* The Zydas driver somehow forgets to set this value. It's
+		 * only set for Japan. We are using internal power control
+		 * for now.
+		 */
+		0x294128, /* internal power */
+		/* 0x28252c, */ /* External control TX power */
+		/* CR31_CCK, CR51_6-36M, CR52_48M, CR53_54M */
+		0x2c0000,
+		0x300000,
+		0x340000,  /* REG13(0xD) */
+		0x381e0f,  /* REG14(0xE) */
+		/* Bogus, RF2959's data sheet doesn't know register 27, which is
+		 * actually referenced here. The commented 0x11 is 17.
+		 */
+		0x6c180f,  /* REG27(0x11) */
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+}
+
+static int rf2959_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int i, r;
+	const u32 *rv = rf2959_table[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	for (i = 0; i < 2; i++) {
+		r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS);
+		if (r)
+			return r;
+	}
+	return 0;
+}
+
+static int rf2959_switch_radio_on(struct zd_rf *rf)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR10, 0x89 },
+		{ CR11, 0x00 },
+	};
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int rf2959_switch_radio_off(struct zd_rf *rf)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR10, 0x15 },
+		{ CR11, 0x81 },
+	};
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rf_init_rf2959(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	if (zd_chip_is_zd1211b(chip)) {
+		dev_err(zd_chip_dev(chip),
+		       "RF2959 is currently not supported for ZD1211B"
+		       " devices\n");
+		return -ENODEV;
+	}
+	rf->init_hw = rf2959_init_hw;
+	rf->set_channel = rf2959_set_channel;
+	rf->switch_radio_on = rf2959_switch_radio_on;
+	rf->switch_radio_off = rf2959_switch_radio_off;
+	return 0;
+}
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_uw2453.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_uw2453.c
@@ -0,0 +1,534 @@
+/* zd_rf_uw2453.c: Functions for the UW2453 RF controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+/* This RF programming code is based upon the code found in v2.16.0.0 of the
+ * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs
+ * for this RF on their website, so we're able to understand more than
+ * usual as to what is going on. Thumbs up for Ubec for doing that. */
+
+/* The 3-wire serial interface provides access to 8 write-only registers.
+ * The data format is a 4 bit register address followed by a 20 bit value. */
+#define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff))
+
+/* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth
+ * fractional divide ratio) and 3 (VCO config).
+ *
+ * We configure the RF to produce an interrupt when the PLL is locked onto
+ * the configured frequency. During initialization, we run through a variety
+ * of different VCO configurations on channel 1 until we detect a PLL lock.
+ * When this happens, we remember which VCO configuration produced the lock
+ * and use it later. Actually, we use the configuration *after* the one that
+ * produced the lock, which seems odd, but it works.
+ *
+ * If we do not see a PLL lock on any standard VCO config, we fall back on an
+ * autocal configuration, which has a fixed (as opposed to per-channel) VCO
+ * config and different synth values from the standard set (divide ratio
+ * is still shared with the standard set). */
+
+/* The per-channel synth values for all standard VCO configurations. These get
+ * written to register 1. */
+static const u8 uw2453_std_synth[] = {
+	RF_CHANNEL( 1) = 0x47,
+	RF_CHANNEL( 2) = 0x47,
+	RF_CHANNEL( 3) = 0x67,
+	RF_CHANNEL( 4) = 0x67,
+	RF_CHANNEL( 5) = 0x67,
+	RF_CHANNEL( 6) = 0x67,
+	RF_CHANNEL( 7) = 0x57,
+	RF_CHANNEL( 8) = 0x57,
+	RF_CHANNEL( 9) = 0x57,
+	RF_CHANNEL(10) = 0x57,
+	RF_CHANNEL(11) = 0x77,
+	RF_CHANNEL(12) = 0x77,
+	RF_CHANNEL(13) = 0x77,
+	RF_CHANNEL(14) = 0x4f,
+};
+
+/* This table stores the synthesizer fractional divide ratio for *all* VCO
+ * configurations (both standard and autocal). These get written to register 2.
+ */
+static const u16 uw2453_synth_divide[] = {
+	RF_CHANNEL( 1) = 0x999,
+	RF_CHANNEL( 2) = 0x99b,
+	RF_CHANNEL( 3) = 0x998,
+	RF_CHANNEL( 4) = 0x99a,
+	RF_CHANNEL( 5) = 0x999,
+	RF_CHANNEL( 6) = 0x99b,
+	RF_CHANNEL( 7) = 0x998,
+	RF_CHANNEL( 8) = 0x99a,
+	RF_CHANNEL( 9) = 0x999,
+	RF_CHANNEL(10) = 0x99b,
+	RF_CHANNEL(11) = 0x998,
+	RF_CHANNEL(12) = 0x99a,
+	RF_CHANNEL(13) = 0x999,
+	RF_CHANNEL(14) = 0xccc,
+};
+
+/* Here is the data for all the standard VCO configurations. We shrink our
+ * table a little by observing that both channels in a consecutive pair share
+ * the same value. We also observe that the high 4 bits ([0:3] in the specs)
+ * are all 'Reserved' and are always set to 0x4 - we chop them off in the data
+ * below. */
+#define CHAN_TO_PAIRIDX(a) ((a - 1) / 2)
+#define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)]
+static const u16 uw2453_std_vco_cfg[][7] = {
+	{ /* table 1 */
+		RF_CHANPAIR( 1,  2) = 0x664d,
+		RF_CHANPAIR( 3,  4) = 0x604d,
+		RF_CHANPAIR( 5,  6) = 0x6675,
+		RF_CHANPAIR( 7,  8) = 0x6475,
+		RF_CHANPAIR( 9, 10) = 0x6655,
+		RF_CHANPAIR(11, 12) = 0x6455,
+		RF_CHANPAIR(13, 14) = 0x6665,
+	},
+	{ /* table 2 */
+		RF_CHANPAIR( 1,  2) = 0x666d,
+		RF_CHANPAIR( 3,  4) = 0x606d,
+		RF_CHANPAIR( 5,  6) = 0x664d,
+		RF_CHANPAIR( 7,  8) = 0x644d,
+		RF_CHANPAIR( 9, 10) = 0x6675,
+		RF_CHANPAIR(11, 12) = 0x6475,
+		RF_CHANPAIR(13, 14) = 0x6655,
+	},
+	{ /* table 3 */
+		RF_CHANPAIR( 1,  2) = 0x665d,
+		RF_CHANPAIR( 3,  4) = 0x605d,
+		RF_CHANPAIR( 5,  6) = 0x666d,
+		RF_CHANPAIR( 7,  8) = 0x646d,
+		RF_CHANPAIR( 9, 10) = 0x664d,
+		RF_CHANPAIR(11, 12) = 0x644d,
+		RF_CHANPAIR(13, 14) = 0x6675,
+	},
+	{ /* table 4 */
+		RF_CHANPAIR( 1,  2) = 0x667d,
+		RF_CHANPAIR( 3,  4) = 0x607d,
+		RF_CHANPAIR( 5,  6) = 0x665d,
+		RF_CHANPAIR( 7,  8) = 0x645d,
+		RF_CHANPAIR( 9, 10) = 0x666d,
+		RF_CHANPAIR(11, 12) = 0x646d,
+		RF_CHANPAIR(13, 14) = 0x664d,
+	},
+	{ /* table 5 */
+		RF_CHANPAIR( 1,  2) = 0x6643,
+		RF_CHANPAIR( 3,  4) = 0x6043,
+		RF_CHANPAIR( 5,  6) = 0x667d,
+		RF_CHANPAIR( 7,  8) = 0x647d,
+		RF_CHANPAIR( 9, 10) = 0x665d,
+		RF_CHANPAIR(11, 12) = 0x645d,
+		RF_CHANPAIR(13, 14) = 0x666d,
+	},
+	{ /* table 6 */
+		RF_CHANPAIR( 1,  2) = 0x6663,
+		RF_CHANPAIR( 3,  4) = 0x6063,
+		RF_CHANPAIR( 5,  6) = 0x6643,
+		RF_CHANPAIR( 7,  8) = 0x6443,
+		RF_CHANPAIR( 9, 10) = 0x667d,
+		RF_CHANPAIR(11, 12) = 0x647d,
+		RF_CHANPAIR(13, 14) = 0x665d,
+	},
+	{ /* table 7 */
+		RF_CHANPAIR( 1,  2) = 0x6653,
+		RF_CHANPAIR( 3,  4) = 0x6053,
+		RF_CHANPAIR( 5,  6) = 0x6663,
+		RF_CHANPAIR( 7,  8) = 0x6463,
+		RF_CHANPAIR( 9, 10) = 0x6643,
+		RF_CHANPAIR(11, 12) = 0x6443,
+		RF_CHANPAIR(13, 14) = 0x667d,
+	},
+	{ /* table 8 */
+		RF_CHANPAIR( 1,  2) = 0x6673,
+		RF_CHANPAIR( 3,  4) = 0x6073,
+		RF_CHANPAIR( 5,  6) = 0x6653,
+		RF_CHANPAIR( 7,  8) = 0x6453,
+		RF_CHANPAIR( 9, 10) = 0x6663,
+		RF_CHANPAIR(11, 12) = 0x6463,
+		RF_CHANPAIR(13, 14) = 0x6643,
+	},
+	{ /* table 9 */
+		RF_CHANPAIR( 1,  2) = 0x664b,
+		RF_CHANPAIR( 3,  4) = 0x604b,
+		RF_CHANPAIR( 5,  6) = 0x6673,
+		RF_CHANPAIR( 7,  8) = 0x6473,
+		RF_CHANPAIR( 9, 10) = 0x6653,
+		RF_CHANPAIR(11, 12) = 0x6453,
+		RF_CHANPAIR(13, 14) = 0x6663,
+	},
+	{ /* table 10 */
+		RF_CHANPAIR( 1,  2) = 0x666b,
+		RF_CHANPAIR( 3,  4) = 0x606b,
+		RF_CHANPAIR( 5,  6) = 0x664b,
+		RF_CHANPAIR( 7,  8) = 0x644b,
+		RF_CHANPAIR( 9, 10) = 0x6673,
+		RF_CHANPAIR(11, 12) = 0x6473,
+		RF_CHANPAIR(13, 14) = 0x6653,
+	},
+	{ /* table 11 */
+		RF_CHANPAIR( 1,  2) = 0x665b,
+		RF_CHANPAIR( 3,  4) = 0x605b,
+		RF_CHANPAIR( 5,  6) = 0x666b,
+		RF_CHANPAIR( 7,  8) = 0x646b,
+		RF_CHANPAIR( 9, 10) = 0x664b,
+		RF_CHANPAIR(11, 12) = 0x644b,
+		RF_CHANPAIR(13, 14) = 0x6673,
+	},
+
+};
+
+/* The per-channel synth values for autocal. These get written to register 1. */
+static const u16 uw2453_autocal_synth[] = {
+	RF_CHANNEL( 1) = 0x6847,
+	RF_CHANNEL( 2) = 0x6847,
+	RF_CHANNEL( 3) = 0x6867,
+	RF_CHANNEL( 4) = 0x6867,
+	RF_CHANNEL( 5) = 0x6867,
+	RF_CHANNEL( 6) = 0x6867,
+	RF_CHANNEL( 7) = 0x6857,
+	RF_CHANNEL( 8) = 0x6857,
+	RF_CHANNEL( 9) = 0x6857,
+	RF_CHANNEL(10) = 0x6857,
+	RF_CHANNEL(11) = 0x6877,
+	RF_CHANNEL(12) = 0x6877,
+	RF_CHANNEL(13) = 0x6877,
+	RF_CHANNEL(14) = 0x684f,
+};
+
+/* The VCO configuration for autocal (all channels) */
+static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662;
+
+/* TX gain settings. The array index corresponds to the TX power integration
+ * values found in the EEPROM. The values get written to register 7. */
+static u32 uw2453_txgain[] = {
+	[0x00] = 0x0e313,
+	[0x01] = 0x0fb13,
+	[0x02] = 0x0e093,
+	[0x03] = 0x0f893,
+	[0x04] = 0x0ea93,
+	[0x05] = 0x1f093,
+	[0x06] = 0x1f493,
+	[0x07] = 0x1f693,
+	[0x08] = 0x1f393,
+	[0x09] = 0x1f35b,
+	[0x0a] = 0x1e6db,
+	[0x0b] = 0x1ff3f,
+	[0x0c] = 0x1ffff,
+	[0x0d] = 0x361d7,
+	[0x0e] = 0x37fbf,
+	[0x0f] = 0x3ff8b,
+	[0x10] = 0x3ff33,
+	[0x11] = 0x3fb3f,
+	[0x12] = 0x3ffff,
+};
+
+/* RF-specific structure */
+struct uw2453_priv {
+	/* index into synth/VCO config tables where PLL lock was found
+	 * -1 means autocal */
+	int config;
+};
+
+#define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv)
+
+static int uw2453_synth_set_channel(struct zd_chip *chip, int channel,
+	bool autocal)
+{
+	int r;
+	int idx = channel - 1;
+	u32 val;
+
+	if (autocal)
+		val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]);
+	else
+		val = UW2453_REGWRITE(1, uw2453_std_synth[idx]);
+
+	r = zd_rfwrite_locked(chip, val, RF_RV_BITS);
+	if (r)
+		return r;
+
+	return zd_rfwrite_locked(chip,
+		UW2453_REGWRITE(2, uw2453_synth_divide[idx]), RF_RV_BITS);
+}
+
+static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value)
+{
+	/* vendor driver always sets these upper bits even though the specs say
+	 * they are reserved */
+	u32 val = 0x40000 | value;
+	return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), RF_RV_BITS);
+}
+
+static int uw2453_init_mode(struct zd_chip *chip)
+{
+	static const u32 rv[] = {
+		UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */
+		UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */
+		UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */
+		UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */
+	};
+
+	return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+}
+
+static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel)
+{
+	u8 int_value = chip->pwr_int_values[channel - 1];
+
+	if (int_value >= ARRAY_SIZE(uw2453_txgain)) {
+		dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for "
+			  "int value %x on channel %d\n", int_value, channel);
+		return 0;
+	}
+
+	return zd_rfwrite_locked(chip,
+		UW2453_REGWRITE(7, uw2453_txgain[int_value]), RF_RV_BITS);
+}
+
+static int uw2453_init_hw(struct zd_rf *rf)
+{
+	int i, r;
+	int found_config = -1;
+	u16 intr_status;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR10,  0x89 }, { CR15,  0x20 },
+		{ CR17,  0x28 }, /* 6112 no change */
+		{ CR23,  0x38 }, { CR24,  0x20 }, { CR26,  0x93 },
+		{ CR27,  0x15 }, { CR28,  0x3e }, { CR29,  0x00 },
+		{ CR33,  0x28 }, { CR34,  0x30 },
+		{ CR35,  0x43 }, /* 6112 3e->43 */
+		{ CR41,  0x24 }, { CR44,  0x32 },
+		{ CR46,  0x92 }, /* 6112 96->92 */
+		{ CR47,  0x1e },
+		{ CR48,  0x04 }, /* 5602 Roger */
+		{ CR49,  0xfa }, { CR79,  0x58 }, { CR80,  0x30 },
+		{ CR81,  0x30 }, { CR87,  0x0a }, { CR89,  0x04 },
+		{ CR91,  0x00 }, { CR92,  0x0a }, { CR98,  0x8d },
+		{ CR99,  0x28 }, { CR100, 0x02 },
+		{ CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */
+		{ CR102, 0x27 },
+		{ CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f 6221 1f->1c */
+		{ CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */
+		{ CR109, 0x13 },
+		{ CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */
+		{ CR111, 0x13 }, { CR112, 0x1f }, { CR113, 0x27 },
+		{ CR114, 0x23 }, /* 6221 27->23 */
+		{ CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */
+		{ CR116, 0x24 }, /* 6220 1c->24 */
+		{ CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */
+		{ CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */
+		{ CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */
+		{ CR120, 0x4f },
+		{ CR121, 0x1f }, /* 6220 4f->1f */
+		{ CR122, 0xf0 }, { CR123, 0x57 }, { CR125, 0xad },
+		{ CR126, 0x6c }, { CR127, 0x03 },
+		{ CR128, 0x14 }, /* 6302 12->11 */
+		{ CR129, 0x12 }, /* 6301 10->0f */
+		{ CR130, 0x10 }, { CR137, 0x50 }, { CR138, 0xa8 },
+		{ CR144, 0xac }, { CR146, 0x20 }, { CR252, 0xff },
+		{ CR253, 0xff },
+	};
+
+	static const u32 rv[] = {
+		UW2453_REGWRITE(4, 0x2b),    /* configure reciever gain */
+		UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */
+		UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */
+		UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */
+
+		/* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins,
+		 * RSSI circuit powered down, reduced RSSI range */
+		UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */
+
+		/* synthesizer configuration for channel 1 */
+		UW2453_REGWRITE(1, 0x47),
+		UW2453_REGWRITE(2, 0x999),
+
+		/* disable manual VCO band selection */
+		UW2453_REGWRITE(3, 0x7602),
+
+		/* enable manual VCO band selection, configure current level */
+		UW2453_REGWRITE(3, 0x46063),
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+	if (r)
+		return r;
+
+	r = uw2453_init_mode(chip);
+	if (r)
+		return r;
+
+	/* Try all standard VCO configuration settings on channel 1 */
+	for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) {
+		/* Configure synthesizer for channel 1 */
+		r = uw2453_synth_set_channel(chip, 1, false);
+		if (r)
+			return r;
+
+		/* Write VCO config */
+		r = uw2453_write_vco_cfg(chip, uw2453_std_vco_cfg[i][0]);
+		if (r)
+			return r;
+
+		/* ack interrupt event */
+		r = zd_iowrite16_locked(chip, 0x0f, UW2453_INTR_REG);
+		if (r)
+			return r;
+
+		/* check interrupt status */
+		r = zd_ioread16_locked(chip, &intr_status, UW2453_INTR_REG);
+		if (r)
+			return r;
+
+		if (!intr_status & 0xf) {
+			dev_dbg_f(zd_chip_dev(chip),
+				"PLL locked on configuration %d\n", i);
+			found_config = i;
+			break;
+		}
+	}
+
+	if (found_config == -1) {
+		/* autocal */
+		dev_dbg_f(zd_chip_dev(chip),
+			"PLL did not lock, using autocal\n");
+
+		r = uw2453_synth_set_channel(chip, 1, true);
+		if (r)
+			return r;
+
+		r = uw2453_write_vco_cfg(chip, UW2453_AUTOCAL_VCO_CFG);
+		if (r)
+			return r;
+	}
+
+	/* To match the vendor driver behaviour, we use the configuration after
+	 * the one that produced a lock. */
+	UW2453_PRIV(rf)->config = found_config + 1;
+
+	return zd_iowrite16_locked(chip, 0x06, CR203);
+}
+
+static int uw2453_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	u16 vco_cfg;
+	int config = UW2453_PRIV(rf)->config;
+	bool autocal = (config == -1);
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR80,  0x30 }, { CR81,  0x30 }, { CR79,  0x58 },
+		{ CR12,  0xf0 }, { CR77,  0x1b }, { CR78,  0x58 },
+	};
+
+	r = uw2453_synth_set_channel(chip, channel, autocal);
+	if (r)
+		return r;
+
+	if (autocal)
+		vco_cfg = UW2453_AUTOCAL_VCO_CFG;
+	else
+		vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)];
+
+	r = uw2453_write_vco_cfg(chip, vco_cfg);
+	if (r)
+		return r;
+
+	r = uw2453_init_mode(chip);
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	r = uw2453_set_tx_gain_level(chip, channel);
+	if (r)
+		return r;
+
+	return zd_iowrite16_locked(chip, 0x06, CR203);
+}
+
+static int uw2453_switch_radio_on(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 }, { CR251, 0x3f },
+	};
+
+	/* enter RXTX mode */
+	r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), RF_RV_BITS);
+	if (r)
+		return r;
+
+	if (zd_chip_is_zd1211b(chip))
+		ioreqs[1].value = 0x7f;
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int uw2453_switch_radio_off(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x04 }, { CR251, 0x2f },
+	};
+
+	/* enter IDLE mode */
+	/* FIXME: shouldn't we go to SLEEP? sent email to zydas */
+	r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), RF_RV_BITS);
+	if (r)
+		return r;
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static void uw2453_clear(struct zd_rf *rf)
+{
+	kfree(rf->priv);
+}
+
+int zd_rf_init_uw2453(struct zd_rf *rf)
+{
+	rf->init_hw = uw2453_init_hw;
+	rf->set_channel = uw2453_set_channel;
+	rf->switch_radio_on = uw2453_switch_radio_on;
+	rf->switch_radio_off = uw2453_switch_radio_off;
+	rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
+	rf->clear = uw2453_clear;
+	/* we have our own TX integration code */
+	rf->update_channel_int = 0;
+
+	rf->priv = kmalloc(sizeof(struct uw2453_priv), GFP_KERNEL);
+	if (rf->priv == NULL)
+		return -ENOMEM;
+
+	return 0;
+}
+
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_usb.c
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_usb.c
@@ -0,0 +1,1528 @@
+/* zd_usb.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/unaligned.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <net/mac80211.h>
+
+#include "zd_def.h"
+#include "zd_mac.h"
+#include "zd_usb.h"
+
+static struct usb_device_id usb_ids[] = {
+	/* ZD1211 */
+	{ USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 },
+	/* ZD1211B */
+	{ USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B },
+	/* "Driverless" devices that need ejecting */
+	{ USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER },
+	{ USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER },
+	{}
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip.");
+MODULE_AUTHOR("Ulrich Kunitz");
+MODULE_AUTHOR("Daniel Drake");
+MODULE_VERSION("1.0");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+#define FW_ZD1211_PREFIX	"zd1211/zd1211_"
+#define FW_ZD1211B_PREFIX	"zd1211/zd1211b_"
+
+/* USB device initialization */
+
+static int request_fw_file(
+	const struct firmware **fw, const char *name, struct device *device)
+{
+	int r;
+
+	dev_dbg_f(device, "fw name %s\n", name);
+
+	r = request_firmware(fw, name, device);
+	if (r)
+		dev_err(device,
+		       "Could not load firmware file %s. Error number %d\n",
+		       name, r);
+	return r;
+}
+
+static inline u16 get_bcdDevice(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+enum upload_code_flags {
+	REBOOT = 1,
+};
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+static int upload_code(struct usb_device *udev,
+	const u8 *data, size_t size, u16 code_offset, int flags)
+{
+	u8 *p;
+	int r;
+
+	/* USB request blocks need "kmalloced" buffers.
+	 */
+	p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL);
+	if (!p) {
+		dev_err(&udev->dev, "out of memory\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	size &= ~1;
+	while (size > 0) {
+		size_t transfer_size = size <= MAX_TRANSFER_SIZE ?
+			size : MAX_TRANSFER_SIZE;
+
+		dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size);
+
+		memcpy(p, data, transfer_size);
+		r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			USB_REQ_FIRMWARE_DOWNLOAD,
+			USB_DIR_OUT | USB_TYPE_VENDOR,
+			code_offset, 0, p, transfer_size, 1000 /* ms */);
+		if (r < 0) {
+			dev_err(&udev->dev,
+			       "USB control request for firmware upload"
+			       " failed. Error number %d\n", r);
+			goto error;
+		}
+		transfer_size = r & ~1;
+
+		size -= transfer_size;
+		data += transfer_size;
+		code_offset += transfer_size/sizeof(u16);
+	}
+
+	if (flags & REBOOT) {
+		u8 ret;
+
+		r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			USB_REQ_FIRMWARE_CONFIRM,
+			USB_DIR_IN | USB_TYPE_VENDOR,
+			0, 0, &ret, sizeof(ret), 5000 /* ms */);
+		if (r != sizeof(ret)) {
+			dev_err(&udev->dev,
+				"control request firmeware confirmation failed."
+				" Return value %d\n", r);
+			if (r >= 0)
+				r = -ENODEV;
+			goto error;
+		}
+		if (ret & 0x80) {
+			dev_err(&udev->dev,
+				"Internal error while downloading."
+				" Firmware confirm return value %#04x\n",
+				(unsigned int)ret);
+			r = -ENODEV;
+			goto error;
+		}
+		dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n",
+			(unsigned int)ret);
+	}
+
+	r = 0;
+error:
+	kfree(p);
+	return r;
+}
+
+static u16 get_word(const void *data, u16 offset)
+{
+	const __le16 *p = data;
+	return le16_to_cpu(p[offset]);
+}
+
+static char *get_fw_name(struct zd_usb *usb, char *buffer, size_t size,
+	               const char* postfix)
+{
+	scnprintf(buffer, size, "%s%s",
+		usb->is_zd1211b ?
+			FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX,
+		postfix);
+	return buffer;
+}
+
+static int handle_version_mismatch(struct zd_usb *usb,
+	const struct firmware *ub_fw)
+{
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
+	const struct firmware *ur_fw = NULL;
+	int offset;
+	int r = 0;
+	char fw_name[128];
+
+	r = request_fw_file(&ur_fw,
+		get_fw_name(usb, fw_name, sizeof(fw_name), "ur"),
+		&udev->dev);
+	if (r)
+		goto error;
+
+	r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT);
+	if (r)
+		goto error;
+
+	offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16));
+	r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset,
+		E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT);
+
+	/* At this point, the vendor driver downloads the whole firmware
+	 * image, hacks around with version IDs, and uploads it again,
+	 * completely overwriting the boot code. We do not do this here as
+	 * it is not required on any tested devices, and it is suspected to
+	 * cause problems. */
+error:
+	release_firmware(ur_fw);
+	return r;
+}
+
+static int upload_firmware(struct zd_usb *usb)
+{
+	int r;
+	u16 fw_bcdDevice;
+	u16 bcdDevice;
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
+	const struct firmware *ub_fw = NULL;
+	const struct firmware *uph_fw = NULL;
+	char fw_name[128];
+
+	bcdDevice = get_bcdDevice(udev);
+
+	r = request_fw_file(&ub_fw,
+		get_fw_name(usb, fw_name, sizeof(fw_name), "ub"),
+		&udev->dev);
+	if (r)
+		goto error;
+
+	fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET);
+
+	if (fw_bcdDevice != bcdDevice) {
+		dev_info(&udev->dev,
+			"firmware version %#06x and device bootcode version "
+			"%#06x differ\n", fw_bcdDevice, bcdDevice);
+		if (bcdDevice <= 0x4313)
+			dev_warn(&udev->dev, "device has old bootcode, please "
+				"report success or failure\n");
+
+		r = handle_version_mismatch(usb, ub_fw);
+		if (r)
+			goto error;
+	} else {
+		dev_dbg_f(&udev->dev,
+			"firmware device id %#06x is equal to the "
+			"actual device id\n", fw_bcdDevice);
+	}
+
+
+	r = request_fw_file(&uph_fw,
+		get_fw_name(usb, fw_name, sizeof(fw_name), "uphr"),
+		&udev->dev);
+	if (r)
+		goto error;
+
+	r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT);
+	if (r) {
+		dev_err(&udev->dev,
+			"Could not upload firmware code uph. Error number %d\n",
+			r);
+	}
+
+	/* FALL-THROUGH */
+error:
+	release_firmware(ub_fw);
+	release_firmware(uph_fw);
+	return r;
+}
+
+/* Read data from device address space using "firmware interface" which does
+ * not require firmware to be loaded. */
+int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len)
+{
+	int r;
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
+
+	r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+		USB_REQ_FIRMWARE_READ_DATA, USB_DIR_IN | 0x40, addr, 0,
+		data, len, 5000);
+	if (r < 0) {
+		dev_err(&udev->dev,
+			"read over firmware interface failed: %d\n", r);
+		return r;
+	} else if (r != len) {
+		dev_err(&udev->dev,
+			"incomplete read over firmware interface: %d/%d\n",
+			r, len);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+static inline void handle_regs_int(struct urb *urb)
+{
+	struct zd_usb *usb = urb->context;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	int len;
+
+	ZD_ASSERT(in_interrupt());
+	spin_lock(&intr->lock);
+
+	if (intr->read_regs_enabled) {
+		intr->read_regs.length = len = urb->actual_length;
+
+		if (len > sizeof(intr->read_regs.buffer))
+			len = sizeof(intr->read_regs.buffer);
+		memcpy(intr->read_regs.buffer, urb->transfer_buffer, len);
+		intr->read_regs_enabled = 0;
+		complete(&intr->read_regs.completion);
+		goto out;
+	}
+
+	dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n");
+out:
+	spin_unlock(&intr->lock);
+}
+
+static void int_urb_complete(struct urb *urb)
+{
+	int r;
+	struct usb_int_header *hdr;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		goto kfree;
+	default:
+		goto resubmit;
+	}
+
+	if (urb->actual_length < sizeof(hdr)) {
+		dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb);
+		goto resubmit;
+	}
+
+	hdr = urb->transfer_buffer;
+	if (hdr->type != USB_INT_TYPE) {
+		dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb);
+		goto resubmit;
+	}
+
+	switch (hdr->id) {
+	case USB_INT_ID_REGS:
+		handle_regs_int(urb);
+		break;
+	case USB_INT_ID_RETRY_FAILED:
+		zd_mac_tx_failed(zd_usb_to_hw(urb->context));
+		break;
+	default:
+		dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb,
+			(unsigned int)hdr->id);
+		goto resubmit;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg_f(urb_dev(urb), "resubmit urb %p\n", urb);
+		goto kfree;
+	}
+	return;
+kfree:
+	kfree(urb->transfer_buffer);
+}
+
+static inline int int_urb_interval(struct usb_device *udev)
+{
+	switch (udev->speed) {
+	case USB_SPEED_HIGH:
+		return 4;
+	case USB_SPEED_LOW:
+		return 10;
+	case USB_SPEED_FULL:
+	default:
+		return 1;
+	}
+}
+
+static inline int usb_int_enabled(struct zd_usb *usb)
+{
+	unsigned long flags;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	struct urb *urb;
+
+	spin_lock_irqsave(&intr->lock, flags);
+	urb = intr->urb;
+	spin_unlock_irqrestore(&intr->lock, flags);
+	return urb != NULL;
+}
+
+int zd_usb_enable_int(struct zd_usb *usb)
+{
+	int r;
+	struct usb_device *udev;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	void *transfer_buffer = NULL;
+	struct urb *urb;
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&intr->lock);
+	if (intr->urb) {
+		spin_unlock_irq(&intr->lock);
+		r = 0;
+		goto error_free_urb;
+	}
+	intr->urb = urb;
+	spin_unlock_irq(&intr->lock);
+
+	/* TODO: make it a DMA buffer */
+	r = -ENOMEM;
+	transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"couldn't allocate transfer_buffer\n");
+		goto error_set_urb_null;
+	}
+
+	udev = zd_usb_to_usbdev(usb);
+	usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN),
+			 transfer_buffer, USB_MAX_EP_INT_BUFFER,
+			 int_urb_complete, usb,
+			 intr->interval);
+
+	dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb);
+	r = usb_submit_urb(urb, GFP_KERNEL);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "Couldn't submit urb. Error number %d\n", r);
+		goto error;
+	}
+
+	return 0;
+error:
+	kfree(transfer_buffer);
+error_set_urb_null:
+	spin_lock_irq(&intr->lock);
+	intr->urb = NULL;
+	spin_unlock_irq(&intr->lock);
+error_free_urb:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+void zd_usb_disable_int(struct zd_usb *usb)
+{
+	unsigned long flags;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	struct urb *urb;
+
+	spin_lock_irqsave(&intr->lock, flags);
+	urb = intr->urb;
+	if (!urb) {
+		spin_unlock_irqrestore(&intr->lock, flags);
+		return;
+	}
+	intr->urb = NULL;
+	spin_unlock_irqrestore(&intr->lock, flags);
+
+	usb_kill_urb(urb);
+	dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb);
+	usb_free_urb(urb);
+}
+
+static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	int i;
+	const struct rx_length_info *length_info;
+
+	if (length < sizeof(struct rx_length_info)) {
+		/* It's not a complete packet anyhow. */
+		return;
+	}
+	length_info = (struct rx_length_info *)
+		(buffer + length - sizeof(struct rx_length_info));
+
+	/* It might be that three frames are merged into a single URB
+	 * transaction. We have to check for the length info tag.
+	 *
+	 * While testing we discovered that length_info might be unaligned,
+	 * because if USB transactions are merged, the last packet will not
+	 * be padded. Unaligned access might also happen if the length_info
+	 * structure is not present.
+	 */
+	if (get_unaligned(&length_info->tag) == cpu_to_le16(RX_LENGTH_INFO_TAG))
+	{
+		unsigned int l, k, n;
+		for (i = 0, l = 0;; i++) {
+			k = le16_to_cpu(get_unaligned(&length_info->length[i]));
+			if (k == 0)
+				return;
+			n = l+k;
+			if (n > length)
+				return;
+			zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k);
+			if (i >= 2)
+				return;
+			l = (n+3) & ~3;
+		}
+	} else {
+		zd_mac_rx(zd_usb_to_hw(usb), buffer, length);
+	}
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	struct zd_usb *usb;
+	struct zd_usb_rx *rx;
+	const u8 *buffer;
+	unsigned int length;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		return;
+	default:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = urb->actual_length;
+	usb = urb->context;
+	rx = &usb->rx;
+
+	if (length%rx->usb_packet_size > rx->usb_packet_size-4) {
+		/* If there is an old first fragment, we don't care. */
+		dev_dbg_f(urb_dev(urb), "*** first fragment ***\n");
+		ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment));
+		spin_lock(&rx->lock);
+		memcpy(rx->fragment, buffer, length);
+		rx->fragment_length = length;
+		spin_unlock(&rx->lock);
+		goto resubmit;
+	}
+
+	spin_lock(&rx->lock);
+	if (rx->fragment_length > 0) {
+		/* We are on a second fragment, we believe */
+		ZD_ASSERT(length + rx->fragment_length <=
+			  ARRAY_SIZE(rx->fragment));
+		dev_dbg_f(urb_dev(urb), "*** second fragment ***\n");
+		memcpy(rx->fragment+rx->fragment_length, buffer, length);
+		handle_rx_packet(usb, rx->fragment,
+			         rx->fragment_length + length);
+		rx->fragment_length = 0;
+		spin_unlock(&rx->lock);
+	} else {
+		spin_unlock(&rx->lock);
+		handle_rx_packet(usb, buffer, length);
+	}
+
+resubmit:
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static struct urb *alloc_rx_urb(struct zd_usb *usb)
+{
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+	buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+		                  &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+		          buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+		        urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+int zd_usb_enable_rx(struct zd_usb *usb)
+{
+	int i, r;
+	struct zd_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&rx->lock);
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		usb_kill_urb(urbs[i]);
+	}
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+void zd_usb_disable_rx(struct zd_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct zd_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+/**
+ * zd_usb_disable_tx - disable transmission
+ * @usb: the zd1211rw-private USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void zd_usb_disable_tx(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+	struct list_head *pos, *n;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	list_for_each_safe(pos, n, &tx->free_urb_list) {
+		list_del(pos);
+		usb_free_urb(list_entry(pos, struct urb, urb_list));
+	}
+	tx->enabled = 0;
+	tx->submitted_urbs = 0;
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following zd_usb_enable_tx().
+	 */
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * zd_usb_enable_tx - enables transmission
+ * @usb: a &struct zd_usb pointer
+ *
+ * This function enables transmission and prepares the &zd_usb_tx data
+ * structure.
+ */
+void zd_usb_enable_tx(struct zd_usb *usb)
+{
+	unsigned long flags;
+	struct zd_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	tx->enabled = 1;
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(zd_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * alloc_tx_urb - provides an tx URB
+ * @usb: a &struct zd_usb pointer
+ *
+ * Allocates a new URB. If possible takes the urb from the free list in
+ * usb->tx.
+ */
+static struct urb *alloc_tx_urb(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+	struct list_head *entry;
+	struct urb *urb;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	if (list_empty(&tx->free_urb_list)) {
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		goto out;
+	}
+	entry = tx->free_urb_list.next;
+	list_del(entry);
+	urb = list_entry(entry, struct urb, urb_list);
+out:
+	spin_unlock_irqrestore(&tx->lock, flags);
+	return urb;
+}
+
+/**
+ * free_tx_urb - frees a used tx URB
+ * @usb: a &struct zd_usb pointer
+ * @urb: URB to be freed
+ *
+ * Frees the the transmission URB, which means to put it on the free URB
+ * list.
+ */
+static void free_tx_urb(struct zd_usb *usb, struct urb *urb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	if (!tx->enabled) {
+		usb_free_urb(urb);
+		goto out;
+	}
+	list_add(&urb->urb_list, &tx->free_urb_list);
+out:
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void tx_dec_submitted_urbs(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	--tx->submitted_urbs;
+	if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) {
+		ieee80211_wake_queues(zd_usb_to_hw(usb));
+		tx->stopped = 0;
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void tx_inc_submitted_urbs(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	++tx->submitted_urbs;
+	if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) {
+		ieee80211_stop_queues(zd_usb_to_hw(usb));
+		tx->stopped = 1;
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+static void tx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct sk_buff *skb;
+	struct zd_tx_skb_control_block *cb;
+	struct zd_usb *usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+free_urb:
+	skb = (struct sk_buff *)urb->context;
+	zd_mac_tx_to_dev(skb, urb->status);
+	cb = (struct zd_tx_skb_control_block *)skb->cb;
+	usb = &zd_hw_mac(cb->hw)->chip.usb;
+	free_tx_urb(usb, urb);
+	tx_dec_submitted_urbs(usb);
+	return;
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r);
+		goto free_urb;
+	}
+}
+
+/**
+ * zd_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the zd1211rw-private USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
+	struct urb *urb;
+
+	urb = alloc_tx_urb(usb);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+		          skb->data, skb->len, tx_urb_complete, skb);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		goto error;
+	tx_inc_submitted_urbs(usb);
+	return 0;
+error:
+	free_tx_urb(usb, urb);
+out:
+	return r;
+}
+
+static inline void init_usb_interrupt(struct zd_usb *usb)
+{
+	struct zd_usb_interrupt *intr = &usb->intr;
+
+	spin_lock_init(&intr->lock);
+	intr->interval = int_urb_interval(zd_usb_to_usbdev(usb));
+	init_completion(&intr->read_regs.completion);
+	intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT);
+}
+
+static inline void init_usb_rx(struct zd_usb *usb)
+{
+	struct zd_usb_rx *rx = &usb->rx;
+	spin_lock_init(&rx->lock);
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) {
+		rx->usb_packet_size = 512;
+	} else {
+		rx->usb_packet_size = 64;
+	}
+	ZD_ASSERT(rx->fragment_length == 0);
+}
+
+static inline void init_usb_tx(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	spin_lock_init(&tx->lock);
+	tx->enabled = 0;
+	tx->stopped = 0;
+	INIT_LIST_HEAD(&tx->free_urb_list);
+	tx->submitted_urbs = 0;
+}
+
+void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
+	         struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_interrupt(usb);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void zd_usb_clear(struct zd_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	ZD_MEMCLEAR(usb, sizeof(*usb));
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+static const char *speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown speed";
+	}
+}
+
+static int scnprint_id(struct usb_device *udev, char *buffer, size_t size)
+{
+	return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		get_bcdDevice(udev),
+		speed(udev->speed));
+}
+
+int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size)
+{
+	struct usb_device *udev = interface_to_usbdev(usb->intf);
+	return scnprint_id(udev, buffer, size);
+}
+
+#ifdef DEBUG
+static void print_id(struct usb_device *udev)
+{
+	char buffer[40];
+
+	scnprint_id(udev, buffer, sizeof(buffer));
+	buffer[sizeof(buffer)-1] = 0;
+	dev_dbg_f(&udev->dev, "%s\n", buffer);
+}
+#else
+#define print_id(udev) do { } while (0)
+#endif
+
+static int eject_installer(struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *iface_desc = &intf->altsetting[0];
+	struct usb_endpoint_descriptor *endpoint;
+	unsigned char *cmd;
+	u8 bulk_out_ep;
+	int r;
+
+	/* Find bulk out endpoint */
+	endpoint = &iface_desc->endpoint[1].desc;
+	if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT &&
+	    (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+	    USB_ENDPOINT_XFER_BULK) {
+		bulk_out_ep = endpoint->bEndpointAddress;
+	} else {
+		dev_err(&udev->dev,
+			"zd1211rw: Could not find bulk out endpoint\n");
+		return -ENODEV;
+	}
+
+	cmd = kzalloc(31, GFP_KERNEL);
+	if (cmd == NULL)
+		return -ENODEV;
+
+	/* USB bulk command block */
+	cmd[0] = 0x55;	/* bulk command signature */
+	cmd[1] = 0x53;	/* bulk command signature */
+	cmd[2] = 0x42;	/* bulk command signature */
+	cmd[3] = 0x43;	/* bulk command signature */
+	cmd[14] = 6;	/* command length */
+
+	cmd[15] = 0x1b;	/* SCSI command: START STOP UNIT */
+	cmd[19] = 0x2;	/* eject disc */
+
+	dev_info(&udev->dev, "Ejecting virtual installer media...\n");
+	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep),
+		cmd, 31, NULL, 2000);
+	kfree(cmd);
+	if (r)
+		return r;
+
+	/* At this point, the device disconnects and reconnects with the real
+	 * ID numbers. */
+
+	usb_set_intfdata(intf, NULL);
+	return 0;
+}
+
+int zd_usb_init_hw(struct zd_usb *usb)
+{
+	int r;
+	struct zd_mac *mac = zd_usb_to_mac(usb);
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	r = upload_firmware(usb);
+	if (r) {
+		dev_err(zd_usb_dev(usb),
+		       "couldn't load firmware. Error number %d\n", r);
+		return r;
+	}
+
+	r = usb_reset_configuration(zd_usb_to_usbdev(usb));
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"couldn't reset configuration. Error number %d\n", r);
+		return r;
+	}
+
+	r = zd_mac_init_hw(mac->hw);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+		         "couldn't initialize mac. Error number %d\n", r);
+		return r;
+	}
+
+	usb->initialized = 1;
+	return 0;
+}
+
+static int probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct zd_usb *usb;
+	struct ieee80211_hw *hw = NULL;
+
+	print_id(udev);
+
+	if (id->driver_info & DEVICE_INSTALLER)
+		return eject_installer(intf);
+
+	switch (udev->speed) {
+	case USB_SPEED_LOW:
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+		break;
+	default:
+		dev_dbg_f(&intf->dev, "Unknown USB speed\n");
+		r = -ENODEV;
+		goto error;
+	}
+
+	r = usb_reset_device(udev);
+	if (r) {
+		dev_err(&intf->dev,
+			"couldn't reset usb device. Error number %d\n", r);
+		goto error;
+	}
+
+	hw = zd_mac_alloc_hw(intf);
+	if (hw == NULL) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	usb = &zd_hw_mac(hw)->chip.usb;
+	usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0;
+
+	r = zd_mac_preinit_hw(hw);
+	if (r) {
+		dev_dbg_f(&intf->dev,
+		         "couldn't initialize mac. Error number %d\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg_f(&intf->dev,
+			 "couldn't register device. Error number %d\n", r);
+		goto error;
+	}
+
+	dev_dbg_f(&intf->dev, "successful\n");
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+	return 0;
+error:
+	usb_reset_device(interface_to_usbdev(intf));
+	if (hw) {
+		zd_mac_clear(zd_hw_mac(hw));
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = zd_intf_to_hw(intf);
+	struct zd_mac *mac;
+	struct zd_usb *usb;
+
+	/* Either something really bad happened, or we're just dealing with
+	 * a DEVICE_INSTALLER. */
+	if (hw == NULL)
+		return;
+
+	mac = zd_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	ieee80211_unregister_hw(hw);
+
+	/* Just in case something has gone wrong! */
+	zd_usb_disable_rx(usb);
+	zd_usb_disable_int(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here, the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	zd_mac_clear(mac);
+	ieee80211_free_hw(hw);
+	dev_dbg(&intf->dev, "disconnected\n");
+}
+
+static struct usb_driver driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= usb_ids,
+	.probe		= probe,
+	.disconnect	= disconnect,
+};
+
+struct workqueue_struct *zd_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	pr_debug("%s usb_init()\n", driver.name);
+
+	zd_workqueue = create_singlethread_workqueue(driver.name);
+	if (zd_workqueue == NULL) {
+		printk(KERN_ERR "%s couldn't create workqueue\n", driver.name);
+		return -ENOMEM;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(zd_workqueue);
+		printk(KERN_ERR "%s usb_register() failed. Error number %d\n",
+		       driver.name, r);
+		return r;
+	}
+
+	pr_debug("%s initialized\n", driver.name);
+	return 0;
+}
+
+static void __exit usb_exit(void)
+{
+	pr_debug("%s usb_exit()\n", driver.name);
+	usb_deregister(&driver);
+	destroy_workqueue(zd_workqueue);
+}
+
+module_init(usb_init);
+module_exit(usb_exit);
+
+static int usb_int_regs_length(unsigned int count)
+{
+	return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data);
+}
+
+static void prepare_read_regs_int(struct zd_usb *usb)
+{
+	struct zd_usb_interrupt *intr = &usb->intr;
+
+	spin_lock_irq(&intr->lock);
+	intr->read_regs_enabled = 1;
+	INIT_COMPLETION(intr->read_regs.completion);
+	spin_unlock_irq(&intr->lock);
+}
+
+static void disable_read_regs_int(struct zd_usb *usb)
+{
+	struct zd_usb_interrupt *intr = &usb->intr;
+
+	spin_lock_irq(&intr->lock);
+	intr->read_regs_enabled = 0;
+	spin_unlock_irq(&intr->lock);
+}
+
+static int get_results(struct zd_usb *usb, u16 *values,
+	               struct usb_req_read_regs *req, unsigned int count)
+{
+	int r;
+	int i;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	struct read_regs_int *rr = &intr->read_regs;
+	struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer;
+
+	spin_lock_irq(&intr->lock);
+
+	r = -EIO;
+	/* The created block size seems to be larger than expected.
+	 * However results appear to be correct.
+	 */
+	if (rr->length < usb_int_regs_length(count)) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: actual length %d less than expected %d\n",
+			 rr->length, usb_int_regs_length(count));
+		goto error_unlock;
+	}
+	if (rr->length > sizeof(rr->buffer)) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: actual length %d exceeds buffer size %zu\n",
+			 rr->length, sizeof(rr->buffer));
+		goto error_unlock;
+	}
+
+	for (i = 0; i < count; i++) {
+		struct reg_data *rd = &regs->regs[i];
+		if (rd->addr != req->addr[i]) {
+			dev_dbg_f(zd_usb_dev(usb),
+				 "rd[%d] addr %#06hx expected %#06hx\n", i,
+				 le16_to_cpu(rd->addr),
+				 le16_to_cpu(req->addr[i]));
+			goto error_unlock;
+		}
+		values[i] = le16_to_cpu(rd->value);
+	}
+
+	r = 0;
+error_unlock:
+	spin_unlock_irq(&intr->lock);
+	return r;
+}
+
+int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
+	             const zd_addr_t *addresses, unsigned int count)
+{
+	int r;
+	int i, req_len, actual_req_len;
+	struct usb_device *udev;
+	struct usb_req_read_regs *req = NULL;
+	unsigned long timeout;
+
+	if (count < 1) {
+		dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n");
+		return -EINVAL;
+	}
+	if (count > USB_MAX_IOREAD16_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: count %u exceeds possible max %u\n",
+			 count, USB_MAX_IOREAD16_COUNT);
+		return -EINVAL;
+	}
+	if (in_atomic()) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: io in atomic context not supported\n");
+		return -EWOULDBLOCK;
+	}
+	if (!usb_int_enabled(usb)) {
+		 dev_dbg_f(zd_usb_dev(usb),
+			  "error: usb interrupt not enabled\n");
+		return -EWOULDBLOCK;
+	}
+
+	req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16);
+	req = kmalloc(req_len, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+	req->id = cpu_to_le16(USB_REQ_READ_REGS);
+	for (i = 0; i < count; i++)
+		req->addr[i] = cpu_to_le16((u16)addresses[i]);
+
+	udev = zd_usb_to_usbdev(usb);
+	prepare_read_regs_int(usb);
+	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
+		         req, req_len, &actual_req_len, 1000 /* ms */);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg(). Error number %d\n", r);
+		goto error;
+	}
+	if (req_len != actual_req_len) {
+		dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()\n"
+			" req_len %d != actual_req_len %d\n",
+			req_len, actual_req_len);
+		r = -EIO;
+		goto error;
+	}
+
+	timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion,
+	                                      msecs_to_jiffies(1000));
+	if (!timeout) {
+		disable_read_regs_int(usb);
+		dev_dbg_f(zd_usb_dev(usb), "read timed out\n");
+		r = -ETIMEDOUT;
+		goto error;
+	}
+
+	r = get_results(usb, values, req, count);
+error:
+	kfree(req);
+	return r;
+}
+
+int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+	              unsigned int count)
+{
+	int r;
+	struct usb_device *udev;
+	struct usb_req_write_regs *req = NULL;
+	int i, req_len, actual_req_len;
+
+	if (count == 0)
+		return 0;
+	if (count > USB_MAX_IOWRITE16_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: count %u exceeds possible max %u\n",
+			count, USB_MAX_IOWRITE16_COUNT);
+		return -EINVAL;
+	}
+	if (in_atomic()) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: io in atomic context not supported\n");
+		return -EWOULDBLOCK;
+	}
+
+	req_len = sizeof(struct usb_req_write_regs) +
+		  count * sizeof(struct reg_data);
+	req = kmalloc(req_len, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->id = cpu_to_le16(USB_REQ_WRITE_REGS);
+	for (i = 0; i < count; i++) {
+		struct reg_data *rw  = &req->reg_writes[i];
+		rw->addr = cpu_to_le16((u16)ioreqs[i].addr);
+		rw->value = cpu_to_le16(ioreqs[i].value);
+	}
+
+	udev = zd_usb_to_usbdev(usb);
+	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
+		         req, req_len, &actual_req_len, 1000 /* ms */);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg(). Error number %d\n", r);
+		goto error;
+	}
+	if (req_len != actual_req_len) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg()"
+			" req_len %d != actual_req_len %d\n",
+			req_len, actual_req_len);
+		r = -EIO;
+		goto error;
+	}
+
+	/* FALL-THROUGH with r == 0 */
+error:
+	kfree(req);
+	return r;
+}
+
+int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
+{
+	int r;
+	struct usb_device *udev;
+	struct usb_req_rfwrite *req = NULL;
+	int i, req_len, actual_req_len;
+	u16 bit_value_template;
+
+	if (in_atomic()) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: io in atomic context not supported\n");
+		return -EWOULDBLOCK;
+	}
+	if (bits < USB_MIN_RFWRITE_BIT_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: bits %d are smaller than"
+			" USB_MIN_RFWRITE_BIT_COUNT %d\n",
+			bits, USB_MIN_RFWRITE_BIT_COUNT);
+		return -EINVAL;
+	}
+	if (bits > USB_MAX_RFWRITE_BIT_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n",
+			bits, USB_MAX_RFWRITE_BIT_COUNT);
+		return -EINVAL;
+	}
+#ifdef DEBUG
+	if (value & (~0UL << bits)) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: value %#09x has bits >= %d set\n",
+			value, bits);
+		return -EINVAL;
+	}
+#endif /* DEBUG */
+
+	dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits);
+
+	r = zd_usb_ioread16(usb, &bit_value_template, CR203);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error %d: Couldn't read CR203\n", r);
+		goto out;
+	}
+	bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA);
+
+	req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16);
+	req = kmalloc(req_len, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->id = cpu_to_le16(USB_REQ_WRITE_RF);
+	/* 1: 3683a, but not used in ZYDAS driver */
+	req->value = cpu_to_le16(2);
+	req->bits = cpu_to_le16(bits);
+
+	for (i = 0; i < bits; i++) {
+		u16 bv = bit_value_template;
+		if (value & (1 << (bits-1-i)))
+			bv |= RF_DATA;
+		req->bit_values[i] = cpu_to_le16(bv);
+	}
+
+	udev = zd_usb_to_usbdev(usb);
+	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
+		         req, req_len, &actual_req_len, 1000 /* ms */);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg(). Error number %d\n", r);
+		goto out;
+	}
+	if (req_len != actual_req_len) {
+		dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()"
+			" req_len %d != actual_req_len %d\n",
+			req_len, actual_req_len);
+		r = -EIO;
+		goto out;
+	}
+
+	/* FALL-THROUGH with r == 0 */
+out:
+	kfree(req);
+	return r;
+}
diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_usb.h
--- /dev/null
+++ a/drivers/net/wireless/zd1211rw-mac80211/zd_usb.h
@@ -0,0 +1,263 @@
+/* zd_usb.h: Header for USB interface implemented by ZD1211 chip
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_USB_H
+#define _ZD_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "zd_def.h"
+
+#define ZD_USB_TX_HIGH  5
+#define ZD_USB_TX_LOW   2
+
+enum devicetype {
+	DEVICE_ZD1211  = 0,
+	DEVICE_ZD1211B = 1,
+	DEVICE_INSTALLER = 2,
+};
+
+enum endpoints {
+	EP_CTRL	    = 0,
+	EP_DATA_OUT = 1,
+	EP_DATA_IN  = 2,
+	EP_INT_IN   = 3,
+	EP_REGS_OUT = 4,
+};
+
+enum {
+	USB_MAX_TRANSFER_SIZE		= 4096, /* bytes */
+	/* FIXME: The original driver uses this value. We have to check,
+	 * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be
+	 * used if one combined frame is split over two USB transactions.
+	 */
+	USB_MAX_RX_SIZE			= 4800, /* bytes */
+	USB_MAX_IOWRITE16_COUNT		= 15,
+	USB_MAX_IOWRITE32_COUNT		= USB_MAX_IOWRITE16_COUNT/2,
+	USB_MAX_IOREAD16_COUNT		= 15,
+	USB_MAX_IOREAD32_COUNT		= USB_MAX_IOREAD16_COUNT/2,
+	USB_MIN_RFWRITE_BIT_COUNT	= 16,
+	USB_MAX_RFWRITE_BIT_COUNT	= 28,
+	USB_MAX_EP_INT_BUFFER		= 64,
+	USB_ZD1211B_BCD_DEVICE		= 0x4810,
+};
+
+enum control_requests {
+	USB_REQ_WRITE_REGS		= 0x21,
+	USB_REQ_READ_REGS		= 0x22,
+	USB_REQ_WRITE_RF		= 0x23,
+	USB_REQ_PROG_FLASH		= 0x24,
+	USB_REQ_EEPROM_START		= 0x0128, /* ? request is a byte */
+	USB_REQ_EEPROM_MID		= 0x28,
+	USB_REQ_EEPROM_END		= 0x0228, /* ? request is a byte */
+	USB_REQ_FIRMWARE_DOWNLOAD	= 0x30,
+	USB_REQ_FIRMWARE_CONFIRM	= 0x31,
+	USB_REQ_FIRMWARE_READ_DATA	= 0x32,
+};
+
+struct usb_req_read_regs {
+	__le16 id;
+	__le16 addr[0];
+} __attribute__((packed));
+
+struct reg_data {
+	__le16 addr;
+	__le16 value;
+} __attribute__((packed));
+
+struct usb_req_write_regs {
+	__le16 id;
+	struct reg_data reg_writes[0];
+} __attribute__((packed));
+
+enum {
+	RF_IF_LE = 0x02,
+	RF_CLK   = 0x04,
+	RF_DATA	 = 0x08,
+};
+
+struct usb_req_rfwrite {
+	__le16 id;
+	__le16 value;
+	/* 1: 3683a */
+	/* 2: other (default) */
+	__le16 bits;
+	/* RF2595: 24 */
+	__le16 bit_values[0];
+	/* (CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */
+} __attribute__((packed));
+
+/* USB interrupt */
+
+enum usb_int_id {
+	USB_INT_TYPE			= 0x01,
+	USB_INT_ID_REGS			= 0x90,
+	USB_INT_ID_RETRY_FAILED		= 0xa0,
+};
+
+enum usb_int_flags {
+	USB_INT_READ_REGS_EN		= 0x01,
+};
+
+struct usb_int_header {
+	u8 type;	/* must always be 1 */
+	u8 id;
+} __attribute__((packed));
+
+struct usb_int_regs {
+	struct usb_int_header hdr;
+	struct reg_data regs[0];
+} __attribute__((packed));
+
+struct usb_int_retry_fail {
+	struct usb_int_header hdr;
+	u8 new_rate;
+	u8 _dummy;
+	u8 addr[ETH_ALEN];
+	u8 ibss_wakeup_dest;
+} __attribute__((packed));
+
+struct read_regs_int {
+	struct completion completion;
+	/* Stores the USB int structure and contains the USB address of the
+	 * first requested register before request.
+	 */
+	u8 buffer[USB_MAX_EP_INT_BUFFER];
+	int length;
+	__le16 cr_int_addr;
+};
+
+struct zd_ioreq16 {
+	zd_addr_t addr;
+	u16 value;
+};
+
+struct zd_ioreq32 {
+	zd_addr_t addr;
+	u32 value;
+};
+
+struct zd_usb_interrupt {
+	struct read_regs_int read_regs;
+	spinlock_t lock;
+	struct urb *urb;
+	int interval;
+	u8 read_regs_enabled:1;
+};
+
+static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr)
+{
+	return (struct usb_int_regs *)intr->read_regs.buffer;
+}
+
+#define RX_URBS_COUNT 5
+
+struct zd_usb_rx {
+	spinlock_t lock;
+	u8 fragment[2*USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+/**
+ * struct zd_usb_tx - structure used for transmitting frames
+ * @lock: lock for transmission
+ * @free_urb_list: list of free URBs, contains all the URBs, which can be used
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *	device, which haven't been completed
+ * @enabled: enabled flag, indicates whether tx is enabled
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+struct zd_usb_tx {
+	spinlock_t lock;
+	struct list_head free_urb_list;
+	int submitted_urbs;
+	int enabled;
+	int stopped;
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct zd_usb {
+	struct zd_usb_interrupt intr;
+	struct zd_usb_rx rx;
+	struct zd_usb_tx tx;
+	struct usb_interface *intf;
+	u8 is_zd1211b:1, initialized:1;
+};
+
+#define zd_usb_dev(usb) (&usb->intf->dev)
+
+static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *zd_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *zd_usb_to_hw(struct zd_usb *usb)
+{
+	return zd_intf_to_hw(usb->intf);
+}
+
+void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
+	         struct usb_interface *intf);
+int zd_usb_init_hw(struct zd_usb *usb);
+void zd_usb_clear(struct zd_usb *usb);
+
+int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size);
+
+int zd_usb_enable_int(struct zd_usb *usb);
+void zd_usb_disable_int(struct zd_usb *usb);
+
+int zd_usb_enable_rx(struct zd_usb *usb);
+void zd_usb_disable_rx(struct zd_usb *usb);
+
+void zd_usb_enable_tx(struct zd_usb *usb);
+void zd_usb_disable_tx(struct zd_usb *usb);
+
+int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb);
+
+int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
+	         const zd_addr_t *addresses, unsigned int count);
+
+static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value,
+	                      const zd_addr_t addr)
+{
+	return zd_usb_ioread16v(usb, value, (const zd_addr_t *)&addr, 1);
+}
+
+int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+	              unsigned int count);
+
+int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits);
+
+int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len);
+
+extern struct workqueue_struct *zd_workqueue;
+
+#endif /* _ZD_USB_H */
diff -puN drivers/usb/host/Kconfig~git-wireless drivers/usb/host/Kconfig
--- a/drivers/usb/host/Kconfig~git-wireless
+++ a/drivers/usb/host/Kconfig
@@ -154,6 +154,19 @@ config USB_OHCI_HCD_PCI
 	  Enables support for PCI-bus plug-in USB controller cards.
 	  If unsure, say Y.
 
+config USB_OHCI_HCD_SSB
+	bool "OHCI support for the Broadcom SSB OHCI core (embedded systems only)"
+	depends on USB_OHCI_HCD && ((USB_OHCI_HCD=m && SSB) || (USB_OHCI_HCD=y && SSB=y)) && EXPERIMENTAL
+	default n
+	---help---
+	  Support for the Sonics Silicon Backplane (SSB) attached
+	  Broadcom USB OHCI core.
+
+	  This device is only present in some embedded devices with
+	  Broadcom based SSB bus.
+
+	  If unsure, say N.
+
 config USB_OHCI_BIG_ENDIAN_DESC
 	bool
 	depends on USB_OHCI_HCD
diff -puN drivers/usb/host/ohci-hcd.c~git-wireless drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c~git-wireless
+++ a/drivers/usb/host/ohci-hcd.c
@@ -1033,11 +1033,17 @@ MODULE_LICENSE ("GPL");
 #define PS3_SYSTEM_BUS_DRIVER	ps3_ohci_driver
 #endif
 
+#ifdef CONFIG_USB_OHCI_HCD_SSB
+#include "ohci-ssb.c"
+#define SSB_OHCI_DRIVER		ssb_ohci_driver
+#endif
+
 #if	!defined(PCI_DRIVER) &&		\
 	!defined(PLATFORM_DRIVER) &&	\
 	!defined(OF_PLATFORM_DRIVER) &&	\
 	!defined(SA1111_DRIVER) &&	\
-	!defined(PS3_SYSTEM_BUS_DRIVER)
+	!defined(PS3_SYSTEM_BUS_DRIVER) && \
+	!defined(SSB_OHCI_DRIVER)
 #error "missing bus glue for ohci-hcd"
 #endif
 
@@ -1082,10 +1088,20 @@ static int __init ohci_hcd_mod_init(void
 		goto error_pci;
 #endif
 
+#ifdef SSB_OHCI_DRIVER
+	retval = ssb_driver_register(&SSB_OHCI_DRIVER);
+	if (retval)
+		goto error_ssb;
+#endif
+
 	return retval;
 
 	/* Error path */
+#ifdef SSB_OHCI_DRIVER
+ error_ssb:
+#endif
 #ifdef PCI_DRIVER
+	pci_unregister_driver(&PCI_DRIVER);
  error_pci:
 #endif
 #ifdef SA1111_DRIVER
@@ -1110,6 +1126,9 @@ module_init(ohci_hcd_mod_init);
 
 static void __exit ohci_hcd_mod_exit(void)
 {
+#ifdef SSB_OHCI_DRIVER
+	ssb_driver_unregister(&SSB_OHCI_DRIVER);
+#endif
 #ifdef PCI_DRIVER
 	pci_unregister_driver(&PCI_DRIVER);
 #endif
diff -puN /dev/null drivers/usb/host/ohci-ssb.c
--- /dev/null
+++ a/drivers/usb/host/ohci-ssb.c
@@ -0,0 +1,254 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom USB-core OHCI driver
+ *
+ * Copyright 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Derived from the USBcore related parts of Broadcom-SB
+ * Copyright 2005 Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+
+
+#define SSB_OHCI_TMSLOW_HOSTMODE	(1 << 29)
+
+struct ssb_ohci_device {
+	struct ohci_hcd ohci; /* _must_ be at the beginning. */
+
+	u32 enable_flags;
+};
+
+
+static inline
+struct ssb_ohci_device * hcd_to_ssb_ohci(struct usb_hcd *hcd)
+{
+	return (struct ssb_ohci_device *)(hcd->hcd_priv);
+}
+
+
+static const struct ssb_device_id ssb_ohci_table[] = {
+	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
+	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
+	SSB_DEVTABLE_END
+};
+MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
+
+
+static int ssb_ohci_reset(struct usb_hcd *hcd)
+{
+	struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
+	struct ohci_hcd *ohci = &ohcidev->ohci;
+	int err;
+
+	ohci_hcd_init(ohci);
+	err = ohci_init(ohci);
+
+	return err;
+}
+
+static int ssb_ohci_start(struct usb_hcd *hcd)
+{
+	struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
+	struct ohci_hcd *ohci = &ohcidev->ohci;
+	int err;
+
+	err = ohci_run(ohci);
+	if (err < 0) {
+		ohci_err(ohci, "can't start\n");
+		ohci_stop(hcd);
+	}
+
+	return err;
+}
+
+#ifdef CONFIG_PM
+static int ssb_ohci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
+{
+	struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
+	struct ohci_hcd *ohci = &ohcidev->ohci;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ohci->lock, flags);
+
+	ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+	ohci_readl(ohci, &ohci->regs->intrdisable); /* commit write */
+
+	/* make sure snapshot being resumed re-enumerates everything */
+	if (message.event == PM_EVENT_PRETHAW)
+		ohci_usb_reset(ohci);
+
+	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+	spin_unlock_irqrestore(&ohci->lock, flags);
+
+	return 0;
+}
+
+static int ssb_ohci_hcd_resume(struct usb_hcd *hcd)
+{
+	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+	usb_hcd_resume_root_hub(hcd);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct hc_driver ssb_ohci_hc_driver = {
+	.description		= "ssb-usb-ohci",
+	.product_desc		= "SSB OHCI Controller",
+	.hcd_priv_size		= sizeof(struct ssb_ohci_device),
+
+	.irq			= ohci_irq,
+	.flags			= HCD_MEMORY | HCD_USB11,
+
+	.reset			= ssb_ohci_reset,
+	.start			= ssb_ohci_start,
+	.stop			= ohci_stop,
+	.shutdown		= ohci_shutdown,
+
+#ifdef CONFIG_PM
+	.suspend		= ssb_ohci_hcd_suspend,
+	.resume			= ssb_ohci_hcd_resume,
+#endif
+
+	.urb_enqueue		= ohci_urb_enqueue,
+	.urb_dequeue		= ohci_urb_dequeue,
+	.endpoint_disable	= ohci_endpoint_disable,
+
+	.get_frame_number	= ohci_get_frame,
+
+	.hub_status_data	= ohci_hub_status_data,
+	.hub_control		= ohci_hub_control,
+	.hub_irq_enable		= ohci_rhsc_enable,
+
+#ifdef CONFIG_PM
+	.bus_suspend		= ohci_bus_suspend,
+	.bus_resume		= ohci_bus_resume,
+#endif
+
+	.start_port_reset	= ohci_start_port_reset,
+};
+
+
+static void ssb_ohci_detach(struct ssb_device *dev)
+{
+	struct usb_hcd *hcd = ssb_get_drvdata(dev);
+
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	usb_put_hcd(hcd);
+	ssb_device_disable(dev, 0);
+}
+
+static int ssb_ohci_attach(struct ssb_device *dev)
+{
+	struct ssb_ohci_device *ohcidev;
+	struct usb_hcd *hcd;
+	int err = -ENOMEM;
+	u32 tmp, flags = 0;
+
+	if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
+		flags |= SSB_OHCI_TMSLOW_HOSTMODE;
+
+	ssb_device_enable(dev, flags);
+
+	hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
+			     dev->dev->bus_id);
+	if (!hcd)
+		goto err_dev_disable;
+	ohcidev = hcd_to_ssb_ohci(hcd);
+	ohcidev->enable_flags = flags;
+
+	tmp = ssb_read32(dev, SSB_ADMATCH0);
+	hcd->rsrc_start = ssb_admatch_base(tmp);
+	hcd->rsrc_len = ssb_admatch_size(tmp);
+	hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs)
+		goto err_put_hcd;
+	err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
+	if (err)
+		goto err_iounmap;
+
+	ssb_set_drvdata(dev, hcd);
+
+	return err;
+
+err_iounmap:
+	iounmap(hcd->regs);
+err_put_hcd:
+	usb_put_hcd(hcd);
+err_dev_disable:
+	ssb_device_disable(dev, flags);
+	return err;
+}
+
+static int ssb_ohci_probe(struct ssb_device *dev,
+			  const struct ssb_device_id *id)
+{
+	int err;
+	u16 chipid_top;
+
+	chipid_top = (dev->bus->chip_id & 0xFF00);
+	if (chipid_top != 0x4700 &&
+	    chipid_top != 0x5300) {
+		/* USBcores are only connected on embedded devices. */
+		return -ENODEV;
+	}
+	/* TODO: Probably need more checks here whether the core is connected. */
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	/* We currently always attach SSB_DEV_USB11_HOSTDEV
+	 * as HOST OHCI. If we want to attach it as Client device,
+	 * we must branch here and call into the (yet to
+	 * be written) Client mode driver. Same for remove(). */
+
+	err = ssb_ohci_attach(dev);
+
+	return err;
+}
+
+static void ssb_ohci_remove(struct ssb_device *dev)
+{
+	ssb_ohci_detach(dev);
+}
+
+#ifdef CONFIG_PM
+static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state)
+{
+	ssb_device_disable(dev, 0);
+
+	return 0;
+}
+
+static int ssb_ohci_resume(struct ssb_device *dev)
+{
+	struct usb_hcd *hcd = ssb_get_drvdata(dev);
+	struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
+
+	ssb_device_enable(dev, ohcidev->enable_flags);
+
+	return 0;
+}
+#else /* CONFIG_PM */
+# define ssb_ohci_suspend	NULL
+# define ssb_ohci_resume	NULL
+#endif /* CONFIG_PM */
+
+static struct ssb_driver ssb_ohci_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= ssb_ohci_table,
+	.probe		= ssb_ohci_probe,
+	.remove		= ssb_ohci_remove,
+	.suspend	= ssb_ohci_suspend,
+	.resume		= ssb_ohci_resume,
+};
_

